@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 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, distributed caching, 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, deployment lifecycle, uploads, storage and standalone Node.js deployment.
4
4
 
5
- > **Development target:** `0.2.16Cache Platform v2`
5
+ > **Development target:** `0.2.18Deployment Platform v2`
6
6
  >
7
- > `0.2.16` remains unreleased until local validation, RC checks, tagging and npm publication complete.
7
+ > `0.2.18` remains unreleased until local validation, RC checks, tagging and npm publication complete.
8
8
 
9
9
  ## Current platform
10
10
 
@@ -27,11 +27,12 @@ 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
- | 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 |
30
+ | Caching | 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 |
32
+ | Deployment | Resource lifecycle, readiness, diagnostics, runtime identity, signal handling and graceful shutdown |
32
33
  | Uploads & storage | Multipart streaming, Local/S3-compatible storage and signed URLs |
33
34
  | Configuration | Typed config/environment validation and diagnostics |
34
- | Production | Standalone Node.js build, packaging, dependency pruning, Docker starter and graceful shutdown |
35
+ | Production | Standalone Node.js build, compiled server entrypoints, packaging, dependency pruning and Docker starter |
35
36
  | Documentation | Manifest-driven docs, platform metadata and API reference |
36
37
 
37
38
  ## Requirements
@@ -78,468 +79,221 @@ import { createTransactionalOutbox } from "bcp/events";
78
79
  import { createRealtime } from "bcp/realtime";
79
80
  import { createTestApp } from "bcp/testing";
80
81
  import { createPluginHost } from "bcp/plugins";
81
- import { createMetricsRegistry } from "bcp/observability";
82
+ import { createTracer } from "bcp/observability";
83
+ import { createDeploymentRuntime } from "bcp/deployment";
82
84
  ```
83
85
 
84
- ## Durable jobs and scheduling
86
+ ## Durable application infrastructure
85
87
 
86
- ```ts
87
- import {
88
- createJobQueue,
89
- createJobScheduler,
90
- } from "bcp/jobs";
88
+ BCP keeps durable application state separate from transient delivery:
91
89
 
92
- export const jobs =
93
- createJobQueue();
94
-
95
- export const scheduler =
96
- createJobScheduler({
97
- queue: jobs,
98
- });
90
+ ```text
91
+ HTTP / API
92
+ |
93
+ +-- Database
94
+ | +-- Transactional Outbox
95
+ |
96
+ +-- Jobs / Scheduler
97
+ | +-- Workflow
98
+ |
99
+ +-- Realtime
100
+ |
101
+ +-- Cache
102
+ |
103
+ +-- Observability
104
+ |
105
+ +-- Deployment lifecycle
99
106
  ```
100
107
 
101
- Production adapters can provide Redis-compatible durable queue/schedule storage without BCP owning the Redis connection.
108
+ Database/outbox/jobs/workflows remain durable truth. Realtime is transient delivery. Cache is an optimization layer. Tracing correlates operations without replacing state contracts.
102
109
 
103
- ## Workflow Orchestration — 0.2.11
110
+ ## Deployment Platform v2 — 0.2.18
111
+
112
+ `0.2.18` adds the server-only `bcp/deployment` entrypoint.
104
113
 
105
114
  ```ts
106
115
  import {
107
- createWorkflow,
108
- } from "bcp/workflow";
109
-
110
- export const onboarding =
111
- createWorkflow<{
112
- userId: number;
113
- }>(
114
- "user.onboarding",
115
- workflow => {
116
- workflow.step(
117
- "profile",
118
- createProfile
119
- );
120
-
121
- workflow.parallel(
122
- "initialize",
123
- parallel => {
124
- parallel.step(
125
- "preferences",
126
- createPreferences
127
- );
128
- parallel.step(
129
- "workspace",
130
- createWorkspace
131
- );
132
- }
133
- );
134
-
135
- workflow.delay(
136
- "cooldown",
137
- 1_000
138
- );
139
- }
140
- );
141
- ```
116
+ createDeploymentRuntime,
117
+ } from "bcp/deployment";
142
118
 
143
- Workflows support retries, compensation, persisted delays and optional durable queue execution.
144
-
145
- ## Transactional Outbox & Events — 0.2.12
119
+ export const deployment =
120
+ createDeploymentRuntime({
121
+ serviceName: "orders-api",
122
+ version: "1.0.0",
123
+ });
124
+ ```
146
125
 
147
- Use the same SQL transaction for business data and its outbox event:
126
+ Register resources in dependency order:
148
127
 
149
128
  ```ts
150
- await db.transaction(
151
- async tx => {
152
- await tx.execute(
153
- "INSERT INTO orders ..."
154
- );
155
-
156
- await outbox.publish(
157
- tx,
158
- "order.created",
159
- {
160
- orderId: 42,
161
- }
162
- );
163
- }
164
- );
165
- ```
129
+ deployment.addResource({
130
+ name: "database",
166
131
 
167
- After commit, an outbox dispatcher can deliver through `bcp/jobs`, a custom publisher or the local event bus.
132
+ async start() {
133
+ await db.connect();
134
+ },
168
135
 
169
- Read more: [Transactional Outbox & Events](docs/transactional-outbox-events.md)
136
+ async ready() {
137
+ return db.status === "ready";
138
+ },
170
139
 
171
- ## Realtime Platform — 0.2.13
140
+ async stop() {
141
+ await db.close();
142
+ },
143
+ });
172
144
 
173
- ```ts
174
- import {
175
- createRealtime,
176
- } from "bcp/realtime";
145
+ deployment.addResource({
146
+ name: "workers",
177
147
 
178
- export const realtime =
179
- createRealtime();
180
- ```
148
+ start() {
149
+ worker = jobs.startWorker();
150
+ },
181
151
 
182
- Join a channel and broadcast:
152
+ async stop() {
153
+ await worker.stop();
154
+ },
155
+ });
183
156
 
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
- );
157
+ await deployment.start();
199
158
  ```
200
159
 
201
- 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
-
203
- Read more: [Realtime Platform](docs/realtime-platform.md)
204
-
205
- ## Testing Platform — 0.2.14
206
-
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.
160
+ Startup follows registration order. Shutdown runs in reverse order, so workers can stop before their database/cache/Redis dependencies close. If startup fails midway, already-started resources are rolled back in reverse order.
208
161
 
209
- ### Request and route tests
162
+ Readiness:
210
163
 
211
164
  ```ts
212
165
  import {
213
- createRouteTestHandler,
214
- createTestApp,
215
- expectResponse,
216
- } from "bcp/testing";
217
-
218
- const handler =
219
- createRouteTestHandler({
220
- GET() {
221
- return {
222
- ok: true,
223
- };
224
- },
225
- });
226
-
227
- const app =
228
- createTestApp({
229
- handler,
230
- });
166
+ createDeploymentReadinessResponse,
167
+ } from "bcp/deployment";
231
168
 
232
- const response =
233
- await app.get(
234
- "/api/health"
169
+ export function GET() {
170
+ return createDeploymentReadinessResponse(
171
+ deployment
235
172
  );
236
-
237
- await expectResponse(response)
238
- .status(200)
239
- .json({
240
- ok: true,
241
- });
173
+ }
242
174
  ```
243
175
 
244
- `createTestApp()` keeps an in-memory cookie jar, supports default headers and can send JSON bodies directly.
245
-
246
- ### Authentication tests
176
+ A ready runtime returns `200`; starting, draining, failed or unhealthy runtimes return `503`.
247
177
 
248
- Create a real signed BCP session token instead of a fake test-only user header:
178
+ Diagnostics:
249
179
 
250
180
  ```ts
251
181
  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
- );
182
+ createDeploymentDiagnosticsResponse,
183
+ } from "bcp/deployment";
273
184
  ```
274
185
 
275
- When `store` is provided, the matching server-side auth session record is inserted as well.
276
-
277
- ### Rollback database tests
186
+ Deployment metadata can use:
278
187
 
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
- );
188
+ ```text
189
+ BCP_DEPLOYMENT_ID
190
+ BCP_INSTANCE_ID
191
+ BCP_RELEASE
192
+ NODE_ENV
193
+ BCP_SHUTDOWN_TIMEOUT_MS
294
194
  ```
295
195
 
296
- The callback uses the real BCP transaction and is deliberately rolled back after the test callback succeeds.
297
-
298
- ### Infrastructure harnesses
196
+ Signal ownership is optional:
299
197
 
300
198
  ```ts
301
- import {
302
- createJobTestHarness,
303
- createOutboxTestHarness,
304
- createRealtimeTestHarness,
305
- createWorkflowTestHarness,
306
- } from "bcp/testing";
199
+ const removeSignalHandlers =
200
+ deployment.installSignalHandlers();
307
201
  ```
308
202
 
309
- These harnesses use the real platform contracts rather than separate mock implementations.
203
+ Default signals are `SIGTERM` and `SIGINT`. BCP sets `process.exitCode` after graceful shutdown instead of immediately terminating the process.
310
204
 
311
- Background jobs:
205
+ The runtime can also integrate with the existing framework shutdown registry:
312
206
 
313
207
  ```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
- );
208
+ const unregister =
209
+ deployment.registerShutdownHook();
328
210
  ```
329
211
 
330
- Realtime:
212
+ Read more: [Deployment Platform v2](docs/deployment-platform-v2.md)
331
213
 
332
- ```ts
333
- const realtimeTest =
334
- createRealtimeTestHarness(
335
- realtime
336
- );
214
+ ## Compiled production entrypoints
337
215
 
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:
216
+ Prepared npm packages use compiled ESM for the main server runtime surfaces:
364
217
 
365
218
  ```text
366
- createFakeClock()
367
- createSequenceIdFactory()
368
- runTestMiddleware()
369
- createRealtimeTestSocket()
370
- readSseEvents()
371
- ```
372
-
373
- Read more: [Testing Platform](docs/testing-platform.md)
374
-
375
- ## Plugin & Module Platform — 0.2.15
376
-
377
- `0.2.15` adds the server-only `bcp/plugins` entrypoint for reusable application/framework extensions.
378
-
379
- Define plugins with explicit dependencies:
219
+ bcp/cache -> cache.mjs
220
+ bcp/config -> config.mjs
221
+ bcp/database -> database.mjs
222
+ bcp/auth -> auth.mjs
223
+ bcp/jobs -> jobs.mjs
224
+ bcp/workflow -> workflow.mjs
225
+ bcp/events -> events.mjs
226
+ bcp/realtime -> realtime.mjs
227
+ bcp/testing -> testing.mjs
228
+ bcp/plugins -> plugins.mjs
229
+ bcp/observability -> observability.mjs
230
+ bcp/deployment -> deployment.mjs
231
+ bcp/server -> server.mjs
232
+ bcp/middleware -> middleware.mjs
233
+ ```
234
+
235
+ TypeScript source remains the type surface, while prepared production runtime resolution points to compiled `.mjs` files.
236
+
237
+ ## Observability Platform v3 — 0.2.17
380
238
 
381
239
  ```ts
382
240
  import {
383
- createPluginHost,
384
- definePlugin,
385
- } from "bcp/plugins";
386
-
387
- const databasePlugin =
388
- definePlugin({
389
- name: "database",
390
- setup(context) {
391
- context.services.provide(
392
- "database",
393
- db
394
- );
395
- },
396
- start() {
397
- return db.connect();
398
- },
399
- stop() {
400
- return db.disconnect();
401
- },
402
- });
241
+ createTracer,
242
+ } from "bcp/observability";
403
243
 
404
- const jobsPlugin =
405
- definePlugin({
406
- name: "jobs",
407
- requires: [
408
- "database",
409
- ],
410
- });
411
-
412
- const host =
413
- createPluginHost({
414
- plugins: [
415
- jobsPlugin,
416
- databasePlugin,
417
- ],
418
- });
419
-
420
- await host.start();
421
- ```
422
-
423
- Dependency order is resolved automatically. Startup follows dependency order while stop/dispose runs in reverse order.
424
-
425
- Modules group reusable plugin sets:
426
-
427
- ```ts
428
- import {
429
- defineModule,
430
- } from "bcp/plugins";
431
-
432
- const backendModule =
433
- defineModule({
434
- name: "backend",
435
- plugins: [
436
- databasePlugin,
437
- jobsPlugin,
438
- ],
244
+ export const tracer =
245
+ createTracer({
246
+ serviceName: "orders-api",
439
247
  });
440
248
  ```
441
249
 
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`.
250
+ `bcp/observability` supports root/child spans, AsyncLocalStorage context, W3C `traceparent`, correlation IDs, request tracing, trace carriers, memory/composite exporters and trace-to-Prometheus metrics.
445
251
 
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)
252
+ Read more: [Observability Platform v3](docs/observability-v3.md)
449
253
 
450
254
  ## Cache Platform v2 — 0.2.16
451
255
 
452
- `0.2.16` keeps the original `cache()` and `dedupe()` APIs while adding provider-neutral asynchronous cache stores for production multi-instance applications.
453
-
454
- Create a cache store:
455
-
456
256
  ```ts
457
257
  import {
458
258
  createCacheStore,
259
+ createRedisCacheAdapter,
260
+ createRedisCacheLockAdapter,
459
261
  } from "bcp/cache";
460
-
461
- export const applicationCache =
462
- createCacheStore();
463
262
  ```
464
263
 
465
- Cache-aside loading:
264
+ Cache Store v2 supports local singleflight, distributed cache-fill leases, heartbeat renewal, TTL, tag/path invalidation and cache metrics while keeping the original `cache()` / `dedupe()` APIs available.
466
265
 
467
- ```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
- );
483
- ```
266
+ Read more: [Cache Platform v2](docs/cache-platform-v2.md)
484
267
 
485
- Within one store, concurrent misses share one loader automatically.
268
+ ## Plugin & Module Platform 0.2.15
486
269
 
487
- For multiple application instances, use shared cache and lock adapters:
270
+ `bcp/plugins` supports dependency ordering, setup/start/stop/dispose lifecycle, modules, config parsing, shared services and async hooks.
488
271
 
489
- ```ts
490
- import {
491
- createRedisCacheAdapter,
492
- createRedisCacheLockAdapter,
493
- } from "bcp/cache";
272
+ Read more: [Plugin & Module Platform](docs/plugin-module-platform.md)
494
273
 
495
- const redisCache =
496
- createRedisCacheAdapter({
497
- client: redisClient,
498
- });
274
+ ## Testing Platform — 0.2.14
499
275
 
500
- const redisLock =
501
- createRedisCacheLockAdapter({
502
- client: redisClient,
503
- });
276
+ `bcp/testing` provides runner-neutral request/route/page/auth/database/middleware/jobs/workflow/outbox/realtime/SSE test helpers.
504
277
 
505
- export const cache =
506
- createCacheStore({
507
- adapter: redisCache,
508
- lock: redisLock,
509
- });
510
- ```
278
+ Read more: [Testing Platform](docs/testing-platform.md)
511
279
 
512
- The default Redis namespace is `bcp:{cache}`. BCP does not install or own a Redis library/connection.
280
+ ## Realtime Platform 0.2.13
513
281
 
514
- Distributed `getOrSet()` uses owner-scoped lock leases, heartbeat renewal when supported, double-check-after-lock, contention wait/poll and configurable lock-timeout behavior.
282
+ `bcp/realtime` provides channels/rooms, presence, broker delivery, provider-neutral socket adapters, SSE and heartbeat handling.
515
283
 
516
- Tag/path invalidation remains available through the new async store:
284
+ Read more: [Realtime Platform](docs/realtime-platform.md)
517
285
 
518
- ```ts
519
- await cache.revalidateTag(
520
- "users"
521
- );
286
+ ## Transactional Outbox & Events — 0.2.12
522
287
 
523
- await cache.revalidatePath(
524
- "/dashboard"
525
- );
526
- ```
288
+ Use `bcp/events` to persist integration events in the same SQL transaction as business data, then dispatch after commit through durable jobs or custom publishers.
527
289
 
528
- Connect cache events to BCP metrics:
290
+ Read more: [Transactional Outbox & Events](docs/transactional-outbox-events.md)
529
291
 
530
- ```ts
531
- const cache =
532
- createCacheStore({
533
- metrics:
534
- createCacheMetrics(
535
- metricsRegistry
536
- ),
537
- });
538
- ```
292
+ ## Workflow Orchestration — 0.2.11
539
293
 
540
- Prepared npm packages compile `bcp/cache` to `cache.mjs` for standalone Node runtime use.
294
+ `bcp/workflow` supports sequential/parallel steps, retries, persisted delays, run leases, compensation and optional durable queue execution.
541
295
 
542
- Read more: [Cache Platform v2](docs/cache-platform-v2.md)
296
+ Read more: [Workflow Orchestration](docs/workflow-orchestration.md)
543
297
 
544
298
  ## Public entrypoints
545
299
 
@@ -559,6 +313,7 @@ bcp/realtime
559
313
  bcp/testing
560
314
  bcp/plugins
561
315
  bcp/observability
316
+ bcp/deployment
562
317
  bcp/server
563
318
  bcp/server-only
564
319
  bcp/middleware
@@ -599,40 +354,23 @@ bcp generate middleware
599
354
  bcp generate migration create_users
600
355
  ```
601
356
 
602
- ## Production model
603
-
604
- ```text
605
- Browser / API / Realtime clients
606
- |
607
- security + auth
608
- |
609
- Plugin Host
610
- / | \
611
- database workflows realtime
612
- | | ^
613
- outbox jobs |
614
- \__________|_________/
615
- durable state
616
- |
617
- shared cache layer
618
- Redis / other
619
- ```
620
-
621
- Cache is an optimization layer and must not replace durable application truth or transactional invariants.
622
-
623
357
  ## Packaging
624
358
 
359
+ Build and run directly:
360
+
625
361
  ```bash
626
362
  npm run build
627
363
  npm start
628
364
  ```
629
365
 
630
- Deployment package:
366
+ Create a standalone deployment package:
631
367
 
632
368
  ```bash
633
369
  bcp package
634
370
  ```
635
371
 
372
+ Application packages include production dependency manifests, deployment/environment metadata, file integrity metadata and a Docker starter while excluding `.env` secrets and application devDependencies.
373
+
636
374
  ## Documentation Platform
637
375
 
638
376
  Machine-readable contracts:
@@ -645,7 +383,7 @@ docs/api-manifest.json
645
383
 
646
384
  ## Release validation
647
385
 
648
- Before publishing `0.2.16`:
386
+ Before publishing `0.2.18`:
649
387
 
650
388
  ```bash
651
389
  npm run typecheck
@@ -656,7 +394,7 @@ npm run test:package
656
394
  npm run rc:check
657
395
  ```
658
396
 
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.
397
+ `0.2.18` adds unit and prepared-package smoke coverage for deployment lifecycle ordering, startup rollback, readiness/diagnostics, runtime metadata, server-only boundaries and compiled config/auth/observability/deployment/server/middleware runtimes.
660
398
 
661
399
  Do not tag or publish until the exact final release commit passes the full RC sequence.
662
400
 
@@ -687,12 +425,16 @@ Do not tag or publish until the exact final release commit passes the full RC se
687
425
  | `0.2.14` | Testing Platform |
688
426
  | `0.2.15` | Plugin & Module Platform |
689
427
  | `0.2.16` | Cache Platform v2 |
428
+ | `0.2.17` | Observability Platform v3 |
429
+ | `0.2.18` | Deployment Platform v2 |
690
430
 
691
431
  ## Roadmap
692
432
 
693
- `0.2.16` establishes provider-neutral shared caching and distributed cache-fill coordination while preserving the original process-local cache API.
433
+ `0.2.18` establishes a consistent production lifecycle and compiled server runtime boundary for the current BCP 0.2.x platform.
434
+
435
+ The next milestone is **`0.2.19 — Stability & API Freeze`**, focused on final API consistency, deprecation policy, performance/regression hardening, migration diagnostics and release-quality compatibility before `0.3.0`.
694
436
 
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.
437
+ `0.3.0` is planned as the next BCP Application Platform baseline.
696
438
 
697
439
  Native desktop/mobile compilation remains later roadmap work.
698
440