first commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# `middleware-recommendations`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`MIDDLEWARE_RECOMMENDATIONS` is a frozen, typed bundle of recommended rate-limit and audit configuration for each documented site route group. It exists so host applications wiring the site plugin together with `@suaveplan/api-plugin-rate-limit` and `@suaveplan/api-plugin-audit` can derive their middleware configuration from a single canonical source instead of re-deriving the trade-offs from first principles.
|
||||
|
||||
The site plugin deliberately ships zero runtime dependencies on the rate-limit and audit plugins — composition stays in the host via the Hono `app.use(prefix, middleware)` pattern. This module exposes the per-route-group knobs and the rationale behind each number; consumers translate them into the input shape the middleware plugins expect.
|
||||
|
||||
## Features
|
||||
|
||||
- Three canonical route groups: `public:resolve`, `public:read`, `private:write` — one entry per group.
|
||||
- Each entry carries a `RecommendedRateLimit` (`limit`, `windowMs`, `pathPattern`) and a `SiteAuditCategory` (`system.lookup`, `data.read`, `data.mutation`).
|
||||
- A human-readable `rationale` string explains why the numbers were chosen.
|
||||
- The bundle is `Object.freeze`'d at every level — callers can read but cannot mutate the shared bundle.
|
||||
- Pure data: zero runtime dependencies on `api-plugin-rate-limit` or `api-plugin-audit`; the types are local.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { MIDDLEWARE_RECOMMENDATIONS } from "@suaveplan/api-plugin-site";
|
||||
|
||||
const writes = MIDDLEWARE_RECOMMENDATIONS.find(
|
||||
(r) => r.routeGroup === "private:write"
|
||||
);
|
||||
// writes.rateLimit.limit === 30 requests / 60s
|
||||
// writes.auditCategory === "data.mutation"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Drive the rate-limit plugin's configuration directly from the recommendations:
|
||||
|
||||
```ts
|
||||
import {
|
||||
MIDDLEWARE_RECOMMENDATIONS,
|
||||
type SiteRouteGroup,
|
||||
} from "@suaveplan/api-plugin-site";
|
||||
import {
|
||||
createDefaultStorage,
|
||||
createRateLimitService,
|
||||
rateLimit,
|
||||
RateLimitPluginOptionsSchema,
|
||||
} from "@suaveplan/api-plugin-rate-limit";
|
||||
|
||||
const groups: SiteRouteGroup[] = [
|
||||
"public:resolve",
|
||||
"public:read",
|
||||
"private:write",
|
||||
];
|
||||
const config = RateLimitPluginOptionsSchema.parse({
|
||||
defaultLimit: 100,
|
||||
defaultWindowMs: 60_000,
|
||||
routes: MIDDLEWARE_RECOMMENDATIONS.filter((r) => groups.includes(r.routeGroup)).map(
|
||||
(r, i) => ({
|
||||
id: r.routeGroup,
|
||||
method: "ALL",
|
||||
pathPattern: r.rateLimit.pathPattern,
|
||||
limit: r.rateLimit.limit,
|
||||
windowMs: r.rateLimit.windowMs,
|
||||
priority: i,
|
||||
})
|
||||
),
|
||||
});
|
||||
const clock = () => Date.now();
|
||||
const service = createRateLimitService({
|
||||
config,
|
||||
storage: createDefaultStorage({ clock }),
|
||||
clock,
|
||||
logger: console,
|
||||
});
|
||||
|
||||
app.use("/sites/*", rateLimit({ service }));
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `MIDDLEWARE_RECOMMENDATIONS: readonly MiddlewareRecommendation[]` — frozen three-element array.
|
||||
- `MiddlewareRecommendation` — `{ routeGroup, rationale, rateLimit, auditCategory }`.
|
||||
- `SiteRouteGroup` — `"public:resolve" | "public:read" | "private:write"`.
|
||||
- `SiteAuditCategory` — `"data.read" | "data.mutation" | "system.lookup"`.
|
||||
- `RecommendedRateLimit` — `{ limit: number, windowMs: number, pathPattern: string }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Numbers reflect a public, multi-tenant SaaS profile. Internal-only deployments should override; the bundle is documentation, not policy.
|
||||
- `pathPattern` strings match the path-matcher syntax in `@suaveplan/api-plugin-rate-limit/path-matcher`.
|
||||
- The bundle is deeply frozen so callers can safely share the reference across module scopes without defensive cloning.
|
||||
- Consumers MAY skip the bundle entirely; it is offered for convenience and is not part of any side-effecting code path inside the site plugin.
|
||||
Reference in New Issue
Block a user