diff --git a/skills/design-patterns/references/architecture-patterns.md b/skills/design-patterns/references/architecture-patterns.md index 8c6841b..f98eb6f 100644 --- a/skills/design-patterns/references/architecture-patterns.md +++ b/skills/design-patterns/references/architecture-patterns.md @@ -58,10 +58,12 @@ src/ ```typescript // app/api/users/route.ts — Controller (THIN) import { UserService } from "@/services/user.service"; +import { CreateUserSchema } from "@/types/user.types"; export async function POST(req: Request) { const body = await req.json(); - const result = await UserService.create(body); + const validated = CreateUserSchema.parse(body); // ← validate before passing to service + const result = await UserService.create(validated); return Response.json(result, { status: 201 }); } diff --git a/skills/design-patterns/references/code-patterns.md b/skills/design-patterns/references/code-patterns.md index 4aedf40..b919c68 100644 --- a/skills/design-patterns/references/code-patterns.md +++ b/skills/design-patterns/references/code-patterns.md @@ -314,13 +314,19 @@ function withAuth(next: Middleware): Middleware { }; } -function withRateLimit(limit: number, next: Middleware): Middleware { - const counts = new Map(); +function withRateLimit(limit: number, windowMs: number, next: Middleware): Middleware { + const windows = new Map(); return async (req) => { const ip = req.headers.get("x-forwarded-for") ?? "unknown"; - const current = counts.get(ip) ?? 0; - if (current >= limit) return new Response("Too Many Requests", { status: 429 }); - counts.set(ip, current + 1); + const now = Date.now(); + const w = windows.get(ip); + if (!w || w.resetAt <= now) { + windows.set(ip, { count: 1, resetAt: now + windowMs }); + } else if (w.count >= limit) { + return new Response("Too Many Requests", { status: 429 }); + } else { + w.count++; + } return next(req); }; } diff --git a/skills/design-patterns/references/framework-catalog.md b/skills/design-patterns/references/framework-catalog.md index cc78c01..a5ae08c 100644 --- a/skills/design-patterns/references/framework-catalog.md +++ b/skills/design-patterns/references/framework-catalog.md @@ -57,10 +57,11 @@ src/ // app/actions/user.actions.ts "use server" import { UserService } from "@/services/user.service"; +import { CreateUserSchema } from "@/types/user.types"; import { revalidatePath } from "next/cache"; export async function createUser(formData: FormData) { - const data = Object.fromEntries(formData); + const data = CreateUserSchema.parse(Object.fromEntries(formData)); // ← validate first await UserService.create(data); revalidatePath("/dashboard/users"); } diff --git a/skills/design-patterns/references/testing-patterns.md b/skills/design-patterns/references/testing-patterns.md index 9fe6f13..69c6afd 100644 --- a/skills/design-patterns/references/testing-patterns.md +++ b/skills/design-patterns/references/testing-patterns.md @@ -31,7 +31,8 @@ describe("NotificationFactory", () => { }); it("throws for unknown type", () => { - expect(() => createNotification("pigeon" as any)).toThrow(); + // @ts-expect-error — testing runtime rejection of an invalid type + expect(() => createNotification("pigeon")).toThrow(); }); }); ``` @@ -172,7 +173,7 @@ describe("Middleware Decorators", () => { }); it("withRateLimit blocks after limit", async () => { - const handler = withRateLimit(2, async () => new Response("OK")); + const handler = withRateLimit(2, 60_000, async () => new Response("OK")); const req = new Request("http://test.com"); await handler(req); // 1 await handler(req); // 2