@chidchanun/bcp 0.2.15 → 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
@@ -1,10 +1,10 @@
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, realtime delivery, framework-native testing, plugin/module composition, 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, plugin/module composition, distributed caching, observability, uploads, storage and standalone Node.js deployment.
4
4
 
5
- > **Development target:** `0.2.15Plugin & Module Platform`
5
+ > **Development target:** `0.2.17Observability Platform v3`
6
6
  >
7
- > `0.2.15` 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
 
@@ -27,9 +27,9 @@ BCP Framework is a React full-stack framework for file-based routing, SSR, SPA n
27
27
  | Realtime | Channels/rooms, presence, broker delivery, WebSocket adapter contract, SSE and heartbeat |
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
- | Observability | Structured logs, metrics, Prometheus output and health/readiness checks |
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 | Metrics, Prometheus, health/readiness, distributed tracing, W3C trace context and correlation IDs |
31
32
  | Uploads & storage | Multipart streaming, Local/S3-compatible storage and signed URLs |
32
- | Caching | Response cache and revalidation primitives |
33
33
  | Configuration | Typed config/environment validation and diagnostics |
34
34
  | Production | Standalone Node.js build, packaging, dependency pruning, Docker starter and graceful shutdown |
35
35
  | Documentation | Manifest-driven docs, platform metadata and API reference |
@@ -69,6 +69,7 @@ Generated projects normally use one framework dependency:
69
69
  ## Core backend entrypoints
70
70
 
71
71
  ```ts
72
+ import { createCacheStore } from "bcp/cache";
72
73
  import { db } from "bcp/database";
73
74
  import { createAuth } from "bcp/auth";
74
75
  import { createJobQueue } from "bcp/jobs";
@@ -77,7 +78,10 @@ import { createTransactionalOutbox } from "bcp/events";
77
78
  import { createRealtime } from "bcp/realtime";
78
79
  import { createTestApp } from "bcp/testing";
79
80
  import { createPluginHost } from "bcp/plugins";
80
- import { createMetricsRegistry } from "bcp/observability";
81
+ import {
82
+ createMetricsRegistry,
83
+ createTracer,
84
+ } from "bcp/observability";
81
85
  ```
82
86
 
83
87
  ## Durable jobs and scheduling
@@ -116,7 +120,6 @@ export const onboarding =
116
120
  "profile",
117
121
  createProfile
118
122
  );
119
-
120
123
  workflow.parallel(
121
124
  "initialize",
122
125
  parallel => {
@@ -130,7 +133,6 @@ export const onboarding =
130
133
  );
131
134
  }
132
135
  );
133
-
134
136
  workflow.delay(
135
137
  "cooldown",
136
138
  1_000
@@ -178,34 +180,13 @@ export const realtime =
178
180
  createRealtime();
179
181
  ```
180
182
 
181
- Join a channel and broadcast:
182
-
183
- ```ts
184
- const connection =
185
- await realtime.connect();
186
-
187
- await connection.join(
188
- "orders:42"
189
- );
190
-
191
- await realtime.broadcast(
192
- "orders:42",
193
- "order.updated",
194
- {
195
- status: "paid",
196
- }
197
- );
198
- ```
199
-
200
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`.
201
184
 
202
185
  Read more: [Realtime Platform](docs/realtime-platform.md)
203
186
 
204
187
  ## Testing Platform — 0.2.14
205
188
 
206
- `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.
207
-
208
- ### Request and route tests
189
+ `bcp/testing` provides runner-neutral request/route/page/auth/database/middleware/jobs/workflow/outbox/realtime/SSE test helpers.
209
190
 
210
191
  ```ts
211
192
  import {
@@ -214,237 +195,228 @@ import {
214
195
  expectResponse,
215
196
  } from "bcp/testing";
216
197
 
217
- const handler =
218
- createRouteTestHandler({
219
- GET() {
220
- return {
221
- ok: true,
222
- };
223
- },
224
- });
225
-
226
198
  const app =
227
199
  createTestApp({
228
- handler,
200
+ handler:
201
+ createRouteTestHandler({
202
+ GET() {
203
+ return {
204
+ ok: true,
205
+ };
206
+ },
207
+ }),
229
208
  });
230
209
 
231
- const response =
232
- await app.get(
233
- "/api/health"
234
- );
235
-
236
- await expectResponse(response)
210
+ await expectResponse(
211
+ await app.get("/api/health")
212
+ )
237
213
  .status(200)
238
214
  .json({
239
215
  ok: true,
240
216
  });
241
217
  ```
242
218
 
243
- `createTestApp()` keeps an in-memory cookie jar, supports default headers and can send JSON bodies directly.
244
-
245
- ### Authentication tests
219
+ Read more: [Testing Platform](docs/testing-platform.md)
246
220
 
247
- Create a real signed BCP session token instead of a fake test-only user header:
221
+ ## Plugin & Module Platform 0.2.15
248
222
 
249
223
  ```ts
250
224
  import {
251
- createTestAuthSession,
252
- } from "bcp/testing";
225
+ createPluginHost,
226
+ definePlugin,
227
+ } from "bcp/plugins";
253
228
 
254
- const session =
255
- await createTestAuthSession(
256
- {
257
- id: 42,
258
- role: "admin",
229
+ const databasePlugin =
230
+ definePlugin({
231
+ name: "database",
232
+ setup(context) {
233
+ context.services.provide(
234
+ "database",
235
+ db
236
+ );
259
237
  },
260
- {
261
- secret:
262
- process.env.BCP_SESSION_SECRET,
263
- store:
264
- authSessionStore,
265
- }
266
- );
238
+ });
267
239
 
268
- app.setCookie(
269
- session.cookieName,
270
- session.token
271
- );
240
+ const jobsPlugin =
241
+ definePlugin({
242
+ name: "jobs",
243
+ requires: [
244
+ "database",
245
+ ],
246
+ });
247
+
248
+ export const plugins =
249
+ createPluginHost({
250
+ plugins: [
251
+ jobsPlugin,
252
+ databasePlugin,
253
+ ],
254
+ });
272
255
  ```
273
256
 
274
- When `store` is provided, the matching server-side auth session record is inserted as well.
257
+ Startup follows dependency order; stop/dispose runs in reverse order. Modules, config parsing, shared services and async hook buses are supported.
275
258
 
276
- ### Rollback database tests
259
+ Read more: [Plugin & Module Platform](docs/plugin-module-platform.md)
277
260
 
278
- ```ts
279
- import {
280
- withTestTransaction,
281
- } from "bcp/testing";
261
+ ## Cache Platform v2 — 0.2.16
282
262
 
283
- await withTestTransaction(
284
- db,
285
- async tx => {
286
- await tx.execute(
287
- "INSERT INTO users ..."
288
- );
263
+ `0.2.16` keeps the original `cache()` / `dedupe()` APIs and adds provider-neutral asynchronous cache stores.
289
264
 
290
- // assertions run here
291
- }
292
- );
265
+ ```ts
266
+ import {
267
+ createCacheStore,
268
+ createRedisCacheAdapter,
269
+ createRedisCacheLockAdapter,
270
+ } from "bcp/cache";
271
+
272
+ export const cache =
273
+ createCacheStore({
274
+ adapter:
275
+ createRedisCacheAdapter({
276
+ client: redisClient,
277
+ }),
278
+ lock:
279
+ createRedisCacheLockAdapter({
280
+ client: redisClient,
281
+ }),
282
+ });
293
283
  ```
294
284
 
295
- The callback uses the real BCP transaction and is deliberately rolled back after the test callback succeeds.
285
+ Distributed `getOrSet()` supports local singleflight, owner-scoped lock leases, heartbeat renewal, double-check-after-lock and contention wait/poll.
296
286
 
297
- ### Infrastructure harnesses
287
+ Read more: [Cache Platform v2](docs/cache-platform-v2.md)
298
288
 
299
- ```ts
300
- import {
301
- createJobTestHarness,
302
- createOutboxTestHarness,
303
- createRealtimeTestHarness,
304
- createWorkflowTestHarness,
305
- } from "bcp/testing";
306
- ```
289
+ ## Observability Platform v3 — 0.2.17
307
290
 
308
- These harnesses use the real platform contracts rather than separate mock implementations.
291
+ `0.2.17` keeps the metrics/Prometheus/health APIs from Observability v2 and adds provider-neutral tracing and correlation.
309
292
 
310
- Background jobs:
293
+ Create a tracer:
311
294
 
312
295
  ```ts
313
- const jobTest =
314
- createJobTestHarness(
315
- jobs
316
- );
296
+ import {
297
+ createMemoryTraceSpanExporter,
298
+ createTracer,
299
+ } from "bcp/observability";
317
300
 
318
- await jobTest.drain();
301
+ const traces =
302
+ createMemoryTraceSpanExporter();
319
303
 
320
- await jobTest.expectCount(
321
- 1,
322
- {
323
- name: "email.welcome",
324
- state: "succeeded",
325
- }
326
- );
304
+ export const tracer =
305
+ createTracer({
306
+ exporter: traces,
307
+ serviceName: "api",
308
+ });
327
309
  ```
328
310
 
329
- Realtime:
311
+ Create root/child spans:
330
312
 
331
313
  ```ts
332
- const realtimeTest =
333
- createRealtimeTestHarness(
334
- realtime
335
- );
336
-
337
- const {
338
- socket,
339
- connection,
340
- } =
341
- await realtimeTest.connect();
342
-
343
- await connection.join(
344
- "orders:42"
345
- );
346
-
347
- await realtime.broadcast(
348
- "orders:42",
349
- "order.updated",
350
- {
351
- status: "paid",
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
+ );
352
324
  }
353
325
  );
354
-
355
- realtimeTest.expectEvent(
356
- socket,
357
- "order.updated",
358
- "orders:42"
359
- );
360
326
  ```
361
327
 
362
- Other testing primitives include:
328
+ Tracing context flows through normal awaited async work via Node `AsyncLocalStorage`.
363
329
 
364
- ```text
365
- createFakeClock()
366
- createSequenceIdFactory()
367
- runTestMiddleware()
368
- createRealtimeTestSocket()
369
- readSseEvents()
370
- ```
330
+ ### HTTP tracing
371
331
 
372
- Read more: [Testing Platform](docs/testing-platform.md)
332
+ ```ts
333
+ import {
334
+ createRequestTracingMiddleware,
335
+ } from "bcp/observability";
373
336
 
374
- ## Plugin & Module Platform — 0.2.15
337
+ export const middleware =
338
+ createRequestTracingMiddleware(
339
+ tracer
340
+ );
341
+ ```
375
342
 
376
- `0.2.15` adds the server-only `bcp/plugins` entrypoint for reusable application/framework extensions.
343
+ The middleware continues valid W3C `traceparent` headers, preserves `x-correlation-id`, creates a server span and returns current trace headers on the response.
377
344
 
378
- Define plugins with explicit dependencies:
345
+ ### Jobs / workflow / events / realtime propagation
379
346
 
380
347
  ```ts
381
348
  import {
382
- createPluginHost,
383
- definePlugin,
384
- } from "bcp/plugins";
349
+ createTraceCarrier,
350
+ runWithTraceCarrier,
351
+ } from "bcp/observability";
385
352
 
386
- const databasePlugin =
387
- definePlugin({
388
- name: "database",
389
- setup(context) {
390
- context.services.provide(
391
- "database",
392
- db
393
- );
394
- },
395
- start() {
396
- return db.connect();
397
- },
398
- stop() {
399
- return db.disconnect();
400
- },
401
- });
353
+ const trace =
354
+ createTraceCarrier();
402
355
 
403
- const jobsPlugin =
404
- definePlugin({
405
- name: "jobs",
406
- requires: [
407
- "database",
408
- ],
409
- });
356
+ await jobs.enqueue(
357
+ "order.process",
358
+ {
359
+ orderId,
360
+ trace,
361
+ }
362
+ );
363
+ ```
410
364
 
411
- const host =
412
- createPluginHost({
413
- plugins: [
414
- jobsPlugin,
415
- databasePlugin,
416
- ],
417
- });
365
+ Consumer:
418
366
 
419
- await host.start();
367
+ ```ts
368
+ await runWithTraceCarrier(
369
+ payload.trace,
370
+ () =>
371
+ tracer.withSpan(
372
+ "job order.process",
373
+ handler,
374
+ {
375
+ kind: "consumer",
376
+ }
377
+ )
378
+ );
420
379
  ```
421
380
 
422
- Dependency order is resolved automatically. Startup follows dependency order while stop/dispose runs in reverse order.
381
+ The same carrier can be placed in workflow input, outbox/event metadata and realtime payloads when trace continuity is needed across those boundaries.
423
382
 
424
- Modules group reusable plugin sets:
383
+ ### Trace-to-metrics and logs
425
384
 
426
385
  ```ts
427
386
  import {
428
- defineModule,
429
- } from "bcp/plugins";
430
-
431
- const backendModule =
432
- defineModule({
433
- name: "backend",
434
- plugins: [
435
- databasePlugin,
436
- jobsPlugin,
437
- ],
438
- });
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
+ );
439
411
  ```
440
412
 
441
- Plugin configuration can be parsed at setup time and overridden through `createPluginHost({ configs })`.
413
+ Default trace metrics use low-cardinality `kind` and `status` labels. Span-name labels are opt-in.
442
414
 
443
- Plugins share a service registry and awaited in-process hook bus through `context.services` and `context.hooks`.
415
+ BCP does not install an OpenTelemetry SDK or vendor APM package; production exporters remain application-owned through `TraceSpanExporter`.
444
416
 
445
- If startup fails, already-started plugins are stopped in reverse order before the lifecycle error is propagated.
417
+ Prepared npm packages compile `bcp/observability` to `observability.mjs` for standalone Node runtime use.
446
418
 
447
- Read more: [Plugin & Module Platform](docs/plugin-module-platform.md)
419
+ Read more: [Observability Platform v3](docs/observability-v3.md)
448
420
 
449
421
  ## Public entrypoints
450
422
 
@@ -509,7 +481,7 @@ bcp generate migration create_users
509
481
  ```text
510
482
  Browser / API / Realtime clients
511
483
  |
512
- security + auth
484
+ tracing + security
513
485
  |
514
486
  Plugin Host
515
487
  / | \
@@ -518,10 +490,14 @@ Browser / API / Realtime clients
518
490
  outbox jobs |
519
491
  \__________|_________/
520
492
  durable state
521
-
522
- Testing Platform exercises these server contracts without becoming part of browser runtime.
493
+ |
494
+ shared cache layer
495
+ |
496
+ metrics + trace export
523
497
  ```
524
498
 
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.
500
+
525
501
  ## Packaging
526
502
 
527
503
  ```bash
@@ -547,7 +523,7 @@ docs/api-manifest.json
547
523
 
548
524
  ## Release validation
549
525
 
550
- Before publishing `0.2.15`:
526
+ Before publishing `0.2.17`:
551
527
 
552
528
  ```bash
553
529
  npm run typecheck
@@ -558,7 +534,7 @@ npm run test:package
558
534
  npm run rc:check
559
535
  ```
560
536
 
561
- `0.2.15` adds unit and prepared-package smoke coverage for plugin dependency ordering, missing/cyclic dependencies, modules, config parsing, shared services, async hooks, lifecycle rollback, compiled `plugins.mjs` execution and browser boundary enforcement.
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.
562
538
 
563
539
  Do not tag or publish until the exact final release commit passes the full RC sequence.
564
540
 
@@ -588,12 +564,16 @@ Do not tag or publish until the exact final release commit passes the full RC se
588
564
  | `0.2.13` | Realtime Platform |
589
565
  | `0.2.14` | Testing Platform |
590
566
  | `0.2.15` | Plugin & Module Platform |
567
+ | `0.2.16` | Cache Platform v2 |
568
+ | `0.2.17` | Observability Platform v3 |
591
569
 
592
570
  ## Roadmap
593
571
 
594
- `0.2.15` establishes reusable server-side extension composition while keeping BCP subsystem contracts provider-neutral and independently testable.
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.
595
575
 
596
- The next logical milestone is **`0.2.16 Cache Platform v2`**, focused on distributed cache adapters, Redis-compatible caching, locking, stampede protection and production cache observability.
576
+ `0.2.19` is planned as the stabilization/API-freeze pass before the next `0.3.0` platform baseline.
597
577
 
598
578
  Native desktop/mobile compilation remains later roadmap work.
599
579