@chidchanun/bcp 0.1.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +241 -0
  4. package/docs/caching.md +76 -0
  5. package/docs/configuration.md +97 -0
  6. package/docs/deployment.md +74 -0
  7. package/docs/getting-started.md +82 -0
  8. package/docs/middleware.md +58 -0
  9. package/docs/releasing.md +299 -0
  10. package/docs/routing.md +103 -0
  11. package/docs/security.md +57 -0
  12. package/package.json +68 -0
  13. package/packages/bundler/src/client-islands.ts +1457 -0
  14. package/packages/bundler/src/incremental-context.ts +206 -0
  15. package/packages/bundler/src/index.ts +1991 -0
  16. package/packages/bundler/src/module-graph.ts +317 -0
  17. package/packages/bundler/src/partial-hydration.ts +414 -0
  18. package/packages/bundler/src/production.ts +974 -0
  19. package/packages/bundler/src/server-production-middleware.ts +447 -0
  20. package/packages/bundler/src/server-production.ts +1193 -0
  21. package/packages/bundler/src/special-files.ts +131 -0
  22. package/packages/cache/src/index.ts +761 -0
  23. package/packages/cli/bin/bcp.mjs +93 -0
  24. package/packages/cli/src/args.ts +305 -0
  25. package/packages/cli/src/bootstrap.ts +514 -0
  26. package/packages/cli/src/index.ts +504 -0
  27. package/packages/cli/src/version.ts +45 -0
  28. package/packages/client/src/cache.ts +11 -0
  29. package/packages/client/src/config.ts +18 -0
  30. package/packages/client/src/error-boundary.tsx +149 -0
  31. package/packages/client/src/hydration.ts +3 -0
  32. package/packages/client/src/index.tsx +57 -0
  33. package/packages/client/src/islands.tsx +315 -0
  34. package/packages/client/src/metadata.ts +281 -0
  35. package/packages/client/src/navigation-loading.ts +52 -0
  36. package/packages/client/src/navigation-state.ts +80 -0
  37. package/packages/client/src/not-found.ts +34 -0
  38. package/packages/client/src/persistent-layout-runtime.ts +273 -0
  39. package/packages/client/src/router-v2.tsx +969 -0
  40. package/packages/client/src/router.tsx +1 -0
  41. package/packages/config/src/index.ts +1038 -0
  42. package/packages/env/src/index.ts +593 -0
  43. package/packages/router/src/advanced-router.ts +1032 -0
  44. package/packages/router/src/index.ts +1 -0
  45. package/packages/server/src/compression.ts +249 -0
  46. package/packages/server/src/dev-document-metadata.ts +154 -0
  47. package/packages/server/src/dev-hmr.ts +225 -0
  48. package/packages/server/src/index.ts +2265 -0
  49. package/packages/server/src/metadata.ts +478 -0
  50. package/packages/server/src/middleware-dev-server.ts +260 -0
  51. package/packages/server/src/middleware-loader.ts +140 -0
  52. package/packages/server/src/middleware-proxy.ts +516 -0
  53. package/packages/server/src/middleware.ts +704 -0
  54. package/packages/server/src/navigation-payload.ts +471 -0
  55. package/packages/server/src/production-server.ts +1746 -0
  56. package/packages/server/src/response-cache-proxy.ts +828 -0
  57. package/packages/server/src/security-proxy.ts +406 -0
  58. package/packages/server/src/security.ts +451 -0
  59. package/packages/server/src/standalone-production-runtime-v2.ts +2047 -0
  60. package/packages/server/src/standalone-production-runtime-v3.ts +250 -0
  61. package/packages/server/src/standalone-production-runtime-v4.ts +289 -0
  62. package/packages/server/src/standalone-production-runtime-v5.ts +289 -0
  63. package/packages/server/src/standalone-production-runtime.ts +1951 -0
  64. package/packages/server/src/standalone-production-server.ts +6 -0
  65. package/packages/server/src/static-assets.ts +262 -0
  66. package/packages/server/src/static-dev-server.ts +854 -0
@@ -0,0 +1,250 @@
1
+ import fs from "node:fs";
2
+ import * as net from "node:net";
3
+ import path from "node:path";
4
+ import {
5
+ pathToFileURL,
6
+ } from "node:url";
7
+
8
+ import {
9
+ assertMiddlewareModule,
10
+ } from "./middleware-loader.js";
11
+
12
+ import {
13
+ createMiddlewareProxy,
14
+ } from "./middleware-proxy.js";
15
+
16
+ import {
17
+ createStandaloneProductionServer as createBaseStandaloneProductionServer,
18
+ type StandaloneApiRoute,
19
+ type StandalonePageRoute,
20
+ type StandaloneProductionServerOptions as BaseStandaloneProductionServerOptions,
21
+ } from "./standalone-production-runtime-v2.js";
22
+
23
+ import type {
24
+ MiddlewareModule,
25
+ } from "./middleware.js";
26
+
27
+ export type {
28
+ StandaloneApiRoute,
29
+ StandalonePageRoute,
30
+ };
31
+
32
+ export interface StandaloneProductionServerOptions
33
+ extends BaseStandaloneProductionServerOptions {}
34
+
35
+ export function createStandaloneProductionServer(
36
+ options: StandaloneProductionServerOptions
37
+ ) {
38
+ let baseServer:
39
+ ReturnType<
40
+ typeof createBaseStandaloneProductionServer
41
+ > | null =
42
+ null;
43
+
44
+ let middlewareProxy:
45
+ ReturnType<
46
+ typeof createMiddlewareProxy
47
+ > | null =
48
+ null;
49
+
50
+ let started =
51
+ false;
52
+
53
+ async function start(): Promise<void> {
54
+ if (started) {
55
+ return;
56
+ }
57
+
58
+ const middlewareFile =
59
+ path.join(
60
+ options.buildDirectory,
61
+ "server",
62
+ "middleware.mjs"
63
+ );
64
+
65
+ if (
66
+ !fs.existsSync(
67
+ middlewareFile
68
+ )
69
+ ) {
70
+ baseServer =
71
+ createBaseStandaloneProductionServer(
72
+ options
73
+ );
74
+ await baseServer.start();
75
+ started =
76
+ true;
77
+ return;
78
+ }
79
+
80
+ const module =
81
+ await import(
82
+ pathToFileURL(
83
+ middlewareFile
84
+ ).href
85
+ ) as MiddlewareModule;
86
+
87
+ assertMiddlewareModule(
88
+ module,
89
+ middlewareFile
90
+ );
91
+
92
+ const internalPort =
93
+ await findFreePort();
94
+
95
+ baseServer =
96
+ createBaseStandaloneProductionServer({
97
+ ...options,
98
+ port:
99
+ internalPort,
100
+ hostname:
101
+ "127.0.0.1",
102
+ });
103
+
104
+ await baseServer.start();
105
+
106
+ middlewareProxy =
107
+ createMiddlewareProxy({
108
+ port:
109
+ options.port,
110
+ hostname:
111
+ options.hostname,
112
+ upstreamPort:
113
+ internalPort,
114
+ upstreamHostname:
115
+ "127.0.0.1",
116
+ getMiddleware:
117
+ () =>
118
+ module,
119
+ });
120
+
121
+ try {
122
+ await middlewareProxy.start();
123
+ } catch (error) {
124
+ await baseServer.stop();
125
+ baseServer =
126
+ null;
127
+ middlewareProxy =
128
+ null;
129
+ throw error;
130
+ }
131
+
132
+ started =
133
+ true;
134
+
135
+ console.log(
136
+ `[BCP Middleware] Production gateway: http://${options.hostname}:${options.port}`
137
+ );
138
+ console.log(
139
+ `[BCP Middleware] Standalone module: ${middlewareFile}`
140
+ );
141
+ console.log("");
142
+ }
143
+
144
+ async function stop(): Promise<void> {
145
+ if (middlewareProxy) {
146
+ await middlewareProxy.stop();
147
+ middlewareProxy =
148
+ null;
149
+ }
150
+
151
+ if (baseServer) {
152
+ await baseServer.stop();
153
+ baseServer =
154
+ null;
155
+ }
156
+
157
+ started =
158
+ false;
159
+ }
160
+
161
+ return {
162
+ start,
163
+ stop,
164
+ };
165
+ }
166
+
167
+ async function findFreePort(): Promise<number> {
168
+ const server =
169
+ net.createServer();
170
+
171
+ try {
172
+ await new Promise<void>(
173
+ (
174
+ resolve,
175
+ reject
176
+ ) => {
177
+ const onError =
178
+ (error: Error) => {
179
+ server.off(
180
+ "listening",
181
+ onListening
182
+ );
183
+ reject(
184
+ error
185
+ );
186
+ };
187
+
188
+ const onListening =
189
+ () => {
190
+ server.off(
191
+ "error",
192
+ onError
193
+ );
194
+ resolve();
195
+ };
196
+
197
+ server.once(
198
+ "error",
199
+ onError
200
+ );
201
+ server.once(
202
+ "listening",
203
+ onListening
204
+ );
205
+ server.listen(
206
+ 0,
207
+ "127.0.0.1"
208
+ );
209
+ }
210
+ );
211
+
212
+ const address =
213
+ server.address();
214
+
215
+ if (
216
+ !address ||
217
+ typeof address ===
218
+ "string"
219
+ ) {
220
+ throw new Error(
221
+ "BCP Framework: could not allocate middleware production port."
222
+ );
223
+ }
224
+
225
+ return address.port;
226
+ } finally {
227
+ if (
228
+ server.listening
229
+ ) {
230
+ await new Promise<void>(
231
+ (
232
+ resolve,
233
+ reject
234
+ ) => {
235
+ server.close(
236
+ (error) => {
237
+ if (error) {
238
+ reject(
239
+ error
240
+ );
241
+ return;
242
+ }
243
+ resolve();
244
+ }
245
+ );
246
+ }
247
+ );
248
+ }
249
+ }
250
+ }
@@ -0,0 +1,289 @@
1
+ import fs from "node:fs";
2
+ import * as net from "node:net";
3
+ import path from "node:path";
4
+
5
+ import {
6
+ createResponseCacheProxy,
7
+ type ResponseCacheManifest,
8
+ } from "./response-cache-proxy.js";
9
+
10
+ import {
11
+ createStandaloneProductionServer as createBaseStandaloneProductionServer,
12
+ type StandaloneApiRoute,
13
+ type StandalonePageRoute,
14
+ type StandaloneProductionServerOptions as BaseStandaloneProductionServerOptions,
15
+ } from "./standalone-production-runtime-v3.js";
16
+
17
+ export type {
18
+ StandaloneApiRoute,
19
+ StandalonePageRoute,
20
+ };
21
+
22
+ export interface StandaloneProductionServerOptions
23
+ extends BaseStandaloneProductionServerOptions {}
24
+
25
+ export function createStandaloneProductionServer(
26
+ options: StandaloneProductionServerOptions
27
+ ) {
28
+ let baseServer:
29
+ ReturnType<
30
+ typeof createBaseStandaloneProductionServer
31
+ > | null =
32
+ null;
33
+
34
+ let cacheProxy:
35
+ ReturnType<
36
+ typeof createResponseCacheProxy
37
+ > | null =
38
+ null;
39
+
40
+ let started =
41
+ false;
42
+
43
+ async function start(): Promise<void> {
44
+ if (started) {
45
+ return;
46
+ }
47
+
48
+ const manifest =
49
+ readCacheManifest(
50
+ options.buildDirectory
51
+ );
52
+
53
+ const responseCacheEnabled =
54
+ process.env
55
+ .BCP_RESPONSE_CACHE !==
56
+ "0";
57
+
58
+ const hasCacheRoutes =
59
+ responseCacheEnabled &&
60
+ (
61
+ manifest.pages.length > 0 ||
62
+ manifest.apiRoutes.length > 0
63
+ );
64
+
65
+ if (!hasCacheRoutes) {
66
+ baseServer =
67
+ createBaseStandaloneProductionServer(
68
+ options
69
+ );
70
+ await baseServer.start();
71
+ started =
72
+ true;
73
+
74
+ if (
75
+ !responseCacheEnabled
76
+ ) {
77
+ console.log(
78
+ "[BCP Cache] Production response cache disabled by configuration."
79
+ );
80
+ console.log("");
81
+ }
82
+
83
+ return;
84
+ }
85
+
86
+ const internalPort =
87
+ await findFreePort();
88
+
89
+ baseServer =
90
+ createBaseStandaloneProductionServer({
91
+ ...options,
92
+ port:
93
+ internalPort,
94
+ hostname:
95
+ "127.0.0.1",
96
+ });
97
+
98
+ await baseServer.start();
99
+
100
+ cacheProxy =
101
+ createResponseCacheProxy({
102
+ port:
103
+ options.port,
104
+ hostname:
105
+ options.hostname,
106
+ upstreamPort:
107
+ internalPort,
108
+ upstreamHostname:
109
+ "127.0.0.1",
110
+ manifest,
111
+ });
112
+
113
+ try {
114
+ await cacheProxy.start();
115
+ } catch (error) {
116
+ await baseServer.stop();
117
+ baseServer =
118
+ null;
119
+ cacheProxy =
120
+ null;
121
+ throw error;
122
+ }
123
+
124
+ started =
125
+ true;
126
+
127
+ console.log(
128
+ `[BCP Cache] Production response cache: ${manifest.pages.length} page(s), ${manifest.apiRoutes.length} API route(s)`
129
+ );
130
+ console.log(
131
+ `[BCP Cache] Gateway: http://${options.hostname}:${options.port}`
132
+ );
133
+ console.log("");
134
+ }
135
+
136
+ async function stop(): Promise<void> {
137
+ if (cacheProxy) {
138
+ await cacheProxy.stop();
139
+ cacheProxy =
140
+ null;
141
+ }
142
+
143
+ if (baseServer) {
144
+ await baseServer.stop();
145
+ baseServer =
146
+ null;
147
+ }
148
+
149
+ started =
150
+ false;
151
+ }
152
+
153
+ return {
154
+ start,
155
+ stop,
156
+ };
157
+ }
158
+
159
+ function readCacheManifest(
160
+ buildDirectory: string
161
+ ): ResponseCacheManifest {
162
+ const filePath =
163
+ path.join(
164
+ buildDirectory,
165
+ "server",
166
+ "cache-manifest.json"
167
+ );
168
+
169
+ if (
170
+ !fs.existsSync(
171
+ filePath
172
+ )
173
+ ) {
174
+ return {
175
+ version: 1,
176
+ pages: [],
177
+ apiRoutes: [],
178
+ };
179
+ }
180
+
181
+ const parsed =
182
+ JSON.parse(
183
+ fs.readFileSync(
184
+ filePath,
185
+ "utf8"
186
+ )
187
+ ) as Partial<ResponseCacheManifest>;
188
+
189
+ if (
190
+ parsed.version !== 1 ||
191
+ !Array.isArray(
192
+ parsed.pages
193
+ ) ||
194
+ !Array.isArray(
195
+ parsed.apiRoutes
196
+ )
197
+ ) {
198
+ throw new Error(
199
+ `BCP Framework: invalid cache manifest at "${filePath}".`
200
+ );
201
+ }
202
+
203
+ return parsed as ResponseCacheManifest;
204
+ }
205
+
206
+ async function findFreePort(): Promise<number> {
207
+ const server =
208
+ net.createServer();
209
+
210
+ try {
211
+ await new Promise<void>(
212
+ (
213
+ resolve,
214
+ reject
215
+ ) => {
216
+ const onError =
217
+ (error: Error) => {
218
+ server.off(
219
+ "listening",
220
+ onListening
221
+ );
222
+ reject(
223
+ error
224
+ );
225
+ };
226
+
227
+ const onListening =
228
+ () => {
229
+ server.off(
230
+ "error",
231
+ onError
232
+ );
233
+ resolve();
234
+ };
235
+
236
+ server.once(
237
+ "error",
238
+ onError
239
+ );
240
+ server.once(
241
+ "listening",
242
+ onListening
243
+ );
244
+ server.listen(
245
+ 0,
246
+ "127.0.0.1"
247
+ );
248
+ }
249
+ );
250
+
251
+ const address =
252
+ server.address();
253
+
254
+ if (
255
+ !address ||
256
+ typeof address ===
257
+ "string"
258
+ ) {
259
+ throw new Error(
260
+ "BCP Framework: could not allocate production cache port."
261
+ );
262
+ }
263
+
264
+ return address.port;
265
+ } finally {
266
+ if (
267
+ server.listening
268
+ ) {
269
+ await new Promise<void>(
270
+ (
271
+ resolve,
272
+ reject
273
+ ) => {
274
+ server.close(
275
+ (error) => {
276
+ if (error) {
277
+ reject(
278
+ error
279
+ );
280
+ return;
281
+ }
282
+ resolve();
283
+ }
284
+ );
285
+ }
286
+ );
287
+ }
288
+ }
289
+ }