@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,386 @@
1
+ # BCP Module System v2
2
+
3
+ BCP Framework `0.3.2` adds a server-only application module system on top of the `0.3.0` Application Platform and `0.3.1` dependency-injection container.
4
+
5
+ > **Release state:** unreleased until the complete RC workflow passes, the exact `v0.3.2` release commit is tagged and npm publication succeeds.
6
+
7
+ ## Public entrypoint
8
+
9
+ ```ts
10
+ import {
11
+ defineModule,
12
+ composeModules,
13
+ } from "bcp/modules";
14
+ ```
15
+
16
+ `bcp/modules` is server-only and is blocked from browser/page dependency graphs.
17
+
18
+ The old `defineModule()` exported by `bcp/plugins` remains supported as the legacy plugin-group module API. Module System v2 lives in `bcp/modules` so existing imports do not change behavior.
19
+
20
+ ## Basic module
21
+
22
+ ```ts
23
+ import {
24
+ defineModule,
25
+ } from "bcp/modules";
26
+ import {
27
+ createServiceToken,
28
+ provideValue,
29
+ } from "bcp/container";
30
+
31
+ export const databaseToken =
32
+ createServiceToken<Database>(
33
+ "database"
34
+ );
35
+
36
+ export const databaseModule =
37
+ defineModule({
38
+ name: "database",
39
+ providers: [
40
+ provideValue(
41
+ databaseToken,
42
+ database
43
+ ),
44
+ ],
45
+ exports: [
46
+ databaseToken,
47
+ ],
48
+ });
49
+ ```
50
+
51
+ Pass modules to the application composition root:
52
+
53
+ ```ts
54
+ import {
55
+ createApp,
56
+ } from "bcp/application";
57
+
58
+ export const app =
59
+ createApp({
60
+ name: "orders-api",
61
+ modules: [
62
+ databaseModule,
63
+ ],
64
+ });
65
+ ```
66
+
67
+ ## Imports and dependency ordering
68
+
69
+ Modules can import other v2 modules:
70
+
71
+ ```ts
72
+ export const ordersModule =
73
+ defineModule({
74
+ name: "orders",
75
+ imports: [
76
+ databaseModule,
77
+ ],
78
+ });
79
+ ```
80
+
81
+ The graph is resolved dependency-first:
82
+
83
+ ```text
84
+ database
85
+
86
+ orders
87
+
88
+ application
89
+ ```
90
+
91
+ Repeated references to the same module definition are de-duplicated. Two different definitions using the same module name are rejected. Circular imports are rejected with `ModuleDependencyError`.
92
+
93
+ ## Providers and exports
94
+
95
+ A module can own DI providers:
96
+
97
+ ```ts
98
+ const repositoryToken =
99
+ createServiceToken<OrderRepository>(
100
+ "orders.repository"
101
+ );
102
+
103
+ export const ordersModule =
104
+ defineModule({
105
+ name: "orders",
106
+ imports: [
107
+ databaseModule,
108
+ ],
109
+ providers: [
110
+ provideFactory(
111
+ repositoryToken,
112
+ [
113
+ databaseToken,
114
+ ] as const,
115
+ (_context, [database]) =>
116
+ new OrderRepository(
117
+ database
118
+ )
119
+ ),
120
+ ],
121
+ exports: [
122
+ repositoryToken,
123
+ ],
124
+ });
125
+ ```
126
+
127
+ An exported token must either:
128
+
129
+ - be provided by the module itself; or
130
+ - be exported by one of its imported modules.
131
+
132
+ That allows explicit re-export:
133
+
134
+ ```ts
135
+ export const applicationDataModule =
136
+ defineModule({
137
+ name: "application-data",
138
+ imports: [
139
+ databaseModule,
140
+ ],
141
+ exports: [
142
+ databaseToken,
143
+ ],
144
+ });
145
+ ```
146
+
147
+ The current `0.3.2` runtime uses the application container as the physical provider store. Export metadata defines the reviewed module boundary and is available for diagnostics/introspection; it does not create a second DI container per module.
148
+
149
+ ## Plugins
150
+
151
+ Modules may contribute existing BCP plugins:
152
+
153
+ ```ts
154
+ const auditPlugin =
155
+ definePlugin({
156
+ name: "audit",
157
+ });
158
+
159
+ export const auditModule =
160
+ defineModule({
161
+ name: "audit-module",
162
+ plugins: [
163
+ auditPlugin,
164
+ ],
165
+ });
166
+ ```
167
+
168
+ All module plugins are composed into the application Plugin Host before deployment resources start.
169
+
170
+ ## Shared legacy services
171
+
172
+ For compatibility with `bcp/plugins`, a module may contribute entries to the legacy shared service registry:
173
+
174
+ ```ts
175
+ export const featureModule =
176
+ defineModule({
177
+ name: "feature",
178
+ services: [
179
+ [
180
+ "feature-flags",
181
+ featureFlags,
182
+ ],
183
+ ],
184
+ });
185
+ ```
186
+
187
+ New typed dependencies should generally prefer `bcp/container` providers/tokens. `services` exists for interoperability with existing plugins.
188
+
189
+ ## Resources
190
+
191
+ Modules can own Deployment Platform resources:
192
+
193
+ ```ts
194
+ export const jobsModule =
195
+ defineModule({
196
+ name: "jobs",
197
+ resources: [
198
+ {
199
+ name: "jobs-worker",
200
+
201
+ start() {
202
+ return worker.start();
203
+ },
204
+
205
+ ready() {
206
+ return worker.ready;
207
+ },
208
+
209
+ stop() {
210
+ return worker.stop();
211
+ },
212
+ },
213
+ ],
214
+ });
215
+ ```
216
+
217
+ Resources from imported modules are registered before resources from importing modules.
218
+
219
+ ## Module lifecycle
220
+
221
+ Each module may define:
222
+
223
+ ```text
224
+ setup
225
+ start
226
+ stop
227
+ dispose
228
+ ```
229
+
230
+ Example:
231
+
232
+ ```ts
233
+ export const mailModule =
234
+ defineModule({
235
+ name: "mail",
236
+
237
+ setup(context) {
238
+ // Providers/services are already registered.
239
+ },
240
+
241
+ start(context) {
242
+ // Module resources and imported modules are available.
243
+ },
244
+
245
+ stop(context) {
246
+ // Stop module-level work before its resources disappear.
247
+ },
248
+
249
+ dispose(context) {
250
+ // Release module-only in-process state.
251
+ },
252
+ });
253
+ ```
254
+
255
+ For each module, its deployment resources are registered before its lifecycle resource. Therefore startup is:
256
+
257
+ ```text
258
+ imported module resources/lifecycle
259
+
260
+ module resources
261
+
262
+ module setup/start
263
+ ```
264
+
265
+ Shutdown is the reverse:
266
+
267
+ ```text
268
+ module stop/dispose
269
+
270
+ module resources stop (reverse)
271
+
272
+ imported module stop/dispose
273
+ ```
274
+
275
+ If `setup()` or `start()` fails, Module System v2 runs that module's `dispose()` before propagating the startup error. Deployment Platform then rolls back resources/modules that were already started earlier.
276
+
277
+ Lifecycle errors use `ModuleLifecycleError`.
278
+
279
+ ## Typed module config
280
+
281
+ Modules support parser functions or schema objects with `parse()`:
282
+
283
+ ```ts
284
+ export const mailModule =
285
+ defineModule({
286
+ name: "mail",
287
+ config: {
288
+ from:
289
+ process.env.MAIL_FROM,
290
+ },
291
+ schema: {
292
+ parse(value) {
293
+ const raw =
294
+ value as {
295
+ from?: string;
296
+ };
297
+
298
+ if (!raw.from) {
299
+ throw new Error(
300
+ "MAIL_FROM is required"
301
+ );
302
+ }
303
+
304
+ return {
305
+ from: raw.from,
306
+ };
307
+ },
308
+ },
309
+ start(context) {
310
+ console.log(
311
+ context.config.from
312
+ );
313
+ },
314
+ });
315
+ ```
316
+
317
+ ## Composition diagnostics
318
+
319
+ Application instances expose the resolved composition:
320
+
321
+ ```ts
322
+ app.modules.records();
323
+ ```
324
+
325
+ A record includes:
326
+
327
+ ```text
328
+ name
329
+ version
330
+ imports
331
+ providers
332
+ exports
333
+ plugins
334
+ resources
335
+ ```
336
+
337
+ Inspect one module's exports:
338
+
339
+ ```ts
340
+ app.modules.exportedTokens(
341
+ "orders"
342
+ );
343
+ ```
344
+
345
+ Application diagnostics also include the module records.
346
+
347
+ ## Legacy plugin modules
348
+
349
+ Existing code remains valid:
350
+
351
+ ```ts
352
+ import {
353
+ defineModule,
354
+ } from "bcp/plugins";
355
+
356
+ const legacy =
357
+ defineModule({
358
+ name: "legacy",
359
+ plugins: [
360
+ plugin,
361
+ ],
362
+ });
363
+ ```
364
+
365
+ `createApp({ modules: [...] })` accepts both legacy plugin modules and Module System v2 definitions. The two formats are distinguished by the v2 module marker created by `bcp/modules` `defineModule()`.
366
+
367
+ ## Package/runtime contract
368
+
369
+ Prepared npm packages expose:
370
+
371
+ ```text
372
+ bcp/modules -> packages/client/src/modules.mjs
373
+ ```
374
+
375
+ Types remain in `packages/client/src/modules.ts`, and browser builds resolve to the standard server-only poison module.
376
+
377
+ ## Compatibility
378
+
379
+ `0.3.2` is additive relative to `0.3.1`:
380
+
381
+ - existing `bcp/plugins` module behavior remains available;
382
+ - existing `createApp()` definitions without v2 modules behave as before;
383
+ - `bcp/modules` is a new public entrypoint;
384
+ - `ApplicationContext` and `Application` gain the additive `modules` composition view.
385
+
386
+ No intentional breaking change is introduced from `0.3.1`.
@@ -2,7 +2,7 @@
2
2
 
3
3
  BCP Framework exposes an explicit, machine-readable application-platform contract rather than relying on private repository structure.
4
4
 
5
- The current development baseline is **`0.3.0BCP Application Platform`** and remains unreleased until the complete RC sequence passes, the exact release commit is tagged and npm publication completes.
5
+ The current development baseline is **`0.3.1Dependency Injection & Service Container`** and remains unreleased until the complete RC sequence passes, the exact release commit is tagged and npm publication completes.
6
6
 
7
7
  ## Sources of truth
8
8
 
@@ -20,11 +20,11 @@ docs/docs-web-manifest.json
20
20
  -> documentation routes and release navigation
21
21
  ```
22
22
 
23
- Framework source and tests remain authoritative for runtime behavior. The manifests make the supported surface testable during release validation.
23
+ Framework source and tests remain authoritative for runtime behavior.
24
24
 
25
25
  ## Public entrypoints
26
26
 
27
- The `0.3.0` baseline supports:
27
+ The `0.3.1` baseline supports:
28
28
 
29
29
  ```text
30
30
  bcp
@@ -43,43 +43,57 @@ bcp/testing
43
43
  bcp/plugins
44
44
  bcp/observability
45
45
  bcp/deployment
46
+ bcp/container
46
47
  bcp/application
47
48
  bcp/server
48
49
  bcp/server-only
49
50
  bcp/middleware
50
51
  ```
51
52
 
52
- `bcp/application` is the one new entrypoint relative to the `0.2.19` baseline. No existing `0.2.19` public entrypoint is intentionally removed.
53
+ `bcp/container` is the new entrypoint relative to `0.3.0`. No `0.3.0` public entrypoint is intentionally removed.
53
54
 
54
- Application code should use these entrypoints instead of private `packages/*` implementation paths.
55
+ The prepared package also exposes `./package.json`; it is part of package-export snapshot validation but is not an application API module.
55
56
 
56
- The prepared npm package also exposes `./package.json`; that package export is included in the API snapshot even though it is not an application API module.
57
+ ## Application and DI baseline
57
58
 
58
- ## Application Platform baseline
59
+ `bcp/application` remains the composition root while `bcp/container` adds typed dependency injection:
59
60
 
60
- `bcp/application` adds a server-only composition root:
61
+ ```text
62
+ ServiceToken<T>
63
+ value providers
64
+ factory providers
65
+ class providers
66
+ singleton/scoped/transient lifetimes
67
+ child/request scopes
68
+ test overrides
69
+ dependency graph diagnostics
70
+ circular dependency detection
71
+ reverse disposal
72
+ ```
73
+
74
+ Application integration exposes:
61
75
 
62
- ```ts
63
- import {
64
- createApp,
65
- defineApp,
66
- } from "bcp/application";
76
+ ```text
77
+ app.container
78
+ app.context.container
79
+ app.register(provider)
80
+ app.createScope(options)
67
81
  ```
68
82
 
69
- The Application Platform reuses the existing Plugin and Deployment platforms for:
83
+ The existing Plugin Platform registry remains available through `app.services` / `context.services` for compatibility.
84
+
85
+ ## Lifecycle contract
86
+
87
+ Application deployment resources start in this order:
70
88
 
71
89
  ```text
72
- typed application config
73
- shared services and hooks
74
- plugins/modules
75
- resource lifecycle
76
- startup rollback
77
- readiness/diagnostics
78
- signal handling
79
- graceful shutdown
90
+ bcp:container
91
+ bcp:plugins
92
+ application resources
93
+ bcp:application
80
94
  ```
81
95
 
82
- It does not replace existing database, jobs, cache, events, realtime or other subsystem APIs. Applications may use those independently or compose selected instances through the application root.
96
+ Shutdown reverses that order. This keeps injected dependencies available while workers/resources/plugins stop, then disposes injected services before final application disposal.
83
97
 
84
98
  ## CLI baseline
85
99
 
@@ -112,21 +126,25 @@ build target: standalone-node
112
126
  package target: standalone-node
113
127
  ```
114
128
 
115
- Prepared server/runtime entrypoints resolve to compiled ESM where required by the package contract. `0.3.0` adds compiled `application.mjs`.
129
+ Prepared npm packages expose compiled server ESM. `0.3.1` adds:
130
+
131
+ ```text
132
+ bcp/container -> container.mjs
133
+ ```
134
+
135
+ and preserves compiled `application.mjs` from `0.3.0`.
116
136
 
117
137
  ## Compatibility policy
118
138
 
119
- For `0.3.0`:
139
+ For `0.3.1`:
120
140
 
121
141
  ```text
122
- previous baseline: 0.2.19
142
+ previous baseline: 0.3.0
123
143
  intentional breaking changes: false
124
- baseline: application-platform
144
+ baseline: dependency-injection-service-container
125
145
  ```
126
146
 
127
- `0.2.19` remains the historical freeze point for `0.2.x`. `0.3.0` intentionally advances the reviewed API snapshot by adding `bcp/application` and becomes the next compatibility baseline.
128
-
129
- Bug fixes must not silently remove a documented public entrypoint or change its prepared package-resolution/browser-boundary contract.
147
+ Bug fixes must not silently remove a documented entrypoint or change its prepared package-resolution/browser-boundary contract.
130
148
 
131
149
  ## API compatibility gate
132
150
 
@@ -134,16 +152,14 @@ Bug fixes must not silently remove a documented public entrypoint or change its
134
152
  npm run api:check
135
153
  ```
136
154
 
137
- The gate prepares the publish package, regenerates the current contract in memory and compares it to `docs/api-freeze-snapshot.json`.
155
+ The gate prepares the publish package, regenerates the current contract in memory and compares it with `docs/api-freeze-snapshot.json`.
138
156
 
139
- To intentionally regenerate the snapshot for a reviewed platform-baseline change:
157
+ Regenerate the snapshot only for an intentional reviewed baseline change:
140
158
 
141
159
  ```bash
142
160
  npm run api:snapshot
143
161
  ```
144
162
 
145
- Do not regenerate the snapshot merely to silence an unexpected compatibility failure.
146
-
147
163
  ## Release readiness
148
164
 
149
165
  ```bash
@@ -151,11 +167,11 @@ npm run release:readiness
151
167
  npm run release:readiness:report
152
168
  ```
153
169
 
154
- The readiness gate checks version/lock/manifests parity, compatibility metadata, application entrypoint ownership, API snapshot parity, release docs and Application Platform capability flags. The optional report is written to `.bcp-framework/release-readiness.json`.
170
+ The readiness gate checks package/lock/manifests parity, the `0.3.0` previous baseline, public entrypoint/API snapshot parity, container/application ownership, release docs and DI/Application capability flags.
155
171
 
156
172
  ## Release validation
157
173
 
158
- Before `0.3.0` is tagged or published:
174
+ Before `0.3.1` is tagged or published:
159
175
 
160
176
  ```bash
161
177
  npm run typecheck
@@ -170,4 +186,4 @@ npm run rc:check
170
186
 
171
187
  `rc:check` must pass on the exact commit used for the release tag.
172
188
 
173
- See [Application Platform](application-platform.md), [Migrating to 0.3.0](migration-0.3.md), and the historical [Stability & API Freeze](stability-api-freeze.md).
189
+ See [Dependency Injection & Service Container](service-container.md), [Application Platform](application-platform.md), [Migrating to 0.3.x](migration-0.3.md), and the historical [Stability & API Freeze](stability-api-freeze.md).
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "framework": "bcp",
4
- "version": "0.3.0",
4
+ "version": "0.3.2",
5
5
  "releaseState": "unreleased",
6
- "baseline": "application-platform",
6
+ "baseline": "module-system-v2",
7
7
  "runtime": {
8
8
  "node": ">=24.11.0",
9
9
  "react": "19",
@@ -27,6 +27,8 @@
27
27
  "bcp/plugins",
28
28
  "bcp/observability",
29
29
  "bcp/deployment",
30
+ "bcp/container",
31
+ "bcp/modules",
30
32
  "bcp/application",
31
33
  "bcp/server",
32
34
  "bcp/server-only",
@@ -109,6 +111,34 @@
109
111
  "applicationLifecycleRollback": true,
110
112
  "applicationLifecycleIdempotency": true,
111
113
  "compiledApplicationRuntime": true,
114
+ "dependencyInjectionContainer": true,
115
+ "typedServiceTokens": true,
116
+ "serviceValueProviders": true,
117
+ "serviceFactoryProviders": true,
118
+ "serviceClassProviders": true,
119
+ "serviceSingletonLifetime": true,
120
+ "serviceScopedLifetime": true,
121
+ "serviceTransientLifetime": true,
122
+ "serviceChildScopes": true,
123
+ "serviceScopeOverrides": true,
124
+ "serviceDependencyGraph": true,
125
+ "serviceCircularDependencyDetection": true,
126
+ "serviceReverseDisposal": true,
127
+ "applicationServiceContainer": true,
128
+ "compiledContainerRuntime": true,
129
+ "moduleSystemV2": true,
130
+ "applicationModulesV2": true,
131
+ "moduleDependencyGraph": true,
132
+ "moduleImports": true,
133
+ "moduleExports": true,
134
+ "moduleProviderComposition": true,
135
+ "modulePluginComposition": true,
136
+ "moduleServiceComposition": true,
137
+ "moduleResourceComposition": true,
138
+ "moduleLifecycle": true,
139
+ "moduleConfigSchemas": true,
140
+ "moduleDiagnostics": true,
141
+ "compiledModulesRuntime": true,
112
142
  "stabilityApiFreeze": true,
113
143
  "apiFreezeSnapshot": true,
114
144
  "apiCompatibilityGate": true,
@@ -244,7 +274,7 @@
244
274
  "s3-compatible"
245
275
  ],
246
276
  "compatibility": {
247
- "previousBaseline": "0.2.19",
277
+ "previousBaseline": "0.3.1",
248
278
  "intentionalBreakingChangesFromPreviousBaseline": false,
249
279
  "migrationGuide": "migration-0.3.md"
250
280
  },
@@ -258,6 +288,8 @@
258
288
  "apiReference": "api-reference.md",
259
289
  "stabilityApiFreeze": "stability-api-freeze.md",
260
290
  "applicationPlatform": "application-platform.md",
291
+ "serviceContainer": "service-container.md",
292
+ "moduleSystemV2": "module-system-v2.md",
261
293
  "environmentValidation": "environment-validation.md",
262
294
  "applicationPackaging": "application-packaging.md",
263
295
  "authentication": "authentication.md",
@@ -276,6 +308,6 @@
276
308
  "pluginModulePlatform": "plugin-module-platform.md",
277
309
  "cachePlatformV2": "cache-platform-v2.md",
278
310
  "migrationGuide": "migration-0.3.md",
279
- "releaseNotes": "releases/0.3.0.md"
311
+ "releaseNotes": "releases/0.3.2.md"
280
312
  }
281
313
  }