@chidchanun/bcp 0.1.5 → 0.1.7

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.
@@ -0,0 +1,82 @@
1
+ import {
2
+ requestUrl,
3
+ } from "./request-context.js";
4
+
5
+ export type RedirectStatus =
6
+ | 301
7
+ | 302
8
+ | 303
9
+ | 307
10
+ | 308;
11
+
12
+ export function json<T>(
13
+ data: T,
14
+ init?: ResponseInit
15
+ ): Response {
16
+ return Response.json(
17
+ data,
18
+ init
19
+ );
20
+ }
21
+
22
+ export async function redirect(
23
+ destination:
24
+ string |
25
+ URL,
26
+ status:
27
+ RedirectStatus =
28
+ 307
29
+ ): Promise<Response> {
30
+ assertRedirectStatus(
31
+ status
32
+ );
33
+
34
+ const base =
35
+ await requestUrl();
36
+
37
+ const target =
38
+ destination instanceof URL
39
+ ? new URL(
40
+ destination.href
41
+ )
42
+ : new URL(
43
+ destination,
44
+ base
45
+ );
46
+
47
+ if (
48
+ target.protocol !== "http:" &&
49
+ target.protocol !== "https:"
50
+ ) {
51
+ throw new Error(
52
+ `BCP Framework: redirect only supports http and https URLs, received "${target.protocol}".`
53
+ );
54
+ }
55
+
56
+ return new Response(
57
+ null,
58
+ {
59
+ status,
60
+ headers: {
61
+ Location:
62
+ target.href,
63
+ },
64
+ }
65
+ );
66
+ }
67
+
68
+ function assertRedirectStatus(
69
+ status: number
70
+ ): asserts status is RedirectStatus {
71
+ if (
72
+ status !== 301 &&
73
+ status !== 302 &&
74
+ status !== 303 &&
75
+ status !== 307 &&
76
+ status !== 308
77
+ ) {
78
+ throw new Error(
79
+ `BCP Framework: invalid redirect status ${status}. Use 301, 302, 303, 307 or 308.`
80
+ );
81
+ }
82
+ }
@@ -49,6 +49,11 @@ import {
49
49
  type MetadataSource,
50
50
  } from "./metadata.js";
51
51
 
52
+ import {
53
+ applyResponseCookies,
54
+ runWithRequestContext,
55
+ } from "./request-context.ts";
56
+
52
57
  import {
53
58
  getContentType,
54
59
  readPublicAsset,
@@ -1405,7 +1410,8 @@ async function handleApiRoute(
1405
1410
 
1406
1411
  if (
1407
1412
  !handler &&
1408
- method === "HEAD"
1413
+ method ===
1414
+ "HEAD"
1409
1415
  ) {
1410
1416
  handler =
1411
1417
  getApiHandler(
@@ -1434,23 +1440,48 @@ async function handleApiRoute(
1434
1440
  return;
1435
1441
  }
1436
1442
 
1443
+ const request =
1444
+ await createWebRequest(
1445
+ req,
1446
+ requestUrl
1447
+ );
1448
+
1437
1449
  const response =
1438
- await handler(
1439
- await createWebRequest(
1440
- req,
1441
- requestUrl
1442
- ),
1450
+ await runWithRequestContext(
1451
+ request,
1452
+
1453
+ async () => {
1454
+ const result =
1455
+ await handler(
1456
+ request,
1457
+ {
1458
+ params,
1459
+ }
1460
+ );
1461
+
1462
+ if (
1463
+ !(
1464
+ result instanceof
1465
+ Response
1466
+ )
1467
+ ) {
1468
+ throw new Error(
1469
+ `API handler "${method} ${definition.pathname}" must return a Response object.`
1470
+ );
1471
+ }
1472
+
1473
+ return applyResponseCookies(
1474
+ result
1475
+ );
1476
+ },
1443
1477
  {
1444
- params,
1478
+ remoteAddress:
1479
+ req.socket
1480
+ .remoteAddress ??
1481
+ null,
1445
1482
  }
1446
1483
  );
1447
1484
 
1448
- if (!(response instanceof Response)) {
1449
- throw new Error(
1450
- `API handler "${method} ${definition.pathname}" must return a Response object.`
1451
- );
1452
- }
1453
-
1454
1485
  await sendWebResponse(
1455
1486
  req,
1456
1487
  res,
@@ -0,0 +1,205 @@
1
+ import * as net from "node:net";
2
+ import path from "node:path";
3
+
4
+ import {
5
+ createCriticalCssProxy,
6
+ } from "./critical-css-proxy.js";
7
+
8
+ import {
9
+ createStandaloneProductionServer as createBaseStandaloneProductionServer,
10
+ type StandaloneApiRoute,
11
+ type StandalonePageRoute,
12
+ type StandaloneProductionServerOptions as BaseStandaloneProductionServerOptions,
13
+ } from "./standalone-production-runtime-v5.js";
14
+
15
+ export type {
16
+ StandaloneApiRoute,
17
+ StandalonePageRoute,
18
+ };
19
+
20
+ export interface StandaloneProductionServerOptions
21
+ extends BaseStandaloneProductionServerOptions {}
22
+
23
+ export function createStandaloneProductionServer(
24
+ options: StandaloneProductionServerOptions
25
+ ) {
26
+ let baseServer:
27
+ ReturnType<
28
+ typeof createBaseStandaloneProductionServer
29
+ > | null =
30
+ null;
31
+
32
+ let criticalCssProxy:
33
+ ReturnType<
34
+ typeof createCriticalCssProxy
35
+ > | null =
36
+ null;
37
+
38
+ let started =
39
+ false;
40
+
41
+ async function start(): Promise<void> {
42
+ if (started) {
43
+ return;
44
+ }
45
+
46
+ const internalPort =
47
+ await findFreePort();
48
+
49
+ baseServer =
50
+ createBaseStandaloneProductionServer({
51
+ ...options,
52
+ port:
53
+ internalPort,
54
+ hostname:
55
+ "127.0.0.1",
56
+ });
57
+
58
+ await baseServer.start();
59
+
60
+ criticalCssProxy =
61
+ createCriticalCssProxy({
62
+ port:
63
+ options.port,
64
+ hostname:
65
+ options.hostname,
66
+ upstreamPort:
67
+ internalPort,
68
+ upstreamHostname:
69
+ "127.0.0.1",
70
+ cssFile:
71
+ path.join(
72
+ options.buildDirectory,
73
+ "public",
74
+ "bcp.css"
75
+ ),
76
+ });
77
+
78
+ try {
79
+ await criticalCssProxy.start();
80
+ } catch (error) {
81
+ await baseServer.stop();
82
+ baseServer =
83
+ null;
84
+ criticalCssProxy =
85
+ null;
86
+ throw error;
87
+ }
88
+
89
+ started =
90
+ true;
91
+
92
+ console.log(
93
+ `[BCP CSS] Inline /bcp.css when <= 8 KiB.`
94
+ );
95
+ console.log("");
96
+ }
97
+
98
+ async function stop(): Promise<void> {
99
+ if (criticalCssProxy) {
100
+ await criticalCssProxy.stop();
101
+ criticalCssProxy =
102
+ null;
103
+ }
104
+
105
+ if (baseServer) {
106
+ await baseServer.stop();
107
+ baseServer =
108
+ null;
109
+ }
110
+
111
+ started =
112
+ false;
113
+ }
114
+
115
+ return {
116
+ start,
117
+ stop,
118
+ };
119
+ }
120
+
121
+ async function findFreePort(): Promise<number> {
122
+ const server =
123
+ net.createServer();
124
+
125
+ try {
126
+ await new Promise<void>(
127
+ (
128
+ resolve,
129
+ reject
130
+ ) => {
131
+ const onError =
132
+ (error: Error) => {
133
+ server.off(
134
+ "listening",
135
+ onListening
136
+ );
137
+ reject(
138
+ error
139
+ );
140
+ };
141
+
142
+ const onListening =
143
+ () => {
144
+ server.off(
145
+ "error",
146
+ onError
147
+ );
148
+ resolve();
149
+ };
150
+
151
+ server.once(
152
+ "error",
153
+ onError
154
+ );
155
+ server.once(
156
+ "listening",
157
+ onListening
158
+ );
159
+ server.listen(
160
+ 0,
161
+ "127.0.0.1"
162
+ );
163
+ }
164
+ );
165
+
166
+ const address =
167
+ server.address();
168
+
169
+ if (
170
+ !address ||
171
+ typeof address ===
172
+ "string"
173
+ ) {
174
+ throw new Error(
175
+ "BCP Framework: could not allocate critical CSS production port."
176
+ );
177
+ }
178
+
179
+ return address.port;
180
+ } finally {
181
+ if (
182
+ server.listening
183
+ ) {
184
+ await new Promise<void>(
185
+ (
186
+ resolve,
187
+ reject
188
+ ) => {
189
+ server.close(
190
+ (error) => {
191
+ if (error) {
192
+ reject(
193
+ error
194
+ );
195
+ return;
196
+ }
197
+
198
+ resolve();
199
+ }
200
+ );
201
+ }
202
+ );
203
+ }
204
+ }
205
+ }
@@ -3,4 +3,4 @@ export {
3
3
  type StandaloneApiRoute,
4
4
  type StandalonePageRoute,
5
5
  type StandaloneProductionServerOptions,
6
- } from "./standalone-production-runtime-v5.js";
6
+ } from "./standalone-production-runtime-v6.js";