@chidchanun/bcp 0.1.7 → 0.1.9
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 +71 -0
- package/README.md +190 -4
- package/docs/application-modules.md +63 -3
- package/docs/server-data-loaders.md +240 -0
- package/docs/session-auth.md +186 -0
- package/package.json +1 -1
- package/packages/bundler/src/server-production.ts +25 -0
- package/packages/client/src/index.tsx +4 -0
- package/packages/client/src/loader-data.tsx +99 -0
- package/packages/client/src/router-v2.tsx +254 -29
- package/packages/client/src/server.ts +13 -0
- package/packages/server/src/dev-navigation-target.ts +188 -0
- package/packages/server/src/index.ts +209 -119
- package/packages/server/src/navigation-payload.ts +24 -1
- package/packages/server/src/navigation-response.ts +284 -0
- package/packages/server/src/page-loader.ts +346 -0
- package/packages/server/src/session.ts +694 -0
- package/packages/server/src/standalone-production-runtime-v2-navigation.ts +785 -0
- package/packages/server/src/standalone-production-runtime-v2.ts +131 -19
- package/packages/server/src/standalone-production-runtime-v3.ts +1 -1
- package/packages/server/src/standalone-production-runtime-v4.ts +42 -2
- package/packages/server/src/static-dev-server.ts +21 -17
|
@@ -18,6 +18,10 @@ import {
|
|
|
18
18
|
renderToString,
|
|
19
19
|
} from "react-dom/server";
|
|
20
20
|
|
|
21
|
+
import {
|
|
22
|
+
BcpLoaderDataProvider,
|
|
23
|
+
} from "../../client/src/loader-data.js";
|
|
24
|
+
|
|
21
25
|
import {
|
|
22
26
|
assertNoPageApiConflicts,
|
|
23
27
|
matchApiRoute,
|
|
@@ -41,7 +45,15 @@ import {
|
|
|
41
45
|
DevHmrServer,
|
|
42
46
|
} from "./dev-hmr.js";
|
|
43
47
|
|
|
44
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
attachLoaderDataToParams,
|
|
50
|
+
executePageLoader,
|
|
51
|
+
} from "./page-loader.js";
|
|
52
|
+
|
|
53
|
+
import {
|
|
54
|
+
runWithRequestContext,
|
|
55
|
+
applyResponseCookies,
|
|
56
|
+
} from "./request-context.ts";
|
|
45
57
|
|
|
46
58
|
/*
|
|
47
59
|
* =====================================
|
|
@@ -71,7 +83,7 @@ interface FrameworkData {
|
|
|
71
83
|
|
|
72
84
|
params: Record<
|
|
73
85
|
string,
|
|
74
|
-
|
|
86
|
+
any
|
|
75
87
|
>;
|
|
76
88
|
}
|
|
77
89
|
|
|
@@ -501,124 +513,15 @@ export function createDevServer(
|
|
|
501
513
|
);
|
|
502
514
|
}
|
|
503
515
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
* Server Components Import
|
|
507
|
-
* =================================
|
|
508
|
-
*/
|
|
509
|
-
|
|
510
|
-
const importStartedAt =
|
|
511
|
-
performance.now();
|
|
512
|
-
|
|
513
|
-
const Page =
|
|
514
|
-
await importComponent(
|
|
515
|
-
route.filePath,
|
|
516
|
-
"Page",
|
|
517
|
-
moduleVersion
|
|
518
|
-
);
|
|
519
|
-
|
|
520
|
-
const Layouts:
|
|
521
|
-
ComponentType<any>[] =
|
|
522
|
-
[];
|
|
523
|
-
|
|
524
|
-
for (
|
|
525
|
-
const layoutFile
|
|
526
|
-
of route.layouts
|
|
527
|
-
) {
|
|
528
|
-
const Layout =
|
|
529
|
-
await importComponent(
|
|
530
|
-
layoutFile,
|
|
531
|
-
"Layout",
|
|
532
|
-
moduleVersion
|
|
533
|
-
);
|
|
534
|
-
|
|
535
|
-
Layouts.push(
|
|
536
|
-
Layout
|
|
537
|
-
);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
console.log(
|
|
541
|
-
`Import: ${(performance.now() - importStartedAt).toFixed(2)}ms`
|
|
542
|
-
);
|
|
543
|
-
|
|
544
|
-
/*
|
|
545
|
-
* =================================
|
|
546
|
-
* React Tree
|
|
547
|
-
* =================================
|
|
548
|
-
*/
|
|
549
|
-
|
|
550
|
-
const renderStartedAt =
|
|
551
|
-
performance.now();
|
|
552
|
-
|
|
553
|
-
let element:
|
|
554
|
-
ReactElement =
|
|
555
|
-
createElement(
|
|
556
|
-
Page,
|
|
557
|
-
{
|
|
558
|
-
params,
|
|
559
|
-
}
|
|
560
|
-
);
|
|
561
|
-
|
|
562
|
-
for (
|
|
563
|
-
let index =
|
|
564
|
-
Layouts.length -
|
|
565
|
-
1;
|
|
566
|
-
|
|
567
|
-
index >= 0;
|
|
568
|
-
|
|
569
|
-
index--
|
|
570
|
-
) {
|
|
571
|
-
const Layout =
|
|
572
|
-
Layouts[index];
|
|
573
|
-
|
|
574
|
-
element =
|
|
575
|
-
createElement(
|
|
576
|
-
Layout,
|
|
577
|
-
{
|
|
578
|
-
params,
|
|
579
|
-
},
|
|
580
|
-
element
|
|
581
|
-
);
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
const content =
|
|
585
|
-
renderToString(
|
|
586
|
-
element
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
console.log(
|
|
590
|
-
`SSR: ${(performance.now() - renderStartedAt).toFixed(2)}ms`
|
|
591
|
-
);
|
|
592
|
-
|
|
593
|
-
/*
|
|
594
|
-
* =================================
|
|
595
|
-
* Framework Data
|
|
596
|
-
* =================================
|
|
597
|
-
*/
|
|
598
|
-
|
|
599
|
-
const frameworkData:
|
|
600
|
-
FrameworkData = {
|
|
601
|
-
route:
|
|
602
|
-
route.pathname,
|
|
603
|
-
|
|
604
|
-
params,
|
|
605
|
-
};
|
|
606
|
-
|
|
607
|
-
const html =
|
|
608
|
-
renderDocument(
|
|
609
|
-
content,
|
|
610
|
-
|
|
611
|
-
pageClientBundle
|
|
612
|
-
.publicPath,
|
|
613
|
-
|
|
614
|
-
frameworkData,
|
|
615
|
-
|
|
616
|
-
clientVersion
|
|
617
|
-
);
|
|
618
|
-
|
|
619
|
-
sendHtml(
|
|
516
|
+
await handlePageRoute(
|
|
517
|
+
req,
|
|
620
518
|
res,
|
|
621
|
-
|
|
519
|
+
requestUrl,
|
|
520
|
+
route,
|
|
521
|
+
params,
|
|
522
|
+
pageClientBundle,
|
|
523
|
+
moduleVersion,
|
|
524
|
+
clientVersion
|
|
622
525
|
);
|
|
623
526
|
} catch (error) {
|
|
624
527
|
console.error("");
|
|
@@ -1335,6 +1238,193 @@ export function createDevServer(
|
|
|
1335
1238
|
};
|
|
1336
1239
|
}
|
|
1337
1240
|
|
|
1241
|
+
/*
|
|
1242
|
+
* =====================================
|
|
1243
|
+
* Page Route
|
|
1244
|
+
* =====================================
|
|
1245
|
+
*/
|
|
1246
|
+
|
|
1247
|
+
async function handlePageRoute(
|
|
1248
|
+
req: http.IncomingMessage,
|
|
1249
|
+
res: http.ServerResponse,
|
|
1250
|
+
requestUrl: URL,
|
|
1251
|
+
route: Route,
|
|
1252
|
+
params: Record<string, any>,
|
|
1253
|
+
pageClientBundle: ClientBundle,
|
|
1254
|
+
moduleVersion: number,
|
|
1255
|
+
clientVersion: number
|
|
1256
|
+
): Promise<void> {
|
|
1257
|
+
const request =
|
|
1258
|
+
await createWebRequest(
|
|
1259
|
+
req,
|
|
1260
|
+
requestUrl
|
|
1261
|
+
);
|
|
1262
|
+
|
|
1263
|
+
const response =
|
|
1264
|
+
await runWithRequestContext(
|
|
1265
|
+
request,
|
|
1266
|
+
async () => {
|
|
1267
|
+
const loaderExecution =
|
|
1268
|
+
await executePageLoader(
|
|
1269
|
+
route.filePath,
|
|
1270
|
+
params,
|
|
1271
|
+
requestUrl,
|
|
1272
|
+
moduleVersion
|
|
1273
|
+
);
|
|
1274
|
+
|
|
1275
|
+
if (
|
|
1276
|
+
loaderExecution.response
|
|
1277
|
+
) {
|
|
1278
|
+
return applyResponseCookies(
|
|
1279
|
+
loaderExecution.response
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
const frameworkParams =
|
|
1284
|
+
attachLoaderDataToParams(
|
|
1285
|
+
params,
|
|
1286
|
+
loaderExecution
|
|
1287
|
+
);
|
|
1288
|
+
|
|
1289
|
+
const importStartedAt =
|
|
1290
|
+
performance.now();
|
|
1291
|
+
|
|
1292
|
+
const Page =
|
|
1293
|
+
await importComponent(
|
|
1294
|
+
route.filePath,
|
|
1295
|
+
"Page",
|
|
1296
|
+
moduleVersion
|
|
1297
|
+
);
|
|
1298
|
+
|
|
1299
|
+
const Layouts:
|
|
1300
|
+
ComponentType<any>[] = [];
|
|
1301
|
+
|
|
1302
|
+
for (
|
|
1303
|
+
const layoutFile
|
|
1304
|
+
of route.layouts
|
|
1305
|
+
) {
|
|
1306
|
+
const Layout =
|
|
1307
|
+
await importComponent(
|
|
1308
|
+
layoutFile,
|
|
1309
|
+
"Layout",
|
|
1310
|
+
moduleVersion
|
|
1311
|
+
);
|
|
1312
|
+
|
|
1313
|
+
Layouts.push(
|
|
1314
|
+
Layout
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
console.log(
|
|
1319
|
+
`Import: ${(performance.now() - importStartedAt).toFixed(2)}ms`
|
|
1320
|
+
);
|
|
1321
|
+
|
|
1322
|
+
const renderStartedAt =
|
|
1323
|
+
performance.now();
|
|
1324
|
+
|
|
1325
|
+
let element:
|
|
1326
|
+
ReactElement =
|
|
1327
|
+
createElement(
|
|
1328
|
+
Page,
|
|
1329
|
+
{
|
|
1330
|
+
params:
|
|
1331
|
+
frameworkParams,
|
|
1332
|
+
}
|
|
1333
|
+
);
|
|
1334
|
+
|
|
1335
|
+
for (
|
|
1336
|
+
let index =
|
|
1337
|
+
Layouts.length - 1;
|
|
1338
|
+
index >= 0;
|
|
1339
|
+
index--
|
|
1340
|
+
) {
|
|
1341
|
+
const Layout =
|
|
1342
|
+
Layouts[index];
|
|
1343
|
+
|
|
1344
|
+
element =
|
|
1345
|
+
createElement(
|
|
1346
|
+
Layout,
|
|
1347
|
+
{
|
|
1348
|
+
params:
|
|
1349
|
+
frameworkParams,
|
|
1350
|
+
},
|
|
1351
|
+
element
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
if (
|
|
1356
|
+
loaderExecution.hasLoader
|
|
1357
|
+
) {
|
|
1358
|
+
element =
|
|
1359
|
+
createElement(
|
|
1360
|
+
BcpLoaderDataProvider,
|
|
1361
|
+
{
|
|
1362
|
+
data:
|
|
1363
|
+
loaderExecution.data,
|
|
1364
|
+
},
|
|
1365
|
+
element
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const content =
|
|
1370
|
+
renderToString(
|
|
1371
|
+
element
|
|
1372
|
+
);
|
|
1373
|
+
|
|
1374
|
+
console.log(
|
|
1375
|
+
`SSR: ${(performance.now() - renderStartedAt).toFixed(2)}ms`
|
|
1376
|
+
);
|
|
1377
|
+
|
|
1378
|
+
const frameworkData:
|
|
1379
|
+
FrameworkData = {
|
|
1380
|
+
route:
|
|
1381
|
+
route.pathname,
|
|
1382
|
+
params:
|
|
1383
|
+
frameworkParams,
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
const html =
|
|
1387
|
+
renderDocument(
|
|
1388
|
+
content,
|
|
1389
|
+
pageClientBundle
|
|
1390
|
+
.publicPath,
|
|
1391
|
+
frameworkData,
|
|
1392
|
+
clientVersion
|
|
1393
|
+
);
|
|
1394
|
+
|
|
1395
|
+
return applyResponseCookies(
|
|
1396
|
+
new Response(
|
|
1397
|
+
html,
|
|
1398
|
+
{
|
|
1399
|
+
status:
|
|
1400
|
+
200,
|
|
1401
|
+
headers: {
|
|
1402
|
+
"Content-Type":
|
|
1403
|
+
"text/html; charset=utf-8",
|
|
1404
|
+
"Cache-Control":
|
|
1405
|
+
"no-store",
|
|
1406
|
+
},
|
|
1407
|
+
}
|
|
1408
|
+
)
|
|
1409
|
+
);
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
remoteAddress:
|
|
1413
|
+
req.socket
|
|
1414
|
+
.remoteAddress ??
|
|
1415
|
+
null,
|
|
1416
|
+
}
|
|
1417
|
+
);
|
|
1418
|
+
|
|
1419
|
+
await sendWebResponse(
|
|
1420
|
+
res,
|
|
1421
|
+
response,
|
|
1422
|
+
normalizeHttpMethod(
|
|
1423
|
+
req.method
|
|
1424
|
+
) === "HEAD"
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1338
1428
|
/*
|
|
1339
1429
|
* =====================================
|
|
1340
1430
|
* API Route
|
|
@@ -28,9 +28,13 @@ import {
|
|
|
28
28
|
type MetadataSource,
|
|
29
29
|
} from "./metadata.js";
|
|
30
30
|
|
|
31
|
+
import {
|
|
32
|
+
routeHasPageLoader,
|
|
33
|
+
} from "./page-loader.js";
|
|
34
|
+
|
|
31
35
|
export interface NavigationPayload {
|
|
32
36
|
route: string;
|
|
33
|
-
params: Record<string,
|
|
37
|
+
params: Record<string, unknown>;
|
|
34
38
|
clientScript: string;
|
|
35
39
|
clientImports: string[];
|
|
36
40
|
title: string;
|
|
@@ -51,6 +55,8 @@ export interface NavigationPayloadOptions {
|
|
|
51
55
|
title?: string;
|
|
52
56
|
|
|
53
57
|
version?: number;
|
|
58
|
+
|
|
59
|
+
allowLoaderRoute?: boolean;
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
export async function resolveNavigationPayload(
|
|
@@ -81,6 +87,23 @@ export async function resolveNavigationPayload(
|
|
|
81
87
|
return null;
|
|
82
88
|
}
|
|
83
89
|
|
|
90
|
+
/*
|
|
91
|
+
* A loader-backed route must opt in here because this helper only knows
|
|
92
|
+
* how to build the route/navigation metadata. Step 2 navigation callers
|
|
93
|
+
* execute the loader in a request context first and then attach the fresh
|
|
94
|
+
* loader data to params before sending the payload. Document metadata
|
|
95
|
+
* resolution also opts in because the page loader has already run in the
|
|
96
|
+
* normal SSR request pipeline.
|
|
97
|
+
*/
|
|
98
|
+
if (
|
|
99
|
+
!options.allowLoaderRoute &&
|
|
100
|
+
routeHasPageLoader(
|
|
101
|
+
match.route.filePath
|
|
102
|
+
)
|
|
103
|
+
) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
84
107
|
const loadingFile =
|
|
85
108
|
findNearestLoadingFile(
|
|
86
109
|
appDirectory,
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import * as http from "node:http";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
appendVary,
|
|
5
|
+
compressDynamicContent,
|
|
6
|
+
selectContentEncoding,
|
|
7
|
+
} from "./compression.js";
|
|
8
|
+
|
|
9
|
+
export interface NavigationRedirectPayload {
|
|
10
|
+
redirect: {
|
|
11
|
+
location: string;
|
|
12
|
+
status: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createNavigationJsonResponse(
|
|
17
|
+
value: unknown,
|
|
18
|
+
init: ResponseInit = {}
|
|
19
|
+
): Response {
|
|
20
|
+
const headers =
|
|
21
|
+
new Headers(
|
|
22
|
+
init.headers
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (
|
|
26
|
+
!headers.has(
|
|
27
|
+
"Cache-Control"
|
|
28
|
+
)
|
|
29
|
+
) {
|
|
30
|
+
headers.set(
|
|
31
|
+
"Cache-Control",
|
|
32
|
+
"no-store"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (
|
|
37
|
+
!headers.has(
|
|
38
|
+
"X-Content-Type-Options"
|
|
39
|
+
)
|
|
40
|
+
) {
|
|
41
|
+
headers.set(
|
|
42
|
+
"X-Content-Type-Options",
|
|
43
|
+
"nosniff"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return Response.json(
|
|
48
|
+
value,
|
|
49
|
+
{
|
|
50
|
+
...init,
|
|
51
|
+
headers,
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createNavigationRedirectResponse(
|
|
57
|
+
response: Response
|
|
58
|
+
): Response | null {
|
|
59
|
+
if (
|
|
60
|
+
!isRedirectStatus(
|
|
61
|
+
response.status
|
|
62
|
+
)
|
|
63
|
+
) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const location =
|
|
68
|
+
response.headers.get(
|
|
69
|
+
"location"
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (!location) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const headers =
|
|
77
|
+
new Headers();
|
|
78
|
+
|
|
79
|
+
copySetCookieHeaders(
|
|
80
|
+
response.headers,
|
|
81
|
+
headers
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return createNavigationJsonResponse(
|
|
85
|
+
{
|
|
86
|
+
redirect: {
|
|
87
|
+
location,
|
|
88
|
+
status:
|
|
89
|
+
response.status,
|
|
90
|
+
},
|
|
91
|
+
} satisfies NavigationRedirectPayload,
|
|
92
|
+
{
|
|
93
|
+
status:
|
|
94
|
+
200,
|
|
95
|
+
headers,
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function sendNavigationWebResponse(
|
|
101
|
+
req: http.IncomingMessage,
|
|
102
|
+
res: http.ServerResponse,
|
|
103
|
+
response: Response
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
res.statusCode =
|
|
106
|
+
response.status;
|
|
107
|
+
|
|
108
|
+
response.headers.forEach(
|
|
109
|
+
(
|
|
110
|
+
value,
|
|
111
|
+
key
|
|
112
|
+
) => {
|
|
113
|
+
const normalized =
|
|
114
|
+
key.toLowerCase();
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
normalized ===
|
|
118
|
+
"set-cookie" ||
|
|
119
|
+
normalized ===
|
|
120
|
+
"content-length" ||
|
|
121
|
+
normalized ===
|
|
122
|
+
"content-encoding"
|
|
123
|
+
) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
res.setHeader(
|
|
128
|
+
key,
|
|
129
|
+
value
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const cookies =
|
|
135
|
+
getSetCookieHeaders(
|
|
136
|
+
response.headers
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (
|
|
140
|
+
cookies.length > 0
|
|
141
|
+
) {
|
|
142
|
+
res.setHeader(
|
|
143
|
+
"Set-Cookie",
|
|
144
|
+
cookies
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (
|
|
149
|
+
response.status === 204 ||
|
|
150
|
+
response.status === 304
|
|
151
|
+
) {
|
|
152
|
+
res.end();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const rawBody =
|
|
157
|
+
Buffer.from(
|
|
158
|
+
await response.arrayBuffer()
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const contentType =
|
|
162
|
+
response.headers.get(
|
|
163
|
+
"content-type"
|
|
164
|
+
) ??
|
|
165
|
+
"application/octet-stream";
|
|
166
|
+
|
|
167
|
+
const existingEncoding =
|
|
168
|
+
response.headers.get(
|
|
169
|
+
"content-encoding"
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const encoding =
|
|
173
|
+
existingEncoding
|
|
174
|
+
? null
|
|
175
|
+
: selectContentEncoding(
|
|
176
|
+
req,
|
|
177
|
+
contentType,
|
|
178
|
+
rawBody.length
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const body =
|
|
182
|
+
existingEncoding
|
|
183
|
+
? rawBody
|
|
184
|
+
: compressDynamicContent(
|
|
185
|
+
rawBody,
|
|
186
|
+
encoding
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (existingEncoding) {
|
|
190
|
+
res.setHeader(
|
|
191
|
+
"Content-Encoding",
|
|
192
|
+
existingEncoding
|
|
193
|
+
);
|
|
194
|
+
} else if (encoding) {
|
|
195
|
+
res.setHeader(
|
|
196
|
+
"Content-Encoding",
|
|
197
|
+
encoding
|
|
198
|
+
);
|
|
199
|
+
res.setHeader(
|
|
200
|
+
"Vary",
|
|
201
|
+
appendVary(
|
|
202
|
+
res.getHeader(
|
|
203
|
+
"Vary"
|
|
204
|
+
)?.toString(),
|
|
205
|
+
"Accept-Encoding"
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
res.setHeader(
|
|
211
|
+
"Content-Length",
|
|
212
|
+
body.length
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
if (
|
|
216
|
+
(
|
|
217
|
+
req.method ??
|
|
218
|
+
"GET"
|
|
219
|
+
).toUpperCase() ===
|
|
220
|
+
"HEAD"
|
|
221
|
+
) {
|
|
222
|
+
res.end();
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
res.end(
|
|
227
|
+
body
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function copySetCookieHeaders(
|
|
232
|
+
source: Headers,
|
|
233
|
+
target: Headers
|
|
234
|
+
): void {
|
|
235
|
+
for (
|
|
236
|
+
const cookie
|
|
237
|
+
of getSetCookieHeaders(
|
|
238
|
+
source
|
|
239
|
+
)
|
|
240
|
+
) {
|
|
241
|
+
target.append(
|
|
242
|
+
"Set-Cookie",
|
|
243
|
+
cookie
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function getSetCookieHeaders(
|
|
249
|
+
headers: Headers
|
|
250
|
+
): string[] {
|
|
251
|
+
const extended =
|
|
252
|
+
headers as Headers & {
|
|
253
|
+
getSetCookie?: () =>
|
|
254
|
+
string[];
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const values =
|
|
258
|
+
extended.getSetCookie?.();
|
|
259
|
+
|
|
260
|
+
if (values) {
|
|
261
|
+
return values;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const fallback =
|
|
265
|
+
headers.get(
|
|
266
|
+
"set-cookie"
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
return fallback
|
|
270
|
+
? [fallback]
|
|
271
|
+
: [];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function isRedirectStatus(
|
|
275
|
+
status: number
|
|
276
|
+
): boolean {
|
|
277
|
+
return (
|
|
278
|
+
status === 301 ||
|
|
279
|
+
status === 302 ||
|
|
280
|
+
status === 303 ||
|
|
281
|
+
status === 307 ||
|
|
282
|
+
status === 308
|
|
283
|
+
);
|
|
284
|
+
}
|