react-router 8.4.0 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -1
- package/dist/development/dom-export.d.ts +2 -1
- package/dist/development/dom-export.js +1 -1
- package/dist/development/index-react-server-client.js +2 -2
- package/dist/development/index-react-server.d.ts +583 -165
- package/dist/development/index-react-server.js +27 -19
- package/dist/development/index.d.ts +9 -8
- package/dist/development/index.js +5 -4
- package/dist/development/lib/actions.js +1 -1
- package/dist/development/lib/components.js +22 -3
- package/dist/development/lib/context.d.ts +6 -1
- package/dist/development/lib/context.js +1 -1
- package/dist/development/lib/dom/dom.d.ts +3 -0
- package/dist/development/lib/dom/dom.js +1 -1
- package/dist/development/lib/dom/global.d.ts +35 -0
- package/dist/development/lib/dom/lib.d.ts +34 -9
- package/dist/development/lib/dom/lib.js +17 -6
- package/dist/development/lib/dom/server.js +2 -2
- package/dist/development/lib/dom/ssr/components.js +12 -9
- package/dist/development/lib/dom/ssr/data.js +1 -1
- package/dist/development/lib/dom/ssr/entry.d.ts +9 -2
- package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/development/lib/dom/ssr/fallback.js +1 -1
- package/dist/development/lib/dom/ssr/fog-of-war.d.ts +3 -2
- package/dist/development/lib/dom/ssr/fog-of-war.js +14 -11
- package/dist/development/lib/dom/ssr/hydration.js +1 -1
- package/dist/development/lib/dom/ssr/invariant.js +1 -1
- package/dist/development/lib/dom/ssr/links.js +1 -1
- package/dist/development/lib/dom/ssr/markup.js +1 -1
- package/dist/development/lib/dom/ssr/route-discovery.d.ts +120 -0
- package/dist/development/lib/dom/ssr/route-discovery.js +415 -0
- package/dist/development/lib/dom/ssr/routeModules.js +1 -1
- package/dist/development/lib/dom/ssr/routes-test-stub.js +2 -2
- package/dist/development/lib/dom/ssr/routes.d.ts +2 -2
- package/dist/development/lib/dom/ssr/routes.js +8 -8
- package/dist/development/lib/dom/ssr/server.js +3 -1
- package/dist/development/lib/dom/ssr/single-fetch.d.ts +28 -2
- package/dist/development/lib/dom/ssr/single-fetch.js +73 -55
- package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/development/lib/dom-export/hydrated-router.d.ts +28 -0
- package/dist/development/lib/dom-export/hydrated-router.js +32 -12
- package/dist/development/lib/errors.js +1 -1
- package/dist/development/lib/hooks.d.ts +3 -3
- package/dist/development/lib/hooks.js +4 -4
- package/dist/development/lib/href.js +1 -1
- package/dist/development/lib/router/history.js +1 -1
- package/dist/development/lib/router/instrumentation.js +1 -1
- package/dist/development/lib/router/matcher-route-pattern.js +1 -1
- package/dist/development/lib/router/matcher-route-pattern.preload.js +1 -1
- package/dist/development/lib/router/matcher.js +1 -1
- package/dist/development/lib/router/navigation.js +1 -1
- package/dist/development/lib/router/router.d.ts +6 -0
- package/dist/development/lib/router/router.js +157 -65
- package/dist/development/lib/router/url.js +1 -1
- package/dist/development/lib/router/utils.d.ts +24 -4
- package/dist/development/lib/router/utils.js +4 -3
- package/dist/development/lib/rsc/browser.js +2 -2
- package/dist/development/lib/rsc/errorBoundaries.js +1 -1
- package/dist/development/lib/rsc/html-stream/browser.js +1 -1
- package/dist/development/lib/rsc/html-stream/server.js +24 -12
- package/dist/development/lib/rsc/route-modules.js +1 -1
- package/dist/development/lib/rsc/server.rsc.d.ts +3 -2
- package/dist/development/lib/rsc/server.ssr.js +1 -1
- package/dist/development/lib/server-runtime/build.d.ts +14 -0
- package/dist/development/lib/server-runtime/cookies.js +1 -1
- package/dist/development/lib/server-runtime/crypto.js +1 -1
- package/dist/development/lib/server-runtime/data.js +1 -1
- package/dist/development/lib/server-runtime/dev.js +1 -1
- package/dist/development/lib/server-runtime/entry.js +1 -1
- package/dist/development/lib/server-runtime/errors.js +1 -1
- package/dist/development/lib/server-runtime/headers.js +1 -1
- package/dist/development/lib/server-runtime/invariant.js +1 -1
- package/dist/development/lib/server-runtime/mode.js +1 -1
- package/dist/development/lib/server-runtime/routeMatching.js +1 -1
- package/dist/development/lib/server-runtime/routes.d.ts +1 -1
- package/dist/development/lib/server-runtime/routes.js +1 -1
- package/dist/development/lib/server-runtime/server.js +12 -4
- package/dist/development/lib/server-runtime/serverHandoff.d.ts +17 -0
- package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions.js +1 -1
- package/dist/development/lib/server-runtime/single-fetch.js +1 -1
- package/dist/development/lib/server-runtime/urls.js +1 -1
- package/dist/development/lib/server-runtime/warnings.js +1 -1
- package/dist/development/lib/types/internal.js +1 -1
- package/dist/development/lib/types/route-data.d.ts +2 -2
- package/dist/development/lib/types/route-module-annotations.d.ts +1 -1
- package/dist/development/route-pattern.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
- package/dist/production/dom-export.d.ts +2 -1
- package/dist/production/dom-export.js +1 -1
- package/dist/production/index-react-server-client.js +2 -2
- package/dist/production/index-react-server.d.ts +583 -165
- package/dist/production/index-react-server.js +27 -19
- package/dist/production/index.d.ts +9 -8
- package/dist/production/index.js +5 -4
- package/dist/production/lib/actions.js +1 -1
- package/dist/production/lib/components.js +22 -3
- package/dist/production/lib/context.d.ts +6 -1
- package/dist/production/lib/context.js +1 -1
- package/dist/production/lib/dom/dom.d.ts +3 -0
- package/dist/production/lib/dom/dom.js +1 -1
- package/dist/production/lib/dom/global.d.ts +35 -0
- package/dist/production/lib/dom/lib.d.ts +34 -9
- package/dist/production/lib/dom/lib.js +17 -6
- package/dist/production/lib/dom/server.js +2 -2
- package/dist/production/lib/dom/ssr/components.js +12 -9
- package/dist/production/lib/dom/ssr/data.js +1 -1
- package/dist/production/lib/dom/ssr/entry.d.ts +9 -2
- package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/production/lib/dom/ssr/fallback.js +1 -1
- package/dist/production/lib/dom/ssr/fog-of-war.d.ts +3 -2
- package/dist/production/lib/dom/ssr/fog-of-war.js +14 -11
- package/dist/production/lib/dom/ssr/hydration.js +1 -1
- package/dist/production/lib/dom/ssr/invariant.js +1 -1
- package/dist/production/lib/dom/ssr/links.js +1 -1
- package/dist/production/lib/dom/ssr/markup.js +1 -1
- package/dist/production/lib/dom/ssr/route-discovery.d.ts +120 -0
- package/dist/production/lib/dom/ssr/route-discovery.js +415 -0
- package/dist/production/lib/dom/ssr/routeModules.js +1 -1
- package/dist/production/lib/dom/ssr/routes-test-stub.js +2 -2
- package/dist/production/lib/dom/ssr/routes.d.ts +2 -2
- package/dist/production/lib/dom/ssr/routes.js +8 -8
- package/dist/production/lib/dom/ssr/server.js +3 -1
- package/dist/production/lib/dom/ssr/single-fetch.d.ts +28 -2
- package/dist/production/lib/dom/ssr/single-fetch.js +73 -55
- package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/production/lib/dom-export/hydrated-router.d.ts +28 -0
- package/dist/production/lib/dom-export/hydrated-router.js +32 -12
- package/dist/production/lib/errors.js +1 -1
- package/dist/production/lib/hooks.d.ts +3 -3
- package/dist/production/lib/hooks.js +3 -3
- package/dist/production/lib/href.js +1 -1
- package/dist/production/lib/router/history.js +1 -1
- package/dist/production/lib/router/instrumentation.js +1 -1
- package/dist/production/lib/router/matcher-route-pattern.js +1 -1
- package/dist/production/lib/router/matcher-route-pattern.preload.js +1 -1
- package/dist/production/lib/router/matcher.js +1 -1
- package/dist/production/lib/router/navigation.js +1 -1
- package/dist/production/lib/router/router.d.ts +6 -0
- package/dist/production/lib/router/router.js +157 -65
- package/dist/production/lib/router/url.js +1 -1
- package/dist/production/lib/router/utils.d.ts +24 -4
- package/dist/production/lib/router/utils.js +4 -3
- package/dist/production/lib/rsc/browser.js +2 -2
- package/dist/production/lib/rsc/errorBoundaries.js +1 -1
- package/dist/production/lib/rsc/html-stream/browser.js +1 -1
- package/dist/production/lib/rsc/html-stream/server.js +24 -12
- package/dist/production/lib/rsc/route-modules.js +1 -1
- package/dist/production/lib/rsc/server.rsc.d.ts +3 -2
- package/dist/production/lib/rsc/server.ssr.js +1 -1
- package/dist/production/lib/server-runtime/build.d.ts +14 -0
- package/dist/production/lib/server-runtime/cookies.js +1 -1
- package/dist/production/lib/server-runtime/crypto.js +1 -1
- package/dist/production/lib/server-runtime/data.js +1 -1
- package/dist/production/lib/server-runtime/dev.js +1 -1
- package/dist/production/lib/server-runtime/entry.js +1 -1
- package/dist/production/lib/server-runtime/errors.js +1 -1
- package/dist/production/lib/server-runtime/headers.js +1 -1
- package/dist/production/lib/server-runtime/invariant.js +1 -1
- package/dist/production/lib/server-runtime/mode.js +1 -1
- package/dist/production/lib/server-runtime/routeMatching.js +1 -1
- package/dist/production/lib/server-runtime/routes.d.ts +1 -1
- package/dist/production/lib/server-runtime/routes.js +1 -1
- package/dist/production/lib/server-runtime/server.js +12 -4
- package/dist/production/lib/server-runtime/serverHandoff.d.ts +17 -0
- package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions.js +1 -1
- package/dist/production/lib/server-runtime/single-fetch.js +1 -1
- package/dist/production/lib/server-runtime/urls.js +1 -1
- package/dist/production/lib/server-runtime/warnings.js +1 -1
- package/dist/production/lib/types/internal.js +1 -1
- package/dist/production/lib/types/route-data.d.ts +2 -2
- package/dist/production/lib/types/route-module-annotations.d.ts +1 -1
- package/dist/production/route-pattern.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
- package/docs/explanation/lazy-route-discovery.md +4 -0
- package/docs/explanation/sessions-and-cookies.md +108 -4
- package/docs/how-to/custom-fetch.md +218 -0
- package/docs/how-to/data-strategy.md +23 -1
- package/docs/how-to/middleware.md +1 -0
- package/docs/how-to/optimize-revalidation.md +2 -2
- package/docs/how-to/pre-rendering.md +23 -1
- package/docs/how-to/route-discovery.md +145 -0
- package/docs/how-to/spa.md +2 -0
- package/docs/how-to/view-transitions.md +80 -13
- package/docs/start/framework/navigating.md +5 -9
- package/docs/start/framework/rendering.md +37 -0
- package/package.json +9 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.5.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -14,12 +14,12 @@ import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils.j
|
|
|
14
14
|
import { validateNavigationTarget } from "../router/navigation.js";
|
|
15
15
|
import { createRouter, hasInvalidProtocol, isMutationMethod } from "../router/router.js";
|
|
16
16
|
import { RSCRouterContext } from "../context.js";
|
|
17
|
-
import { RouterProvider } from "../components.js";
|
|
18
17
|
import { createRequestInit } from "../dom/ssr/data.js";
|
|
19
18
|
import { getSingleFetchDataStrategyImpl, singleFetchUrl, stripIndexParam } from "../dom/ssr/single-fetch.js";
|
|
20
19
|
import { noActionDefinedError, shouldHydrateRouteLoader } from "../dom/ssr/routes.js";
|
|
21
20
|
import { getPathsWithAncestors, handleClientVersionMismatch } from "../dom/ssr/fog-of-war.js";
|
|
22
21
|
import { FrameworkContext, setIsHydrated } from "../dom/ssr/components.js";
|
|
22
|
+
import { RouterProvider } from "../components.js";
|
|
23
23
|
import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries.js";
|
|
24
24
|
import { populateRSCRouteModules } from "./route-modules.js";
|
|
25
25
|
import { getHydrationData } from "../dom/ssr/hydration.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.5.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -21,11 +21,31 @@ function injectRSCPayload(rscStream, { nonce } = {}) {
|
|
|
21
21
|
let rscReader = null;
|
|
22
22
|
let buffered = [];
|
|
23
23
|
let timeout = null;
|
|
24
|
+
async function stop(reason) {
|
|
25
|
+
cancelled = true;
|
|
26
|
+
if (timeout) {
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
timeout = null;
|
|
29
|
+
}
|
|
30
|
+
buffered.length = 0;
|
|
31
|
+
resolveFlightDataPromise();
|
|
32
|
+
if (rscReader && rscStream.locked) await rscReader.cancel(reason).catch(() => {});
|
|
33
|
+
else await rscStream.cancel(reason).catch(() => {});
|
|
34
|
+
}
|
|
35
|
+
function tryEnqueue(controller, chunk) {
|
|
36
|
+
try {
|
|
37
|
+
controller.enqueue(chunk);
|
|
38
|
+
return true;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
stop(error);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
24
44
|
function flushBufferedChunks(controller) {
|
|
25
45
|
for (let chunk of buffered) {
|
|
26
46
|
let buf = decoder.decode(chunk, { stream: true });
|
|
27
47
|
if (buf.endsWith(trailer)) buf = buf.slice(0, -14);
|
|
28
|
-
controller
|
|
48
|
+
if (!tryEnqueue(controller, encoder.encode(buf))) return;
|
|
29
49
|
}
|
|
30
50
|
buffered.length = 0;
|
|
31
51
|
timeout = null;
|
|
@@ -50,18 +70,10 @@ function injectRSCPayload(rscStream, { nonce } = {}) {
|
|
|
50
70
|
clearTimeout(timeout);
|
|
51
71
|
flushBufferedChunks(controller);
|
|
52
72
|
}
|
|
53
|
-
controller
|
|
73
|
+
tryEnqueue(controller, encoder.encode("</body></html>"));
|
|
54
74
|
},
|
|
55
75
|
async cancel(reason) {
|
|
56
|
-
|
|
57
|
-
if (timeout) {
|
|
58
|
-
clearTimeout(timeout);
|
|
59
|
-
timeout = null;
|
|
60
|
-
}
|
|
61
|
-
buffered.length = 0;
|
|
62
|
-
if (rscReader) await rscReader.cancel(reason).catch(() => {});
|
|
63
|
-
else await rscStream.cancel(reason).catch(() => {});
|
|
64
|
-
resolveFlightDataPromise();
|
|
76
|
+
await stop(reason);
|
|
65
77
|
}
|
|
66
78
|
});
|
|
67
79
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { Location } from "../router/history.js";
|
|
3
|
-
import { ActionFunction, LoaderFunction, Params, RouterContextProvider, ShouldRevalidateFunction } from "../router/utils.js";
|
|
3
|
+
import { ActionFunction, LoaderFunction, MiddlewareFunction, Params, RouterContextProvider, ShouldRevalidateFunction } from "../router/utils.js";
|
|
4
4
|
import { ClientActionFunction, ClientLoaderFunction, HeadersFunction, LinksFunction, MetaFunction } from "../dom/ssr/routeModules.js";
|
|
5
5
|
import * as React$1 from "react";
|
|
6
6
|
import { ReactFormState } from "react-dom/client";
|
|
@@ -19,6 +19,7 @@ type RSCRouteConfigEntryBase = {
|
|
|
19
19
|
links?: LinksFunction;
|
|
20
20
|
loader?: LoaderFunction;
|
|
21
21
|
meta?: MetaFunction;
|
|
22
|
+
middleware?: MiddlewareFunction<any>[];
|
|
22
23
|
shouldRevalidate?: ShouldRevalidateFunction;
|
|
23
24
|
};
|
|
24
25
|
type RSCRouteConfigEntry = RSCRouteConfigEntryBase & {
|
|
@@ -204,7 +205,7 @@ declare function matchRSCServerRequest({
|
|
|
204
205
|
onError,
|
|
205
206
|
request,
|
|
206
207
|
routes,
|
|
207
|
-
generateResponse
|
|
208
|
+
generateResponse: encodeResponse
|
|
208
209
|
}: {
|
|
209
210
|
allowedActionOrigins?: string[];
|
|
210
211
|
createTemporaryReferenceSet: () => unknown;
|
|
@@ -19,7 +19,21 @@ interface ServerBuild {
|
|
|
19
19
|
publicPath: string;
|
|
20
20
|
assetsBuildDirectory: string;
|
|
21
21
|
future: FutureConfig;
|
|
22
|
+
/**
|
|
23
|
+
* Whether runtime server requests are supported, including API-only builds.
|
|
24
|
+
*/
|
|
22
25
|
ssr: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The origin to use for server requests in API-only mode.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
unstable_apiServerOrigin?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Whether production document rendering is disabled while data and route
|
|
33
|
+
* discovery requests remain supported.
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
unstable_apiOnly?: boolean;
|
|
23
37
|
unstable_getCriticalCss?: (args: {
|
|
24
38
|
pathname: string;
|
|
25
39
|
}) => OptionalCriticalCss | Promise<OptionalCriticalCss>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { RouteManifest } from "../router/utils.js";
|
|
3
|
-
import { ServerRouteModule } from "../dom/ssr/routeModules.js";
|
|
4
3
|
import { Route } from "../dom/ssr/routes.js";
|
|
4
|
+
import { ServerRouteModule } from "../dom/ssr/routeModules.js";
|
|
5
5
|
|
|
6
6
|
//#region lib/server-runtime/routes.d.ts
|
|
7
7
|
type ServerRouteManifest = RouteManifest<Omit<ServerRoute, "children">>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.5.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -58,7 +58,7 @@ function derive(build, mode) {
|
|
|
58
58
|
let normalizedPath = getNormalizedPath(request);
|
|
59
59
|
let normalizedPathname = normalizedPath.pathname;
|
|
60
60
|
let isSpaMode = getBuildTimeHeader(request, "X-React-Router-SPA-Mode") === "yes";
|
|
61
|
-
if (!build.ssr) {
|
|
61
|
+
if (!build.ssr || build.unstable_apiOnly) {
|
|
62
62
|
let decodedPath = decodeURI(normalizedPathname);
|
|
63
63
|
if (build.basename && build.basename !== "/") {
|
|
64
64
|
let strippedPath = stripBasename(decodedPath, build.basename);
|
|
@@ -76,7 +76,7 @@ function derive(build, mode) {
|
|
|
76
76
|
decodedPath = strippedPath;
|
|
77
77
|
}
|
|
78
78
|
if (build.prerender.length === 0) isSpaMode = true;
|
|
79
|
-
else if (!build.prerender.some((p) => removeTrailingSlash(p) === removeTrailingSlash(decodedPath))) if (requestUrl.pathname.endsWith(".data")) {
|
|
79
|
+
else if (!build.prerender.some((p) => removeTrailingSlash(p) === removeTrailingSlash(decodedPath))) if (requestUrl.pathname.endsWith(".data") && !build.unstable_apiOnly) {
|
|
80
80
|
errorHandler(new ErrorResponseImpl(404, "Not Found", `Refusing to SSR the path \`${decodedPath}\` because \`ssr:false\` is set and the path is not included in the \`prerender\` config, so in production the path will be a 404.`), {
|
|
81
81
|
context: loadContext,
|
|
82
82
|
params,
|
|
@@ -95,6 +95,10 @@ function derive(build, mode) {
|
|
|
95
95
|
handleError(e);
|
|
96
96
|
return new Response("Unknown Server Error", { status: 500 });
|
|
97
97
|
}
|
|
98
|
+
if (build.unstable_apiOnly && serverMode !== "development" && !requestUrl.pathname.endsWith(".data") && getBuildTimeHeader(request, "X-React-Router-SPA-Mode") !== "yes" && getBuildTimeHeader(request, "X-React-Router-Prerender") !== "yes") return new Response(null, {
|
|
99
|
+
status: 404,
|
|
100
|
+
statusText: "Not Found"
|
|
101
|
+
});
|
|
98
102
|
let matches = matchServerRoutes(build.routes, staticHandler, normalizedPathname);
|
|
99
103
|
if (matches && matches.length > 0) Object.assign(params, matches[0].params);
|
|
100
104
|
if (requestHandlerInstrumentations?.length) loadContext.set(instrumentationResultMetaContext, {
|
|
@@ -252,7 +256,9 @@ async function handleDocumentRequest(serverMode, build, staticHandler, request,
|
|
|
252
256
|
future: build.future,
|
|
253
257
|
routeDiscovery: build.routeDiscovery,
|
|
254
258
|
ssr: build.ssr,
|
|
255
|
-
isSpaMode
|
|
259
|
+
isSpaMode,
|
|
260
|
+
unstable_apiOnly: build.unstable_apiOnly,
|
|
261
|
+
unstable_apiServerOrigin: build.unstable_apiServerOrigin
|
|
256
262
|
};
|
|
257
263
|
let entryContext = {
|
|
258
264
|
manifest: build.assets,
|
|
@@ -270,6 +276,8 @@ async function handleDocumentRequest(serverMode, build, staticHandler, request,
|
|
|
270
276
|
ssr: build.ssr,
|
|
271
277
|
routeDiscovery: build.routeDiscovery,
|
|
272
278
|
isSpaMode,
|
|
279
|
+
unstable_apiOnly: build.unstable_apiOnly,
|
|
280
|
+
unstable_apiServerOrigin: build.unstable_apiServerOrigin,
|
|
273
281
|
serializeError: (err) => serializeError(err, serverMode)
|
|
274
282
|
};
|
|
275
283
|
let handleDocumentRequestFunction = build.entry.module.default;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
import { CriticalCss, FutureConfig } from "../dom/ssr/entry.js";
|
|
3
|
+
import { ServerBuild } from "./build.js";
|
|
4
|
+
|
|
5
|
+
//#region lib/server-runtime/serverHandoff.d.ts
|
|
6
|
+
type ServerHandoff = {
|
|
7
|
+
criticalCss?: CriticalCss;
|
|
8
|
+
basename: string | undefined;
|
|
9
|
+
future: FutureConfig;
|
|
10
|
+
ssr: boolean;
|
|
11
|
+
isSpaMode: boolean;
|
|
12
|
+
unstable_apiOnly?: boolean;
|
|
13
|
+
unstable_apiServerOrigin?: string;
|
|
14
|
+
routeDiscovery: ServerBuild["routeDiscovery"];
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { ServerHandoff };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
import { DataWithResponseInit, RouterContextProvider } from "../router/utils.js";
|
|
3
|
+
import { ClientActionFunctionArgs, ClientLoaderFunctionArgs } from "../dom/ssr/routeModules.js";
|
|
4
|
+
import { Serializable } from "../server-runtime/single-fetch.js";
|
|
3
5
|
import { Equal, Func, IsAny } from "./utils.js";
|
|
4
6
|
import { RouteModule } from "./route-module.js";
|
|
5
7
|
import { unstable_SerializesTo } from "./serializes-to.js";
|
|
6
|
-
import { Serializable } from "../server-runtime/single-fetch.js";
|
|
7
|
-
import { ClientActionFunctionArgs, ClientLoaderFunctionArgs } from "../dom/ssr/routeModules.js";
|
|
8
8
|
|
|
9
9
|
//#region lib/types/route-data.d.ts
|
|
10
10
|
type Serialize<T> = T extends unstable_SerializesTo<infer To> ? To : T extends Serializable ? T : T extends ((...args: any[]) => unknown) ? undefined : T extends Promise<infer U> ? Promise<Serialize<U>> : T extends Map<infer K, infer V> ? Map<Serialize<K>, Serialize<V>> : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<Serialize<K>, Serialize<V>> : T extends Set<infer U> ? Set<Serialize<U>> : T extends ReadonlySet<infer U> ? ReadonlySet<Serialize<U>> : T extends [] ? [] : T extends readonly [infer F, ...infer R] ? [Serialize<F>, ...Serialize<R>] : T extends Array<infer U> ? Array<Serialize<U>> : T extends readonly unknown[] ? readonly Serialize<T[number]>[] : T extends Record<any, any> ? { [K in keyof T]: Serialize<T[K]> } : undefined;
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { Location } from "../router/history.js";
|
|
3
3
|
import { DataStrategyResult, MiddlewareNextFunction } from "../router/utils.js";
|
|
4
4
|
import { LinkDescriptor } from "../router/links.js";
|
|
5
|
+
import { MetaDescriptor } from "../dom/ssr/routeModules.js";
|
|
5
6
|
import { Func, Pretty } from "./utils.js";
|
|
6
7
|
import { RouteModule } from "./route-module.js";
|
|
7
8
|
import { ClientDataFunctionArgs, GetLoaderData, ServerDataFrom, ServerDataFunctionArgs } from "./route-data.js";
|
|
8
|
-
import { MetaDescriptor } from "../dom/ssr/routeModules.js";
|
|
9
9
|
|
|
10
10
|
//#region lib/types/route-module-annotations.d.ts
|
|
11
11
|
type MaybePromise<T> = T | Promise<T>;
|
|
@@ -76,3 +76,7 @@ When using lazy route discovery, ensure your deployment setup handles manifest r
|
|
|
76
76
|
- **Route Handling** - Ensure `/__manifest` requests reach your React Router handler
|
|
77
77
|
- **CDN Caching** - If using CDN/edge caching, include `version` and `paths` query parameters in your cache key for the manifest endpoint
|
|
78
78
|
- **Multiple Applications** - Use a custom `manifestPath` if running multiple React Router applications on the same domain
|
|
79
|
+
|
|
80
|
+
## Application-Controlled Discovery
|
|
81
|
+
|
|
82
|
+
Use the experimental `unstable_useRouteDiscovery` hook to discover selected destinations, complete the route tree after startup, or control manifest-mismatch recovery. See [Application-Controlled Route Discovery](../how-to/route-discovery).
|
|
@@ -59,28 +59,130 @@ The input/output to a session storage object are HTTP cookies. `getSession()` re
|
|
|
59
59
|
|
|
60
60
|
You'll use methods to get access to sessions in your `loader` and `action` functions.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
### Session API
|
|
63
|
+
|
|
64
|
+
After retrieving a session with `getSession()`, the returned session object has a handful of methods and properties:
|
|
63
65
|
|
|
64
66
|
```tsx
|
|
65
67
|
export async function action({
|
|
66
68
|
request,
|
|
67
|
-
}:
|
|
69
|
+
}: Route.ActionArgs) {
|
|
68
70
|
const session = await getSession(
|
|
69
71
|
request.headers.get("Cookie"),
|
|
70
72
|
);
|
|
73
|
+
|
|
71
74
|
session.get("foo");
|
|
72
75
|
session.has("bar");
|
|
73
76
|
// etc.
|
|
77
|
+
|
|
78
|
+
return redirect("/", {
|
|
79
|
+
headers: {
|
|
80
|
+
"Set-Cookie": await commitSession(session),
|
|
81
|
+
},
|
|
82
|
+
});
|
|
74
83
|
}
|
|
75
84
|
```
|
|
76
85
|
|
|
77
|
-
|
|
86
|
+
<docs-warning>Every time you modify session data, you must `commitSession()` or your changes will be lost. When using cookie session storage, you must also send the `Set-Cookie` header it returns in your response.</docs-warning>
|
|
87
|
+
|
|
88
|
+
See the [Session API][session-api] for all methods and properties available on the session object.
|
|
89
|
+
|
|
90
|
+
#### `session.has(key)`
|
|
91
|
+
|
|
92
|
+
Returns `true` if the session has a value (or an unread flash value) for the given `key`. This does not consume flash values.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
session.has("userId");
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### `session.set(key, value)`
|
|
99
|
+
|
|
100
|
+
Sets a session value for use in subsequent requests:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
session.set("userId", "1234");
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `session.flash(key, value)`
|
|
107
|
+
|
|
108
|
+
Sets a session value that is only valid until the next `session.get()` for that key. After that, it's gone. Most useful for "flash messages" and server-side form validation messages:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
session.flash(
|
|
112
|
+
"globalMessage",
|
|
113
|
+
"Project successfully archived",
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
A flash value is typically set in an `action` before a redirect, then read in the `loader` of the page being redirected to:
|
|
118
|
+
|
|
119
|
+
```tsx filename=app/routes/projects.tsx
|
|
120
|
+
export async function action({
|
|
121
|
+
request,
|
|
122
|
+
}: Route.ActionArgs) {
|
|
123
|
+
const session = await getSession(
|
|
124
|
+
request.headers.get("Cookie"),
|
|
125
|
+
);
|
|
126
|
+
// ...archive the project
|
|
127
|
+
session.flash(
|
|
128
|
+
"globalMessage",
|
|
129
|
+
"Project successfully archived",
|
|
130
|
+
);
|
|
131
|
+
return redirect("/projects", {
|
|
132
|
+
headers: {
|
|
133
|
+
"Set-Cookie": await commitSession(session),
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function loader({
|
|
139
|
+
request,
|
|
140
|
+
}: Route.LoaderArgs) {
|
|
141
|
+
const session = await getSession(
|
|
142
|
+
request.headers.get("Cookie"),
|
|
143
|
+
);
|
|
144
|
+
// Reading a flash value removes it from the session
|
|
145
|
+
const message = session.get("globalMessage");
|
|
146
|
+
|
|
147
|
+
return data(
|
|
148
|
+
{ message },
|
|
149
|
+
{
|
|
150
|
+
headers: {
|
|
151
|
+
// Commit the session so the flash value is removed
|
|
152
|
+
"Set-Cookie": await commitSession(session),
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Flash values are read with `session.get()` using the same key they were set with. Reading a flash value removes it from the session, so you must still `commitSession()` after reading it; otherwise the flash value will be read again on the next request.
|
|
160
|
+
|
|
161
|
+
Avoid using the same key for both `session.set()` and `session.flash()`. `session.get()` returns the regular value first, so the flash value would never be read or removed.
|
|
162
|
+
|
|
163
|
+
You can type flash values separately from regular session values with the second generic on your session storage, like the `SessionFlashData` type in [Using Sessions](#using-sessions).
|
|
164
|
+
|
|
165
|
+
#### `session.get(key)`
|
|
166
|
+
|
|
167
|
+
Accesses a session value from a previous request:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
session.get("name");
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `session.unset(key)`
|
|
174
|
+
|
|
175
|
+
Removes a value from the session. This does not remove pending flash values; those are only removed when read with `session.get()`.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
session.unset("name");
|
|
179
|
+
```
|
|
78
180
|
|
|
79
181
|
### Login form example
|
|
80
182
|
|
|
81
183
|
A login form might look something like this:
|
|
82
184
|
|
|
83
|
-
```tsx filename=app/routes/login.tsx lines=[4-7,12-14,16,22,
|
|
185
|
+
```tsx filename=app/routes/login.tsx lines=[4-7,12-14,16,22-23,26,35-37,48-49,54,59,64]
|
|
84
186
|
import { data, redirect } from "react-router";
|
|
85
187
|
import type { Route } from "./+types/login";
|
|
86
188
|
|
|
@@ -102,6 +204,7 @@ export async function loader({
|
|
|
102
204
|
}
|
|
103
205
|
|
|
104
206
|
return data(
|
|
207
|
+
// Read and unset the flash message set by the route action.
|
|
105
208
|
{ error: session.get("error") },
|
|
106
209
|
{
|
|
107
210
|
headers: {
|
|
@@ -127,6 +230,7 @@ export async function action({
|
|
|
127
230
|
);
|
|
128
231
|
|
|
129
232
|
if (userId == null) {
|
|
233
|
+
// Set a single-use flash message to be read by the route loader.
|
|
130
234
|
session.flash("error", "Invalid username/password");
|
|
131
235
|
|
|
132
236
|
// Redirect back to the login page with errors.
|