@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,593 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ export type EnvironmentMode =
5
+ | "development"
6
+ | "production"
7
+ | "test"
8
+ | string;
9
+
10
+ export interface EnvironmentLoadResult {
11
+ mode: EnvironmentMode;
12
+ files: string[];
13
+ values: Record<string, string>;
14
+ publicValues: Record<string, string>;
15
+ }
16
+
17
+ interface EnvironmentState {
18
+ externalValues: Record<string, string | undefined>;
19
+ managedKeys: Set<string>;
20
+ }
21
+
22
+ const PUBLIC_PREFIX =
23
+ "BCP_PUBLIC_";
24
+
25
+ const environmentStates =
26
+ new Map<string, EnvironmentState>();
27
+
28
+ export function loadEnvironment(
29
+ rootDirectory: string,
30
+ mode: EnvironmentMode
31
+ ): EnvironmentLoadResult {
32
+ const root =
33
+ path.resolve(
34
+ rootDirectory
35
+ );
36
+
37
+ const stateKey =
38
+ `${normalizeStatePath(root)}\0${mode}`;
39
+
40
+ let state =
41
+ environmentStates.get(
42
+ stateKey
43
+ );
44
+
45
+ if (!state) {
46
+ state = {
47
+ externalValues:
48
+ snapshotProcessEnvironment(),
49
+ managedKeys:
50
+ new Set<string>(),
51
+ };
52
+
53
+ environmentStates.set(
54
+ stateKey,
55
+ state
56
+ );
57
+ } else {
58
+ restoreExternalEnvironment(
59
+ state
60
+ );
61
+ }
62
+
63
+ const merged:
64
+ Record<string, string> = {};
65
+
66
+ const loadedFiles:
67
+ string[] = [];
68
+
69
+ for (
70
+ const fileName
71
+ of getEnvironmentFileNames(
72
+ mode
73
+ )
74
+ ) {
75
+ const filePath =
76
+ path.join(
77
+ root,
78
+ fileName
79
+ );
80
+
81
+ if (
82
+ !fs.existsSync(
83
+ filePath
84
+ )
85
+ ) {
86
+ continue;
87
+ }
88
+
89
+ const stat =
90
+ fs.statSync(
91
+ filePath
92
+ );
93
+
94
+ if (!stat.isFile()) {
95
+ continue;
96
+ }
97
+
98
+ const parsed =
99
+ parseEnvironmentFile(
100
+ fs.readFileSync(
101
+ filePath,
102
+ "utf8"
103
+ ),
104
+ {
105
+ ...state.externalValues,
106
+ ...merged,
107
+ }
108
+ );
109
+
110
+ Object.assign(
111
+ merged,
112
+ parsed
113
+ );
114
+
115
+ loadedFiles.push(
116
+ fileName
117
+ );
118
+ }
119
+
120
+ const nextManagedKeys =
121
+ new Set<string>();
122
+
123
+ for (
124
+ const [
125
+ key,
126
+ value,
127
+ ]
128
+ of Object.entries(
129
+ merged
130
+ )
131
+ ) {
132
+ if (
133
+ state.externalValues[
134
+ key
135
+ ] !== undefined
136
+ ) {
137
+ continue;
138
+ }
139
+
140
+ process.env[
141
+ key
142
+ ] =
143
+ value;
144
+
145
+ nextManagedKeys.add(
146
+ key
147
+ );
148
+ }
149
+
150
+ state.managedKeys =
151
+ nextManagedKeys;
152
+
153
+ const values =
154
+ collectEnvironmentValues();
155
+
156
+ return {
157
+ mode,
158
+ files:
159
+ loadedFiles,
160
+ values,
161
+ publicValues:
162
+ getPublicEnvironment(
163
+ values
164
+ ),
165
+ };
166
+ }
167
+
168
+ export function getPublicEnvironment(
169
+ source:
170
+ NodeJS.ProcessEnv |
171
+ Record<string, string> =
172
+ process.env
173
+ ): Record<string, string> {
174
+ const result:
175
+ Record<string, string> = {};
176
+
177
+ for (
178
+ const [
179
+ key,
180
+ value,
181
+ ]
182
+ of Object.entries(
183
+ source
184
+ )
185
+ ) {
186
+ if (
187
+ !key.startsWith(
188
+ PUBLIC_PREFIX
189
+ ) ||
190
+ value === undefined
191
+ ) {
192
+ continue;
193
+ }
194
+
195
+ result[
196
+ key
197
+ ] =
198
+ value;
199
+ }
200
+
201
+ return result;
202
+ }
203
+
204
+ export function createClientEnvironmentDefines(
205
+ mode: "development" | "production"
206
+ ): Record<string, string> {
207
+ const publicValues =
208
+ getPublicEnvironment();
209
+
210
+ const safeEnvironment = {
211
+ NODE_ENV:
212
+ mode,
213
+ ...publicValues,
214
+ };
215
+
216
+ const defines:
217
+ Record<string, string> = {
218
+ "process.env":
219
+ JSON.stringify(
220
+ safeEnvironment
221
+ ),
222
+ "process.env.NODE_ENV":
223
+ JSON.stringify(
224
+ mode
225
+ ),
226
+ };
227
+
228
+ for (
229
+ const [
230
+ key,
231
+ value,
232
+ ]
233
+ of Object.entries(
234
+ publicValues
235
+ )
236
+ ) {
237
+ defines[
238
+ `process.env.${key}`
239
+ ] =
240
+ JSON.stringify(
241
+ value
242
+ );
243
+ }
244
+
245
+ return defines;
246
+ }
247
+
248
+ export function createServerEnvironmentDefines(
249
+ mode: "development" | "production"
250
+ ): Record<string, string> {
251
+ const defines:
252
+ Record<string, string> = {
253
+ "process.env.NODE_ENV":
254
+ JSON.stringify(
255
+ mode
256
+ ),
257
+ };
258
+
259
+ for (
260
+ const [
261
+ key,
262
+ value,
263
+ ]
264
+ of Object.entries(
265
+ getPublicEnvironment()
266
+ )
267
+ ) {
268
+ defines[
269
+ `process.env.${key}`
270
+ ] =
271
+ JSON.stringify(
272
+ value
273
+ );
274
+ }
275
+
276
+ return defines;
277
+ }
278
+
279
+ export function getEnvironmentFileNames(
280
+ mode: EnvironmentMode
281
+ ): string[] {
282
+ return [
283
+ ".env",
284
+ ".env.local",
285
+ `.env.${mode}`,
286
+ `.env.${mode}.local`,
287
+ ];
288
+ }
289
+
290
+ export function parseEnvironmentFile(
291
+ source: string,
292
+ expansionSource:
293
+ NodeJS.ProcessEnv |
294
+ Record<string, string | undefined> =
295
+ process.env
296
+ ): Record<string, string> {
297
+ const result:
298
+ Record<string, string> = {};
299
+
300
+ const lines =
301
+ source
302
+ .replace(
303
+ /^\uFEFF/,
304
+ ""
305
+ )
306
+ .split(
307
+ /\r?\n/
308
+ );
309
+
310
+ for (
311
+ let index = 0;
312
+ index <
313
+ lines.length;
314
+ index++
315
+ ) {
316
+ const originalLine =
317
+ lines[
318
+ index
319
+ ];
320
+
321
+ const trimmed =
322
+ originalLine.trim();
323
+
324
+ if (
325
+ trimmed.length === 0 ||
326
+ trimmed.startsWith(
327
+ "#"
328
+ )
329
+ ) {
330
+ continue;
331
+ }
332
+
333
+ const normalized =
334
+ trimmed.startsWith(
335
+ "export "
336
+ )
337
+ ? trimmed.slice(
338
+ "export ".length
339
+ ).trimStart()
340
+ : trimmed;
341
+
342
+ const separator =
343
+ normalized.indexOf(
344
+ "="
345
+ );
346
+
347
+ if (
348
+ separator <= 0
349
+ ) {
350
+ throw new Error(
351
+ `BCP Framework: invalid environment assignment on line ${index + 1}.`
352
+ );
353
+ }
354
+
355
+ const key =
356
+ normalized
357
+ .slice(
358
+ 0,
359
+ separator
360
+ )
361
+ .trim();
362
+
363
+ if (
364
+ !/^[A-Za-z_][A-Za-z0-9_]*$/.test(
365
+ key
366
+ )
367
+ ) {
368
+ throw new Error(
369
+ `BCP Framework: invalid environment variable name "${key}" on line ${index + 1}.`
370
+ );
371
+ }
372
+
373
+ const rawValue =
374
+ normalized
375
+ .slice(
376
+ separator + 1
377
+ )
378
+ .trim();
379
+
380
+ const value =
381
+ parseEnvironmentValue(
382
+ rawValue
383
+ );
384
+
385
+ result[
386
+ key
387
+ ] =
388
+ expandEnvironmentValue(
389
+ value,
390
+ {
391
+ ...expansionSource,
392
+ ...result,
393
+ }
394
+ );
395
+ }
396
+
397
+ return result;
398
+ }
399
+
400
+ function parseEnvironmentValue(
401
+ rawValue: string
402
+ ): string {
403
+ if (
404
+ rawValue.length === 0
405
+ ) {
406
+ return "";
407
+ }
408
+
409
+ if (
410
+ rawValue.startsWith(
411
+ "'"
412
+ )
413
+ ) {
414
+ if (
415
+ !rawValue.endsWith(
416
+ "'"
417
+ ) ||
418
+ rawValue.length < 2
419
+ ) {
420
+ throw new Error(
421
+ "BCP Framework: unterminated single-quoted environment value."
422
+ );
423
+ }
424
+
425
+ return rawValue.slice(
426
+ 1,
427
+ -1
428
+ );
429
+ }
430
+
431
+ if (
432
+ rawValue.startsWith(
433
+ '"'
434
+ )
435
+ ) {
436
+ if (
437
+ !rawValue.endsWith(
438
+ '"'
439
+ ) ||
440
+ rawValue.length < 2
441
+ ) {
442
+ throw new Error(
443
+ "BCP Framework: unterminated double-quoted environment value."
444
+ );
445
+ }
446
+
447
+ return rawValue
448
+ .slice(
449
+ 1,
450
+ -1
451
+ )
452
+ .replace(
453
+ /\\n/g,
454
+ "\n"
455
+ )
456
+ .replace(
457
+ /\\r/g,
458
+ "\r"
459
+ )
460
+ .replace(
461
+ /\\t/g,
462
+ "\t"
463
+ )
464
+ .replace(
465
+ /\\"/g,
466
+ '"'
467
+ )
468
+ .replace(
469
+ /\\\\/g,
470
+ "\\"
471
+ );
472
+ }
473
+
474
+ const commentMatch =
475
+ /\s+#/.exec(
476
+ rawValue
477
+ );
478
+
479
+ return (
480
+ commentMatch
481
+ ? rawValue.slice(
482
+ 0,
483
+ commentMatch.index
484
+ )
485
+ : rawValue
486
+ ).trimEnd();
487
+ }
488
+
489
+ function expandEnvironmentValue(
490
+ value: string,
491
+ source:
492
+ NodeJS.ProcessEnv |
493
+ Record<string, string | undefined>
494
+ ): string {
495
+ return value.replace(
496
+ /(^|[^\\])\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))/g,
497
+ (
498
+ _match,
499
+ prefix: string,
500
+ braced: string | undefined,
501
+ plain: string | undefined
502
+ ) => {
503
+ const key =
504
+ braced ??
505
+ plain ??
506
+ "";
507
+
508
+ return `${prefix}${
509
+ source[
510
+ key
511
+ ] ??
512
+ ""
513
+ }`;
514
+ }
515
+ )
516
+ .replace(
517
+ /\\\$/g,
518
+ "$"
519
+ );
520
+ }
521
+
522
+ function snapshotProcessEnvironment(): Record<string, string | undefined> {
523
+ return {
524
+ ...process.env,
525
+ };
526
+ }
527
+
528
+ function restoreExternalEnvironment(
529
+ state: EnvironmentState
530
+ ): void {
531
+ for (
532
+ const key
533
+ of state.managedKeys
534
+ ) {
535
+ const externalValue =
536
+ state.externalValues[
537
+ key
538
+ ];
539
+
540
+ if (
541
+ externalValue === undefined
542
+ ) {
543
+ delete process.env[
544
+ key
545
+ ];
546
+ } else {
547
+ process.env[
548
+ key
549
+ ] =
550
+ externalValue;
551
+ }
552
+ }
553
+ }
554
+
555
+ function normalizeStatePath(
556
+ value: string
557
+ ): string {
558
+ const normalized =
559
+ path.normalize(
560
+ value
561
+ );
562
+
563
+ return process.platform ===
564
+ "win32"
565
+ ? normalized.toLowerCase()
566
+ : normalized;
567
+ }
568
+
569
+ function collectEnvironmentValues(): Record<string, string> {
570
+ const result:
571
+ Record<string, string> = {};
572
+
573
+ for (
574
+ const [
575
+ key,
576
+ value,
577
+ ]
578
+ of Object.entries(
579
+ process.env
580
+ )
581
+ ) {
582
+ if (
583
+ value !== undefined
584
+ ) {
585
+ result[
586
+ key
587
+ ] =
588
+ value;
589
+ }
590
+ }
591
+
592
+ return result;
593
+ }