@chidchanun/bcp 0.3.0 → 0.3.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,116 @@
1
+ # BCP Framework 0.3.1 — Dependency Injection & Service Container
2
+
3
+ **State:** unreleased
4
+
5
+ `0.3.1` adds typed dependency injection to the Application Platform without removing the Plugin Platform service registry introduced in `0.2.x`.
6
+
7
+ ## Highlights
8
+
9
+ - new server-only `bcp/container` public entrypoint;
10
+ - typed `ServiceToken<T>` values;
11
+ - value, factory and class providers;
12
+ - singleton, scoped and transient lifetimes;
13
+ - child/request scopes;
14
+ - scope-local provider overrides for tests;
15
+ - dependency graph diagnostics;
16
+ - circular dependency detection;
17
+ - reverse-order disposal and aggregate disposal errors;
18
+ - concurrent/repeated disposal idempotency;
19
+ - `createApp()` integration through `providers`, `container`, `register()` and `createScope()`;
20
+ - compiled `container.mjs` production runtime;
21
+ - server-only browser poison boundary;
22
+ - unit and prepared-package smoke coverage.
23
+
24
+ ## Public APIs
25
+
26
+ ```text
27
+ createServiceToken
28
+ createServiceContainer
29
+ provideValue
30
+ provideFactory
31
+ provideClass
32
+ ServiceNotFoundError
33
+ ServiceResolutionError
34
+ ServiceDisposalError
35
+ ```
36
+
37
+ Important types:
38
+
39
+ ```text
40
+ ServiceToken<T>
41
+ ServiceProvider<T>
42
+ ServiceLifetime
43
+ ServiceFactoryContext
44
+ ServiceContainer
45
+ ServiceScope
46
+ ServiceScopeOptions
47
+ ServiceGraphNode
48
+ ```
49
+
50
+ ## Application Platform changes
51
+
52
+ `ApplicationDefinition` now accepts:
53
+
54
+ ```ts
55
+ providers?: readonly ServiceProvider<any>[];
56
+ ```
57
+
58
+ `Application` and `ApplicationContext` now expose:
59
+
60
+ ```text
61
+ container
62
+ ```
63
+
64
+ Applications can also call:
65
+
66
+ ```text
67
+ app.register(provider)
68
+ app.createScope(options)
69
+ ```
70
+
71
+ The existing `app.services` / `context.services` Plugin Platform registry remains supported for compatibility.
72
+
73
+ ## Lifecycle
74
+
75
+ The application deployment chain now starts the DI container resource before the plugin host and application infrastructure. Shutdown reverses the chain so disposable injected services remain alive until application/plugin/resource shutdown completes.
76
+
77
+ ## Compatibility
78
+
79
+ Previous baseline:
80
+
81
+ ```text
82
+ 0.3.0
83
+ ```
84
+
85
+ Intentional breaking changes:
86
+
87
+ ```text
88
+ false
89
+ ```
90
+
91
+ `0.3.1` is additive. Existing `0.3.0` applications do not need to adopt dependency injection immediately.
92
+
93
+ ## Packaging
94
+
95
+ Prepared npm packages expose:
96
+
97
+ ```text
98
+ bcp/container -> packages/client/src/container.mjs
99
+ ```
100
+
101
+ with TypeScript declarations sourced from `packages/client/src/container.ts` and browser builds redirected to the server-only poison module.
102
+
103
+ ## Validation
104
+
105
+ Before publication run the complete framework RC sequence on the final commit:
106
+
107
+ ```bash
108
+ npm run typecheck
109
+ npm run test:unit
110
+ npm run test:integration
111
+ npm run test:e2e
112
+ npm run test:package
113
+ npm run api:check
114
+ npm run release:readiness
115
+ npm run rc:check
116
+ ```
@@ -0,0 +1,125 @@
1
+ # BCP Framework 0.3.2 — Module System v2
2
+
3
+ **State:** unreleased
4
+
5
+ BCP Framework `0.3.2` adds a first-class server-side application module system on top of the Application Platform and Dependency Injection container introduced in `0.3.0` and `0.3.1`.
6
+
7
+ ## Highlights
8
+
9
+ - new server-only `bcp/modules` public entrypoint;
10
+ - dependency-first module import graph;
11
+ - duplicate-name and circular-dependency detection;
12
+ - typed DI providers owned by modules;
13
+ - explicit token exports and imported-token re-exports;
14
+ - existing BCP plugin contribution from modules;
15
+ - legacy shared-service contribution for plugin interoperability;
16
+ - module-owned Deployment Platform resources;
17
+ - module `setup/start/stop/dispose` lifecycle;
18
+ - startup cleanup when a module setup/start hook fails;
19
+ - module config parser/schema support;
20
+ - module graph/record diagnostics;
21
+ - `createApp()` integration with both Module System v2 and legacy `bcp/plugins` modules;
22
+ - compiled `modules.mjs` production runtime;
23
+ - browser poison boundary for `bcp/modules`;
24
+ - unit and prepared-package smoke coverage.
25
+
26
+ ## Public API
27
+
28
+ ```ts
29
+ import {
30
+ MODULE_V2_KIND,
31
+ ModuleDependencyError,
32
+ ModuleLifecycleError,
33
+ composeModules,
34
+ defineModule,
35
+ isModuleDefinition,
36
+ resolveModuleOrder,
37
+ } from "bcp/modules";
38
+ ```
39
+
40
+ Important public types:
41
+
42
+ ```text
43
+ ModuleDefinition
44
+ ModuleInput
45
+ ModuleContext
46
+ ModuleComposition
47
+ ModuleRecord
48
+ ModuleConfigSchema
49
+ ModuleConfigParser
50
+ ```
51
+
52
+ ## Application integration
53
+
54
+ `ApplicationDefinition.modules` now accepts both:
55
+
56
+ ```text
57
+ legacy PluginModule from bcp/plugins
58
+ ModuleDefinition from bcp/modules
59
+ ```
60
+
61
+ The application exposes:
62
+
63
+ ```ts
64
+ app.modules
65
+ app.context.modules
66
+ ```
67
+
68
+ Module providers are registered into `app.container`; module plugins are composed into `app.plugins`; module shared services are composed into `app.services`; and module resources/lifecycle are registered into `app.deployment`.
69
+
70
+ ## Lifecycle ordering
71
+
72
+ For v2 modules, imports are resolved before importers. Within each module, resources start before the module lifecycle hook:
73
+
74
+ ```text
75
+ imported modules
76
+
77
+ module resources
78
+
79
+ module setup/start
80
+ ```
81
+
82
+ Shutdown reverses this order:
83
+
84
+ ```text
85
+ module stop/dispose
86
+
87
+ module resources
88
+
89
+ imported modules
90
+ ```
91
+
92
+ If a module's own setup/start fails, its dispose hook is run before the error propagates and Deployment Platform rolls back previously started resources.
93
+
94
+ ## Compatibility
95
+
96
+ Previous baseline: `0.3.1`.
97
+
98
+ Intentional breaking changes from `0.3.1`: **none**.
99
+
100
+ The existing `defineModule()` from `bcp/plugins` remains supported and unchanged. New application modules should import `defineModule()` from `bcp/modules`.
101
+
102
+ ## Packaging
103
+
104
+ Prepared npm packages add:
105
+
106
+ ```text
107
+ bcp/modules -> packages/client/src/modules.mjs
108
+ ```
109
+
110
+ with TypeScript types from `packages/client/src/modules.ts` and the standard server-only browser boundary.
111
+
112
+ ## Validation
113
+
114
+ Before tagging or publishing `0.3.2`:
115
+
116
+ ```powershell
117
+ npm run typecheck
118
+ npm run test:unit
119
+ npm run test:integration
120
+ npm run test:e2e
121
+ npm run test:package
122
+ npm run api:check
123
+ npm run release:readiness
124
+ npm run rc:check
125
+ ```
@@ -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
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chidchanun/bcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
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",
@@ -17,6 +17,8 @@
17
17
  "react",
18
18
  "framework",
19
19
  "application-platform",
20
+ "dependency-injection",
21
+ "module-system",
20
22
  "ssr",
21
23
  "routing",
22
24
  "fullstack"
@@ -103,6 +105,16 @@
103
105
  "browser": "./packages/client/src/server-only.browser.mjs",
104
106
  "default": "./packages/client/src/deployment.mjs"
105
107
  },
108
+ "./container": {
109
+ "types": "./packages/client/src/container.ts",
110
+ "browser": "./packages/client/src/server-only.browser.mjs",
111
+ "default": "./packages/client/src/container.mjs"
112
+ },
113
+ "./modules": {
114
+ "types": "./packages/client/src/modules.ts",
115
+ "browser": "./packages/client/src/server-only.browser.mjs",
116
+ "default": "./packages/client/src/modules.mjs"
117
+ },
106
118
  "./application": {
107
119
  "types": "./packages/client/src/application.ts",
108
120
  "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/modules",
39
41
  "bcp/application",
40
42
  ]);
41
43