first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
# `caching-strategies/router.ts`
## Purpose
`Router` evaluates incoming `Request`s against a list of registered routes
in order and dispatches the first match to its strategy. The router is the
glue between the SW `fetch` event and the package's caching strategies. It
owns the default `Cache` and `fetcher` references so individual strategies
stay free of global access — strategies receive their `StrategyContext`
through the router and need not touch `caches`, `fetch`, or `Date.now`.
## Features
- HTTP-method filtering (`GET`, `POST`, `PUT`, …) with case-insensitive
comparison.
- Three pattern types: `RegExp`, substring `string`, and predicate function
(`(Request) => boolean`).
- Per-route cache-name override so different routes can write to isolated
caches without re-instantiating the router.
- Fall-through to `fetcher(request)` for unmatched requests — the router is
always safe to `respondWith()` because every request gets a response.
- OTel `pwa_cache_strategy_ms` histogram recording strategy latency.
## Usage: Basic
```ts
import {
Router,
cacheFirst,
networkFirst,
staleWhileRevalidate,
} from "@suaveplan/pwa";
const router = new Router({ cacheName: "v1" });
router.registerRoute({
pattern: /\.(png|jpg|webp)$/,
strategy: cacheFirst({ ttlMs: 86_400_000 }),
});
router.registerRoute({
pattern: /\/api\/static\//,
strategy: staleWhileRevalidate(),
});
router.registerRoute({
method: "GET",
pattern: /\/api\//,
strategy: networkFirst({ ttlMs: 30_000 }),
});
self.addEventListener("fetch", (event) =>
event.respondWith(router.handle(event.request))
);
```
## Usage: Advanced
Per-route cache + predicate matching:
```ts
import { Router, cacheOnly } from "@suaveplan/pwa";
const router = new Router();
router.registerRoute({
method: "GET",
pattern: (req) => req.url.startsWith("/offline-fallback/"),
strategy: cacheOnly(),
cacheName: "offline-fallbacks",
});
```
## API Reference
- `new Router(options?: RouterOptions)`.
- `registerRoute(route: Route): void`.
- `handle(request: Request): Promise<Response>`.
- `size: number`.
- `Route` — `{ method?, pattern, strategy, cacheName? }`.
- `RouterOptions` — `{ cacheStorage?, cacheName?, fetcher?, clock?, observability? }`.
## Implementation Notes
The router opens caches lazily via `cacheStorage.open()` on every `handle()`
call. The platform `CacheStorage.open()` is idempotent and returns the same
underlying `Cache` instance for the same name, so the per-call open is a
trivial pointer lookup rather than a heavy initialisation. The fall-through
path bypasses both the cache open and the strategy invocation entirely, so
unmatched requests pay no per-request cost beyond the linear route scan.
For applications with hundreds of routes, the scan can be optimised by the
caller via predicate matchers that group routes by URL prefix.