@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,348 @@
1
+ # BCP Application Platform
2
+
3
+ BCP Framework `0.3.x` provides a server-side application composition root that coordinates the infrastructure platforms introduced throughout `0.2.x` without replacing their independent APIs.
4
+
5
+ `0.3.1` extends the original `0.3.0` runtime with typed dependency injection through `bcp/container`.
6
+
7
+ ## Public entrypoint
8
+
9
+ ```ts
10
+ import {
11
+ createApp,
12
+ defineApp,
13
+ } from "bcp/application";
14
+ ```
15
+
16
+ `bcp/application` is server-only and blocked from browser/page dependency graphs.
17
+
18
+ ## Composition model
19
+
20
+ ```text
21
+ BCP Application
22
+ |
23
+ +-- typed application config
24
+ +-- typed DI container
25
+ +-- legacy plugin service registry
26
+ +-- plugin/module host
27
+ +-- infrastructure resources
28
+ +-- deployment metadata/readiness/diagnostics
29
+ +-- graceful shutdown
30
+ ```
31
+
32
+ Applications still construct database, cache, jobs, workflow, event, realtime and observability implementations explicitly. The application root composes their lifecycle; it does not hide provider creation.
33
+
34
+ ## Basic application
35
+
36
+ ```ts
37
+ import {
38
+ createApp,
39
+ } from "bcp/application";
40
+
41
+ export const app =
42
+ createApp({
43
+ name: "orders-api",
44
+ version: "1.0.0",
45
+ });
46
+
47
+ await app.start();
48
+ ```
49
+
50
+ Shutdown:
51
+
52
+ ```ts
53
+ await app.stop();
54
+ ```
55
+
56
+ `shutdown()` and `close()` are aliases for the same terminal application shutdown operation.
57
+
58
+ ## Typed application config
59
+
60
+ ```ts
61
+ const app =
62
+ createApp<{
63
+ port: number;
64
+ }>({
65
+ name: "orders-api",
66
+ config: {
67
+ port: process.env.PORT,
68
+ },
69
+ schema: {
70
+ parse(value) {
71
+ const raw = value as {
72
+ port?: string;
73
+ };
74
+
75
+ return {
76
+ port:
77
+ Number(
78
+ raw.port ?? 3000
79
+ ),
80
+ };
81
+ },
82
+ },
83
+ });
84
+ ```
85
+
86
+ The schema may be an object with `parse()` or a parser function.
87
+
88
+ ## Typed dependency injection — 0.3.1
89
+
90
+ Define providers with `bcp/container`:
91
+
92
+ ```ts
93
+ import {
94
+ createServiceToken,
95
+ provideFactory,
96
+ provideValue,
97
+ } from "bcp/container";
98
+
99
+ const configToken =
100
+ createServiceToken<{
101
+ apiUrl: string;
102
+ }>("config");
103
+
104
+ const repositoryToken =
105
+ createServiceToken<UserRepository>(
106
+ "user-repository"
107
+ );
108
+
109
+ const app =
110
+ createApp({
111
+ name: "orders-api",
112
+ providers: [
113
+ provideValue(
114
+ configToken,
115
+ {
116
+ apiUrl: "https://api.example.com",
117
+ }
118
+ ),
119
+ provideFactory(
120
+ repositoryToken,
121
+ [
122
+ configToken,
123
+ ] as const,
124
+ (_context, [config]) =>
125
+ createRepository(
126
+ config.apiUrl
127
+ )
128
+ ),
129
+ ],
130
+ });
131
+ ```
132
+
133
+ Resolve from application hooks:
134
+
135
+ ```ts
136
+ createApp({
137
+ name: "orders-api",
138
+ providers: [
139
+ repositoryProvider,
140
+ ],
141
+
142
+ async setup(context) {
143
+ const repository =
144
+ await context.container.resolve(
145
+ repositoryToken
146
+ );
147
+ },
148
+ });
149
+ ```
150
+
151
+ Application DI APIs:
152
+
153
+ ```text
154
+ app.container
155
+ app.context.container
156
+ app.register(provider)
157
+ app.createScope(options)
158
+ ```
159
+
160
+ `app.register()` follows the same deterministic mutation boundary as plugins/resources and is only allowed before startup begins.
161
+
162
+ For request/job/test boundaries:
163
+
164
+ ```ts
165
+ const scope =
166
+ app.createScope({
167
+ name: "request:123",
168
+ });
169
+ ```
170
+
171
+ Testing overrides can be supplied to the scope without modifying the application root.
172
+
173
+ See [Dependency Injection & Service Container](service-container.md).
174
+
175
+ ## Legacy plugin services
176
+
177
+ `app.services` / `context.services` remain the registry used by `bcp/plugins`:
178
+
179
+ ```ts
180
+ app.provide(
181
+ "database",
182
+ database
183
+ );
184
+ ```
185
+
186
+ This remains supported for compatibility. New typed application dependencies should prefer `app.container`.
187
+
188
+ The two registries are intentionally not silently synchronized because typed `ServiceToken<T>` values and plugin string/symbol service keys have different contracts.
189
+
190
+ ## Plugins and modules
191
+
192
+ Existing `bcp/plugins` definitions plug directly into the application runtime:
193
+
194
+ ```ts
195
+ const app =
196
+ createApp({
197
+ name: "orders-api",
198
+ modules: [
199
+ backendModule,
200
+ ],
201
+ });
202
+ ```
203
+
204
+ Before startup:
205
+
206
+ ```ts
207
+ app.use(mailPlugin);
208
+ ```
209
+
210
+ ## Infrastructure resources
211
+
212
+ Use the existing Deployment Platform resource contract:
213
+
214
+ ```ts
215
+ app.addResource({
216
+ name: "database",
217
+
218
+ async start() {
219
+ await database.connect();
220
+ },
221
+
222
+ ready() {
223
+ return database.ready;
224
+ },
225
+
226
+ async stop() {
227
+ await database.close();
228
+ },
229
+ });
230
+ ```
231
+
232
+ ## Lifecycle order
233
+
234
+ `setup()` runs before deployment startup, allowing config and DI services to be resolved before resource startup.
235
+
236
+ Deployment order in `0.3.1`:
237
+
238
+ ```text
239
+ application.setup()
240
+ |
241
+ v
242
+ bcp:container
243
+ |
244
+ v
245
+ bcp:plugins
246
+ |
247
+ v
248
+ resource 1
249
+ resource 2
250
+ ...
251
+ |
252
+ v
253
+ bcp:application / application.start()
254
+ |
255
+ v
256
+ state = ready
257
+ ```
258
+
259
+ Shutdown reverses deployment dependencies:
260
+
261
+ ```text
262
+ application.stop()
263
+ |
264
+ v
265
+ resource N
266
+ ...
267
+ resource 1
268
+ |
269
+ v
270
+ plugins stop/dispose
271
+ |
272
+ v
273
+ container dispose
274
+ |
275
+ v
276
+ application.dispose()
277
+ ```
278
+
279
+ This keeps injected services alive while application resources and plugins are shutting down.
280
+
281
+ Startup failures use Deployment Platform rollback. Application cleanup also closes plugins and disposes the DI container idempotently when startup fails before the deployment graph becomes active.
282
+
283
+ ## Readiness and diagnostics
284
+
285
+ ```ts
286
+ const readiness =
287
+ await app.readiness();
288
+
289
+ const diagnostics =
290
+ await app.diagnostics();
291
+ ```
292
+
293
+ Readiness now includes internal `bcp:container`, `bcp:plugins`, application resources and `bcp:application` entries.
294
+
295
+ Application diagnostics expose registered container provider descriptions in addition to plugin/service/deployment information.
296
+
297
+ ## Signals and framework shutdown hooks
298
+
299
+ ```ts
300
+ const removeSignals =
301
+ app.installSignalHandlers();
302
+
303
+ const unregister =
304
+ app.registerShutdownHook();
305
+ ```
306
+
307
+ Both paths perform full application shutdown, including application stop hooks and container disposal.
308
+
309
+ ## Application state
310
+
311
+ ```text
312
+ created
313
+ starting
314
+ ready
315
+ stopping
316
+ stopped
317
+ failed
318
+ ```
319
+
320
+ Concurrent/repeated start and shutdown calls are idempotent in their supported states. A stopped application is terminal and cannot be restarted.
321
+
322
+ ## Mutation boundary
323
+
324
+ The following are rejected once startup begins:
325
+
326
+ ```ts
327
+ app.use(...)
328
+ app.provide(...)
329
+ app.register(...)
330
+ app.addResource(...)
331
+ ```
332
+
333
+ Creating child scopes is allowed while the container is active. Resolving/creating scopes after container disposal is rejected.
334
+
335
+ ## Compatibility
336
+
337
+ `0.3.1` is additive over `0.3.0`:
338
+
339
+ ```text
340
+ previous baseline: 0.3.0
341
+ intentional breaking changes: false
342
+ ```
343
+
344
+ Existing applications that use only `app.services` continue to work. DI adoption can be incremental.
345
+
346
+ ## Next platform work
347
+
348
+ The next planned milestone is `0.3.2 — Module System v2`, using the Application Platform and DI container as the composition foundation for reusable application-native modules.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "framework": "bcp",
4
- "versionTarget": "0.2.19",
4
+ "versionTarget": "0.3.1",
5
5
  "releaseState": "unreleased",
6
6
  "sections": [
7
7
  {
@@ -58,8 +58,10 @@
58
58
  {
59
59
  "id": "runtime",
60
60
  "title": "Runtime & Infrastructure",
61
- "description": "Middleware, jobs, scheduling, workflows, event delivery, realtime channels, observability, deployment lifecycle, distributed caching, security and production hardening.",
61
+ "description": "Application composition, dependency injection, middleware, jobs, scheduling, workflows, event delivery, realtime channels, observability, deployment lifecycle, distributed caching, security and production hardening.",
62
62
  "pages": [
63
+ { "route": "/docs/application-platform", "source": "application-platform.md", "title": "Application Platform" },
64
+ { "route": "/docs/service-container", "source": "service-container.md", "title": "Dependency Injection & Service Container" },
63
65
  { "route": "/docs/middleware", "source": "middleware.md", "title": "Middleware" },
64
66
  { "route": "/docs/hydration", "source": "hydration.md", "title": "Hydration" },
65
67
  { "route": "/docs/development-logging", "source": "development-logging.md", "title": "Logging" },
@@ -102,11 +104,12 @@
102
104
  {
103
105
  "id": "platform-compatibility",
104
106
  "title": "Platform & Compatibility",
105
- "description": "Supported platform contracts, API freeze, documentation integration and migration guidance.",
107
+ "description": "Supported platform contracts, API baselines, documentation integration and migration guidance.",
106
108
  "pages": [
107
109
  { "route": "/docs/platform-contract", "source": "platform-contract.md", "title": "Framework Platform Contract" },
108
- { "route": "/docs/stability-api-freeze", "source": "stability-api-freeze.md", "title": "Stability & API Freeze" },
110
+ { "route": "/docs/stability-api-freeze", "source": "stability-api-freeze.md", "title": "0.2 Stability & API Freeze" },
109
111
  { "route": "/docs/documentation-platform", "source": "documentation-platform.md", "title": "Documentation Platform" },
112
+ { "route": "/docs/migration-0.3", "source": "migration-0.3.md", "title": "Migrating to 0.3.x" },
110
113
  { "route": "/docs/migration-0.2", "source": "migration-0.2.md", "title": "Migrating to 0.2.x" }
111
114
  ]
112
115
  },
@@ -120,7 +123,9 @@
120
123
  }
121
124
  ],
122
125
  "releases": [
123
- { "route": "/releases/0.2.19", "source": "releases/0.2.19.md", "version": "0.2.19", "state": "unreleased" },
126
+ { "route": "/releases/0.3.1", "source": "releases/0.3.1.md", "version": "0.3.1", "state": "unreleased" },
127
+ { "route": "/releases/0.3.0", "source": "releases/0.3.0.md", "version": "0.3.0" },
128
+ { "route": "/releases/0.2.19", "source": "releases/0.2.19.md", "version": "0.2.19" },
124
129
  { "route": "/releases/0.2.18", "source": "releases/0.2.18.md", "version": "0.2.18" },
125
130
  { "route": "/releases/0.2.17", "source": "releases/0.2.17.md", "version": "0.2.17" },
126
131
  { "route": "/releases/0.2.16", "source": "releases/0.2.16.md", "version": "0.2.16" },
@@ -0,0 +1,280 @@
1
+ # Migrating to BCP Framework 0.3.x
2
+
3
+ BCP Framework `0.3.0` established the Application Platform baseline. `0.3.1` extends it additively with typed dependency injection through `bcp/container`.
4
+
5
+ ## Compatibility
6
+
7
+ Current target:
8
+
9
+ ```text
10
+ 0.3.1 — Dependency Injection & Service Container
11
+ ```
12
+
13
+ Previous baseline:
14
+
15
+ ```text
16
+ 0.3.0
17
+ ```
18
+
19
+ Intentional breaking changes:
20
+
21
+ ```text
22
+ false
23
+ ```
24
+
25
+ Existing `0.3.0` applications do not have to adopt dependency injection immediately.
26
+
27
+ ## Upgrade to 0.3.1
28
+
29
+ After publication:
30
+
31
+ ```powershell
32
+ npm exec -- bcp-framework update --check
33
+ npm run update -- 0.3.1
34
+ ```
35
+
36
+ Then validate the application:
37
+
38
+ ```powershell
39
+ npm run typecheck
40
+ npm run build
41
+ npm exec -- bcp-framework routes
42
+ npm exec -- bcp-framework doctor
43
+ ```
44
+
45
+ ## Existing Application Platform code remains valid
46
+
47
+ This remains supported:
48
+
49
+ ```ts
50
+ import {
51
+ createApp,
52
+ } from "bcp/application";
53
+
54
+ const app =
55
+ createApp({
56
+ name: "my-app",
57
+ });
58
+
59
+ app.provide(
60
+ "database",
61
+ database
62
+ );
63
+ ```
64
+
65
+ `app.services` and `context.services` continue to use the Plugin Platform registry.
66
+
67
+ ## Optional migration to typed DI
68
+
69
+ New typed dependencies can use `bcp/container`:
70
+
71
+ ```ts
72
+ import {
73
+ createServiceToken,
74
+ provideValue,
75
+ } from "bcp/container";
76
+
77
+ const databaseToken =
78
+ createServiceToken<typeof database>(
79
+ "database"
80
+ );
81
+
82
+ const app =
83
+ createApp({
84
+ name: "my-app",
85
+ providers: [
86
+ provideValue(
87
+ databaseToken,
88
+ database
89
+ ),
90
+ ],
91
+ });
92
+ ```
93
+
94
+ Then resolve from application hooks or server composition code:
95
+
96
+ ```ts
97
+ const database =
98
+ await app.container.resolve(
99
+ databaseToken
100
+ );
101
+ ```
102
+
103
+ You do not need to migrate every plugin service at once.
104
+
105
+ ## Factory dependencies
106
+
107
+ Replace manual service construction:
108
+
109
+ ```ts
110
+ const repository =
111
+ createRepository(
112
+ database,
113
+ logger
114
+ );
115
+ ```
116
+
117
+ with explicit typed provider dependencies when useful:
118
+
119
+ ```ts
120
+ const repositoryProvider =
121
+ provideFactory(
122
+ repositoryToken,
123
+ [
124
+ databaseToken,
125
+ loggerToken,
126
+ ] as const,
127
+ (
128
+ _context,
129
+ [database, logger]
130
+ ) =>
131
+ createRepository(
132
+ database,
133
+ logger
134
+ )
135
+ );
136
+ ```
137
+
138
+ No decorators or reflection metadata are required.
139
+
140
+ ## Request/job/test scopes
141
+
142
+ Use child scopes for dependencies that should not be application singletons:
143
+
144
+ ```ts
145
+ const requestScope =
146
+ app.createScope({
147
+ name: `request:${requestId}`,
148
+ });
149
+
150
+ try {
151
+ const service =
152
+ await requestScope.resolve(
153
+ requestServiceToken
154
+ );
155
+ } finally {
156
+ await requestScope.dispose();
157
+ }
158
+ ```
159
+
160
+ Testing overrides:
161
+
162
+ ```ts
163
+ const testScope =
164
+ app.createScope({
165
+ name: "test",
166
+ overrides: [
167
+ provideValue(
168
+ mailerToken,
169
+ fakeMailer
170
+ ),
171
+ ],
172
+ });
173
+ ```
174
+
175
+ ## Lifetimes
176
+
177
+ Provider lifetimes are:
178
+
179
+ ```text
180
+ singleton
181
+ scoped
182
+ transient
183
+ ```
184
+
185
+ The default is `singleton`.
186
+
187
+ A common mapping is:
188
+
189
+ ```text
190
+ database pool singleton
191
+ cache client singleton
192
+ repository scoped or singleton depending on state
193
+ request context scoped
194
+ small stateless factory output transient when required
195
+ ```
196
+
197
+ ## Disposal ownership
198
+
199
+ If the DI container owns a resource through a provider `dispose()` callback, avoid also registering an independent shutdown owner for the same instance unless the cleanup operation is explicitly idempotent.
200
+
201
+ Example:
202
+
203
+ ```ts
204
+ provideFactory(
205
+ databaseToken,
206
+ [] as const,
207
+ createDatabase,
208
+ {
209
+ dispose(database) {
210
+ return database.close();
211
+ },
212
+ }
213
+ );
214
+ ```
215
+
216
+ The Application Platform keeps the container alive until application resources/plugins stop, then disposes injected services in reverse creation order.
217
+
218
+ ## Application resource lifecycle
219
+
220
+ Long-running components can still use `app.addResource()`:
221
+
222
+ ```ts
223
+ app.addResource({
224
+ name: "worker",
225
+ start: () => worker.start(),
226
+ stop: () => worker.stop(),
227
+ });
228
+ ```
229
+
230
+ DI and Deployment resources solve different concerns:
231
+
232
+ ```text
233
+ container provider -> construct/resolve/dispose dependencies
234
+ application resource -> start/readiness/stop long-running runtime components
235
+ ```
236
+
237
+ A provider may construct the worker while an application resource starts/stops it.
238
+
239
+ ## Server-only boundary
240
+
241
+ Both entrypoints are server-only:
242
+
243
+ ```text
244
+ bcp/container
245
+ bcp/application
246
+ ```
247
+
248
+ Do not import them into React client pages/islands.
249
+
250
+ ## API baseline
251
+
252
+ `0.3.1` adds one public entrypoint over `0.3.0`:
253
+
254
+ ```text
255
+ bcp/container
256
+ ```
257
+
258
+ The reviewed API snapshot records the new `container.mjs` prepared runtime and browser poison boundary.
259
+
260
+ ## Framework validation checklist
261
+
262
+ ```powershell
263
+ npm run typecheck
264
+ npm run test:unit
265
+ npm run test:integration
266
+ npm run test:e2e
267
+ npm run test:package
268
+ npm run api:check
269
+ npm run release:readiness
270
+ npm run rc:check
271
+ ```
272
+
273
+ For application upgrades:
274
+
275
+ ```powershell
276
+ npm run typecheck
277
+ npm run build
278
+ npm exec -- bcp-framework routes
279
+ npm exec -- bcp-framework doctor
280
+ ```