create-bcp-app 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 +89 -263
- package/package.json +1 -1
- package/template/README.md +90 -260
package/README.md
CHANGED
|
@@ -10,9 +10,7 @@ npm run dev
|
|
|
10
10
|
|
|
11
11
|
## Project-local BCP CLI
|
|
12
12
|
|
|
13
|
-
`create-bcp-app` installs BCP Framework as a project-local dependency.
|
|
14
|
-
|
|
15
|
-
Generated npm scripts use `bcp` because npm places `node_modules/.bin` on the script `PATH`:
|
|
13
|
+
`create-bcp-app` installs BCP Framework as a project-local dependency. Generated npm scripts use the local CLI:
|
|
16
14
|
|
|
17
15
|
```json
|
|
18
16
|
{
|
|
@@ -28,13 +26,12 @@ Generated npm scripts use `bcp` because npm places `node_modules/.bin` on the sc
|
|
|
28
26
|
}
|
|
29
27
|
```
|
|
30
28
|
|
|
31
|
-
For direct PowerShell usage, prefer the collision-free
|
|
29
|
+
For direct PowerShell usage, prefer the collision-free alias:
|
|
32
30
|
|
|
33
31
|
```powershell
|
|
34
32
|
npm exec -- bcp-framework --version
|
|
35
33
|
npm exec -- bcp-framework doctor
|
|
36
34
|
npm exec -- bcp-framework inspect
|
|
37
|
-
npm exec -- bcp-framework generate page dashboard/users
|
|
38
35
|
npm exec -- bcp-framework routes
|
|
39
36
|
npm exec -- bcp-framework dev
|
|
40
37
|
npm exec -- bcp-framework build
|
|
@@ -45,8 +42,6 @@ Microsoft SQL Server can install another Windows executable named `bcp.exe`, so
|
|
|
45
42
|
|
|
46
43
|
## Interactive choices
|
|
47
44
|
|
|
48
|
-
The generator asks for optional application presets:
|
|
49
|
-
|
|
50
45
|
```text
|
|
51
46
|
Use Tailwind CSS?
|
|
52
47
|
Select a database:
|
|
@@ -69,17 +64,17 @@ Select storage provider:
|
|
|
69
64
|
|
|
70
65
|
New projects include `bcp.project.json`.
|
|
71
66
|
|
|
72
|
-
Example for the `0.2.
|
|
67
|
+
Example for the `0.2.18` target:
|
|
73
68
|
|
|
74
69
|
```json
|
|
75
70
|
{
|
|
76
71
|
"schemaVersion": 1,
|
|
77
72
|
"framework": "bcp",
|
|
78
73
|
"projectName": "my-app",
|
|
79
|
-
"frameworkPackage": "npm:@chidchanun/bcp@0.2.
|
|
74
|
+
"frameworkPackage": "npm:@chidchanun/bcp@0.2.18",
|
|
80
75
|
"createdWith": {
|
|
81
76
|
"package": "create-bcp-app",
|
|
82
|
-
"version": "0.2.
|
|
77
|
+
"version": "0.2.18"
|
|
83
78
|
},
|
|
84
79
|
"packageManager": "npm",
|
|
85
80
|
"presets": {
|
|
@@ -91,7 +86,7 @@ Example for the `0.2.16` target:
|
|
|
91
86
|
}
|
|
92
87
|
```
|
|
93
88
|
|
|
94
|
-
This manifest records scaffold identity only.
|
|
89
|
+
This manifest records scaffold identity only. Do not place secrets in it.
|
|
95
90
|
|
|
96
91
|
## Database presets
|
|
97
92
|
|
|
@@ -107,88 +102,23 @@ For MySQL, PostgreSQL and SQLite, generated `lib/database.ts` exposes BCP databa
|
|
|
107
102
|
|
|
108
103
|
## Authentication and security
|
|
109
104
|
|
|
110
|
-
The JWT Cookie preset creates
|
|
111
|
-
|
|
112
|
-
## Observability
|
|
113
|
-
|
|
114
|
-
```ts
|
|
115
|
-
import {
|
|
116
|
-
createHealthRegistry,
|
|
117
|
-
createMetricsRegistry,
|
|
118
|
-
} from "bcp/observability";
|
|
119
|
-
```
|
|
105
|
+
The JWT Cookie preset creates starter authentication code. BCP supports revocable session stores, permission/policy authorization, route guards and same-origin/CSRF helpers.
|
|
120
106
|
|
|
121
|
-
## Background jobs and
|
|
107
|
+
## Background jobs, workflows and events
|
|
122
108
|
|
|
123
109
|
```ts
|
|
124
110
|
import {
|
|
125
111
|
createJobQueue,
|
|
126
112
|
createJobScheduler,
|
|
127
113
|
} from "bcp/jobs";
|
|
128
|
-
|
|
129
|
-
export const jobs =
|
|
130
|
-
createJobQueue();
|
|
131
|
-
|
|
132
|
-
export const scheduler =
|
|
133
|
-
createJobScheduler({
|
|
134
|
-
queue: jobs,
|
|
135
|
-
});
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
`0.2.10+` adds visibility leases, heartbeat renewal, stale-running recovery, DLQ/requeue, retention cleanup and Redis-compatible durable adapters.
|
|
139
|
-
|
|
140
|
-
## Workflow orchestration — 0.2.11+
|
|
141
|
-
|
|
142
|
-
```ts
|
|
143
114
|
import {
|
|
144
115
|
createWorkflow,
|
|
145
116
|
} from "bcp/workflow";
|
|
146
|
-
|
|
147
|
-
export const onboarding =
|
|
148
|
-
createWorkflow(
|
|
149
|
-
"user.onboarding",
|
|
150
|
-
workflow => {
|
|
151
|
-
workflow.step(
|
|
152
|
-
"profile",
|
|
153
|
-
createProfile
|
|
154
|
-
);
|
|
155
|
-
workflow.delay(
|
|
156
|
-
"cooldown",
|
|
157
|
-
1_000
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
);
|
|
161
117
|
```
|
|
162
118
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
## Transactional Outbox & Events — 0.2.12+
|
|
119
|
+
Durable Redis-compatible queues/schedules, workflow orchestration and transactional outbox/event delivery are available without forcing a Redis client dependency.
|
|
166
120
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
```ts
|
|
170
|
-
await db.transaction(
|
|
171
|
-
async tx => {
|
|
172
|
-
await tx.execute(
|
|
173
|
-
"INSERT INTO orders ..."
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
await outbox.publish(
|
|
177
|
-
tx,
|
|
178
|
-
"order.created",
|
|
179
|
-
{
|
|
180
|
-
orderId: 42,
|
|
181
|
-
}
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
);
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
After commit, `createOutboxDispatcher()` can hand off events to durable jobs/custom publishers.
|
|
188
|
-
|
|
189
|
-
## Realtime Platform — 0.2.13+
|
|
190
|
-
|
|
191
|
-
Generated applications can add server-side realtime channels without changing the scaffold preset model:
|
|
121
|
+
## Realtime
|
|
192
122
|
|
|
193
123
|
```ts
|
|
194
124
|
import {
|
|
@@ -199,242 +129,138 @@ export const realtime =
|
|
|
199
129
|
createRealtime();
|
|
200
130
|
```
|
|
201
131
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
```ts
|
|
205
|
-
const connection =
|
|
206
|
-
await realtime.connect();
|
|
207
|
-
|
|
208
|
-
await connection.join(
|
|
209
|
-
"orders:42"
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
await realtime.broadcast(
|
|
213
|
-
"orders:42",
|
|
214
|
-
"order.updated",
|
|
215
|
-
{
|
|
216
|
-
status: "paid",
|
|
217
|
-
}
|
|
218
|
-
);
|
|
219
|
-
```
|
|
132
|
+
BCP does not install a WebSocket server library. Applications adapt their provider to `RealtimeSocket`; SSE is built in.
|
|
220
133
|
|
|
221
|
-
|
|
134
|
+
## Testing
|
|
222
135
|
|
|
223
|
-
|
|
136
|
+
`bcp/testing` provides request/route/page/auth/database/middleware/jobs/workflow/outbox/realtime test harnesses and does not require Jest or Vitest.
|
|
224
137
|
|
|
225
|
-
##
|
|
138
|
+
## Plugins
|
|
226
139
|
|
|
227
|
-
`bcp/
|
|
140
|
+
`bcp/plugins` provides dependency ordering, module composition, lifecycle hooks, config parsing, shared services and async hooks.
|
|
228
141
|
|
|
229
|
-
|
|
142
|
+
## Cache Platform v2
|
|
230
143
|
|
|
231
144
|
```ts
|
|
232
145
|
import {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
expectResponse,
|
|
236
|
-
} from "bcp/testing";
|
|
237
|
-
|
|
238
|
-
const app =
|
|
239
|
-
createTestApp({
|
|
240
|
-
handler:
|
|
241
|
-
createRouteTestHandler({
|
|
242
|
-
GET() {
|
|
243
|
-
return {
|
|
244
|
-
ok: true,
|
|
245
|
-
};
|
|
246
|
-
},
|
|
247
|
-
}),
|
|
248
|
-
});
|
|
146
|
+
createCacheStore,
|
|
147
|
+
} from "bcp/cache";
|
|
249
148
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
)
|
|
253
|
-
.status(200)
|
|
254
|
-
.json({
|
|
255
|
-
ok: true,
|
|
256
|
-
});
|
|
149
|
+
export const cache =
|
|
150
|
+
createCacheStore();
|
|
257
151
|
```
|
|
258
152
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
import {
|
|
263
|
-
createTestAuthSession,
|
|
264
|
-
} from "bcp/testing";
|
|
265
|
-
|
|
266
|
-
const session =
|
|
267
|
-
await createTestAuthSession(
|
|
268
|
-
{
|
|
269
|
-
id: 42,
|
|
270
|
-
role: "admin",
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
secret:
|
|
274
|
-
process.env.BCP_SESSION_SECRET,
|
|
275
|
-
}
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
app.setCookie(
|
|
279
|
-
session.cookieName,
|
|
280
|
-
session.token
|
|
281
|
-
);
|
|
282
|
-
```
|
|
153
|
+
Multi-instance applications can use Redis-compatible cache and lock adapters for distributed cache-fill coordination.
|
|
283
154
|
|
|
284
|
-
|
|
155
|
+
## Observability Platform v3
|
|
285
156
|
|
|
286
157
|
```ts
|
|
287
158
|
import {
|
|
288
|
-
|
|
289
|
-
} from "bcp/
|
|
290
|
-
|
|
291
|
-
await withTestTransaction(
|
|
292
|
-
db,
|
|
293
|
-
async tx => {
|
|
294
|
-
await tx.execute(
|
|
295
|
-
"INSERT INTO users ..."
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
);
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
Infrastructure helpers include:
|
|
159
|
+
createTracer,
|
|
160
|
+
} from "bcp/observability";
|
|
302
161
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
createRealtimeTestSocket()
|
|
308
|
-
createRealtimeTestHarness()
|
|
309
|
-
readSseEvents()
|
|
310
|
-
createFakeClock()
|
|
311
|
-
createSequenceIdFactory()
|
|
312
|
-
runTestMiddleware()
|
|
162
|
+
export const tracer =
|
|
163
|
+
createTracer({
|
|
164
|
+
serviceName: "my-app",
|
|
165
|
+
});
|
|
313
166
|
```
|
|
314
167
|
|
|
315
|
-
|
|
168
|
+
Tracing supports W3C `traceparent`, correlation IDs, request middleware, explicit carriers for jobs/workflows/events/realtime and provider-neutral exporters.
|
|
316
169
|
|
|
317
|
-
##
|
|
170
|
+
## Deployment Platform v2 — 0.2.18+
|
|
318
171
|
|
|
319
|
-
Use `bcp/
|
|
172
|
+
Use `bcp/deployment` to coordinate application resources in production:
|
|
320
173
|
|
|
321
174
|
```ts
|
|
322
175
|
import {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
definePlugin,
|
|
326
|
-
} from "bcp/plugins";
|
|
327
|
-
|
|
328
|
-
const databasePlugin =
|
|
329
|
-
definePlugin({
|
|
330
|
-
name: "database",
|
|
331
|
-
setup(context) {
|
|
332
|
-
context.services.provide(
|
|
333
|
-
"database",
|
|
334
|
-
db
|
|
335
|
-
);
|
|
336
|
-
},
|
|
337
|
-
});
|
|
176
|
+
createDeploymentRuntime,
|
|
177
|
+
} from "bcp/deployment";
|
|
338
178
|
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
requires: [
|
|
343
|
-
"database",
|
|
344
|
-
],
|
|
179
|
+
export const deployment =
|
|
180
|
+
createDeploymentRuntime({
|
|
181
|
+
serviceName: "my-app",
|
|
345
182
|
});
|
|
183
|
+
```
|
|
346
184
|
|
|
347
|
-
|
|
348
|
-
defineModule({
|
|
349
|
-
name: "backend",
|
|
350
|
-
plugins: [
|
|
351
|
-
databasePlugin,
|
|
352
|
-
jobsPlugin,
|
|
353
|
-
],
|
|
354
|
-
});
|
|
185
|
+
Register shared dependencies before components that use them:
|
|
355
186
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
backendModule,
|
|
360
|
-
],
|
|
361
|
-
});
|
|
187
|
+
```ts
|
|
188
|
+
deployment.addResource({
|
|
189
|
+
name: "database",
|
|
362
190
|
|
|
363
|
-
|
|
364
|
-
|
|
191
|
+
async start() {
|
|
192
|
+
await db.connect();
|
|
193
|
+
},
|
|
365
194
|
|
|
366
|
-
|
|
195
|
+
ready() {
|
|
196
|
+
return db.status === "ready";
|
|
197
|
+
},
|
|
367
198
|
|
|
368
|
-
|
|
199
|
+
async stop() {
|
|
200
|
+
await db.close();
|
|
201
|
+
},
|
|
202
|
+
});
|
|
369
203
|
|
|
370
|
-
|
|
204
|
+
deployment.addResource({
|
|
205
|
+
name: "workers",
|
|
371
206
|
|
|
372
|
-
|
|
207
|
+
start() {
|
|
208
|
+
worker = jobs.startWorker();
|
|
209
|
+
},
|
|
373
210
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
211
|
+
async stop() {
|
|
212
|
+
await worker.stop();
|
|
213
|
+
},
|
|
214
|
+
});
|
|
378
215
|
|
|
379
|
-
|
|
380
|
-
createCacheStore();
|
|
216
|
+
await deployment.start();
|
|
381
217
|
```
|
|
382
218
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
```ts
|
|
386
|
-
const user =
|
|
387
|
-
await cache.getOrSet(
|
|
388
|
-
"user:42",
|
|
389
|
-
() => loadUser(42),
|
|
390
|
-
{
|
|
391
|
-
ttlMs: 60_000,
|
|
392
|
-
tags: ["users"],
|
|
393
|
-
paths: ["/users/42"],
|
|
394
|
-
}
|
|
395
|
-
);
|
|
396
|
-
```
|
|
219
|
+
Startup follows registration order and shutdown reverses it. This naturally stops workers before database/cache/Redis connections.
|
|
397
220
|
|
|
398
|
-
|
|
221
|
+
Readiness endpoint:
|
|
399
222
|
|
|
400
223
|
```ts
|
|
401
224
|
import {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
} from "bcp/cache";
|
|
225
|
+
createDeploymentReadinessResponse,
|
|
226
|
+
} from "bcp/deployment";
|
|
405
227
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
228
|
+
export function GET() {
|
|
229
|
+
return createDeploymentReadinessResponse(
|
|
230
|
+
deployment
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
410
234
|
|
|
411
|
-
|
|
412
|
-
createRedisCacheLockAdapter({
|
|
413
|
-
client: redisClient,
|
|
414
|
-
});
|
|
235
|
+
Runtime identity can use:
|
|
415
236
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
237
|
+
```text
|
|
238
|
+
BCP_DEPLOYMENT_ID
|
|
239
|
+
BCP_INSTANCE_ID
|
|
240
|
+
BCP_RELEASE
|
|
241
|
+
NODE_ENV
|
|
242
|
+
BCP_SHUTDOWN_TIMEOUT_MS
|
|
421
243
|
```
|
|
422
244
|
|
|
423
|
-
|
|
245
|
+
Install graceful signal handling with:
|
|
424
246
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
247
|
+
```ts
|
|
248
|
+
const removeSignals =
|
|
249
|
+
deployment.installSignalHandlers();
|
|
250
|
+
```
|
|
428
251
|
|
|
429
|
-
|
|
252
|
+
Default signals are `SIGTERM` and `SIGINT`.
|
|
430
253
|
|
|
431
254
|
## Application packaging
|
|
432
255
|
|
|
433
256
|
```bash
|
|
257
|
+
npm run build
|
|
434
258
|
npm run package
|
|
435
259
|
```
|
|
436
260
|
|
|
437
|
-
The deployment package excludes application `devDependencies` and project `.env` values. Supply
|
|
261
|
+
The deployment package excludes application `devDependencies` and project `.env` values. Supply secrets through the deployment environment.
|
|
262
|
+
|
|
263
|
+
BCP `0.2.18` prepared framework packages use compiled ESM runtime files for the main server entrypoints including config, auth, observability, deployment, server and middleware.
|
|
438
264
|
|
|
439
265
|
## Project generators
|
|
440
266
|
|
|
@@ -462,5 +288,5 @@ npm run generate -- migration create_users
|
|
|
462
288
|
For prerelease/local package verification:
|
|
463
289
|
|
|
464
290
|
```bash
|
|
465
|
-
npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.
|
|
291
|
+
npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.18.tgz
|
|
466
292
|
```
|
package/package.json
CHANGED
package/template/README.md
CHANGED
|
@@ -27,113 +27,33 @@ npm run update
|
|
|
27
27
|
|
|
28
28
|
Generated projects include `bcp.project.json` with non-secret scaffold metadata. Commit it with the project, but never put passwords, tokens or access keys in it.
|
|
29
29
|
|
|
30
|
-
## Authentication
|
|
30
|
+
## Authentication and authorization
|
|
31
31
|
|
|
32
|
-
JWT Cookie projects can
|
|
32
|
+
JWT Cookie projects can use `createAuth()` and optional `AuthSessionStore` revocation. Permission guards/resource policies live in `bcp/auth`; same-origin/CSRF helpers live in `bcp/server`.
|
|
33
33
|
|
|
34
|
-
##
|
|
34
|
+
## Database
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
MySQL, PostgreSQL and SQLite projects expose the BCP database platform through `bcp/database`.
|
|
37
37
|
|
|
38
|
-
##
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
import {
|
|
42
|
-
createHealthRegistry,
|
|
43
|
-
createMetricsRegistry,
|
|
44
|
-
} from "bcp/observability";
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Background jobs — BCP 0.2.8+
|
|
38
|
+
## Jobs, scheduling and workflows
|
|
48
39
|
|
|
49
40
|
```ts
|
|
50
41
|
import {
|
|
51
42
|
createJobQueue,
|
|
52
|
-
} from "bcp/jobs";
|
|
53
|
-
|
|
54
|
-
export const jobs =
|
|
55
|
-
createJobQueue();
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Job scheduling — BCP 0.2.9+
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
import {
|
|
62
43
|
createJobScheduler,
|
|
63
44
|
} from "bcp/jobs";
|
|
64
|
-
|
|
65
|
-
export const scheduler =
|
|
66
|
-
createJobScheduler({
|
|
67
|
-
queue: jobs,
|
|
68
|
-
});
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Durable jobs — BCP 0.2.10+
|
|
72
|
-
|
|
73
|
-
Workers can use visibility leases, heartbeat renewal, stale recovery and DLQ/requeue. Redis-compatible adapters are available without forcing a Redis client dependency.
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
const worker =
|
|
77
|
-
jobs.startWorker({
|
|
78
|
-
workerId: "worker-a",
|
|
79
|
-
concurrency: 4,
|
|
80
|
-
visibilityTimeoutMs: 30_000,
|
|
81
|
-
heartbeatIntervalMs: 10_000,
|
|
82
|
-
});
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Workflow orchestration — BCP 0.2.11+
|
|
86
|
-
|
|
87
|
-
```ts
|
|
88
45
|
import {
|
|
89
46
|
createWorkflow,
|
|
90
47
|
} from "bcp/workflow";
|
|
91
|
-
|
|
92
|
-
export const onboarding =
|
|
93
|
-
createWorkflow(
|
|
94
|
-
"user.onboarding",
|
|
95
|
-
workflow => {
|
|
96
|
-
workflow.step(
|
|
97
|
-
"profile",
|
|
98
|
-
createProfile
|
|
99
|
-
);
|
|
100
|
-
workflow.delay(
|
|
101
|
-
"cooldown",
|
|
102
|
-
1_000
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
);
|
|
106
48
|
```
|
|
107
49
|
|
|
108
|
-
Workflows support sequential/parallel steps, retries, persisted delays
|
|
109
|
-
|
|
110
|
-
## Transactional Outbox & Events — BCP 0.2.12+
|
|
111
|
-
|
|
112
|
-
Use `bcp/events` when application data and an integration event must commit atomically in the same SQL transaction.
|
|
113
|
-
|
|
114
|
-
```ts
|
|
115
|
-
await db.transaction(
|
|
116
|
-
async tx => {
|
|
117
|
-
await tx.execute(
|
|
118
|
-
"INSERT INTO orders ..."
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
await outbox.publish(
|
|
122
|
-
tx,
|
|
123
|
-
"order.created",
|
|
124
|
-
{
|
|
125
|
-
orderId: 42,
|
|
126
|
-
}
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
```
|
|
50
|
+
Durable jobs support leases, heartbeat, stale recovery and DLQ. Workflows support sequential/parallel steps, retries, persisted delays and compensation.
|
|
131
51
|
|
|
132
|
-
|
|
52
|
+
## Transactional events
|
|
133
53
|
|
|
134
|
-
|
|
54
|
+
Use `bcp/events` when business data and an integration event must commit in the same SQL transaction. The outbox dispatcher can deliver through durable jobs or an application publisher after commit.
|
|
135
55
|
|
|
136
|
-
|
|
56
|
+
## Realtime
|
|
137
57
|
|
|
138
58
|
```ts
|
|
139
59
|
import {
|
|
@@ -144,224 +64,118 @@ export const realtime =
|
|
|
144
64
|
createRealtime();
|
|
145
65
|
```
|
|
146
66
|
|
|
147
|
-
|
|
67
|
+
BCP does not install a WebSocket server library. Adapt the selected provider through `RealtimeSocket`; SSE is built in.
|
|
148
68
|
|
|
149
|
-
|
|
150
|
-
const connection =
|
|
151
|
-
await realtime.connect();
|
|
152
|
-
|
|
153
|
-
await connection.join(
|
|
154
|
-
"orders:42"
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
await realtime.broadcast(
|
|
158
|
-
"orders:42",
|
|
159
|
-
"order.updated",
|
|
160
|
-
{
|
|
161
|
-
status: "paid",
|
|
162
|
-
}
|
|
163
|
-
);
|
|
164
|
-
```
|
|
69
|
+
## Testing
|
|
165
70
|
|
|
166
|
-
|
|
71
|
+
`bcp/testing` provides request/route/page/auth/database/middleware/jobs/workflow/outbox/realtime/SSE test helpers without requiring Jest or Vitest.
|
|
167
72
|
|
|
168
|
-
|
|
73
|
+
## Plugins
|
|
169
74
|
|
|
170
|
-
|
|
75
|
+
`bcp/plugins` provides reusable server-side plugins/modules, dependency ordering, lifecycle hooks, config parsing, shared services and awaited hooks.
|
|
171
76
|
|
|
172
|
-
|
|
77
|
+
## Cache Platform v2
|
|
173
78
|
|
|
174
79
|
```ts
|
|
175
80
|
import {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
expectResponse,
|
|
179
|
-
} from "bcp/testing";
|
|
180
|
-
|
|
181
|
-
const app =
|
|
182
|
-
createTestApp({
|
|
183
|
-
handler:
|
|
184
|
-
createRouteTestHandler({
|
|
185
|
-
GET() {
|
|
186
|
-
return {
|
|
187
|
-
ok: true,
|
|
188
|
-
};
|
|
189
|
-
},
|
|
190
|
-
}),
|
|
191
|
-
});
|
|
81
|
+
createCacheStore,
|
|
82
|
+
} from "bcp/cache";
|
|
192
83
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
196
|
-
.status(200)
|
|
197
|
-
.json({
|
|
198
|
-
ok: true,
|
|
199
|
-
});
|
|
84
|
+
export const cache =
|
|
85
|
+
createCacheStore();
|
|
200
86
|
```
|
|
201
87
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
```ts
|
|
205
|
-
import {
|
|
206
|
-
createTestAuthSession,
|
|
207
|
-
} from "bcp/testing";
|
|
208
|
-
|
|
209
|
-
const session =
|
|
210
|
-
await createTestAuthSession(
|
|
211
|
-
{
|
|
212
|
-
id: 42,
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
secret:
|
|
216
|
-
process.env.BCP_SESSION_SECRET,
|
|
217
|
-
}
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
app.setCookie(
|
|
221
|
-
session.cookieName,
|
|
222
|
-
session.token
|
|
223
|
-
);
|
|
224
|
-
```
|
|
88
|
+
Use Redis-compatible cache/lock adapters for multi-instance cache-fill coordination when needed.
|
|
225
89
|
|
|
226
|
-
|
|
90
|
+
## Observability Platform v3
|
|
227
91
|
|
|
228
92
|
```ts
|
|
229
93
|
import {
|
|
230
|
-
|
|
231
|
-
} from "bcp/
|
|
232
|
-
|
|
233
|
-
await withTestTransaction(
|
|
234
|
-
db,
|
|
235
|
-
async tx => {
|
|
236
|
-
await tx.execute(
|
|
237
|
-
"INSERT INTO users ..."
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
);
|
|
241
|
-
```
|
|
94
|
+
createTracer,
|
|
95
|
+
} from "bcp/observability";
|
|
242
96
|
|
|
243
|
-
|
|
97
|
+
export const tracer =
|
|
98
|
+
createTracer({
|
|
99
|
+
serviceName: "bcp-app",
|
|
100
|
+
});
|
|
101
|
+
```
|
|
244
102
|
|
|
245
|
-
|
|
103
|
+
Tracing supports AsyncLocalStorage context, W3C `traceparent`, correlation IDs, request tracing, trace carriers and provider-neutral exporters.
|
|
246
104
|
|
|
247
|
-
##
|
|
105
|
+
## Deployment Platform v2 — BCP 0.2.18+
|
|
248
106
|
|
|
249
|
-
Use `bcp/
|
|
107
|
+
Use `bcp/deployment` to manage production resource lifecycle:
|
|
250
108
|
|
|
251
109
|
```ts
|
|
252
110
|
import {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
} from "bcp/plugins";
|
|
256
|
-
|
|
257
|
-
const databasePlugin =
|
|
258
|
-
definePlugin({
|
|
259
|
-
name: "database",
|
|
260
|
-
setup(context) {
|
|
261
|
-
context.services.provide(
|
|
262
|
-
"database",
|
|
263
|
-
db
|
|
264
|
-
);
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
const jobsPlugin =
|
|
269
|
-
definePlugin({
|
|
270
|
-
name: "jobs",
|
|
271
|
-
requires: [
|
|
272
|
-
"database",
|
|
273
|
-
],
|
|
274
|
-
});
|
|
111
|
+
createDeploymentRuntime,
|
|
112
|
+
} from "bcp/deployment";
|
|
275
113
|
|
|
276
|
-
export const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
jobsPlugin,
|
|
280
|
-
databasePlugin,
|
|
281
|
-
],
|
|
114
|
+
export const deployment =
|
|
115
|
+
createDeploymentRuntime({
|
|
116
|
+
serviceName: "bcp-app",
|
|
282
117
|
});
|
|
283
118
|
```
|
|
284
119
|
|
|
285
|
-
|
|
120
|
+
Register dependencies first:
|
|
286
121
|
|
|
287
|
-
|
|
122
|
+
```ts
|
|
123
|
+
deployment.addResource({
|
|
124
|
+
name: "database",
|
|
288
125
|
|
|
289
|
-
|
|
126
|
+
async start() {
|
|
127
|
+
await db.connect();
|
|
128
|
+
},
|
|
290
129
|
|
|
291
|
-
|
|
130
|
+
ready() {
|
|
131
|
+
return db.status === "ready";
|
|
132
|
+
},
|
|
292
133
|
|
|
293
|
-
|
|
134
|
+
async stop() {
|
|
135
|
+
await db.close();
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
294
139
|
|
|
295
|
-
|
|
296
|
-
import {
|
|
297
|
-
createCacheStore,
|
|
298
|
-
} from "bcp/cache";
|
|
140
|
+
Register workers/realtime services afterward so reverse-order shutdown stops them before their shared database/cache/Redis dependencies.
|
|
299
141
|
|
|
300
|
-
|
|
301
|
-
createCacheStore();
|
|
302
|
-
```
|
|
142
|
+
Start runtime:
|
|
303
143
|
|
|
304
144
|
```ts
|
|
305
|
-
|
|
306
|
-
await cache.getOrSet(
|
|
307
|
-
"user:42",
|
|
308
|
-
() => loadUser(42),
|
|
309
|
-
{
|
|
310
|
-
ttlMs: 60_000,
|
|
311
|
-
tags: ["users"],
|
|
312
|
-
paths: ["/users/42"],
|
|
313
|
-
}
|
|
314
|
-
);
|
|
145
|
+
await deployment.start();
|
|
315
146
|
```
|
|
316
147
|
|
|
317
|
-
|
|
148
|
+
Readiness endpoint:
|
|
318
149
|
|
|
319
150
|
```ts
|
|
320
151
|
import {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
} from "bcp/cache";
|
|
324
|
-
|
|
325
|
-
const redisCache =
|
|
326
|
-
createRedisCacheAdapter({
|
|
327
|
-
client: redisClient,
|
|
328
|
-
});
|
|
152
|
+
createDeploymentReadinessResponse,
|
|
153
|
+
} from "bcp/deployment";
|
|
329
154
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
export const cache =
|
|
336
|
-
createCacheStore({
|
|
337
|
-
adapter: redisCache,
|
|
338
|
-
lock: redisLock,
|
|
339
|
-
});
|
|
155
|
+
export function GET() {
|
|
156
|
+
return createDeploymentReadinessResponse(
|
|
157
|
+
deployment
|
|
158
|
+
);
|
|
159
|
+
}
|
|
340
160
|
```
|
|
341
161
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
## Generate framework files
|
|
162
|
+
Install graceful signal handling when the app owns process signals:
|
|
345
163
|
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
npm run generate -- middleware
|
|
350
|
-
npm run generate -- migration create_users
|
|
164
|
+
```ts
|
|
165
|
+
const removeSignals =
|
|
166
|
+
deployment.installSignalHandlers();
|
|
351
167
|
```
|
|
352
168
|
|
|
353
|
-
|
|
169
|
+
Default signals are `SIGTERM` and `SIGINT`.
|
|
354
170
|
|
|
355
|
-
|
|
171
|
+
Deployment identity can be supplied through:
|
|
356
172
|
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
npm exec -- bcp-framework build
|
|
364
|
-
npm exec -- bcp-framework package
|
|
173
|
+
```text
|
|
174
|
+
BCP_DEPLOYMENT_ID
|
|
175
|
+
BCP_INSTANCE_ID
|
|
176
|
+
BCP_RELEASE
|
|
177
|
+
NODE_ENV
|
|
178
|
+
BCP_SHUTDOWN_TIMEOUT_MS
|
|
365
179
|
```
|
|
366
180
|
|
|
367
181
|
## Production build
|
|
@@ -378,3 +192,19 @@ npm run package
|
|
|
378
192
|
```
|
|
379
193
|
|
|
380
194
|
BCP excludes project `.env` files and application `devDependencies` from the deployment package. Supply secrets through the deployment environment.
|
|
195
|
+
|
|
196
|
+
BCP 0.2.18 prepared framework packages use compiled `.mjs` runtimes for the main server entrypoints, including config, auth, observability, deployment, server and middleware.
|
|
197
|
+
|
|
198
|
+
## Direct CLI usage
|
|
199
|
+
|
|
200
|
+
For PowerShell:
|
|
201
|
+
|
|
202
|
+
```powershell
|
|
203
|
+
npm exec -- bcp-framework --version
|
|
204
|
+
npm exec -- bcp-framework doctor
|
|
205
|
+
npm exec -- bcp-framework inspect
|
|
206
|
+
npm exec -- bcp-framework routes
|
|
207
|
+
npm exec -- bcp-framework dev
|
|
208
|
+
npm exec -- bcp-framework build
|
|
209
|
+
npm exec -- bcp-framework package
|
|
210
|
+
```
|