@reckona/mreact-router 0.0.203 → 0.0.205
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 +53 -9
- package/dist/actions.d.ts +6 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +70 -10
- package/dist/actions.js.map +1 -1
- package/dist/adapters/aws-lambda.d.ts +15 -4
- package/dist/adapters/aws-lambda.d.ts.map +1 -1
- package/dist/adapters/aws-lambda.js +301 -63
- package/dist/adapters/aws-lambda.js.map +1 -1
- package/dist/adapters/cloudflare.d.ts +2 -1
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +18 -10
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/edge.d.ts +1 -1
- package/dist/adapters/edge.d.ts.map +1 -1
- package/dist/adapters/edge.js.map +1 -1
- package/dist/adapters/node.d.ts +5 -3
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +2 -1
- package/dist/adapters/node.js.map +1 -1
- package/dist/adapters/static.d.ts.map +1 -1
- package/dist/adapters/static.js +24 -2
- package/dist/adapters/static.js.map +1 -1
- package/dist/build.d.ts +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +91 -5
- package/dist/build.js.map +1 -1
- package/dist/built-runtime.d.ts +2 -0
- package/dist/built-runtime.d.ts.map +1 -1
- package/dist/built-runtime.js +16 -0
- package/dist/built-runtime.js.map +1 -1
- package/dist/cache.d.ts +3 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +211 -13
- package/dist/cache.js.map +1 -1
- package/dist/cli.js +12 -1
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +32 -1
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +21 -2
- package/dist/config.js.map +1 -1
- package/dist/csp.d.ts +1 -0
- package/dist/csp.d.ts.map +1 -1
- package/dist/csp.js +11 -6
- package/dist/csp.js.map +1 -1
- package/dist/dehydrate-policy.d.ts +3 -0
- package/dist/dehydrate-policy.d.ts.map +1 -0
- package/dist/dehydrate-policy.js +16 -0
- package/dist/dehydrate-policy.js.map +1 -0
- package/dist/dev-server.d.ts +7 -2
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +120 -11
- package/dist/dev-server.js.map +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +147 -7
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +22 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +22 -8
- package/dist/logger.js.map +1 -1
- package/dist/metadata.d.ts +8 -0
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +60 -22
- package/dist/metadata.js.map +1 -1
- package/dist/navigation-marker.d.ts.map +1 -1
- package/dist/navigation-marker.js +72 -9
- package/dist/navigation-marker.js.map +1 -1
- package/dist/navigation.d.ts +1 -0
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +1 -1
- package/dist/navigation.js.map +1 -1
- package/dist/node-server.d.ts +5 -2
- package/dist/node-server.d.ts.map +1 -1
- package/dist/node-server.js +75 -6
- package/dist/node-server.js.map +1 -1
- package/dist/prerender-entry.d.ts +14 -0
- package/dist/prerender-entry.d.ts.map +1 -1
- package/dist/prerender-entry.js +40 -3
- package/dist/prerender-entry.js.map +1 -1
- package/dist/render.d.ts +9 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +102 -123
- package/dist/render.js.map +1 -1
- package/dist/serve.d.ts +8 -2
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +22 -20
- package/dist/serve.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +52 -19
- package/dist/session.js.map +1 -1
- package/dist/upgrade.d.ts +59 -2
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +282 -1
- package/dist/upgrade.js.map +1 -1
- package/dist/vite-config.d.ts +2 -0
- package/dist/vite-config.d.ts.map +1 -1
- package/dist/vite-config.js +2 -0
- package/dist/vite-config.js.map +1 -1
- package/dist/vite.d.ts +4 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +117 -6
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +81 -11
- package/src/adapters/aws-lambda.ts +426 -115
- package/src/adapters/cloudflare.ts +29 -13
- package/src/adapters/edge.ts +2 -0
- package/src/adapters/node.ts +8 -10
- package/src/adapters/static.ts +31 -2
- package/src/build.ts +117 -10
- package/src/built-runtime.ts +28 -0
- package/src/cache.ts +262 -15
- package/src/cli.ts +14 -1
- package/src/client.ts +32 -1
- package/src/config.ts +43 -6
- package/src/csp.ts +14 -11
- package/src/dehydrate-policy.ts +23 -0
- package/src/dev-server.ts +188 -15
- package/src/http.ts +172 -6
- package/src/index.ts +14 -1
- package/src/logger.ts +47 -10
- package/src/metadata.ts +125 -60
- package/src/navigation-marker.ts +79 -9
- package/src/navigation.ts +1 -1
- package/src/node-server.ts +128 -22
- package/src/prerender-entry.ts +52 -3
- package/src/render.ts +807 -753
- package/src/serve.ts +49 -56
- package/src/session.ts +56 -25
- package/src/upgrade.ts +402 -3
- package/src/vite-config.ts +6 -0
- package/src/vite.ts +157 -5
package/src/serve.ts
CHANGED
|
@@ -23,10 +23,7 @@ import {
|
|
|
23
23
|
loadBuiltServerModuleArtifacts,
|
|
24
24
|
loadBuiltServerModuleArtifactsForRequest,
|
|
25
25
|
} from "./built-server-module-artifacts.js";
|
|
26
|
-
import {
|
|
27
|
-
type AppRoute,
|
|
28
|
-
type MatchedRoute,
|
|
29
|
-
} from "./routes.js";
|
|
26
|
+
import { type AppRoute, type MatchedRoute } from "./routes.js";
|
|
30
27
|
import type { AppRouterServerActionOptions } from "./actions.js";
|
|
31
28
|
import type { AppRouterImportPolicy } from "./import-policy.js";
|
|
32
29
|
import {
|
|
@@ -44,26 +41,21 @@ import {
|
|
|
44
41
|
traceContextFromRequest,
|
|
45
42
|
type RouterInstrumentation,
|
|
46
43
|
} from "./trace.js";
|
|
47
|
-
import {
|
|
48
|
-
|
|
49
|
-
htmlResponse,
|
|
50
|
-
} from "./http.js";
|
|
51
|
-
import {
|
|
52
|
-
emitRouterLog,
|
|
53
|
-
logDurationMs,
|
|
54
|
-
logNow,
|
|
55
|
-
type AppRouterLogger,
|
|
56
|
-
} from "./logger.js";
|
|
44
|
+
import { bytesResponse, htmlResponse } from "./http.js";
|
|
45
|
+
import { emitRouterLog, logDurationMs, logNow, type AppRouterLogger } from "./logger.js";
|
|
57
46
|
import { startNodeRequestServer } from "./node-server.js";
|
|
58
47
|
import { builtAppRuntimePreloadPlan } from "./preload-policy.js";
|
|
59
48
|
import { normalizeRoutePath } from "./route-path.js";
|
|
60
|
-
import type {
|
|
49
|
+
import type { HttpUpgradeOriginPolicy, ManagedHttpUpgradeHandler } from "./upgrade.js";
|
|
61
50
|
import {
|
|
62
51
|
isCurrentPrerenderedRoute,
|
|
63
52
|
isVisitorDependentResponse,
|
|
53
|
+
mergePrerenderedNavigationHtml,
|
|
54
|
+
protectNonceBearingResponse,
|
|
64
55
|
PRERENDERED_ROUTE_SCHEMA_VERSION,
|
|
65
56
|
replayedPrerenderedRouteHeaders,
|
|
66
57
|
storedPrerenderedRouteHeaders,
|
|
58
|
+
validatedPrerenderedNavigationHtml,
|
|
67
59
|
} from "./prerender-entry.js";
|
|
68
60
|
|
|
69
61
|
interface BuiltRuntimeCacheEntry {
|
|
@@ -168,7 +160,11 @@ export interface StartServerOptions {
|
|
|
168
160
|
// a generic "Internal Server Error" body and logs the stack to stderr
|
|
169
161
|
// via console.error. Issue 071: stack traces must never end up in
|
|
170
162
|
// production responses.
|
|
171
|
-
errorHandler?: (error: unknown) => {
|
|
163
|
+
errorHandler?: (error: unknown) => {
|
|
164
|
+
body: string;
|
|
165
|
+
status: number;
|
|
166
|
+
headers?: Record<string, string>;
|
|
167
|
+
};
|
|
172
168
|
// When set, an incoming Host header that does not exactly match one of
|
|
173
169
|
// the listed values is replaced with the configured hostname/port for
|
|
174
170
|
// origin reconstruction. Use this in front of public deployments to
|
|
@@ -185,7 +181,13 @@ export interface StartServerOptions {
|
|
|
185
181
|
routeCache?: AppRouterCache | undefined;
|
|
186
182
|
serverActions?: AppRouterServerActionOptions | undefined;
|
|
187
183
|
sinkStrategy?: ResponseSinkStrategy;
|
|
188
|
-
onUpgrade?:
|
|
184
|
+
onUpgrade?: ManagedHttpUpgradeHandler | undefined;
|
|
185
|
+
/** Maximum time to wait for application-owned upgrade sockets during shutdown. */
|
|
186
|
+
upgradeCloseTimeoutMs?: number | undefined;
|
|
187
|
+
/** Maximum time an asynchronous upgrade handler may leave ownership undecided. */
|
|
188
|
+
upgradeDecisionTimeoutMs?: number | undefined;
|
|
189
|
+
/** Origin policy for application HTTP upgrades. Defaults to exact same-origin validation. */
|
|
190
|
+
upgradeOriginPolicy?: HttpUpgradeOriginPolicy | undefined;
|
|
189
191
|
/**
|
|
190
192
|
* Trusts the first `X-Forwarded-Proto` value when the socket is not TLS.
|
|
191
193
|
*
|
|
@@ -224,7 +226,7 @@ export function warnIfImplicitHostTrust(options: {
|
|
|
224
226
|
|
|
225
227
|
warnedImplicitHostTrust = true;
|
|
226
228
|
console.error(
|
|
227
|
-
|
|
229
|
+
'[mreact] Host header trust is implicit because neither allowedHosts nor hostPolicy is configured. Set allowedHosts for public deployments, hostPolicy: "strict" to reject unlisted Host headers, or hostPolicy: "trusted-proxy" when a trusted reverse proxy normalizes Host.',
|
|
228
230
|
);
|
|
229
231
|
}
|
|
230
232
|
|
|
@@ -260,6 +262,7 @@ export async function createBuiltRequestRuntime(
|
|
|
260
262
|
emitBuiltRequestStart(renderOptions.instrumentation, request);
|
|
261
263
|
const response = await renderBuiltAppRequestWithRuntime({
|
|
262
264
|
...renderOptions,
|
|
265
|
+
dehydrateOptions: renderOptions.dehydrateOptions ?? runtime.dehydrateOptions,
|
|
263
266
|
instrumentation: withoutBuiltRequestInstrumentation(renderOptions.instrumentation),
|
|
264
267
|
importPolicy:
|
|
265
268
|
renderOptions.importPolicy === undefined
|
|
@@ -405,8 +408,11 @@ function withoutBuiltRequestInstrumentation(
|
|
|
405
408
|
return undefined;
|
|
406
409
|
}
|
|
407
410
|
|
|
408
|
-
const {
|
|
409
|
-
|
|
411
|
+
const {
|
|
412
|
+
onRequestEnd: _onRequestEnd,
|
|
413
|
+
onRequestStart: _onRequestStart,
|
|
414
|
+
...remaining
|
|
415
|
+
} = instrumentation;
|
|
410
416
|
return remaining;
|
|
411
417
|
}
|
|
412
418
|
|
|
@@ -470,21 +476,14 @@ async function renderBuiltAppRequestWithRuntime(
|
|
|
470
476
|
// no middleware at all. Otherwise a middleware that gates access (auth,
|
|
471
477
|
// geo blocking, maintenance mode) would be bypassed for exactly the routes
|
|
472
478
|
// it is most often used to protect.
|
|
473
|
-
if (
|
|
474
|
-
!options.runtime.hasMiddleware &&
|
|
475
|
-
(request.method === "GET" || request.method === "HEAD")
|
|
476
|
-
) {
|
|
479
|
+
if (!options.runtime.hasMiddleware && (request.method === "GET" || request.method === "HEAD")) {
|
|
477
480
|
// Sync fast path when no external prerender store is configured (the
|
|
478
481
|
// common case): skip the Promise wrap that `readPrerenderedRoute`
|
|
479
482
|
// would otherwise introduce just to satisfy the async signature.
|
|
480
483
|
const prerendered =
|
|
481
484
|
options.prerenderStore === undefined
|
|
482
485
|
? options.runtime.prerenderedRoutes.get(normalizedPath)
|
|
483
|
-
: await readPrerenderedRoute(
|
|
484
|
-
options.runtime,
|
|
485
|
-
normalizedPath,
|
|
486
|
-
options.prerenderStore,
|
|
487
|
-
);
|
|
486
|
+
: await readPrerenderedRoute(options.runtime, normalizedPath, options.prerenderStore);
|
|
488
487
|
const prerenderedResponse = prerenderedRouteResponse(prerendered, request);
|
|
489
488
|
|
|
490
489
|
if (prerenderedResponse !== undefined) {
|
|
@@ -508,18 +507,11 @@ async function renderBuiltAppRequestWithRuntime(
|
|
|
508
507
|
// Middleware apps resolve prerendered HTML here instead, so that a
|
|
509
508
|
// middleware response or rewrite is honoured before the stored HTML is
|
|
510
509
|
// served. The lookup uses the post-middleware path.
|
|
511
|
-
if (
|
|
512
|
-
options.runtime.hasMiddleware &&
|
|
513
|
-
(request.method === "GET" || request.method === "HEAD")
|
|
514
|
-
) {
|
|
510
|
+
if (options.runtime.hasMiddleware && (request.method === "GET" || request.method === "HEAD")) {
|
|
515
511
|
const prerendered =
|
|
516
512
|
options.prerenderStore === undefined
|
|
517
513
|
? options.runtime.prerenderedRoutes.get(normalizedPath)
|
|
518
|
-
: await readPrerenderedRoute(
|
|
519
|
-
options.runtime,
|
|
520
|
-
normalizedPath,
|
|
521
|
-
options.prerenderStore,
|
|
522
|
-
);
|
|
514
|
+
: await readPrerenderedRoute(options.runtime, normalizedPath, options.prerenderStore);
|
|
523
515
|
const prerenderedResponse = prerenderedRouteResponse(prerendered, request);
|
|
524
516
|
|
|
525
517
|
if (prerenderedResponse !== undefined) {
|
|
@@ -545,15 +537,9 @@ async function renderBuiltAppRequestWithRuntime(
|
|
|
545
537
|
requestUrl: new URL(request.url),
|
|
546
538
|
});
|
|
547
539
|
|
|
548
|
-
await applyBuiltPrerenderInvalidations(
|
|
549
|
-
options.runtime,
|
|
550
|
-
response,
|
|
551
|
-
options.prerenderStore,
|
|
552
|
-
);
|
|
540
|
+
await applyBuiltPrerenderInvalidations(options.runtime, response, options.prerenderStore);
|
|
553
541
|
|
|
554
|
-
return options.sinkStrategy === "buffer"
|
|
555
|
-
? await materializeResponseAsBuffer(response)
|
|
556
|
-
: response;
|
|
542
|
+
return options.sinkStrategy === "buffer" ? await materializeResponseAsBuffer(response) : response;
|
|
557
543
|
}
|
|
558
544
|
|
|
559
545
|
function prerenderedRouteResponse(
|
|
@@ -565,7 +551,7 @@ function prerenderedRouteResponse(
|
|
|
565
551
|
}
|
|
566
552
|
|
|
567
553
|
const navigation = request.headers.get("x-mreact-navigation") === "1";
|
|
568
|
-
const html = navigation ? prerendered
|
|
554
|
+
const html = navigation ? validatedPrerenderedNavigationHtml(prerendered) : prerendered.html;
|
|
569
555
|
if (html === undefined) {
|
|
570
556
|
return undefined;
|
|
571
557
|
}
|
|
@@ -673,7 +659,7 @@ async function applyBuiltAppResponseHook(
|
|
|
673
659
|
request: options.request,
|
|
674
660
|
});
|
|
675
661
|
|
|
676
|
-
return hooked instanceof Response ? hooked : response;
|
|
662
|
+
return protectNonceBearingResponse(hooked instanceof Response ? hooked : response);
|
|
677
663
|
}
|
|
678
664
|
|
|
679
665
|
async function materializeResponseAsBuffer(response: Response): Promise<Response> {
|
|
@@ -718,6 +704,9 @@ export async function startServer(
|
|
|
718
704
|
port: options.port,
|
|
719
705
|
resolveHost: resolveRequestHost,
|
|
720
706
|
trustForwardedProto: options.trustForwardedProto,
|
|
707
|
+
upgradeCloseTimeoutMs: options.upgradeCloseTimeoutMs,
|
|
708
|
+
upgradeDecisionTimeoutMs: options.upgradeDecisionTimeoutMs,
|
|
709
|
+
upgradeOriginPolicy: options.upgradeOriginPolicy,
|
|
721
710
|
render: (request) =>
|
|
722
711
|
runtime.render(request, {
|
|
723
712
|
dehydrateOptions: options.dehydrateOptions,
|
|
@@ -973,7 +962,9 @@ function builtRenderAppRequestOptions(
|
|
|
973
962
|
|
|
974
963
|
function mergeBuiltServerActionOptions(
|
|
975
964
|
options: AppRouterServerActionOptions | undefined,
|
|
976
|
-
allowedActions:
|
|
965
|
+
allowedActions:
|
|
966
|
+
| readonly { moduleId: string; exportName: string; inferred?: boolean }[]
|
|
967
|
+
| undefined,
|
|
977
968
|
): AppRouterServerActionOptions | undefined {
|
|
978
969
|
if (allowedActions === undefined) {
|
|
979
970
|
return options;
|
|
@@ -990,9 +981,7 @@ async function renderAndCachePrerenderWithLock(
|
|
|
990
981
|
path: string,
|
|
991
982
|
): Promise<Response> {
|
|
992
983
|
const lockKey =
|
|
993
|
-
options.request.headers.get("x-mreact-navigation") === "1"
|
|
994
|
-
? `${path}\0navigation`
|
|
995
|
-
: path;
|
|
984
|
+
options.request.headers.get("x-mreact-navigation") === "1" ? `${path}\0navigation` : path;
|
|
996
985
|
const existing = options.runtime.prerenderLocks.get(lockKey);
|
|
997
986
|
|
|
998
987
|
if (existing !== undefined) {
|
|
@@ -1019,9 +1008,7 @@ async function runPrerenderRegeneration(
|
|
|
1019
1008
|
path: string,
|
|
1020
1009
|
): Promise<{ response: Response; shareable: boolean }> {
|
|
1021
1010
|
const lockKey =
|
|
1022
|
-
options.request.headers.get("x-mreact-navigation") === "1"
|
|
1023
|
-
? `${path}\0navigation`
|
|
1024
|
-
: path;
|
|
1011
|
+
options.request.headers.get("x-mreact-navigation") === "1" ? `${path}\0navigation` : path;
|
|
1025
1012
|
const regenerate = async () => {
|
|
1026
1013
|
const stored = await readPrerenderedRoute(options.runtime, path, options.prerenderStore);
|
|
1027
1014
|
const storedResponse = prerenderedRouteResponse(stored, options.request);
|
|
@@ -1128,7 +1115,13 @@ async function cacheRegeneratedPrerenderedRoute(
|
|
|
1128
1115
|
status: response.status,
|
|
1129
1116
|
...(strictTransportSecurity === undefined ? {} : { strictTransportSecurity }),
|
|
1130
1117
|
}
|
|
1131
|
-
:
|
|
1118
|
+
: mergePrerenderedNavigationHtml(existing, body);
|
|
1119
|
+
if (entry === existing) {
|
|
1120
|
+
return htmlResponse(body, {
|
|
1121
|
+
headers: response.headers,
|
|
1122
|
+
status: response.status,
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1132
1125
|
runtime.prerenderedRoutes.set(path, entry);
|
|
1133
1126
|
await store?.set(path, entry);
|
|
1134
1127
|
|
package/src/session.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deleteCookie, parseCookieHeader, setCookie } from "./cookies.js";
|
|
1
|
+
import { deleteCookie, parseCookieHeader, serializeCookie, setCookie } from "./cookies.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Stores session data and expiration metadata for one session id.
|
|
@@ -43,32 +43,54 @@ const DEFAULT_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;
|
|
|
43
43
|
const SESSION_COOKIE_DEV = "mreact.session";
|
|
44
44
|
const SESSION_COOKIE_PROD = "__Host-mreact.session";
|
|
45
45
|
|
|
46
|
-
function
|
|
47
|
-
|
|
46
|
+
function usesHardenedCookieDefaults(): boolean {
|
|
47
|
+
const nodeEnv = typeof process === "undefined" ? undefined : process["env"]?.["NODE_ENV"];
|
|
48
|
+
return nodeEnv !== "development" && nodeEnv !== "test";
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
function sessionCookieOptions(options: SessionCookieOptions = {}) {
|
|
55
|
-
const production = isProduction();
|
|
56
|
-
|
|
57
|
-
return {
|
|
51
|
+
function resolveSessionCookie(options: SessionCookieOptions = {}) {
|
|
52
|
+
const hardened = usesHardenedCookieDefaults();
|
|
53
|
+
const cookieOptions = {
|
|
58
54
|
httpOnly: true,
|
|
59
55
|
maxAge: options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS,
|
|
60
|
-
path:
|
|
56
|
+
path: options.path ?? "/",
|
|
61
57
|
sameSite: options.sameSite ?? "Lax",
|
|
62
|
-
secure: options.secure ??
|
|
58
|
+
secure: options.secure ?? hardened,
|
|
63
59
|
} as const;
|
|
60
|
+
const canUseHostPrefix = cookieOptions.secure && cookieOptions.path === "/";
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
name:
|
|
64
|
+
options.cookieName ??
|
|
65
|
+
(hardened && canUseHostPrefix ? SESSION_COOKIE_PROD : SESSION_COOKIE_DEV),
|
|
66
|
+
options: cookieOptions,
|
|
67
|
+
};
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
function validateResolvedSessionCookie(cookie: ReturnType<typeof resolveSessionCookie>): void {
|
|
71
|
+
serializeCookie(cookie.name, "", cookie.options);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function assertMutableResponseHeaders(response: Response): void {
|
|
75
|
+
const probeName = "x-mreact-session-header-probe";
|
|
76
|
+
const previous = response.headers.get(probeName);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
response.headers.set(probeName, "1");
|
|
80
|
+
if (previous === null) {
|
|
81
|
+
response.headers.delete(probeName);
|
|
82
|
+
} else {
|
|
83
|
+
response.headers.set(probeName, previous);
|
|
84
|
+
}
|
|
85
|
+
} catch (cause) {
|
|
86
|
+
throw new TypeError("Session helpers require a Response with mutable headers.", { cause });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function readSessionId(request: Request, options: SessionCookieOptions = {}): string | undefined {
|
|
91
|
+
const cookie = resolveSessionCookie(options);
|
|
70
92
|
const values = parseCookieHeader(request.headers.get("cookie"));
|
|
71
|
-
return values.get(
|
|
93
|
+
return values.get(cookie.name);
|
|
72
94
|
}
|
|
73
95
|
|
|
74
96
|
function createSessionId(): string {
|
|
@@ -208,6 +230,9 @@ export async function createSession<TData>(
|
|
|
208
230
|
): Promise<SessionRecord<TData>> {
|
|
209
231
|
const now = Date.now();
|
|
210
232
|
const maxAgeSeconds = options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;
|
|
233
|
+
const cookie = resolveSessionCookie({ ...options, maxAgeSeconds });
|
|
234
|
+
validateResolvedSessionCookie(cookie);
|
|
235
|
+
assertMutableResponseHeaders(response);
|
|
211
236
|
const record: SessionRecord<TData> = {
|
|
212
237
|
createdAt: now,
|
|
213
238
|
data,
|
|
@@ -216,9 +241,7 @@ export async function createSession<TData>(
|
|
|
216
241
|
};
|
|
217
242
|
|
|
218
243
|
await store.set(record);
|
|
219
|
-
setCookie(response,
|
|
220
|
-
...sessionCookieOptions({ ...options, maxAgeSeconds }),
|
|
221
|
-
});
|
|
244
|
+
setCookie(response, cookie.name, record.id, cookie.options);
|
|
222
245
|
|
|
223
246
|
return record;
|
|
224
247
|
}
|
|
@@ -232,16 +255,22 @@ export async function destroySession<TData>(
|
|
|
232
255
|
store: SessionStore<TData>,
|
|
233
256
|
options: SessionCookieOptions = {},
|
|
234
257
|
): Promise<void> {
|
|
258
|
+
const cookie = resolveSessionCookie(options);
|
|
259
|
+
validateResolvedSessionCookie({
|
|
260
|
+
name: cookie.name,
|
|
261
|
+
options: { ...cookie.options, maxAge: 0 },
|
|
262
|
+
});
|
|
263
|
+
assertMutableResponseHeaders(response);
|
|
235
264
|
const id = readSessionId(request, options);
|
|
236
265
|
|
|
237
266
|
if (id !== undefined) {
|
|
238
267
|
await store.delete(id);
|
|
239
268
|
}
|
|
240
269
|
|
|
241
|
-
deleteCookie(response,
|
|
242
|
-
path:
|
|
243
|
-
sameSite: options.sameSite
|
|
244
|
-
secure: options.secure
|
|
270
|
+
deleteCookie(response, cookie.name, {
|
|
271
|
+
path: cookie.options.path,
|
|
272
|
+
sameSite: cookie.options.sameSite,
|
|
273
|
+
secure: cookie.options.secure,
|
|
245
274
|
});
|
|
246
275
|
}
|
|
247
276
|
|
|
@@ -256,6 +285,8 @@ export async function rotateSession<TData>(
|
|
|
256
285
|
store: SessionStore<TData>,
|
|
257
286
|
options: SessionCookieOptions = {},
|
|
258
287
|
): Promise<SessionRecord<TData> | undefined> {
|
|
288
|
+
validateResolvedSessionCookie(resolveSessionCookie(options));
|
|
289
|
+
assertMutableResponseHeaders(response);
|
|
259
290
|
const current = await getSession(request, store, options);
|
|
260
291
|
|
|
261
292
|
if (current === undefined) {
|