Migrate to VineXT: Auth + API Routes Verification#1048
Conversation
- Add vite.config.ts with VineXT plugin and custom font patch for Google Fonts not pre-exported by VineXT shim - Simplify next.config.ts by removing Sentry, MDX, and next-intl wrappers handled natively by VineXT - Update package.json scripts to use vinext CLI, add type:module, and add VineXT/Vite 7 dependencies - Convert MCP endpoint from Pages Router to App Router using Web Standard transport - Remove src/pages/ directory to prevent VineXT Pages Router handler from intercepting App Router API routes - Add /dist and /.vinext to .gitignore Verified: auth endpoints return 200 OK, vinext build completes, all 652 tests pass.
📝 WalkthroughWalkthroughThis PR migrates the project from Next.js Pages Router to App Router, removes Sentry/next-intl/MDX integrations, introduces Vite as the build system via vinext, updates React dependencies to ^19.2.4, and eliminates the custom Sentry error page. Configuration is simplified and modernized. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Handler as App Router<br/>Handler
participant Transport as WebStandard<br/>Transport
participant Server as MCP<br/>Server
participant Response as NextResponse
Client->>Handler: POST/GET/DELETE Request
Handler->>Transport: Initialize & pass request
Transport->>Server: Handle via transport
Server->>Transport: Process & return result
Transport->>Handler: Transform stream ready
Handler->>Response: Create NextResponse<br/>with stream
Response->>Client: Return streamed response
Handler->>Transport: Cleanup on completion
Handler->>Server: Shutdown
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/app/api/mcp/route.ts (2)
1393-1408:⚠️ Potential issue | 🟠 MajorMove auth/setup into the protected error path.
Line 1393 (
authenticateApiKey) and Line 1406 (createServer) run before thetryat Line 1408. If either throws (e.g., DB outage), this handler skips your JSON-RPC error response path.💡 Suggested fix
export async function POST(request: NextRequest) { const url = new URL(request.url); @@ - // Authenticate user if API key is provided - const authenticatedUser = await authenticateApiKey(apiKey); - - const serverOptions: ServerOptions = { authenticatedUser }; - if (categoriesParam) { - serverOptions.categories = categoriesParam.split(",").map((c) => c.trim()); - } - if (tagsParam) { - serverOptions.tags = tagsParam.split(",").map((t) => t.trim()); - } - if (usersParam) { - serverOptions.users = usersParam.split(",").map((u) => u.trim()); - } - - const server = createServer(serverOptions); + let server: ReturnType<typeof createServer> | null = null; + let transport: WebStandardStreamableHTTPServerTransport | null = null; try { - const transport = new WebStandardStreamableHTTPServerTransport({ + const authenticatedUser = await authenticateApiKey(apiKey); + const serverOptions: ServerOptions = { authenticatedUser }; + if (categoriesParam) serverOptions.categories = categoriesParam.split(",").map((c) => c.trim()); + if (tagsParam) serverOptions.tags = tagsParam.split(",").map((t) => t.trim()); + if (usersParam) serverOptions.users = usersParam.split(",").map((u) => u.trim()); + + server = createServer(serverOptions); + transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); @@ } catch (error) { + transport?.close(); + server?.close(); console.error("MCP error:", error); return NextResponse.json(Also applies to: 1447-1456
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/api/mcp/route.ts` around lines 1393 - 1408, Move the authentication and server setup into the protected try/catch so any exceptions (e.g., DB outage) are handled by the JSON-RPC error path: wrap the calls to authenticateApiKey(...) and createServer(...) (and the serverOptions construction that depends on categoriesParam/tagsParam/usersParam) inside the existing try block (the same protected error-handling scope used for request handling) so thrown errors are caught and converted to the JSON-RPC error response; apply the same change to the analogous setup at the later block that spans the code around createServer/auth there (the 1447-1456 area).
1383-1404:⚠️ Potential issue | 🟠 MajorAdd Zod schema validation with bounds for query parameters before parsing.
Lines 1383-1404 accept raw query strings without validation. Add a Zod schema that validates and bounds
categories,tags, andusersparameters (e.g., max array length, max item length) before thesplit(",")operations to prevent DoS attacks and enforce API constraints.const querySchema = z.object({ categories: z.string().max(1000).optional(), tags: z.string().max(1000).optional(), users: z.string().max(1000).optional(), api_key: z.string().optional(), }); const queryParams = querySchema.parse(Object.fromEntries(url.searchParams));Then validate and split:
if (queryParams.categories) { const categoryArray = queryParams.categories.split(",").map((c) => c.trim()); if (categoryArray.length > 50) throw new Error("Too many categories"); serverOptions.categories = categoryArray; }Per coding guidelines,
src/app/api/**/route.tsrequires Zod for request validation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/api/mcp/route.ts` around lines 1383 - 1404, Add Zod validation for the incoming query params before using categoriesParam/tagsParam/usersParam: define a z.object schema validating optional strings for categories, tags, users and api_key with max length bounds (e.g., 1000 chars) and parse Object.fromEntries(url.searchParams) into queryParams; replace the raw categoriesParam/tagsParam/usersParam usage with queryParams, split and trim into arrays, and enforce array size limits (e.g., max 50 items) before assigning to serverOptions.categories/tags/users; keep the existing api key extraction/authenticateApiKey flow but validate api_key via the schema as well.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 5: package.json sets "type":"module" but scripts/setup.js and
scripts/lint-mdx.js use CommonJS require() which will fail; fix by either
converting those two scripts to ESM (replace require/module.exports with
import/export and update any __dirname usage) or rename the files to
scripts/setup.cjs and scripts/lint-mdx.cjs so Node treats them as CommonJS;
update package.json npm script entries if you rename files to .cjs and verify
any child modules invoked by setup.js or lint-mdx.js are also ESM/CommonJS
compatible.
In `@vite.config.ts`:
- Around line 34-36: The current logic uses importRe and a single call to
code.match to capture only the first named import from "next/font/google" and
thus rewrites only one occurrence; update this to iterate over all matches (use
importRe with the global flag or use code.matchAll) and process each match so
every named import occurrence is rewritten. Specifically replace the
single-match usage of importRe/match with a loop over matches (e.g., for (const
m of code.matchAll(importRe)) { ... }) and apply the same rewriting logic used
at the rewrite site (the code that currently handles the single match around
where match is checked and where the rewrite near Line 61 happens) for each
found match. Ensure you reconstruct imports correctly for each match and return
the fully transformed code.
---
Outside diff comments:
In `@src/app/api/mcp/route.ts`:
- Around line 1393-1408: Move the authentication and server setup into the
protected try/catch so any exceptions (e.g., DB outage) are handled by the
JSON-RPC error path: wrap the calls to authenticateApiKey(...) and
createServer(...) (and the serverOptions construction that depends on
categoriesParam/tagsParam/usersParam) inside the existing try block (the same
protected error-handling scope used for request handling) so thrown errors are
caught and converted to the JSON-RPC error response; apply the same change to
the analogous setup at the later block that spans the code around
createServer/auth there (the 1447-1456 area).
- Around line 1383-1404: Add Zod validation for the incoming query params before
using categoriesParam/tagsParam/usersParam: define a z.object schema validating
optional strings for categories, tags, users and api_key with max length bounds
(e.g., 1000 chars) and parse Object.fromEntries(url.searchParams) into
queryParams; replace the raw categoriesParam/tagsParam/usersParam usage with
queryParams, split and trim into arrays, and enforce array size limits (e.g.,
max 50 items) before assigning to serverOptions.categories/tags/users; keep the
existing api key extraction/authenticateApiKey flow but validate api_key via the
schema as well.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
.gitignorenext.config.tspackage.jsonsrc/app/api/mcp/route.tssrc/pages/_error.tsxvite.config.ts
💤 Files with no reviewable changes (1)
- src/pages/_error.tsx
| "name": "prompts.chat-v2", | ||
| "version": "0.1.0", | ||
| "private": true, | ||
| "type": "module", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n package.jsonRepository: f/prompts.chat
Length of output: 5140
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json, re, pathlib
pkg = json.loads(pathlib.Path("package.json").read_text())
targets = set()
for name, cmd in pkg.get("scripts", {}).items():
for m in re.finditer(r'\bnode\s+([^\s]+\.js)\b', cmd):
targets.add(m.group(1))
if not targets:
print("No node-invoked .js scripts found.")
raise SystemExit(0)
for rel in sorted(targets):
path = pathlib.Path(rel)
print(f"\n== {rel} ==")
if not path.exists():
print("MISSING FILE")
continue
text = path.read_text(errors="ignore").splitlines()
found = False
for i, line in enumerate(text, 1):
if "require(" in line or "module.exports" in line or re.search(r'\bexports\.', line):
found = True
print(f"{i}: {line}")
if not found:
print("No CommonJS patterns found.")
PYRepository: f/prompts.chat
Length of output: 452
Convert scripts to ESM or rename to .cjs format.
Both scripts/setup.js and scripts/lint-mdx.js use CommonJS require() syntax, which is incompatible with the "type": "module" setting in package.json. When these scripts run via npm run setup and npm run lint:mdx, Node will interpret them as ESM modules, causing runtime failures.
Migrate to ESM imports (import statements) or rename files to .cjs extension to preserve CommonJS compatibility.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 5, package.json sets "type":"module" but
scripts/setup.js and scripts/lint-mdx.js use CommonJS require() which will fail;
fix by either converting those two scripts to ESM (replace
require/module.exports with import/export and update any __dirname usage) or
rename the files to scripts/setup.cjs and scripts/lint-mdx.cjs so Node treats
them as CommonJS; update package.json npm script entries if you rename files to
.cjs and verify any child modules invoked by setup.js or lint-mdx.js are also
ESM/CommonJS compatible.
| const importRe = /import\s*\{([^}]+)\}\s*from\s*['"]next\/font\/google['"]/; | ||
| const match = code.match(importRe); | ||
| if (!match) return null; |
There was a problem hiding this comment.
Patch currently rewrites only the first next/font/google named import.
Line 34/Line 35 uses a single match, so Line 61 rewrites only one import occurrence. Files with multiple next/font/google imports can still break for unlisted fonts.
💡 Suggested fix
- const importRe = /import\s*\{([^}]+)\}\s*from\s*['"]next\/font\/google['"]/;
- const match = code.match(importRe);
- if (!match) return null;
-
- // Parse import specifiers, handling "Name as Alias" syntax
- const specifiers = match[1].split(",").map((s) => s.trim()).filter(Boolean);
- const parsed = specifiers.map((spec) => {
- const parts = spec.split(/\s+as\s+/);
- return { name: parts[0].trim(), alias: (parts[1] || parts[0]).trim() };
- });
-
- const missing = parsed.filter((p) => !preExported.has(p.name));
- if (missing.length === 0) return null;
-
- // Keep pre-exported fonts as named imports, access others via default Proxy
- const kept = parsed.filter((p) => preExported.has(p.name));
- const keptClause = kept.length > 0
- ? `{ ${kept.map((p) => p.name === p.alias ? p.name : `${p.name} as ${p.alias}`).join(", ")} }`
- : "";
- const defaultClause = "__googleFonts";
- const importParts = [defaultClause, keptClause].filter(Boolean).join(", ");
-
- let replacement = `import ${importParts} from "next/font/google";\n`;
- for (const { name, alias } of missing) {
- replacement += `const ${alias} = __googleFonts.${name};\n`;
- }
-
- return code.replace(match[0], replacement);
+ const importRe = /import\s*\{([^}]+)\}\s*from\s*['"]next\/font\/google['"];?/g;
+ let patched = false;
+ const rewritten = code.replace(importRe, (_full, rawSpecifiers: string) => {
+ const specifiers = rawSpecifiers.split(",").map((s) => s.trim()).filter(Boolean);
+ const parsed = specifiers.map((spec) => {
+ const parts = spec.split(/\s+as\s+/);
+ return { name: parts[0].trim(), alias: (parts[1] || parts[0]).trim() };
+ });
+
+ const missing = parsed.filter((p) => !preExported.has(p.name));
+ if (missing.length === 0) return _full;
+
+ patched = true;
+ const kept = parsed.filter((p) => preExported.has(p.name));
+ const keptClause = kept.length > 0
+ ? `{ ${kept.map((p) => p.name === p.alias ? p.name : `${p.name} as ${p.alias}`).join(", ")} }`
+ : "";
+ const defaultClause = "__googleFonts";
+ const importParts = [defaultClause, keptClause].filter(Boolean).join(", ");
+
+ let replacement = `import ${importParts} from "next/font/google";\n`;
+ for (const { name, alias } of missing) {
+ replacement += `const ${alias} = __googleFonts.${name};\n`;
+ }
+ return replacement;
+ });
+
+ return patched ? rewritten : null;Also applies to: 61-61
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@vite.config.ts` around lines 34 - 36, The current logic uses importRe and a
single call to code.match to capture only the first named import from
"next/font/google" and thus rewrites only one occurrence; update this to iterate
over all matches (use importRe with the global flag or use code.matchAll) and
process each match so every named import occurrence is rewritten. Specifically
replace the single-match usage of importRe/match with a loop over matches (e.g.,
for (const m of code.matchAll(importRe)) { ... }) and apply the same rewriting
logic used at the rewrite site (the code that currently handles the single match
around where match is checked and where the rewrite near Line 61 happens) for
each found match. Ensure you reconstruct imports correctly for each match and
return the fully transformed code.
Summary
vite.config.tswith VineXT plugin and a customvinextFontPatchVite plugin that rewrites ESM named imports for Google Fonts not in VineXT's pre-exported set to use the Proxy default exportnext.config.tsby removing Sentry, MDX, and next-intl wrappers (handled natively by VineXT)package.jsonscripts to usevinextCLI (dev/build/start), add"type": "module", and add VineXT + Vite 7 dependenciessrc/pages/api/mcp.ts) to App Router (src/app/api/mcp/route.ts) usingWebStandardStreamableHTTPServerTransportsrc/pages/directory to prevent VineXT Pages Router handler from intercepting App Router API routes/distand/.vinextto.gitignoreTest plan
/api/auth/providers) returns 200 OK with GitHub, Google, Apple/api/auth/csrf) returns 200 OK with valid token/api/auth/session) returns 200 OK/api/health) returns 503 (expected without DB)vinext buildcompletes successfullynpm run lintpasses with no new issuesSummary by CodeRabbit