diff --git a/.nvmrc b/.nvmrc index 2bd5a0a..6f4247a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22 +26 diff --git a/AGENTS.md b/AGENTS.md index 4af3007..cffc943 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,8 @@ # AGENTS.md — WebDevBlogsite Living Hub -**Project:** WebDevBlogsite — `/home/hermes/Projects/WebDevBlogsite` · Next.js 16.3.3 · Bun 1.3.10 · Drizzle 0.45.2 · LibSQL/Turso · NextAuth 4.24.15 +**Project:** WebDevBlogsite — `/home/hermes/Projects/WebDevBlogsite` · Next.js 16.3.4 · Bun 1.3.10 · Drizzle 0.45.2 · LibSQL 0.18.0/Turso · NextAuth 4.24.15 · Vitest 5.0.0 **Branch contract:** `main` is the modernized line (stale `feature/phase1-baseline`/`safety/phase1-baseline` deleted 2026-08-28, worktree `.worktrees/t_bf5e500e` removed). Work on new branches off `main`. -**Last verified:** 2026-08-28 20:35 UTC — `main@06350d5` all 4 CI jobs SUCCESS (`ci`/`sast`/`secrets-scan`/`dependency-scan`, run 33208912835) · gates `tsc 0` `eslint 0` `prettier pass` `vitest 89/89` `next build 23/23`. +**Last verified:** 2026-10-15 (docs audit) on branch `chore/deps-wave2-webdev` — gates `tsc 0` `eslint 0` `prettier pass` `vitest 89/89`. `next build` NOT run during this audit (out of scope); CI runs it on `bun run build`. ## Table of Contents (entry point — keep updated) @@ -10,6 +10,7 @@ | ------------------ | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | Handoff / scores | `/home/hermes/PROJECT_STATUS.md:1` (mirrors `/home/jevonx/PROJECT_STATUS.md:1`, `/tmp/PROJECT_STATUS.md:1`) | 12-repo snapshot, 0–100 scores, TDD gates, T1–T12, evidence | | Modernization TODO | `./MODERNIZATION_TODO.md:1` | CSP, IP trust, RL store, query projection, serverActions — checklist | +| Plan (historical) | `./docs/PLAN.md:1` | Superseded 2026-08-25 plan (Cloudinary + publish API); read banner | | Rate limit | `./docs/RATE_LIMIT.md:1` | In-memory Map vs Upstash, rightmost-XFF, cost | | CI fix | `./docs/CI_FIX.md:1` | Zod dummy env + `trivy@v0.36.0` root cause | | Vuln fix | `./docs/VULN_FIX.md:1` | `next`/`drizzle` 13 HIGH Honest fix (`06350d5`) | @@ -25,6 +26,14 @@ - [2026-08-28T20:00:00Z] Always apply Dependabot fixes by adopting suggestions, then close the PR — never merge. (canonical in `./agents.md:1` + `~/.opencode/agents.md:1`; log `remember ` here with timestamp) +## Gotchas (read before editing code or docs) + +- **`src/proxy.ts` is correct — do NOT rename it to `middleware.ts`.** Next.js 16 deprecated the `middleware.ts` convention in favor of `proxy.ts` exporting `proxy()`. Docs written before 2026-08-25 (e.g. `docs/PLAN.md`, `docs/ASSESSMENT.md` item #1, `MODERNIZATION_TODO.md` QoL row) still describe the old name — those are historical. +- **Bun is canonical** (`packageManager: bun@1.3.10`, scripts like `bun run db:migrate`). `bun` is not installed on every host; `npx`/`./node_modules/.bin/*` equivalents work (`vitest run`, `tsc --noEmit`, `eslint .`, `prettier --check .`). +- **Env file is `.env`** (gitignored). `README.md` historically said `.env.local` — both are gitignored; either works, `.env.example` is the template. +- **`src/lib/env.ts` only falls back in `NODE_ENV=development|test`.** CI `next build` runs production mode and needs real/dummy env (see `docs/CI_FIX.md`). +- **`MODERNIZATION_TODO.md` and `docs/PLAN.md` are point-in-time snapshots** — several items are already resolved (prettier config added, `@auth/core` removed, XFF rightmost fix). Verify against source before acting on them. + ## How to Work Here (TDD + Orchestrator — 5 steps) 1. **RED before GREEN:** Define acceptance criteria + failing check first — no code without pass criteria. @@ -37,7 +46,7 @@ > Lean by design — details in `PROJECT_STATUS` + `docs/*`, not duplicated here. -- **Status:** `main` green and push-safe — see `/home/hermes/PROJECT_STATUS.md:1` §Current + `./docs/VULN_FIX.md:1` / `./docs/CI_FIX.md:1`. +- **Status:** gates green on the working branch (`tsc 0` `eslint 0` `prettier pass` `vitest 89/89`, verified 2026-10-15); `main` CI status last recorded in `PROJECT_STATUS` — see `/home/hermes/PROJECT_STATUS.md:1` §Current + `./docs/VULN_FIX.md:1` / `./docs/CI_FIX.md:1`. Note: `chore/deps-wave2-webdev` (vitest 5.0.0, Node 26) is the current working line. - **Blockers:** P0 CSP hydration + P0 rate-limit deferred are in `./MODERNIZATION_TODO.md:1` → tracked as T1–T12 in `PROJECT_STATUS`. - **Dependabot/other repos:** See `PROJECT_STATUS` evidence (2026-08-28 batch: 10 adopted, 2 reverted; 7 PRs pushed 19:46 UTC). diff --git a/MODERNIZATION_TODO.md b/MODERNIZATION_TODO.md index f308acd..fb3136f 100644 --- a/MODERNIZATION_TODO.md +++ b/MODERNIZATION_TODO.md @@ -1,5 +1,10 @@ # WebDevBlogsite — Modernization Todo List +> ⚠️ **HISTORICAL (audit date 2026-08-24) — PARTIALLY RESOLVED.** Changes verified against current source: +> `.prettierrc` + `format`/`format:check` scripts exist; `@auth/core` no longer in `package.json` (NextAuth v4 only); +> `getClientIp` now takes the **rightmost** `X-Forwarded-For` hop (`src/proxy.ts`); `@types/node@26` matches `engines.node 26.x`. +> Other items below were never resolved — verify each against current source before acting. See `AGENTS.md` §Gotchas. + **Project:** Next.js 16.1.6 App Router blog/portfolio · Bun 1.3.10 · Drizzle ORM · LibSQL/Turso · NextAuth v4 **Audit date:** 2026-08-24 **Re-audited (Phase 2):** `main` @ `fd0907235580255d68a564f3ec277a6c572f602b` — see `/tmp/second-audit-WebDev.md` and `/tmp/baseline-WebDev.md`. diff --git a/README.md b/README.md index acbacfe..c83f492 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,20 @@ A production Next.js application combining a public portfolio with a database-ba - GitHub OAuth — single-admin model via `ADMIN_GITHUB_ID` - SEO: metadata, sitemap, robots.txt, and RSS feed - Markdown rendering with Shiki syntax highlighting and table of contents -- Security: rate limiting, CSP headers, and input validation via middleware +- Security: rate limiting, CSP headers, and input validation via `src/proxy.ts` ## Tech stack | Layer | Technology | | ---------- | --------------------------------- | -| Framework | Next.js `16.3.3` (App Router) | +| Framework | Next.js `16.3.4` (App Router) | | Language | TypeScript `5.9.3` | -| Styles | Tailwind CSS `4.2.1` | -| ORM | Drizzle ORM `0.45.1` | -| Database | LibSQL / Turso | -| Auth | NextAuth `4.24.13` + GitHub OAuth | -| Validation | Zod `4.3.6` | +| Styles | Tailwind CSS `4.3.3` | +| ORM | Drizzle ORM `0.45.2` | +| Database | LibSQL `0.18.0` / Turso | +| Auth | NextAuth `4.24.15` + GitHub OAuth | +| Validation | Zod `4.5.4` | +| Testing | Vitest `5.0.0` | | Runtime | Bun `1.3.10` | | Hosting | Vercel | @@ -41,13 +42,11 @@ bun install ### 2. Create your local env file ```bash -# Mac/Linux -cp .env.example .env.local - -# PowerShell -Copy-Item .env.example .env.local +cp .env.example .env ``` +(Next.js loads `.env` automatically. `.env.local` is gitignored too if you prefer it.) + ### 3. Fill in environment variables ```env @@ -113,19 +112,22 @@ public/ # Static assets ### Key routes -| Route | Description | -| ------------------------- | --------------------------- | -| `/` | Homepage | -| `/blog` | Blog index | -| `/blog/[slug]` | Blog post | -| `/projects` | Projects index | -| `/projects/[slug]` | Project detail | -| `/about` | About page | -| `/admin` | Admin dashboard (protected) | -| `/admin/blog/new` | Create post (protected) | -| `/admin/blog/[slug]/edit` | Edit post (protected) | -| `/feed.xml` | RSS feed | -| `/sitemap.xml` | Sitemap | +| Route | Description | +| ------------------------- | ---------------------------------------------------- | +| `/` | Homepage | +| `/blog` | Blog index | +| `/blog/[slug]` | Blog post | +| `/projects` | Projects index | +| `/projects/[slug]` | Project detail | +| `/about` | About page | +| `/admin` | Admin dashboard (protected) | +| `/admin/blog/new` | Create post (protected) | +| `/admin/blog/[slug]/edit` | Edit post (protected) | +| `/feed.xml` | RSS feed | +| `/sitemap.xml` | Sitemap | +| `/api/publish` | Programmatic post creation (bearer token, protected) | + +> **Note:** In Next.js 16 the `middleware.ts` file convention is deprecated in favor of `proxy.ts` exporting `proxy()`. This repo uses `src/proxy.ts` (rate limiting + CSP nonce). Do not rename it back — see `docs/RATE_LIMIT.md`. ## Authentication @@ -149,20 +151,22 @@ Auth files: Single `posts` table managed through Drizzle ORM and LibSQL. -| Field | Type | -| ------------ | -------------------- | -| `id` | integer, primary key | -| `title` | text | -| `slug` | text, unique | -| `content` | text (markdown) | -| `excerpt` | text | -| `category` | text | -| `coverImage` | text, nullable | -| `published` | boolean | -| `createdAt` | text (ISO 8601) | -| `updatedAt` | text (ISO 8601) | - -Schema: `drizzle/schema.ts` +| Field | Type | +| ------------- | ----------------------------------------- | +| `id` | integer, primary key | +| `title` | text | +| `slug` | text, unique | +| `content` | text (markdown) | +| `excerpt` | text | +| `category` | text | +| `coverImage` | text, nullable | +| `published` | boolean | +| `toc` | json text (precomputed table of contents) | +| `readingTime` | integer (minutes) | +| `createdAt` | text (ISO 8601) | +| `updatedAt` | text (ISO 8601) | + +Schema: `drizzle/schema.ts` (migrations `0000_initial`, `0001_post_query_indexes`, `0002_post_toc_reading_time` in `drizzle/migrations/`) Migrations run automatically on `bun run dev` and `bun run build`. Applied migrations are tracked in `__migrations` so re-runs are safe. @@ -183,12 +187,22 @@ Projects are statically defined in `content/projects.ts`. This is intentional ### Database -| Variable | Description | -| --------------------- | ------------------------------------------------------ | -| `DATABASE_URL` | SQLite file path or hosted LibSQL URL | -| `DATABASE_AUTH_TOKEN` | Required for hosted Turso with token auth | -| `TURSO_DATABASE_URL` | Vercel Turso integration variable (supported natively) | -| `TURSO_AUTH_TOKEN` | Vercel Turso integration token (supported natively) | +| Variable | Description | +| --------------------- | ------------------------------------------------------- | +| `DATABASE_URL` | SQLite file path or hosted LibSQL URL | +| `DATABASE_AUTH_TOKEN` | Required for hosted Turso with token auth | +| `TURSO_DATABASE_URL` | Vercel Turso integration variable (supported natively) | +| `TURSO_AUTH_TOKEN` | Vercel Turso integration token (supported natively) | +| `ALLOWED_IMAGE_HOSTS` | Comma-separated allowlist for `next/image` remote hosts | + +### Media & API + +| Variable | Description | +| ----------------------- | -------------------------------------------- | +| `CLOUDINARY_CLOUD_NAME` | Cloudinary cloud name (image CDN) | +| `CLOUDINARY_API_KEY` | Cloudinary API key | +| `CLOUDINARY_API_SECRET` | Cloudinary API secret | +| `PUBLISH_TOKEN` | Bearer token required by `POST /api/publish` | ### App @@ -234,9 +248,10 @@ DATABASE_URL="libsql://your-db.turso.io" DATABASE_AUTH_TOKEN="your-token" bun ru ## Local development notes - `local.db` is created automatically and is gitignored -- `.env.local` is gitignored — never commit real secrets +- `.env` / `.env.local` are gitignored — never commit real secrets - Development mode provides fallback env values so the app starts without all variables set - Production requires all variables to be present; missing values throw a validation error at startup +- Rate limiting + CSP nonce run from `src/proxy.ts` (Next.js 16 proxy convention) ## Git remotes @@ -260,6 +275,10 @@ git log origin/main --oneline -1 ## Extra docs +- [`docs/INDEX.md`](docs/INDEX.md) — full documentation index - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — detailed codebase walkthrough - [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md) — full deployment and auth/database setup reference - [`docs/ASSESSMENT.md`](docs/ASSESSMENT.md) — project assessment checklist with prioritized improvements +- [`docs/RATE_LIMIT.md`](docs/RATE_LIMIT.md) — rate limiting design and Upstash proposal +- [`docs/PLAN.md`](docs/PLAN.md) — historical implementation plan (superseded; see banner) +- [`MODERNIZATION_TODO.md`](MODERNIZATION_TODO.md) — point-in-time audit checklist (historical; see banner) diff --git a/agents.md b/agents.md index d75c838..3eff044 100644 --- a/agents.md +++ b/agents.md @@ -1,7 +1,7 @@ # agents.md — WebDevBlogsite (local, per-repo) **Mirror of AGENTS.md (uppercase) — this lowercase file is the canonical per instruction.** -**Last updated:** 2026-08-28 20:00 UTC +**Last updated:** 2026-10-15 (docs audit — see AGENTS.md §Gotchas for the `src/proxy.ts` note) ## Persistent Rules @@ -11,6 +11,8 @@ - Canonical hub: ./AGENTS.md - Project status: /home/hermes/PROJECT_STATUS.md +- Docs index: ./docs/INDEX.md - Rate limit: ./docs/RATE_LIMIT.md - CI fix: ./docs/CI_FIX.md - Vuln fix: ./docs/VULN_FIX.md +- Plan (historical): ./docs/PLAN.md diff --git a/bun.lock b/bun.lock index cd3dd6f..8abdade 100644 --- a/bun.lock +++ b/bun.lock @@ -37,7 +37,7 @@ "prettier-plugin-tailwindcss": "0.8.1", "tailwindcss": "4.3.3", "typescript": "5.9.3", - "vitest": "4.1.11", + "vitest": "5.0.0", }, }, }, @@ -368,8 +368,6 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], - "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@swc/helpers": ["@swc/helpers@0.5.23", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw=="], "@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="], @@ -502,19 +500,9 @@ "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.12.2", "", { "os": "win32", "cpu": "x64" }, "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA=="], - "@vitest/expect": ["@vitest/expect@4.1.11", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw=="], - - "@vitest/mocker": ["@vitest/mocker@4.1.11", "", { "dependencies": { "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ=="], - - "@vitest/pretty-format": ["@vitest/pretty-format@4.1.11", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw=="], - - "@vitest/runner": ["@vitest/runner@4.1.11", "", { "dependencies": { "@vitest/utils": "4.1.11", "pathe": "^2.0.3" } }, "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw=="], - - "@vitest/snapshot": ["@vitest/snapshot@4.1.11", "", { "dependencies": { "@vitest/pretty-format": "4.1.11", "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog=="], + "@vitest/mocker": ["@vitest/mocker@5.0.0", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "@vitest/spy": "5.0.0", "estree-walker": "^3.0.3", "magic-string": "^1.2.3" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA=="], - "@vitest/spy": ["@vitest/spy@4.1.11", "", {}, "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA=="], - - "@vitest/utils": ["@vitest/utils@4.1.11", "", { "dependencies": { "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ=="], + "@vitest/spy": ["@vitest/spy@5.0.0", "", {}, "sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g=="], "acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], @@ -956,7 +944,7 @@ "lucide-react": ["lucide-react@0.577.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A=="], - "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "magic-string": ["magic-string@1.2.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g=="], "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], @@ -1122,8 +1110,6 @@ "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], @@ -1282,14 +1268,12 @@ "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + "tinybench": ["tinybench@6.1.4", "", {}, "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ=="], "tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], - "tinyrainbow": ["tinyrainbow@3.1.1", "", {}, "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw=="], - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], @@ -1350,7 +1334,7 @@ "vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="], - "vitest": ["vitest@4.1.11", "", { "dependencies": { "@vitest/expect": "4.1.11", "@vitest/mocker": "4.1.11", "@vitest/pretty-format": "4.1.11", "@vitest/runner": "4.1.11", "@vitest/snapshot": "4.1.11", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.11", "@vitest/browser-preview": "4.1.11", "@vitest/browser-webdriverio": "4.1.11", "@vitest/coverage-istanbul": "4.1.11", "@vitest/coverage-v8": "4.1.11", "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw=="], + "vitest": ["vitest@5.0.0", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/mocker": "5.0.0", "chai": "^6.2.2", "es-module-lexer": "^2.3.2", "expect-type": "^1.4.0", "magic-string": "^1.2.3", "obug": "^2.1.4", "picomatch": "^4.0.7", "std-env": "^4.2.0", "tinybench": "6.1.4", "tinyexec": "1.3.0", "tinyglobby": "^0.2.17", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "5.0.0", "@vitest/browser-preview": "5.0.0", "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", "@vitest/coverage-istanbul": "5.0.0", "@vitest/coverage-v8": "5.0.0", "@vitest/ui": "5.0.0", "happy-dom": "*", "jsdom": "*", "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -1394,6 +1378,8 @@ "@next/eslint-plugin-next/@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + "@tailwindcss/node/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], diff --git a/docs/INDEX.md b/docs/INDEX.md new file mode 100644 index 0000000..e19f259 --- /dev/null +++ b/docs/INDEX.md @@ -0,0 +1,26 @@ +# Documentation Index + +One line per document. Keep this in sync when adding/removing docs. + +## Root docs + +| Doc | Path | Summary | +| --------------------- | -------------------------- | -------------------------------------------------------------------------- | +| README | `../README.md` | Project overview, quick start, commands, routes, env vars, deployment | +| Agent hub (uppercase) | `../AGENTS.md` | Canonical agent entry point: TOC, rules, workflow, gates, gotchas | +| Agent mirror (lower) | `../agents.md` | Per-repo mirror of AGENTS.md (declared canonical for per-repo rules) | +| Modernization TODO | `../MODERNIZATION_TODO.md` | **Historical** 2026-08-24 audit; several items resolved since — see banner | + +## docs/ + +| Doc | Path | Summary | +| ------------ | ---------------------- | -------------------------------------------------------------------------- | +| Architecture | `ARCHITECTURE.md` | Layers, request flows, file guide, security features, known-issues history | +| Assessment | `ASSESSMENT.md` | 2026-06-18 30-item assessment checklist (most items completed) | +| CI fix | `CI_FIX.md` | 2026-08-28 CI stabilization: Zod dummy env for build + trivy `v0.36.0` | +| Deletion log | `DELETION_LOG.md` | 2026-04-11 and 2026-06-18 file/dependency deletion records | +| Deployment | `DEPLOYMENT.md` | GitHub → Vercel deploy guide, env vars, Turso, post-deploy verification | +| Loop agent | `LOOP-AGENT-PROMPT.md` | Prompt template driving the assessment task queue through a loop agent | +| Plan | `PLAN.md` | **Historical** 2026-08-25 plan (Cloudinary + publish API); see banner | +| Rate limit | `RATE_LIMIT.md` | Rate-limit design (`src/proxy.ts`, rightmost XFF) + Upstash proposal | +| Vuln fix | `VULN_FIX.md` | 2026-08-28 `next`/`drizzle-orm` HIGH CVE fix record (commit `06350d5`) | diff --git a/docs/PLAN.md b/docs/PLAN.md index f9c92fc..a2678cf 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -1,5 +1,10 @@ # Implementation Plan — Repository Audit & Roadmap +> ⚠️ **HISTORICAL (2026-08-25) — SUPERSEDED.** This plan predates the Next.js 16 `proxy.ts` +> convention and the Cloudinary/publish work it describes is now committed. The "Dead Middleware" +> finding below is **resolved**: `src/proxy.ts` exporting `proxy()` is the correct Next.js 16 form +> (Next deprecated `middleware.ts`). Kept for context; verify against source before acting. + **Date:** 2026-08-25 **Project:** WebDevBlogsite (Next.js 16 App Router, TypeScript, Drizzle ORM, LibSQL/Turso) **Status:** Active development — Cloudinary integration + Publish API in progress diff --git a/package.json b/package.json index f68ff0d..f8ad2d8 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "packageManager": "bun@1.3.10", "engines": { - "node": "22.x", + "node": "26.x", "bun": "1.3.10" }, "scripts": { @@ -54,7 +54,7 @@ "prettier-plugin-tailwindcss": "0.8.1", "tailwindcss": "4.3.3", "typescript": "5.9.3", - "vitest": "4.1.11" + "vitest": "5.0.0" }, "lint-staged": { "*.{ts,tsx,js,jsx,mjs,cjs,json,md,mdx,yml,yaml}": [