@chidchanun/bcp 0.2.14 → 0.2.16
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 +190 -12
- package/docs/README.md +45 -43
- package/docs/api-manifest.json +13 -5
- package/docs/api-reference.md +108 -3
- package/docs/cache-platform-v2.md +487 -0
- package/docs/docs-web-manifest.json +9 -5
- package/docs/platform-manifest.json +29 -4
- package/docs/plugin-module-platform.md +391 -0
- package/docs/releases/0.2.15.md +118 -0
- package/docs/releases/0.2.16.md +147 -0
- package/package.json +7 -2
- package/packages/bundler/src/client-boundary.ts +1 -0
- 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/plugins.mjs +811 -0
- package/packages/client/src/plugins.ts +26 -0
- package/packages/server/src/plugins.ts +1225 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
# Cache Platform v2
|
|
2
|
+
|
|
3
|
+
BCP Framework `0.2.16` extends `bcp/cache` with provider-neutral asynchronous cache storage, distributed locking, cache-stampede protection, Redis-compatible adapters and metrics integration while preserving the existing request-cache and `cache()` APIs.
|
|
4
|
+
|
|
5
|
+
## Compatibility
|
|
6
|
+
|
|
7
|
+
The existing APIs remain available and keep their process-local behavior:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
cache,
|
|
12
|
+
dedupe,
|
|
13
|
+
revalidatePath,
|
|
14
|
+
revalidateTag,
|
|
15
|
+
} from "bcp/cache";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`cache()` is still useful for simple single-process data caching and `dedupe()` is still request-scoped work sharing.
|
|
19
|
+
|
|
20
|
+
Use Cache Platform v2 when cached state must be shared across multiple application instances or when cache fill work needs distributed stampede protection.
|
|
21
|
+
|
|
22
|
+
## Create a cache store
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import {
|
|
26
|
+
createCacheStore,
|
|
27
|
+
} from "bcp/cache";
|
|
28
|
+
|
|
29
|
+
export const applicationCache =
|
|
30
|
+
createCacheStore();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Without an adapter, the store uses the built-in memory adapter.
|
|
34
|
+
|
|
35
|
+
Basic operations:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
await applicationCache.set(
|
|
39
|
+
"user:42",
|
|
40
|
+
{
|
|
41
|
+
id: 42,
|
|
42
|
+
name: "Bank",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
ttlMs: 60_000,
|
|
46
|
+
tags: ["users"],
|
|
47
|
+
paths: ["/users/42"],
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const user =
|
|
52
|
+
await applicationCache.get(
|
|
53
|
+
"user:42"
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
await applicationCache.delete(
|
|
57
|
+
"user:42"
|
|
58
|
+
);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Cache adapter contract
|
|
62
|
+
|
|
63
|
+
Shared cache providers implement `CacheAdapter`:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
interface CacheAdapter {
|
|
67
|
+
get<T>(key: string):
|
|
68
|
+
Promise<CacheAdapterEntry<T> | null>;
|
|
69
|
+
|
|
70
|
+
set<T>(
|
|
71
|
+
key: string,
|
|
72
|
+
value: T,
|
|
73
|
+
options?: CacheAdapterSetOptions
|
|
74
|
+
): Promise<void>;
|
|
75
|
+
|
|
76
|
+
delete(key: string): Promise<boolean>;
|
|
77
|
+
clear(): Promise<number>;
|
|
78
|
+
revalidateTag(tag: string): Promise<number>;
|
|
79
|
+
revalidatePath(path: string): Promise<number>;
|
|
80
|
+
entries?(): Promise<number>;
|
|
81
|
+
close?(): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The built-in implementations are:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
createMemoryCacheAdapter()
|
|
89
|
+
createRedisCacheAdapter()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This contract allows another provider to be added without changing application cache usage.
|
|
93
|
+
|
|
94
|
+
## TTL
|
|
95
|
+
|
|
96
|
+
Cache Store v2 uses millisecond TTL values:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
await applicationCache.set(
|
|
100
|
+
"dashboard:summary",
|
|
101
|
+
summary,
|
|
102
|
+
{
|
|
103
|
+
ttlMs: 30_000,
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`ttlMs: false` or an omitted TTL creates an entry without an application-level expiration.
|
|
109
|
+
|
|
110
|
+
`ttlMs: 0` deletes the existing entry instead of storing a new one.
|
|
111
|
+
|
|
112
|
+
The older `cache()` API continues to use `revalidate` in seconds for backward compatibility.
|
|
113
|
+
|
|
114
|
+
## Tag invalidation
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
await applicationCache.set(
|
|
118
|
+
"user:42",
|
|
119
|
+
user,
|
|
120
|
+
{
|
|
121
|
+
tags: [
|
|
122
|
+
"users",
|
|
123
|
+
"tenant:7",
|
|
124
|
+
],
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
await applicationCache.revalidateTag(
|
|
129
|
+
"users"
|
|
130
|
+
);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The returned number is the count of matching entries removed by the adapter.
|
|
134
|
+
|
|
135
|
+
## Path invalidation
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
await applicationCache.set(
|
|
139
|
+
"users-page",
|
|
140
|
+
data,
|
|
141
|
+
{
|
|
142
|
+
paths: [
|
|
143
|
+
"/dashboard/users",
|
|
144
|
+
],
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
await applicationCache.revalidatePath(
|
|
149
|
+
"/dashboard"
|
|
150
|
+
);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Path invalidation is hierarchical. Invalidating `/dashboard` also invalidates cached paths below `/dashboard/...`.
|
|
154
|
+
|
|
155
|
+
## `getOrSet()`
|
|
156
|
+
|
|
157
|
+
Use `getOrSet()` for cache-aside loading:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const user =
|
|
161
|
+
await applicationCache.getOrSet(
|
|
162
|
+
"user:42",
|
|
163
|
+
async () =>
|
|
164
|
+
databaseUser(42),
|
|
165
|
+
{
|
|
166
|
+
ttlMs: 60_000,
|
|
167
|
+
tags: ["users"],
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Within one `CacheStore`, concurrent calls for the same key share one in-flight loader.
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
Request A ----\
|
|
176
|
+
Request B -----+--> one loader --> cache
|
|
177
|
+
Request C ----/
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
This local singleflight behavior is available even without a distributed lock adapter.
|
|
181
|
+
|
|
182
|
+
## Distributed stampede protection
|
|
183
|
+
|
|
184
|
+
For multiple application instances, provide a shared `CacheLockAdapter`:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
const applicationCache =
|
|
188
|
+
createCacheStore({
|
|
189
|
+
adapter: redisCache,
|
|
190
|
+
lock: redisLock,
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The load flow becomes:
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
Instance A cache miss
|
|
198
|
+
|
|
|
199
|
+
+--> acquire distributed lock
|
|
200
|
+
| |
|
|
201
|
+
| +--> double-check cache
|
|
202
|
+
| +--> execute loader
|
|
203
|
+
| +--> store result
|
|
204
|
+
| +--> release lock
|
|
205
|
+
|
|
|
206
|
+
Instance B cache miss
|
|
207
|
+
|
|
|
208
|
+
+--> lock contention
|
|
209
|
+
|
|
|
210
|
+
+--> poll shared cache
|
|
211
|
+
+--> consume A's result
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Default values:
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
lockTtlMs 30000
|
|
218
|
+
waitTimeoutMs 5000
|
|
219
|
+
pollIntervalMs 25
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Override them per operation:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
await applicationCache.getOrSet(
|
|
226
|
+
"report:monthly",
|
|
227
|
+
buildReport,
|
|
228
|
+
{
|
|
229
|
+
lockTtlMs: 60_000,
|
|
230
|
+
waitTimeoutMs: 10_000,
|
|
231
|
+
pollIntervalMs: 50,
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Lock heartbeat
|
|
237
|
+
|
|
238
|
+
If the selected lock adapter implements `extend()`, BCP renews the acquired load lock while the loader is still active. The renewal interval is approximately one third of `lockTtlMs`.
|
|
239
|
+
|
|
240
|
+
This reduces duplicate work when a legitimate load takes longer than the original lease.
|
|
241
|
+
|
|
242
|
+
A process crash can still let the lease expire, which is intentional: another process must eventually be able to recover and refill the cache.
|
|
243
|
+
|
|
244
|
+
## Lock timeout policy
|
|
245
|
+
|
|
246
|
+
The default timeout behavior is availability-oriented. If a process waits for another lock owner but still cannot acquire the lock after the timeout, it executes the loader rather than blocking indefinitely.
|
|
247
|
+
|
|
248
|
+
For workloads where duplicate computation is worse than a failed request:
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
await applicationCache.getOrSet(
|
|
252
|
+
"expensive-model",
|
|
253
|
+
buildModel,
|
|
254
|
+
{
|
|
255
|
+
onLockTimeout: "error",
|
|
256
|
+
}
|
|
257
|
+
);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
This throws instead of running an unlocked fallback loader.
|
|
261
|
+
|
|
262
|
+
## Memory lock adapter
|
|
263
|
+
|
|
264
|
+
Tests and single-process deployments can use:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
import {
|
|
268
|
+
createMemoryCacheAdapter,
|
|
269
|
+
createMemoryCacheLockAdapter,
|
|
270
|
+
} from "bcp/cache";
|
|
271
|
+
|
|
272
|
+
const adapter =
|
|
273
|
+
createMemoryCacheAdapter();
|
|
274
|
+
const lock =
|
|
275
|
+
createMemoryCacheLockAdapter();
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Multiple `CacheStore` instances can share these objects during tests to exercise cross-store contention behavior deterministically.
|
|
279
|
+
|
|
280
|
+
## Redis-compatible cache adapter
|
|
281
|
+
|
|
282
|
+
BCP does not install or own a Redis library.
|
|
283
|
+
|
|
284
|
+
Provide a minimal command client:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
interface RedisCacheCommandClient {
|
|
288
|
+
sendCommand(
|
|
289
|
+
command: string[]
|
|
290
|
+
): Promise<unknown>;
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Example adapter wiring:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import {
|
|
298
|
+
createCacheStore,
|
|
299
|
+
createRedisCacheAdapter,
|
|
300
|
+
createRedisCacheLockAdapter,
|
|
301
|
+
} from "bcp/cache";
|
|
302
|
+
|
|
303
|
+
const redisCache =
|
|
304
|
+
createRedisCacheAdapter({
|
|
305
|
+
client: redisClient,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const redisLock =
|
|
309
|
+
createRedisCacheLockAdapter({
|
|
310
|
+
client: redisClient,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
export const cache =
|
|
314
|
+
createCacheStore({
|
|
315
|
+
adapter: redisCache,
|
|
316
|
+
lock: redisLock,
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
The default namespace is:
|
|
321
|
+
|
|
322
|
+
```text
|
|
323
|
+
bcp:{cache}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The `{cache}` hash tag keeps the reference adapter's related keys in one Redis Cluster slot for its Lua state transitions.
|
|
327
|
+
|
|
328
|
+
When overriding the namespace for Redis Cluster, keep a hash tag, for example:
|
|
329
|
+
|
|
330
|
+
```text
|
|
331
|
+
my-app:{cache}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Redis value serialization
|
|
335
|
+
|
|
336
|
+
The reference Redis adapter stores serialized values inside cache records. The default serializer is JSON.
|
|
337
|
+
|
|
338
|
+
Use custom serialization for values that JSON cannot safely represent:
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
createRedisCacheAdapter({
|
|
342
|
+
client: redisClient,
|
|
343
|
+
serialize(value) {
|
|
344
|
+
return customEncode(value);
|
|
345
|
+
},
|
|
346
|
+
deserialize(value) {
|
|
347
|
+
return customDecode(value);
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
The serializer must return a string.
|
|
353
|
+
|
|
354
|
+
## Redis connection ownership
|
|
355
|
+
|
|
356
|
+
BCP never creates a Redis connection. The application owns:
|
|
357
|
+
|
|
358
|
+
- connection URL and credentials
|
|
359
|
+
- TLS
|
|
360
|
+
- Sentinel/Cluster configuration
|
|
361
|
+
- reconnect behavior
|
|
362
|
+
- process shutdown
|
|
363
|
+
|
|
364
|
+
An adapter can receive a close hook:
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
createRedisCacheAdapter({
|
|
368
|
+
client: redisClient,
|
|
369
|
+
close: async () => {
|
|
370
|
+
await redisClient.quit();
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
If the cache and lock adapters share one Redis connection, avoid registering two independent close hooks for that same connection. Prefer application-owned coordinated shutdown.
|
|
376
|
+
|
|
377
|
+
## Redis atomic transitions
|
|
378
|
+
|
|
379
|
+
The reference cache adapter uses Lua for cache record/index mutations, tag invalidation and distributed lock compare-and-release / compare-and-renew operations.
|
|
380
|
+
|
|
381
|
+
This prevents one lock owner from releasing or renewing a lock that has already expired and been acquired by another owner.
|
|
382
|
+
|
|
383
|
+
Path invalidation first discovers registered path indexes and then performs a Lua cleanup over the matching sets. For extremely large path catalogs, a specialized provider can implement a more scalable indexing strategy behind the same `CacheAdapter` contract.
|
|
384
|
+
|
|
385
|
+
## Metrics integration
|
|
386
|
+
|
|
387
|
+
Cache Platform v2 can emit metrics to the existing BCP metrics registry:
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
import {
|
|
391
|
+
createCacheMetrics,
|
|
392
|
+
createCacheStore,
|
|
393
|
+
} from "bcp/cache";
|
|
394
|
+
import {
|
|
395
|
+
createMetricsRegistry,
|
|
396
|
+
} from "bcp/observability";
|
|
397
|
+
|
|
398
|
+
const metrics =
|
|
399
|
+
createMetricsRegistry();
|
|
400
|
+
|
|
401
|
+
const cache =
|
|
402
|
+
createCacheStore({
|
|
403
|
+
metrics:
|
|
404
|
+
createCacheMetrics(
|
|
405
|
+
metrics
|
|
406
|
+
),
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The default metrics are:
|
|
411
|
+
|
|
412
|
+
```text
|
|
413
|
+
bcp_cache_operations_total{event="..."}
|
|
414
|
+
bcp_cache_in_flight
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Events include hits, misses, sets, deletes, invalidations, loader outcomes and lock contention/timeout activity.
|
|
418
|
+
|
|
419
|
+
Use a custom prefix when multiple logical caches need separate metric families:
|
|
420
|
+
|
|
421
|
+
```ts
|
|
422
|
+
createCacheMetrics(
|
|
423
|
+
metrics,
|
|
424
|
+
{
|
|
425
|
+
prefix:
|
|
426
|
+
"bcp_session_cache",
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Store statistics
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
const stats =
|
|
435
|
+
await cache.stats();
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
The snapshot includes:
|
|
439
|
+
|
|
440
|
+
```text
|
|
441
|
+
hits
|
|
442
|
+
misses
|
|
443
|
+
sets
|
|
444
|
+
deletes
|
|
445
|
+
invalidations
|
|
446
|
+
loads
|
|
447
|
+
loadErrors
|
|
448
|
+
lockAcquired
|
|
449
|
+
lockContentions
|
|
450
|
+
lockTimeouts
|
|
451
|
+
inFlight
|
|
452
|
+
adapterEntries (when supported)
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Statistics belong to the `CacheStore` process. `adapterEntries` can come from shared storage when the provider supports it.
|
|
456
|
+
|
|
457
|
+
## Failure model
|
|
458
|
+
|
|
459
|
+
A cache is an optimization, not the system of record.
|
|
460
|
+
|
|
461
|
+
Recommended production rules:
|
|
462
|
+
|
|
463
|
+
1. Keep durable application truth in the database/object store.
|
|
464
|
+
2. Design loaders so cache loss can be recovered by recomputation.
|
|
465
|
+
3. Choose `onLockTimeout` based on whether availability or duplicate-work avoidance matters more.
|
|
466
|
+
4. Do not use distributed cache locks as a replacement for database transaction/uniqueness rules protecting business invariants.
|
|
467
|
+
5. Treat Redis/cache outages separately from durable queues, workflows and outbox guarantees.
|
|
468
|
+
|
|
469
|
+
## Closing the store
|
|
470
|
+
|
|
471
|
+
```ts
|
|
472
|
+
await cache.close();
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
`close()` clears local in-flight bookkeeping and calls configured adapter/lock lifecycle hooks.
|
|
476
|
+
|
|
477
|
+
## Published runtime
|
|
478
|
+
|
|
479
|
+
`0.2.16` prepares `bcp/cache` as compiled `cache.mjs` for the npm package. TypeScript source remains the type surface, while standalone Node imports execute the compiled runtime.
|
|
480
|
+
|
|
481
|
+
## Related guides
|
|
482
|
+
|
|
483
|
+
- [Caching](caching.md)
|
|
484
|
+
- [Observability Platform v2](observability.md)
|
|
485
|
+
- [Plugin & Module Platform](plugin-module-platform.md)
|
|
486
|
+
- [Testing Platform](testing-platform.md)
|
|
487
|
+
- [Production Hardening](production-hardening.md)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"framework": "bcp",
|
|
4
|
-
"versionTarget": "0.2.
|
|
4
|
+
"versionTarget": "0.2.16",
|
|
5
5
|
"releaseState": "unreleased",
|
|
6
6
|
"sections": [
|
|
7
7
|
{
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
{
|
|
59
59
|
"id": "runtime",
|
|
60
60
|
"title": "Runtime & Infrastructure",
|
|
61
|
-
"description": "Middleware, jobs, scheduling, workflows, event delivery, realtime channels, observability, caching, security and production hardening.",
|
|
61
|
+
"description": "Middleware, jobs, scheduling, workflows, event delivery, realtime channels, observability, distributed caching, security and production hardening.",
|
|
62
62
|
"pages": [
|
|
63
63
|
{ "route": "/docs/middleware", "source": "middleware.md", "title": "Middleware" },
|
|
64
64
|
{ "route": "/docs/hydration", "source": "hydration.md", "title": "Hydration" },
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
{ "route": "/docs/workflow-orchestration", "source": "workflow-orchestration.md", "title": "Workflow Orchestration" },
|
|
71
71
|
{ "route": "/docs/realtime-platform", "source": "realtime-platform.md", "title": "Realtime Platform" },
|
|
72
72
|
{ "route": "/docs/caching", "source": "caching.md", "title": "Caching" },
|
|
73
|
+
{ "route": "/docs/cache-platform-v2", "source": "cache-platform-v2.md", "title": "Cache Platform v2" },
|
|
73
74
|
{ "route": "/docs/security", "source": "security.md", "title": "Security" },
|
|
74
75
|
{ "route": "/docs/production-hardening", "source": "production-hardening.md", "title": "Production Hardening" }
|
|
75
76
|
]
|
|
@@ -88,11 +89,12 @@
|
|
|
88
89
|
{
|
|
89
90
|
"id": "developer-experience",
|
|
90
91
|
"title": "Developer Experience",
|
|
91
|
-
"description": "Project generators, diagnostics, testing, project metadata and framework maintenance tooling.",
|
|
92
|
+
"description": "Project generators, diagnostics, testing, plugin composition, project metadata and framework maintenance tooling.",
|
|
92
93
|
"pages": [
|
|
93
94
|
{ "route": "/docs/generators", "source": "generators.md", "title": "Project Generators" },
|
|
94
95
|
{ "route": "/docs/developer-tools", "source": "developer-tools.md", "title": "Doctor & Inspect" },
|
|
95
|
-
{ "route": "/docs/testing-platform", "source": "testing-platform.md", "title": "Testing Platform" }
|
|
96
|
+
{ "route": "/docs/testing-platform", "source": "testing-platform.md", "title": "Testing Platform" },
|
|
97
|
+
{ "route": "/docs/plugin-module-platform", "source": "plugin-module-platform.md", "title": "Plugin & Module Platform" }
|
|
96
98
|
]
|
|
97
99
|
},
|
|
98
100
|
{
|
|
@@ -115,7 +117,9 @@
|
|
|
115
117
|
}
|
|
116
118
|
],
|
|
117
119
|
"releases": [
|
|
118
|
-
{ "route": "/releases/0.2.
|
|
120
|
+
{ "route": "/releases/0.2.16", "source": "releases/0.2.16.md", "version": "0.2.16", "state": "unreleased" },
|
|
121
|
+
{ "route": "/releases/0.2.15", "source": "releases/0.2.15.md", "version": "0.2.15" },
|
|
122
|
+
{ "route": "/releases/0.2.14", "source": "releases/0.2.14.md", "version": "0.2.14" },
|
|
119
123
|
{ "route": "/releases/0.2.13", "source": "releases/0.2.13.md", "version": "0.2.13" },
|
|
120
124
|
{ "route": "/releases/0.2.12", "source": "releases/0.2.12.md", "version": "0.2.12" },
|
|
121
125
|
{ "route": "/releases/0.2.11", "source": "releases/0.2.11.md", "version": "0.2.11" },
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"framework": "bcp",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.16",
|
|
5
5
|
"releaseState": "unreleased",
|
|
6
|
-
"baseline": "
|
|
6
|
+
"baseline": "cache-platform-v2",
|
|
7
7
|
"runtime": {
|
|
8
8
|
"node": ">=24.11.0",
|
|
9
9
|
"react": "19",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"bcp/events",
|
|
25
25
|
"bcp/realtime",
|
|
26
26
|
"bcp/testing",
|
|
27
|
+
"bcp/plugins",
|
|
27
28
|
"bcp/observability",
|
|
28
29
|
"bcp/server",
|
|
29
30
|
"bcp/server-only",
|
|
@@ -138,6 +139,28 @@
|
|
|
138
139
|
"testRealtimeSocket": true,
|
|
139
140
|
"testSseReader": true,
|
|
140
141
|
"deterministicTestClock": true,
|
|
142
|
+
"pluginModulePlatform": true,
|
|
143
|
+
"pluginDefinitions": true,
|
|
144
|
+
"pluginModules": true,
|
|
145
|
+
"pluginDependencyOrdering": true,
|
|
146
|
+
"pluginLifecycleHooks": true,
|
|
147
|
+
"pluginConfigSchemas": true,
|
|
148
|
+
"pluginServiceRegistry": true,
|
|
149
|
+
"pluginHookBus": true,
|
|
150
|
+
"pluginLifecycleRollback": true,
|
|
151
|
+
"cachePlatformV2": true,
|
|
152
|
+
"cacheAdapterContract": true,
|
|
153
|
+
"memoryCacheAdapter": true,
|
|
154
|
+
"redisCacheAdapter": true,
|
|
155
|
+
"cacheLockAdapter": true,
|
|
156
|
+
"redisCacheLockAdapter": true,
|
|
157
|
+
"cacheStampedeProtection": true,
|
|
158
|
+
"cacheLockHeartbeats": true,
|
|
159
|
+
"cacheTtl": true,
|
|
160
|
+
"cacheTagInvalidation": true,
|
|
161
|
+
"cachePathInvalidation": true,
|
|
162
|
+
"cacheMetrics": true,
|
|
163
|
+
"compiledCacheRuntime": true,
|
|
141
164
|
"databaseMigrations": true,
|
|
142
165
|
"databaseAdapterContract": true,
|
|
143
166
|
"databasePostgresql": true,
|
|
@@ -177,7 +200,7 @@
|
|
|
177
200
|
"s3-compatible"
|
|
178
201
|
],
|
|
179
202
|
"compatibility": {
|
|
180
|
-
"previousBaseline": "0.2.
|
|
203
|
+
"previousBaseline": "0.2.15",
|
|
181
204
|
"intentionalBreakingChangesFromPreviousBaseline": false,
|
|
182
205
|
"migrationGuide": "migration-0.2.md"
|
|
183
206
|
},
|
|
@@ -201,7 +224,9 @@
|
|
|
201
224
|
"transactionalOutboxEvents": "transactional-outbox-events.md",
|
|
202
225
|
"realtimePlatform": "realtime-platform.md",
|
|
203
226
|
"testingPlatform": "testing-platform.md",
|
|
227
|
+
"pluginModulePlatform": "plugin-module-platform.md",
|
|
228
|
+
"cachePlatformV2": "cache-platform-v2.md",
|
|
204
229
|
"migrationGuide": "migration-0.2.md",
|
|
205
|
-
"releaseNotes": "releases/0.2.
|
|
230
|
+
"releaseNotes": "releases/0.2.16.md"
|
|
206
231
|
}
|
|
207
232
|
}
|