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.
Files changed (47) hide show
  1. package/README.md +8 -2
  2. package/dist/client/entry.js +1 -1
  3. package/dist/client/entry.js.map +1 -1
  4. package/dist/config/config-matchers.d.ts.map +1 -1
  5. package/dist/config/config-matchers.js +11 -10
  6. package/dist/config/config-matchers.js.map +1 -1
  7. package/dist/config/next-config.js.map +1 -1
  8. package/dist/deploy.d.ts.map +1 -1
  9. package/dist/deploy.js +218 -11
  10. package/dist/deploy.js.map +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +25 -13
  13. package/dist/index.js.map +1 -1
  14. package/dist/routing/app-router.js +12 -12
  15. package/dist/routing/app-router.js.map +1 -1
  16. package/dist/routing/pages-router.js +9 -9
  17. package/dist/routing/pages-router.js.map +1 -1
  18. package/dist/server/app-dev-server.d.ts.map +1 -1
  19. package/dist/server/app-dev-server.js +35 -25
  20. package/dist/server/app-dev-server.js.map +1 -1
  21. package/dist/server/app-router-entry.d.ts.map +1 -1
  22. package/dist/server/app-router-entry.js +13 -15
  23. package/dist/server/app-router-entry.js.map +1 -1
  24. package/dist/server/middleware-codegen.js +1 -1
  25. package/dist/server/middleware-codegen.js.map +1 -1
  26. package/dist/server/middleware.d.ts.map +1 -1
  27. package/dist/server/middleware.js +11 -2
  28. package/dist/server/middleware.js.map +1 -1
  29. package/dist/server/prod-server.d.ts.map +1 -1
  30. package/dist/server/prod-server.js +6 -2
  31. package/dist/server/prod-server.js.map +1 -1
  32. package/dist/shims/cache-runtime.d.ts.map +1 -1
  33. package/dist/shims/cache-runtime.js +2 -1
  34. package/dist/shims/cache-runtime.js.map +1 -1
  35. package/dist/shims/form.js.map +1 -1
  36. package/dist/shims/link.d.ts.map +1 -1
  37. package/dist/shims/link.js +1 -0
  38. package/dist/shims/link.js.map +1 -1
  39. package/dist/shims/navigation.d.ts.map +1 -1
  40. package/dist/shims/navigation.js +25 -15
  41. package/dist/shims/navigation.js.map +1 -1
  42. package/dist/shims/router.js +2 -2
  43. package/dist/shims/router.js.map +1 -1
  44. package/dist/utils/project.d.ts.map +1 -1
  45. package/dist/utils/project.js +2 -1
  46. package/dist/utils/project.js.map +1 -1
  47. package/package.json +5 -1
@@ -7,6 +7,7 @@
7
7
  * the SSR entry for HTML generation.
8
8
  */
9
9
  import fs from "node:fs";
10
+ import { fileURLToPath } from "node:url";
10
11
  import { generateDevOriginCheckCode } from "./dev-origin-check.js";
11
12
  import { generateSafeRegExpCode, generateMiddlewareMatcherCode, generateNormalizePathCode } from "./middleware-codegen.js";
12
13
  /**
@@ -200,7 +201,7 @@ import { ErrorBoundary, NotFoundBoundary } from "vinext/error-boundary";
200
201
  import { LayoutSegmentProvider } from "vinext/layout-segment-context";
201
202
  import { MetadataHead, mergeMetadata, resolveModuleMetadata, ViewportHead, mergeViewport, resolveModuleViewport } from "vinext/metadata";
202
203
  ${middlewarePath ? `import * as middlewareModule from ${JSON.stringify(middlewarePath.replace(/\\/g, "/"))};` : ""}
203
- ${effectiveMetaRoutes.length > 0 ? `import { sitemapToXml, robotsToText, manifestToJson } from ${JSON.stringify(new URL("./metadata-routes.js", import.meta.url).pathname.replace(/\\/g, "/"))};` : ""}
204
+ ${effectiveMetaRoutes.length > 0 ? `import { sitemapToXml, robotsToText, manifestToJson } from ${JSON.stringify(fileURLToPath(new URL("./metadata-routes.js", import.meta.url)).replace(/\\/g, "/"))};` : ""}
204
205
  import { _consumeRequestScopedCacheLife, _runWithCacheState } from "next/cache";
205
206
  import { runWithFetchCache } from "vinext/fetch-cache";
206
207
  import { runWithPrivateCache as _runWithPrivateCache } from "vinext/cache-runtime";
@@ -516,7 +517,9 @@ async function renderErrorBoundaryPage(route, error, isRscRequest, request) {
516
517
  function matchRoute(url, routes) {
517
518
  const pathname = url.split("?")[0];
518
519
  let normalizedUrl = pathname === "/" ? "/" : pathname.replace(/\\/$/, "");
519
- try { normalizedUrl = decodeURIComponent(normalizedUrl); } catch {}
520
+ // NOTE: Do NOT decodeURIComponent here. The caller is responsible for decoding
521
+ // the pathname exactly once at the request entry point. Decoding again here
522
+ // would cause inconsistent path matching between middleware and routing.
520
523
  for (const route of routes) {
521
524
  const params = matchPattern(normalizedUrl, route.pattern);
522
525
  if (params !== null) return { route, params };
@@ -895,15 +898,15 @@ ${generateNormalizePathCode("modern")}
895
898
 
896
899
  // ── Config pattern matching (redirects, rewrites, headers) ──────────────
897
900
  function __matchConfigPattern(pathname, pattern) {
898
- if (pattern.includes("(") || pattern.includes("\\\\") || /:\\w+[*+][^/]/.test(pattern)) {
901
+ if (pattern.includes("(") || pattern.includes("\\\\") || /:[\\w-]+[*+][^/]/.test(pattern)) {
899
902
  try {
900
903
  const paramNames = [];
901
904
  const regexStr = pattern
902
905
  .replace(/\\./g, "\\\\.")
903
- .replace(/:([a-zA-Z_]\\w*)\\*(?:\\(([^)]+)\\))?/g, (_, name, c) => { paramNames.push(name); return c ? "(" + c + ")" : "(.*)"; })
904
- .replace(/:([a-zA-Z_]\\w*)\\+(?:\\(([^)]+)\\))?/g, (_, name, c) => { paramNames.push(name); return c ? "(" + c + ")" : "(.+)"; })
905
- .replace(/:([a-zA-Z_]\\w*)\\(([^)]+)\\)/g, (_, name, c) => { paramNames.push(name); return "(" + c + ")"; })
906
- .replace(/:([a-zA-Z_]\\w*)/g, (_, name) => { paramNames.push(name); return "([^/]+)"; });
906
+ .replace(/:([\\w-]+)\\*(?:\\(([^)]+)\\))?/g, (_, name, c) => { paramNames.push(name); return c ? "(" + c + ")" : "(.*)"; })
907
+ .replace(/:([\\w-]+)\\+(?:\\(([^)]+)\\))?/g, (_, name, c) => { paramNames.push(name); return c ? "(" + c + ")" : "(.+)"; })
908
+ .replace(/:([\\w-]+)\\(([^)]+)\\)/g, (_, name, c) => { paramNames.push(name); return "(" + c + ")"; })
909
+ .replace(/:([\\w-]+)/g, (_, name) => { paramNames.push(name); return "([^/]+)"; });
907
910
  const re = __safeRegExp("^" + regexStr + "$");
908
911
  if (!re) return null;
909
912
  const match = re.exec(pathname);
@@ -913,7 +916,7 @@ function __matchConfigPattern(pathname, pattern) {
913
916
  return params;
914
917
  } catch { /* fall through */ }
915
918
  }
916
- const catchAllMatch = pattern.match(/:([a-zA-Z_]\\w*)(\\*|\\+)$/);
919
+ const catchAllMatch = pattern.match(/:([\\w-]+)(\\*|\\+)$/);
917
920
  if (catchAllMatch) {
918
921
  const prefix = pattern.slice(0, pattern.lastIndexOf(":"));
919
922
  const paramName = catchAllMatch[1];
@@ -922,7 +925,8 @@ function __matchConfigPattern(pathname, pattern) {
922
925
  const rest = pathname.slice(prefix.replace(/\\/$/, "").length);
923
926
  if (isPlus && (!rest || rest === "/")) return null;
924
927
  let restValue = rest.startsWith("/") ? rest.slice(1) : rest;
925
- try { restValue = decodeURIComponent(restValue); } catch {}
928
+ // NOTE: Do NOT decodeURIComponent here. The pathname is already decoded at
929
+ // the request entry point. Decoding again would produce incorrect param values.
926
930
  return { [paramName]: restValue };
927
931
  }
928
932
  const parts = pattern.split("/");
@@ -1144,7 +1148,7 @@ function __applyConfigHeaders(pathname) {
1144
1148
  .replace(/\\+/g, "\\\\+")
1145
1149
  .replace(/\\?/g, "\\\\?")
1146
1150
  .replace(/\\*/g, ".*")
1147
- .replace(/:[a-zA-Z_]\\w*/g, "[^/]+")
1151
+ .replace(/:[\\w-]+/g, "[^/]+")
1148
1152
  .replace(/___GROUP_(\\d+)___/g, (_, idx) => "(" + groups[Number(idx)] + ")");
1149
1153
  const sourceRegex = __safeRegExp("^" + escaped + "$");
1150
1154
  if (sourceRegex && sourceRegex.test(pathname)) result.push(...rule.headers);
@@ -1170,7 +1174,8 @@ export default async function handler(request) {
1170
1174
  // and Next.js doesn't apply custom headers to redirects anyway.
1171
1175
  if (__configHeaders.length && response && response.headers && !(response.status >= 300 && response.status < 400)) {
1172
1176
  const url = new URL(request.url);
1173
- let pathname = url.pathname;
1177
+ let pathname;
1178
+ try { pathname = __normalizePath(decodeURIComponent(url.pathname)); } catch { pathname = url.pathname; }
1174
1179
  ${bp ? `if (pathname.startsWith(${JSON.stringify(bp)})) pathname = pathname.slice(${JSON.stringify(bp)}.length) || "/";` : ""}
1175
1180
  const extraHeaders = __applyConfigHeaders(pathname);
1176
1181
  for (const h of extraHeaders) {
@@ -1269,26 +1274,27 @@ async function _handleRequest(request) {
1269
1274
  let _middlewareRewriteStatus = null;
1270
1275
 
1271
1276
  ${middlewarePath ? `
1272
- // Run proxy/middleware if present and path matches
1277
+ // Run proxy/middleware if present and path matches
1273
1278
  const middlewareFn = middlewareModule.default || middlewareModule.proxy || middlewareModule.middleware;
1274
1279
  const middlewareMatcher = middlewareModule.config?.matcher;
1275
1280
  if (typeof middlewareFn === "function" && matchesMiddleware(cleanPathname, middlewareMatcher)) {
1276
1281
  try {
1277
1282
  // Wrap in NextRequest so middleware gets .nextUrl, .cookies, .geo, .ip, etc.
1278
- // Strip .rsc suffix from the URL it's an internal transport detail that
1279
- // middleware should never see (matches Next.js behavior).
1280
- let mwRequest = request;
1281
- if (isRscRequest && pathname.endsWith(".rsc")) {
1282
- const mwUrl = new URL(request.url);
1283
- mwUrl.pathname = cleanPathname;
1284
- mwRequest = new Request(mwUrl, request);
1285
- }
1283
+ // Always construct a new Request with the fully decoded + normalized pathname
1284
+ // so middleware and the router see the same canonical path.
1285
+ const mwUrl = new URL(request.url);
1286
+ mwUrl.pathname = cleanPathname;
1287
+ const mwRequest = new Request(mwUrl, request);
1286
1288
  const nextRequest = mwRequest instanceof NextRequest ? mwRequest : new NextRequest(mwRequest);
1287
1289
  const mwResponse = await middlewareFn(nextRequest);
1288
1290
  if (mwResponse) {
1289
1291
  // Check for x-middleware-next (continue)
1290
1292
  if (mwResponse.headers.get("x-middleware-next") === "1") {
1291
- // Middleware wants to continue - save headers to merge into final response
1293
+ // Middleware wants to continue collect all headers except the two
1294
+ // control headers we've already consumed. x-middleware-request-*
1295
+ // headers are kept so applyMiddlewareRequestHeaders() can unpack them;
1296
+ // the blanket strip loop after that call removes every remaining
1297
+ // x-middleware-* header before the set is merged into the response.
1292
1298
  _middlewareResponseHeaders = new Headers();
1293
1299
  for (const [key, value] of mwResponse.headers) {
1294
1300
  if (key !== "x-middleware-next" && key !== "x-middleware-rewrite") {
@@ -1552,7 +1558,9 @@ async function _handleRequest(request) {
1552
1558
  err instanceof Error ? err : new Error(String(err)),
1553
1559
  { path: cleanPathname, method: request.method, headers: Object.fromEntries(request.headers.entries()) },
1554
1560
  { routerKind: "App Router", routePath: cleanPathname, routeType: "action" },
1555
- ).catch(() => {});
1561
+ ).catch((reportErr) => {
1562
+ console.error("[vinext] Failed to report server action error:", reportErr);
1563
+ });
1556
1564
  setHeadersContext(null);
1557
1565
  setNavigationContext(null);
1558
1566
  return new Response(
@@ -1716,7 +1724,9 @@ async function _handleRequest(request) {
1716
1724
  err instanceof Error ? err : new Error(String(err)),
1717
1725
  { path: cleanPathname, method: request.method, headers: Object.fromEntries(request.headers.entries()) },
1718
1726
  { routerKind: "App Router", routePath: route.pattern, routeType: "route" },
1719
- ).catch(() => {});
1727
+ ).catch((reportErr) => {
1728
+ console.error("[vinext] Failed to report route handler error:", reportErr);
1729
+ });
1720
1730
  return new Response(null, { status: 500 });
1721
1731
  }
1722
1732
  }
@@ -2191,7 +2201,7 @@ if (import.meta.hot) {
2191
2201
  export function generateSsrEntry() {
2192
2202
  return `
2193
2203
  import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
2194
- import { renderToReadableStream } from "react-dom/server.edge";
2204
+ import { renderToReadableStream, renderToStaticMarkup } from "react-dom/server.edge";
2195
2205
  import { setNavigationContext } from "next/navigation";
2196
2206
  import { runWithNavigationContext as _runWithNavCtx } from "vinext/navigation-state";
2197
2207
  import { safeJsonStringify } from "vinext/html";
@@ -2396,7 +2406,6 @@ export async function handleSsr(rscStream, navContext, fontData) {
2396
2406
  const insertedElements = flushServerInsertedHTML();
2397
2407
 
2398
2408
  // Render the inserted elements to HTML strings
2399
- const { renderToStaticMarkup } = await import("react-dom/server.edge");
2400
2409
  const { createElement, Fragment } = await import("react");
2401
2410
  let insertedHTML = "";
2402
2411
  for (const el of insertedElements) {
@@ -2820,6 +2829,7 @@ async function main() {
2820
2829
  if (!navResponse) {
2821
2830
  navResponse = await fetch(rscUrl, {
2822
2831
  headers: { Accept: "text/x-component" },
2832
+ credentials: "include",
2823
2833
  });
2824
2834
  }
2825
2835