@chidchanun/bcp 0.2.0 → 0.2.2
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/README.md +222 -239
- package/docs/README.md +153 -240
- package/docs/api-manifest.json +130 -0
- package/docs/api-reference.md +224 -0
- package/docs/configuration.md +96 -1
- package/docs/docs-web-manifest.json +23 -3
- package/docs/documentation-platform.md +136 -0
- package/docs/environment-validation.md +224 -0
- package/docs/platform-manifest.json +15 -4
- package/docs/releases/0.2.1.md +132 -0
- package/docs/releases/0.2.2.md +118 -0
- package/package.json +1 -1
- package/packages/cli/src/args.ts +50 -9
- package/packages/cli/src/bootstrap.ts +22 -7
- package/packages/cli/src/configuration.ts +235 -0
- package/packages/cli/src/index.ts +101 -1
- package/packages/client/src/config.ts +26 -0
- package/packages/config/src/diagnostics.ts +226 -0
- package/packages/config/src/environment-loader.ts +101 -0
- package/packages/config/src/environment-schema.ts +677 -0
|
@@ -14,6 +14,9 @@ import {
|
|
|
14
14
|
getConfigFileNames,
|
|
15
15
|
resolveBcpConfig,
|
|
16
16
|
} from "../../config/src/index.js";
|
|
17
|
+
import {
|
|
18
|
+
getEnvironmentSchemaFileNames,
|
|
19
|
+
} from "../../config/src/environment-loader.js";
|
|
17
20
|
|
|
18
21
|
import {
|
|
19
22
|
getEnvironmentFileNames,
|
|
@@ -86,12 +89,6 @@ async function runCli(
|
|
|
86
89
|
);
|
|
87
90
|
}
|
|
88
91
|
|
|
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
92
|
if (
|
|
96
93
|
cliOptions.command !==
|
|
97
94
|
"start"
|
|
@@ -159,9 +156,20 @@ async function runDevSupervisor(
|
|
|
159
156
|
)
|
|
160
157
|
);
|
|
161
158
|
|
|
159
|
+
const environmentSchemaFiles =
|
|
160
|
+
getEnvironmentSchemaFileNames()
|
|
161
|
+
.map(
|
|
162
|
+
(fileName) =>
|
|
163
|
+
path.join(
|
|
164
|
+
rootDirectory,
|
|
165
|
+
fileName
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
|
|
162
169
|
const watchedFiles = [
|
|
163
170
|
...envFiles,
|
|
164
171
|
...configFiles,
|
|
172
|
+
...environmentSchemaFiles,
|
|
165
173
|
];
|
|
166
174
|
|
|
167
175
|
const bootstrapFile =
|
|
@@ -360,7 +368,11 @@ async function runDevSupervisor(
|
|
|
360
368
|
"bcp.config."
|
|
361
369
|
)
|
|
362
370
|
? "Config"
|
|
363
|
-
:
|
|
371
|
+
: fileName.startsWith(
|
|
372
|
+
"bcp.environment."
|
|
373
|
+
)
|
|
374
|
+
? "Environment Schema"
|
|
375
|
+
: "Env";
|
|
364
376
|
|
|
365
377
|
console.log("");
|
|
366
378
|
console.log(
|
|
@@ -486,6 +498,9 @@ async function runDevSupervisor(
|
|
|
486
498
|
console.log(
|
|
487
499
|
`[BCP Config] Watching: ${getConfigFileNames().join(", ")}`
|
|
488
500
|
);
|
|
501
|
+
console.log(
|
|
502
|
+
`[BCP Environment Schema] Watching: ${getEnvironmentSchemaFileNames().join(", ")}`
|
|
503
|
+
);
|
|
489
504
|
|
|
490
505
|
startChild(
|
|
491
506
|
false
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadEnvironment,
|
|
3
|
+
} from "../../env/src/index.js";
|
|
4
|
+
import {
|
|
5
|
+
type ResolveBcpConfigOverrides,
|
|
6
|
+
} from "../../config/src/index.js";
|
|
7
|
+
import {
|
|
8
|
+
loadBcpEnvironmentSchema,
|
|
9
|
+
} from "../../config/src/environment-loader.js";
|
|
10
|
+
import {
|
|
11
|
+
applyEnvironmentDefaults,
|
|
12
|
+
} from "../../config/src/environment-schema.js";
|
|
13
|
+
import {
|
|
14
|
+
assertConfigurationDiagnostics,
|
|
15
|
+
diagnoseBcpConfiguration,
|
|
16
|
+
type BcpConfigurationDiagnosticsReport,
|
|
17
|
+
} from "../../config/src/diagnostics.js";
|
|
18
|
+
|
|
19
|
+
export interface RunConfigurationCheckOptions {
|
|
20
|
+
rootDirectory: string;
|
|
21
|
+
mode?: "development" | "production" | "test";
|
|
22
|
+
json?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function runConfigurationCheck(
|
|
26
|
+
options: RunConfigurationCheckOptions
|
|
27
|
+
): Promise<BcpConfigurationDiagnosticsReport> {
|
|
28
|
+
const mode =
|
|
29
|
+
options.mode ??
|
|
30
|
+
resolveCheckMode();
|
|
31
|
+
const loadedEnvironment =
|
|
32
|
+
loadEnvironment(
|
|
33
|
+
options.rootDirectory,
|
|
34
|
+
mode
|
|
35
|
+
);
|
|
36
|
+
const schema =
|
|
37
|
+
await loadBcpEnvironmentSchema(
|
|
38
|
+
options.rootDirectory
|
|
39
|
+
);
|
|
40
|
+
const appliedDefaults =
|
|
41
|
+
applyEnvironmentDefaults(
|
|
42
|
+
schema.schema,
|
|
43
|
+
process.env
|
|
44
|
+
);
|
|
45
|
+
const report =
|
|
46
|
+
await diagnoseBcpConfiguration({
|
|
47
|
+
rootDirectory:
|
|
48
|
+
options.rootDirectory,
|
|
49
|
+
mode,
|
|
50
|
+
environment:
|
|
51
|
+
process.env,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
options.json
|
|
56
|
+
) {
|
|
57
|
+
console.log(
|
|
58
|
+
JSON.stringify(
|
|
59
|
+
{
|
|
60
|
+
...report,
|
|
61
|
+
environmentFiles:
|
|
62
|
+
loadedEnvironment.files,
|
|
63
|
+
appliedDefaults,
|
|
64
|
+
},
|
|
65
|
+
null,
|
|
66
|
+
2
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
!report.ok
|
|
72
|
+
) {
|
|
73
|
+
process.exitCode =
|
|
74
|
+
1;
|
|
75
|
+
|
|
76
|
+
return report;
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
printConfigurationReport(
|
|
80
|
+
report,
|
|
81
|
+
loadedEnvironment.files,
|
|
82
|
+
appliedDefaults
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
assertConfigurationDiagnostics(
|
|
87
|
+
report
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return report;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function runStartupConfigurationDiagnostics(
|
|
94
|
+
rootDirectory: string,
|
|
95
|
+
mode: "development" | "production",
|
|
96
|
+
overrides: ResolveBcpConfigOverrides = {}
|
|
97
|
+
): Promise<BcpConfigurationDiagnosticsReport> {
|
|
98
|
+
const schema =
|
|
99
|
+
await loadBcpEnvironmentSchema(
|
|
100
|
+
rootDirectory
|
|
101
|
+
);
|
|
102
|
+
const appliedDefaults =
|
|
103
|
+
applyEnvironmentDefaults(
|
|
104
|
+
schema.schema,
|
|
105
|
+
process.env
|
|
106
|
+
);
|
|
107
|
+
const report =
|
|
108
|
+
await diagnoseBcpConfiguration({
|
|
109
|
+
rootDirectory,
|
|
110
|
+
mode,
|
|
111
|
+
environment:
|
|
112
|
+
process.env,
|
|
113
|
+
overrides,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
report.environmentSchemaFile
|
|
118
|
+
) {
|
|
119
|
+
console.log(
|
|
120
|
+
`[BCP Config] Environment schema: ${report.environmentSchemaFile} (${report.environment.checked} variable(s), ${appliedDefaults} default(s) applied)`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (
|
|
125
|
+
const diagnostic
|
|
126
|
+
of report.diagnostics
|
|
127
|
+
) {
|
|
128
|
+
if (
|
|
129
|
+
diagnostic.severity ===
|
|
130
|
+
"warning"
|
|
131
|
+
) {
|
|
132
|
+
console.warn(
|
|
133
|
+
`[BCP Config] Warning: ${diagnostic.message}`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
assertConfigurationDiagnostics(
|
|
139
|
+
report
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
return report;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function printConfigurationReport(
|
|
146
|
+
report: BcpConfigurationDiagnosticsReport,
|
|
147
|
+
environmentFiles: string[],
|
|
148
|
+
appliedDefaults: number
|
|
149
|
+
): void {
|
|
150
|
+
const errors =
|
|
151
|
+
report.diagnostics.filter(
|
|
152
|
+
(diagnostic) =>
|
|
153
|
+
diagnostic.severity ===
|
|
154
|
+
"error"
|
|
155
|
+
);
|
|
156
|
+
const warnings =
|
|
157
|
+
report.diagnostics.filter(
|
|
158
|
+
(diagnostic) =>
|
|
159
|
+
diagnostic.severity ===
|
|
160
|
+
"warning"
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
console.log("");
|
|
164
|
+
console.log(
|
|
165
|
+
`BCP Configuration Check (${report.mode})`
|
|
166
|
+
);
|
|
167
|
+
console.log("");
|
|
168
|
+
console.log(
|
|
169
|
+
` Config: ${report.configFile ?? "defaults"}`
|
|
170
|
+
);
|
|
171
|
+
console.log(
|
|
172
|
+
` Environment files: ${environmentFiles.length > 0 ? environmentFiles.join(", ") : "(none)"}`
|
|
173
|
+
);
|
|
174
|
+
console.log(
|
|
175
|
+
` Environment schema: ${report.environmentSchemaFile ?? "(none)"}`
|
|
176
|
+
);
|
|
177
|
+
console.log(
|
|
178
|
+
` Variables: ${report.environment.present}/${report.environment.checked} provided | ${appliedDefaults} default(s) applied`
|
|
179
|
+
);
|
|
180
|
+
console.log(
|
|
181
|
+
` Server: ${report.resolvedConfig.server.hostname}:${report.resolvedConfig.server.port}`
|
|
182
|
+
);
|
|
183
|
+
console.log(
|
|
184
|
+
` Build: minify=${report.resolvedConfig.build.minify} sourceMaps=${report.resolvedConfig.build.sourceMaps}`
|
|
185
|
+
);
|
|
186
|
+
console.log(
|
|
187
|
+
` Diagnostics: ${errors.length} error(s), ${warnings.length} warning(s)`
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (
|
|
191
|
+
report.diagnostics.length >
|
|
192
|
+
0
|
|
193
|
+
) {
|
|
194
|
+
console.log("");
|
|
195
|
+
|
|
196
|
+
for (
|
|
197
|
+
const diagnostic
|
|
198
|
+
of report.diagnostics
|
|
199
|
+
) {
|
|
200
|
+
console.log(
|
|
201
|
+
` ${diagnostic.severity === "error" ? "✖" : "⚠"} ${diagnostic.message}`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (
|
|
207
|
+
report.ok
|
|
208
|
+
) {
|
|
209
|
+
console.log("");
|
|
210
|
+
console.log(
|
|
211
|
+
"[BCP Config] Configuration check passed."
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.log("");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function resolveCheckMode():
|
|
219
|
+
"development" | "production" | "test" {
|
|
220
|
+
if (
|
|
221
|
+
process.env.NODE_ENV ===
|
|
222
|
+
"production"
|
|
223
|
+
) {
|
|
224
|
+
return "production";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (
|
|
228
|
+
process.env.NODE_ENV ===
|
|
229
|
+
"test"
|
|
230
|
+
) {
|
|
231
|
+
return "test";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return "development";
|
|
235
|
+
}
|
|
@@ -76,6 +76,11 @@ switch (cliOptions.command) {
|
|
|
76
76
|
break;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
case "config": {
|
|
80
|
+
await runConfigCommand();
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
|
|
79
84
|
case "doctor": {
|
|
80
85
|
await runDeveloperCommand(
|
|
81
86
|
"doctor"
|
|
@@ -112,6 +117,24 @@ async function runDev() {
|
|
|
112
117
|
cliOptions.rootDirectory
|
|
113
118
|
);
|
|
114
119
|
|
|
120
|
+
const {
|
|
121
|
+
runStartupConfigurationDiagnostics,
|
|
122
|
+
} =
|
|
123
|
+
await import(
|
|
124
|
+
"./configuration.js"
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
await runStartupConfigurationDiagnostics(
|
|
128
|
+
rootDirectory,
|
|
129
|
+
"development",
|
|
130
|
+
{
|
|
131
|
+
port:
|
|
132
|
+
cliOptions.port,
|
|
133
|
+
hostname:
|
|
134
|
+
cliOptions.hostname,
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
|
|
115
138
|
installApplicationModuleAlias(
|
|
116
139
|
rootDirectory
|
|
117
140
|
);
|
|
@@ -185,6 +208,24 @@ async function runBuild() {
|
|
|
185
208
|
cliOptions.rootDirectory
|
|
186
209
|
);
|
|
187
210
|
|
|
211
|
+
const {
|
|
212
|
+
runStartupConfigurationDiagnostics,
|
|
213
|
+
} =
|
|
214
|
+
await import(
|
|
215
|
+
"./configuration.js"
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
await runStartupConfigurationDiagnostics(
|
|
219
|
+
rootDirectory,
|
|
220
|
+
"production",
|
|
221
|
+
{
|
|
222
|
+
port:
|
|
223
|
+
cliOptions.port,
|
|
224
|
+
hostname:
|
|
225
|
+
cliOptions.hostname,
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
|
|
188
229
|
const appDirectory =
|
|
189
230
|
path.join(
|
|
190
231
|
rootDirectory,
|
|
@@ -518,6 +559,36 @@ async function runGenerateCommand(): Promise<void> {
|
|
|
518
559
|
});
|
|
519
560
|
}
|
|
520
561
|
|
|
562
|
+
async function runConfigCommand(): Promise<void> {
|
|
563
|
+
const action =
|
|
564
|
+
cliOptions.configAction;
|
|
565
|
+
|
|
566
|
+
if (
|
|
567
|
+
action === undefined ||
|
|
568
|
+
action === "help"
|
|
569
|
+
) {
|
|
570
|
+
printConfigHelp();
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const rootDirectory =
|
|
575
|
+
resolveProjectRoot(
|
|
576
|
+
cliOptions.rootDirectory
|
|
577
|
+
);
|
|
578
|
+
const {
|
|
579
|
+
runConfigurationCheck,
|
|
580
|
+
} =
|
|
581
|
+
await import(
|
|
582
|
+
"./configuration.js"
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
await runConfigurationCheck({
|
|
586
|
+
rootDirectory,
|
|
587
|
+
json:
|
|
588
|
+
cliOptions.json,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
521
592
|
async function runDeveloperCommand(
|
|
522
593
|
command:
|
|
523
594
|
"doctor" |
|
|
@@ -759,6 +830,32 @@ Examples:
|
|
|
759
830
|
`);
|
|
760
831
|
}
|
|
761
832
|
|
|
833
|
+
function printConfigHelp() {
|
|
834
|
+
console.log(`
|
|
835
|
+
BCP Configuration & Environment
|
|
836
|
+
|
|
837
|
+
Usage:
|
|
838
|
+
bcp config <command> [options]
|
|
839
|
+
|
|
840
|
+
Commands:
|
|
841
|
+
check Validate bcp.config.*, bcp.environment.* and loaded environment values
|
|
842
|
+
help Show configuration command help
|
|
843
|
+
|
|
844
|
+
Options:
|
|
845
|
+
--root <path> Project root directory
|
|
846
|
+
--json Emit the configuration diagnostics report as JSON
|
|
847
|
+
|
|
848
|
+
Environment mode:
|
|
849
|
+
NODE_ENV=production bcp config check
|
|
850
|
+
NODE_ENV=test bcp config check
|
|
851
|
+
bcp config check (development by default)
|
|
852
|
+
|
|
853
|
+
Examples:
|
|
854
|
+
bcp config check
|
|
855
|
+
bcp config check --json
|
|
856
|
+
`);
|
|
857
|
+
}
|
|
858
|
+
|
|
762
859
|
function printHelp() {
|
|
763
860
|
console.log(`
|
|
764
861
|
BCP Framework v${FRAMEWORK_VERSION}
|
|
@@ -774,6 +871,7 @@ Commands:
|
|
|
774
871
|
update [target] Update BCP Framework (default target: latest)
|
|
775
872
|
db <command> Manage database migrations
|
|
776
873
|
generate <kind> Generate pages, API routes, middleware or migrations
|
|
874
|
+
config <command> Validate project configuration and environment schema
|
|
777
875
|
doctor Run project/runtime health diagnostics
|
|
778
876
|
inspect Print resolved env, config, dependencies and routes
|
|
779
877
|
help Show this help message
|
|
@@ -786,7 +884,7 @@ Options:
|
|
|
786
884
|
--host <host> Alias for --hostname
|
|
787
885
|
--check Check for a BCP update without changing files
|
|
788
886
|
--dry-run Preview a BCP update without changing files
|
|
789
|
-
--json JSON output for doctor/inspect
|
|
887
|
+
--json JSON output for doctor/inspect/config
|
|
790
888
|
--force Replace existing generated scaffold files
|
|
791
889
|
-h, --help Show help
|
|
792
890
|
-v, --version Show version
|
|
@@ -803,6 +901,8 @@ Examples:
|
|
|
803
901
|
bcp generate page dashboard/users
|
|
804
902
|
bcp generate api users
|
|
805
903
|
bcp generate middleware
|
|
904
|
+
bcp config check
|
|
905
|
+
bcp config check --json
|
|
806
906
|
bcp doctor
|
|
807
907
|
bcp doctor --json
|
|
808
908
|
bcp inspect
|
|
@@ -16,3 +16,29 @@ export {
|
|
|
16
16
|
type ResolveBcpConfigOverrides,
|
|
17
17
|
type ResolvedBcpConfig,
|
|
18
18
|
} from "../../config/src/index.js";
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
applyEnvironmentDefaults,
|
|
22
|
+
defineEnvironment,
|
|
23
|
+
validateEnvironment,
|
|
24
|
+
type BcpEnvironmentSchema,
|
|
25
|
+
type BcpEnvironmentValidationIssue,
|
|
26
|
+
type BcpEnvironmentValidationResult,
|
|
27
|
+
type BcpEnvironmentValueType,
|
|
28
|
+
type BcpEnvironmentVariableRule,
|
|
29
|
+
} from "../../config/src/environment-schema.js";
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
getEnvironmentSchemaFileNames,
|
|
33
|
+
loadBcpEnvironmentSchema,
|
|
34
|
+
type LoadedBcpEnvironmentSchema,
|
|
35
|
+
} from "../../config/src/environment-loader.js";
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
assertConfigurationDiagnostics,
|
|
39
|
+
diagnoseBcpConfiguration,
|
|
40
|
+
type BcpConfigurationDiagnostic,
|
|
41
|
+
type BcpConfigurationDiagnosticSeverity,
|
|
42
|
+
type BcpConfigurationDiagnosticsReport,
|
|
43
|
+
type DiagnoseBcpConfigurationOptions,
|
|
44
|
+
} from "../../config/src/diagnostics.js";
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
resolveBcpConfig,
|
|
5
|
+
type ResolveBcpConfigOverrides,
|
|
6
|
+
type ResolvedBcpConfig,
|
|
7
|
+
} from "./index.js";
|
|
8
|
+
import {
|
|
9
|
+
loadBcpEnvironmentSchema,
|
|
10
|
+
} from "./environment-loader.js";
|
|
11
|
+
import {
|
|
12
|
+
validateEnvironment,
|
|
13
|
+
type BcpEnvironmentValidationResult,
|
|
14
|
+
} from "./environment-schema.js";
|
|
15
|
+
|
|
16
|
+
export type BcpConfigurationDiagnosticSeverity =
|
|
17
|
+
| "error"
|
|
18
|
+
| "warning";
|
|
19
|
+
|
|
20
|
+
export interface BcpConfigurationDiagnostic {
|
|
21
|
+
severity: BcpConfigurationDiagnosticSeverity;
|
|
22
|
+
code: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface BcpConfigurationDiagnosticsReport {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
mode: string;
|
|
29
|
+
configFile: string | null;
|
|
30
|
+
environmentSchemaFile: string | null;
|
|
31
|
+
resolvedConfig: ResolvedBcpConfig;
|
|
32
|
+
environment: {
|
|
33
|
+
checked: number;
|
|
34
|
+
present: number;
|
|
35
|
+
defaults: number;
|
|
36
|
+
};
|
|
37
|
+
diagnostics: BcpConfigurationDiagnostic[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DiagnoseBcpConfigurationOptions {
|
|
41
|
+
rootDirectory: string;
|
|
42
|
+
mode: string;
|
|
43
|
+
environment?: NodeJS.ProcessEnv;
|
|
44
|
+
overrides?: ResolveBcpConfigOverrides;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function diagnoseBcpConfiguration(
|
|
48
|
+
options: DiagnoseBcpConfigurationOptions
|
|
49
|
+
): Promise<BcpConfigurationDiagnosticsReport> {
|
|
50
|
+
const environment =
|
|
51
|
+
options.environment ??
|
|
52
|
+
process.env;
|
|
53
|
+
const resolved =
|
|
54
|
+
await resolveBcpConfig(
|
|
55
|
+
options.rootDirectory,
|
|
56
|
+
options.overrides ?? {},
|
|
57
|
+
environment
|
|
58
|
+
);
|
|
59
|
+
const loadedSchema =
|
|
60
|
+
await loadBcpEnvironmentSchema(
|
|
61
|
+
options.rootDirectory
|
|
62
|
+
);
|
|
63
|
+
const environmentValidation =
|
|
64
|
+
validateEnvironment(
|
|
65
|
+
loadedSchema.schema,
|
|
66
|
+
environment
|
|
67
|
+
);
|
|
68
|
+
const diagnostics:
|
|
69
|
+
BcpConfigurationDiagnostic[] = [];
|
|
70
|
+
|
|
71
|
+
appendEnvironmentDiagnostics(
|
|
72
|
+
diagnostics,
|
|
73
|
+
environmentValidation
|
|
74
|
+
);
|
|
75
|
+
appendRuntimeDiagnostics(
|
|
76
|
+
diagnostics,
|
|
77
|
+
options.mode,
|
|
78
|
+
resolved.config,
|
|
79
|
+
environment
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
ok:
|
|
84
|
+
!diagnostics.some(
|
|
85
|
+
(diagnostic) =>
|
|
86
|
+
diagnostic.severity ===
|
|
87
|
+
"error"
|
|
88
|
+
),
|
|
89
|
+
mode:
|
|
90
|
+
options.mode,
|
|
91
|
+
configFile:
|
|
92
|
+
resolved.file
|
|
93
|
+
? path.basename(
|
|
94
|
+
resolved.file
|
|
95
|
+
)
|
|
96
|
+
: null,
|
|
97
|
+
environmentSchemaFile:
|
|
98
|
+
loadedSchema.file
|
|
99
|
+
? path.basename(
|
|
100
|
+
loadedSchema.file
|
|
101
|
+
)
|
|
102
|
+
: null,
|
|
103
|
+
resolvedConfig:
|
|
104
|
+
resolved.config,
|
|
105
|
+
environment: {
|
|
106
|
+
checked:
|
|
107
|
+
environmentValidation.checked,
|
|
108
|
+
present:
|
|
109
|
+
environmentValidation.present,
|
|
110
|
+
defaults:
|
|
111
|
+
environmentValidation.defaults,
|
|
112
|
+
},
|
|
113
|
+
diagnostics,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function assertConfigurationDiagnostics(
|
|
118
|
+
report: BcpConfigurationDiagnosticsReport
|
|
119
|
+
): void {
|
|
120
|
+
if (
|
|
121
|
+
report.ok
|
|
122
|
+
) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const errors =
|
|
127
|
+
report.diagnostics
|
|
128
|
+
.filter(
|
|
129
|
+
(diagnostic) =>
|
|
130
|
+
diagnostic.severity ===
|
|
131
|
+
"error"
|
|
132
|
+
)
|
|
133
|
+
.map(
|
|
134
|
+
(diagnostic) =>
|
|
135
|
+
diagnostic.message
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
throw new Error(
|
|
139
|
+
`BCP Configuration Error:\n${errors.map((message) => `- ${message}`).join("\n")}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function appendEnvironmentDiagnostics(
|
|
144
|
+
diagnostics: BcpConfigurationDiagnostic[],
|
|
145
|
+
validation: BcpEnvironmentValidationResult
|
|
146
|
+
): void {
|
|
147
|
+
for (
|
|
148
|
+
const issue
|
|
149
|
+
of validation.issues
|
|
150
|
+
) {
|
|
151
|
+
diagnostics.push({
|
|
152
|
+
severity:
|
|
153
|
+
issue.severity,
|
|
154
|
+
code:
|
|
155
|
+
`environment.${issue.code}`,
|
|
156
|
+
message:
|
|
157
|
+
issue.message,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function appendRuntimeDiagnostics(
|
|
163
|
+
diagnostics: BcpConfigurationDiagnostic[],
|
|
164
|
+
mode: string,
|
|
165
|
+
config: ResolvedBcpConfig,
|
|
166
|
+
environment: NodeJS.ProcessEnv
|
|
167
|
+
): void {
|
|
168
|
+
if (
|
|
169
|
+
mode !== "production"
|
|
170
|
+
) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (
|
|
175
|
+
config.build.sourceMaps
|
|
176
|
+
) {
|
|
177
|
+
diagnostics.push({
|
|
178
|
+
severity: "warning",
|
|
179
|
+
code: "production.source_maps",
|
|
180
|
+
message:
|
|
181
|
+
"Production source maps are enabled. Confirm that exposing source information is intentional.",
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (
|
|
186
|
+
config.security.poweredByHeader
|
|
187
|
+
) {
|
|
188
|
+
diagnostics.push({
|
|
189
|
+
severity: "warning",
|
|
190
|
+
code: "production.powered_by",
|
|
191
|
+
message:
|
|
192
|
+
"security.poweredByHeader is enabled in production.",
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (
|
|
197
|
+
config.security.contentSecurityPolicy ===
|
|
198
|
+
false
|
|
199
|
+
) {
|
|
200
|
+
diagnostics.push({
|
|
201
|
+
severity: "warning",
|
|
202
|
+
code: "production.csp_disabled",
|
|
203
|
+
message:
|
|
204
|
+
"Content-Security-Policy is disabled. Configure a policy when the application can support one.",
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const trustProxy =
|
|
209
|
+
environment.BCP_TRUST_PROXY
|
|
210
|
+
?.trim()
|
|
211
|
+
.toLowerCase();
|
|
212
|
+
|
|
213
|
+
if (
|
|
214
|
+
trustProxy === "true" ||
|
|
215
|
+
trustProxy === "1" ||
|
|
216
|
+
trustProxy === "yes" ||
|
|
217
|
+
trustProxy === "on"
|
|
218
|
+
) {
|
|
219
|
+
diagnostics.push({
|
|
220
|
+
severity: "warning",
|
|
221
|
+
code: "production.trust_proxy",
|
|
222
|
+
message:
|
|
223
|
+
"BCP_TRUST_PROXY is enabled. Ensure untrusted clients cannot bypass the trusted reverse proxy/load balancer.",
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|