@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.
- package/README.md +135 -111
- package/docs/README.md +53 -78
- package/docs/api-freeze-snapshot.json +24 -2
- package/docs/api-manifest.json +29 -13
- package/docs/api-reference.md +195 -197
- package/docs/application-platform.md +141 -171
- package/docs/docs-web-manifest.json +9 -5
- package/docs/migration-0.3.md +189 -68
- package/docs/module-system-v2.md +386 -0
- package/docs/platform-contract.md +53 -37
- package/docs/platform-manifest.json +36 -4
- package/docs/releases/0.3.1.md +116 -0
- package/docs/releases/0.3.2.md +125 -0
- package/docs/service-container.md +333 -0
- package/package.json +13 -1
- package/packages/bundler/src/client-boundary.ts +2 -0
- package/packages/client/src/application.mjs +1004 -34
- package/packages/client/src/container.mjs +573 -0
- package/packages/client/src/container.ts +24 -0
- package/packages/client/src/modules.mjs +392 -0
- package/packages/client/src/modules.ts +17 -0
- package/packages/server/src/application.ts +171 -11
- package/packages/server/src/container.ts +970 -0
- package/packages/server/src/modules.ts +682 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# BCP Application Platform
|
|
2
2
|
|
|
3
|
-
BCP Framework `0.3.
|
|
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
4
|
|
|
5
|
-
|
|
5
|
+
`0.3.1` extends the original `0.3.0` runtime with typed dependency injection through `bcp/container`.
|
|
6
6
|
|
|
7
7
|
## Public entrypoint
|
|
8
8
|
|
|
@@ -13,34 +13,23 @@ import {
|
|
|
13
13
|
} from "bcp/application";
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
`bcp/application` is server-only and
|
|
16
|
+
`bcp/application` is server-only and blocked from browser/page dependency graphs.
|
|
17
17
|
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
Before `0.3.0`, applications could use BCP databases, jobs, workflows, events, realtime, plugins, cache, observability and deployment independently. That remains supported.
|
|
21
|
-
|
|
22
|
-
The Application Platform adds one composition root for applications that want those systems to share lifecycle and services:
|
|
18
|
+
## Composition model
|
|
23
19
|
|
|
24
20
|
```text
|
|
25
21
|
BCP Application
|
|
26
22
|
|
|
|
27
23
|
+-- typed application config
|
|
28
|
-
+--
|
|
29
|
-
+-- plugin
|
|
24
|
+
+-- typed DI container
|
|
25
|
+
+-- legacy plugin service registry
|
|
26
|
+
+-- plugin/module host
|
|
30
27
|
+-- infrastructure resources
|
|
31
28
|
+-- deployment metadata/readiness/diagnostics
|
|
32
29
|
+-- graceful shutdown
|
|
33
|
-
|
|
|
34
|
-
+-- database
|
|
35
|
-
+-- cache / Redis
|
|
36
|
-
+-- jobs / scheduler
|
|
37
|
-
+-- workflow
|
|
38
|
-
+-- events / outbox
|
|
39
|
-
+-- realtime
|
|
40
|
-
+-- observability
|
|
41
30
|
```
|
|
42
31
|
|
|
43
|
-
|
|
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.
|
|
44
33
|
|
|
45
34
|
## Basic application
|
|
46
35
|
|
|
@@ -96,245 +85,228 @@ const app =
|
|
|
96
85
|
|
|
97
86
|
The schema may be an object with `parse()` or a parser function.
|
|
98
87
|
|
|
99
|
-
##
|
|
100
|
-
|
|
101
|
-
The application service registry is the same registry used by BCP plugins.
|
|
88
|
+
## Typed dependency injection — 0.3.1
|
|
102
89
|
|
|
103
|
-
|
|
90
|
+
Define providers with `bcp/container`:
|
|
104
91
|
|
|
105
92
|
```ts
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
import {
|
|
94
|
+
createServiceToken,
|
|
95
|
+
provideFactory,
|
|
96
|
+
provideValue,
|
|
97
|
+
} from "bcp/container";
|
|
110
98
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
115
|
-
```
|
|
99
|
+
const configToken =
|
|
100
|
+
createServiceToken<{
|
|
101
|
+
apiUrl: string;
|
|
102
|
+
}>("config");
|
|
116
103
|
|
|
117
|
-
|
|
104
|
+
const repositoryToken =
|
|
105
|
+
createServiceToken<UserRepository>(
|
|
106
|
+
"user-repository"
|
|
107
|
+
);
|
|
118
108
|
|
|
119
|
-
```ts
|
|
120
109
|
const app =
|
|
121
110
|
createApp({
|
|
122
111
|
name: "orders-api",
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
"
|
|
128
|
-
|
|
129
|
-
|
|
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
130
|
});
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
Resolve from application hooks:
|
|
134
134
|
|
|
135
|
-
|
|
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
|
+
```
|
|
136
150
|
|
|
137
|
-
|
|
151
|
+
Application DI APIs:
|
|
138
152
|
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
} from "bcp/application";
|
|
153
|
+
```text
|
|
154
|
+
app.container
|
|
155
|
+
app.context.container
|
|
156
|
+
app.register(provider)
|
|
157
|
+
app.createScope(options)
|
|
158
|
+
```
|
|
146
159
|
|
|
147
|
-
|
|
148
|
-
definePlugin({
|
|
149
|
-
name: "mail",
|
|
160
|
+
`app.register()` follows the same deterministic mutation boundary as plugins/resources and is only allowed before startup begins.
|
|
150
161
|
|
|
151
|
-
|
|
152
|
-
context.services.provide(
|
|
153
|
-
"mail",
|
|
154
|
-
mailer
|
|
155
|
-
);
|
|
156
|
-
},
|
|
157
|
-
});
|
|
162
|
+
For request/job/test boundaries:
|
|
158
163
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
mailPlugin,
|
|
164
|
-
],
|
|
164
|
+
```ts
|
|
165
|
+
const scope =
|
|
166
|
+
app.createScope({
|
|
167
|
+
name: "request:123",
|
|
165
168
|
});
|
|
166
169
|
```
|
|
167
170
|
|
|
168
|
-
|
|
171
|
+
Testing overrides can be supplied to the scope without modifying the application root.
|
|
169
172
|
|
|
170
|
-
|
|
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`:
|
|
171
178
|
|
|
172
179
|
```ts
|
|
173
|
-
app.
|
|
180
|
+
app.provide(
|
|
181
|
+
"database",
|
|
182
|
+
database
|
|
183
|
+
);
|
|
174
184
|
```
|
|
175
185
|
|
|
176
|
-
|
|
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
|
|
177
191
|
|
|
178
|
-
|
|
192
|
+
Existing `bcp/plugins` definitions plug directly into the application runtime:
|
|
179
193
|
|
|
180
194
|
```ts
|
|
181
195
|
const app =
|
|
182
196
|
createApp({
|
|
183
197
|
name: "orders-api",
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
name: "database",
|
|
187
|
-
|
|
188
|
-
async start() {
|
|
189
|
-
await database.connect();
|
|
190
|
-
},
|
|
191
|
-
|
|
192
|
-
ready() {
|
|
193
|
-
return database.ready;
|
|
194
|
-
},
|
|
195
|
-
|
|
196
|
-
async stop() {
|
|
197
|
-
await database.close();
|
|
198
|
-
},
|
|
199
|
-
},
|
|
198
|
+
modules: [
|
|
199
|
+
backendModule,
|
|
200
200
|
],
|
|
201
201
|
});
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
Before startup:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
app.use(mailPlugin);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Infrastructure resources
|
|
211
|
+
|
|
212
|
+
Use the existing Deployment Platform resource contract:
|
|
205
213
|
|
|
206
214
|
```ts
|
|
207
215
|
app.addResource({
|
|
208
|
-
name: "
|
|
216
|
+
name: "database",
|
|
209
217
|
|
|
210
|
-
start() {
|
|
211
|
-
|
|
218
|
+
async start() {
|
|
219
|
+
await database.connect();
|
|
212
220
|
},
|
|
213
221
|
|
|
214
|
-
|
|
215
|
-
return
|
|
222
|
+
ready() {
|
|
223
|
+
return database.ready;
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
async stop() {
|
|
227
|
+
await database.close();
|
|
216
228
|
},
|
|
217
229
|
});
|
|
218
230
|
```
|
|
219
231
|
|
|
220
232
|
## Lifecycle order
|
|
221
233
|
|
|
222
|
-
|
|
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`:
|
|
223
237
|
|
|
224
238
|
```text
|
|
225
239
|
application.setup()
|
|
226
240
|
|
|
|
227
241
|
v
|
|
228
|
-
|
|
242
|
+
bcp:container
|
|
229
243
|
|
|
|
230
244
|
v
|
|
231
|
-
|
|
232
|
-
|
|
245
|
+
bcp:plugins
|
|
246
|
+
|
|
|
247
|
+
v
|
|
248
|
+
resource 1
|
|
249
|
+
resource 2
|
|
233
250
|
...
|
|
234
251
|
|
|
|
235
252
|
v
|
|
236
|
-
application.start()
|
|
253
|
+
bcp:application / application.start()
|
|
237
254
|
|
|
|
238
255
|
v
|
|
239
256
|
state = ready
|
|
240
257
|
```
|
|
241
258
|
|
|
242
|
-
Shutdown reverses
|
|
259
|
+
Shutdown reverses deployment dependencies:
|
|
243
260
|
|
|
244
261
|
```text
|
|
245
262
|
application.stop()
|
|
246
263
|
|
|
|
247
264
|
v
|
|
248
|
-
resource N
|
|
265
|
+
resource N
|
|
249
266
|
...
|
|
250
|
-
resource 1
|
|
267
|
+
resource 1
|
|
268
|
+
|
|
|
269
|
+
v
|
|
270
|
+
plugins stop/dispose
|
|
251
271
|
|
|
|
252
272
|
v
|
|
253
|
-
|
|
273
|
+
container dispose
|
|
254
274
|
|
|
|
255
275
|
v
|
|
256
276
|
application.dispose()
|
|
257
277
|
```
|
|
258
278
|
|
|
259
|
-
This
|
|
260
|
-
|
|
261
|
-
Startup failures use Deployment Platform rollback. Already-started resources are stopped in reverse order and application/plugin disposal still runs.
|
|
279
|
+
This keeps injected services alive while application resources and plugins are shutting down.
|
|
262
280
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
`setup()` runs before the deployment resource graph starts. It is useful for service registration and in-process configuration:
|
|
266
|
-
|
|
267
|
-
```ts
|
|
268
|
-
createApp({
|
|
269
|
-
name: "orders-api",
|
|
270
|
-
|
|
271
|
-
setup(context) {
|
|
272
|
-
context.services.provide(
|
|
273
|
-
"feature-flags",
|
|
274
|
-
flags
|
|
275
|
-
);
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
`start()` runs after plugins and infrastructure resources have started:
|
|
281
|
-
|
|
282
|
-
```ts
|
|
283
|
-
createApp({
|
|
284
|
-
name: "orders-api",
|
|
285
|
-
|
|
286
|
-
start(context) {
|
|
287
|
-
const database =
|
|
288
|
-
context.services.get(
|
|
289
|
-
"database"
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
// Application-level startup after infrastructure is available.
|
|
293
|
-
},
|
|
294
|
-
});
|
|
295
|
-
```
|
|
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.
|
|
296
282
|
|
|
297
283
|
## Readiness and diagnostics
|
|
298
284
|
|
|
299
|
-
Application readiness delegates to the Deployment Platform:
|
|
300
|
-
|
|
301
285
|
```ts
|
|
302
|
-
const
|
|
286
|
+
const readiness =
|
|
303
287
|
await app.readiness();
|
|
304
|
-
```
|
|
305
288
|
|
|
306
|
-
The report includes the internal plugin host, registered resources and the application lifecycle resource.
|
|
307
|
-
|
|
308
|
-
Diagnostics:
|
|
309
|
-
|
|
310
|
-
```ts
|
|
311
289
|
const diagnostics =
|
|
312
290
|
await app.diagnostics();
|
|
313
291
|
```
|
|
314
292
|
|
|
315
|
-
|
|
293
|
+
Readiness now includes internal `bcp:container`, `bcp:plugins`, application resources and `bcp:application` entries.
|
|
316
294
|
|
|
317
|
-
|
|
295
|
+
Application diagnostics expose registered container provider descriptions in addition to plugin/service/deployment information.
|
|
318
296
|
|
|
319
|
-
|
|
297
|
+
## Signals and framework shutdown hooks
|
|
320
298
|
|
|
321
299
|
```ts
|
|
322
300
|
const removeSignals =
|
|
323
301
|
app.installSignalHandlers();
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
The default deployment signals remain `SIGTERM` and `SIGINT`.
|
|
327
302
|
|
|
328
|
-
To participate in the existing BCP production shutdown registry:
|
|
329
|
-
|
|
330
|
-
```ts
|
|
331
303
|
const unregister =
|
|
332
304
|
app.registerShutdownHook();
|
|
333
305
|
```
|
|
334
306
|
|
|
335
|
-
|
|
307
|
+
Both paths perform full application shutdown, including application stop hooks and container disposal.
|
|
336
308
|
|
|
337
|
-
|
|
309
|
+
## Application state
|
|
338
310
|
|
|
339
311
|
```text
|
|
340
312
|
created
|
|
@@ -345,34 +317,32 @@ stopped
|
|
|
345
317
|
failed
|
|
346
318
|
```
|
|
347
319
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
A stopped application is terminal and cannot be started again. Create a new application runtime for a new process lifecycle.
|
|
320
|
+
Concurrent/repeated start and shutdown calls are idempotent in their supported states. A stopped application is terminal and cannot be restarted.
|
|
351
321
|
|
|
352
322
|
## Mutation boundary
|
|
353
323
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
The following are intentionally rejected after startup begins:
|
|
324
|
+
The following are rejected once startup begins:
|
|
357
325
|
|
|
358
326
|
```ts
|
|
359
327
|
app.use(...)
|
|
360
328
|
app.provide(...)
|
|
329
|
+
app.register(...)
|
|
361
330
|
app.addResource(...)
|
|
362
331
|
```
|
|
363
332
|
|
|
364
|
-
|
|
333
|
+
Creating child scopes is allowed while the container is active. Resolving/creating scopes after container disposal is rejected.
|
|
365
334
|
|
|
366
|
-
##
|
|
335
|
+
## Compatibility
|
|
367
336
|
|
|
368
|
-
`0.
|
|
337
|
+
`0.3.1` is additive over `0.3.0`:
|
|
369
338
|
|
|
370
339
|
```text
|
|
371
|
-
|
|
340
|
+
previous baseline: 0.3.0
|
|
341
|
+
intentional breaking changes: false
|
|
372
342
|
```
|
|
373
343
|
|
|
374
|
-
Existing `
|
|
344
|
+
Existing applications that use only `app.services` continue to work. DI adoption can be incremental.
|
|
375
345
|
|
|
376
346
|
## Next platform work
|
|
377
347
|
|
|
378
|
-
The next planned milestone is `0.3.
|
|
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.3.
|
|
4
|
+
"versionTarget": "0.3.2",
|
|
5
5
|
"releaseState": "unreleased",
|
|
6
6
|
"sections": [
|
|
7
7
|
{
|
|
@@ -58,9 +58,11 @@
|
|
|
58
58
|
{
|
|
59
59
|
"id": "runtime",
|
|
60
60
|
"title": "Runtime & Infrastructure",
|
|
61
|
-
"description": "Application composition, middleware, jobs, scheduling, workflows, event delivery, realtime channels, observability, deployment lifecycle, distributed caching, security and production hardening.",
|
|
61
|
+
"description": "Application composition, dependency injection, modules, middleware, jobs, scheduling, workflows, event delivery, realtime channels, observability, deployment lifecycle, distributed caching, security and production hardening.",
|
|
62
62
|
"pages": [
|
|
63
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" },
|
|
65
|
+
{ "route": "/docs/module-system-v2", "source": "module-system-v2.md", "title": "Module System v2" },
|
|
64
66
|
{ "route": "/docs/middleware", "source": "middleware.md", "title": "Middleware" },
|
|
65
67
|
{ "route": "/docs/hydration", "source": "hydration.md", "title": "Hydration" },
|
|
66
68
|
{ "route": "/docs/development-logging", "source": "development-logging.md", "title": "Logging" },
|
|
@@ -97,7 +99,7 @@
|
|
|
97
99
|
{ "route": "/docs/generators", "source": "generators.md", "title": "Project Generators" },
|
|
98
100
|
{ "route": "/docs/developer-tools", "source": "developer-tools.md", "title": "Doctor & Inspect" },
|
|
99
101
|
{ "route": "/docs/testing-platform", "source": "testing-platform.md", "title": "Testing Platform" },
|
|
100
|
-
{ "route": "/docs/plugin-module-platform", "source": "plugin-module-platform.md", "title": "Plugin & Module Platform" }
|
|
102
|
+
{ "route": "/docs/plugin-module-platform", "source": "plugin-module-platform.md", "title": "Plugin & Legacy Module Platform" }
|
|
101
103
|
]
|
|
102
104
|
},
|
|
103
105
|
{
|
|
@@ -108,7 +110,7 @@
|
|
|
108
110
|
{ "route": "/docs/platform-contract", "source": "platform-contract.md", "title": "Framework Platform Contract" },
|
|
109
111
|
{ "route": "/docs/stability-api-freeze", "source": "stability-api-freeze.md", "title": "0.2 Stability & API Freeze" },
|
|
110
112
|
{ "route": "/docs/documentation-platform", "source": "documentation-platform.md", "title": "Documentation Platform" },
|
|
111
|
-
{ "route": "/docs/migration-0.3", "source": "migration-0.3.md", "title": "Migrating to 0.3.
|
|
113
|
+
{ "route": "/docs/migration-0.3", "source": "migration-0.3.md", "title": "Migrating to 0.3.x" },
|
|
112
114
|
{ "route": "/docs/migration-0.2", "source": "migration-0.2.md", "title": "Migrating to 0.2.x" }
|
|
113
115
|
]
|
|
114
116
|
},
|
|
@@ -122,7 +124,9 @@
|
|
|
122
124
|
}
|
|
123
125
|
],
|
|
124
126
|
"releases": [
|
|
125
|
-
{ "route": "/releases/0.3.
|
|
127
|
+
{ "route": "/releases/0.3.2", "source": "releases/0.3.2.md", "version": "0.3.2", "state": "unreleased" },
|
|
128
|
+
{ "route": "/releases/0.3.1", "source": "releases/0.3.1.md", "version": "0.3.1" },
|
|
129
|
+
{ "route": "/releases/0.3.0", "source": "releases/0.3.0.md", "version": "0.3.0" },
|
|
126
130
|
{ "route": "/releases/0.2.19", "source": "releases/0.2.19.md", "version": "0.2.19" },
|
|
127
131
|
{ "route": "/releases/0.2.18", "source": "releases/0.2.18.md", "version": "0.2.18" },
|
|
128
132
|
{ "route": "/releases/0.2.17", "source": "releases/0.2.17.md", "version": "0.2.17" },
|