first commit
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# `adaptive-cache` — `navigator.storage.estimate()`-driven cache sizing
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`createAdaptiveCacheSizer` periodically resizes a `PackedResourceCache`
|
||||
from a `navigator.storage.estimate()`-style quota report so the LRU
|
||||
ceiling tracks the actual storage the origin has available rather than a
|
||||
static guess. The sizer is the production implementation of
|
||||
PACKER-BROWSER-17 — adaptive cache sizing — and is what enables a
|
||||
packer-browser deployment to ride the upstream quota without manually
|
||||
re-tuning the cache on every device.
|
||||
|
||||
The module is **Clock-free**: it relies on the configured `intervalMs`
|
||||
through an injectable timer (default `setInterval`), so deterministic
|
||||
tests substitute a manual tick implementation rather than mocking time.
|
||||
Cancellation is propagated through an `AbortSignal`; pre-aborted signals
|
||||
short-circuit construction so the sizer never schedules work.
|
||||
|
||||
## Features
|
||||
|
||||
- Initial estimate fired synchronously on construction
|
||||
- Recurring refresh on a configurable interval (default `60_000` ms)
|
||||
- Pluggable `storageEstimate` injection (defaults to
|
||||
`() => navigator.storage.estimate()` when available)
|
||||
- Graceful no-op fallback when neither the injection nor the global API
|
||||
is present
|
||||
- `AbortSignal`-aware: aborting cancels the timer and stops further
|
||||
estimate calls
|
||||
- Disposer is idempotent and can be called from a manager's
|
||||
shutdown path
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createAdaptiveCacheSizer } from "@suaveplan/packer-browser/adaptive-cache";
|
||||
import { PackedResourceCache } from "@suaveplan/packer-browser";
|
||||
|
||||
const cache = new PackedResourceCache({ runtime, observability });
|
||||
const sizer = createAdaptiveCacheSizer({ cache });
|
||||
// On teardown:
|
||||
sizer.dispose();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
const controller = new AbortController();
|
||||
const sizer = createAdaptiveCacheSizer({
|
||||
cache,
|
||||
fraction: 0.1, // 10 % of quota
|
||||
intervalMs: 30_000,
|
||||
storageEstimate: async () => ({ quota: 8 * 1024 * 1024 * 1024 }),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
await sizer.refresh(); // one-shot refresh
|
||||
console.log(sizer.getLastApplied()); // 858_993_459
|
||||
|
||||
controller.abort(); // stops the recurring updater
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createAdaptiveCacheSizer(opts: AdaptiveCacheSizerOptions): AdaptiveCacheSizer`
|
||||
- `opts.cache` — `PackedResourceCache` to size. Required.
|
||||
- `opts.fraction` — clamped to `(0, 1]`. Default `0.25`.
|
||||
- `opts.intervalMs` — refresh interval. Default `60_000`.
|
||||
- `opts.storageEstimate` — pluggable estimate function.
|
||||
- `opts.signal` — cancellation `AbortSignal`.
|
||||
- `opts.setIntervalImpl` — test-only timer injection.
|
||||
- Returns: `{ dispose, refresh, getLastApplied }`.
|
||||
|
||||
- `AdaptiveCacheSizer.dispose()` — idempotent stop.
|
||||
- `AdaptiveCacheSizer.refresh(): Promise<void>` — one-shot refresh.
|
||||
- `AdaptiveCacheSizer.getLastApplied(): number | undefined` — most recent
|
||||
ceiling applied, or `undefined` before the first refresh.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The initial estimate is fired with `void refresh()` so construction
|
||||
is synchronous and the call returns the disposer immediately. Callers
|
||||
who need deterministic timing can `await sizer.refresh()`.
|
||||
- When `quota` is missing or non-positive the sizer is a defensive no-op
|
||||
for that tick — the cache's previous ceiling is preserved.
|
||||
- The default `setInterval` adapter calls `.unref()` when present so a
|
||||
Node test process is not held open by the recurring updater.
|
||||
- The sizer never reads the cache; it only writes via
|
||||
`cache.setMaxCacheSize(n)`, which is itself a runtime mutator that
|
||||
performs eviction in-place.
|
||||
Reference in New Issue
Block a user