@chidchanun/bcp 0.1.21 → 0.1.23

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,142 @@
1
+ # BCP Framework 0.1.22
2
+
3
+ BCP 0.1.22 adds the first dedicated Developer Tools milestone.
4
+
5
+ ## Highlights
6
+
7
+ - Added `bcp doctor` for project/runtime health diagnostics.
8
+ - Added `bcp inspect` for environment, resolved config, dependency and route inspection.
9
+ - Added `--json` output for both developer-tool commands.
10
+ - `bcp doctor` returns a non-zero exit code when blocking checks fail, so it can be used as a CI health gate.
11
+ - Added React / React DOM version and package-location checks to detect duplicate React installations before they surface as `Invalid hook call` runtime failures.
12
+ - Added route conflict and client-boundary validation without starting the dev server.
13
+ - Added development environment diagnostics without printing private environment values.
14
+ - Added unit coverage for CLI parsing, version checks, missing-project diagnostics and deterministic inspection output.
15
+ - Added the `bcp-framework` executable alias for Windows systems where the `bcp` command is already occupied by Microsoft SQL Server's `bcp.exe` utility.
16
+ - Fixed `<Form>` SSR so framework runtime rendering no longer depends on a global `React` binding when an application uses the classic JSX transform.
17
+ - Added CLI preflight protection against installing `bcp` and `@chidchanun/bcp` as two separate framework copies, which can split loader/context state across package instances.
18
+
19
+ ## CLI executable compatibility
20
+
21
+ The published package now exposes both executable names:
22
+
23
+ ```text
24
+ bcp
25
+ bcp-framework
26
+ ```
27
+
28
+ Both names execute the same BCP Framework CLI. Existing applications and npm scripts can continue using `bcp` unchanged.
29
+
30
+ On Windows installations that include Microsoft SQL Server tools, use the collision-free alias for direct shell commands:
31
+
32
+ ```powershell
33
+ bcp-framework doctor
34
+ bcp-framework inspect
35
+ bcp-framework dev
36
+ bcp-framework build
37
+ ```
38
+
39
+ Alternatively, explicitly run the project-local `bcp` executable through npm:
40
+
41
+ ```powershell
42
+ npm exec -- bcp doctor
43
+ ```
44
+
45
+ ## `bcp doctor`
46
+
47
+ ```bash
48
+ bcp doctor
49
+ ```
50
+
51
+ The health report checks:
52
+
53
+ - Node.js support,
54
+ - `package.json`, `app/` and optional `public/`,
55
+ - declared and installed BCP dependencies,
56
+ - React and React DOM versions,
57
+ - whether BCP and the application resolve the same React/React DOM package roots,
58
+ - development environment loading,
59
+ - resolved framework config,
60
+ - page/API route discovery,
61
+ - client/server module boundaries.
62
+
63
+ Use JSON output in automation:
64
+
65
+ ```bash
66
+ bcp doctor --json
67
+ ```
68
+
69
+ ## `bcp inspect`
70
+
71
+ ```bash
72
+ bcp inspect
73
+ ```
74
+
75
+ The inspection report includes:
76
+
77
+ - framework/runtime identity,
78
+ - loaded development env filenames,
79
+ - public environment variable names,
80
+ - resolved BCP configuration,
81
+ - installed BCP/React/React DOM versions,
82
+ - page and API route mappings.
83
+
84
+ Machine-readable output:
85
+
86
+ ```bash
87
+ bcp inspect --json
88
+ ```
89
+
90
+ ## Duplicate React diagnostics
91
+
92
+ The Developer Tools milestone includes a check motivated by the 0.1.21 local release verification workflow.
93
+
94
+ If an application is linked directly to a framework staging directory, the application may resolve React from its own `node_modules` while the BCP SSR renderer resolves React DOM from the framework checkout. `bcp doctor` detects this package-root split and reports it as a failure before SSR reaches the `Invalid hook call` error.
95
+
96
+ For local release verification, continue using packed `.tgz` artifacts rather than installing `.package/bcp` as a linked directory.
97
+
98
+ ## Duplicate BCP package protection
99
+
100
+ Local tarball verification must keep one BCP package root in the application.
101
+
102
+ Installing a tarball directly when the application already declares an npm alias named `bcp` can leave both of these paths installed:
103
+
104
+ ```text
105
+ node_modules/bcp
106
+ node_modules/@chidchanun/bcp
107
+ ```
108
+
109
+ That layout is unsafe because application imports can use one `BcpLoaderDataProvider` context while the CLI/SSR runtime uses the other package copy. The visible error can therefore incorrectly say that `useLoaderData()` has no loader data.
110
+
111
+ BCP 0.1.22 now checks this before runtime commands start and fails with an actionable duplicate-install message.
112
+
113
+ For local tarball verification, install the artifact under the existing dependency key:
114
+
115
+ ```powershell
116
+ npm install `
117
+ --no-save `
118
+ --package-lock=false `
119
+ "bcp@file:D:\bcp-framework\.package\artifacts\chidchanun-bcp-0.1.22.tgz"
120
+ ```
121
+
122
+ ## SSR JSX-runtime compatibility
123
+
124
+ Framework runtime components must not inherit JSX runtime assumptions from the consuming application's `tsconfig.json`.
125
+
126
+ `<Form>` now renders its provider and native `<form>` element through `createElement()`. This keeps SSR compatible with both automatic JSX runtime projects and projects that still use the classic JSX transform, where unbound generated `React.createElement(...)` calls would otherwise produce:
127
+
128
+ ```text
129
+ ReferenceError: React is not defined
130
+ ```
131
+
132
+ Regression coverage includes server rendering and a classic-JSX transform check that rejects implicit `React.createElement` output from the Form runtime source.
133
+
134
+ ## Compatibility
135
+
136
+ 0.1.22 does not change public rendering, routing, database, authentication, validation, middleware or error-handling APIs.
137
+
138
+ The Developer Tools commands and `bcp-framework` executable alias are additive. Existing BCP applications can continue using current commands unchanged.
139
+
140
+ ## Next milestone
141
+
142
+ The next planned milestone is BCP 0.1.23 — Logging & Observability.
@@ -0,0 +1,131 @@
1
+ # BCP Framework 0.1.23
2
+
3
+ BCP 0.1.23 introduces the Logging & Observability foundation and carries forward the stabilization fixes verified against the BCP documentation application during the 0.1.22 release-candidate cycle.
4
+
5
+ ## Highlights
6
+
7
+ - Added structured server logging through `bcp/server`.
8
+ - Added `logger.debug()`, `logger.info()`, `logger.warn()` and `logger.error()`.
9
+ - Added child loggers with persistent structured bindings.
10
+ - Added `requestLogger()` for request-scoped `requestId`, HTTP method and path bindings.
11
+ - Added `attachRequestId()` for explicit `X-Request-Id` response propagation.
12
+ - Added `BCP_LOG_LEVEL=debug|info|warn|error|silent`.
13
+ - Added `BCP_LOG_FORMAT=pretty|json`.
14
+ - Added safe serialization for `Error`, `bigint` and circular structured fields.
15
+ - Development API request logs now flow through the structured logger while framework bootstrap traffic remains filtered.
16
+ - Development module-import and SSR timing lines are normalized into `module.import` and `ssr.render` debug events.
17
+ - Added regression coverage for log filtering, JSON records, error serialization, request-scoped identity and response request IDs.
18
+
19
+ ## Server logger
20
+
21
+ ```ts
22
+ import {
23
+ logger,
24
+ } from "bcp/server";
25
+
26
+ logger.info(
27
+ "User loaded",
28
+ {
29
+ userId: 42,
30
+ }
31
+ );
32
+ ```
33
+
34
+ The default logger reads its level and format from the environment at log time, so normal application configuration can set:
35
+
36
+ ```text
37
+ BCP_LOG_LEVEL=info
38
+ BCP_LOG_FORMAT=json
39
+ ```
40
+
41
+ ## Request-scoped logger
42
+
43
+ ```ts
44
+ import {
45
+ requestLogger,
46
+ } from "bcp/server";
47
+
48
+ export async function loader() {
49
+ const log =
50
+ await requestLogger({
51
+ feature: "categories",
52
+ });
53
+
54
+ log.info(
55
+ "Loading categories"
56
+ );
57
+
58
+ return {
59
+ items: [],
60
+ };
61
+ }
62
+ ```
63
+
64
+ The child logger binds:
65
+
66
+ ```text
67
+ requestId
68
+ method
69
+ path
70
+ ```
71
+
72
+ using the same request context already used by `requestId()`, `requestMethod()` and `requestUrl()`.
73
+
74
+ ## Request ID response helper
75
+
76
+ ```ts
77
+ import {
78
+ attachRequestId,
79
+ json,
80
+ } from "bcp/server";
81
+
82
+ export async function GET() {
83
+ return attachRequestId(
84
+ json({
85
+ ok: true,
86
+ })
87
+ );
88
+ }
89
+ ```
90
+
91
+ The response contains `X-Request-Id` with the active request identity.
92
+
93
+ ## Development observability
94
+
95
+ Application API request logs retain the existing development noise filter but are now emitted as structured events with:
96
+
97
+ ```text
98
+ event=http.request
99
+ method
100
+ path
101
+ status
102
+ durationMs
103
+ ```
104
+
105
+ SSR and module import measurements are emitted at `debug` level as:
106
+
107
+ ```text
108
+ ssr.render
109
+ module.import
110
+ ```
111
+
112
+ Set `BCP_LOG_LEVEL=debug` when profiling development rendering.
113
+
114
+ ## Stabilization fixes included
115
+
116
+ The 0.1.23 source line also contains the fixes completed while validating 0.1.22 against `bcp-docs`:
117
+
118
+ - the `bcp-framework` Windows-safe executable alias avoids the Microsoft SQL Server `bcp.exe` command collision,
119
+ - `<Form>` SSR no longer depends on an implicit global `React` binding under classic JSX transforms,
120
+ - the CLI detects duplicate `bcp` / `@chidchanun/bcp` installations before they produce split React contexts or misleading `useLoaderData()` failures,
121
+ - local release verification guidance uses packed tarballs under the existing `bcp` dependency key so only one framework copy is installed.
122
+
123
+ ## Compatibility
124
+
125
+ Logging is additive and server-only. Existing rendering, routing, loaders, guards, actions, middleware, validation, database and authentication APIs remain compatible.
126
+
127
+ Existing application code does not need to adopt the logger to upgrade to 0.1.23.
128
+
129
+ ## Next milestone
130
+
131
+ The next planned milestone is BCP 0.1.24 — File Upload foundation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chidchanun/bcp",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
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",
@@ -24,7 +24,8 @@
24
24
  "node": ">=24.11.0"
25
25
  },
26
26
  "bin": {
27
- "bcp": "packages/cli/bin/bcp.mjs"
27
+ "bcp": "packages/cli/bin/bcp.mjs",
28
+ "bcp-framework": "packages/cli/bin/bcp.mjs"
28
29
  },
29
30
  "exports": {
30
31
  ".": {
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import fs from "node:fs";
4
+ import path from "node:path";
3
5
  import {
4
6
  spawn,
5
7
  } from "node:child_process";
@@ -7,6 +9,46 @@ import {
7
9
  fileURLToPath,
8
10
  } from "node:url";
9
11
 
12
+ const command =
13
+ process.argv[2] ??
14
+ "dev";
15
+
16
+ const duplicateInstall =
17
+ findDuplicateFrameworkInstall(
18
+ process.cwd()
19
+ );
20
+
21
+ if (duplicateInstall) {
22
+ const message = [
23
+ "BCP Framework: duplicate framework installations detected.",
24
+ "",
25
+ `bcp: ${duplicateInstall.bcp}`,
26
+ `@chidchanun/bcp: ${duplicateInstall.scoped}`,
27
+ "",
28
+ "The application and the BCP CLI can load different framework copies. This breaks shared React contexts such as useLoaderData() and may produce misleading missing-loader errors.",
29
+ "",
30
+ "Keep only one application dependency named bcp. For local tarball verification install it under the bcp dependency key, for example:",
31
+ "npm install --no-save --package-lock=false \"bcp@file:D:\\\\path\\\\to\\\\chidchanun-bcp-0.1.22.tgz\"",
32
+ ].join("\n");
33
+
34
+ if (
35
+ command !== "doctor" &&
36
+ command !== "inspect"
37
+ ) {
38
+ console.error(
39
+ message
40
+ );
41
+ process.exitCode =
42
+ 1;
43
+ process.exit();
44
+ }
45
+
46
+ console.warn(
47
+ message
48
+ );
49
+ console.warn("");
50
+ }
51
+
10
52
  const bootstrapFile =
11
53
  fileURLToPath(
12
54
  new URL(
@@ -91,3 +133,72 @@ child.once(
91
133
  code ?? 1;
92
134
  }
93
135
  );
136
+
137
+ function findDuplicateFrameworkInstall(
138
+ rootDirectory
139
+ ) {
140
+ const bcp =
141
+ resolvePackageRoot(
142
+ path.join(
143
+ rootDirectory,
144
+ "node_modules",
145
+ "bcp"
146
+ )
147
+ );
148
+ const scoped =
149
+ resolvePackageRoot(
150
+ path.join(
151
+ rootDirectory,
152
+ "node_modules",
153
+ "@chidchanun",
154
+ "bcp"
155
+ )
156
+ );
157
+
158
+ if (
159
+ !bcp ||
160
+ !scoped ||
161
+ normalizePath(bcp) ===
162
+ normalizePath(scoped)
163
+ ) {
164
+ return null;
165
+ }
166
+
167
+ return {
168
+ bcp,
169
+ scoped,
170
+ };
171
+ }
172
+
173
+ function resolvePackageRoot(
174
+ directory
175
+ ) {
176
+ if (
177
+ !fs.existsSync(
178
+ path.join(
179
+ directory,
180
+ "package.json"
181
+ )
182
+ )
183
+ ) {
184
+ return null;
185
+ }
186
+
187
+ try {
188
+ return fs.realpathSync(
189
+ directory
190
+ );
191
+ } catch {
192
+ return path.resolve(
193
+ directory
194
+ );
195
+ }
196
+ }
197
+
198
+ function normalizePath(
199
+ value
200
+ ) {
201
+ return process.platform === "win32"
202
+ ? value.toLowerCase()
203
+ : value;
204
+ }
@@ -8,6 +8,8 @@ export type CliCommand =
8
8
  | "routes"
9
9
  | "update"
10
10
  | "db"
11
+ | "doctor"
12
+ | "inspect"
11
13
  | "help"
12
14
  | "version";
13
15
 
@@ -33,6 +35,8 @@ export interface CliOptions {
33
35
 
34
36
  updateDryRun?: boolean;
35
37
 
38
+ json?: boolean;
39
+
36
40
  dbAction?: DatabaseCliAction;
37
41
 
38
42
  dbMigrationName?: string;
@@ -61,6 +65,9 @@ export function parseCliArgs(
61
65
  let updateDryRun =
62
66
  false;
63
67
 
68
+ let json =
69
+ false;
70
+
64
71
  let dbAction:
65
72
  DatabaseCliAction | undefined;
66
73
 
@@ -209,6 +216,14 @@ export function parseCliArgs(
209
216
  continue;
210
217
  }
211
218
 
219
+ if (
220
+ argument === "--json"
221
+ ) {
222
+ json =
223
+ true;
224
+ continue;
225
+ }
226
+
212
227
  if (
213
228
  argument.startsWith("-")
214
229
  ) {
@@ -297,6 +312,8 @@ export function parseCliArgs(
297
312
  argument === "routes" ||
298
313
  argument === "update" ||
299
314
  argument === "db" ||
315
+ argument === "doctor" ||
316
+ argument === "inspect" ||
300
317
  argument === "help" ||
301
318
  argument === "version"
302
319
  ) {
@@ -322,6 +339,16 @@ export function parseCliArgs(
322
339
  );
323
340
  }
324
341
 
342
+ if (
343
+ json &&
344
+ command !== "doctor" &&
345
+ command !== "inspect"
346
+ ) {
347
+ throw new Error(
348
+ "Option --json is only valid with `bcp doctor` or `bcp inspect`."
349
+ );
350
+ }
351
+
325
352
  return {
326
353
  command,
327
354
  rootDirectory,
@@ -330,6 +357,11 @@ export function parseCliArgs(
330
357
  updateTarget,
331
358
  updateCheck,
332
359
  updateDryRun,
360
+ ...(json
361
+ ? {
362
+ json: true,
363
+ }
364
+ : {}),
333
365
  ...(command === "db"
334
366
  ? {
335
367
  dbAction,