vinext 0.0.23 → 0.0.25

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 (105) hide show
  1. package/README.md +24 -0
  2. package/dist/check.d.ts.map +1 -1
  3. package/dist/check.js +3 -2
  4. package/dist/check.js.map +1 -1
  5. package/dist/client/entry.js +1 -1
  6. package/dist/client/entry.js.map +1 -1
  7. package/dist/config/config-matchers.d.ts +21 -0
  8. package/dist/config/config-matchers.d.ts.map +1 -1
  9. package/dist/config/config-matchers.js +59 -9
  10. package/dist/config/config-matchers.js.map +1 -1
  11. package/dist/config/next-config.d.ts +8 -2
  12. package/dist/config/next-config.d.ts.map +1 -1
  13. package/dist/config/next-config.js +90 -35
  14. package/dist/config/next-config.js.map +1 -1
  15. package/dist/deploy.d.ts +10 -0
  16. package/dist/deploy.d.ts.map +1 -1
  17. package/dist/deploy.js +140 -56
  18. package/dist/deploy.js.map +1 -1
  19. package/dist/index.d.ts +14 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +195 -25
  22. package/dist/index.js.map +1 -1
  23. package/dist/plugins/async-hooks-stub.d.ts +16 -0
  24. package/dist/plugins/async-hooks-stub.d.ts.map +1 -0
  25. package/dist/plugins/async-hooks-stub.js +45 -0
  26. package/dist/plugins/async-hooks-stub.js.map +1 -0
  27. package/dist/routing/app-router.d.ts +12 -6
  28. package/dist/routing/app-router.d.ts.map +1 -1
  29. package/dist/routing/app-router.js +19 -40
  30. package/dist/routing/app-router.js.map +1 -1
  31. package/dist/routing/pages-router.d.ts.map +1 -1
  32. package/dist/routing/pages-router.js +3 -9
  33. package/dist/routing/pages-router.js.map +1 -1
  34. package/dist/routing/utils.d.ts +9 -0
  35. package/dist/routing/utils.d.ts.map +1 -1
  36. package/dist/routing/utils.js +10 -0
  37. package/dist/routing/utils.js.map +1 -1
  38. package/dist/server/api-handler.d.ts.map +1 -1
  39. package/dist/server/api-handler.js +6 -0
  40. package/dist/server/api-handler.js.map +1 -1
  41. package/dist/server/app-dev-server.d.ts +2 -2
  42. package/dist/server/app-dev-server.d.ts.map +1 -1
  43. package/dist/server/app-dev-server.js +294 -133
  44. package/dist/server/app-dev-server.js.map +1 -1
  45. package/dist/server/dev-module-runner.d.ts +84 -0
  46. package/dist/server/dev-module-runner.d.ts.map +1 -0
  47. package/dist/server/dev-module-runner.js +105 -0
  48. package/dist/server/dev-module-runner.js.map +1 -0
  49. package/dist/server/dev-server.js.map +1 -1
  50. package/dist/server/instrumentation.d.ts +52 -9
  51. package/dist/server/instrumentation.d.ts.map +1 -1
  52. package/dist/server/instrumentation.js +52 -15
  53. package/dist/server/instrumentation.js.map +1 -1
  54. package/dist/server/middleware.d.ts +7 -3
  55. package/dist/server/middleware.d.ts.map +1 -1
  56. package/dist/server/middleware.js +16 -6
  57. package/dist/server/middleware.js.map +1 -1
  58. package/dist/server/prod-server.d.ts +8 -2
  59. package/dist/server/prod-server.d.ts.map +1 -1
  60. package/dist/server/prod-server.js +56 -35
  61. package/dist/server/prod-server.js.map +1 -1
  62. package/dist/server/worker-utils.d.ts +15 -0
  63. package/dist/server/worker-utils.d.ts.map +1 -0
  64. package/dist/server/worker-utils.js +41 -0
  65. package/dist/server/worker-utils.js.map +1 -0
  66. package/dist/shims/cache.d.ts.map +1 -1
  67. package/dist/shims/cache.js +14 -2
  68. package/dist/shims/cache.js.map +1 -1
  69. package/dist/shims/fetch-cache.d.ts.map +1 -1
  70. package/dist/shims/fetch-cache.js +139 -29
  71. package/dist/shims/fetch-cache.js.map +1 -1
  72. package/dist/shims/form.d.ts.map +1 -1
  73. package/dist/shims/form.js +2 -3
  74. package/dist/shims/form.js.map +1 -1
  75. package/dist/shims/headers.d.ts +6 -0
  76. package/dist/shims/headers.d.ts.map +1 -1
  77. package/dist/shims/headers.js +8 -0
  78. package/dist/shims/headers.js.map +1 -1
  79. package/dist/shims/layout-segment-context.d.ts +5 -4
  80. package/dist/shims/layout-segment-context.d.ts.map +1 -1
  81. package/dist/shims/layout-segment-context.js +6 -5
  82. package/dist/shims/layout-segment-context.js.map +1 -1
  83. package/dist/shims/link.d.ts.map +1 -1
  84. package/dist/shims/link.js +32 -17
  85. package/dist/shims/link.js.map +1 -1
  86. package/dist/shims/navigation.d.ts +14 -11
  87. package/dist/shims/navigation.d.ts.map +1 -1
  88. package/dist/shims/navigation.js +122 -102
  89. package/dist/shims/navigation.js.map +1 -1
  90. package/dist/shims/router.d.ts.map +1 -1
  91. package/dist/shims/router.js +37 -21
  92. package/dist/shims/router.js.map +1 -1
  93. package/dist/shims/server.d.ts +2 -0
  94. package/dist/shims/server.d.ts.map +1 -1
  95. package/dist/shims/server.js +4 -0
  96. package/dist/shims/server.js.map +1 -1
  97. package/dist/shims/url-utils.d.ts +13 -0
  98. package/dist/shims/url-utils.d.ts.map +1 -0
  99. package/dist/shims/url-utils.js +28 -0
  100. package/dist/shims/url-utils.js.map +1 -0
  101. package/dist/utils/project.d.ts +13 -1
  102. package/dist/utils/project.d.ts.map +1 -1
  103. package/dist/utils/project.js +63 -13
  104. package/dist/utils/project.js.map +1 -1
  105. package/package.json +6 -1
@@ -1 +1 @@
1
- {"version":3,"file":"pages-router.js","sourceRoot":"","sources":["../../src/routing/pages-router.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport { routePrecedence } from \"./utils.js\";\nimport {\n createValidFileMatcher,\n scanWithExtensions,\n type ValidFileMatcher,\n} from \"./file-matcher.js\";\n\nexport interface Route {\n /** URL pattern, e.g. \"/\" or \"/about\" or \"/posts/:id\" */\n pattern: string;\n /** Absolute file path to the page component */\n filePath: string;\n /** Whether this is a dynamic route */\n isDynamic: boolean;\n /** Parameter names for dynamic segments */\n params: string[];\n}\n\n// Route cache — invalidated when pages directory changes\nconst routeCache = new Map<string, { routes: Route[]; promise: Promise<Route[]> }>();\n\n/**\n * Invalidate cached routes for a given pages directory.\n * Called by the file watcher when pages are added/removed.\n */\nexport function invalidateRouteCache(pagesDir: string): void {\n for (const key of routeCache.keys()) {\n if (key.startsWith(`pages:${pagesDir}:`) || key.startsWith(`api:${pagesDir}:`)) {\n routeCache.delete(key);\n }\n }\n}\n\n/**\n * Scan the pages/ directory and return a list of routes.\n * Results are cached — call invalidateRouteCache() when files change.\n *\n * Follows Next.js Pages Router conventions:\n * - pages/index.tsx -> /\n * - pages/about.tsx -> /about\n * - pages/posts/[id].tsx -> /posts/:id\n * - pages/[...slug].tsx -> /:slug+\n * - Ignores _app.tsx, _document.tsx, _error.tsx, files starting with _\n * - Ignores pages/api/ (handled separately later)\n */\nexport async function pagesRouter(\n pagesDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<Route[]> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const cacheKey = `pages:${pagesDir}:${JSON.stringify(matcher.extensions)}`;\n const cached = routeCache.get(cacheKey);\n if (cached) return cached.promise;\n\n const promise = scanPageRoutes(pagesDir, matcher);\n routeCache.set(cacheKey, { routes: [], promise });\n const routes = await promise;\n routeCache.set(cacheKey, { routes, promise });\n return routes;\n}\n\nasync function scanPageRoutes(\n pagesDir: string,\n matcher: ValidFileMatcher,\n): Promise<Route[]> {\n const routes: Route[] = [];\n\n // Use function form of exclude for Node < 22.14 compatibility (string arrays require >= 22.14)\n for await (const file of scanWithExtensions(\n \"**/*\",\n pagesDir,\n matcher.extensions,\n (name: string) => name === \"api\" || name.startsWith(\"_\"),\n )) {\n const route = fileToRoute(file, pagesDir, matcher);\n if (route) routes.push(route);\n }\n\n // Sort: static routes first, then dynamic, then catch-all\n routes.sort((a, b) => {\n const diff = routePrecedence(a.pattern) - routePrecedence(b.pattern);\n return diff !== 0 ? diff : a.pattern.localeCompare(b.pattern);\n });\n\n return routes;\n}\n\n/**\n * Convert a file path relative to pages/ into a Route.\n */\nfunction fileToRoute(\n file: string,\n pagesDir: string,\n matcher: ValidFileMatcher,\n): Route | null {\n // Remove extension\n const withoutExt = matcher.stripExtension(file);\n if (withoutExt === file) return null;\n\n // Convert to URL segments\n const segments = withoutExt.split(path.sep);\n\n // Handle index files: pages/index.tsx -> /\n const lastSegment = segments[segments.length - 1];\n if (lastSegment === \"index\") {\n segments.pop();\n }\n\n const params: string[] = [];\n let isDynamic = false;\n\n // Convert Next.js dynamic segments to URL patterns\n const urlSegments = segments.map((segment) => {\n // Catch-all: [...slug] -> :slug+ (param names may contain hyphens)\n const catchAllMatch = segment.match(/^\\[\\.\\.\\.([\\w-]+)\\]$/);\n if (catchAllMatch) {\n isDynamic = true;\n params.push(catchAllMatch[1]);\n return `:${catchAllMatch[1]}+`;\n }\n\n // Optional catch-all: [[...slug]] -> :slug* (param names may contain hyphens)\n const optionalCatchAllMatch = segment.match(/^\\[\\[\\.\\.\\.([\\w-]+)\\]\\]$/);\n if (optionalCatchAllMatch) {\n isDynamic = true;\n params.push(optionalCatchAllMatch[1]);\n return `:${optionalCatchAllMatch[1]}*`;\n }\n\n // Dynamic segment: [id] -> :id (param names may contain hyphens)\n const dynamicMatch = segment.match(/^\\[([\\w-]+)\\]$/);\n if (dynamicMatch) {\n isDynamic = true;\n params.push(dynamicMatch[1]);\n return `:${dynamicMatch[1]}`;\n }\n\n return segment;\n });\n\n const pattern = \"/\" + urlSegments.join(\"/\");\n\n return {\n pattern: pattern === \"/\" ? \"/\" : pattern,\n filePath: path.join(pagesDir, file),\n isDynamic,\n params,\n };\n}\n\n/**\n * Match a URL path against a route pattern.\n * Returns the matched params or null if no match.\n */\nexport function matchRoute(\n url: string,\n routes: Route[],\n): { route: Route; params: Record<string, string | string[]> } | null {\n // Normalize: strip query string and trailing slash\n const pathname = url.split(\"?\")[0];\n let normalizedUrl =\n pathname === \"/\" ? \"/\" : pathname.replace(/\\/$/, \"\");\n try { normalizedUrl = decodeURIComponent(normalizedUrl); } catch { /* malformed percent-encoding — match as-is */ }\n\n for (const route of routes) {\n const params = matchPattern(normalizedUrl, route.pattern);\n if (params !== null) {\n return { route, params };\n }\n }\n\n return null;\n}\n\n/**\n * Scan the pages/api/ directory and return API routes.\n * Results are cached — call invalidateRouteCache() when files change.\n *\n * Follows Next.js conventions:\n * - pages/api/hello.ts -> /api/hello\n * - pages/api/users/[id].ts -> /api/users/:id\n */\nexport async function apiRouter(\n pagesDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<Route[]> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const cacheKey = `api:${pagesDir}:${JSON.stringify(matcher.extensions)}`;\n const cached = routeCache.get(cacheKey);\n if (cached) return cached.promise;\n\n const promise = scanApiRoutes(pagesDir, matcher);\n routeCache.set(cacheKey, { routes: [], promise });\n const routes = await promise;\n routeCache.set(cacheKey, { routes, promise });\n return routes;\n}\n\nasync function scanApiRoutes(\n pagesDir: string,\n matcher: ValidFileMatcher,\n): Promise<Route[]> {\n const apiDir = path.join(pagesDir, \"api\");\n let files: string[];\n try {\n files = [];\n for await (const file of scanWithExtensions(\n \"**/*\",\n apiDir,\n matcher.extensions,\n )) {\n files.push(file);\n }\n } catch {\n files = [];\n }\n\n const routes: Route[] = [];\n\n for (const file of files) {\n // Reuse fileToRoute but pretend the file is under a virtual \"api/\" prefix\n const route = fileToRoute(path.join(\"api\", file), pagesDir, matcher);\n if (route) {\n routes.push(route);\n }\n }\n\n // Sort same as page routes\n routes.sort((a, b) => {\n const diff = routePrecedence(a.pattern) - routePrecedence(b.pattern);\n return diff !== 0 ? diff : a.pattern.localeCompare(b.pattern);\n });\n\n return routes;\n}\n\nfunction matchPattern(\n url: string,\n pattern: string,\n): Record<string, string | string[]> | null {\n const urlParts = url.split(\"/\").filter(Boolean);\n const patternParts = pattern.split(\"/\").filter(Boolean);\n\n const params: Record<string, string | string[]> = Object.create(null);\n\n for (let i = 0; i < patternParts.length; i++) {\n const pp = patternParts[i];\n\n // Catch-all: :slug+\n if (pp.endsWith(\"+\")) {\n const paramName = pp.slice(1, -1);\n const remaining = urlParts.slice(i);\n if (remaining.length === 0) return null;\n params[paramName] = remaining;\n return params;\n }\n\n // Optional catch-all: :slug*\n if (pp.endsWith(\"*\")) {\n const paramName = pp.slice(1, -1);\n const remaining = urlParts.slice(i);\n params[paramName] = remaining;\n return params;\n }\n\n // Dynamic segment: :id\n if (pp.startsWith(\":\")) {\n const paramName = pp.slice(1);\n if (i >= urlParts.length) return null;\n params[paramName] = urlParts[i];\n continue;\n }\n\n // Static segment\n if (i >= urlParts.length || urlParts[i] !== pp) return null;\n }\n\n // All pattern parts matched - check url doesn't have extra segments\n if (urlParts.length !== patternParts.length) return null;\n\n return params;\n}\n\n/**\n * Convert internal route pattern (e.g., \"/posts/:id\", \"/docs/:slug+\")\n * to Next.js bracket format (e.g., \"/posts/[id]\", \"/docs/[...slug]\").\n * Used for __NEXT_DATA__.page which apps expect in Next.js format.\n */\nexport function patternToNextFormat(pattern: string): string {\n return pattern\n .replace(/:([\\w-]+)\\*/g, \"[[...$1]]\") // optional catch-all :slug* -> [[...slug]]\n .replace(/:([\\w-]+)\\+/g, \"[...$1]\") // catch-all :slug+ -> [...slug]\n .replace(/:([\\w-]+)/g, \"[$1]\"); // dynamic :id -> [id]\n}\n"]}
1
+ {"version":3,"file":"pages-router.js","sourceRoot":"","sources":["../../src/routing/pages-router.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport { compareRoutes } from \"./utils.js\";\nimport {\n createValidFileMatcher,\n scanWithExtensions,\n type ValidFileMatcher,\n} from \"./file-matcher.js\";\n\nexport interface Route {\n /** URL pattern, e.g. \"/\" or \"/about\" or \"/posts/:id\" */\n pattern: string;\n /** Absolute file path to the page component */\n filePath: string;\n /** Whether this is a dynamic route */\n isDynamic: boolean;\n /** Parameter names for dynamic segments */\n params: string[];\n}\n\n// Route cache — invalidated when pages directory changes\nconst routeCache = new Map<string, { routes: Route[]; promise: Promise<Route[]> }>();\n\n/**\n * Invalidate cached routes for a given pages directory.\n * Called by the file watcher when pages are added/removed.\n */\nexport function invalidateRouteCache(pagesDir: string): void {\n for (const key of routeCache.keys()) {\n if (key.startsWith(`pages:${pagesDir}:`) || key.startsWith(`api:${pagesDir}:`)) {\n routeCache.delete(key);\n }\n }\n}\n\n/**\n * Scan the pages/ directory and return a list of routes.\n * Results are cached — call invalidateRouteCache() when files change.\n *\n * Follows Next.js Pages Router conventions:\n * - pages/index.tsx -> /\n * - pages/about.tsx -> /about\n * - pages/posts/[id].tsx -> /posts/:id\n * - pages/[...slug].tsx -> /:slug+\n * - Ignores _app.tsx, _document.tsx, _error.tsx, files starting with _\n * - Ignores pages/api/ (handled separately later)\n */\nexport async function pagesRouter(\n pagesDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<Route[]> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const cacheKey = `pages:${pagesDir}:${JSON.stringify(matcher.extensions)}`;\n const cached = routeCache.get(cacheKey);\n if (cached) return cached.promise;\n\n const promise = scanPageRoutes(pagesDir, matcher);\n routeCache.set(cacheKey, { routes: [], promise });\n const routes = await promise;\n routeCache.set(cacheKey, { routes, promise });\n return routes;\n}\n\nasync function scanPageRoutes(\n pagesDir: string,\n matcher: ValidFileMatcher,\n): Promise<Route[]> {\n const routes: Route[] = [];\n\n // Use function form of exclude for Node < 22.14 compatibility (string arrays require >= 22.14)\n for await (const file of scanWithExtensions(\n \"**/*\",\n pagesDir,\n matcher.extensions,\n (name: string) => name === \"api\" || name.startsWith(\"_\"),\n )) {\n const route = fileToRoute(file, pagesDir, matcher);\n if (route) routes.push(route);\n }\n\n // Sort: static routes first, then dynamic, then catch-all\n routes.sort(compareRoutes);\n\n return routes;\n}\n\n/**\n * Convert a file path relative to pages/ into a Route.\n */\nfunction fileToRoute(\n file: string,\n pagesDir: string,\n matcher: ValidFileMatcher,\n): Route | null {\n // Remove extension\n const withoutExt = matcher.stripExtension(file);\n if (withoutExt === file) return null;\n\n // Convert to URL segments\n const segments = withoutExt.split(path.sep);\n\n // Handle index files: pages/index.tsx -> /\n const lastSegment = segments[segments.length - 1];\n if (lastSegment === \"index\") {\n segments.pop();\n }\n\n const params: string[] = [];\n let isDynamic = false;\n\n // Convert Next.js dynamic segments to URL patterns\n const urlSegments = segments.map((segment) => {\n // Catch-all: [...slug] -> :slug+ (param names may contain hyphens)\n const catchAllMatch = segment.match(/^\\[\\.\\.\\.([\\w-]+)\\]$/);\n if (catchAllMatch) {\n isDynamic = true;\n params.push(catchAllMatch[1]);\n return `:${catchAllMatch[1]}+`;\n }\n\n // Optional catch-all: [[...slug]] -> :slug* (param names may contain hyphens)\n const optionalCatchAllMatch = segment.match(/^\\[\\[\\.\\.\\.([\\w-]+)\\]\\]$/);\n if (optionalCatchAllMatch) {\n isDynamic = true;\n params.push(optionalCatchAllMatch[1]);\n return `:${optionalCatchAllMatch[1]}*`;\n }\n\n // Dynamic segment: [id] -> :id (param names may contain hyphens)\n const dynamicMatch = segment.match(/^\\[([\\w-]+)\\]$/);\n if (dynamicMatch) {\n isDynamic = true;\n params.push(dynamicMatch[1]);\n return `:${dynamicMatch[1]}`;\n }\n\n return segment;\n });\n\n const pattern = \"/\" + urlSegments.join(\"/\");\n\n return {\n pattern: pattern === \"/\" ? \"/\" : pattern,\n filePath: path.join(pagesDir, file),\n isDynamic,\n params,\n };\n}\n\n/**\n * Match a URL path against a route pattern.\n * Returns the matched params or null if no match.\n */\nexport function matchRoute(\n url: string,\n routes: Route[],\n): { route: Route; params: Record<string, string | string[]> } | null {\n // Normalize: strip query string and trailing slash\n const pathname = url.split(\"?\")[0];\n let normalizedUrl =\n pathname === \"/\" ? \"/\" : pathname.replace(/\\/$/, \"\");\n try { normalizedUrl = decodeURIComponent(normalizedUrl); } catch { /* malformed percent-encoding — match as-is */ }\n\n for (const route of routes) {\n const params = matchPattern(normalizedUrl, route.pattern);\n if (params !== null) {\n return { route, params };\n }\n }\n\n return null;\n}\n\n/**\n * Scan the pages/api/ directory and return API routes.\n * Results are cached — call invalidateRouteCache() when files change.\n *\n * Follows Next.js conventions:\n * - pages/api/hello.ts -> /api/hello\n * - pages/api/users/[id].ts -> /api/users/:id\n */\nexport async function apiRouter(\n pagesDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<Route[]> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const cacheKey = `api:${pagesDir}:${JSON.stringify(matcher.extensions)}`;\n const cached = routeCache.get(cacheKey);\n if (cached) return cached.promise;\n\n const promise = scanApiRoutes(pagesDir, matcher);\n routeCache.set(cacheKey, { routes: [], promise });\n const routes = await promise;\n routeCache.set(cacheKey, { routes, promise });\n return routes;\n}\n\nasync function scanApiRoutes(\n pagesDir: string,\n matcher: ValidFileMatcher,\n): Promise<Route[]> {\n const apiDir = path.join(pagesDir, \"api\");\n let files: string[];\n try {\n files = [];\n for await (const file of scanWithExtensions(\n \"**/*\",\n apiDir,\n matcher.extensions,\n )) {\n files.push(file);\n }\n } catch {\n files = [];\n }\n\n const routes: Route[] = [];\n\n for (const file of files) {\n // Reuse fileToRoute but pretend the file is under a virtual \"api/\" prefix\n const route = fileToRoute(path.join(\"api\", file), pagesDir, matcher);\n if (route) {\n routes.push(route);\n }\n }\n\n // Sort same as page routes\n routes.sort(compareRoutes);\n\n return routes;\n}\n\nfunction matchPattern(\n url: string,\n pattern: string,\n): Record<string, string | string[]> | null {\n const urlParts = url.split(\"/\").filter(Boolean);\n const patternParts = pattern.split(\"/\").filter(Boolean);\n\n const params: Record<string, string | string[]> = Object.create(null);\n\n for (let i = 0; i < patternParts.length; i++) {\n const pp = patternParts[i];\n\n // Catch-all: :slug+\n if (pp.endsWith(\"+\")) {\n const paramName = pp.slice(1, -1);\n const remaining = urlParts.slice(i);\n if (remaining.length === 0) return null;\n params[paramName] = remaining;\n return params;\n }\n\n // Optional catch-all: :slug*\n if (pp.endsWith(\"*\")) {\n const paramName = pp.slice(1, -1);\n const remaining = urlParts.slice(i);\n params[paramName] = remaining;\n return params;\n }\n\n // Dynamic segment: :id\n if (pp.startsWith(\":\")) {\n const paramName = pp.slice(1);\n if (i >= urlParts.length) return null;\n params[paramName] = urlParts[i];\n continue;\n }\n\n // Static segment\n if (i >= urlParts.length || urlParts[i] !== pp) return null;\n }\n\n // All pattern parts matched - check url doesn't have extra segments\n if (urlParts.length !== patternParts.length) return null;\n\n return params;\n}\n\n/**\n * Convert internal route pattern (e.g., \"/posts/:id\", \"/docs/:slug+\")\n * to Next.js bracket format (e.g., \"/posts/[id]\", \"/docs/[...slug]\").\n * Used for __NEXT_DATA__.page which apps expect in Next.js format.\n */\nexport function patternToNextFormat(pattern: string): string {\n return pattern\n .replace(/:([\\w-]+)\\*/g, \"[[...$1]]\") // optional catch-all :slug* -> [[...slug]]\n .replace(/:([\\w-]+)\\+/g, \"[...$1]\") // catch-all :slug+ -> [...slug]\n .replace(/:([\\w-]+)/g, \"[$1]\"); // dynamic :id -> [id]\n}\n"]}
@@ -22,4 +22,13 @@
22
22
  * even when both share the same "locale-test" static prefix.
23
23
  */
24
24
  export declare function routePrecedence(pattern: string): number;
25
+ /**
26
+ * Sort comparator for routes — lower precedence score sorts first (higher priority).
27
+ * Lexicographic tiebreaker on pattern for determinism.
28
+ *
29
+ * Usage: routes.sort(compareRoutes)
30
+ */
31
+ export declare function compareRoutes<T extends {
32
+ pattern: string;
33
+ }>(a: T, b: T): number;
25
34
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/routing/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+CvD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/routing/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+CvD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,MAAM,CAG/E"}
@@ -67,4 +67,14 @@ export function routePrecedence(pattern) {
67
67
  }
68
68
  return score;
69
69
  }
70
+ /**
71
+ * Sort comparator for routes — lower precedence score sorts first (higher priority).
72
+ * Lexicographic tiebreaker on pattern for determinism.
73
+ *
74
+ * Usage: routes.sort(compareRoutes)
75
+ */
76
+ export function compareRoutes(a, b) {
77
+ const diff = routePrecedence(a.pattern) - routePrecedence(b.pattern);
78
+ return diff !== 0 ? diff : a.pattern.localeCompare(b.pattern);
79
+ }
70
80
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/routing/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,MAAM;QACnE,iBAAiB,EAAE,CAAC;IACtB,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,KAAK,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,8BAA8B;QACnD,CAAC;aAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,KAAK,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,mCAAmC;QACxD,CAAC;aAAM,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,KAAK,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,qCAAqC;QACzD,CAAC;aAAM,IAAI,CAAC,IAAI,iBAAiB,EAAE,CAAC;YAClC,qEAAqE;YACrE,wDAAwD;YACxD,wEAAwE;YACxE,sEAAsE;YACtE,qEAAqE;YACrE,sEAAsE;YACtE,KAAK,IAAI,GAAG,CAAC;QACf,CAAC;QACD,oEAAoE;IACtE,CAAC;IAED,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,wEAAwE;IACxE,EAAE;IACF,uEAAuE;IACvE,wEAAwE;IACxE,0EAA0E;IAC1E,+DAA+D;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAC1B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAC/D,CAAC;IACF,IAAI,SAAS,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;QACvC,KAAK,IAAI,iBAAiB,GAAG,EAAE,CAAC;IAClC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/**\n * Route precedence — lower score is higher priority.\n * Matches Next.js specificity rules:\n * 1. Static routes first (scored by segment count, more = more specific)\n * 2. Dynamic segments penalized by position\n * 3. Catch-all comes after dynamic\n * 4. Optional catch-all last\n * 5. Lexicographic tiebreaker for determinism\n *\n * Key insight: routes with a static prefix before a dynamic/catch-all segment\n * should have higher priority than bare dynamic/catch-all routes at the same\n * depth. E.g., /_sites/:subdomain should match before /:subdomain, and\n * /_sites/:subdomain/:slug* should match before /:slug*.\n *\n * The static-prefix reduction uses a small value (-50 per segment) so that:\n * - It beats the per-dynamic-segment penalty (100), placing prefix routes\n * above their no-prefix equivalents.\n * - It never goes negative, so purely-static routes (score 0) always win.\n * - It is small enough that infix-static bonuses (-500) and catch-all\n * penalties (1000+) are not swamped, preserving their relative ordering.\n * E.g. /:locale/blog/:path+ (with infix \"blog\") correctly beats /:locale/:path+\n * even when both share the same \"locale-test\" static prefix.\n */\nexport function routePrecedence(pattern: string): number {\n const parts = pattern.split(\"/\").filter(Boolean);\n let score = 0;\n\n let staticPrefixCount = 0;\n for (const p of parts) {\n if (p.startsWith(\":\") || p.endsWith(\"+\") || p.endsWith(\"*\")) break;\n staticPrefixCount++;\n }\n\n for (let i = 0; i < parts.length; i++) {\n const p = parts[i];\n if (p.endsWith(\"+\")) {\n score += 1000 + i; // catch-all: moderate penalty\n } else if (p.endsWith(\"*\")) {\n score += 2000 + i; // optional catch-all: high penalty\n } else if (p.startsWith(\":\")) {\n score += 100 + i; // dynamic: small penalty by position\n } else if (i >= staticPrefixCount) {\n // Static segment interleaved after a dynamic segment (infix static).\n // Boost priority — more specific than a bare catch-all.\n // The -500 compounds for each infix static segment, so routes with more\n // static infixes score lower (higher priority) than those with fewer.\n // E.g. /:a/x/y/:b+ (-1000) beats /:a/x/:b+ (-500) beats /:a/:b+ (0).\n // This is intentional: more static constraints = more specific route.\n score -= 500;\n }\n // Static prefix segments (i < staticPrefixCount) are handled below.\n }\n\n // Apply a small reduction per static-prefix segment for routes that also\n // contain dynamic segments. This ensures /_sites/:subdomain sorts above\n // /:subdomain, and /_sites/:slug* sorts above /:slug*, while keeping the\n // final score positive (so purely-static routes at score=0 always win).\n //\n // 50 is deliberately smaller than the dynamic-segment penalty (100) so\n // one static prefix segment is enough to beat one bare dynamic segment,\n // and smaller than the infix-static bonus (500) so that infix ordering is\n // not disturbed between two routes that share the same prefix.\n const isDynamic = parts.some(\n (p) => p.startsWith(\":\") || p.endsWith(\"+\") || p.endsWith(\"*\"),\n );\n if (isDynamic && staticPrefixCount > 0) {\n score -= staticPrefixCount * 50;\n }\n\n return score;\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/routing/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,MAAM;QACnE,iBAAiB,EAAE,CAAC;IACtB,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,KAAK,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,8BAA8B;QACnD,CAAC;aAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,KAAK,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,mCAAmC;QACxD,CAAC;aAAM,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,KAAK,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,qCAAqC;QACzD,CAAC;aAAM,IAAI,CAAC,IAAI,iBAAiB,EAAE,CAAC;YAClC,qEAAqE;YACrE,wDAAwD;YACxD,wEAAwE;YACxE,sEAAsE;YACtE,qEAAqE;YACrE,sEAAsE;YACtE,KAAK,IAAI,GAAG,CAAC;QACf,CAAC;QACD,oEAAoE;IACtE,CAAC;IAED,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,wEAAwE;IACxE,EAAE;IACF,uEAAuE;IACvE,wEAAwE;IACxE,0EAA0E;IAC1E,+DAA+D;IAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAC1B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAC/D,CAAC;IACF,IAAI,SAAS,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;QACvC,KAAK,IAAI,iBAAiB,GAAG,EAAE,CAAC;IAClC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAgC,CAAI,EAAE,CAAI;IACrE,MAAM,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACrE,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AAChE,CAAC","sourcesContent":["/**\n * Route precedence — lower score is higher priority.\n * Matches Next.js specificity rules:\n * 1. Static routes first (scored by segment count, more = more specific)\n * 2. Dynamic segments penalized by position\n * 3. Catch-all comes after dynamic\n * 4. Optional catch-all last\n * 5. Lexicographic tiebreaker for determinism\n *\n * Key insight: routes with a static prefix before a dynamic/catch-all segment\n * should have higher priority than bare dynamic/catch-all routes at the same\n * depth. E.g., /_sites/:subdomain should match before /:subdomain, and\n * /_sites/:subdomain/:slug* should match before /:slug*.\n *\n * The static-prefix reduction uses a small value (-50 per segment) so that:\n * - It beats the per-dynamic-segment penalty (100), placing prefix routes\n * above their no-prefix equivalents.\n * - It never goes negative, so purely-static routes (score 0) always win.\n * - It is small enough that infix-static bonuses (-500) and catch-all\n * penalties (1000+) are not swamped, preserving their relative ordering.\n * E.g. /:locale/blog/:path+ (with infix \"blog\") correctly beats /:locale/:path+\n * even when both share the same \"locale-test\" static prefix.\n */\nexport function routePrecedence(pattern: string): number {\n const parts = pattern.split(\"/\").filter(Boolean);\n let score = 0;\n\n let staticPrefixCount = 0;\n for (const p of parts) {\n if (p.startsWith(\":\") || p.endsWith(\"+\") || p.endsWith(\"*\")) break;\n staticPrefixCount++;\n }\n\n for (let i = 0; i < parts.length; i++) {\n const p = parts[i];\n if (p.endsWith(\"+\")) {\n score += 1000 + i; // catch-all: moderate penalty\n } else if (p.endsWith(\"*\")) {\n score += 2000 + i; // optional catch-all: high penalty\n } else if (p.startsWith(\":\")) {\n score += 100 + i; // dynamic: small penalty by position\n } else if (i >= staticPrefixCount) {\n // Static segment interleaved after a dynamic segment (infix static).\n // Boost priority — more specific than a bare catch-all.\n // The -500 compounds for each infix static segment, so routes with more\n // static infixes score lower (higher priority) than those with fewer.\n // E.g. /:a/x/y/:b+ (-1000) beats /:a/x/:b+ (-500) beats /:a/:b+ (0).\n // This is intentional: more static constraints = more specific route.\n score -= 500;\n }\n // Static prefix segments (i < staticPrefixCount) are handled below.\n }\n\n // Apply a small reduction per static-prefix segment for routes that also\n // contain dynamic segments. This ensures /_sites/:subdomain sorts above\n // /:subdomain, and /_sites/:slug* sorts above /:slug*, while keeping the\n // final score positive (so purely-static routes at score=0 always win).\n //\n // 50 is deliberately smaller than the dynamic-segment penalty (100) so\n // one static prefix segment is enough to beat one bare dynamic segment,\n // and smaller than the infix-static bonus (500) so that infix ordering is\n // not disturbed between two routes that share the same prefix.\n const isDynamic = parts.some(\n (p) => p.startsWith(\":\") || p.endsWith(\"+\") || p.endsWith(\"*\"),\n );\n if (isDynamic && staticPrefixCount > 0) {\n score -= staticPrefixCount * 50;\n }\n\n return score;\n}\n\n/**\n * Sort comparator for routes — lower precedence score sorts first (higher priority).\n * Lexicographic tiebreaker on pattern for determinism.\n *\n * Usage: routes.sort(compareRoutes)\n */\nexport function compareRoutes<T extends { pattern: string }>(a: T, b: T): number {\n const diff = routePrecedence(a.pattern) - routePrecedence(b.pattern);\n return diff !== 0 ? diff : a.pattern.localeCompare(b.pattern);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"api-handler.d.ts","sourceRoot":"","sources":["../../src/server/api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,KAAK,KAAK,EAAc,MAAM,4BAA4B,CAAC;AAoJpE;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,KAAK,EAAE,GACjB,OAAO,CAAC,OAAO,CAAC,CAiDlB"}
1
+ {"version":3,"file":"api-handler.d.ts","sourceRoot":"","sources":["../../src/server/api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,KAAK,KAAK,EAAc,MAAM,4BAA4B,CAAC;AAqJpE;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,KAAK,EAAE,GACjB,OAAO,CAAC,OAAO,CAAC,CA4DlB"}
@@ -1,4 +1,5 @@
1
1
  import { matchRoute } from "../routing/pages-router.js";
2
+ import { reportRequestError } from "./instrumentation.js";
2
3
  import { addQueryParam } from "../utils/query.js";
3
4
  /**
4
5
  * Maximum request body size (1 MB). Matches Next.js default bodyParser sizeLimit.
@@ -156,6 +157,11 @@ export async function handleApiRoute(server, req, res, url, apiRoutes) {
156
157
  catch (e) {
157
158
  server.ssrFixStacktrace(e);
158
159
  console.error(e);
160
+ reportRequestError(e instanceof Error ? e : new Error(String(e)), {
161
+ path: url,
162
+ method: req.method ?? "GET",
163
+ headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v ?? "")])),
164
+ }, { routerKind: "Pages Router", routePath: match.route.pattern, routeType: "route" }).catch(() => { });
159
165
  if (e.message === "Request body too large") {
160
166
  res.statusCode = 413;
161
167
  res.end("Request body too large");
@@ -1 +1 @@
1
- {"version":3,"file":"api-handler.js","sourceRoot":"","sources":["../../src/server/api-handler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * API route handler for Pages Router (pages/api/*).\n *\n * Next.js API routes export a default handler function:\n * export default function handler(req, res) { ... }\n *\n * The req/res objects are Node.js IncomingMessage/ServerResponse with\n * Next.js extensions: req.query, req.body, res.json(), res.status(), etc.\n */\nimport type { ViteDevServer } from \"vite\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { type Route, matchRoute } from \"../routing/pages-router.js\";\nimport { addQueryParam } from \"../utils/query.js\";\n\n/**\n * Extend the Node.js request with Next.js-style helpers.\n */\ninterface NextApiRequest extends IncomingMessage {\n query: Record<string, string | string[]>;\n body: unknown;\n cookies: Record<string, string>;\n}\n\n/**\n * Extend the Node.js response with Next.js-style helpers.\n */\ninterface NextApiResponse extends ServerResponse {\n status(code: number): NextApiResponse;\n json(data: unknown): void;\n send(data: unknown): void;\n redirect(statusOrUrl: number | string, url?: string): void;\n}\n\n/**\n * Maximum request body size (1 MB). Matches Next.js default bodyParser sizeLimit.\n * @see https://nextjs.org/docs/pages/building-your-application/routing/api-routes#custom-config\n * Prevents denial-of-service via unbounded request body buffering.\n */\nconst MAX_BODY_SIZE = 1 * 1024 * 1024;\n\n/**\n * Parse the request body based on content-type.\n * Enforces a size limit to prevent memory exhaustion attacks.\n */\nasync function parseBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n let settled = false;\n req.on(\"data\", (chunk: Buffer) => {\n totalSize += chunk.length;\n if (totalSize > MAX_BODY_SIZE) {\n settled = true;\n req.destroy();\n reject(new Error(\"Request body too large\"));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"error\", (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n req.on(\"end\", () => {\n if (settled) return;\n settled = true;\n const raw = Buffer.concat(chunks).toString(\"utf-8\");\n if (!raw) {\n resolve(undefined);\n return;\n }\n const contentType = req.headers[\"content-type\"] ?? \"\";\n if (contentType.includes(\"application/json\")) {\n try {\n resolve(JSON.parse(raw));\n } catch {\n resolve(raw);\n }\n } else if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n const params = new URLSearchParams(raw);\n const obj: Record<string, string> = {};\n for (const [key, value] of params) {\n obj[key] = value;\n }\n resolve(obj);\n } else {\n resolve(raw);\n }\n });\n });\n}\n\n/**\n * Parse cookies from the Cookie header.\n */\nfunction parseCookies(req: IncomingMessage): Record<string, string> {\n const header = req.headers.cookie ?? \"\";\n const cookies: Record<string, string> = {};\n for (const part of header.split(\";\")) {\n const [key, ...rest] = part.split(\"=\");\n if (key) {\n cookies[key.trim()] = rest.join(\"=\").trim();\n }\n }\n return cookies;\n}\n\n/**\n * Enhance a Node.js req/res pair with Next.js API route helpers.\n */\nfunction enhanceApiObjects(\n req: IncomingMessage,\n res: ServerResponse,\n query: Record<string, string | string[]>,\n body: unknown,\n): { apiReq: NextApiRequest; apiRes: NextApiResponse } {\n const apiReq = req as NextApiRequest;\n apiReq.query = query;\n apiReq.body = body;\n apiReq.cookies = parseCookies(req);\n\n const apiRes = res as NextApiResponse;\n\n apiRes.status = function (code: number) {\n this.statusCode = code;\n return this;\n };\n\n apiRes.json = function (data: unknown) {\n this.setHeader(\"Content-Type\", \"application/json\");\n this.end(JSON.stringify(data));\n };\n\n apiRes.send = function (data: unknown) {\n if (typeof data === \"object\" && data !== null) {\n this.setHeader(\"Content-Type\", \"application/json\");\n this.end(JSON.stringify(data));\n } else {\n if (!this.getHeader(\"Content-Type\")) {\n this.setHeader(\"Content-Type\", \"text/plain\");\n }\n this.end(String(data));\n }\n };\n\n apiRes.redirect = function (statusOrUrl: number | string, url?: string) {\n if (typeof statusOrUrl === \"string\") {\n this.writeHead(307, { Location: statusOrUrl });\n } else {\n this.writeHead(statusOrUrl, { Location: url! });\n }\n this.end();\n };\n\n return { apiReq, apiRes };\n}\n\n/**\n * Handle an API route request.\n * Returns true if the request was handled, false if no API route matched.\n */\nexport async function handleApiRoute(\n server: ViteDevServer,\n req: IncomingMessage,\n res: ServerResponse,\n url: string,\n apiRoutes: Route[],\n): Promise<boolean> {\n const match = matchRoute(url, apiRoutes);\n if (!match) return false;\n\n const { route, params } = match;\n\n try {\n // Load the API route module through Vite\n const apiModule = await server.ssrLoadModule(route.filePath);\n const handler = apiModule.default;\n\n if (typeof handler !== \"function\") {\n console.error(`[vinext] API route ${route.filePath} does not export a default function`);\n res.statusCode = 500;\n res.end(\"API route does not export a default function\");\n return true;\n }\n\n // Parse query from URL + route params\n const query: Record<string, string | string[]> = { ...params };\n const queryString = url.split(\"?\")[1];\n if (queryString) {\n const searchParams = new URLSearchParams(queryString);\n for (const [key, value] of searchParams) {\n addQueryParam(query, key, value);\n }\n }\n\n // Parse body\n const body = await parseBody(req);\n\n // Enhance req/res with Next.js helpers\n const { apiReq, apiRes } = enhanceApiObjects(req, res, query, body);\n\n // Call the handler\n await handler(apiReq, apiRes);\n return true;\n } catch (e) {\n server.ssrFixStacktrace(e as Error);\n console.error(e);\n if ((e as Error).message === \"Request body too large\") {\n res.statusCode = 413;\n res.end(\"Request body too large\");\n } else {\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n }\n return true;\n }\n}\n"]}
1
+ {"version":3,"file":"api-handler.js","sourceRoot":"","sources":["../../src/server/api-handler.ts"],"names":[],"mappings":"AAWA,OAAO,EAAc,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAqBlD;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtC;;;GAGG;AACH,KAAK,UAAU,SAAS,CAAC,GAAoB;IAC3C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC;YAC1B,IAAI,SAAS,GAAG,aAAa,EAAE,CAAC;gBAC9B,OAAO,GAAG,IAAI,CAAC;gBACf,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;gBAC5C,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACpD,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO,CAAC,SAAS,CAAC,CAAC;gBACnB,OAAO;YACT,CAAC;YACD,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;YACtD,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC7C,IAAI,CAAC;oBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3B,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,CAAC,GAAG,CAAC,CAAC;gBACf,CAAC;YACH,CAAC;iBAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,CAAC;gBACrE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;gBACxC,MAAM,GAAG,GAA2B,EAAE,CAAC;gBACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;oBAClC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBACnB,CAAC;gBACD,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,GAAoB;IACxC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IACxC,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CACxB,GAAoB,EACpB,GAAmB,EACnB,KAAwC,EACxC,IAAa;IAEb,MAAM,MAAM,GAAG,GAAqB,CAAC;IACrC,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,MAAM,CAAC,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAEnC,MAAM,MAAM,GAAG,GAAsB,CAAC;IAEtC,MAAM,CAAC,MAAM,GAAG,UAAU,IAAY;QACpC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,CAAC,IAAI,GAAG,UAAU,IAAa;QACnC,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM,CAAC,IAAI,GAAG,UAAU,IAAa;QACnC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAC9C,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;YACnD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;gBACpC,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;YAC/C,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,CAAC,QAAQ,GAAG,UAAU,WAA4B,EAAE,GAAY;QACpE,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,GAAI,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,IAAI,CAAC,GAAG,EAAE,CAAC;IACb,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAqB,EACrB,GAAoB,EACpB,GAAmB,EACnB,GAAW,EACX,SAAkB;IAElB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEzB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAEhC,IAAI,CAAC;QACH,yCAAyC;QACzC,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;QAElC,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,CAAC,sBAAsB,KAAK,CAAC,QAAQ,qCAAqC,CAAC,CAAC;YACzF,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;YACxD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,sCAAsC;QACtC,MAAM,KAAK,GAAsC,EAAE,GAAG,MAAM,EAAE,CAAC;QAC/D,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC;YACtD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;gBACxC,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAED,aAAa;QACb,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;QAElC,uCAAuC;QACvC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAEpE,mBAAmB;QACnB,MAAM,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,gBAAgB,CAAC,CAAU,CAAC,CAAC;QACpC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjB,kBAAkB,CAChB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAC7C;YACE,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,KAAK;YAC3B,OAAO,EAAE,MAAM,CAAC,WAAW,CACzB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CACpG;SACF,EACD,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CACnF,CAAC,KAAK,CAAC,GAAG,EAAE,GAAiC,CAAC,CAAC,CAAC;QACjD,IAAK,CAAW,CAAC,OAAO,KAAK,wBAAwB,EAAE,CAAC;YACtD,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["/**\n * API route handler for Pages Router (pages/api/*).\n *\n * Next.js API routes export a default handler function:\n * export default function handler(req, res) { ... }\n *\n * The req/res objects are Node.js IncomingMessage/ServerResponse with\n * Next.js extensions: req.query, req.body, res.json(), res.status(), etc.\n */\nimport type { ViteDevServer } from \"vite\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { type Route, matchRoute } from \"../routing/pages-router.js\";\nimport { reportRequestError } from \"./instrumentation.js\";\nimport { addQueryParam } from \"../utils/query.js\";\n\n/**\n * Extend the Node.js request with Next.js-style helpers.\n */\ninterface NextApiRequest extends IncomingMessage {\n query: Record<string, string | string[]>;\n body: unknown;\n cookies: Record<string, string>;\n}\n\n/**\n * Extend the Node.js response with Next.js-style helpers.\n */\ninterface NextApiResponse extends ServerResponse {\n status(code: number): NextApiResponse;\n json(data: unknown): void;\n send(data: unknown): void;\n redirect(statusOrUrl: number | string, url?: string): void;\n}\n\n/**\n * Maximum request body size (1 MB). Matches Next.js default bodyParser sizeLimit.\n * @see https://nextjs.org/docs/pages/building-your-application/routing/api-routes#custom-config\n * Prevents denial-of-service via unbounded request body buffering.\n */\nconst MAX_BODY_SIZE = 1 * 1024 * 1024;\n\n/**\n * Parse the request body based on content-type.\n * Enforces a size limit to prevent memory exhaustion attacks.\n */\nasync function parseBody(req: IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalSize = 0;\n let settled = false;\n req.on(\"data\", (chunk: Buffer) => {\n totalSize += chunk.length;\n if (totalSize > MAX_BODY_SIZE) {\n settled = true;\n req.destroy();\n reject(new Error(\"Request body too large\"));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"error\", (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n req.on(\"end\", () => {\n if (settled) return;\n settled = true;\n const raw = Buffer.concat(chunks).toString(\"utf-8\");\n if (!raw) {\n resolve(undefined);\n return;\n }\n const contentType = req.headers[\"content-type\"] ?? \"\";\n if (contentType.includes(\"application/json\")) {\n try {\n resolve(JSON.parse(raw));\n } catch {\n resolve(raw);\n }\n } else if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n const params = new URLSearchParams(raw);\n const obj: Record<string, string> = {};\n for (const [key, value] of params) {\n obj[key] = value;\n }\n resolve(obj);\n } else {\n resolve(raw);\n }\n });\n });\n}\n\n/**\n * Parse cookies from the Cookie header.\n */\nfunction parseCookies(req: IncomingMessage): Record<string, string> {\n const header = req.headers.cookie ?? \"\";\n const cookies: Record<string, string> = {};\n for (const part of header.split(\";\")) {\n const [key, ...rest] = part.split(\"=\");\n if (key) {\n cookies[key.trim()] = rest.join(\"=\").trim();\n }\n }\n return cookies;\n}\n\n/**\n * Enhance a Node.js req/res pair with Next.js API route helpers.\n */\nfunction enhanceApiObjects(\n req: IncomingMessage,\n res: ServerResponse,\n query: Record<string, string | string[]>,\n body: unknown,\n): { apiReq: NextApiRequest; apiRes: NextApiResponse } {\n const apiReq = req as NextApiRequest;\n apiReq.query = query;\n apiReq.body = body;\n apiReq.cookies = parseCookies(req);\n\n const apiRes = res as NextApiResponse;\n\n apiRes.status = function (code: number) {\n this.statusCode = code;\n return this;\n };\n\n apiRes.json = function (data: unknown) {\n this.setHeader(\"Content-Type\", \"application/json\");\n this.end(JSON.stringify(data));\n };\n\n apiRes.send = function (data: unknown) {\n if (typeof data === \"object\" && data !== null) {\n this.setHeader(\"Content-Type\", \"application/json\");\n this.end(JSON.stringify(data));\n } else {\n if (!this.getHeader(\"Content-Type\")) {\n this.setHeader(\"Content-Type\", \"text/plain\");\n }\n this.end(String(data));\n }\n };\n\n apiRes.redirect = function (statusOrUrl: number | string, url?: string) {\n if (typeof statusOrUrl === \"string\") {\n this.writeHead(307, { Location: statusOrUrl });\n } else {\n this.writeHead(statusOrUrl, { Location: url! });\n }\n this.end();\n };\n\n return { apiReq, apiRes };\n}\n\n/**\n * Handle an API route request.\n * Returns true if the request was handled, false if no API route matched.\n */\nexport async function handleApiRoute(\n server: ViteDevServer,\n req: IncomingMessage,\n res: ServerResponse,\n url: string,\n apiRoutes: Route[],\n): Promise<boolean> {\n const match = matchRoute(url, apiRoutes);\n if (!match) return false;\n\n const { route, params } = match;\n\n try {\n // Load the API route module through Vite\n const apiModule = await server.ssrLoadModule(route.filePath);\n const handler = apiModule.default;\n\n if (typeof handler !== \"function\") {\n console.error(`[vinext] API route ${route.filePath} does not export a default function`);\n res.statusCode = 500;\n res.end(\"API route does not export a default function\");\n return true;\n }\n\n // Parse query from URL + route params\n const query: Record<string, string | string[]> = { ...params };\n const queryString = url.split(\"?\")[1];\n if (queryString) {\n const searchParams = new URLSearchParams(queryString);\n for (const [key, value] of searchParams) {\n addQueryParam(query, key, value);\n }\n }\n\n // Parse body\n const body = await parseBody(req);\n\n // Enhance req/res with Next.js helpers\n const { apiReq, apiRes } = enhanceApiObjects(req, res, query, body);\n\n // Call the handler\n await handler(apiReq, apiRes);\n return true;\n } catch (e) {\n server.ssrFixStacktrace(e as Error);\n console.error(e);\n reportRequestError(\n e instanceof Error ? e : new Error(String(e)),\n {\n path: url,\n method: req.method ?? \"GET\",\n headers: Object.fromEntries(\n Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(\", \") : String(v ?? \"\")]),\n ),\n },\n { routerKind: \"Pages Router\", routePath: match.route.pattern, routeType: \"route\" },\n ).catch(() => { /* ignore reporting errors */ });\n if ((e as Error).message === \"Request body too large\") {\n res.statusCode = 413;\n res.end(\"Request body too large\");\n } else {\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n }\n return true;\n }\n}\n"]}
@@ -15,7 +15,7 @@ export interface AppRouterConfig {
15
15
  headers?: NextHeader[];
16
16
  /** Extra origins allowed for server action CSRF checks (from experimental.serverActions.allowedOrigins). */
17
17
  allowedOrigins?: string[];
18
- /** Extra origins allowed for dev server access (from serverActionsAllowedOrigins or custom config). */
18
+ /** Extra origins allowed for dev server access (from allowedDevOrigins). */
19
19
  allowedDevOrigins?: string[];
20
20
  }
21
21
  /**
@@ -25,7 +25,7 @@ export interface AppRouterConfig {
25
25
  * It matches the incoming request URL to an app route, builds the
26
26
  * nested layout + page tree, and renders it to an RSC stream.
27
27
  */
28
- export declare function generateRscEntry(appDir: string, routes: AppRoute[], middlewarePath?: string | null, metadataRoutes?: MetadataFileRoute[], globalErrorPath?: string | null, basePath?: string, trailingSlash?: boolean, config?: AppRouterConfig): string;
28
+ export declare function generateRscEntry(appDir: string, routes: AppRoute[], middlewarePath?: string | null, metadataRoutes?: MetadataFileRoute[], globalErrorPath?: string | null, basePath?: string, trailingSlash?: boolean, config?: AppRouterConfig, instrumentationPath?: string | null): string;
29
29
  /**
30
30
  * Generate the virtual SSR entry module.
31
31
  *
@@ -1 +1 @@
1
- {"version":3,"file":"app-dev-server.d.ts","sourceRoot":"","sources":["../../src/server/app-dev-server.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAKtF;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE;QACT,WAAW,EAAE,WAAW,EAAE,CAAC;QAC3B,UAAU,EAAE,WAAW,EAAE,CAAC;QAC1B,QAAQ,EAAE,WAAW,EAAE,CAAC;KACzB,CAAC;IACF,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,4GAA4G;IAC5G,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,uGAAuG;IACvG,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,EAAE,EAClB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,EAC9B,cAAc,CAAC,EAAE,iBAAiB,EAAE,EACpC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,QAAQ,CAAC,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,MAAM,CAAC,EAAE,eAAe,GACvB,MAAM,CAgvER;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAibzC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CA+T7C"}
1
+ {"version":3,"file":"app-dev-server.d.ts","sourceRoot":"","sources":["../../src/server/app-dev-server.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAKtF;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE;QACT,WAAW,EAAE,WAAW,EAAE,CAAC;QAC3B,UAAU,EAAE,WAAW,EAAE,CAAC;QAC1B,QAAQ,EAAE,WAAW,EAAE,CAAC;KACzB,CAAC;IACF,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,4GAA4G;IAC5G,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,EAAE,EAClB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,EAC9B,cAAc,CAAC,EAAE,iBAAiB,EAAE,EACpC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,QAAQ,CAAC,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,MAAM,CAAC,EAAE,eAAe,EACxB,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,GAClC,MAAM,CAi5ER;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAibzC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CA+T7C"}