@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 +184 -204
- package/docs/README.md +47 -34
- package/docs/api-manifest.json +14 -14
- package/docs/api-reference.md +232 -231
- package/docs/cache-platform-v2.md +487 -0
- package/docs/docs-web-manifest.json +7 -3
- package/docs/observability-v3.md +402 -0
- package/docs/platform-manifest.json +30 -4
- package/docs/releases/0.2.16.md +147 -0
- package/docs/releases/0.2.17.md +166 -0
- package/package.json +3 -3
- package/packages/cache/src/platform-v2.ts +1705 -0
- package/packages/client/src/cache.mjs +1554 -0
- package/packages/client/src/cache.ts +29 -0
- package/packages/client/src/observability.mjs +1251 -0
- package/packages/client/src/observability.ts +37 -0
- package/packages/server/src/observability-v3.ts +878 -0
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.
|
|
5
|
+
> **Development target:** `0.2.17 — Observability Platform v3`
|
|
6
6
|
>
|
|
7
|
-
> `0.2.
|
|
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
|
-
|
|
|
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 {
|
|
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
|
-
`
|
|
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
|
-
|
|
232
|
-
await app.get(
|
|
233
|
-
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
### Authentication tests
|
|
219
|
+
Read more: [Testing Platform](docs/testing-platform.md)
|
|
246
220
|
|
|
247
|
-
|
|
221
|
+
## Plugin & Module Platform — 0.2.15
|
|
248
222
|
|
|
249
223
|
```ts
|
|
250
224
|
import {
|
|
251
|
-
|
|
252
|
-
|
|
225
|
+
createPluginHost,
|
|
226
|
+
definePlugin,
|
|
227
|
+
} from "bcp/plugins";
|
|
253
228
|
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
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
|
-
|
|
259
|
+
Read more: [Plugin & Module Platform](docs/plugin-module-platform.md)
|
|
277
260
|
|
|
278
|
-
|
|
279
|
-
import {
|
|
280
|
-
withTestTransaction,
|
|
281
|
-
} from "bcp/testing";
|
|
261
|
+
## Cache Platform v2 — 0.2.16
|
|
282
262
|
|
|
283
|
-
|
|
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
|
-
|
|
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
|
-
|
|
285
|
+
Distributed `getOrSet()` supports local singleflight, owner-scoped lock leases, heartbeat renewal, double-check-after-lock and contention wait/poll.
|
|
296
286
|
|
|
297
|
-
|
|
287
|
+
Read more: [Cache Platform v2](docs/cache-platform-v2.md)
|
|
298
288
|
|
|
299
|
-
|
|
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
|
-
|
|
291
|
+
`0.2.17` keeps the metrics/Prometheus/health APIs from Observability v2 and adds provider-neutral tracing and correlation.
|
|
309
292
|
|
|
310
|
-
|
|
293
|
+
Create a tracer:
|
|
311
294
|
|
|
312
295
|
```ts
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
296
|
+
import {
|
|
297
|
+
createMemoryTraceSpanExporter,
|
|
298
|
+
createTracer,
|
|
299
|
+
} from "bcp/observability";
|
|
317
300
|
|
|
318
|
-
|
|
301
|
+
const traces =
|
|
302
|
+
createMemoryTraceSpanExporter();
|
|
319
303
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
);
|
|
304
|
+
export const tracer =
|
|
305
|
+
createTracer({
|
|
306
|
+
exporter: traces,
|
|
307
|
+
serviceName: "api",
|
|
308
|
+
});
|
|
327
309
|
```
|
|
328
310
|
|
|
329
|
-
|
|
311
|
+
Create root/child spans:
|
|
330
312
|
|
|
331
313
|
```ts
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
|
|
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
|
-
|
|
328
|
+
Tracing context flows through normal awaited async work via Node `AsyncLocalStorage`.
|
|
363
329
|
|
|
364
|
-
|
|
365
|
-
createFakeClock()
|
|
366
|
-
createSequenceIdFactory()
|
|
367
|
-
runTestMiddleware()
|
|
368
|
-
createRealtimeTestSocket()
|
|
369
|
-
readSseEvents()
|
|
370
|
-
```
|
|
330
|
+
### HTTP tracing
|
|
371
331
|
|
|
372
|
-
|
|
332
|
+
```ts
|
|
333
|
+
import {
|
|
334
|
+
createRequestTracingMiddleware,
|
|
335
|
+
} from "bcp/observability";
|
|
373
336
|
|
|
374
|
-
|
|
337
|
+
export const middleware =
|
|
338
|
+
createRequestTracingMiddleware(
|
|
339
|
+
tracer
|
|
340
|
+
);
|
|
341
|
+
```
|
|
375
342
|
|
|
376
|
-
`
|
|
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
|
-
|
|
345
|
+
### Jobs / workflow / events / realtime propagation
|
|
379
346
|
|
|
380
347
|
```ts
|
|
381
348
|
import {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
} from "bcp/
|
|
349
|
+
createTraceCarrier,
|
|
350
|
+
runWithTraceCarrier,
|
|
351
|
+
} from "bcp/observability";
|
|
385
352
|
|
|
386
|
-
const
|
|
387
|
-
|
|
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
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
356
|
+
await jobs.enqueue(
|
|
357
|
+
"order.process",
|
|
358
|
+
{
|
|
359
|
+
orderId,
|
|
360
|
+
trace,
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
```
|
|
410
364
|
|
|
411
|
-
|
|
412
|
-
createPluginHost({
|
|
413
|
-
plugins: [
|
|
414
|
-
jobsPlugin,
|
|
415
|
-
databasePlugin,
|
|
416
|
-
],
|
|
417
|
-
});
|
|
365
|
+
Consumer:
|
|
418
366
|
|
|
419
|
-
|
|
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
|
-
|
|
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
|
-
|
|
383
|
+
### Trace-to-metrics and logs
|
|
425
384
|
|
|
426
385
|
```ts
|
|
427
386
|
import {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
413
|
+
Default trace metrics use low-cardinality `kind` and `status` labels. Span-name labels are opt-in.
|
|
442
414
|
|
|
443
|
-
|
|
415
|
+
BCP does not install an OpenTelemetry SDK or vendor APM package; production exporters remain application-owned through `TraceSpanExporter`.
|
|
444
416
|
|
|
445
|
-
|
|
417
|
+
Prepared npm packages compile `bcp/observability` to `observability.mjs` for standalone Node runtime use.
|
|
446
418
|
|
|
447
|
-
Read more: [
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|