Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
# `middleware`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`middleware.ts` builds the `DriveMiddleware` registered on a `Drive` by `driveCachePlugin`. The middleware intercepts `read(ctx, next)` to short-circuit hot paths from the cache and to populate fresh entries on a miss. `write` and `delete` interceptions are delegated to `middleware.invalidation`. `stat` and `list` are pure passthroughs in this iteration.
A cache is a perf optimisation, not a correctness boundary. Every underlying cache failure (`get` rejection, `set` rejection, `delete` rejection) is captured into `stats.errors` and the OTel `errors_total{namespace, op}` counter — and is never propagated to the consumer.
## Features
- Pre-aborted `AbortSignal` short-circuits the cache work and defers to `next()` so the inner storage surfaces canonical `DRIVE.ABORTED`.
- Live `AbortSignal` is forwarded to `cache.get` for caches whose `get` accepts a signal.
- `shouldCache(path, bytes)` predicate gates population — defaults to a 16-MiB byte ceiling matching `drive-mesh-protocol`'s `MAX_FRAME_BYTES`.
- Population enforces the byte ceiling defensively; an oversize entry raises a structured `DRIVE_CACHE.CACHE_TOO_LARGE` internally and is suppressed (counted on `stats.errors`).
- `stat` and `list` pass straight to `next()` without touching the cache.
## Usage: Basic
```ts
import { buildMiddleware } from "@suaveplan/drive-plugin-cache/middleware";
import { createMutableStats } from "@suaveplan/drive-plugin-cache/handle";
const stats = createMutableStats();
const mw = buildMiddleware({ cache, stats, namespace: "drive" });
drive.useMiddleware(mw);
```
## Usage: Advanced
```ts
const mw = buildMiddleware({
cache,
stats,
namespace: "media",
invalidateOnWrite: true,
invalidateOnDelete: false,
shouldCache: (path, bytes) =>
path.endsWith(".mov") && bytes.byteLength <= 8 * 1024 * 1024,
maxEntryBytes: 8 * 1024 * 1024,
logger,
});
```
## API Reference
| Export | Shape | Description |
|---|---|---|
| `BuildMiddlewareArgs` | `{ cache; stats; namespace?; invalidateOnWrite?; invalidateOnDelete?; shouldCache?; maxEntryBytes?; logger? }` | Constructor bag. |
| `buildMiddleware(args)` | `(BuildMiddlewareArgs) => DriveMiddleware` | Returns the full `DriveMiddleware` (read + write + delete + stat + list). |
## Implementation Notes
The cache key is computed as `${namespace}::${ctx.path}`. The `::` separator avoids accidental collision with paths that already contain `/`. Population happens **after** `next()` resolves — i.e. only when the inner storage actually produced bytes — so a thrown `next()` cannot poison the cache. Symmetrically, invalidation happens **after** a successful `write`/`delete` `next()`. The optimistic alternative (delete before next()) opens a race window where a concurrent read could repopulate stale bytes between the cache delete and the storage write.