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,51 @@
# context/audit-context
## Purpose
Builds the per-request `AuditContext` handle the audit middleware attaches to `c.set("audit", context)` before invoking `next()`. Handlers call `c.get("audit").log({...})` to fire explicit, semantic audit events alongside the implicit per-request audit that the middleware writes. This is the public extension point for application code — instead of importing `auditService` and threading it through every route, handlers reach into the Hono context.
The two-method surface (`log` + `getLogger`) is intentionally small so handler code stays focused on the event payload rather than the plumbing. The same underlying `AuditLogger` instance is reused across every request — there is one logger per app, not one per request — so chain-integrity links hold across handler-level and middleware-level events.
## Features
- Stable two-method surface (`log`, `getLogger`).
- Hono `ContextVariableMap` augmentation declared in `./context-augmentation.ts` so `c.get("audit")` types as `AuditContext` (not `unknown`).
- Zero per-request allocation beyond the lightweight wrapper object (no closures over the request, so the object can be cached if needed).
## Usage: Basic
```ts
// In a route handler:
app.post("/users/:id/delete", async (c) => {
const audit = c.get("audit");
await audit.log({
type: "user.deleted",
severity: "warning",
actor: { userId: c.get("user")?.id },
target: { type: "user", id: c.req.param("id") },
});
return c.json({ deleted: true });
});
```
## Usage: Advanced
```ts
// Reach the underlying logger for advanced ops:
app.get("/admin/audit-chain/verify", async (c) => {
const logger = c.get("audit").getLogger();
const events = await logger.query({ limit: 1000, order: "asc" });
const verdict = await logger.verifyChainIntegrity(events);
return c.json(verdict);
});
```
## API Reference
- `createAuditContext(deps: { logger: AuditLogger }): AuditContext`
- `log(event)` → `Promise<AuditEvent>`
- `getLogger()` → `AuditLogger`
## Implementation Notes
`AuditContext` does not carry the request, the principal, or the tenant. Handlers fill `actor.userId`, `actor.sessionId`, `tenantId`, and so on from `c.get("user")` / `c.get("tenant")` themselves. The middleware's per-request event populates those fields automatically; handler-level events stay explicit because the handler knows the semantic of the event (which user is the actor versus the target) better than the middleware does.

View File

@@ -0,0 +1,58 @@
# context/context-augmentation
## Purpose
TypeScript module augmentation that adds the `audit: AuditContext` slot to Hono's `ContextVariableMap`. Without it, `c.get("audit")` types as `unknown` and every handler call site needs an `as AuditContext` cast — defeating the point of the per-request helper. Importing the package barrel of `@suaveplan/api-plugin-audit` pulls this file in as a side effect, so handlers and downstream middlewares see the typed slot automatically.
The augmentation is intentionally narrow: it declares only `audit`. The `user`, `tenant`, and `requestId` slots that the middleware reads are owned by sibling plugins (`@suaveplan/api-plugin-auth`, `@suaveplan/api-tenant`, `@suaveplan/api`). TypeScript merges declarations across modules, so coexistence is safe — every plugin contributes its own slot to the same map.
## Features
- Declares `audit: AuditContext` on Hono's `ContextVariableMap`.
- Zero runtime cost — pure TypeScript.
- Side-effect imported by the package barrel.
## Usage: Basic
```ts
// Anywhere in the consuming app:
import "@suaveplan/api-plugin-audit";
app.post("/things", async (c) => {
const audit = c.get("audit"); // typed AuditContext
await audit.log({ type: "thing.created", severity: "info", actor: {} });
return c.json({ ok: true });
});
```
## Usage: Advanced
```ts
// In a handler that combines `user` (auth plugin) and `audit`:
import "@suaveplan/api-plugin-auth";
import "@suaveplan/api-plugin-audit";
app.delete("/vaults/:id", async (c) => {
const principal = c.get("user");
const audit = c.get("audit");
if (principal === null) return c.text("unauthenticated", 401);
await audit.log({
type: "vault.deleted",
severity: "warning",
actor: { userId: principal.id },
target: { type: "vault", id: c.req.param("id") },
});
return c.json({ deleted: true });
});
```
## API Reference
This file contributes one TypeScript `declare module "hono"` block adding:
- `audit: AuditContext` on `interface ContextVariableMap`.
## Implementation Notes
The augmentation declares `audit` as a non-optional slot. The middleware always sets it before calling `next()` (even for excluded paths — the slot is populated so handlers that opt into auditing on a normally-excluded path can still call `c.get("audit").log(...)`). If you mount the plugin AFTER user handlers in your middleware chain, `c.get("audit")` will be `undefined` at runtime even though TypeScript types it as `AuditContext`. The recommended fix is to register the audit plugin first; the `definePlugin` priority field can enforce this.