@chidchanun/bcp 0.2.19 → 0.3.1

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,333 @@
1
+ # Dependency Injection & Service Container — 0.3.1
2
+
3
+ BCP Framework `0.3.1` adds a typed, server-only dependency injection container through `bcp/container` and integrates it with `bcp/application`.
4
+
5
+ The container is intentionally provider-neutral. It does not require decorators or reflection metadata.
6
+
7
+ ## Create typed service tokens
8
+
9
+ ```ts
10
+ import {
11
+ createServiceToken,
12
+ } from "bcp/container";
13
+
14
+ export interface UserRepository {
15
+ findById(id: string): Promise<unknown>;
16
+ }
17
+
18
+ export const userRepositoryToken =
19
+ createServiceToken<UserRepository>(
20
+ "user-repository"
21
+ );
22
+ ```
23
+
24
+ The token carries its value type at compile time, so `resolve(userRepositoryToken)` resolves as `UserRepository`.
25
+
26
+ ## Provider kinds
27
+
28
+ ### Value provider
29
+
30
+ ```ts
31
+ import {
32
+ provideValue,
33
+ } from "bcp/container";
34
+
35
+ const configToken =
36
+ createServiceToken<{
37
+ apiUrl: string;
38
+ }>("config");
39
+
40
+ const configProvider =
41
+ provideValue(
42
+ configToken,
43
+ {
44
+ apiUrl: "https://api.example.com",
45
+ }
46
+ );
47
+ ```
48
+
49
+ ### Factory provider
50
+
51
+ ```ts
52
+ const repositoryProvider =
53
+ provideFactory(
54
+ userRepositoryToken,
55
+ [
56
+ configToken,
57
+ ] as const,
58
+ (
59
+ context,
60
+ [config]
61
+ ) =>
62
+ createUserRepository(
63
+ config.apiUrl
64
+ )
65
+ );
66
+ ```
67
+
68
+ Dependency tuples are typed from their tokens.
69
+
70
+ A factory may also resolve dynamic dependencies:
71
+
72
+ ```ts
73
+ provideFactory(
74
+ serviceToken,
75
+ [] as const,
76
+ async (context) => {
77
+ const logger =
78
+ await context.resolve(
79
+ loggerToken
80
+ );
81
+ return new Service(logger);
82
+ }
83
+ );
84
+ ```
85
+
86
+ ### Class provider
87
+
88
+ ```ts
89
+ provideClass(
90
+ userServiceToken,
91
+ [
92
+ userRepositoryToken,
93
+ ] as const,
94
+ UserService
95
+ );
96
+ ```
97
+
98
+ BCP does not inspect constructor metadata. Dependencies remain explicit and reviewable.
99
+
100
+ ## Lifetimes
101
+
102
+ Providers support three lifetimes:
103
+
104
+ ```text
105
+ singleton one instance for the provider owner; shared across application scopes
106
+ scoped one instance per scope
107
+ transient a new instance for every resolve
108
+ ```
109
+
110
+ Example:
111
+
112
+ ```ts
113
+ provideFactory(
114
+ requestContextToken,
115
+ [] as const,
116
+ createRequestContext,
117
+ {
118
+ lifetime: "scoped",
119
+ }
120
+ );
121
+ ```
122
+
123
+ The default lifetime is `singleton`.
124
+
125
+ ## Create a standalone container
126
+
127
+ ```ts
128
+ import {
129
+ createServiceContainer,
130
+ } from "bcp/container";
131
+
132
+ const container =
133
+ createServiceContainer({
134
+ name: "orders",
135
+ providers: [
136
+ configProvider,
137
+ repositoryProvider,
138
+ ],
139
+ });
140
+
141
+ const repository =
142
+ await container.resolve(
143
+ userRepositoryToken
144
+ );
145
+ ```
146
+
147
+ Optional resolution:
148
+
149
+ ```ts
150
+ const feature =
151
+ await container.optional(
152
+ optionalFeatureToken
153
+ );
154
+ ```
155
+
156
+ A missing required provider throws `ServiceNotFoundError`.
157
+
158
+ ## Child and request scopes
159
+
160
+ Use `createScope()` for request/job/test lifetime boundaries:
161
+
162
+ ```ts
163
+ const requestScope =
164
+ container.createScope({
165
+ name: "request:123",
166
+ });
167
+
168
+ const requestContext =
169
+ await requestScope.resolve(
170
+ requestContextToken
171
+ );
172
+
173
+ await requestScope.dispose();
174
+ ```
175
+
176
+ Singleton instances remain shared. Scoped instances are isolated per scope. Transient instances are never cached.
177
+
178
+ Disposing a parent container disposes child scopes first.
179
+
180
+ ## Testing overrides
181
+
182
+ Scopes can replace selected providers without mutating the application container:
183
+
184
+ ```ts
185
+ const testScope =
186
+ container.createScope({
187
+ name: "test",
188
+ overrides: [
189
+ provideValue(
190
+ mailerToken,
191
+ fakeMailer
192
+ ),
193
+ ],
194
+ });
195
+ ```
196
+
197
+ This is the recommended dependency override mechanism for framework/application tests.
198
+
199
+ ## Dependency graph
200
+
201
+ ```ts
202
+ const graph =
203
+ container.graph();
204
+ ```
205
+
206
+ Each node reports:
207
+
208
+ ```text
209
+ token
210
+ description
211
+ lifetime
212
+ dependencies
213
+ overridden
214
+ ```
215
+
216
+ The graph is suitable for diagnostics and future developer tooling.
217
+
218
+ ## Circular dependencies
219
+
220
+ Explicit and dynamic resolutions keep a resolution path. Circular graphs fail with `ServiceResolutionError`:
221
+
222
+ ```text
223
+ A -> B -> C -> A
224
+ ```
225
+
226
+ BCP does not silently inject partial instances.
227
+
228
+ ## Disposal
229
+
230
+ Providers may define cleanup:
231
+
232
+ ```ts
233
+ provideFactory(
234
+ databaseToken,
235
+ [] as const,
236
+ createDatabase,
237
+ {
238
+ async dispose(database) {
239
+ await database.close();
240
+ },
241
+ }
242
+ );
243
+ ```
244
+
245
+ Resolved disposable services are cleaned up in reverse creation order. Concurrent/repeated `dispose()` calls share the same disposal operation.
246
+
247
+ If one or more disposers fail, the container completes the remaining cleanup and throws `ServiceDisposalError` containing the failures.
248
+
249
+ ## Application Platform integration
250
+
251
+ `createApp()` accepts providers directly:
252
+
253
+ ```ts
254
+ import {
255
+ createApp,
256
+ } from "bcp/application";
257
+
258
+ const app =
259
+ createApp({
260
+ name: "orders-api",
261
+ providers: [
262
+ configProvider,
263
+ repositoryProvider,
264
+ ],
265
+ async setup(context) {
266
+ const repository =
267
+ await context.container
268
+ .resolve(
269
+ userRepositoryToken
270
+ );
271
+ },
272
+ });
273
+ ```
274
+
275
+ Application accessors:
276
+
277
+ ```text
278
+ app.container
279
+ app.context.container
280
+ app.register(provider)
281
+ app.createScope(options)
282
+ ```
283
+
284
+ `app.register()` is only available before `start()`, matching plugin/resource registration semantics.
285
+
286
+ The Application Platform deployment order is now:
287
+
288
+ ```text
289
+ bcp:container
290
+ -> bcp:plugins
291
+ -> application infrastructure resources
292
+ -> bcp:application
293
+ ```
294
+
295
+ Shutdown reverses that order:
296
+
297
+ ```text
298
+ bcp:application
299
+ -> infrastructure resources
300
+ -> bcp:plugins
301
+ -> bcp:container
302
+ ```
303
+
304
+ This keeps injected infrastructure alive until workers/plugins/application hooks have stopped.
305
+
306
+ ## Legacy plugin services
307
+
308
+ `context.services` and `app.services` remain available for Plugin Platform compatibility. `0.3.1` does not silently translate string/symbol plugin-service keys into typed DI tokens.
309
+
310
+ Use `context.container` for new typed dependency injection code. Existing plugins can migrate incrementally.
311
+
312
+ ## Packaging
313
+
314
+ `bcp/container` is server-only and prepared npm packages expose:
315
+
316
+ ```text
317
+ types -> packages/client/src/container.ts
318
+ default -> packages/client/src/container.mjs
319
+ browser -> packages/client/src/server-only.browser.mjs
320
+ ```
321
+
322
+ ## Validation
323
+
324
+ Framework maintainers should run:
325
+
326
+ ```bash
327
+ npm run typecheck
328
+ npm run test:unit
329
+ npm run test:package
330
+ npm run api:check
331
+ npm run release:readiness
332
+ npm run rc:check
333
+ ```
@@ -1,27 +1,12 @@
1
1
  # Stability & API Freeze — 0.2.19
2
2
 
3
- BCP Framework `0.2.19` is the final stabilization milestone for the `0.2.x` platform before the next `0.3.0` application-platform baseline.
3
+ BCP Framework `0.2.19` is the final stabilization milestone for the `0.2.x` platform and the compatibility point used to establish the `0.3.0` Application Platform baseline.
4
4
 
5
- This release intentionally does **not** add a new application subsystem. Its purpose is to freeze the supported public package/CLI contract, strengthen release gates and catch accidental compatibility regressions before publication.
5
+ This release intentionally did **not** add a new application subsystem. Its purpose was to freeze the supported public package/CLI contract, strengthen release gates and catch accidental compatibility regressions.
6
6
 
7
- ## Frozen contract
7
+ ## Historical 0.2.19 contract
8
8
 
9
- The committed source of truth is:
10
-
11
- ```text
12
- docs/api-freeze-snapshot.json
13
- ```
14
-
15
- It records:
16
-
17
- - the supported `bcp/*` public entrypoint set;
18
- - the supported CLI command set;
19
- - prepared npm `exports` including `types`, `default` and `browser` targets;
20
- - API source/environment ownership from `docs/api-manifest.json`;
21
- - the compatibility baseline (`0.2.18`);
22
- - the no-intentional-breaking-change policy for `0.2.19`.
23
-
24
- The frozen public package entrypoints are:
9
+ The `0.2.19` release froze these public package entrypoints:
25
10
 
26
11
  ```text
27
12
  bcp
@@ -45,7 +30,37 @@ bcp/server-only
45
30
  bcp/middleware
46
31
  ```
47
32
 
48
- `./package.json` is also frozen as a package export, although it is not an application API entrypoint.
33
+ `./package.json` was also included in the prepared npm package contract, although it is not an application API module.
34
+
35
+ The historical compatibility policy was:
36
+
37
+ ```text
38
+ release: 0.2.19
39
+ previous baseline: 0.2.18
40
+ intentional breaking changes: false
41
+ freeze state: frozen
42
+ ```
43
+
44
+ ## Current snapshot file
45
+
46
+ The repository continues to use:
47
+
48
+ ```text
49
+ docs/api-freeze-snapshot.json
50
+ ```
51
+
52
+ as the **current reviewed API baseline**, not as a permanently immutable copy of the `0.2.19` file.
53
+
54
+ After the Application Platform work begins, that file advances to the reviewed `0.3.0` baseline with:
55
+
56
+ ```text
57
+ version: 0.3.0
58
+ baselineVersion: 0.2.19
59
+ ```
60
+
61
+ and adds `bcp/application` while preserving the `0.2.19` entrypoints.
62
+
63
+ The historical `0.2.19` release notes and this guide document why the freeze exists; the current snapshot is the contract enforced by the active release gate.
49
64
 
50
65
  ## API compatibility gate
51
66
 
@@ -59,81 +74,49 @@ The compatibility checker prepares the exact npm staging package, generates the
59
74
 
60
75
  The check fails when, for example:
61
76
 
62
- - a public entrypoint is removed or added;
63
- - a CLI command changes without an explicit new platform baseline;
64
- - `bcp/server` stops resolving to `server.mjs` in the prepared package;
77
+ - a public entrypoint changes unexpectedly;
78
+ - a CLI command changes outside an intentional baseline update;
79
+ - a compiled runtime target drifts;
65
80
  - a server-only browser poison target disappears;
66
81
  - an entrypoint changes API source/environment ownership;
67
- - the current version/baseline no longer matches the committed freeze.
68
-
69
- This prevents source metadata, prepared npm metadata and documentation metadata from drifting independently.
82
+ - version/baseline metadata no longer matches the committed contract.
70
83
 
71
84
  ## Snapshot regeneration
72
85
 
73
- To intentionally regenerate the contract:
86
+ To intentionally regenerate the current contract:
74
87
 
75
88
  ```bash
76
89
  npm run api:snapshot
77
90
  ```
78
91
 
79
- That command rewrites:
80
-
81
- ```text
82
- docs/api-freeze-snapshot.json
83
- ```
84
-
85
- For `0.2.19`, snapshot changes require explicit review because the milestone promises no intentional breaking changes from `0.2.18`.
92
+ Do not regenerate the snapshot merely to silence an unexpected compatibility failure. Snapshot updates should accompany an intentional, reviewed platform-baseline change with migration documentation and regression coverage.
86
93
 
87
- Large or breaking public-surface changes should normally be deferred to `0.3.0` rather than silently updating the `0.2.19` snapshot.
94
+ `0.3.0` is such an intentional additive baseline update: it adds `bcp/application` and uses `0.2.19` as its previous compatibility baseline.
88
95
 
89
96
  ## Release readiness
90
97
 
91
- Run:
98
+ The current release uses:
92
99
 
93
100
  ```bash
94
101
  npm run release:readiness
95
- ```
96
-
97
- The readiness report checks:
98
-
99
- - root/client/create-app version parity;
100
- - `package-lock.json` version parity;
101
- - platform/API/docs-web version parity;
102
- - `unreleased` release state;
103
- - `0.2.18` previous-baseline metadata;
104
- - no intentional breaking changes;
105
- - API freeze snapshot version/baseline;
106
- - public entrypoint parity;
107
- - CLI freeze parity;
108
- - current release notes and docs route;
109
- - stability capability flags.
110
-
111
- To also persist a local machine-readable report:
112
-
113
- ```bash
114
102
  npm run release:readiness:report
115
103
  ```
116
104
 
117
- The report is written to:
105
+ The readiness gate evolves with the active milestone. During `0.2.19` it validated freeze metadata; for `0.3.0` it validates the Application Platform baseline, `bcp/application` ownership, package/runtime parity and the `0.2.19` previous baseline.
106
+
107
+ The optional report is written to:
118
108
 
119
109
  ```text
120
110
  .bcp-framework/release-readiness.json
121
111
  ```
122
112
 
123
- It is a local build artifact and is not the release source of truth.
124
-
125
113
  ## RC integration
126
114
 
127
- `0.2.19` strengthens the release pipeline:
115
+ The stability infrastructure remains part of every later RC:
128
116
 
129
117
  ```text
130
118
  npm run typecheck
131
119
  npm test
132
- -> unit
133
- -> integration
134
- -> e2e
135
- -> prepared-package smoke
136
- -> Stability & API Freeze smoke
137
120
  npm run api:check
138
121
  npm run release:readiness
139
122
  release metadata check
@@ -143,37 +126,25 @@ npm publish dry-run
143
126
 
144
127
  `npm run rc:check` must pass on the exact commit that will be tagged.
145
128
 
146
- ## Lifecycle stabilization
147
-
148
- The stability suite adds explicit idempotency coverage for Deployment Platform v2:
149
-
150
- - concurrent/repeated `start()` calls must start a resource once;
151
- - concurrent/repeated `shutdown()` calls must stop a resource once;
152
- - shutdown remains reverse ordered;
153
- - a stopped runtime cannot be restarted.
154
-
155
- Existing subsystem-specific suites remain authoritative for jobs, workflows, outbox/events, realtime, cache, plugins and deployment behavior.
129
+ ## Lifecycle stabilization retained
156
130
 
157
- ## Compatibility policy
131
+ The `0.2.19` suite added explicit idempotency coverage for Deployment Platform v2:
158
132
 
159
- `0.2.19` declares:
160
-
161
- ```text
162
- previous baseline: 0.2.18
163
- intentional breaking changes: false
164
- freeze state: frozen
165
- ```
133
+ - concurrent/repeated `start()` calls start a resource once;
134
+ - concurrent/repeated `shutdown()` calls stop a resource once;
135
+ - shutdown remains reverse ordered.
166
136
 
167
- Bug fixes may still change incorrect implementation behavior, but they must not silently remove documented public entrypoints or change their package-resolution contract.
137
+ Those regressions remain part of the unit suite after the baseline advances.
168
138
 
169
- ## Moving to 0.3.0
139
+ ## Relationship to 0.3.0
170
140
 
171
- `0.3.0` is the next platform baseline. That milestone may intentionally revise the frozen surface, but changes should be accompanied by:
141
+ `0.3.0 BCP Application Platform` follows the process established by this freeze:
172
142
 
173
- 1. a reviewed API snapshot update;
174
- 2. explicit compatibility/breaking-change metadata;
175
- 3. migration documentation;
176
- 4. updated package/docs manifests;
177
- 5. regression and prepared-package coverage.
143
+ 1. use `0.2.19` as the previous baseline;
144
+ 2. add the intentional public surface (`bcp/application`);
145
+ 3. update and review the current API snapshot;
146
+ 4. add migration documentation;
147
+ 5. update package/platform/docs manifests;
148
+ 6. add unit and prepared-package regression coverage.
178
149
 
179
- The `0.2.19` snapshot therefore becomes the comparison point for the next platform-generation work.
150
+ See [Application Platform](application-platform.md) and [Migrating to 0.3.0](migration-0.3.md).
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chidchanun/bcp",
3
- "version": "0.2.19",
4
- "description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
3
+ "version": "0.3.1",
4
+ "description": "BCP Framework - a React full-stack application platform with routing, SSR, APIs, lifecycle composition and standalone production builds.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -16,6 +16,8 @@
16
16
  "bcp",
17
17
  "react",
18
18
  "framework",
19
+ "application-platform",
20
+ "dependency-injection",
19
21
  "ssr",
20
22
  "routing",
21
23
  "fullstack"
@@ -102,6 +104,16 @@
102
104
  "browser": "./packages/client/src/server-only.browser.mjs",
103
105
  "default": "./packages/client/src/deployment.mjs"
104
106
  },
107
+ "./container": {
108
+ "types": "./packages/client/src/container.ts",
109
+ "browser": "./packages/client/src/server-only.browser.mjs",
110
+ "default": "./packages/client/src/container.mjs"
111
+ },
112
+ "./application": {
113
+ "types": "./packages/client/src/application.ts",
114
+ "browser": "./packages/client/src/server-only.browser.mjs",
115
+ "default": "./packages/client/src/application.mjs"
116
+ },
105
117
  "./server": {
106
118
  "types": "./packages/client/src/server.ts",
107
119
  "browser": "./packages/client/src/server-only.browser.mjs",
@@ -36,6 +36,8 @@ const SERVER_ONLY_IMPORTS =
36
36
  "bcp/plugins",
37
37
  "bcp/observability",
38
38
  "bcp/deployment",
39
+ "bcp/container",
40
+ "bcp/application",
39
41
  ]);
40
42
 
41
43
  export function validateClientBoundaries(