@chidchanun/bcp 0.2.1 → 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.
@@ -0,0 +1,224 @@
1
+ # Environment Validation
2
+
3
+ BCP Framework `0.2.2` adds an optional typed environment schema for application-specific environment variables.
4
+
5
+ The schema is separate from `bcp.config.*` and uses the project convention:
6
+
7
+ ```text
8
+ bcp.environment.ts
9
+ bcp.environment.mts
10
+ bcp.environment.js
11
+ bcp.environment.mjs
12
+ ```
13
+
14
+ Keep only one environment schema file in an application.
15
+
16
+ ## Define a schema
17
+
18
+ ```ts
19
+ import {
20
+ defineEnvironment,
21
+ } from "bcp/config";
22
+
23
+ export default defineEnvironment({
24
+ DB_HOST: {
25
+ type: "string",
26
+ required: true,
27
+ },
28
+
29
+ DB_PORT: {
30
+ type: "number",
31
+ default: 3306,
32
+ min: 1,
33
+ max: 65535,
34
+ },
35
+
36
+ SESSION_SECRET: {
37
+ type: "string",
38
+ required: true,
39
+ secret: true,
40
+ minLength: 32,
41
+ },
42
+
43
+ FEATURE_ENABLED: {
44
+ type: "boolean",
45
+ default: false,
46
+ },
47
+
48
+ BCP_PUBLIC_API_URL: {
49
+ type: "url",
50
+ required: true,
51
+ },
52
+ });
53
+ ```
54
+
55
+ Supported types:
56
+
57
+ ```text
58
+ string
59
+ number
60
+ boolean
61
+ url
62
+ ```
63
+
64
+ Supported rule fields:
65
+
66
+ ```text
67
+ type
68
+ required
69
+ secret
70
+ minLength
71
+ maxLength
72
+ min
73
+ max
74
+ default
75
+ description
76
+ ```
77
+
78
+ `minLength` / `maxLength` are intended for string and URL values. `min` / `max` are intended for numbers.
79
+
80
+ ## Validate from the CLI
81
+
82
+ ```bash
83
+ bcp config check
84
+ ```
85
+
86
+ On Windows where another `bcp.exe` may exist:
87
+
88
+ ```powershell
89
+ npm exec -- bcp-framework config check
90
+ ```
91
+
92
+ JSON output:
93
+
94
+ ```powershell
95
+ npm exec -- bcp-framework config check --json
96
+ ```
97
+
98
+ The command validates:
99
+
100
+ - `bcp.config.*`,
101
+ - `bcp.environment.*`,
102
+ - loaded `.env` files,
103
+ - configured BCP runtime values,
104
+ - application environment-schema values,
105
+ - selected production safety diagnostics.
106
+
107
+ Development is the default mode.
108
+
109
+ To inspect production diagnostics from a shell where `NODE_ENV` can be set:
110
+
111
+ ```bash
112
+ NODE_ENV=production bcp config check
113
+ ```
114
+
115
+ PowerShell example:
116
+
117
+ ```powershell
118
+ $env:NODE_ENV = "production"
119
+ npm exec -- bcp-framework config check
120
+ Remove-Item Env:NODE_ENV
121
+ ```
122
+
123
+ ## Startup validation
124
+
125
+ When a project contains `bcp.environment.*`, `bcp dev` and `bcp build` validate the schema before the application starts or a production build is produced.
126
+
127
+ A missing or invalid required environment variable stops startup/build with a configuration error.
128
+
129
+ Example:
130
+
131
+ ```text
132
+ BCP Configuration Error:
133
+ - SESSION_SECRET must contain at least 32 character(s).
134
+ ```
135
+
136
+ Changing `bcp.environment.*` while the development supervisor is running automatically restarts the dev worker, the same way changes to `.env*` or `bcp.config.*` do.
137
+
138
+ ## Public variables and secrets
139
+
140
+ Variables beginning with:
141
+
142
+ ```text
143
+ BCP_PUBLIC_
144
+ ```
145
+
146
+ can be embedded into browser bundles.
147
+
148
+ Do not use that prefix for credentials, session secrets, access keys or private service tokens.
149
+
150
+ BCP treats this as an error:
151
+
152
+ ```ts
153
+ export default defineEnvironment({
154
+ BCP_PUBLIC_TOKEN: {
155
+ type: "string",
156
+ required: true,
157
+ secret: true,
158
+ },
159
+ });
160
+ ```
161
+
162
+ The `secret: true` flag is metadata used by validation/tooling. It does not encrypt the environment value. Secrets must still be stored in an appropriate environment/secret manager and kept out of public client variables.
163
+
164
+ ## Programmatic validation
165
+
166
+ The same primitives are public through `bcp/config`:
167
+
168
+ ```ts
169
+ import {
170
+ defineEnvironment,
171
+ validateEnvironment,
172
+ } from "bcp/config";
173
+
174
+ const schema = defineEnvironment({
175
+ API_URL: {
176
+ type: "url",
177
+ required: true,
178
+ },
179
+ });
180
+
181
+ const result = validateEnvironment(
182
+ schema,
183
+ process.env
184
+ );
185
+
186
+ if (!result.ok) {
187
+ console.error(
188
+ result.issues
189
+ );
190
+ }
191
+ ```
192
+
193
+ The parsed `result.values` contains only keys declared by the schema. BCP configuration diagnostics do not print raw secret values.
194
+
195
+ ## Production diagnostics
196
+
197
+ Production configuration checks can emit warnings for potentially risky choices such as:
198
+
199
+ - production source maps enabled,
200
+ - `security.poweredByHeader` enabled,
201
+ - Content-Security-Policy disabled,
202
+ - trusted proxy mode enabled.
203
+
204
+ Warnings do not block a build. Environment-schema errors do.
205
+
206
+ Trusted proxy warnings are informational safety reminders. Enable `BCP_TRUST_PROXY` only when untrusted clients cannot bypass the trusted reverse proxy/load balancer.
207
+
208
+ ## Existing applications
209
+
210
+ Environment schemas are optional.
211
+
212
+ Projects without `bcp.environment.*` continue to use the existing `.env` loading and BCP configuration behavior. The `0.2.2` schema system is additive and does not change the established configuration precedence:
213
+
214
+ ```text
215
+ CLI override
216
+
217
+ BCP_* environment
218
+
219
+ bcp.config.*
220
+
221
+ framework defaults
222
+ ```
223
+
224
+ Application-specific variables are validated against `bcp.environment.*` after environment files are loaded.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "framework": "bcp",
4
- "version": "0.2.1",
4
+ "version": "0.2.2",
5
5
  "releaseState": "unreleased",
6
6
  "baseline": "framework-platform",
7
7
  "runtime": {
@@ -30,6 +30,7 @@
30
30
  "update",
31
31
  "db",
32
32
  "generate",
33
+ "config",
33
34
  "doctor",
34
35
  "inspect",
35
36
  "help",
@@ -55,7 +56,10 @@
55
56
  "projectGenerators": true,
56
57
  "projectDiagnostics": true,
57
58
  "documentationPlatform": true,
58
- "apiManifest": true
59
+ "apiManifest": true,
60
+ "typedEnvironmentSchema": true,
61
+ "configurationDiagnostics": true,
62
+ "configCheckCli": true
59
63
  },
60
64
  "storageProviders": [
61
65
  "local",
@@ -64,7 +68,7 @@
64
68
  "s3-compatible"
65
69
  ],
66
70
  "compatibility": {
67
- "previousBaseline": "0.2.0",
71
+ "previousBaseline": "0.2.1",
68
72
  "intentionalBreakingChangesFromPreviousBaseline": false,
69
73
  "migrationGuide": "migration-0.2.md"
70
74
  },
@@ -75,7 +79,8 @@
75
79
  "platformContract": "platform-contract.md",
76
80
  "documentationPlatform": "documentation-platform.md",
77
81
  "apiReference": "api-reference.md",
82
+ "environmentValidation": "environment-validation.md",
78
83
  "migrationGuide": "migration-0.2.md",
79
- "releaseNotes": "releases/0.2.1.md"
84
+ "releaseNotes": "releases/0.2.2.md"
80
85
  }
81
86
  }
@@ -0,0 +1,118 @@
1
+ # BCP Framework 0.2.2
2
+
3
+ > **Release state:** unreleased development target.
4
+
5
+ BCP Framework `0.2.2` is the **Configuration & Environment v2** milestone.
6
+
7
+ The release keeps the established BCP configuration precedence and adds an optional typed application environment schema, configuration diagnostics and a dedicated CLI validation workflow.
8
+
9
+ ## Highlights
10
+
11
+ ### Typed application environment schema
12
+
13
+ Projects can add:
14
+
15
+ ```text
16
+ bcp.environment.ts
17
+ ```
18
+
19
+ using the public `bcp/config` API:
20
+
21
+ ```ts
22
+ import {
23
+ defineEnvironment,
24
+ } from "bcp/config";
25
+
26
+ export default defineEnvironment({
27
+ DB_HOST: {
28
+ type: "string",
29
+ required: true,
30
+ },
31
+ DB_PORT: {
32
+ type: "number",
33
+ default: 3306,
34
+ },
35
+ SESSION_SECRET: {
36
+ type: "string",
37
+ required: true,
38
+ secret: true,
39
+ minLength: 32,
40
+ },
41
+ });
42
+ ```
43
+
44
+ Supported values include string, number, boolean and absolute HTTP(S) URL rules.
45
+
46
+ ### `bcp config check`
47
+
48
+ New CLI command:
49
+
50
+ ```bash
51
+ bcp config check
52
+ bcp config check --json
53
+ ```
54
+
55
+ It validates project configuration, environment schema values and production diagnostics without printing secret values.
56
+
57
+ ### Startup diagnostics
58
+
59
+ `bcp dev` and `bcp build` validate `bcp.environment.*` before starting the application/build.
60
+
61
+ Schema errors fail early. Production configuration warnings are reported without blocking the build.
62
+
63
+ ### Development schema watching
64
+
65
+ The dev supervisor now watches:
66
+
67
+ ```text
68
+ .env*
69
+ bcp.config.*
70
+ bcp.environment.*
71
+ ```
72
+
73
+ and restarts the worker when any of these configuration sources change.
74
+
75
+ ### Public configuration API
76
+
77
+ `bcp/config` now exposes additive environment/configuration helpers including:
78
+
79
+ ```text
80
+ defineEnvironment
81
+ validateEnvironment
82
+ loadBcpEnvironmentSchema
83
+ diagnoseBcpConfiguration
84
+ assertConfigurationDiagnostics
85
+ ```
86
+
87
+ ## Compatibility
88
+
89
+ `0.2.2` does not intentionally remove public entrypoints from `0.2.1`.
90
+
91
+ Existing projects without `bcp.environment.*` continue to work with the previous `.env` and `bcp.config.*` behavior.
92
+
93
+ The configuration precedence remains:
94
+
95
+ ```text
96
+ CLI > BCP_* environment > bcp.config.* > framework defaults
97
+ ```
98
+
99
+ ## Security
100
+
101
+ A schema variable marked `secret: true` must not use the `BCP_PUBLIC_` prefix. BCP treats that combination as a configuration error because public-prefixed values can be embedded into browser bundles.
102
+
103
+ `secret: true` is metadata for validation/tooling and does not encrypt environment values.
104
+
105
+ ## Validation
106
+
107
+ Before tagging/publishing:
108
+
109
+ ```bash
110
+ npm run typecheck
111
+ npm run test:unit
112
+ npm run test:integration
113
+ npm run test:package
114
+ npm run test:e2e
115
+ npm run rc:check
116
+ ```
117
+
118
+ The final release tag must point at the exact commit that passed the complete RC sequence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chidchanun/bcp",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -9,6 +9,7 @@ export type CliCommand =
9
9
  | "update"
10
10
  | "db"
11
11
  | "generate"
12
+ | "config"
12
13
  | "doctor"
13
14
  | "inspect"
14
15
  | "help"
@@ -28,6 +29,10 @@ export type GenerateCliKind =
28
29
  | "migration"
29
30
  | "help";
30
31
 
32
+ export type ConfigCliAction =
33
+ | "check"
34
+ | "help";
35
+
31
36
  export interface CliOptions {
32
37
  command: CliCommand;
33
38
 
@@ -54,6 +59,8 @@ export interface CliOptions {
54
59
  generateKind?: GenerateCliKind;
55
60
 
56
61
  generateName?: string;
62
+
63
+ configAction?: ConfigCliAction;
57
64
  }
58
65
 
59
66
  export function parseCliArgs(
@@ -97,6 +104,9 @@ export function parseCliArgs(
97
104
  let generateName:
98
105
  string | undefined;
99
106
 
107
+ let configAction:
108
+ ConfigCliAction | undefined;
109
+
100
110
  let commandSet = false;
101
111
 
102
112
  for (
@@ -120,6 +130,11 @@ export function parseCliArgs(
120
130
  command === "generate"
121
131
  ) {
122
132
  generateKind = "help";
133
+ } else if (
134
+ commandSet &&
135
+ command === "config"
136
+ ) {
137
+ configAction = "help";
123
138
  } else {
124
139
  command = "help";
125
140
  }
@@ -268,13 +283,6 @@ export function parseCliArgs(
268
283
  );
269
284
  }
270
285
 
271
- /**
272
- * npm compatibility
273
- *
274
- * Some npm environments may consume an unknown script
275
- * option such as --port and leave only its numeric value
276
- * in argv. Accept a bare number for dev/start servers.
277
- */
278
286
  if (
279
287
  isPortShortcut(argument) &&
280
288
  (
@@ -374,6 +382,32 @@ export function parseCliArgs(
374
382
  );
375
383
  }
376
384
 
385
+ if (
386
+ commandSet &&
387
+ command === "config"
388
+ ) {
389
+ if (
390
+ configAction === undefined
391
+ ) {
392
+ if (
393
+ argument === "check" ||
394
+ argument === "help"
395
+ ) {
396
+ configAction =
397
+ argument;
398
+ continue;
399
+ }
400
+
401
+ throw new Error(
402
+ `Unknown config command: ${argument}`
403
+ );
404
+ }
405
+
406
+ throw new Error(
407
+ `Unexpected argument: ${argument}`
408
+ );
409
+ }
410
+
377
411
  if (commandSet) {
378
412
  throw new Error(
379
413
  `Unexpected argument: ${argument}`
@@ -388,6 +422,7 @@ export function parseCliArgs(
388
422
  argument === "update" ||
389
423
  argument === "db" ||
390
424
  argument === "generate" ||
425
+ argument === "config" ||
391
426
  argument === "doctor" ||
392
427
  argument === "inspect" ||
393
428
  argument === "help" ||
@@ -418,10 +453,11 @@ export function parseCliArgs(
418
453
  if (
419
454
  json &&
420
455
  command !== "doctor" &&
421
- command !== "inspect"
456
+ command !== "inspect" &&
457
+ command !== "config"
422
458
  ) {
423
459
  throw new Error(
424
- "Option --json is only valid with `bcp doctor` or `bcp inspect`."
460
+ "Option --json is only valid with `bcp doctor`, `bcp inspect`, or `bcp config`."
425
461
  );
426
462
  }
427
463
 
@@ -464,6 +500,11 @@ export function parseCliArgs(
464
500
  generateName,
465
501
  }
466
502
  : {}),
503
+ ...(command === "config"
504
+ ? {
505
+ configAction,
506
+ }
507
+ : {}),
467
508
  };
468
509
  }
469
510
 
@@ -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
- : "Env";
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