vinext 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/client/entry.js +1 -1
- package/dist/client/entry.js.map +1 -1
- package/dist/config/config-matchers.d.ts.map +1 -1
- package/dist/config/config-matchers.js +11 -10
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/next-config.js.map +1 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +218 -11
- package/dist/deploy.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -13
- package/dist/index.js.map +1 -1
- package/dist/routing/app-router.js +12 -12
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/pages-router.js +9 -9
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/server/app-dev-server.d.ts.map +1 -1
- package/dist/server/app-dev-server.js +35 -25
- package/dist/server/app-dev-server.js.map +1 -1
- package/dist/server/app-router-entry.d.ts.map +1 -1
- package/dist/server/app-router-entry.js +13 -15
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/middleware-codegen.js +1 -1
- package/dist/server/middleware-codegen.js.map +1 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +11 -2
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/prod-server.d.ts.map +1 -1
- package/dist/server/prod-server.js +6 -2
- package/dist/server/prod-server.js.map +1 -1
- package/dist/shims/cache-runtime.d.ts.map +1 -1
- package/dist/shims/cache-runtime.js +2 -1
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/form.js.map +1 -1
- package/dist/shims/link.d.ts.map +1 -1
- package/dist/shims/link.js +1 -0
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/shims/navigation.js +25 -15
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/router.js +2 -2
- package/dist/shims/router.js.map +1 -1
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/project.js +2 -1
- package/dist/utils/project.js.map +1 -1
- package/package.json +5 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-router-entry.d.ts","sourceRoot":"","sources":["../../src/server/app-router-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;;
|
|
1
|
+
{"version":3,"file":"app-router-entry.d.ts","sourceRoot":"","sources":["../../src/server/app-router-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;;mBAMoB,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;;AADlD,wBA0CE"}
|
|
@@ -13,35 +13,33 @@
|
|
|
13
13
|
*/
|
|
14
14
|
// @ts-expect-error — virtual module resolved by vinext
|
|
15
15
|
import rscHandler from "virtual:vinext-rsc-entry";
|
|
16
|
-
import { normalizePath } from "./normalize-path.js";
|
|
17
16
|
export default {
|
|
18
17
|
async fetch(request) {
|
|
19
18
|
const url = new URL(request.url);
|
|
20
|
-
// Normalize backslashes (browsers treat /\ as //)
|
|
19
|
+
// Normalize backslashes (browsers treat /\ as //) before any other checks.
|
|
21
20
|
const rawPathname = url.pathname.replaceAll("\\", "/");
|
|
22
21
|
// Block protocol-relative URL open redirects (//evil.com/ or /\evil.com/).
|
|
22
|
+
// Check rawPathname BEFORE decode so the guard fires before normalization.
|
|
23
23
|
if (rawPathname.startsWith("//")) {
|
|
24
24
|
return new Response("404 Not Found", { status: 404 });
|
|
25
25
|
}
|
|
26
|
-
//
|
|
27
|
-
|
|
26
|
+
// Validate that percent-encoding is well-formed. The RSC handler performs
|
|
27
|
+
// the actual decode + normalize; we only check here to return a clean 400
|
|
28
|
+
// instead of letting a malformed sequence crash downstream.
|
|
28
29
|
try {
|
|
29
|
-
|
|
30
|
+
decodeURIComponent(rawPathname);
|
|
30
31
|
}
|
|
31
32
|
catch {
|
|
32
33
|
// Malformed percent-encoding (e.g. /%E0%A4%A) — return 400 instead of throwing.
|
|
33
34
|
return new Response("Bad Request", { status: 400 });
|
|
34
35
|
}
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
// Delegate to RSC handler
|
|
44
|
-
const result = await rscHandler(normalizedRequest);
|
|
36
|
+
// Do NOT decode/normalize the pathname here. The RSC handler
|
|
37
|
+
// (virtual:vinext-rsc-entry) is the single point of decoding — it calls
|
|
38
|
+
// decodeURIComponent + normalizePath on the incoming URL. Decoding here
|
|
39
|
+
// AND in the handler would double-decode, causing inconsistent path
|
|
40
|
+
// matching between middleware and routing.
|
|
41
|
+
// Delegate to RSC handler (which decodes + normalizes the pathname itself)
|
|
42
|
+
const result = await rscHandler(request);
|
|
45
43
|
if (result instanceof Response) {
|
|
46
44
|
return result;
|
|
47
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-router-entry.js","sourceRoot":"","sources":["../../src/server/app-router-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uDAAuD;AACvD,OAAO,UAAU,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"app-router-entry.js","sourceRoot":"","sources":["../../src/server/app-router-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uDAAuD;AACvD,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAElD,eAAe;IACb,KAAK,CAAC,KAAK,CAAC,OAAgB;QAC1B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAEjC,2EAA2E;QAC3E,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAEvD,2EAA2E;QAC3E,2EAA2E;QAC3E,IAAI,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,QAAQ,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,0EAA0E;QAC1E,0EAA0E;QAC1E,4DAA4D;QAC5D,IAAI,CAAC;YACH,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,gFAAgF;YAChF,OAAO,IAAI,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACtD,CAAC;QAEA,6DAA6D;QAC7D,wEAAwE;QACxE,wEAAwE;QACxE,oEAAoE;QACpE,2CAA2C;QAE5C,2EAA2E;QAC3E,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;QAEzC,IAAI,MAAM,YAAY,QAAQ,EAAE,CAAC;YAC/B,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC5C,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACvD,CAAC;CACF,CAAC","sourcesContent":["/**\n * Default Cloudflare Worker entry point for vinext App Router.\n *\n * Use this directly in wrangler.jsonc:\n * \"main\": \"vinext/server/app-router-entry\"\n *\n * Or import and delegate to it from a custom worker:\n * import handler from \"vinext/server/app-router-entry\";\n * return handler.fetch(request);\n *\n * This file runs in the RSC environment. Configure the Cloudflare plugin with:\n * cloudflare({ viteEnvironment: { name: \"rsc\", childEnvironments: [\"ssr\"] } })\n */\n\n// @ts-expect-error — virtual module resolved by vinext\nimport rscHandler from \"virtual:vinext-rsc-entry\";\n\nexport default {\n async fetch(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n // Normalize backslashes (browsers treat /\\ as //) before any other checks.\n const rawPathname = url.pathname.replaceAll(\"\\\\\", \"/\");\n\n // Block protocol-relative URL open redirects (//evil.com/ or /\\evil.com/).\n // Check rawPathname BEFORE decode so the guard fires before normalization.\n if (rawPathname.startsWith(\"//\")) {\n return new Response(\"404 Not Found\", { status: 404 });\n }\n\n // Validate that percent-encoding is well-formed. The RSC handler performs\n // the actual decode + normalize; we only check here to return a clean 400\n // instead of letting a malformed sequence crash downstream.\n try {\n decodeURIComponent(rawPathname);\n } catch {\n // Malformed percent-encoding (e.g. /%E0%A4%A) — return 400 instead of throwing.\n return new Response(\"Bad Request\", { status: 400 });\n }\n\n // Do NOT decode/normalize the pathname here. The RSC handler\n // (virtual:vinext-rsc-entry) is the single point of decoding — it calls\n // decodeURIComponent + normalizePath on the incoming URL. Decoding here\n // AND in the handler would double-decode, causing inconsistent path\n // matching between middleware and routing.\n\n // Delegate to RSC handler (which decodes + normalizes the pathname itself)\n const result = await rscHandler(request);\n\n if (result instanceof Response) {\n return result;\n }\n\n if (result === null || result === undefined) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n return new Response(String(result), { status: 200 });\n },\n};\n"]}
|
|
@@ -156,7 +156,7 @@ function matchMiddlewarePattern(pathname, pattern) {
|
|
|
156
156
|
// Single-pass tokenizer (avoids chained .replace() flagged by CodeQL as
|
|
157
157
|
// incomplete sanitization — later passes could re-process earlier outputs).
|
|
158
158
|
${l} regexStr = "";
|
|
159
|
-
${v} tokenRe = /\\/:([\\w]+)\\*|\\/:([\\w]+)\\+|:([\\w]+)|[.]|[^/:.]+|./g;
|
|
159
|
+
${v} tokenRe = /\\/:([\\w-]+)\\*|\\/:([\\w-]+)\\+|:([\\w-]+)|[.]|[^/:.]+|./g;
|
|
160
160
|
${l} tok;
|
|
161
161
|
while ((tok = tokenRe.exec(pattern)) !== null) {
|
|
162
162
|
if (tok[1] !== undefined) { regexStr += "(?:/.*)?"; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware-codegen.js","sourceRoot":"","sources":["../../src/server/middleware-codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA0B,QAAQ;IACvE,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/C,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,OAAO;;IAEL,CAAC;IACD,CAAC;IACD,CAAC;;MAEC,CAAC;;;;;;;;;;;;;;;;;;;QAmBC,CAAC;;QAED,CAAC;;;;;;;;;;;;;;QAcD,CAAC;;;;;;;;QAQD,CAAC;;;;;;;;;;;;;;;;;;EAkBP,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAA0B,QAAQ;IAC1E,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/C,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,OAAO;;;;;;;;;;;;;;IAcL,CAAC;IACD,CAAC;SACI,CAAC;MACJ,CAAC;;;;;;EAML,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAA0B,QAAQ;IAC9E,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/C,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,MAAM,EAAE,GAAG,KAAK,KAAK,QAAQ;QAC3B,CAAC,CAAC,CAAC,MAAc,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,MAAM,UAAU,IAAI,IAAI;QAChE,CAAC,CAAC,CAAC,MAAc,EAAE,IAAY,EAAE,EAAE,CAAC,YAAY,MAAM,OAAO,IAAI,IAAI,CAAC;IAExE,oEAAoE;IACpE,qEAAqE;IACrE,iCAAiC;IACjC,OAAO;;;;;;MAMH,CAAC;;;;;IAKH,CAAC;IACD,CAAC;IACD,CAAC;;;;;;;;IAQD,CAAC;;;;;;;;IAQD,CAAC;;;WAGM,CAAC;;;;;yBAKa,EAAE,CAAC,GAAG,EAAE,6CAA6C,CAAC;EAC7E,CAAC;AACH,CAAC","sourcesContent":["/**\n * Shared middleware matching code generator.\n *\n * Both the App Router RSC entry (app-dev-server.ts) and the Pages Router\n * production entry (index.ts) need middleware matching logic inlined as\n * generated JavaScript strings. This module provides a single source of\n * truth to prevent the implementations from diverging.\n *\n * The regex detection guard (checking for \"(\" or \"\\\\\") is critical.\n * Without it, dot-escaping corrupts regex patterns like\n * /((?!api|_next).*), causing middleware to silently skip paths.\n */\n\n/**\n * Returns the generated JavaScript source for `__isSafeRegex` and `__safeRegExp`.\n *\n * @param style - \"modern\" emits const/let (for RSC entry), \"es5\" emits var (for prod entry)\n */\nexport function generateSafeRegExpCode(style: \"modern\" | \"es5\" = \"modern\"): string {\n const v = style === \"modern\" ? \"const\" : \"var\";\n const l = style === \"modern\" ? \"let\" : \"var\";\n return `\nfunction __isSafeRegex(pattern) {\n ${v} quantifierAtDepth = [];\n ${l} depth = 0;\n ${l} i = 0;\n while (i < pattern.length) {\n ${v} ch = pattern[i];\n if (ch === \"\\\\\\\\\") { i += 2; continue; }\n if (ch === \"[\") {\n i++;\n while (i < pattern.length && pattern[i] !== \"]\") {\n if (pattern[i] === \"\\\\\\\\\") i++;\n i++;\n }\n i++;\n continue;\n }\n if (ch === \"(\") {\n depth++;\n if (quantifierAtDepth.length <= depth) quantifierAtDepth.push(false);\n else quantifierAtDepth[depth] = false;\n i++;\n continue;\n }\n if (ch === \")\") {\n ${v} hadQ = depth > 0 && quantifierAtDepth[depth];\n if (depth > 0) depth--;\n ${v} next = pattern[i + 1];\n if (next === \"+\" || next === \"*\" || next === \"{\") {\n if (hadQ) return false;\n if (depth >= 0 && depth < quantifierAtDepth.length) quantifierAtDepth[depth] = true;\n }\n i++;\n continue;\n }\n if (ch === \"+\" || ch === \"*\") {\n if (depth > 0) quantifierAtDepth[depth] = true;\n i++;\n continue;\n }\n if (ch === \"?\") {\n ${v} prev = i > 0 ? pattern[i - 1] : \"\";\n if (prev !== \"+\" && prev !== \"*\" && prev !== \"?\" && prev !== \"}\") {\n if (depth > 0) quantifierAtDepth[depth] = true;\n }\n i++;\n continue;\n }\n if (ch === \"{\") {\n ${l} j = i + 1;\n while (j < pattern.length && /[\\\\d,]/.test(pattern[j])) j++;\n if (j < pattern.length && pattern[j] === \"}\" && j > i + 1) {\n if (depth > 0) quantifierAtDepth[depth] = true;\n i = j + 1;\n continue;\n }\n }\n i++;\n }\n return true;\n}\nfunction __safeRegExp(pattern, flags) {\n if (!__isSafeRegex(pattern)) {\n console.warn(\"[vinext] Ignoring potentially unsafe regex pattern (ReDoS risk): \" + pattern);\n return null;\n }\n try { return new RegExp(pattern, flags); } catch { return null; }\n}`;\n}\n\n/**\n * Returns the generated JavaScript source for `__normalizePath`.\n *\n * This must be kept in sync with `normalizePath()` in `normalize-path.ts`.\n * The inline version is used by codegen entries that can't import modules.\n *\n * @param style - \"modern\" emits const/let, \"es5\" emits var\n */\nexport function generateNormalizePathCode(style: \"modern\" | \"es5\" = \"modern\"): string {\n const v = style === \"modern\" ? \"const\" : \"var\";\n const l = style === \"modern\" ? \"let\" : \"var\";\n return `\nfunction __normalizePath(pathname) {\n if (\n pathname === \"/\" ||\n (pathname.length > 1 &&\n pathname[0] === \"/\" &&\n !pathname.includes(\"//\") &&\n !pathname.includes(\"/./\") &&\n !pathname.includes(\"/../\") &&\n !pathname.endsWith(\"/.\") &&\n !pathname.endsWith(\"/..\"))\n ) {\n return pathname;\n }\n ${v} segments = pathname.split(\"/\");\n ${v} resolved = [];\n for (${l} i = 0; i < segments.length; i++) {\n ${v} seg = segments[i];\n if (seg === \"\" || seg === \".\") continue;\n if (seg === \"..\") { resolved.pop(); }\n else { resolved.push(seg); }\n }\n return \"/\" + resolved.join(\"/\");\n}`;\n}\n\n/**\n * Returns the generated JavaScript source for middleware pattern matching.\n *\n * This includes:\n * - `matchMiddlewarePattern(pathname, pattern)` — matches a single pattern\n * - `matchesMiddleware(pathname, matcher)` — matches the full matcher config\n *\n * The generated code depends on `__safeRegExp` being defined in the same scope\n * (use `generateSafeRegExpCode` to emit it).\n *\n * @param style - \"modern\" emits const/let/arrow functions, \"es5\" emits var/function\n */\nexport function generateMiddlewareMatcherCode(style: \"modern\" | \"es5\" = \"modern\"): string {\n const v = style === \"modern\" ? \"const\" : \"var\";\n const l = style === \"modern\" ? \"let\" : \"var\";\n const fn = style === \"modern\"\n ? (params: string, body: string) => `(${params}) => { ${body} }`\n : (params: string, body: string) => `function(${params}) { ${body} }`;\n\n // The pattern matching logic must be identical to matchPattern() in\n // packages/vinext/src/server/middleware.ts. Any changes here must be\n // mirrored there and vice versa.\n return `\nfunction matchMiddlewarePattern(pathname, pattern) {\n // Regex patterns: if the pattern contains \"(\" or \"\\\\\" it's a regex —\n // pass it through to RegExp directly WITHOUT dot-escaping.\n // This guard prevents regex pattern corruption from dot-escaping.\n if (pattern.includes(\"(\") || pattern.includes(\"\\\\\\\\\")) {\n ${v} re = __safeRegExp(\"^\" + pattern + \"$\");\n if (re) return re.test(pathname);\n }\n // Single-pass tokenizer (avoids chained .replace() flagged by CodeQL as\n // incomplete sanitization — later passes could re-process earlier outputs).\n ${l} regexStr = \"\";\n ${v} tokenRe = /\\\\/:([\\\\w]+)\\\\*|\\\\/:([\\\\w]+)\\\\+|:([\\\\w]+)|[.]|[^/:.]+|./g;\n ${l} tok;\n while ((tok = tokenRe.exec(pattern)) !== null) {\n if (tok[1] !== undefined) { regexStr += \"(?:/.*)?\"; }\n else if (tok[2] !== undefined) { regexStr += \"(?:/.+)\"; }\n else if (tok[3] !== undefined) { regexStr += \"([^/]+)\"; }\n else if (tok[0] === \".\") { regexStr += \"\\\\\\\\.\"; }\n else { regexStr += tok[0]; }\n }\n ${v} re2 = __safeRegExp(\"^\" + regexStr + \"$\");\n return re2 ? re2.test(pathname) : pathname === pattern;\n}\n\nfunction matchesMiddleware(pathname, matcher) {\n if (!matcher) {\n return true;\n }\n ${v} patterns = [];\n if (typeof matcher === \"string\") { patterns.push(matcher); }\n else if (Array.isArray(matcher)) {\n for (${v} m of matcher) {\n if (typeof m === \"string\") patterns.push(m);\n else if (m && typeof m === \"object\" && \"source\" in m) patterns.push(m.source);\n }\n }\n return patterns.some(${fn(\"p\", \"return matchMiddlewarePattern(pathname, p);\")});\n}`;\n}\n\n\n"]}
|
|
1
|
+
{"version":3,"file":"middleware-codegen.js","sourceRoot":"","sources":["../../src/server/middleware-codegen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA0B,QAAQ;IACvE,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/C,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,OAAO;;IAEL,CAAC;IACD,CAAC;IACD,CAAC;;MAEC,CAAC;;;;;;;;;;;;;;;;;;;QAmBC,CAAC;;QAED,CAAC;;;;;;;;;;;;;;QAcD,CAAC;;;;;;;;QAQD,CAAC;;;;;;;;;;;;;;;;;;EAkBP,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAA0B,QAAQ;IAC1E,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/C,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,OAAO;;;;;;;;;;;;;;IAcL,CAAC;IACD,CAAC;SACI,CAAC;MACJ,CAAC;;;;;;EAML,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAA0B,QAAQ;IAC9E,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/C,MAAM,CAAC,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,MAAM,EAAE,GAAG,KAAK,KAAK,QAAQ;QAC3B,CAAC,CAAC,CAAC,MAAc,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,MAAM,UAAU,IAAI,IAAI;QAChE,CAAC,CAAC,CAAC,MAAc,EAAE,IAAY,EAAE,EAAE,CAAC,YAAY,MAAM,OAAO,IAAI,IAAI,CAAC;IAExE,oEAAoE;IACpE,qEAAqE;IACrE,iCAAiC;IACjC,OAAO;;;;;;MAMH,CAAC;;;;;IAKH,CAAC;IACD,CAAC;IACD,CAAC;;;;;;;;IAQD,CAAC;;;;;;;;IAQD,CAAC;;;WAGM,CAAC;;;;;yBAKa,EAAE,CAAC,GAAG,EAAE,6CAA6C,CAAC;EAC7E,CAAC;AACH,CAAC","sourcesContent":["/**\n * Shared middleware matching code generator.\n *\n * Both the App Router RSC entry (app-dev-server.ts) and the Pages Router\n * production entry (index.ts) need middleware matching logic inlined as\n * generated JavaScript strings. This module provides a single source of\n * truth to prevent the implementations from diverging.\n *\n * The regex detection guard (checking for \"(\" or \"\\\\\") is critical.\n * Without it, dot-escaping corrupts regex patterns like\n * /((?!api|_next).*), causing middleware to silently skip paths.\n */\n\n/**\n * Returns the generated JavaScript source for `__isSafeRegex` and `__safeRegExp`.\n *\n * @param style - \"modern\" emits const/let (for RSC entry), \"es5\" emits var (for prod entry)\n */\nexport function generateSafeRegExpCode(style: \"modern\" | \"es5\" = \"modern\"): string {\n const v = style === \"modern\" ? \"const\" : \"var\";\n const l = style === \"modern\" ? \"let\" : \"var\";\n return `\nfunction __isSafeRegex(pattern) {\n ${v} quantifierAtDepth = [];\n ${l} depth = 0;\n ${l} i = 0;\n while (i < pattern.length) {\n ${v} ch = pattern[i];\n if (ch === \"\\\\\\\\\") { i += 2; continue; }\n if (ch === \"[\") {\n i++;\n while (i < pattern.length && pattern[i] !== \"]\") {\n if (pattern[i] === \"\\\\\\\\\") i++;\n i++;\n }\n i++;\n continue;\n }\n if (ch === \"(\") {\n depth++;\n if (quantifierAtDepth.length <= depth) quantifierAtDepth.push(false);\n else quantifierAtDepth[depth] = false;\n i++;\n continue;\n }\n if (ch === \")\") {\n ${v} hadQ = depth > 0 && quantifierAtDepth[depth];\n if (depth > 0) depth--;\n ${v} next = pattern[i + 1];\n if (next === \"+\" || next === \"*\" || next === \"{\") {\n if (hadQ) return false;\n if (depth >= 0 && depth < quantifierAtDepth.length) quantifierAtDepth[depth] = true;\n }\n i++;\n continue;\n }\n if (ch === \"+\" || ch === \"*\") {\n if (depth > 0) quantifierAtDepth[depth] = true;\n i++;\n continue;\n }\n if (ch === \"?\") {\n ${v} prev = i > 0 ? pattern[i - 1] : \"\";\n if (prev !== \"+\" && prev !== \"*\" && prev !== \"?\" && prev !== \"}\") {\n if (depth > 0) quantifierAtDepth[depth] = true;\n }\n i++;\n continue;\n }\n if (ch === \"{\") {\n ${l} j = i + 1;\n while (j < pattern.length && /[\\\\d,]/.test(pattern[j])) j++;\n if (j < pattern.length && pattern[j] === \"}\" && j > i + 1) {\n if (depth > 0) quantifierAtDepth[depth] = true;\n i = j + 1;\n continue;\n }\n }\n i++;\n }\n return true;\n}\nfunction __safeRegExp(pattern, flags) {\n if (!__isSafeRegex(pattern)) {\n console.warn(\"[vinext] Ignoring potentially unsafe regex pattern (ReDoS risk): \" + pattern);\n return null;\n }\n try { return new RegExp(pattern, flags); } catch { return null; }\n}`;\n}\n\n/**\n * Returns the generated JavaScript source for `__normalizePath`.\n *\n * This must be kept in sync with `normalizePath()` in `normalize-path.ts`.\n * The inline version is used by codegen entries that can't import modules.\n *\n * @param style - \"modern\" emits const/let, \"es5\" emits var\n */\nexport function generateNormalizePathCode(style: \"modern\" | \"es5\" = \"modern\"): string {\n const v = style === \"modern\" ? \"const\" : \"var\";\n const l = style === \"modern\" ? \"let\" : \"var\";\n return `\nfunction __normalizePath(pathname) {\n if (\n pathname === \"/\" ||\n (pathname.length > 1 &&\n pathname[0] === \"/\" &&\n !pathname.includes(\"//\") &&\n !pathname.includes(\"/./\") &&\n !pathname.includes(\"/../\") &&\n !pathname.endsWith(\"/.\") &&\n !pathname.endsWith(\"/..\"))\n ) {\n return pathname;\n }\n ${v} segments = pathname.split(\"/\");\n ${v} resolved = [];\n for (${l} i = 0; i < segments.length; i++) {\n ${v} seg = segments[i];\n if (seg === \"\" || seg === \".\") continue;\n if (seg === \"..\") { resolved.pop(); }\n else { resolved.push(seg); }\n }\n return \"/\" + resolved.join(\"/\");\n}`;\n}\n\n/**\n * Returns the generated JavaScript source for middleware pattern matching.\n *\n * This includes:\n * - `matchMiddlewarePattern(pathname, pattern)` — matches a single pattern\n * - `matchesMiddleware(pathname, matcher)` — matches the full matcher config\n *\n * The generated code depends on `__safeRegExp` being defined in the same scope\n * (use `generateSafeRegExpCode` to emit it).\n *\n * @param style - \"modern\" emits const/let/arrow functions, \"es5\" emits var/function\n */\nexport function generateMiddlewareMatcherCode(style: \"modern\" | \"es5\" = \"modern\"): string {\n const v = style === \"modern\" ? \"const\" : \"var\";\n const l = style === \"modern\" ? \"let\" : \"var\";\n const fn = style === \"modern\"\n ? (params: string, body: string) => `(${params}) => { ${body} }`\n : (params: string, body: string) => `function(${params}) { ${body} }`;\n\n // The pattern matching logic must be identical to matchPattern() in\n // packages/vinext/src/server/middleware.ts. Any changes here must be\n // mirrored there and vice versa.\n return `\nfunction matchMiddlewarePattern(pathname, pattern) {\n // Regex patterns: if the pattern contains \"(\" or \"\\\\\" it's a regex —\n // pass it through to RegExp directly WITHOUT dot-escaping.\n // This guard prevents regex pattern corruption from dot-escaping.\n if (pattern.includes(\"(\") || pattern.includes(\"\\\\\\\\\")) {\n ${v} re = __safeRegExp(\"^\" + pattern + \"$\");\n if (re) return re.test(pathname);\n }\n // Single-pass tokenizer (avoids chained .replace() flagged by CodeQL as\n // incomplete sanitization — later passes could re-process earlier outputs).\n ${l} regexStr = \"\";\n ${v} tokenRe = /\\\\/:([\\\\w-]+)\\\\*|\\\\/:([\\\\w-]+)\\\\+|:([\\\\w-]+)|[.]|[^/:.]+|./g;\n ${l} tok;\n while ((tok = tokenRe.exec(pattern)) !== null) {\n if (tok[1] !== undefined) { regexStr += \"(?:/.*)?\"; }\n else if (tok[2] !== undefined) { regexStr += \"(?:/.+)\"; }\n else if (tok[3] !== undefined) { regexStr += \"([^/]+)\"; }\n else if (tok[0] === \".\") { regexStr += \"\\\\\\\\.\"; }\n else { regexStr += tok[0]; }\n }\n ${v} re2 = __safeRegExp(\"^\" + regexStr + \"$\");\n return re2 ? re2.test(pathname) : pathname === pattern;\n}\n\nfunction matchesMiddleware(pathname, matcher) {\n if (!matcher) {\n return true;\n }\n ${v} patterns = [];\n if (typeof matcher === \"string\") { patterns.push(matcher); }\n else if (Array.isArray(matcher)) {\n for (${v} m of matcher) {\n if (typeof m === \"string\") patterns.push(m);\n else if (m && typeof m === \"object\" && \"source\" in m) patterns.push(m.source);\n }\n }\n return patterns.some(${fn(\"p\", \"return matchMiddlewarePattern(pathname, p);\")});\n}`;\n}\n\n\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AA+B1C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqB9D;AAED,qDAAqD;AACrD,KAAK,aAAa,GACd,MAAM,GACN,MAAM,EAAE,GACR;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,EAAE,CAAC;AAE1F;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,GAAG,SAAS,GACjC,OAAO,CAqBT;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AA+B1C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqB9D;AAED,qDAAqD;AACrD,KAAK,aAAa,GACd,MAAM,GACN,MAAM,EAAE,GACR;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,EAAE,CAAC;AAE1F;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,GAAG,SAAS,GACjC,OAAO,CAqBT;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAkCvE;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,QAAQ,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,gBAAgB,CAAC,CAyH3B"}
|
|
@@ -113,8 +113,9 @@ export function matchPattern(pathname, pattern) {
|
|
|
113
113
|
}
|
|
114
114
|
// Convert Next.js path patterns to regex in a single pass.
|
|
115
115
|
// Matches /:param*, /:param+, :param, dots, and literal text.
|
|
116
|
+
// Param names may contain hyphens (e.g. [[...sign-in]]).
|
|
116
117
|
let regexStr = "";
|
|
117
|
-
const tokenRe = /\/:(\w+)\*|\/:(\w+)\+|:(\w+)|[.]|[^/:.]+|./g;
|
|
118
|
+
const tokenRe = /\/:([\w-]+)\*|\/:([\w-]+)\+|:([\w-]+)|[.]|[^/:.]+|./g;
|
|
118
119
|
let tok;
|
|
119
120
|
while ((tok = tokenRe.exec(pattern)) !== null) {
|
|
120
121
|
if (tok[1] !== undefined) {
|
|
@@ -176,8 +177,16 @@ export async function runMiddleware(server, middlewarePath, request) {
|
|
|
176
177
|
if (!matchesMiddleware(normalizedPathname, matcher)) {
|
|
177
178
|
return { continue: true };
|
|
178
179
|
}
|
|
180
|
+
// Construct a new Request with the fully decoded + normalized pathname so
|
|
181
|
+
// middleware always sees the same canonical path that the router uses.
|
|
182
|
+
let mwRequest = request;
|
|
183
|
+
if (normalizedPathname !== url.pathname) {
|
|
184
|
+
const mwUrl = new URL(url);
|
|
185
|
+
mwUrl.pathname = normalizedPathname;
|
|
186
|
+
mwRequest = new Request(mwUrl, request);
|
|
187
|
+
}
|
|
179
188
|
// Wrap in NextRequest so middleware gets .nextUrl, .cookies, .geo, .ip, etc.
|
|
180
|
-
const nextRequest =
|
|
189
|
+
const nextRequest = mwRequest instanceof NextRequest ? mwRequest : new NextRequest(mwRequest);
|
|
181
190
|
// Execute the middleware
|
|
182
191
|
let response;
|
|
183
192
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * proxy.ts / middleware.ts runner\n *\n * Loads and executes the user's proxy.ts (Next.js 16) or middleware.ts file\n * before routing. Runs in Node (not Edge Runtime), per the vinext design.\n *\n * In Next.js 16, proxy.ts replaces middleware.ts:\n * - proxy.ts: default export function, runs on Node.js runtime\n * - middleware.ts: deprecated but still supported for Edge runtime use cases\n *\n * The proxy/middleware receives a NextRequest and can:\n * - Return NextResponse.next() to continue to the route\n * - Return NextResponse.redirect() to redirect\n * - Return NextResponse.rewrite() to rewrite the URL\n * - Set/modify headers and cookies\n * - Return a Response directly (e.g., for auth guards)\n *\n * Supports the `config.matcher` export for path filtering.\n */\n\nimport type { ViteDevServer } from \"vite\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { NextRequest } from \"../shims/server.js\";\nimport { safeRegExp } from \"../config/config-matchers.js\";\nimport { normalizePath } from \"./normalize-path.js\";\n\n/**\n * Possible proxy/middleware file names.\n * proxy.ts (Next.js 16) is checked first, then middleware.ts (deprecated).\n */\nconst PROXY_FILES = [\n \"proxy.ts\",\n \"proxy.js\",\n \"proxy.mjs\",\n \"src/proxy.ts\",\n \"src/proxy.js\",\n \"src/proxy.mjs\",\n];\n\nconst MIDDLEWARE_FILES = [\n \"middleware.ts\",\n \"middleware.tsx\",\n \"middleware.js\",\n \"middleware.mjs\",\n \"src/middleware.ts\",\n \"src/middleware.tsx\",\n \"src/middleware.js\",\n \"src/middleware.mjs\",\n];\n\n/**\n * Find the proxy or middleware file in the project root.\n * Checks for proxy.ts (Next.js 16) first, then falls back to middleware.ts.\n * If middleware.ts is found, logs a deprecation warning.\n */\nexport function findMiddlewareFile(root: string): string | null {\n // Check proxy.ts first (Next.js 16 replacement for middleware.ts)\n for (const file of PROXY_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n\n // Fall back to middleware.ts (deprecated in Next.js 16)\n for (const file of MIDDLEWARE_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n console.warn(\n \"[vinext] middleware.ts is deprecated in Next.js 16. \" +\n \"Rename to proxy.ts and export a default function.\",\n );\n return fullPath;\n }\n }\n return null;\n}\n\n/** Matcher pattern from middleware config export. */\ntype MatcherConfig =\n | string\n | string[]\n | { source: string; regexp?: string; locale?: boolean; has?: any[]; missing?: any[] }[];\n\n/**\n * Check if a pathname matches the middleware matcher config.\n * If no matcher is configured, middleware runs on all paths\n * except static files and internal Next.js paths.\n */\nexport function matchesMiddleware(\n pathname: string,\n matcher: MatcherConfig | undefined,\n): boolean {\n if (!matcher) {\n // Next.js default: middleware runs on ALL paths when no matcher is configured.\n // Users opt out of specific paths by configuring a matcher pattern.\n return true;\n }\n\n const patterns: string[] = [];\n if (typeof matcher === \"string\") {\n patterns.push(matcher);\n } else if (Array.isArray(matcher)) {\n for (const m of matcher) {\n if (typeof m === \"string\") {\n patterns.push(m);\n } else if (m && typeof m === \"object\" && \"source\" in m) {\n patterns.push(m.source);\n }\n }\n }\n\n return patterns.some((pattern) => matchPattern(pathname, pattern));\n}\n\n/**\n * Match a single pattern against a pathname.\n * Supports Next.js matcher patterns:\n * /about -> exact match\n * /dashboard/:path* -> prefix match with params\n * /api/:path+ -> one or more segments\n * /((?!api|_next).*) -> regex patterns\n */\nexport function matchPattern(pathname: string, pattern: string): boolean {\n // Handle regex patterns (starts with /)\n if (pattern.includes(\"(\") || pattern.includes(\"\\\\\")) {\n const re = safeRegExp(\"^\" + pattern + \"$\");\n if (re) return re.test(pathname);\n // Fall through to simple matching\n }\n\n // Convert Next.js path patterns to regex in a single pass.\n // Matches /:param*, /:param+, :param, dots, and literal text.\n let regexStr = \"\";\n const tokenRe = /\\/:(\\w+)\\*|\\/:(\\w+)\\+|:(\\w+)|[.]|[^/:.]+|./g;\n let tok: RegExpExecArray | null;\n while ((tok = tokenRe.exec(pattern)) !== null) {\n if (tok[1] !== undefined) {\n // /:param* → optionally match slash + zero or more segments\n regexStr += \"(?:/.*)?\";\n } else if (tok[2] !== undefined) {\n // /:param+ → match slash + one or more segments\n regexStr += \"(?:/.+)\";\n } else if (tok[3] !== undefined) {\n // :param → match one segment\n regexStr += \"([^/]+)\";\n } else if (tok[0] === \".\") {\n regexStr += \"\\\\.\";\n } else {\n regexStr += tok[0];\n }\n }\n\n const re = safeRegExp(\"^\" + regexStr + \"$\");\n if (re) return re.test(pathname);\n return pathname === pattern;\n}\n\n/** Result of running middleware. */\nexport interface MiddlewareResult {\n /** Whether to continue to the route handler. */\n continue: boolean;\n /** If set, redirect to this URL. */\n redirectUrl?: string;\n /** HTTP status for redirect (default 307). */\n redirectStatus?: number;\n /** If set, rewrite to this URL (internal). */\n rewriteUrl?: string;\n /** HTTP status for rewrite (e.g. 403 from NextResponse.rewrite(url, { status: 403 })). */\n rewriteStatus?: number;\n /** Headers to set on the response. */\n responseHeaders?: Headers;\n /** If the middleware returned a full Response, use it directly. */\n response?: Response;\n}\n\n/**\n * Load and execute middleware for a given request.\n *\n * @param server - Vite dev server (for SSR module loading)\n * @param middlewarePath - Absolute path to the middleware file\n * @param request - The incoming Request object\n * @returns Middleware result describing what action to take\n */\nexport async function runMiddleware(\n server: ViteDevServer,\n middlewarePath: string,\n request: Request,\n): Promise<MiddlewareResult> {\n // Load the middleware module via Vite's SSR module loader\n const mod = await server.ssrLoadModule(middlewarePath);\n\n // Accept: default export, named \"proxy\" (Next.js 16), or named \"middleware\"\n const middlewareFn = mod.default ?? mod.proxy ?? mod.middleware;\n if (typeof middlewareFn !== \"function\") {\n // No proxy/middleware function exported — continue as normal\n return { continue: true };\n }\n\n // Check matcher config\n const config = mod.config;\n const matcher = config?.matcher;\n const url = new URL(request.url);\n\n // Normalize the pathname before middleware matching to prevent bypasses\n // via percent-encoding (/%61dmin → /admin) or double slashes (/dashboard//settings).\n let decodedPathname: string;\n try {\n decodedPathname = decodeURIComponent(url.pathname);\n } catch {\n // Malformed percent-encoding (e.g. /%E0%A4%A) — return 400 instead of throwing.\n return { continue: false, response: new Response(\"Bad Request\", { status: 400 }) };\n }\n const normalizedPathname = normalizePath(decodedPathname);\n\n if (!matchesMiddleware(normalizedPathname, matcher)) {\n return { continue: true };\n }\n\n // Wrap in NextRequest so middleware gets .nextUrl, .cookies, .geo, .ip, etc.\n const nextRequest = request instanceof NextRequest ? request : new NextRequest(request);\n\n // Execute the middleware\n let response: Response | undefined;\n try {\n response = await middlewareFn(nextRequest);\n } catch (e: any) {\n console.error(\"[vinext] Middleware error:\", e);\n const message =\n process.env.NODE_ENV === \"production\"\n ? \"Internal Server Error\"\n : \"Middleware Error: \" + (e?.message ?? String(e));\n return {\n continue: false,\n response: new Response(message, {\n status: 500,\n }),\n };\n }\n\n // No response = continue\n if (!response) {\n return { continue: true };\n }\n\n // Check for x-middleware-next header (NextResponse.next())\n if (response.headers.get(\"x-middleware-next\") === \"1\") {\n // Continue to the route, but apply any headers the middleware set\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (\n key !== \"x-middleware-next\" &&\n key !== \"x-middleware-rewrite\"\n ) {\n responseHeaders.set(key, value);\n }\n }\n return { continue: true, responseHeaders };\n }\n\n // Check for redirect (3xx status)\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"Location\") ?? response.headers.get(\"location\");\n if (location) {\n return {\n continue: false,\n redirectUrl: location,\n redirectStatus: response.status,\n };\n }\n }\n\n // Check for rewrite (x-middleware-rewrite header)\n const rewriteUrl = response.headers.get(\"x-middleware-rewrite\");\n if (rewriteUrl) {\n // Continue to the route but with a rewritten URL\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (key !== \"x-middleware-rewrite\") {\n responseHeaders.set(key, value);\n }\n }\n // Parse the rewrite URL — may be absolute or relative\n let rewritePath: string;\n try {\n const rewriteParsed = new URL(rewriteUrl, request.url);\n rewritePath = rewriteParsed.pathname + rewriteParsed.search;\n } catch {\n rewritePath = rewriteUrl;\n }\n return {\n continue: true,\n rewriteUrl: rewritePath,\n rewriteStatus: response.status !== 200 ? response.status : undefined,\n responseHeaders,\n };\n }\n\n // Middleware returned a full Response (e.g., blocking, custom body)\n return { continue: false, response };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * proxy.ts / middleware.ts runner\n *\n * Loads and executes the user's proxy.ts (Next.js 16) or middleware.ts file\n * before routing. Runs in Node (not Edge Runtime), per the vinext design.\n *\n * In Next.js 16, proxy.ts replaces middleware.ts:\n * - proxy.ts: default export function, runs on Node.js runtime\n * - middleware.ts: deprecated but still supported for Edge runtime use cases\n *\n * The proxy/middleware receives a NextRequest and can:\n * - Return NextResponse.next() to continue to the route\n * - Return NextResponse.redirect() to redirect\n * - Return NextResponse.rewrite() to rewrite the URL\n * - Set/modify headers and cookies\n * - Return a Response directly (e.g., for auth guards)\n *\n * Supports the `config.matcher` export for path filtering.\n */\n\nimport type { ViteDevServer } from \"vite\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { NextRequest } from \"../shims/server.js\";\nimport { safeRegExp } from \"../config/config-matchers.js\";\nimport { normalizePath } from \"./normalize-path.js\";\n\n/**\n * Possible proxy/middleware file names.\n * proxy.ts (Next.js 16) is checked first, then middleware.ts (deprecated).\n */\nconst PROXY_FILES = [\n \"proxy.ts\",\n \"proxy.js\",\n \"proxy.mjs\",\n \"src/proxy.ts\",\n \"src/proxy.js\",\n \"src/proxy.mjs\",\n];\n\nconst MIDDLEWARE_FILES = [\n \"middleware.ts\",\n \"middleware.tsx\",\n \"middleware.js\",\n \"middleware.mjs\",\n \"src/middleware.ts\",\n \"src/middleware.tsx\",\n \"src/middleware.js\",\n \"src/middleware.mjs\",\n];\n\n/**\n * Find the proxy or middleware file in the project root.\n * Checks for proxy.ts (Next.js 16) first, then falls back to middleware.ts.\n * If middleware.ts is found, logs a deprecation warning.\n */\nexport function findMiddlewareFile(root: string): string | null {\n // Check proxy.ts first (Next.js 16 replacement for middleware.ts)\n for (const file of PROXY_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n return fullPath;\n }\n }\n\n // Fall back to middleware.ts (deprecated in Next.js 16)\n for (const file of MIDDLEWARE_FILES) {\n const fullPath = path.join(root, file);\n if (fs.existsSync(fullPath)) {\n console.warn(\n \"[vinext] middleware.ts is deprecated in Next.js 16. \" +\n \"Rename to proxy.ts and export a default function.\",\n );\n return fullPath;\n }\n }\n return null;\n}\n\n/** Matcher pattern from middleware config export. */\ntype MatcherConfig =\n | string\n | string[]\n | { source: string; regexp?: string; locale?: boolean; has?: any[]; missing?: any[] }[];\n\n/**\n * Check if a pathname matches the middleware matcher config.\n * If no matcher is configured, middleware runs on all paths\n * except static files and internal Next.js paths.\n */\nexport function matchesMiddleware(\n pathname: string,\n matcher: MatcherConfig | undefined,\n): boolean {\n if (!matcher) {\n // Next.js default: middleware runs on ALL paths when no matcher is configured.\n // Users opt out of specific paths by configuring a matcher pattern.\n return true;\n }\n\n const patterns: string[] = [];\n if (typeof matcher === \"string\") {\n patterns.push(matcher);\n } else if (Array.isArray(matcher)) {\n for (const m of matcher) {\n if (typeof m === \"string\") {\n patterns.push(m);\n } else if (m && typeof m === \"object\" && \"source\" in m) {\n patterns.push(m.source);\n }\n }\n }\n\n return patterns.some((pattern) => matchPattern(pathname, pattern));\n}\n\n/**\n * Match a single pattern against a pathname.\n * Supports Next.js matcher patterns:\n * /about -> exact match\n * /dashboard/:path* -> prefix match with params\n * /api/:path+ -> one or more segments\n * /((?!api|_next).*) -> regex patterns\n */\nexport function matchPattern(pathname: string, pattern: string): boolean {\n // Handle regex patterns (starts with /)\n if (pattern.includes(\"(\") || pattern.includes(\"\\\\\")) {\n const re = safeRegExp(\"^\" + pattern + \"$\");\n if (re) return re.test(pathname);\n // Fall through to simple matching\n }\n\n // Convert Next.js path patterns to regex in a single pass.\n // Matches /:param*, /:param+, :param, dots, and literal text.\n // Param names may contain hyphens (e.g. [[...sign-in]]).\n let regexStr = \"\";\n const tokenRe = /\\/:([\\w-]+)\\*|\\/:([\\w-]+)\\+|:([\\w-]+)|[.]|[^/:.]+|./g;\n let tok: RegExpExecArray | null;\n while ((tok = tokenRe.exec(pattern)) !== null) {\n if (tok[1] !== undefined) {\n // /:param* → optionally match slash + zero or more segments\n regexStr += \"(?:/.*)?\";\n } else if (tok[2] !== undefined) {\n // /:param+ → match slash + one or more segments\n regexStr += \"(?:/.+)\";\n } else if (tok[3] !== undefined) {\n // :param → match one segment\n regexStr += \"([^/]+)\";\n } else if (tok[0] === \".\") {\n regexStr += \"\\\\.\";\n } else {\n regexStr += tok[0];\n }\n }\n\n const re = safeRegExp(\"^\" + regexStr + \"$\");\n if (re) return re.test(pathname);\n return pathname === pattern;\n}\n\n/** Result of running middleware. */\nexport interface MiddlewareResult {\n /** Whether to continue to the route handler. */\n continue: boolean;\n /** If set, redirect to this URL. */\n redirectUrl?: string;\n /** HTTP status for redirect (default 307). */\n redirectStatus?: number;\n /** If set, rewrite to this URL (internal). */\n rewriteUrl?: string;\n /** HTTP status for rewrite (e.g. 403 from NextResponse.rewrite(url, { status: 403 })). */\n rewriteStatus?: number;\n /** Headers to set on the response. */\n responseHeaders?: Headers;\n /** If the middleware returned a full Response, use it directly. */\n response?: Response;\n}\n\n/**\n * Load and execute middleware for a given request.\n *\n * @param server - Vite dev server (for SSR module loading)\n * @param middlewarePath - Absolute path to the middleware file\n * @param request - The incoming Request object\n * @returns Middleware result describing what action to take\n */\nexport async function runMiddleware(\n server: ViteDevServer,\n middlewarePath: string,\n request: Request,\n): Promise<MiddlewareResult> {\n // Load the middleware module via Vite's SSR module loader\n const mod = await server.ssrLoadModule(middlewarePath);\n\n // Accept: default export, named \"proxy\" (Next.js 16), or named \"middleware\"\n const middlewareFn = mod.default ?? mod.proxy ?? mod.middleware;\n if (typeof middlewareFn !== \"function\") {\n // No proxy/middleware function exported — continue as normal\n return { continue: true };\n }\n\n // Check matcher config\n const config = mod.config;\n const matcher = config?.matcher;\n const url = new URL(request.url);\n\n // Normalize the pathname before middleware matching to prevent bypasses\n // via percent-encoding (/%61dmin → /admin) or double slashes (/dashboard//settings).\n let decodedPathname: string;\n try {\n decodedPathname = decodeURIComponent(url.pathname);\n } catch {\n // Malformed percent-encoding (e.g. /%E0%A4%A) — return 400 instead of throwing.\n return { continue: false, response: new Response(\"Bad Request\", { status: 400 }) };\n }\n const normalizedPathname = normalizePath(decodedPathname);\n\n if (!matchesMiddleware(normalizedPathname, matcher)) {\n return { continue: true };\n }\n\n // Construct a new Request with the fully decoded + normalized pathname so\n // middleware always sees the same canonical path that the router uses.\n let mwRequest = request;\n if (normalizedPathname !== url.pathname) {\n const mwUrl = new URL(url);\n mwUrl.pathname = normalizedPathname;\n mwRequest = new Request(mwUrl, request);\n }\n\n // Wrap in NextRequest so middleware gets .nextUrl, .cookies, .geo, .ip, etc.\n const nextRequest = mwRequest instanceof NextRequest ? mwRequest : new NextRequest(mwRequest);\n\n // Execute the middleware\n let response: Response | undefined;\n try {\n response = await middlewareFn(nextRequest);\n } catch (e: any) {\n console.error(\"[vinext] Middleware error:\", e);\n const message =\n process.env.NODE_ENV === \"production\"\n ? \"Internal Server Error\"\n : \"Middleware Error: \" + (e?.message ?? String(e));\n return {\n continue: false,\n response: new Response(message, {\n status: 500,\n }),\n };\n }\n\n // No response = continue\n if (!response) {\n return { continue: true };\n }\n\n // Check for x-middleware-next header (NextResponse.next())\n if (response.headers.get(\"x-middleware-next\") === \"1\") {\n // Continue to the route, but apply any headers the middleware set\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (\n key !== \"x-middleware-next\" &&\n key !== \"x-middleware-rewrite\"\n ) {\n responseHeaders.set(key, value);\n }\n }\n return { continue: true, responseHeaders };\n }\n\n // Check for redirect (3xx status)\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"Location\") ?? response.headers.get(\"location\");\n if (location) {\n return {\n continue: false,\n redirectUrl: location,\n redirectStatus: response.status,\n };\n }\n }\n\n // Check for rewrite (x-middleware-rewrite header)\n const rewriteUrl = response.headers.get(\"x-middleware-rewrite\");\n if (rewriteUrl) {\n // Continue to the route but with a rewritten URL\n const responseHeaders = new Headers();\n for (const [key, value] of response.headers) {\n if (key !== \"x-middleware-rewrite\") {\n responseHeaders.set(key, value);\n }\n }\n // Parse the rewrite URL — may be absolute or relative\n let rewritePath: string;\n try {\n const rewriteParsed = new URL(rewriteUrl, request.url);\n rewritePath = rewriteParsed.pathname + rewriteParsed.search;\n } catch {\n rewritePath = rewriteUrl;\n }\n return {\n continue: true,\n rewriteUrl: rewritePath,\n rewriteStatus: response.status !== 200 ? response.status : undefined,\n responseHeaders,\n };\n }\n\n // Middleware returned a full Response (e.g., blocking, custom body)\n return { continue: false, response };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prod-server.d.ts","sourceRoot":"","sources":["../../src/server/prod-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAuBpF,MAAM,WAAW,iBAAiB;IAChC,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,mDAAmD;AACnD,QAAA,MAAM,kBAAkB,aAetB,CAAC;AAEH,0GAA0G;AAC1G,QAAA,MAAM,kBAAkB,OAAO,CAAC;AAEhC;;;GAGG;AACH,iBAAS,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAQjF;AAoBD;;;GAGG;AACH,iBAAS,cAAc,CACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACzC,QAAQ,GAAE,OAAc,GACvB,IAAI,CAkCN;AA0FD;;;;;;;;;;;GAWG;AACH,iBAAS,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAcnE;AAED,4EAA4E;AAC5E,QAAA,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,CAK7B,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,UAAU,SAAkE,CAAC;AAEnF;;GAEG;AACH,iBAAS,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAmCvD;AA+ED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,iBAAsB,sFA6BpE;
|
|
1
|
+
{"version":3,"file":"prod-server.d.ts","sourceRoot":"","sources":["../../src/server/prod-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAuBpF,MAAM,WAAW,iBAAiB;IAChC,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,mDAAmD;AACnD,QAAA,MAAM,kBAAkB,aAetB,CAAC;AAEH,0GAA0G;AAC1G,QAAA,MAAM,kBAAkB,OAAO,CAAC;AAEhC;;;GAGG;AACH,iBAAS,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAQjF;AAoBD;;;GAGG;AACH,iBAAS,cAAc,CACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACzC,QAAQ,GAAE,OAAc,GACvB,IAAI,CAkCN;AA0FD;;;;;;;;;;;GAWG;AACH,iBAAS,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAcnE;AAED,4EAA4E;AAC5E,QAAA,MAAM,YAAY,EAAE,GAAG,CAAC,MAAM,CAK7B,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,UAAU,SAAkE,CAAC;AAEnF;;GAEG;AACH,iBAAS,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAmCvD;AA+ED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,iBAAsB,sFA6BpE;AA4eD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -524,9 +524,12 @@ async function startPagesRouterServer(options) {
|
|
|
524
524
|
];
|
|
525
525
|
const server = createServer(async (req, res) => {
|
|
526
526
|
const rawUrl = req.url ?? "/";
|
|
527
|
-
let url = rawUrl;
|
|
528
527
|
// Normalize backslashes (browsers treat /\ as //), then decode and normalize path.
|
|
529
|
-
|
|
528
|
+
// Rebuild `url` from the decoded pathname + original query string so all
|
|
529
|
+
// downstream consumers (resolvedUrl, resolvedPathname, config matchers)
|
|
530
|
+
// always work with the decoded, canonical path.
|
|
531
|
+
const rawPagesPathname = rawUrl.split("?")[0].replaceAll("\\", "/");
|
|
532
|
+
const rawQs = rawUrl.includes("?") ? rawUrl.slice(rawUrl.indexOf("?")) : "";
|
|
530
533
|
let pathname;
|
|
531
534
|
try {
|
|
532
535
|
pathname = normalizePath(decodeURIComponent(rawPagesPathname));
|
|
@@ -537,6 +540,7 @@ async function startPagesRouterServer(options) {
|
|
|
537
540
|
res.end("Bad Request");
|
|
538
541
|
return;
|
|
539
542
|
}
|
|
543
|
+
let url = pathname + rawQs;
|
|
540
544
|
// Guard against protocol-relative URL open redirect attacks.
|
|
541
545
|
// Check rawPagesPathname before normalizePath collapses //.
|
|
542
546
|
if (rawPagesPathname.startsWith("//")) {
|