@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,514 @@
1
+ import {
2
+ spawn,
3
+ type ChildProcess,
4
+ } from "node:child_process";
5
+ import path from "node:path";
6
+ import {
7
+ fileURLToPath,
8
+ } from "node:url";
9
+
10
+ import chokidar from "chokidar";
11
+
12
+ import {
13
+ applyResolvedBcpConfig,
14
+ getConfigFileNames,
15
+ resolveBcpConfig,
16
+ } from "../../config/src/index.js";
17
+
18
+ import {
19
+ getEnvironmentFileNames,
20
+ loadEnvironment,
21
+ type EnvironmentMode,
22
+ } from "../../env/src/index.js";
23
+
24
+ import {
25
+ parseCliArgs,
26
+ resolveProjectRoot,
27
+ type CliOptions,
28
+ } from "./args.js";
29
+
30
+ const options =
31
+ parseCliArgs(
32
+ process.argv.slice(2)
33
+ );
34
+
35
+ if (
36
+ options.command === "dev" &&
37
+ process.env.BCP_DEV_CHILD !== "1"
38
+ ) {
39
+ await runDevSupervisor(
40
+ options
41
+ );
42
+ } else {
43
+ await runCli(
44
+ options
45
+ );
46
+ }
47
+
48
+ async function runCli(
49
+ cliOptions: CliOptions
50
+ ): Promise<void> {
51
+ const mode =
52
+ resolveEnvironmentMode(
53
+ cliOptions.command
54
+ );
55
+
56
+ if (mode) {
57
+ process.env.NODE_ENV =
58
+ mode;
59
+
60
+ const rootDirectory =
61
+ resolveProjectRoot(
62
+ cliOptions.rootDirectory
63
+ );
64
+
65
+ const result =
66
+ loadEnvironment(
67
+ rootDirectory,
68
+ mode
69
+ );
70
+
71
+ if (
72
+ result.files.length > 0
73
+ ) {
74
+ console.log(
75
+ `[BCP Env] ${mode}: ${result.files.join(", ")}`
76
+ );
77
+ }
78
+
79
+ if (
80
+ Object.keys(
81
+ result.publicValues
82
+ ).length > 0
83
+ ) {
84
+ console.log(
85
+ `[BCP Env] Public variables: ${Object.keys(result.publicValues).sort().join(", ")}`
86
+ );
87
+ }
88
+
89
+ /*
90
+ * A standalone start uses the configuration frozen at build time.
91
+ * Runtime BCP_* environment variables and CLI server overrides stay
92
+ * available to server.mjs, but source bcp.config.* must not replace
93
+ * build-dependent settings after the artifact has been created.
94
+ */
95
+ if (
96
+ cliOptions.command !==
97
+ "start"
98
+ ) {
99
+ const resolved =
100
+ await resolveBcpConfig(
101
+ rootDirectory,
102
+ {
103
+ port:
104
+ cliOptions.port,
105
+ hostname:
106
+ cliOptions.hostname,
107
+ }
108
+ );
109
+
110
+ applyResolvedBcpConfig(
111
+ resolved.config
112
+ );
113
+
114
+ if (
115
+ resolved.file
116
+ ) {
117
+ console.log(
118
+ `[BCP Config] Loaded: ${path.basename(resolved.file)}`
119
+ );
120
+ }
121
+
122
+ console.log(
123
+ `[BCP Config] Server: ${resolved.config.server.hostname}:${resolved.config.server.port}`
124
+ );
125
+ }
126
+ }
127
+
128
+ await import(
129
+ "./index.js"
130
+ );
131
+ }
132
+
133
+ async function runDevSupervisor(
134
+ cliOptions: CliOptions
135
+ ): Promise<void> {
136
+ const rootDirectory =
137
+ resolveProjectRoot(
138
+ cliOptions.rootDirectory
139
+ );
140
+
141
+ const envFiles =
142
+ getEnvironmentFileNames(
143
+ "development"
144
+ ).map(
145
+ (fileName) =>
146
+ path.join(
147
+ rootDirectory,
148
+ fileName
149
+ )
150
+ );
151
+
152
+ const configFiles =
153
+ getConfigFileNames()
154
+ .map(
155
+ (fileName) =>
156
+ path.join(
157
+ rootDirectory,
158
+ fileName
159
+ )
160
+ );
161
+
162
+ const watchedFiles = [
163
+ ...envFiles,
164
+ ...configFiles,
165
+ ];
166
+
167
+ const bootstrapFile =
168
+ fileURLToPath(
169
+ import.meta.url
170
+ );
171
+
172
+ const tsxImport =
173
+ import.meta.resolve(
174
+ "tsx"
175
+ );
176
+
177
+ let child:
178
+ ChildProcess | null =
179
+ null;
180
+
181
+ let stopping =
182
+ false;
183
+
184
+ let restarting =
185
+ false;
186
+
187
+ let restartQueued =
188
+ false;
189
+
190
+ let restartTimer:
191
+ NodeJS.Timeout | null =
192
+ null;
193
+
194
+ let restartGeneration =
195
+ 0;
196
+
197
+ let resolveSupervisor:
198
+ (() => void) | null =
199
+ null;
200
+
201
+ const supervisorDone =
202
+ new Promise<void>(
203
+ (resolve) => {
204
+ resolveSupervisor =
205
+ resolve;
206
+ }
207
+ );
208
+
209
+ const watcher =
210
+ chokidar.watch(
211
+ watchedFiles,
212
+ {
213
+ persistent: true,
214
+ ignoreInitial: true,
215
+ atomic: true,
216
+ }
217
+ );
218
+
219
+ function startChild(
220
+ configRestart: boolean
221
+ ): void {
222
+ const restartToken =
223
+ configRestart
224
+ ? `${Date.now()}-${++restartGeneration}`
225
+ : "0";
226
+
227
+ child =
228
+ spawn(
229
+ process.execPath,
230
+ [
231
+ "--import",
232
+ tsxImport,
233
+ bootstrapFile,
234
+ ...process.argv.slice(2),
235
+ ],
236
+ {
237
+ cwd:
238
+ process.cwd(),
239
+ stdio:
240
+ "inherit",
241
+ env: {
242
+ ...process.env,
243
+ BCP_DEV_CHILD:
244
+ "1",
245
+ BCP_DEV_ENV_RESTART:
246
+ restartToken,
247
+ },
248
+ }
249
+ );
250
+
251
+ child.once(
252
+ "exit",
253
+ (
254
+ code,
255
+ signal
256
+ ) => {
257
+ child =
258
+ null;
259
+
260
+ if (
261
+ stopping ||
262
+ restarting
263
+ ) {
264
+ return;
265
+ }
266
+
267
+ if (signal) {
268
+ console.error(
269
+ `[BCP Dev] Worker stopped by ${signal}.`
270
+ );
271
+ }
272
+
273
+ void shutdown(
274
+ code ?? 1
275
+ );
276
+ }
277
+ );
278
+ }
279
+
280
+ async function stopChild(): Promise<void> {
281
+ const current =
282
+ child;
283
+
284
+ if (
285
+ !current ||
286
+ current.exitCode !== null ||
287
+ current.signalCode !== null
288
+ ) {
289
+ return;
290
+ }
291
+
292
+ await new Promise<void>(
293
+ (resolve) => {
294
+ let finished =
295
+ false;
296
+
297
+ const finish =
298
+ () => {
299
+ if (finished) {
300
+ return;
301
+ }
302
+
303
+ finished =
304
+ true;
305
+ resolve();
306
+ };
307
+
308
+ current.once(
309
+ "exit",
310
+ finish
311
+ );
312
+
313
+ current.kill(
314
+ "SIGTERM"
315
+ );
316
+
317
+ const forceTimer =
318
+ setTimeout(
319
+ () => {
320
+ if (
321
+ current.exitCode === null &&
322
+ current.signalCode === null
323
+ ) {
324
+ current.kill(
325
+ "SIGKILL"
326
+ );
327
+ }
328
+ },
329
+ 3000
330
+ );
331
+
332
+ forceTimer.unref();
333
+ }
334
+ );
335
+ }
336
+
337
+ async function restartChild(
338
+ changedPath: string
339
+ ): Promise<void> {
340
+ if (stopping) {
341
+ return;
342
+ }
343
+
344
+ if (restarting) {
345
+ restartQueued =
346
+ true;
347
+ return;
348
+ }
349
+
350
+ restarting =
351
+ true;
352
+
353
+ try {
354
+ const fileName =
355
+ path.basename(
356
+ changedPath
357
+ );
358
+ const source =
359
+ fileName.startsWith(
360
+ "bcp.config."
361
+ )
362
+ ? "Config"
363
+ : "Env";
364
+
365
+ console.log("");
366
+ console.log(
367
+ `[BCP ${source}] ${fileName} changed; restarting dev worker...`
368
+ );
369
+
370
+ await stopChild();
371
+
372
+ if (!stopping) {
373
+ startChild(
374
+ true
375
+ );
376
+ }
377
+ } finally {
378
+ restarting =
379
+ false;
380
+
381
+ if (
382
+ restartQueued &&
383
+ !stopping
384
+ ) {
385
+ restartQueued =
386
+ false;
387
+
388
+ await restartChild(
389
+ changedPath
390
+ );
391
+ }
392
+ }
393
+ }
394
+
395
+ function scheduleRestart(
396
+ changedPath: string
397
+ ): void {
398
+ if (restartTimer) {
399
+ clearTimeout(
400
+ restartTimer
401
+ );
402
+ }
403
+
404
+ restartTimer =
405
+ setTimeout(
406
+ () => {
407
+ restartTimer =
408
+ null;
409
+
410
+ void restartChild(
411
+ changedPath
412
+ );
413
+ },
414
+ 120
415
+ );
416
+ }
417
+
418
+ watcher.on(
419
+ "add",
420
+ scheduleRestart
421
+ );
422
+ watcher.on(
423
+ "change",
424
+ scheduleRestart
425
+ );
426
+ watcher.on(
427
+ "unlink",
428
+ scheduleRestart
429
+ );
430
+ watcher.on(
431
+ "error",
432
+ (error) => {
433
+ console.error(
434
+ "[BCP Dev] Configuration watcher error:",
435
+ error
436
+ );
437
+ }
438
+ );
439
+
440
+ async function shutdown(
441
+ exitCode = 0
442
+ ): Promise<void> {
443
+ if (stopping) {
444
+ return;
445
+ }
446
+
447
+ stopping =
448
+ true;
449
+
450
+ if (restartTimer) {
451
+ clearTimeout(
452
+ restartTimer
453
+ );
454
+ restartTimer =
455
+ null;
456
+ }
457
+
458
+ await watcher.close();
459
+ await stopChild();
460
+
461
+ process.exitCode =
462
+ exitCode;
463
+
464
+ resolveSupervisor?.();
465
+ resolveSupervisor =
466
+ null;
467
+ }
468
+
469
+ process.once(
470
+ "SIGINT",
471
+ () => {
472
+ void shutdown(0);
473
+ }
474
+ );
475
+
476
+ process.once(
477
+ "SIGTERM",
478
+ () => {
479
+ void shutdown(0);
480
+ }
481
+ );
482
+
483
+ console.log(
484
+ `[BCP Env] Watching: ${getEnvironmentFileNames("development").join(", ")}`
485
+ );
486
+ console.log(
487
+ `[BCP Config] Watching: ${getConfigFileNames().join(", ")}`
488
+ );
489
+
490
+ startChild(
491
+ false
492
+ );
493
+
494
+ await supervisorDone;
495
+ }
496
+
497
+ function resolveEnvironmentMode(
498
+ command: string
499
+ ): EnvironmentMode | null {
500
+ if (
501
+ command === "dev"
502
+ ) {
503
+ return "development";
504
+ }
505
+
506
+ if (
507
+ command === "build" ||
508
+ command === "start"
509
+ ) {
510
+ return "production";
511
+ }
512
+
513
+ return null;
514
+ }