@alxia/react-router 0.36.1 → 0.38.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 +22 -5
- package/dist/chunks/{index-rgy503w9.js → index-2swe47m6.js} +41 -3
- package/dist/chunks/{index-rgy503w9.js.map → index-2swe47m6.js.map} +5 -4
- package/dist/errors/call-failure.d.ts +1 -1
- package/dist/errors/call-failure.d.ts.map +1 -1
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +18 -2
- package/dist/errors/index.js.map +3 -3
- package/dist/errors/problem-error.d.ts +50 -0
- package/dist/errors/problem-error.d.ts.map +1 -0
- package/dist/errors/route-error.d.ts +3 -1
- package/dist/errors/route-error.d.ts.map +1 -1
- package/dist/fetcher/index.d.ts +1 -0
- package/dist/fetcher/index.d.ts.map +1 -1
- package/dist/fetcher/index.js +57 -1
- package/dist/fetcher/index.js.map +4 -3
- package/dist/fetcher/use-fetcher-promise.d.ts +37 -0
- package/dist/fetcher/use-fetcher-promise.d.ts.map +1 -0
- package/dist/session/cookies.d.ts +7 -0
- package/dist/session/cookies.d.ts.map +1 -1
- package/dist/session/index.d.ts +2 -1
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +89 -7
- package/dist/session/index.js.map +6 -5
- package/dist/session/serialize.d.ts +9 -3
- package/dist/session/serialize.d.ts.map +1 -1
- package/dist/session/signed.d.ts +44 -0
- package/dist/session/signed.d.ts.map +1 -0
- package/docs/README.md +2 -2
- package/docs/guide/errors.md +32 -1
- package/docs/guide/fetcher.md +44 -0
- package/docs/guide/session.md +50 -1
- package/docs/roadmap.md +5 -0
- package/docs/troubleshooting.md +59 -0
- package/package.json +1 -1
package/dist/fetcher/index.js
CHANGED
|
@@ -92,6 +92,60 @@ function useFetcherAnswer(fetcher, onAnswer) {
|
|
|
92
92
|
}, [answer]);
|
|
93
93
|
return latest;
|
|
94
94
|
}
|
|
95
|
+
// src/fetcher/use-fetcher-promise.ts
|
|
96
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
97
|
+
class FetcherPromiseAborted extends Error {
|
|
98
|
+
name = "FetcherPromiseAborted";
|
|
99
|
+
reason;
|
|
100
|
+
constructor(reason) {
|
|
101
|
+
super(reason === "superseded" ? "useFetcherPromise: a newer call replaced this one before the fetcher answered" : "useFetcherPromise: the component unmounted before the fetcher answered");
|
|
102
|
+
this.reason = reason;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function useFetcherPromise(fetcher, options = {}) {
|
|
106
|
+
const waiting = useRef3(null);
|
|
107
|
+
const refusal = useRef3(options.refusal);
|
|
108
|
+
refusal.current = options.refusal;
|
|
109
|
+
useFetcherAnswer(fetcher, (answer) => {
|
|
110
|
+
const pending = waiting.current;
|
|
111
|
+
if (!pending)
|
|
112
|
+
return;
|
|
113
|
+
waiting.current = null;
|
|
114
|
+
let refused;
|
|
115
|
+
try {
|
|
116
|
+
refused = refusal.current?.(answer);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
pending.reject(error);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (refused === undefined)
|
|
122
|
+
pending.resolve(answer);
|
|
123
|
+
else
|
|
124
|
+
pending.reject(typeof refused === "string" ? new Error(refused) : refused);
|
|
125
|
+
});
|
|
126
|
+
useEffect3(() => () => {
|
|
127
|
+
const pending = waiting.current;
|
|
128
|
+
waiting.current = null;
|
|
129
|
+
pending?.reject(new FetcherPromiseAborted("unmounted"));
|
|
130
|
+
}, []);
|
|
131
|
+
return useCallback2((submit) => {
|
|
132
|
+
waiting.current?.reject(new FetcherPromiseAborted("superseded"));
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
const mine = { resolve, reject };
|
|
135
|
+
waiting.current = mine;
|
|
136
|
+
const fail = (error) => {
|
|
137
|
+
if (waiting.current === mine)
|
|
138
|
+
waiting.current = null;
|
|
139
|
+
reject(error);
|
|
140
|
+
};
|
|
141
|
+
try {
|
|
142
|
+
Promise.resolve(submit()).catch(fail);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
fail(error);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}, []);
|
|
148
|
+
}
|
|
95
149
|
// src/fetcher/use-form-refusal.ts
|
|
96
150
|
import { useMemo } from "react";
|
|
97
151
|
function useFormRefusal(result) {
|
|
@@ -101,10 +155,12 @@ function useFormRefusal(result) {
|
|
|
101
155
|
} : undefined, [result]);
|
|
102
156
|
}
|
|
103
157
|
export {
|
|
158
|
+
FetcherPromiseAborted,
|
|
104
159
|
useDebouncedFetcher,
|
|
105
160
|
useFetcherAnswer,
|
|
161
|
+
useFetcherPromise,
|
|
106
162
|
useFormRefusal
|
|
107
163
|
};
|
|
108
164
|
|
|
109
|
-
//# debugId=
|
|
165
|
+
//# debugId=6FFF1F15C0AB3E6F64756E2164756E21
|
|
110
166
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/fetcher/use-debounced-fetcher.ts", "../src/fetcher/use-fetcher-answer.ts", "../src/fetcher/use-form-refusal.ts"],
|
|
3
|
+
"sources": ["../src/fetcher/use-debounced-fetcher.ts", "../src/fetcher/use-fetcher-answer.ts", "../src/fetcher/use-fetcher-promise.ts", "../src/fetcher/use-form-refusal.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"import {\n\tstartTransition,\n\tuseCallback,\n\tuseEffect,\n\tuseLayoutEffect,\n\tuseRef,\n\tuseState,\n} from 'react';\nimport { useFetcher } from 'react-router';\n\n/** What `useFetcher<Data>` hands back as `data` (`Data` may be `typeof loader`). */\ntype FetcherData<Data> = ReturnType<typeof useFetcher<Data>>['data'];\n\n/** What `useDebouncedFetcher` is doing for the value typed. */\nexport type DebouncedFetcherState =\n\t/** Nothing to ask: disabled, or the value is shorter than `minLength`. */\n\t| 'idle'\n\t/** The value changed and the pause after the last change is running. */\n\t| 'waiting'\n\t/** The resource route is loading. */\n\t| 'loading'\n\t/** The route answered for the value as it stands. */\n\t| 'done';\n\nexport interface DebouncedFetcherOptions<Data = unknown> {\n\t/** Pause after the last change before the route is asked. Default 250. */\n\treadonly delayMs?: number;\n\t/** The trimmed value must have this many characters. Default 1. */\n\treadonly minLength?: number;\n\t/** `false` asks nothing and forgets the previous answer. Default `true`. */\n\treadonly enabled?: boolean;\n\t/** The fetcher's key, to share it with another component. */\n\treadonly key?: string;\n\t/**\n\t * Which answers may be kept as the stale `data` while the next value waits\n\t * or loads. Default: all. An answer it refuses (a failure) is still `data`\n\t * while it is the current one, but is never shown as the previous one: after a refused answer, the stale\n\t * data is the last *kept* answer, not the refused one.\n\t */\n\treadonly keep?: (data: NonNullable<FetcherData<Data>>) => boolean;\n}\n\nexport interface DebouncedFetcher<Data> {\n\t/**\n\t * The route's answer for the value as it stands, else (`stale`) the last\n\t * answer for an earlier value, kept until the new one lands. `undefined`\n\t * before any answer, and again once the hook goes `idle`.\n\t */\n\treadonly data: FetcherData<Data>;\n\t/** `data` answers an earlier value (or none is current yet). */\n\treadonly stale: boolean;\n\treadonly state: DebouncedFetcherState;\n\t/** Asks the route again for the value as it stands, without waiting. */\n\treadonly retry: () => void;\n}\n\n/**\n * A resource route loaded through a fetcher for a value that changes as the\n * user types. After `delayMs` without a change, and from `minLength`\n * characters, it calls `fetcher.load` once (a newer load cancels the\n * previous one). An answer only counts for the value it was asked for: while\n * a newer value waits or loads, `data` is the previous answer with `stale`\n * set, and an answer for an older value is never returned as current.\n * `to` is the route's URL, or a function of the trimmed value that builds it\n * (a string without a function is loaded as it is, the value being only the\n * trigger). A refusal the route answers as data is `data` like any answer.\n */\nexport function useDebouncedFetcher<Data>(\n\tvalue: string,\n\tto: string | ((value: string) => string),\n\toptions: DebouncedFetcherOptions<Data> = {},\n): DebouncedFetcher<Data> {\n\tconst { delayMs = 250, minLength = 1, enabled = true, key, keep } = options;\n\tconst keepRef = useRef(keep);\n\tkeepRef.current = keep;\n\tconst fetcher = useFetcher<Data>(key === undefined ? undefined : { key });\n\tconst load = useRef(fetcher.load);\n\tload.current = fetcher.load;\n\tconst text = value.trim();\n\tconst url =\n\t\tenabled && text.length >= minLength\n\t\t\t? typeof to === 'function'\n\t\t\t\t? to(text)\n\t\t\t\t: to\n\t\t\t: null;\n\t// The URL the fetcher was last told to load.\n\tconst [loaded, setLoaded] = useState<string | null>(null);\n\tconst previous = useRef<FetcherData<Data>>(undefined);\n\t// React Router applies the fetcher's `loading` state in a transition, so\n\t// right after `load()` a render can still show `idle` and the previous\n\t// answer. A load has landed only once the fetcher was seen busy for that very load (the load number is kept, so a busy render of a superseded load does not count), or\n\t// its `load()` promise settled and a transition queued after it committed\n\t// (a fast load whose busy state was never rendered, or a route answering\n\t// the very same object twice). `seq` numbers the loads, so a superseded\n\t// one never counts.\n\tconst seq = useRef(0);\n\tconst [settled, setSettled] = useState(0);\n\tconst busySeq = useRef(0);\n\tuseLayoutEffect(() => {\n\t\tif (fetcher.state !== 'idle') busySeq.current = seq.current;\n\t}, [fetcher.state]);\n\tconst ask = (target: string) => {\n\t\tconst mine = ++seq.current;\n\t\tsetLoaded(target);\n\t\tvoid Promise.resolve(load.current(target)).then(() => {\n\t\t\tif (mine === seq.current) startTransition(() => setSettled(mine));\n\t\t});\n\t};\n\tconst askRef = useRef(ask);\n\taskRef.current = ask;\n\n\tuseEffect(() => {\n\t\tif (url === null) {\n\t\t\tprevious.current = undefined;\n\t\t\tsetLoaded(null);\n\t\t\treturn;\n\t\t}\n\t\tconst timer = setTimeout(() => {\n\t\t\taskRef.current(url);\n\t\t}, delayMs);\n\t\treturn () => clearTimeout(timer);\n\t}, [url, text, delayMs]);\n\n\tconst landed =\n\t\tfetcher.state === 'idle' &&\n\t\t(busySeq.current === seq.current || settled === seq.current);\n\tconst current =\n\t\turl !== null && loaded === url && landed && fetcher.data !== undefined;\n\tconst answer = current ? fetcher.data : undefined;\n\tuseEffect(() => {\n\t\tif (answer === undefined || answer === null) return;\n\t\tif (keepRef.current?.(answer) ?? true) previous.current = answer;\n\t}, [answer]);\n\n\tconst data = current ? answer : url === null ? undefined : previous.current;\n\tlet state: DebouncedFetcherState;\n\tif (url === null) state = 'idle';\n\telse if (loaded !== url) state = 'waiting';\n\telse state = landed ? 'done' : 'loading';\n\n\tconst urlRef = useRef(url);\n\turlRef.current = url;\n\tconst retry = useCallback(() => {\n\t\tconst now = urlRef.current;\n\t\tif (now === null) return;\n\t\taskRef.current(now);\n\t}, []);\n\treturn { data, stale: state !== 'done' && data !== undefined, state, retry };\n}\n",
|
|
6
6
|
"import { useEffect, useRef, useState } from 'react';\n\n/** What the hook reads of a React Router fetcher. */\nexport interface AnswerSource<Data> {\n\treadonly state: string;\n\treadonly data: Data | undefined;\n}\n\n/**\n * The answers a fetcher gives, one at a time. A keyed fetcher outlives its\n * component, so the data it holds when the component mounts is an earlier\n * visit's, not this one's. `onAnswer` is called once for each answer that\n * arrives while the fetcher is idle and is another object than the last one\n * seen (the data present at mount counts as seen). The handler is read from\n * a ref, so a new function each render never re-fires it. Returns the latest\n * answer handed over, `undefined` until one arrives.\n */\nexport function useFetcherAnswer<Data>(\n\tfetcher: AnswerSource<Data>,\n\tonAnswer?: (answer: Data) => void,\n): Data | undefined {\n\tconst seen = useRef(fetcher.data);\n\tconst handler = useRef(onAnswer);\n\thandler.current = onAnswer;\n\tconst [latest, setLatest] = useState<Data | undefined>(undefined);\n\tconst answer = fetcher.state === 'idle' ? fetcher.data : undefined;\n\tuseEffect(() => {\n\t\tif (answer === undefined || answer === null || answer === seen.current) {\n\t\t\treturn;\n\t\t}\n\t\tseen.current = answer;\n\t\tsetLatest(answer);\n\t\thandler.current?.(answer);\n\t}, [answer]);\n\treturn latest;\n}\n",
|
|
7
|
+
"import { useCallback, useEffect, useRef } from 'react';\nimport { type AnswerSource, useFetcherAnswer } from './use-fetcher-answer';\n\n/** What `useFetcherPromise` accepts. */\nexport interface FetcherPromiseOptions<Data> {\n\t/**\n\t * Decides whether an answer is a refusal: the message (or the `Error`) the\n\t * promise rejects with, `undefined` to resolve with the answer. Read from a\n\t * ref, so a new function each render is fine. Without it every answer\n\t * resolves.\n\t */\n\trefusal?: (answer: Data) => string | Error | undefined;\n}\n\n/** The rejection of a call replaced by a newer one, or cut off by an unmount. */\nexport class FetcherPromiseAborted extends Error {\n\toverride readonly name = 'FetcherPromiseAborted';\n\treadonly reason: 'superseded' | 'unmounted';\n\tconstructor(reason: 'superseded' | 'unmounted') {\n\t\tsuper(\n\t\t\treason === 'superseded'\n\t\t\t\t? 'useFetcherPromise: a newer call replaced this one before the fetcher answered'\n\t\t\t\t: 'useFetcherPromise: the component unmounted before the fetcher answered',\n\t\t);\n\t\tthis.reason = reason;\n\t}\n}\n\ninterface Waiting<Data> {\n\tresolve(answer: Data): void;\n\treject(error: unknown): void;\n}\n\n/**\n * Ties one submission to its answer. `run(submit)` calls `submit` (usually\n * `() => fetcher.submit(...)`) and returns a Promise settled by the fetcher's\n * next new answer (see `useFetcherAnswer`): it resolves with the answer, or\n * rejects with an `Error` when `options.refusal` returns a message for it. An\n * answer the fetcher already held when `run` was called never settles it.\n *\n * A second `run` while one is pending rejects the older promise with a\n * `FetcherPromiseAborted` (`'superseded'`) and waits for the answer to the\n * newer one. An unmount rejects the pending promise with a\n * `FetcherPromiseAborted` (`'unmounted'`). A `submit` that throws (or whose\n * promise rejects) rejects the promise with that error, and so does a\n * `refusal` that throws. `run` is stable.\n *\n * Catch every promise `run` returns: an abort rejects it, and one nobody\n * handles is an unhandled rejection. Ignore the aborts with\n * `.catch((e) => { if (!(e instanceof FetcherPromiseAborted)) throw e; })`.\n */\nexport function useFetcherPromise<Data>(\n\tfetcher: AnswerSource<Data>,\n\toptions: FetcherPromiseOptions<Data> = {},\n): (submit: () => void | Promise<unknown>) => Promise<Data> {\n\tconst waiting = useRef<Waiting<Data> | null>(null);\n\tconst refusal = useRef(options.refusal);\n\trefusal.current = options.refusal;\n\n\tuseFetcherAnswer(fetcher, (answer) => {\n\t\tconst pending = waiting.current;\n\t\tif (!pending) return;\n\t\twaiting.current = null;\n\t\tlet refused: string | Error | undefined;\n\t\ttry {\n\t\t\trefused = refusal.current?.(answer);\n\t\t} catch (error) {\n\t\t\tpending.reject(error);\n\t\t\treturn;\n\t\t}\n\t\tif (refused === undefined) pending.resolve(answer);\n\t\telse\n\t\t\tpending.reject(\n\t\t\t\ttypeof refused === 'string' ? new Error(refused) : refused,\n\t\t\t);\n\t});\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tconst pending = waiting.current;\n\t\t\twaiting.current = null;\n\t\t\tpending?.reject(new FetcherPromiseAborted('unmounted'));\n\t\t},\n\t\t[],\n\t);\n\n\treturn useCallback((submit) => {\n\t\twaiting.current?.reject(new FetcherPromiseAborted('superseded'));\n\t\treturn new Promise<Data>((resolve, reject) => {\n\t\t\tconst mine: Waiting<Data> = { resolve, reject };\n\t\t\twaiting.current = mine;\n\t\t\tconst fail = (error: unknown) => {\n\t\t\t\tif (waiting.current === mine) waiting.current = null;\n\t\t\t\treject(error);\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tPromise.resolve(submit()).catch(fail);\n\t\t\t} catch (error) {\n\t\t\t\tfail(error);\n\t\t\t}\n\t\t});\n\t}, []);\n}\n",
|
|
7
8
|
"import { useMemo } from 'react';\nimport type { FormErrors } from '../errors/form-errors';\n\n/**\n * What a form's fetcher answers: a done step (`ok: true`), or a refusal\n * (`formRefusal`, or `fetcherResult` with its `error`) that may carry the\n * form's field errors.\n */\nexport type FormRefusalResult =\n\t| { ok: true }\n\t| { ok?: false; fieldErrors?: Record<string, string>; error?: string };\n\n/**\n * The refusal of a form's last answer as its `FormErrors`: `undefined` for\n * no answer, a done step, or a refusal with no `fieldErrors` (a toast's\n * message, not the form's). Stable for one answer.\n */\nexport function useFormRefusal(\n\tresult: FormRefusalResult | undefined,\n): FormErrors | undefined {\n\treturn useMemo(\n\t\t() =>\n\t\t\tresult && !result.ok && result.fieldErrors\n\t\t\t\t? {\n\t\t\t\t\t\tfieldErrors: result.fieldErrors,\n\t\t\t\t\t\tformError: result.error || undefined,\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t[result],\n\t);\n}\n"
|
|
8
9
|
],
|
|
9
|
-
"mappings": "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",
|
|
10
|
-
"debugId": "
|
|
10
|
+
"mappings": ";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA;AA2DO,SAAS,mBAAyB,CACxC,OACA,IACA,UAAyC,CAAC,GACjB;AAAA,EACzB,QAAQ,UAAU,KAAK,YAAY,GAAG,UAAU,MAAM,KAAK,SAAS;AAAA,EACpE,MAAM,UAAU,OAAO,IAAI;AAAA,EAC3B,QAAQ,UAAU;AAAA,EAClB,MAAM,UAAU,WAAiB,QAAQ,YAAY,YAAY,EAAE,IAAI,CAAC;AAAA,EACxE,MAAM,OAAO,OAAO,QAAQ,IAAI;AAAA,EAChC,KAAK,UAAU,QAAQ;AAAA,EACvB,MAAM,OAAO,MAAM,KAAK;AAAA,EACxB,MAAM,MACL,WAAW,KAAK,UAAU,YACvB,OAAO,OAAO,aACb,GAAG,IAAI,IACP,KACD;AAAA,EAEJ,OAAO,QAAQ,aAAa,SAAwB,IAAI;AAAA,EACxD,MAAM,WAAW,OAA0B,SAAS;AAAA,EAQpD,MAAM,MAAM,OAAO,CAAC;AAAA,EACpB,OAAO,SAAS,cAAc,SAAS,CAAC;AAAA,EACxC,MAAM,UAAU,OAAO,CAAC;AAAA,EACxB,gBAAgB,MAAM;AAAA,IACrB,IAAI,QAAQ,UAAU;AAAA,MAAQ,QAAQ,UAAU,IAAI;AAAA,KAClD,CAAC,QAAQ,KAAK,CAAC;AAAA,EAClB,MAAM,MAAM,CAAC,WAAmB;AAAA,IAC/B,MAAM,OAAO,EAAE,IAAI;AAAA,IACnB,UAAU,MAAM;AAAA,IACX,QAAQ,QAAQ,KAAK,QAAQ,MAAM,CAAC,EAAE,KAAK,MAAM;AAAA,MACrD,IAAI,SAAS,IAAI;AAAA,QAAS,gBAAgB,MAAM,WAAW,IAAI,CAAC;AAAA,KAChE;AAAA;AAAA,EAEF,MAAM,SAAS,OAAO,GAAG;AAAA,EACzB,OAAO,UAAU;AAAA,EAEjB,UAAU,MAAM;AAAA,IACf,IAAI,QAAQ,MAAM;AAAA,MACjB,SAAS,UAAU;AAAA,MACnB,UAAU,IAAI;AAAA,MACd;AAAA,IACD;AAAA,IACA,MAAM,QAAQ,WAAW,MAAM;AAAA,MAC9B,OAAO,QAAQ,GAAG;AAAA,OAChB,OAAO;AAAA,IACV,OAAO,MAAM,aAAa,KAAK;AAAA,KAC7B,CAAC,KAAK,MAAM,OAAO,CAAC;AAAA,EAEvB,MAAM,SACL,QAAQ,UAAU,WACjB,QAAQ,YAAY,IAAI,WAAW,YAAY,IAAI;AAAA,EACrD,MAAM,UACL,QAAQ,QAAQ,WAAW,OAAO,UAAU,QAAQ,SAAS;AAAA,EAC9D,MAAM,SAAS,UAAU,QAAQ,OAAO;AAAA,EACxC,UAAU,MAAM;AAAA,IACf,IAAI,WAAW,aAAa,WAAW;AAAA,MAAM;AAAA,IAC7C,IAAI,QAAQ,UAAU,MAAM,KAAK;AAAA,MAAM,SAAS,UAAU;AAAA,KACxD,CAAC,MAAM,CAAC;AAAA,EAEX,MAAM,OAAO,UAAU,SAAS,QAAQ,OAAO,YAAY,SAAS;AAAA,EACpE,IAAI;AAAA,EACJ,IAAI,QAAQ;AAAA,IAAM,QAAQ;AAAA,EAC1B,KAAK,IAAI,WAAW;AAAA,IAAK,QAAQ;AAAA,EACjC;AAAA,IAAK,QAAQ,SAAS,SAAS;AAAA,EAE/B,MAAM,SAAS,OAAO,GAAG;AAAA,EACzB,OAAO,UAAU;AAAA,EACjB,MAAM,QAAQ,YAAY,MAAM;AAAA,IAC/B,MAAM,MAAM,OAAO;AAAA,IACnB,IAAI,QAAQ;AAAA,MAAM;AAAA,IAClB,OAAO,QAAQ,GAAG;AAAA,KAChB,CAAC,CAAC;AAAA,EACL,OAAO,EAAE,MAAM,OAAO,UAAU,UAAU,SAAS,WAAW,OAAO,MAAM;AAAA;;ACnJ5E,sBAAS,sBAAW,qBAAQ;AAiBrB,SAAS,gBAAsB,CACrC,SACA,UACmB;AAAA,EACnB,MAAM,OAAO,QAAO,QAAQ,IAAI;AAAA,EAChC,MAAM,UAAU,QAAO,QAAQ;AAAA,EAC/B,QAAQ,UAAU;AAAA,EAClB,OAAO,QAAQ,aAAa,UAA2B,SAAS;AAAA,EAChE,MAAM,SAAS,QAAQ,UAAU,SAAS,QAAQ,OAAO;AAAA,EACzD,WAAU,MAAM;AAAA,IACf,IAAI,WAAW,aAAa,WAAW,QAAQ,WAAW,KAAK,SAAS;AAAA,MACvE;AAAA,IACD;AAAA,IACA,KAAK,UAAU;AAAA,IACf,UAAU,MAAM;AAAA,IAChB,QAAQ,UAAU,MAAM;AAAA,KACtB,CAAC,MAAM,CAAC;AAAA,EACX,OAAO;AAAA;;AClCR,wBAAS,2BAAa,sBAAW;AAe1B,MAAM,8BAA8B,MAAM;AAAA,EAC9B,OAAO;AAAA,EAChB;AAAA,EACT,WAAW,CAAC,QAAoC;AAAA,IAC/C,MACC,WAAW,eACR,kFACA,wEACJ;AAAA,IACA,KAAK,SAAS;AAAA;AAEhB;AAyBO,SAAS,iBAAuB,CACtC,SACA,UAAuC,CAAC,GACmB;AAAA,EAC3D,MAAM,UAAU,QAA6B,IAAI;AAAA,EACjD,MAAM,UAAU,QAAO,QAAQ,OAAO;AAAA,EACtC,QAAQ,UAAU,QAAQ;AAAA,EAE1B,iBAAiB,SAAS,CAAC,WAAW;AAAA,IACrC,MAAM,UAAU,QAAQ;AAAA,IACxB,IAAI,CAAC;AAAA,MAAS;AAAA,IACd,QAAQ,UAAU;AAAA,IAClB,IAAI;AAAA,IACJ,IAAI;AAAA,MACH,UAAU,QAAQ,UAAU,MAAM;AAAA,MACjC,OAAO,OAAO;AAAA,MACf,QAAQ,OAAO,KAAK;AAAA,MACpB;AAAA;AAAA,IAED,IAAI,YAAY;AAAA,MAAW,QAAQ,QAAQ,MAAM;AAAA,IACjD;AAAA,MACC,QAAQ,OACP,OAAO,YAAY,WAAW,IAAI,MAAM,OAAO,IAAI,OACpD;AAAA,GACD;AAAA,EAED,WACC,MAAM,MAAM;AAAA,IACX,MAAM,UAAU,QAAQ;AAAA,IACxB,QAAQ,UAAU;AAAA,IAClB,SAAS,OAAO,IAAI,sBAAsB,WAAW,CAAC;AAAA,KAEvD,CAAC,CACF;AAAA,EAEA,OAAO,aAAY,CAAC,WAAW;AAAA,IAC9B,QAAQ,SAAS,OAAO,IAAI,sBAAsB,YAAY,CAAC;AAAA,IAC/D,OAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAAA,MAC7C,MAAM,OAAsB,EAAE,SAAS,OAAO;AAAA,MAC9C,QAAQ,UAAU;AAAA,MAClB,MAAM,OAAO,CAAC,UAAmB;AAAA,QAChC,IAAI,QAAQ,YAAY;AAAA,UAAM,QAAQ,UAAU;AAAA,QAChD,OAAO,KAAK;AAAA;AAAA,MAEb,IAAI;AAAA,QACH,QAAQ,QAAQ,OAAO,CAAC,EAAE,MAAM,IAAI;AAAA,QACnC,OAAO,OAAO;AAAA,QACf,KAAK,KAAK;AAAA;AAAA,KAEX;AAAA,KACC,CAAC,CAAC;AAAA;;ACrGN;AAiBO,SAAS,cAAc,CAC7B,QACyB;AAAA,EACzB,OAAO,QACN,MACC,UAAU,CAAC,OAAO,MAAM,OAAO,cAC5B;AAAA,IACA,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO,SAAS;AAAA,EAC5B,IACC,WACJ,CAAC,MAAM,CACR;AAAA;",
|
|
11
|
+
"debugId": "6FFF1F15C0AB3E6F64756E2164756E21",
|
|
11
12
|
"names": []
|
|
12
13
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type AnswerSource } from './use-fetcher-answer';
|
|
2
|
+
/** What `useFetcherPromise` accepts. */
|
|
3
|
+
export interface FetcherPromiseOptions<Data> {
|
|
4
|
+
/**
|
|
5
|
+
* Decides whether an answer is a refusal: the message (or the `Error`) the
|
|
6
|
+
* promise rejects with, `undefined` to resolve with the answer. Read from a
|
|
7
|
+
* ref, so a new function each render is fine. Without it every answer
|
|
8
|
+
* resolves.
|
|
9
|
+
*/
|
|
10
|
+
refusal?: (answer: Data) => string | Error | undefined;
|
|
11
|
+
}
|
|
12
|
+
/** The rejection of a call replaced by a newer one, or cut off by an unmount. */
|
|
13
|
+
export declare class FetcherPromiseAborted extends Error {
|
|
14
|
+
readonly name = "FetcherPromiseAborted";
|
|
15
|
+
readonly reason: 'superseded' | 'unmounted';
|
|
16
|
+
constructor(reason: 'superseded' | 'unmounted');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Ties one submission to its answer. `run(submit)` calls `submit` (usually
|
|
20
|
+
* `() => fetcher.submit(...)`) and returns a Promise settled by the fetcher's
|
|
21
|
+
* next new answer (see `useFetcherAnswer`): it resolves with the answer, or
|
|
22
|
+
* rejects with an `Error` when `options.refusal` returns a message for it. An
|
|
23
|
+
* answer the fetcher already held when `run` was called never settles it.
|
|
24
|
+
*
|
|
25
|
+
* A second `run` while one is pending rejects the older promise with a
|
|
26
|
+
* `FetcherPromiseAborted` (`'superseded'`) and waits for the answer to the
|
|
27
|
+
* newer one. An unmount rejects the pending promise with a
|
|
28
|
+
* `FetcherPromiseAborted` (`'unmounted'`). A `submit` that throws (or whose
|
|
29
|
+
* promise rejects) rejects the promise with that error, and so does a
|
|
30
|
+
* `refusal` that throws. `run` is stable.
|
|
31
|
+
*
|
|
32
|
+
* Catch every promise `run` returns: an abort rejects it, and one nobody
|
|
33
|
+
* handles is an unhandled rejection. Ignore the aborts with
|
|
34
|
+
* `.catch((e) => { if (!(e instanceof FetcherPromiseAborted)) throw e; })`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useFetcherPromise<Data>(fetcher: AnswerSource<Data>, options?: FetcherPromiseOptions<Data>): (submit: () => void | Promise<unknown>) => Promise<Data>;
|
|
37
|
+
//# sourceMappingURL=use-fetcher-promise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-fetcher-promise.d.ts","sourceRoot":"","sources":["../../src/fetcher/use-fetcher-promise.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,sBAAsB,CAAC;AAE3E,wCAAwC;AACxC,MAAM,WAAW,qBAAqB,CAAC,IAAI;IAC1C;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,KAAK,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;CACvD;AAED,iFAAiF;AACjF,qBAAa,qBAAsB,SAAQ,KAAK;IAC/C,SAAkB,IAAI,2BAA2B;IACjD,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAAC;gBAChC,MAAM,EAAE,YAAY,GAAG,WAAW;CAQ9C;AAOD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EACrC,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAC3B,OAAO,GAAE,qBAAqB,CAAC,IAAI,CAAM,GACvC,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAgD1D"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SameSite } from './serialize';
|
|
1
2
|
/** What every `sessionCookies` call takes, with or without a second factor. */
|
|
2
3
|
export interface SessionCookiesBaseOptions {
|
|
3
4
|
/**
|
|
@@ -11,6 +12,12 @@ export interface SessionCookiesBaseOptions {
|
|
|
11
12
|
* prefixes (Safari drops a `Secure` cookie over `http://localhost`).
|
|
12
13
|
*/
|
|
13
14
|
readonly secure: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The `SameSite` attribute of every cookie written. `'lax'` by default;
|
|
17
|
+
* `'strict'` for an app no other site links into signed in; `'none'`
|
|
18
|
+
* needs `secure`.
|
|
19
|
+
*/
|
|
20
|
+
readonly sameSite?: SameSite | undefined;
|
|
14
21
|
}
|
|
15
22
|
/** The options of an app with no second factor: no challenge cookie. */
|
|
16
23
|
export interface SessionCookiesWithoutChallengeOptions extends SessionCookiesBaseOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/session/cookies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/session/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,KAAK,QAAQ,EAGb,MAAM,aAAa,CAAC;AAErB,+EAA+E;AAC/E,MAAM,WAAW,yBAAyB;IACzC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACzC;AAED,wEAAwE;AACxE,MAAM,WAAW,qCAChB,SAAQ,yBAAyB;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;CACtC;AAED,wFAAwF;AACxF,MAAM,WAAW,qBAAsB,SAAQ,yBAAyB;IACvE;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,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,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IAClC,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,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;;;;;OAKG;IACH,iBAAiB,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC;CACjD;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,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;CAC3B;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,aAAa,SAAS,OAAO,GAAG,IAAI,IAC9D,aAAa,SAAS,IAAI,GACvB,kBAAkB,GAAG,gBAAgB,GACrC,kBAAkB,GAAG,kBAAkB,CAAC;AAE5C;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,CAAC;IACrC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC;CACtC;AAiBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC7B,OAAO,EAAE,qBAAqB,GAC5B,cAAc,CAAC,IAAI,CAAC,CAAC;AACxB,wBAAgB,cAAc,CAC7B,OAAO,EAAE,qCAAqC,GAC5C,cAAc,CAAC,KAAK,CAAC,CAAC;AACzB,wBAAgB,cAAc,CAC7B,OAAO,EAAE,yBAAyB,GAAG;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C,GACC,cAAc,CAAC,OAAO,CAAC,CAAC;AA8G3B,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE,CAE3E"}
|
package/dist/session/index.d.ts
CHANGED
|
@@ -10,8 +10,9 @@ export type { SecondFactorSteps, SignInAnswer, SignInCodes, SignInFlow, SignInFl
|
|
|
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';
|
|
13
|
-
export { readCookie, type SerializeOptions, secondsUntil, serializeCookie, } from './serialize';
|
|
13
|
+
export { readCookie, type SameSite, type SerializeOptions, secondsUntil, serializeCookie, } from './serialize';
|
|
14
14
|
export { signInFlow } from './sign-in';
|
|
15
|
+
export { type SignedSession, type SignedSessionOptions, signedSession, } from './signed';
|
|
15
16
|
export { stepUpSteps } from './step-up';
|
|
16
17
|
export { STEP_UP_OVER, STEP_UP_REQUIRED } from './step-up-codes';
|
|
17
18
|
export type { StepUpCall, StepUpCodes, StepUpRun, StepUpSignIn, StepUpStayed, StepUpSteps, StepUpStepsOptions, } from './step-up-types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACN,cAAc,EACd,YAAY,EACZ,cAAc,EACd,eAAe,GACf,MAAM,SAAS,CAAC;AACjB,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,SAAS,EACT,YAAY,EACZ,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,EACN,cAAc,EACd,YAAY,EACZ,cAAc,EACd,eAAe,GACf,MAAM,SAAS,CAAC;AACjB,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,QAAQ,EACb,KAAK,gBAAgB,EACrB,YAAY,EACZ,eAAe,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EACX,UAAU,EACV,WAAW,EACX,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,kBAAkB,GAClB,MAAM,iBAAiB,CAAC"}
|
package/dist/session/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
nextOf2,
|
|
7
7
|
hasSiteCheck,
|
|
8
8
|
refuseCrossSite
|
|
9
|
-
} from "../chunks/index-
|
|
9
|
+
} from "../chunks/index-2swe47m6.js";
|
|
10
10
|
import {
|
|
11
11
|
HOME2,
|
|
12
12
|
safeNext2,
|
|
@@ -26,24 +26,34 @@ var CHALLENGE_OVER = [
|
|
|
26
26
|
var MAIL_THROTTLED = "MAIL_THROTTLED";
|
|
27
27
|
var UNAUTHENTICATED = "UNAUTHENTICATED";
|
|
28
28
|
// src/session/serialize.ts
|
|
29
|
+
var SAME_SITE = { lax: "Lax", strict: "Strict", none: "None" };
|
|
30
|
+
function checkSameSite(caller, sameSite, secure) {
|
|
31
|
+
if (!Object.hasOwn(SAME_SITE, sameSite)) {
|
|
32
|
+
throw new TypeError(`${caller}: sameSite must be "lax", "strict" or "none"; got ${JSON.stringify(sameSite)}.`);
|
|
33
|
+
}
|
|
34
|
+
if (sameSite === "none" && !secure) {
|
|
35
|
+
throw new TypeError(`${caller}: sameSite "none" needs secure: true, since a browser drops a SameSite=None cookie without Secure.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
29
38
|
var TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
30
39
|
var PATH = /^\/[^;\x00-\x1f\x7f]*$/;
|
|
31
40
|
var isCookieName = (name) => TOKEN.test(name);
|
|
32
41
|
var isCookiePath = (path) => PATH.test(path);
|
|
33
|
-
function serializeCookie(name, value, { maxAge, path = "/", secure }) {
|
|
42
|
+
function serializeCookie(name, value, { maxAge, path = "/", secure, sameSite = "lax" }) {
|
|
34
43
|
if (!isCookieName(name)) {
|
|
35
44
|
throw new TypeError(`serializeCookie(): the name ${JSON.stringify(name)} is not a cookie name (an RFC 6265 token).`);
|
|
36
45
|
}
|
|
37
46
|
if (!isCookiePath(path)) {
|
|
38
47
|
throw new TypeError(`serializeCookie(): the path ${JSON.stringify(path)} is not a cookie path: it must start with "/" and hold no ";" or control character.`);
|
|
39
48
|
}
|
|
49
|
+
checkSameSite("serializeCookie():", sameSite, secure);
|
|
40
50
|
return [
|
|
41
51
|
`${name}=${encodeURIComponent(value)}`,
|
|
42
52
|
`Max-Age=${Number.isFinite(maxAge) ? Math.max(0, Math.floor(maxAge)) : 0}`,
|
|
43
53
|
`Path=${path}`,
|
|
44
54
|
"HttpOnly",
|
|
45
55
|
...secure ? ["Secure"] : [],
|
|
46
|
-
|
|
56
|
+
`SameSite=${SAME_SITE[sameSite]}`
|
|
47
57
|
].join("; ");
|
|
48
58
|
}
|
|
49
59
|
function readCookie(request, name) {
|
|
@@ -84,9 +94,11 @@ function checkName(label, name) {
|
|
|
84
94
|
function sessionCookies(options) {
|
|
85
95
|
const {
|
|
86
96
|
secure,
|
|
97
|
+
sameSite = "lax",
|
|
87
98
|
challengePath = "/sign-in",
|
|
88
99
|
challengeSeconds = 5 * 60
|
|
89
100
|
} = options;
|
|
101
|
+
checkSameSite("sessionCookies():", sameSite, secure);
|
|
90
102
|
checkName("session", options.session);
|
|
91
103
|
if (options.challenge !== undefined)
|
|
92
104
|
checkName("challenge", options.challenge);
|
|
@@ -98,7 +110,12 @@ function sessionCookies(options) {
|
|
|
98
110
|
}
|
|
99
111
|
const session = secure ? `__Host-${options.session}` : options.session;
|
|
100
112
|
const challengeName = options.challenge === undefined ? undefined : secure ? `__Secure-${options.challenge}` : options.challenge;
|
|
101
|
-
const clear = (name, path) => serializeCookie(name, "", {
|
|
113
|
+
const clear = (name, path) => serializeCookie(name, "", {
|
|
114
|
+
maxAge: 0,
|
|
115
|
+
secure,
|
|
116
|
+
sameSite,
|
|
117
|
+
...path && { path }
|
|
118
|
+
});
|
|
102
119
|
const read = (request) => {
|
|
103
120
|
const value = readCookie(request, session);
|
|
104
121
|
const dot = value?.indexOf(".") ?? -1;
|
|
@@ -111,7 +128,8 @@ function sessionCookies(options) {
|
|
|
111
128
|
};
|
|
112
129
|
const commit = (token, expiresAt, now) => serializeCookie(session, `${millisOf(expiresAt)}.${token}`, {
|
|
113
130
|
maxAge: secondsUntil(expiresAt, now),
|
|
114
|
-
secure
|
|
131
|
+
secure,
|
|
132
|
+
sameSite
|
|
115
133
|
});
|
|
116
134
|
const base = {
|
|
117
135
|
names: { session },
|
|
@@ -143,7 +161,8 @@ function sessionCookies(options) {
|
|
|
143
161
|
commitChallenge: (value, expiresAt, now) => serializeCookie(challengeName, value, {
|
|
144
162
|
maxAge: Math.min(challengeSeconds, expiresAt ? secondsUntil(expiresAt, now) : challengeSeconds),
|
|
145
163
|
path: challengePath,
|
|
146
|
-
secure
|
|
164
|
+
secure,
|
|
165
|
+
sameSite
|
|
147
166
|
}),
|
|
148
167
|
destroyChallenge: () => clear(challengeName, challengePath)
|
|
149
168
|
};
|
|
@@ -555,6 +574,68 @@ function signInFlow(options) {
|
|
|
555
574
|
codeStep
|
|
556
575
|
};
|
|
557
576
|
}
|
|
577
|
+
// src/session/signed.ts
|
|
578
|
+
var MIN_SECRET = 32;
|
|
579
|
+
var MAC_LENGTH = 43;
|
|
580
|
+
var DIGITS = /^\d{1,16}$/;
|
|
581
|
+
var encoder = new TextEncoder;
|
|
582
|
+
var base64url = (bytes) => btoa(String.fromCharCode(...bytes)).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
583
|
+
var keyOf = (secret) => crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
584
|
+
async function sign(key, payload) {
|
|
585
|
+
const mac = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
|
|
586
|
+
return base64url(new Uint8Array(mac));
|
|
587
|
+
}
|
|
588
|
+
function sameBytes(a, b) {
|
|
589
|
+
if (a.length !== b.length)
|
|
590
|
+
return false;
|
|
591
|
+
let difference = 0;
|
|
592
|
+
for (let index = 0;index < a.length; index++) {
|
|
593
|
+
difference |= a.charCodeAt(index) ^ b.charCodeAt(index);
|
|
594
|
+
}
|
|
595
|
+
return difference === 0;
|
|
596
|
+
}
|
|
597
|
+
function signedSession(options) {
|
|
598
|
+
const { secret, name: base, maxAge, secure, sameSite = "lax" } = options;
|
|
599
|
+
if (typeof secret !== "string" || secret.length < MIN_SECRET) {
|
|
600
|
+
throw new TypeError(`signedSession(): secret must be a string of at least ${MIN_SECRET} characters.`);
|
|
601
|
+
}
|
|
602
|
+
if (!isCookieName(base) || /^__(?:Host|Secure)-/.test(base)) {
|
|
603
|
+
throw new TypeError(`signedSession(): the name ${JSON.stringify(base)} must be an RFC 6265 token without a __Host- or __Secure- prefix; the package adds __Host- when secure is true.`);
|
|
604
|
+
}
|
|
605
|
+
if (!Number.isInteger(maxAge) || maxAge <= 0) {
|
|
606
|
+
throw new TypeError(`signedSession(): maxAge must be a positive integer of seconds; got ${String(maxAge)}.`);
|
|
607
|
+
}
|
|
608
|
+
checkSameSite("signedSession():", sameSite, secure);
|
|
609
|
+
const name = secure ? `__Host-${base}` : base;
|
|
610
|
+
const key = keyOf(secret);
|
|
611
|
+
return {
|
|
612
|
+
name,
|
|
613
|
+
async commit(now = Date.now()) {
|
|
614
|
+
const nonce = base64url(crypto.getRandomValues(new Uint8Array(16)));
|
|
615
|
+
const payload = `${now + maxAge * 1000}.${nonce}`;
|
|
616
|
+
return serializeCookie(name, `${payload}.${await sign(await key, payload)}`, {
|
|
617
|
+
maxAge,
|
|
618
|
+
secure,
|
|
619
|
+
sameSite
|
|
620
|
+
});
|
|
621
|
+
},
|
|
622
|
+
destroy: () => serializeCookie(name, "", { maxAge: 0, secure, sameSite }),
|
|
623
|
+
async valid(request, now = Date.now()) {
|
|
624
|
+
const value = readCookie(request, name);
|
|
625
|
+
const parts = value?.split(".") ?? [];
|
|
626
|
+
if (parts.length !== 3)
|
|
627
|
+
return false;
|
|
628
|
+
const [digits, nonce, mac] = parts;
|
|
629
|
+
if (mac.length !== MAC_LENGTH || !nonce || !DIGITS.test(digits)) {
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
const expected = await sign(await key, `${digits}.${nonce}`);
|
|
633
|
+
if (!sameBytes(mac, expected))
|
|
634
|
+
return false;
|
|
635
|
+
return Number(digits) > now;
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
}
|
|
558
639
|
// src/session/step-up.ts
|
|
559
640
|
import { data as data6 } from "react-router";
|
|
560
641
|
|
|
@@ -783,8 +864,9 @@ export {
|
|
|
783
864
|
serializeCookie,
|
|
784
865
|
sessionCookies,
|
|
785
866
|
signInFlow,
|
|
867
|
+
signedSession,
|
|
786
868
|
stepUpSteps
|
|
787
869
|
};
|
|
788
870
|
|
|
789
|
-
//# debugId=
|
|
871
|
+
//# debugId=384D2137E780CE9564756E2164756E21
|
|
790
872
|
//# sourceMappingURL=index.js.map
|