@chidchanun/bcp 0.2.12 → 0.2.14

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 CHANGED
@@ -1,15 +1,13 @@
1
1
  # BCP Framework
2
2
 
3
- BCP Framework is a React full-stack framework for file-based routing, SSR, SPA navigation, server data loading, API routes, authentication, authorization, SQL databases, background jobs, scheduling, workflow orchestration, transactional events, observability, uploads, storage and standalone Node.js deployment.
3
+ BCP Framework is a React full-stack framework for file-based routing, SSR, SPA navigation, server data loading, API routes, authentication, authorization, SQL databases, background jobs, scheduling, workflow orchestration, transactional events, realtime delivery, framework-native testing, observability, uploads, storage and standalone Node.js deployment.
4
4
 
5
- > **Development target:** `0.2.12Transactional Outbox & Events`
5
+ > **Development target:** `0.2.14Testing Platform`
6
6
  >
7
- > `0.2.12` remains unreleased until local validation, RC checks, tagging and npm publication complete.
7
+ > `0.2.14` remains unreleased until local validation, RC checks, tagging and npm publication complete.
8
8
 
9
9
  ## Current platform
10
10
 
11
- BCP `0.2.x` currently includes:
12
-
13
11
  | Area | Capability |
14
12
  | --- | --- |
15
13
  | Application | React SSR, hydration, layouts, metadata and SPA navigation |
@@ -26,6 +24,8 @@ BCP `0.2.x` currently includes:
26
24
  | Durable jobs | Redis-compatible queue/schedule adapters, visibility timeout, heartbeat, stale recovery and DLQ |
27
25
  | Workflows | Sequential/parallel steps, retries, persisted delays, compensation and run leases |
28
26
  | Events | Transactional outbox, SQL persistence, dispatcher leases, retries, event bus and durable queue handoff |
27
+ | Realtime | Channels/rooms, presence, broker delivery, WebSocket adapter contract, SSE and heartbeat |
28
+ | Testing | Request/route/auth/database/middleware/jobs/workflow/outbox/realtime/SSE test harnesses |
29
29
  | Observability | Structured logs, metrics, Prometheus output and health/readiness checks |
30
30
  | Uploads & storage | Multipart streaming, Local/S3-compatible storage and signed URLs |
31
31
  | Caching | Response cache and revalidation primitives |
@@ -65,86 +65,29 @@ Generated projects normally use one framework dependency:
65
65
  }
66
66
  ```
67
67
 
68
- ## Database Platform
68
+ ## Core backend entrypoints
69
69
 
70
70
  ```ts
71
- import {
72
- db,
73
- createDatabase,
74
- } from "bcp/database";
71
+ import { db } from "bcp/database";
72
+ import { createAuth } from "bcp/auth";
73
+ import { createJobQueue } from "bcp/jobs";
74
+ import { createWorkflow } from "bcp/workflow";
75
+ import { createTransactionalOutbox } from "bcp/events";
76
+ import { createRealtime } from "bcp/realtime";
77
+ import { createTestApp } from "bcp/testing";
78
+ import { createMetricsRegistry } from "bcp/observability";
75
79
  ```
76
80
 
77
- Migration CLI:
78
-
79
- ```bash
80
- bcp db create create_users
81
- bcp db migrate
82
- bcp db status
83
- bcp db rollback
84
- ```
85
-
86
- ## Authentication & authorization
87
-
88
- ```ts
89
- import {
90
- createAuth,
91
- createMemoryAuthSessionStore,
92
- requirePermission,
93
- } from "bcp/auth";
94
- ```
95
-
96
- Request security is available from `bcp/server`:
97
-
98
- ```ts
99
- import {
100
- createCsrfToken,
101
- requireCsrfRequest,
102
- requireSameOriginRequest,
103
- } from "bcp/server";
104
- ```
105
-
106
- ## Observability
107
-
108
- ```ts
109
- import {
110
- createHealthRegistry,
111
- createMetricsRegistry,
112
- createMetricsResponse,
113
- } from "bcp/observability";
114
- ```
115
-
116
- ## Background jobs
81
+ ## Durable jobs and scheduling
117
82
 
118
83
  ```ts
119
84
  import {
120
85
  createJobQueue,
86
+ createJobScheduler,
121
87
  } from "bcp/jobs";
122
88
 
123
89
  export const jobs =
124
90
  createJobQueue();
125
- ```
126
-
127
- Register typed work:
128
-
129
- ```ts
130
- jobs.register<{
131
- userId: number;
132
- }>(
133
- "email.welcome",
134
- async ({ payload }) => {
135
- await sendWelcomeEmail(
136
- payload.userId
137
- );
138
- }
139
- );
140
- ```
141
-
142
- ## Job scheduling
143
-
144
- ```ts
145
- import {
146
- createJobScheduler,
147
- } from "bcp/jobs";
148
91
 
149
92
  export const scheduler =
150
93
  createJobScheduler({
@@ -152,33 +95,7 @@ export const scheduler =
152
95
  });
153
96
  ```
154
97
 
155
- UTC cron example:
156
-
157
- ```ts
158
- await scheduler.schedule(
159
- "report.weekday",
160
- {},
161
- {
162
- cron: "30 9 * * 1-5",
163
- }
164
- );
165
- ```
166
-
167
- ## Durable Jobs Platform — 0.2.10
168
-
169
- Workers support visibility leases, heartbeat renewal and stale-running recovery:
170
-
171
- ```ts
172
- const worker =
173
- jobs.startWorker({
174
- workerId: "email-worker",
175
- concurrency: 4,
176
- visibilityTimeoutMs: 30_000,
177
- heartbeatIntervalMs: 10_000,
178
- });
179
- ```
180
-
181
- Redis-compatible adapters are available from `bcp/jobs`. BCP does not install or own the Redis client; applications provide a command client and manage credentials/TLS/Cluster/lifecycle themselves.
98
+ Production adapters can provide Redis-compatible durable queue/schedule storage without BCP owning the Redis connection.
182
99
 
183
100
  ## Workflow Orchestration — 0.2.11
184
101
 
@@ -220,45 +137,13 @@ export const onboarding =
220
137
  );
221
138
  ```
222
139
 
223
- Workflows support retries, compensation and optional execution through the durable job queue.
140
+ Workflows support retries, compensation, persisted delays and optional durable queue execution.
224
141
 
225
142
  ## Transactional Outbox & Events — 0.2.12
226
143
 
227
- `0.2.12` adds the server-only `bcp/events` entrypoint.
228
-
229
- The problem it solves:
230
-
231
- ```text
232
- DB commit succeeds
233
- +
234
- external publish fails
235
- =
236
- missing integration event
237
- ```
238
-
239
- The outbox writes the event in the **same SQL transaction** as the business data:
144
+ Use the same SQL transaction for business data and its outbox event:
240
145
 
241
146
  ```ts
242
- import {
243
- db,
244
- } from "bcp/database";
245
-
246
- import {
247
- createSqlOutboxStore,
248
- createTransactionalOutbox,
249
- } from "bcp/events";
250
-
251
- const outboxStore =
252
- createSqlOutboxStore({
253
- database: db,
254
- driver: "postgresql",
255
- });
256
-
257
- const outbox =
258
- createTransactionalOutbox({
259
- store: outboxStore,
260
- });
261
-
262
147
  await db.transaction(
263
148
  async tx => {
264
149
  await tx.execute(
@@ -270,107 +155,221 @@ await db.transaction(
270
155
  "order.created",
271
156
  {
272
157
  orderId: 42,
273
- },
274
- {
275
- aggregateId: "42",
276
- correlationId: requestId,
277
158
  }
278
159
  );
279
160
  }
280
161
  );
281
162
  ```
282
163
 
283
- Generate the outbox table migration for the configured provider:
164
+ After commit, an outbox dispatcher can deliver through `bcp/jobs`, a custom publisher or the local event bus.
165
+
166
+ Read more: [Transactional Outbox & Events](docs/transactional-outbox-events.md)
167
+
168
+ ## Realtime Platform — 0.2.13
284
169
 
285
170
  ```ts
286
171
  import {
287
- createOutboxMigrationSql,
288
- } from "bcp/events";
172
+ createRealtime,
173
+ } from "bcp/realtime";
289
174
 
290
- const sql =
291
- createOutboxMigrationSql(
292
- "postgresql"
293
- );
175
+ export const realtime =
176
+ createRealtime();
294
177
  ```
295
178
 
296
- Supported providers:
179
+ Join a channel and broadcast:
297
180
 
298
- ```text
299
- mysql
300
- postgresql
301
- sqlite
181
+ ```ts
182
+ const connection =
183
+ await realtime.connect();
184
+
185
+ await connection.join(
186
+ "orders:42"
187
+ );
188
+
189
+ await realtime.broadcast(
190
+ "orders:42",
191
+ "order.updated",
192
+ {
193
+ status: "paid",
194
+ }
195
+ );
302
196
  ```
303
197
 
304
- ### Dispatcher
198
+ BCP intentionally does not install a WebSocket server library. Applications adapt `ws`, uWebSockets.js or another transport to `RealtimeSocket`. SSE is built in through Web `Response`.
199
+
200
+ Read more: [Realtime Platform](docs/realtime-platform.md)
201
+
202
+ ## Testing Platform — 0.2.14
305
203
 
306
- After the SQL transaction commits, a dispatcher claims due events and delivers them:
204
+ `0.2.14` adds the server-only `bcp/testing` entrypoint. It is test-runner neutral and does not add Jest or Vitest as framework dependencies.
205
+
206
+ ### Request and route tests
307
207
 
308
208
  ```ts
309
209
  import {
310
- createOutboxDispatcher,
311
- } from "bcp/events";
210
+ createRouteTestHandler,
211
+ createTestApp,
212
+ expectResponse,
213
+ } from "bcp/testing";
214
+
215
+ const handler =
216
+ createRouteTestHandler({
217
+ GET() {
218
+ return {
219
+ ok: true,
220
+ };
221
+ },
222
+ });
312
223
 
313
- export const dispatcher =
314
- createOutboxDispatcher({
315
- store: outboxStore,
316
- queue: jobs,
317
- ownerId: "outbox-a",
318
- leaseMs: 30_000,
319
- batchSize: 100,
320
- pollIntervalMs: 1_000,
224
+ const app =
225
+ createTestApp({
226
+ handler,
321
227
  });
322
228
 
323
- const runner =
324
- dispatcher.start();
229
+ const response =
230
+ await app.get(
231
+ "/api/health"
232
+ );
233
+
234
+ await expectResponse(response)
235
+ .status(200)
236
+ .json({
237
+ ok: true,
238
+ });
325
239
  ```
326
240
 
327
- With `queue: jobs`, event type:
241
+ `createTestApp()` keeps an in-memory cookie jar, supports default headers and can send JSON bodies directly.
328
242
 
329
- ```text
330
- order.created
331
- ```
243
+ ### Authentication tests
332
244
 
333
- is handed off as:
245
+ Create a real signed BCP session token instead of a fake test-only user header:
334
246
 
335
- ```text
336
- event.order.created
247
+ ```ts
248
+ import {
249
+ createTestAuthSession,
250
+ } from "bcp/testing";
251
+
252
+ const session =
253
+ await createTestAuthSession(
254
+ {
255
+ id: 42,
256
+ role: "admin",
257
+ },
258
+ {
259
+ secret:
260
+ process.env.BCP_SESSION_SECRET,
261
+ store:
262
+ authSessionStore,
263
+ }
264
+ );
265
+
266
+ app.setCookie(
267
+ session.cookieName,
268
+ session.token
269
+ );
337
270
  ```
338
271
 
339
- Register its durable consumer:
272
+ When `store` is provided, the matching server-side auth session record is inserted as well.
273
+
274
+ ### Rollback database tests
340
275
 
341
276
  ```ts
342
- jobs.register(
343
- "event.order.created",
344
- async ({ payload }) => {
345
- await handleOrderCreated(
346
- payload
277
+ import {
278
+ withTestTransaction,
279
+ } from "bcp/testing";
280
+
281
+ await withTestTransaction(
282
+ db,
283
+ async tx => {
284
+ await tx.execute(
285
+ "INSERT INTO users ..."
347
286
  );
287
+
288
+ // assertions run here
348
289
  }
349
290
  );
350
291
  ```
351
292
 
352
- Dispatcher features include:
293
+ The callback uses the real BCP transaction and is deliberately rolled back after the test callback succeeds.
353
294
 
354
- - batched claims,
355
- - lease ownership,
356
- - stale lease recovery,
357
- - retry/backoff,
358
- - terminal failed state,
359
- - queue delivery,
360
- - custom publisher callbacks,
361
- - local `EventBus`,
362
- - retention cleanup,
363
- - outbox statistics.
295
+ ### Infrastructure harnesses
364
296
 
365
- `published` means the configured destination accepted the event. For queue delivery, it means enqueue succeeded; it does not mean the downstream job handler has completed.
297
+ ```ts
298
+ import {
299
+ createJobTestHarness,
300
+ createOutboxTestHarness,
301
+ createRealtimeTestHarness,
302
+ createWorkflowTestHarness,
303
+ } from "bcp/testing";
304
+ ```
366
305
 
367
- Delivery remains **at-least-once**, so consumers that perform non-idempotent side effects should use stable event IDs/idempotency controls.
306
+ These harnesses use the real platform contracts rather than separate mock implementations.
368
307
 
369
- Read more: [Transactional Outbox & Events](docs/transactional-outbox-events.md)
308
+ Background jobs:
370
309
 
371
- ## Public entrypoints
310
+ ```ts
311
+ const jobTest =
312
+ createJobTestHarness(
313
+ jobs
314
+ );
372
315
 
373
- The current documented platform surface is:
316
+ await jobTest.drain();
317
+
318
+ await jobTest.expectCount(
319
+ 1,
320
+ {
321
+ name: "email.welcome",
322
+ state: "succeeded",
323
+ }
324
+ );
325
+ ```
326
+
327
+ Realtime:
328
+
329
+ ```ts
330
+ const realtimeTest =
331
+ createRealtimeTestHarness(
332
+ realtime
333
+ );
334
+
335
+ const {
336
+ socket,
337
+ connection,
338
+ } =
339
+ await realtimeTest.connect();
340
+
341
+ await connection.join(
342
+ "orders:42"
343
+ );
344
+
345
+ await realtime.broadcast(
346
+ "orders:42",
347
+ "order.updated",
348
+ {
349
+ status: "paid",
350
+ }
351
+ );
352
+
353
+ realtimeTest.expectEvent(
354
+ socket,
355
+ "order.updated",
356
+ "orders:42"
357
+ );
358
+ ```
359
+
360
+ Other testing primitives include:
361
+
362
+ ```text
363
+ createFakeClock()
364
+ createSequenceIdFactory()
365
+ runTestMiddleware()
366
+ createRealtimeTestSocket()
367
+ readSseEvents()
368
+ ```
369
+
370
+ Read more: [Testing Platform](docs/testing-platform.md)
371
+
372
+ ## Public entrypoints
374
373
 
375
374
  ```text
376
375
  bcp
@@ -384,13 +383,15 @@ bcp/auth
384
383
  bcp/jobs
385
384
  bcp/workflow
386
385
  bcp/events
386
+ bcp/realtime
387
+ bcp/testing
387
388
  bcp/observability
388
389
  bcp/server
389
390
  bcp/server-only
390
391
  bcp/middleware
391
392
  ```
392
393
 
393
- Application code should use public entrypoints instead of importing private `packages/*` implementation files.
394
+ Application code should use public entrypoints instead of private `packages/*` implementation files.
394
395
 
395
396
  ## CLI
396
397
 
@@ -407,6 +408,15 @@ bcp inspect
407
408
  bcp version
408
409
  ```
409
410
 
411
+ Database migrations:
412
+
413
+ ```bash
414
+ bcp db create create_users
415
+ bcp db migrate
416
+ bcp db status
417
+ bcp db rollback
418
+ ```
419
+
410
420
  Generators:
411
421
 
412
422
  ```bash
@@ -419,32 +429,23 @@ bcp generate migration create_users
419
429
  ## Production model
420
430
 
421
431
  ```text
422
- Browser / API
423
- |
424
- security + auth + middleware
425
- |
426
- loaders / actions / API routes
427
- |
428
- Database transaction
429
- |\
430
- | +--> transactional outbox
431
- | |
432
- | dispatcher
433
- | |
434
- | durable jobs
435
- | |
436
- | workers
437
- |
438
- SSR / response
439
-
440
- Workflow + scheduler + logs + metrics + health
441
- operate alongside the request path.
432
+ Browser / API / Realtime clients
433
+ |
434
+ security + auth
435
+ |
436
+ application APIs
437
+ / | \
438
+ database workflows realtime
439
+ | | ^
440
+ outbox jobs |
441
+ \__________|_________/
442
+ durable state
443
+
444
+ Testing Platform exercises these server contracts without becoming part of browser runtime.
442
445
  ```
443
446
 
444
447
  ## Packaging
445
448
 
446
- Raw production build:
447
-
448
449
  ```bash
449
450
  npm run build
450
451
  npm start
@@ -458,8 +459,6 @@ bcp package
458
459
 
459
460
  ## Documentation Platform
460
461
 
461
- The framework repository is the documentation source of truth.
462
-
463
462
  Machine-readable contracts:
464
463
 
465
464
  ```text
@@ -470,7 +469,7 @@ docs/api-manifest.json
470
469
 
471
470
  ## Release validation
472
471
 
473
- Before publishing `0.2.12`:
472
+ Before publishing `0.2.14`:
474
473
 
475
474
  ```bash
476
475
  npm run typecheck
@@ -481,7 +480,7 @@ npm run test:package
481
480
  npm run rc:check
482
481
  ```
483
482
 
484
- `0.2.12` adds unit and prepared-package smoke coverage for transaction-bound outbox writes, dispatcher queue handoff, retry/failure behavior, stale lease recovery, SQL migration generation, public export/runtime compilation and browser boundary enforcement.
483
+ `0.2.14` adds unit and prepared-package smoke coverage for request/route handling, cookies, signed auth sessions, rollback transactions, middleware execution, jobs, workflows, outbox delivery, realtime sockets, SSE parsing, public runtime compilation and browser boundary enforcement.
485
484
 
486
485
  Do not tag or publish until the exact final release commit passes the full RC sequence.
487
486
 
@@ -508,12 +507,14 @@ Do not tag or publish until the exact final release commit passes the full RC se
508
507
  | `0.2.10` | Durable Jobs Platform |
509
508
  | `0.2.11` | Workflow Orchestration |
510
509
  | `0.2.12` | Transactional Outbox & Events |
510
+ | `0.2.13` | Realtime Platform |
511
+ | `0.2.14` | Testing Platform |
511
512
 
512
513
  ## Roadmap
513
514
 
514
- `0.2.12` closes the database-commit / external-publish gap and establishes a provider-neutral event-delivery boundary on top of Database + Durable Jobs.
515
+ `0.2.14` establishes framework-native testing for the current BCP application/runtime stack without coupling the framework to a specific test runner.
515
516
 
516
- The next logical milestone is **`0.2.13Realtime Platform`**, adding WebSocket/SSE channels, rooms/presence, authenticated realtime connections and live delivery for workflow/job/event progress without changing the current outbox/job contracts.
517
+ The next logical milestone is **`0.2.15Plugin & Module Platform`**, focused on reusable framework modules, lifecycle hooks, configuration extension, package metadata and plugin discovery/registration while keeping the current public entrypoints stable.
517
518
 
518
519
  Native desktop/mobile compilation remains later roadmap work.
519
520