@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.
- package/CHANGELOG.md +49 -0
- package/LICENSE +21 -0
- package/README.md +241 -0
- package/docs/caching.md +76 -0
- package/docs/configuration.md +97 -0
- package/docs/deployment.md +74 -0
- package/docs/getting-started.md +82 -0
- package/docs/middleware.md +58 -0
- package/docs/releasing.md +299 -0
- package/docs/routing.md +103 -0
- package/docs/security.md +57 -0
- package/package.json +68 -0
- package/packages/bundler/src/client-islands.ts +1457 -0
- package/packages/bundler/src/incremental-context.ts +206 -0
- package/packages/bundler/src/index.ts +1991 -0
- package/packages/bundler/src/module-graph.ts +317 -0
- package/packages/bundler/src/partial-hydration.ts +414 -0
- package/packages/bundler/src/production.ts +974 -0
- package/packages/bundler/src/server-production-middleware.ts +447 -0
- package/packages/bundler/src/server-production.ts +1193 -0
- package/packages/bundler/src/special-files.ts +131 -0
- package/packages/cache/src/index.ts +761 -0
- package/packages/cli/bin/bcp.mjs +93 -0
- package/packages/cli/src/args.ts +305 -0
- package/packages/cli/src/bootstrap.ts +514 -0
- package/packages/cli/src/index.ts +504 -0
- package/packages/cli/src/version.ts +45 -0
- package/packages/client/src/cache.ts +11 -0
- package/packages/client/src/config.ts +18 -0
- package/packages/client/src/error-boundary.tsx +149 -0
- package/packages/client/src/hydration.ts +3 -0
- package/packages/client/src/index.tsx +57 -0
- package/packages/client/src/islands.tsx +315 -0
- package/packages/client/src/metadata.ts +281 -0
- package/packages/client/src/navigation-loading.ts +52 -0
- package/packages/client/src/navigation-state.ts +80 -0
- package/packages/client/src/not-found.ts +34 -0
- package/packages/client/src/persistent-layout-runtime.ts +273 -0
- package/packages/client/src/router-v2.tsx +969 -0
- package/packages/client/src/router.tsx +1 -0
- package/packages/config/src/index.ts +1038 -0
- package/packages/env/src/index.ts +593 -0
- package/packages/router/src/advanced-router.ts +1032 -0
- package/packages/router/src/index.ts +1 -0
- package/packages/server/src/compression.ts +249 -0
- package/packages/server/src/dev-document-metadata.ts +154 -0
- package/packages/server/src/dev-hmr.ts +225 -0
- package/packages/server/src/index.ts +2265 -0
- package/packages/server/src/metadata.ts +478 -0
- package/packages/server/src/middleware-dev-server.ts +260 -0
- package/packages/server/src/middleware-loader.ts +140 -0
- package/packages/server/src/middleware-proxy.ts +516 -0
- package/packages/server/src/middleware.ts +704 -0
- package/packages/server/src/navigation-payload.ts +471 -0
- package/packages/server/src/production-server.ts +1746 -0
- package/packages/server/src/response-cache-proxy.ts +828 -0
- package/packages/server/src/security-proxy.ts +406 -0
- package/packages/server/src/security.ts +451 -0
- package/packages/server/src/standalone-production-runtime-v2.ts +2047 -0
- package/packages/server/src/standalone-production-runtime-v3.ts +250 -0
- package/packages/server/src/standalone-production-runtime-v4.ts +289 -0
- package/packages/server/src/standalone-production-runtime-v5.ts +289 -0
- package/packages/server/src/standalone-production-runtime.ts +1951 -0
- package/packages/server/src/standalone-production-server.ts +6 -0
- package/packages/server/src/static-assets.ts +262 -0
- package/packages/server/src/static-dev-server.ts +854 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GenerateMetadata,
|
|
3
|
+
Metadata,
|
|
4
|
+
MetadataIcons,
|
|
5
|
+
MetadataOpenGraph,
|
|
6
|
+
MetadataRobots,
|
|
7
|
+
ResolvedMetadata,
|
|
8
|
+
} from "../../client/src/metadata.js";
|
|
9
|
+
|
|
10
|
+
export interface MetadataSource {
|
|
11
|
+
label: string;
|
|
12
|
+
metadata?: Metadata;
|
|
13
|
+
generateMetadata?: GenerateMetadata;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function resolveRouteMetadata(
|
|
17
|
+
sources: MetadataSource[],
|
|
18
|
+
params: Record<string, string>,
|
|
19
|
+
fallbackTitle = "BCP Framework"
|
|
20
|
+
): Promise<ResolvedMetadata> {
|
|
21
|
+
let merged: Metadata = {};
|
|
22
|
+
|
|
23
|
+
for (const source of sources) {
|
|
24
|
+
const hasStatic =
|
|
25
|
+
source.metadata !== undefined;
|
|
26
|
+
const hasGenerated =
|
|
27
|
+
source.generateMetadata !== undefined;
|
|
28
|
+
|
|
29
|
+
if (hasStatic && hasGenerated) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`BCP Framework: ${source.label} cannot export both metadata and generateMetadata.`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let next: Metadata | undefined;
|
|
36
|
+
|
|
37
|
+
if (hasGenerated) {
|
|
38
|
+
if (
|
|
39
|
+
typeof source.generateMetadata !==
|
|
40
|
+
"function"
|
|
41
|
+
) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`BCP Framework: generateMetadata in ${source.label} must be a function.`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
next =
|
|
48
|
+
await source.generateMetadata({
|
|
49
|
+
params,
|
|
50
|
+
parent:
|
|
51
|
+
resolveMetadata(
|
|
52
|
+
merged,
|
|
53
|
+
fallbackTitle
|
|
54
|
+
),
|
|
55
|
+
});
|
|
56
|
+
} else if (hasStatic) {
|
|
57
|
+
next =
|
|
58
|
+
source.metadata;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (next === undefined) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
assertMetadata(
|
|
66
|
+
next,
|
|
67
|
+
source.label
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
merged =
|
|
71
|
+
mergeMetadata(
|
|
72
|
+
merged,
|
|
73
|
+
next
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return resolveMetadata(
|
|
78
|
+
merged,
|
|
79
|
+
fallbackTitle
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function mergeMetadata(
|
|
84
|
+
base: Metadata,
|
|
85
|
+
next: Metadata
|
|
86
|
+
): Metadata {
|
|
87
|
+
return {
|
|
88
|
+
...base,
|
|
89
|
+
...next,
|
|
90
|
+
alternates:
|
|
91
|
+
next.alternates
|
|
92
|
+
? {
|
|
93
|
+
...base.alternates,
|
|
94
|
+
...next.alternates,
|
|
95
|
+
}
|
|
96
|
+
: base.alternates,
|
|
97
|
+
openGraph:
|
|
98
|
+
next.openGraph
|
|
99
|
+
? {
|
|
100
|
+
...base.openGraph,
|
|
101
|
+
...next.openGraph,
|
|
102
|
+
}
|
|
103
|
+
: base.openGraph,
|
|
104
|
+
robots:
|
|
105
|
+
mergeRobots(
|
|
106
|
+
base.robots,
|
|
107
|
+
next.robots
|
|
108
|
+
),
|
|
109
|
+
icons:
|
|
110
|
+
mergeIcons(
|
|
111
|
+
base.icons,
|
|
112
|
+
next.icons
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function mergeRobots(
|
|
118
|
+
base: Metadata["robots"],
|
|
119
|
+
next: Metadata["robots"]
|
|
120
|
+
): Metadata["robots"] {
|
|
121
|
+
if (next === undefined) {
|
|
122
|
+
return base;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (
|
|
126
|
+
typeof next === "string" ||
|
|
127
|
+
typeof base === "string" ||
|
|
128
|
+
base === undefined
|
|
129
|
+
) {
|
|
130
|
+
return next;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
...base,
|
|
135
|
+
...next,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function mergeIcons(
|
|
140
|
+
base: Metadata["icons"],
|
|
141
|
+
next: Metadata["icons"]
|
|
142
|
+
): Metadata["icons"] {
|
|
143
|
+
if (next === undefined) {
|
|
144
|
+
return base;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const normalizedNext =
|
|
148
|
+
normalizeIcons(
|
|
149
|
+
next
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const normalizedBase =
|
|
153
|
+
base === undefined
|
|
154
|
+
? undefined
|
|
155
|
+
: normalizeIcons(
|
|
156
|
+
base
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
...normalizedBase,
|
|
161
|
+
...normalizedNext,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function resolveMetadata(
|
|
166
|
+
metadata: Metadata,
|
|
167
|
+
fallbackTitle: string
|
|
168
|
+
): ResolvedMetadata {
|
|
169
|
+
const title =
|
|
170
|
+
metadata.title ??
|
|
171
|
+
fallbackTitle;
|
|
172
|
+
|
|
173
|
+
const description =
|
|
174
|
+
metadata.description ??
|
|
175
|
+
null;
|
|
176
|
+
|
|
177
|
+
const openGraph =
|
|
178
|
+
metadata.openGraph
|
|
179
|
+
? resolveOpenGraph(
|
|
180
|
+
metadata.openGraph,
|
|
181
|
+
title,
|
|
182
|
+
description
|
|
183
|
+
)
|
|
184
|
+
: null;
|
|
185
|
+
|
|
186
|
+
const icons =
|
|
187
|
+
normalizeIcons(
|
|
188
|
+
metadata.icons
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
title,
|
|
193
|
+
description,
|
|
194
|
+
robots:
|
|
195
|
+
resolveRobots(
|
|
196
|
+
metadata.robots
|
|
197
|
+
),
|
|
198
|
+
canonical:
|
|
199
|
+
metadata.alternates
|
|
200
|
+
?.canonical ??
|
|
201
|
+
null,
|
|
202
|
+
openGraph,
|
|
203
|
+
icons: {
|
|
204
|
+
icon:
|
|
205
|
+
icons.icon ??
|
|
206
|
+
null,
|
|
207
|
+
shortcut:
|
|
208
|
+
icons.shortcut ??
|
|
209
|
+
null,
|
|
210
|
+
apple:
|
|
211
|
+
icons.apple ??
|
|
212
|
+
null,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function resolveOpenGraph(
|
|
218
|
+
value: MetadataOpenGraph,
|
|
219
|
+
title: string,
|
|
220
|
+
description: string | null
|
|
221
|
+
): ResolvedMetadata["openGraph"] {
|
|
222
|
+
const images =
|
|
223
|
+
value.images === undefined
|
|
224
|
+
? []
|
|
225
|
+
: Array.isArray(
|
|
226
|
+
value.images
|
|
227
|
+
)
|
|
228
|
+
? value.images
|
|
229
|
+
: [
|
|
230
|
+
value.images,
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
title:
|
|
235
|
+
value.title ??
|
|
236
|
+
title,
|
|
237
|
+
description:
|
|
238
|
+
value.description ??
|
|
239
|
+
description,
|
|
240
|
+
url:
|
|
241
|
+
value.url ??
|
|
242
|
+
null,
|
|
243
|
+
type:
|
|
244
|
+
value.type ??
|
|
245
|
+
null,
|
|
246
|
+
siteName:
|
|
247
|
+
value.siteName ??
|
|
248
|
+
null,
|
|
249
|
+
images,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function resolveRobots(
|
|
254
|
+
value: Metadata["robots"]
|
|
255
|
+
): string | null {
|
|
256
|
+
if (value === undefined) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (typeof value === "string") {
|
|
261
|
+
return value;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const directives:
|
|
265
|
+
string[] = [];
|
|
266
|
+
|
|
267
|
+
pushBooleanDirective(
|
|
268
|
+
directives,
|
|
269
|
+
value.index,
|
|
270
|
+
"index",
|
|
271
|
+
"noindex"
|
|
272
|
+
);
|
|
273
|
+
pushBooleanDirective(
|
|
274
|
+
directives,
|
|
275
|
+
value.follow,
|
|
276
|
+
"follow",
|
|
277
|
+
"nofollow"
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
if (value.noarchive) {
|
|
281
|
+
directives.push(
|
|
282
|
+
"noarchive"
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (value.nosnippet) {
|
|
287
|
+
directives.push(
|
|
288
|
+
"nosnippet"
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (value.noimageindex) {
|
|
293
|
+
directives.push(
|
|
294
|
+
"noimageindex"
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return directives.length > 0
|
|
299
|
+
? directives.join(", ")
|
|
300
|
+
: null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function pushBooleanDirective(
|
|
304
|
+
directives: string[],
|
|
305
|
+
value: boolean | undefined,
|
|
306
|
+
positive: string,
|
|
307
|
+
negative: string
|
|
308
|
+
): void {
|
|
309
|
+
if (value === undefined) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
directives.push(
|
|
314
|
+
value
|
|
315
|
+
? positive
|
|
316
|
+
: negative
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function normalizeIcons(
|
|
321
|
+
value: Metadata["icons"]
|
|
322
|
+
): MetadataIcons {
|
|
323
|
+
if (value === undefined) {
|
|
324
|
+
return {};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (typeof value === "string") {
|
|
328
|
+
return {
|
|
329
|
+
icon: value,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return value;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function assertMetadata(
|
|
337
|
+
value: Metadata,
|
|
338
|
+
label: string
|
|
339
|
+
): void {
|
|
340
|
+
if (
|
|
341
|
+
typeof value !== "object" ||
|
|
342
|
+
value === null ||
|
|
343
|
+
Array.isArray(value)
|
|
344
|
+
) {
|
|
345
|
+
throw new Error(
|
|
346
|
+
`BCP Framework: metadata in ${label} must be an object.`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
assertOptionalString(
|
|
351
|
+
value.title,
|
|
352
|
+
"title",
|
|
353
|
+
label
|
|
354
|
+
);
|
|
355
|
+
assertOptionalString(
|
|
356
|
+
value.description,
|
|
357
|
+
"description",
|
|
358
|
+
label
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
if (
|
|
362
|
+
value.robots !== undefined &&
|
|
363
|
+
typeof value.robots !== "string"
|
|
364
|
+
) {
|
|
365
|
+
assertRobots(
|
|
366
|
+
value.robots,
|
|
367
|
+
label
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (value.alternates) {
|
|
372
|
+
assertOptionalString(
|
|
373
|
+
value.alternates.canonical,
|
|
374
|
+
"alternates.canonical",
|
|
375
|
+
label
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (value.openGraph) {
|
|
380
|
+
for (
|
|
381
|
+
const [name, item]
|
|
382
|
+
of Object.entries({
|
|
383
|
+
title:
|
|
384
|
+
value.openGraph.title,
|
|
385
|
+
description:
|
|
386
|
+
value.openGraph.description,
|
|
387
|
+
url:
|
|
388
|
+
value.openGraph.url,
|
|
389
|
+
type:
|
|
390
|
+
value.openGraph.type,
|
|
391
|
+
siteName:
|
|
392
|
+
value.openGraph.siteName,
|
|
393
|
+
})
|
|
394
|
+
) {
|
|
395
|
+
assertOptionalString(
|
|
396
|
+
item,
|
|
397
|
+
`openGraph.${name}`,
|
|
398
|
+
label
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const images =
|
|
403
|
+
value.openGraph.images;
|
|
404
|
+
|
|
405
|
+
if (
|
|
406
|
+
images !== undefined &&
|
|
407
|
+
!(
|
|
408
|
+
typeof images === "string" ||
|
|
409
|
+
(
|
|
410
|
+
Array.isArray(images) &&
|
|
411
|
+
images.every(
|
|
412
|
+
(image) =>
|
|
413
|
+
typeof image ===
|
|
414
|
+
"string"
|
|
415
|
+
)
|
|
416
|
+
)
|
|
417
|
+
)
|
|
418
|
+
) {
|
|
419
|
+
throw new Error(
|
|
420
|
+
`BCP Framework: metadata.openGraph.images in ${label} must be a string or string array.`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (
|
|
426
|
+
value.icons !== undefined &&
|
|
427
|
+
typeof value.icons !== "string"
|
|
428
|
+
) {
|
|
429
|
+
for (
|
|
430
|
+
const [name, item]
|
|
431
|
+
of Object.entries(
|
|
432
|
+
value.icons
|
|
433
|
+
)
|
|
434
|
+
) {
|
|
435
|
+
assertOptionalString(
|
|
436
|
+
item,
|
|
437
|
+
`icons.${name}`,
|
|
438
|
+
label
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function assertRobots(
|
|
445
|
+
value: MetadataRobots,
|
|
446
|
+
label: string
|
|
447
|
+
): void {
|
|
448
|
+
for (
|
|
449
|
+
const [name, item]
|
|
450
|
+
of Object.entries(
|
|
451
|
+
value
|
|
452
|
+
)
|
|
453
|
+
) {
|
|
454
|
+
if (
|
|
455
|
+
item !== undefined &&
|
|
456
|
+
typeof item !== "boolean"
|
|
457
|
+
) {
|
|
458
|
+
throw new Error(
|
|
459
|
+
`BCP Framework: metadata.robots.${name} in ${label} must be a boolean.`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function assertOptionalString(
|
|
466
|
+
value: unknown,
|
|
467
|
+
field: string,
|
|
468
|
+
label: string
|
|
469
|
+
): void {
|
|
470
|
+
if (
|
|
471
|
+
value !== undefined &&
|
|
472
|
+
typeof value !== "string"
|
|
473
|
+
) {
|
|
474
|
+
throw new Error(
|
|
475
|
+
`BCP Framework: metadata.${field} in ${label} must be a string.`
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import * as net from "node:net";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
DevServerOptions,
|
|
6
|
+
} from "./index.js";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
loadProjectMiddleware,
|
|
10
|
+
} from "./middleware-loader.js";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
createMiddlewareProxy,
|
|
14
|
+
} from "./middleware-proxy.js";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
createSecurityProxy,
|
|
18
|
+
} from "./security-proxy.js";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
createStaticDevServer,
|
|
22
|
+
} from "./static-dev-server.js";
|
|
23
|
+
|
|
24
|
+
export function createMiddlewareDevServer(
|
|
25
|
+
options: DevServerOptions = {}
|
|
26
|
+
) {
|
|
27
|
+
const port =
|
|
28
|
+
options.port ??
|
|
29
|
+
3000;
|
|
30
|
+
|
|
31
|
+
const hostname =
|
|
32
|
+
options.hostname ??
|
|
33
|
+
"localhost";
|
|
34
|
+
|
|
35
|
+
const rootDirectory =
|
|
36
|
+
path.resolve(
|
|
37
|
+
options.rootDirectory ??
|
|
38
|
+
process.cwd()
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
let internalServer:
|
|
42
|
+
ReturnType<
|
|
43
|
+
typeof createStaticDevServer
|
|
44
|
+
> | null =
|
|
45
|
+
null;
|
|
46
|
+
|
|
47
|
+
let middlewareGateway:
|
|
48
|
+
ReturnType<
|
|
49
|
+
typeof createMiddlewareProxy
|
|
50
|
+
> | null =
|
|
51
|
+
null;
|
|
52
|
+
|
|
53
|
+
let securityGateway:
|
|
54
|
+
ReturnType<
|
|
55
|
+
typeof createSecurityProxy
|
|
56
|
+
> | null =
|
|
57
|
+
null;
|
|
58
|
+
|
|
59
|
+
let started =
|
|
60
|
+
false;
|
|
61
|
+
|
|
62
|
+
async function start(): Promise<void> {
|
|
63
|
+
if (started) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const applicationPort =
|
|
68
|
+
await findFreePort();
|
|
69
|
+
const middlewarePort =
|
|
70
|
+
await findFreePort();
|
|
71
|
+
|
|
72
|
+
internalServer =
|
|
73
|
+
createStaticDevServer({
|
|
74
|
+
...options,
|
|
75
|
+
rootDirectory,
|
|
76
|
+
port:
|
|
77
|
+
applicationPort,
|
|
78
|
+
hostname:
|
|
79
|
+
"127.0.0.1",
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await internalServer.start();
|
|
83
|
+
|
|
84
|
+
middlewareGateway =
|
|
85
|
+
createMiddlewareProxy({
|
|
86
|
+
port:
|
|
87
|
+
middlewarePort,
|
|
88
|
+
hostname:
|
|
89
|
+
"127.0.0.1",
|
|
90
|
+
upstreamPort:
|
|
91
|
+
applicationPort,
|
|
92
|
+
upstreamHostname:
|
|
93
|
+
"127.0.0.1",
|
|
94
|
+
getMiddleware:
|
|
95
|
+
() =>
|
|
96
|
+
loadProjectMiddleware(
|
|
97
|
+
rootDirectory
|
|
98
|
+
),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
await middlewareGateway.start();
|
|
103
|
+
} catch (error) {
|
|
104
|
+
await internalServer.stop();
|
|
105
|
+
internalServer =
|
|
106
|
+
null;
|
|
107
|
+
middlewareGateway =
|
|
108
|
+
null;
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
securityGateway =
|
|
113
|
+
createSecurityProxy({
|
|
114
|
+
port,
|
|
115
|
+
hostname,
|
|
116
|
+
upstreamPort:
|
|
117
|
+
middlewarePort,
|
|
118
|
+
upstreamHostname:
|
|
119
|
+
"127.0.0.1",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await securityGateway.start();
|
|
124
|
+
} catch (error) {
|
|
125
|
+
await middlewareGateway.stop();
|
|
126
|
+
await internalServer.stop();
|
|
127
|
+
internalServer =
|
|
128
|
+
null;
|
|
129
|
+
middlewareGateway =
|
|
130
|
+
null;
|
|
131
|
+
securityGateway =
|
|
132
|
+
null;
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
started =
|
|
137
|
+
true;
|
|
138
|
+
|
|
139
|
+
console.log(
|
|
140
|
+
`[BCP Security] Dev gateway: http://${hostname}:${port}`
|
|
141
|
+
);
|
|
142
|
+
console.log(
|
|
143
|
+
`[BCP Middleware] File: ${path.join(rootDirectory, "middleware.ts")} (optional; .tsx/.js/.jsx also supported)`
|
|
144
|
+
);
|
|
145
|
+
console.log("");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function stop(): Promise<void> {
|
|
149
|
+
if (securityGateway) {
|
|
150
|
+
await securityGateway.stop();
|
|
151
|
+
securityGateway =
|
|
152
|
+
null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (middlewareGateway) {
|
|
156
|
+
await middlewareGateway.stop();
|
|
157
|
+
middlewareGateway =
|
|
158
|
+
null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (internalServer) {
|
|
162
|
+
await internalServer.stop();
|
|
163
|
+
internalServer =
|
|
164
|
+
null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
started =
|
|
168
|
+
false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
start,
|
|
173
|
+
stop,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function findFreePort(): Promise<number> {
|
|
178
|
+
const server =
|
|
179
|
+
net.createServer();
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
await new Promise<void>(
|
|
183
|
+
(
|
|
184
|
+
resolve,
|
|
185
|
+
reject
|
|
186
|
+
) => {
|
|
187
|
+
const onError =
|
|
188
|
+
(error: Error) => {
|
|
189
|
+
server.off(
|
|
190
|
+
"listening",
|
|
191
|
+
onListening
|
|
192
|
+
);
|
|
193
|
+
reject(
|
|
194
|
+
error
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const onListening =
|
|
199
|
+
() => {
|
|
200
|
+
server.off(
|
|
201
|
+
"error",
|
|
202
|
+
onError
|
|
203
|
+
);
|
|
204
|
+
resolve();
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
server.once(
|
|
208
|
+
"error",
|
|
209
|
+
onError
|
|
210
|
+
);
|
|
211
|
+
server.once(
|
|
212
|
+
"listening",
|
|
213
|
+
onListening
|
|
214
|
+
);
|
|
215
|
+
server.listen(
|
|
216
|
+
0,
|
|
217
|
+
"127.0.0.1"
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const address =
|
|
223
|
+
server.address();
|
|
224
|
+
|
|
225
|
+
if (
|
|
226
|
+
!address ||
|
|
227
|
+
typeof address ===
|
|
228
|
+
"string"
|
|
229
|
+
) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
"BCP Framework: could not allocate middleware dev port."
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return address.port;
|
|
236
|
+
} finally {
|
|
237
|
+
if (
|
|
238
|
+
server.listening
|
|
239
|
+
) {
|
|
240
|
+
await new Promise<void>(
|
|
241
|
+
(
|
|
242
|
+
resolve,
|
|
243
|
+
reject
|
|
244
|
+
) => {
|
|
245
|
+
server.close(
|
|
246
|
+
(error) => {
|
|
247
|
+
if (error) {
|
|
248
|
+
reject(
|
|
249
|
+
error
|
|
250
|
+
);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
resolve();
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|