@chidchanun/bcp 0.2.16 → 0.2.17

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
@@ -2,9 +2,9 @@
2
2
 
3
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, plugin/module composition, distributed caching, observability, uploads, storage and standalone Node.js deployment.
4
4
 
5
- > **Development target:** `0.2.16Cache Platform v2`
5
+ > **Development target:** `0.2.17Observability Platform v3`
6
6
  >
7
- > `0.2.16` remains unreleased until local validation, RC checks, tagging and npm publication complete.
7
+ > `0.2.17` remains unreleased until local validation, RC checks, tagging and npm publication complete.
8
8
 
9
9
  ## Current platform
10
10
 
@@ -28,7 +28,7 @@ BCP Framework is a React full-stack framework for file-based routing, SSR, SPA n
28
28
  | Testing | Request/route/page/auth/database/middleware/jobs/workflow/outbox/realtime/SSE test harnesses |
29
29
  | Plugins & modules | Dependency ordering, lifecycle hooks, config parsing, service registry and async extension hooks |
30
30
  | Caching | Legacy request/data cache plus Cache Platform v2 adapters, Redis-compatible cache/locks, stampede protection, TTL/tag/path invalidation and metrics |
31
- | Observability | Structured logs, metrics, Prometheus output and health/readiness checks |
31
+ | Observability | Metrics, Prometheus, health/readiness, distributed tracing, W3C trace context and correlation IDs |
32
32
  | Uploads & storage | Multipart streaming, Local/S3-compatible storage and signed URLs |
33
33
  | Configuration | Typed config/environment validation and diagnostics |
34
34
  | Production | Standalone Node.js build, packaging, dependency pruning, Docker starter and graceful shutdown |
@@ -78,7 +78,10 @@ import { createTransactionalOutbox } from "bcp/events";
78
78
  import { createRealtime } from "bcp/realtime";
79
79
  import { createTestApp } from "bcp/testing";
80
80
  import { createPluginHost } from "bcp/plugins";
81
- import { createMetricsRegistry } from "bcp/observability";
81
+ import {
82
+ createMetricsRegistry,
83
+ createTracer,
84
+ } from "bcp/observability";
82
85
  ```
83
86
 
84
87
  ## Durable jobs and scheduling
@@ -117,7 +120,6 @@ export const onboarding =
117
120
  "profile",
118
121
  createProfile
119
122
  );
120
-
121
123
  workflow.parallel(
122
124
  "initialize",
123
125
  parallel => {
@@ -131,7 +133,6 @@ export const onboarding =
131
133
  );
132
134
  }
133
135
  );
134
-
135
136
  workflow.delay(
136
137
  "cooldown",
137
138
  1_000
@@ -179,34 +180,13 @@ export const realtime =
179
180
  createRealtime();
180
181
  ```
181
182
 
182
- Join a channel and broadcast:
183
-
184
- ```ts
185
- const connection =
186
- await realtime.connect();
187
-
188
- await connection.join(
189
- "orders:42"
190
- );
191
-
192
- await realtime.broadcast(
193
- "orders:42",
194
- "order.updated",
195
- {
196
- status: "paid",
197
- }
198
- );
199
- ```
200
-
201
183
  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`.
202
184
 
203
185
  Read more: [Realtime Platform](docs/realtime-platform.md)
204
186
 
205
187
  ## Testing Platform — 0.2.14
206
188
 
207
- `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.
208
-
209
- ### Request and route tests
189
+ `bcp/testing` provides runner-neutral request/route/page/auth/database/middleware/jobs/workflow/outbox/realtime/SSE test helpers.
210
190
 
211
191
  ```ts
212
192
  import {
@@ -215,169 +195,31 @@ import {
215
195
  expectResponse,
216
196
  } from "bcp/testing";
217
197
 
218
- const handler =
219
- createRouteTestHandler({
220
- GET() {
221
- return {
222
- ok: true,
223
- };
224
- },
225
- });
226
-
227
198
  const app =
228
199
  createTestApp({
229
- handler,
200
+ handler:
201
+ createRouteTestHandler({
202
+ GET() {
203
+ return {
204
+ ok: true,
205
+ };
206
+ },
207
+ }),
230
208
  });
231
209
 
232
- const response =
233
- await app.get(
234
- "/api/health"
235
- );
236
-
237
- await expectResponse(response)
210
+ await expectResponse(
211
+ await app.get("/api/health")
212
+ )
238
213
  .status(200)
239
214
  .json({
240
215
  ok: true,
241
216
  });
242
217
  ```
243
218
 
244
- `createTestApp()` keeps an in-memory cookie jar, supports default headers and can send JSON bodies directly.
245
-
246
- ### Authentication tests
247
-
248
- Create a real signed BCP session token instead of a fake test-only user header:
249
-
250
- ```ts
251
- import {
252
- createTestAuthSession,
253
- } from "bcp/testing";
254
-
255
- const session =
256
- await createTestAuthSession(
257
- {
258
- id: 42,
259
- role: "admin",
260
- },
261
- {
262
- secret:
263
- process.env.BCP_SESSION_SECRET,
264
- store:
265
- authSessionStore,
266
- }
267
- );
268
-
269
- app.setCookie(
270
- session.cookieName,
271
- session.token
272
- );
273
- ```
274
-
275
- When `store` is provided, the matching server-side auth session record is inserted as well.
276
-
277
- ### Rollback database tests
278
-
279
- ```ts
280
- import {
281
- withTestTransaction,
282
- } from "bcp/testing";
283
-
284
- await withTestTransaction(
285
- db,
286
- async tx => {
287
- await tx.execute(
288
- "INSERT INTO users ..."
289
- );
290
-
291
- // assertions run here
292
- }
293
- );
294
- ```
295
-
296
- The callback uses the real BCP transaction and is deliberately rolled back after the test callback succeeds.
297
-
298
- ### Infrastructure harnesses
299
-
300
- ```ts
301
- import {
302
- createJobTestHarness,
303
- createOutboxTestHarness,
304
- createRealtimeTestHarness,
305
- createWorkflowTestHarness,
306
- } from "bcp/testing";
307
- ```
308
-
309
- These harnesses use the real platform contracts rather than separate mock implementations.
310
-
311
- Background jobs:
312
-
313
- ```ts
314
- const jobTest =
315
- createJobTestHarness(
316
- jobs
317
- );
318
-
319
- await jobTest.drain();
320
-
321
- await jobTest.expectCount(
322
- 1,
323
- {
324
- name: "email.welcome",
325
- state: "succeeded",
326
- }
327
- );
328
- ```
329
-
330
- Realtime:
331
-
332
- ```ts
333
- const realtimeTest =
334
- createRealtimeTestHarness(
335
- realtime
336
- );
337
-
338
- const {
339
- socket,
340
- connection,
341
- } =
342
- await realtimeTest.connect();
343
-
344
- await connection.join(
345
- "orders:42"
346
- );
347
-
348
- await realtime.broadcast(
349
- "orders:42",
350
- "order.updated",
351
- {
352
- status: "paid",
353
- }
354
- );
355
-
356
- realtimeTest.expectEvent(
357
- socket,
358
- "order.updated",
359
- "orders:42"
360
- );
361
- ```
362
-
363
- Other testing primitives include:
364
-
365
- ```text
366
- createFakeClock()
367
- createSequenceIdFactory()
368
- runTestMiddleware()
369
- createRealtimeTestSocket()
370
- readSseEvents()
371
- ```
372
-
373
219
  Read more: [Testing Platform](docs/testing-platform.md)
374
220
 
375
221
  ## Plugin & Module Platform — 0.2.15
376
222
 
377
- `0.2.15` adds the server-only `bcp/plugins` entrypoint for reusable application/framework extensions.
378
-
379
- Define plugins with explicit dependencies:
380
-
381
223
  ```ts
382
224
  import {
383
225
  createPluginHost,
@@ -393,12 +235,6 @@ const databasePlugin =
393
235
  db
394
236
  );
395
237
  },
396
- start() {
397
- return db.connect();
398
- },
399
- stop() {
400
- return db.disconnect();
401
- },
402
238
  });
403
239
 
404
240
  const jobsPlugin =
@@ -409,137 +245,178 @@ const jobsPlugin =
409
245
  ],
410
246
  });
411
247
 
412
- const host =
248
+ export const plugins =
413
249
  createPluginHost({
414
250
  plugins: [
415
251
  jobsPlugin,
416
252
  databasePlugin,
417
253
  ],
418
254
  });
419
-
420
- await host.start();
421
255
  ```
422
256
 
423
- Dependency order is resolved automatically. Startup follows dependency order while stop/dispose runs in reverse order.
257
+ Startup follows dependency order; stop/dispose runs in reverse order. Modules, config parsing, shared services and async hook buses are supported.
258
+
259
+ Read more: [Plugin & Module Platform](docs/plugin-module-platform.md)
260
+
261
+ ## Cache Platform v2 — 0.2.16
424
262
 
425
- Modules group reusable plugin sets:
263
+ `0.2.16` keeps the original `cache()` / `dedupe()` APIs and adds provider-neutral asynchronous cache stores.
426
264
 
427
265
  ```ts
428
266
  import {
429
- defineModule,
430
- } from "bcp/plugins";
267
+ createCacheStore,
268
+ createRedisCacheAdapter,
269
+ createRedisCacheLockAdapter,
270
+ } from "bcp/cache";
431
271
 
432
- const backendModule =
433
- defineModule({
434
- name: "backend",
435
- plugins: [
436
- databasePlugin,
437
- jobsPlugin,
438
- ],
272
+ export const cache =
273
+ createCacheStore({
274
+ adapter:
275
+ createRedisCacheAdapter({
276
+ client: redisClient,
277
+ }),
278
+ lock:
279
+ createRedisCacheLockAdapter({
280
+ client: redisClient,
281
+ }),
439
282
  });
440
283
  ```
441
284
 
442
- Plugin configuration can be parsed at setup time and overridden through `createPluginHost({ configs })`.
443
-
444
- Plugins share a service registry and awaited in-process hook bus through `context.services` and `context.hooks`.
285
+ Distributed `getOrSet()` supports local singleflight, owner-scoped lock leases, heartbeat renewal, double-check-after-lock and contention wait/poll.
445
286
 
446
- If startup fails, already-started plugins are stopped in reverse order before the lifecycle error is propagated.
447
-
448
- Read more: [Plugin & Module Platform](docs/plugin-module-platform.md)
287
+ Read more: [Cache Platform v2](docs/cache-platform-v2.md)
449
288
 
450
- ## Cache Platform v2 — 0.2.16
289
+ ## Observability Platform v3 — 0.2.17
451
290
 
452
- `0.2.16` keeps the original `cache()` and `dedupe()` APIs while adding provider-neutral asynchronous cache stores for production multi-instance applications.
291
+ `0.2.17` keeps the metrics/Prometheus/health APIs from Observability v2 and adds provider-neutral tracing and correlation.
453
292
 
454
- Create a cache store:
293
+ Create a tracer:
455
294
 
456
295
  ```ts
457
296
  import {
458
- createCacheStore,
459
- } from "bcp/cache";
297
+ createMemoryTraceSpanExporter,
298
+ createTracer,
299
+ } from "bcp/observability";
300
+
301
+ const traces =
302
+ createMemoryTraceSpanExporter();
460
303
 
461
- export const applicationCache =
462
- createCacheStore();
304
+ export const tracer =
305
+ createTracer({
306
+ exporter: traces,
307
+ serviceName: "api",
308
+ });
463
309
  ```
464
310
 
465
- Cache-aside loading:
311
+ Create root/child spans:
466
312
 
467
313
  ```ts
468
- const user =
469
- await applicationCache.getOrSet(
470
- "user:42",
471
- async () =>
472
- loadUser(42),
473
- {
474
- ttlMs: 60_000,
475
- tags: [
476
- "users",
477
- ],
478
- paths: [
479
- "/users/42",
480
- ],
481
- }
482
- );
314
+ await tracer.withSpan(
315
+ "order.checkout",
316
+ async () => {
317
+ await tracer.withSpan(
318
+ "database.order.insert",
319
+ createOrder,
320
+ {
321
+ kind: "client",
322
+ }
323
+ );
324
+ }
325
+ );
483
326
  ```
484
327
 
485
- Within one store, concurrent misses share one loader automatically.
328
+ Tracing context flows through normal awaited async work via Node `AsyncLocalStorage`.
486
329
 
487
- For multiple application instances, use shared cache and lock adapters:
330
+ ### HTTP tracing
488
331
 
489
332
  ```ts
490
333
  import {
491
- createRedisCacheAdapter,
492
- createRedisCacheLockAdapter,
493
- } from "bcp/cache";
334
+ createRequestTracingMiddleware,
335
+ } from "bcp/observability";
494
336
 
495
- const redisCache =
496
- createRedisCacheAdapter({
497
- client: redisClient,
498
- });
499
-
500
- const redisLock =
501
- createRedisCacheLockAdapter({
502
- client: redisClient,
503
- });
504
-
505
- export const cache =
506
- createCacheStore({
507
- adapter: redisCache,
508
- lock: redisLock,
509
- });
337
+ export const middleware =
338
+ createRequestTracingMiddleware(
339
+ tracer
340
+ );
510
341
  ```
511
342
 
512
- The default Redis namespace is `bcp:{cache}`. BCP does not install or own a Redis library/connection.
343
+ The middleware continues valid W3C `traceparent` headers, preserves `x-correlation-id`, creates a server span and returns current trace headers on the response.
513
344
 
514
- Distributed `getOrSet()` uses owner-scoped lock leases, heartbeat renewal when supported, double-check-after-lock, contention wait/poll and configurable lock-timeout behavior.
515
-
516
- Tag/path invalidation remains available through the new async store:
345
+ ### Jobs / workflow / events / realtime propagation
517
346
 
518
347
  ```ts
519
- await cache.revalidateTag(
520
- "users"
348
+ import {
349
+ createTraceCarrier,
350
+ runWithTraceCarrier,
351
+ } from "bcp/observability";
352
+
353
+ const trace =
354
+ createTraceCarrier();
355
+
356
+ await jobs.enqueue(
357
+ "order.process",
358
+ {
359
+ orderId,
360
+ trace,
361
+ }
521
362
  );
363
+ ```
522
364
 
523
- await cache.revalidatePath(
524
- "/dashboard"
365
+ Consumer:
366
+
367
+ ```ts
368
+ await runWithTraceCarrier(
369
+ payload.trace,
370
+ () =>
371
+ tracer.withSpan(
372
+ "job order.process",
373
+ handler,
374
+ {
375
+ kind: "consumer",
376
+ }
377
+ )
525
378
  );
526
379
  ```
527
380
 
528
- Connect cache events to BCP metrics:
381
+ The same carrier can be placed in workflow input, outbox/event metadata and realtime payloads when trace continuity is needed across those boundaries.
382
+
383
+ ### Trace-to-metrics and logs
529
384
 
530
385
  ```ts
531
- const cache =
532
- createCacheStore({
533
- metrics:
534
- createCacheMetrics(
535
- metricsRegistry
536
- ),
537
- });
386
+ import {
387
+ createCompositeTraceSpanExporter,
388
+ createMetricsRegistry,
389
+ createTraceMetricsExporter,
390
+ getTraceLogFields,
391
+ } from "bcp/observability";
392
+
393
+ const metrics =
394
+ createMetricsRegistry();
395
+
396
+ const exporter =
397
+ createCompositeTraceSpanExporter([
398
+ createTraceMetricsExporter(
399
+ metrics
400
+ ),
401
+ productionTraceExporter,
402
+ ]);
403
+
404
+ logger.info(
405
+ "order created",
406
+ {
407
+ ...getTraceLogFields(),
408
+ orderId,
409
+ }
410
+ );
538
411
  ```
539
412
 
540
- Prepared npm packages compile `bcp/cache` to `cache.mjs` for standalone Node runtime use.
413
+ Default trace metrics use low-cardinality `kind` and `status` labels. Span-name labels are opt-in.
541
414
 
542
- Read more: [Cache Platform v2](docs/cache-platform-v2.md)
415
+ BCP does not install an OpenTelemetry SDK or vendor APM package; production exporters remain application-owned through `TraceSpanExporter`.
416
+
417
+ Prepared npm packages compile `bcp/observability` to `observability.mjs` for standalone Node runtime use.
418
+
419
+ Read more: [Observability Platform v3](docs/observability-v3.md)
543
420
 
544
421
  ## Public entrypoints
545
422
 
@@ -604,7 +481,7 @@ bcp generate migration create_users
604
481
  ```text
605
482
  Browser / API / Realtime clients
606
483
  |
607
- security + auth
484
+ tracing + security
608
485
  |
609
486
  Plugin Host
610
487
  / | \
@@ -615,10 +492,11 @@ Browser / API / Realtime clients
615
492
  durable state
616
493
  |
617
494
  shared cache layer
618
- Redis / other
495
+ |
496
+ metrics + trace export
619
497
  ```
620
498
 
621
- Cache is an optimization layer and must not replace durable application truth or transactional invariants.
499
+ Cache remains an optimization layer. Database/outbox/jobs/workflows remain the durable truth. Realtime remains transient delivery. Tracing correlates those operations without replacing their state contracts.
622
500
 
623
501
  ## Packaging
624
502
 
@@ -645,7 +523,7 @@ docs/api-manifest.json
645
523
 
646
524
  ## Release validation
647
525
 
648
- Before publishing `0.2.16`:
526
+ Before publishing `0.2.17`:
649
527
 
650
528
  ```bash
651
529
  npm run typecheck
@@ -656,7 +534,7 @@ npm run test:package
656
534
  npm run rc:check
657
535
  ```
658
536
 
659
- `0.2.16` adds unit and prepared-package smoke coverage for TTL/tag/path invalidation, local singleflight, distributed lock contention, lock timeout behavior, Redis cache/lock command contracts, cache metrics and compiled `cache.mjs` execution.
537
+ `0.2.17` adds unit and prepared-package smoke coverage for root/child spans, async context propagation, W3C trace headers, correlation carriers, request tracing, error spans, trace metrics and compiled `observability.mjs` execution.
660
538
 
661
539
  Do not tag or publish until the exact final release commit passes the full RC sequence.
662
540
 
@@ -687,12 +565,15 @@ Do not tag or publish until the exact final release commit passes the full RC se
687
565
  | `0.2.14` | Testing Platform |
688
566
  | `0.2.15` | Plugin & Module Platform |
689
567
  | `0.2.16` | Cache Platform v2 |
568
+ | `0.2.17` | Observability Platform v3 |
690
569
 
691
570
  ## Roadmap
692
571
 
693
- `0.2.16` establishes provider-neutral shared caching and distributed cache-fill coordination while preserving the original process-local cache API.
572
+ `0.2.17` establishes trace/correlation continuity across the current server platform without coupling BCP to a specific telemetry vendor.
573
+
574
+ The next logical milestone is **`0.2.18 — Deployment Platform v2`**, focused on completing compiled production entrypoints, deployment/runtime adapters, process lifecycle integration, container/runtime metadata and production diagnostics.
694
575
 
695
- The next logical milestone is **`0.2.17 Observability Platform v3`**, focused on tracing, correlation across HTTP/jobs/workflows/events/realtime/cache, richer runtime metrics and exporter/provider integration.
576
+ `0.2.19` is planned as the stabilization/API-freeze pass before the next `0.3.0` platform baseline.
696
577
 
697
578
  Native desktop/mobile compilation remains later roadmap work.
698
579