Files
suaveplan/packages/networking/drive/drive-plugin-cache
2026-05-27 23:47:53 +01:00
..
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00
2026-05-27 23:47:53 +01:00

@suaveplan/drive-plugin-cache - Read-through cache middleware for @suaveplan/drive. Accepts a caller-supplied AbstractCache and intercepts Drive.read to short-circuit hot paths; write/delete invalidate post-success. Cache backend, eviction policy, TTL, and namespacing are entirely caller-owned.

Read-through cache middleware for @suaveplan/drive. Accepts a caller-supplied AbstractCache<Uint8Array> (from @suaveplan/cache) and intercepts every drive.read(path) to return cached plaintext bytes instead of re-running the storage / mesh / decryption stack. write and delete invalidate the corresponding entry post-success.

The cache itself is opaque to the plugin — consumers pick the backend (MemoryCache, StorageCache over OPFS / file / S3 / CDN, RedisCache), the eviction policy, the TTL, the namespacing, the codec, the redis/storage instance. The plugin just talks AbstractCache.

Documentation tree

  • error-codes — error-codes.ts defines the structured error surface for @suaveplan/drive-plugin-cache. It exports the `DRIVE_CACHE_…
    • error-codes — error-codes.ts defines the structured error surface for @suaveplan/drive-plugin-cache. It exports the `DRIVE_CACHE_…
  • handle — handle.ts builds the DriveCacheHandle returned by driveCachePlugin.install(drive). The handle exposes a stats sna…
    • handle — handle.ts builds the DriveCacheHandle returned by driveCachePlugin.install(drive). The handle exposes a stats sna…
  • middleware — middleware.ts builds the DriveMiddleware registered on a Drive by driveCachePlugin. The middleware intercepts `…
    • middleware.invalidation — middleware.invalidation.ts provides the write and delete hooks of the DriveMiddleware built in middleware.ts.…
    • middleware — middleware.ts builds the DriveMiddleware registered on a Drive by driveCachePlugin. The middleware intercepts `…
    • middleware.signal — middleware.signal.ts provides two tiny AbortSignal helpers used by the drive-cache middleware: getSignal(ctx) ext…
  • otel — otel.ts wires the @suaveplan/drive-cache meter and tracer used by every middleware intercept in `@suaveplan/drive-p…
    • otel — otel.ts wires the @suaveplan/drive-cache meter and tracer used by every middleware intercept in `@suaveplan/drive-p…
  • plugin — plugin.ts is the public entry point of @suaveplan/drive-plugin-cache. It exports the `driveCachePlugin(options): Dr…
    • plugin — plugin.ts is the public entry point of @suaveplan/drive-plugin-cache. It exports the `driveCachePlugin(options): Dr…

Modules

  • plugin — driveCachePlugin(options): DrivePlugin<DriveCacheHandle> factory.
  • middleware — the read intercept (hit short-circuit + populate-on-miss).
  • middleware.invalidation — write/delete invalidation hooks.
  • middleware.signal — AbortSignal helpers.
  • handle — DriveCacheHandle builder.
  • otel — @suaveplan/drive-cache meter + tracer wiring.
  • error-codes — DRIVE_CACHE_CODES registry and DriveCacheError.
  • types — public type surface.

Quickstart

import { Drive } from "@suaveplan/drive";
import { MemoryCache } from "@suaveplan/cache";
import { driveCachePlugin } from "@suaveplan/drive-plugin-cache";

const cache = new MemoryCache<Uint8Array>();
const drive = new Drive(storage);
await drive.initialize();
const handle = await drive.usePlugin(driveCachePlugin({ cache }));

await drive.write("clip.mov", payload);
const bytes1 = await drive.read("clip.mov");   // miss → populate
const bytes2 = await drive.read("clip.mov");   // hit  → cached
console.log(handle.getStats());                 // { hits: 1, misses: 1, ... }

Composition recipes

1. Memory cache (zero deps, ephemeral, fastest)

import { MemoryCache } from "@suaveplan/cache";
const cache = new MemoryCache<Uint8Array>({ maxSize: 10_000 });
await drive.usePlugin(driveCachePlugin({ cache }));

2. OPFS-backed cache (browser, survives reloads)

import { createOpfsStorage } from "@suaveplan/storage-browser-opfs";
import { StorageCache, IdentityCodec } from "@suaveplan/cache-storage";

const cache = new StorageCache<Uint8Array>({
  storage: await createOpfsStorage("drive-cache"),
  codec: new IdentityCodec(),
});
await drive.usePlugin(driveCachePlugin({ cache, namespace: "media" }));

3. File-backed cache (server, survives process restart)

import { createNodeFsStorage } from "@suaveplan/storage-file";
import { StorageCache, IdentityCodec } from "@suaveplan/cache-storage";

const cache = new StorageCache<Uint8Array>({
  storage: createNodeFsStorage({ rootDir: "/var/cache/drive", mkdirp: true }),
  codec: new IdentityCodec(),
});
await drive.usePlugin(driveCachePlugin({ cache }));

4. Redis-backed cache (server, cross-process shared)

import { createRedisCache } from "@suaveplan/cache-redis";

const cache = createRedisCache({ client, prefix: "drive-cache:" });
await drive.usePlugin(driveCachePlugin({ cache, namespace: "media" }));

5. Stale-while-revalidate via cache.wrap()

const handle = await drive.usePlugin(driveCachePlugin({ cache }));
const bytes = await handle.cache.wrap(
  "drive::clip.mov",
  () => drive.read("clip.mov"),
  { staleAge: 60_000 }
);

Recipe 5 illustrates why the plugin exposes handle.cache: stale-while-revalidate, lock-and-load, refresh-ahead, and every other policy cache.wrap() already supports stays one composition step away — without the plugin growing options for each.

Defaults

Option Default Rationale
namespace "drive" Single-drive consumers don't need to pick. Multi-drive consumers pass distinct strings and share one cache.
invalidateOnWrite true Cached bytes go stale the moment storage accepts new bytes. Post-success deletion is the safe default.
invalidateOnDelete true Symmetric with invalidateOnWrite.
maxEntryBytes 16 * 1024 * 1024 (16 MiB) Matches drive-mesh-protocol's MAX_FRAME_BYTES. Empirically safe in every consumer already running drive. Bypass via a custom shouldCache for large-blob workflows.
shouldCache bytes.byteLength <= maxEntryBytes The byte-ceiling check is the only failure mode for the default predicate.

Out of scope

  • Caching Drive.stat / Drive.list results.
  • Range / streaming reads (Drive.createReadStream) — bypass middleware by design.
  • Per-tenant key partitioning (keyFn(ctx) extension).
  • Built-in wrap() integration — consumers compose via handle.cache.wrap(...).

See the design proposal for the full design rationale.