@chidchanun/bcp 0.2.16 → 0.2.18
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 +150 -408
- package/docs/README.md +36 -38
- package/docs/api-manifest.json +27 -19
- package/docs/api-reference.md +257 -305
- package/docs/deployment-platform-v2.md +449 -0
- package/docs/docs-web-manifest.json +7 -3
- package/docs/observability-v3.md +402 -0
- package/docs/platform-manifest.json +30 -4
- package/docs/releases/0.2.17.md +166 -0
- package/docs/releases/0.2.18.md +136 -0
- package/package.json +11 -6
- package/packages/bundler/src/client-boundary.ts +1 -0
- package/packages/client/src/auth.mjs +1391 -0
- package/packages/client/src/config.mjs +1132 -0
- package/packages/client/src/deployment.mjs +609 -0
- package/packages/client/src/deployment.ts +20 -0
- package/packages/client/src/observability.mjs +1251 -0
- package/packages/client/src/observability.ts +37 -0
- package/packages/client/src/server.mjs +5615 -0
- package/packages/server/src/deployment.ts +936 -0
- package/packages/server/src/middleware.mjs +631 -0
- package/packages/server/src/observability-v3.ts +878 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# Deployment Platform v2
|
|
2
|
+
|
|
3
|
+
BCP Framework `0.2.18` adds a server-only deployment lifecycle layer through `bcp/deployment` and hardens published production entrypoints so application runtime code does not need to execute framework TypeScript directly.
|
|
4
|
+
|
|
5
|
+
## Goals
|
|
6
|
+
|
|
7
|
+
Deployment Platform v2 provides:
|
|
8
|
+
|
|
9
|
+
- application resource startup ordering;
|
|
10
|
+
- reverse-order graceful shutdown;
|
|
11
|
+
- startup rollback when a later resource fails;
|
|
12
|
+
- readiness checks for orchestrators/load balancers;
|
|
13
|
+
- runtime diagnostics and deployment identity;
|
|
14
|
+
- `SIGTERM` / `SIGINT` integration;
|
|
15
|
+
- integration with the existing BCP shutdown-hook registry;
|
|
16
|
+
- compiled production runtimes for key server entrypoints.
|
|
17
|
+
|
|
18
|
+
It does not replace Kubernetes, Docker, systemd, PM2 or another process orchestrator. BCP provides lifecycle primitives that those systems can drive.
|
|
19
|
+
|
|
20
|
+
## Create a deployment runtime
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import {
|
|
24
|
+
createDeploymentRuntime,
|
|
25
|
+
} from "bcp/deployment";
|
|
26
|
+
|
|
27
|
+
export const deployment =
|
|
28
|
+
createDeploymentRuntime({
|
|
29
|
+
serviceName: "orders-api",
|
|
30
|
+
version: "1.4.0",
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Runtime states are:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
idle
|
|
38
|
+
starting
|
|
39
|
+
ready
|
|
40
|
+
draining
|
|
41
|
+
stopped
|
|
42
|
+
failed
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Register resources
|
|
46
|
+
|
|
47
|
+
Resources are started in registration order and stopped in reverse order.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
deployment.addResource({
|
|
51
|
+
name: "database",
|
|
52
|
+
|
|
53
|
+
async start() {
|
|
54
|
+
await db.connect();
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
async ready() {
|
|
58
|
+
return {
|
|
59
|
+
ok:
|
|
60
|
+
db.status ===
|
|
61
|
+
"ready",
|
|
62
|
+
detail:
|
|
63
|
+
db.status,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async stop() {
|
|
68
|
+
await db.close();
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
diagnostics() {
|
|
72
|
+
return {
|
|
73
|
+
provider:
|
|
74
|
+
"postgresql",
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then register dependent services later:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
deployment.addResource({
|
|
84
|
+
name: "jobs",
|
|
85
|
+
|
|
86
|
+
async start() {
|
|
87
|
+
workers.start();
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
async stop() {
|
|
91
|
+
await workers.stop();
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Starting:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
await deployment.start();
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
produces:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
database.start()
|
|
106
|
+
↓
|
|
107
|
+
jobs.start()
|
|
108
|
+
↓
|
|
109
|
+
state = ready
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Shutdown reverses the order:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
jobs.stop()
|
|
116
|
+
↓
|
|
117
|
+
database.stop()
|
|
118
|
+
↓
|
|
119
|
+
state = stopped
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Startup rollback
|
|
123
|
+
|
|
124
|
+
If a resource fails during startup, already-started resources are stopped in reverse order.
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
database.start() ✅
|
|
128
|
+
jobs.start() ✅
|
|
129
|
+
realtime.start() ❌
|
|
130
|
+
|
|
131
|
+
rollback:
|
|
132
|
+
|
|
133
|
+
jobs.stop()
|
|
134
|
+
database.stop()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The runtime moves to `failed` and `start()` rejects.
|
|
138
|
+
|
|
139
|
+
## Resource context
|
|
140
|
+
|
|
141
|
+
Lifecycle callbacks receive:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
{
|
|
145
|
+
metadata,
|
|
146
|
+
signal,
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`signal` is aborted when shutdown begins. Long-running BCP/application components can use it to stop accepting new work or cancel background loops.
|
|
151
|
+
|
|
152
|
+
## Readiness
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
const report =
|
|
156
|
+
await deployment.readiness();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
A runtime is ready only when:
|
|
160
|
+
|
|
161
|
+
1. runtime state is `ready`;
|
|
162
|
+
2. all registered resources are in the started state;
|
|
163
|
+
3. every resource readiness function reports success.
|
|
164
|
+
|
|
165
|
+
Create an HTTP response directly:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import {
|
|
169
|
+
createDeploymentReadinessResponse,
|
|
170
|
+
} from "bcp/deployment";
|
|
171
|
+
|
|
172
|
+
export function GET() {
|
|
173
|
+
return createDeploymentReadinessResponse(
|
|
174
|
+
deployment
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Successful readiness returns HTTP `200`. A non-ready runtime returns `503`.
|
|
180
|
+
|
|
181
|
+
The response uses:
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
Cache-Control: no-store
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Recommended route:
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
/api/ready
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Do not place authentication/user data in this endpoint when it is consumed by infrastructure health probes.
|
|
194
|
+
|
|
195
|
+
## Diagnostics
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
const diagnostics =
|
|
199
|
+
await deployment.diagnostics();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The report includes:
|
|
203
|
+
|
|
204
|
+
- service/deployment identity;
|
|
205
|
+
- process ID;
|
|
206
|
+
- Node version;
|
|
207
|
+
- platform/architecture;
|
|
208
|
+
- runtime state;
|
|
209
|
+
- uptime;
|
|
210
|
+
- lifecycle state for each registered resource;
|
|
211
|
+
- optional resource-specific diagnostic metadata.
|
|
212
|
+
|
|
213
|
+
An HTTP response helper is available:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import {
|
|
217
|
+
createDeploymentDiagnosticsResponse,
|
|
218
|
+
} from "bcp/deployment";
|
|
219
|
+
|
|
220
|
+
export function GET() {
|
|
221
|
+
return createDeploymentDiagnosticsResponse(
|
|
222
|
+
deployment
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Diagnostics can reveal infrastructure details. Protect the route when exposing it outside a trusted operations network.
|
|
228
|
+
|
|
229
|
+
## Deployment metadata
|
|
230
|
+
|
|
231
|
+
The runtime accepts explicit identifiers:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
createDeploymentRuntime({
|
|
235
|
+
serviceName:
|
|
236
|
+
"orders-api",
|
|
237
|
+
deploymentId:
|
|
238
|
+
"deploy-20260831-01",
|
|
239
|
+
instanceId:
|
|
240
|
+
"orders-api-7f5d9",
|
|
241
|
+
release:
|
|
242
|
+
"2026.08.31",
|
|
243
|
+
environmentName:
|
|
244
|
+
"production",
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Or resolve them from runtime environment variables:
|
|
249
|
+
|
|
250
|
+
```text
|
|
251
|
+
BCP_DEPLOYMENT_ID
|
|
252
|
+
BCP_INSTANCE_ID
|
|
253
|
+
BCP_RELEASE
|
|
254
|
+
NODE_ENV
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
If `BCP_DEPLOYMENT_ID` is not supplied, BCP generates a UUID for the process.
|
|
258
|
+
|
|
259
|
+
## Shutdown timeout
|
|
260
|
+
|
|
261
|
+
Default shutdown timeout:
|
|
262
|
+
|
|
263
|
+
```text
|
|
264
|
+
10 seconds
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Configure it explicitly:
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
createDeploymentRuntime({
|
|
271
|
+
serviceName: "orders-api",
|
|
272
|
+
shutdownTimeoutMs:
|
|
273
|
+
30_000,
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
or through:
|
|
278
|
+
|
|
279
|
+
```dotenv
|
|
280
|
+
BCP_SHUTDOWN_TIMEOUT_MS=30000
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The timeout is shared by reverse-order resource shutdown. If a resource does not finish before the remaining deadline, shutdown records the failure and eventually rejects with `AggregateError`.
|
|
284
|
+
|
|
285
|
+
## Signal handling
|
|
286
|
+
|
|
287
|
+
Install handlers:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
const removeSignals =
|
|
291
|
+
deployment.installSignalHandlers();
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Default signals:
|
|
295
|
+
|
|
296
|
+
```text
|
|
297
|
+
SIGTERM
|
|
298
|
+
SIGINT
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The handlers start graceful shutdown and set `process.exitCode` rather than immediately calling `process.exit()`. This allows pending cleanup/microtasks to finish.
|
|
302
|
+
|
|
303
|
+
Remove handlers when ownership moves elsewhere:
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
removeSignals();
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Custom signal set:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
deployment.installSignalHandlers({
|
|
313
|
+
signals: [
|
|
314
|
+
"SIGTERM",
|
|
315
|
+
],
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Existing BCP shutdown hooks
|
|
320
|
+
|
|
321
|
+
Deployment runtime can participate in the existing production-hardening shutdown registry:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
const unregister =
|
|
325
|
+
deployment.registerShutdownHook();
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
When the framework executes registered shutdown hooks, the deployment runtime drains its resources.
|
|
329
|
+
|
|
330
|
+
Use either direct signal ownership or integrate with an application-level signal coordinator. Avoid installing multiple independent handlers that all own the same resource lifecycle.
|
|
331
|
+
|
|
332
|
+
## Recommended topology
|
|
333
|
+
|
|
334
|
+
```text
|
|
335
|
+
Load balancer / orchestrator
|
|
336
|
+
│
|
|
337
|
+
├── readiness probe
|
|
338
|
+
│ ↓
|
|
339
|
+
│ bcp/deployment
|
|
340
|
+
│
|
|
341
|
+
└── SIGTERM
|
|
342
|
+
↓
|
|
343
|
+
state = draining
|
|
344
|
+
↓
|
|
345
|
+
stop inbound/realtime
|
|
346
|
+
↓
|
|
347
|
+
stop workers/jobs
|
|
348
|
+
↓
|
|
349
|
+
close database
|
|
350
|
+
↓
|
|
351
|
+
state = stopped
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Jobs and workflow workers
|
|
355
|
+
|
|
356
|
+
Register workers as resources so they stop before shared dependencies such as Redis or SQL connections:
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
deployment.addResource({
|
|
360
|
+
name: "workers",
|
|
361
|
+
|
|
362
|
+
start() {
|
|
363
|
+
worker =
|
|
364
|
+
jobs.startWorker();
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
async stop() {
|
|
368
|
+
await worker.stop();
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Register database/cache connections before workers. Reverse-order shutdown then naturally stops workers before closing their dependencies.
|
|
374
|
+
|
|
375
|
+
## Realtime
|
|
376
|
+
|
|
377
|
+
For realtime transports, mark the system not-ready before closing shared storage/transport resources. Runtime state changes to `draining` before resource stop hooks run, so readiness probes begin returning `503` while shutdown is in progress.
|
|
378
|
+
|
|
379
|
+
## Observability
|
|
380
|
+
|
|
381
|
+
Deployment metadata can be attached to logs/traces:
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
const metadata =
|
|
385
|
+
deployment.metadata;
|
|
386
|
+
|
|
387
|
+
logger.info(
|
|
388
|
+
"runtime ready",
|
|
389
|
+
{
|
|
390
|
+
deploymentId:
|
|
391
|
+
metadata.deploymentId,
|
|
392
|
+
instanceId:
|
|
393
|
+
metadata.instanceId,
|
|
394
|
+
release:
|
|
395
|
+
metadata.release,
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Use `bcp/observability` for trace/span correlation and `bcp/deployment` for process/deployment identity and lifecycle state.
|
|
401
|
+
|
|
402
|
+
## Compiled server runtimes
|
|
403
|
+
|
|
404
|
+
Prepared `bcp` packages now compile these production entrypoints:
|
|
405
|
+
|
|
406
|
+
```text
|
|
407
|
+
bcp/cache -> cache.mjs
|
|
408
|
+
bcp/config -> config.mjs
|
|
409
|
+
bcp/database -> database.mjs
|
|
410
|
+
bcp/auth -> auth.mjs
|
|
411
|
+
bcp/jobs -> jobs.mjs
|
|
412
|
+
bcp/workflow -> workflow.mjs
|
|
413
|
+
bcp/events -> events.mjs
|
|
414
|
+
bcp/realtime -> realtime.mjs
|
|
415
|
+
bcp/testing -> testing.mjs
|
|
416
|
+
bcp/plugins -> plugins.mjs
|
|
417
|
+
bcp/observability -> observability.mjs
|
|
418
|
+
bcp/deployment -> deployment.mjs
|
|
419
|
+
bcp/server -> server.mjs
|
|
420
|
+
bcp/middleware -> middleware.mjs
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Type declarations/source types remain available from the TypeScript source entrypoint while runtime resolution points to compiled ESM in prepared npm packages.
|
|
424
|
+
|
|
425
|
+
This reduces reliance on Node TypeScript stripping for server framework runtime entrypoints and provides a more deterministic production package.
|
|
426
|
+
|
|
427
|
+
## Compatibility
|
|
428
|
+
|
|
429
|
+
`0.2.18` is intended to be backward compatible with `0.2.17`.
|
|
430
|
+
|
|
431
|
+
Existing:
|
|
432
|
+
|
|
433
|
+
```text
|
|
434
|
+
bcp build
|
|
435
|
+
bcp package
|
|
436
|
+
npm start
|
|
437
|
+
registerShutdownHook()
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
remain available. `bcp/deployment` is additive.
|
|
441
|
+
|
|
442
|
+
## Related guides
|
|
443
|
+
|
|
444
|
+
- [Application Packaging](application-packaging.md)
|
|
445
|
+
- [Deployment](deployment.md)
|
|
446
|
+
- [Production Hardening](production-hardening.md)
|
|
447
|
+
- [Observability Platform v3](observability-v3.md)
|
|
448
|
+
- [Background Jobs](background-jobs.md)
|
|
449
|
+
- [Realtime Platform](realtime-platform.md)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"framework": "bcp",
|
|
4
|
-
"versionTarget": "0.2.
|
|
4
|
+
"versionTarget": "0.2.18",
|
|
5
5
|
"releaseState": "unreleased",
|
|
6
6
|
"sections": [
|
|
7
7
|
{
|
|
@@ -58,12 +58,14 @@
|
|
|
58
58
|
{
|
|
59
59
|
"id": "runtime",
|
|
60
60
|
"title": "Runtime & Infrastructure",
|
|
61
|
-
"description": "Middleware, jobs, scheduling, workflows, event delivery, realtime channels, observability, distributed caching, security and production hardening.",
|
|
61
|
+
"description": "Middleware, jobs, scheduling, workflows, event delivery, realtime channels, observability, deployment lifecycle, distributed caching, security and production hardening.",
|
|
62
62
|
"pages": [
|
|
63
63
|
{ "route": "/docs/middleware", "source": "middleware.md", "title": "Middleware" },
|
|
64
64
|
{ "route": "/docs/hydration", "source": "hydration.md", "title": "Hydration" },
|
|
65
65
|
{ "route": "/docs/development-logging", "source": "development-logging.md", "title": "Logging" },
|
|
66
66
|
{ "route": "/docs/observability", "source": "observability.md", "title": "Observability Platform v2" },
|
|
67
|
+
{ "route": "/docs/observability-v3", "source": "observability-v3.md", "title": "Observability Platform v3" },
|
|
68
|
+
{ "route": "/docs/deployment-platform-v2", "source": "deployment-platform-v2.md", "title": "Deployment Platform v2" },
|
|
67
69
|
{ "route": "/docs/background-jobs", "source": "background-jobs.md", "title": "Background Jobs Platform" },
|
|
68
70
|
{ "route": "/docs/job-scheduling", "source": "job-scheduling.md", "title": "Job Scheduling Platform" },
|
|
69
71
|
{ "route": "/docs/durable-jobs", "source": "durable-jobs.md", "title": "Durable Jobs Platform" },
|
|
@@ -117,7 +119,9 @@
|
|
|
117
119
|
}
|
|
118
120
|
],
|
|
119
121
|
"releases": [
|
|
120
|
-
{ "route": "/releases/0.2.
|
|
122
|
+
{ "route": "/releases/0.2.18", "source": "releases/0.2.18.md", "version": "0.2.18", "state": "unreleased" },
|
|
123
|
+
{ "route": "/releases/0.2.17", "source": "releases/0.2.17.md", "version": "0.2.17" },
|
|
124
|
+
{ "route": "/releases/0.2.16", "source": "releases/0.2.16.md", "version": "0.2.16" },
|
|
121
125
|
{ "route": "/releases/0.2.15", "source": "releases/0.2.15.md", "version": "0.2.15" },
|
|
122
126
|
{ "route": "/releases/0.2.14", "source": "releases/0.2.14.md", "version": "0.2.14" },
|
|
123
127
|
{ "route": "/releases/0.2.13", "source": "releases/0.2.13.md", "version": "0.2.13" },
|