# `middleware` _(no submodules)_ ## 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.