@chidchanun/bcp 0.1.8 → 0.1.10

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.
Files changed (29) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +218 -7
  3. package/docs/application-modules.md +63 -3
  4. package/docs/releasing.md +34 -19
  5. package/docs/route-guards.md +240 -0
  6. package/docs/server-data-loaders.md +240 -0
  7. package/docs/updating.md +130 -0
  8. package/package.json +1 -1
  9. package/packages/bundler/src/server-production-guards.ts +497 -0
  10. package/packages/bundler/src/server-production-middleware.ts +33 -8
  11. package/packages/bundler/src/server-production.ts +25 -0
  12. package/packages/cli/src/args.ts +58 -0
  13. package/packages/cli/src/index.ts +41 -13
  14. package/packages/cli/src/update.ts +860 -0
  15. package/packages/client/src/index.tsx +5 -0
  16. package/packages/client/src/loader-data.tsx +229 -0
  17. package/packages/client/src/router-v2.tsx +254 -29
  18. package/packages/server/src/dev-navigation-target.ts +188 -0
  19. package/packages/server/src/index.ts +209 -119
  20. package/packages/server/src/navigation-payload.ts +24 -1
  21. package/packages/server/src/navigation-response.ts +284 -0
  22. package/packages/server/src/page-guard.ts +529 -0
  23. package/packages/server/src/page-loader.ts +643 -0
  24. package/packages/server/src/standalone-production-runtime-v2-guard.ts +815 -0
  25. package/packages/server/src/standalone-production-runtime-v2-navigation.ts +785 -0
  26. package/packages/server/src/standalone-production-runtime-v2.ts +131 -19
  27. package/packages/server/src/standalone-production-runtime-v3.ts +1 -1
  28. package/packages/server/src/standalone-production-runtime-v4.ts +42 -2
  29. package/packages/server/src/static-dev-server.ts +21 -17
@@ -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
+ }