vinext 0.1.2 → 0.1.3

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 (199) hide show
  1. package/dist/build/prerender.d.ts +9 -1
  2. package/dist/build/prerender.js +41 -12
  3. package/dist/build/run-prerender.d.ts +10 -2
  4. package/dist/build/run-prerender.js +15 -1
  5. package/dist/client/app-nav-failure-handler.d.ts +8 -0
  6. package/dist/client/app-nav-failure-handler.js +44 -0
  7. package/dist/client/vinext-next-data.d.ts +18 -1
  8. package/dist/client/window-next.d.ts +2 -1
  9. package/dist/client/window-next.js +12 -1
  10. package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
  11. package/dist/config/config-matchers.js +73 -14
  12. package/dist/config/next-config.d.ts +46 -4
  13. package/dist/config/next-config.js +147 -48
  14. package/dist/deploy.d.ts +30 -11
  15. package/dist/deploy.js +180 -99
  16. package/dist/entries/app-browser-entry.d.ts +9 -3
  17. package/dist/entries/app-browser-entry.js +21 -3
  18. package/dist/entries/app-rsc-entry.d.ts +2 -0
  19. package/dist/entries/app-rsc-entry.js +64 -5
  20. package/dist/entries/app-rsc-manifest.js +2 -0
  21. package/dist/entries/app-ssr-entry.js +1 -1
  22. package/dist/entries/pages-client-entry.js +53 -8
  23. package/dist/entries/pages-server-entry.js +41 -5
  24. package/dist/index.js +200 -62
  25. package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
  26. package/dist/plugins/extensionless-dynamic-import.js +152 -0
  27. package/dist/plugins/optimize-imports.d.ts +2 -1
  28. package/dist/plugins/optimize-imports.js +11 -9
  29. package/dist/plugins/postcss.js +7 -7
  30. package/dist/plugins/typeof-window.d.ts +14 -0
  31. package/dist/plugins/typeof-window.js +150 -0
  32. package/dist/routing/app-route-graph.d.ts +2 -1
  33. package/dist/routing/app-route-graph.js +44 -14
  34. package/dist/routing/file-matcher.d.ts +10 -1
  35. package/dist/routing/file-matcher.js +22 -1
  36. package/dist/routing/pages-router.js +3 -3
  37. package/dist/routing/utils.d.ts +35 -6
  38. package/dist/routing/utils.js +59 -7
  39. package/dist/server/api-handler.d.ts +6 -1
  40. package/dist/server/api-handler.js +21 -15
  41. package/dist/server/app-browser-action-result.d.ts +19 -6
  42. package/dist/server/app-browser-action-result.js +19 -10
  43. package/dist/server/app-browser-entry.js +167 -90
  44. package/dist/server/app-browser-error.d.ts +10 -6
  45. package/dist/server/app-browser-error.js +43 -8
  46. package/dist/server/app-browser-hydration.d.ts +2 -0
  47. package/dist/server/app-browser-hydration.js +1 -0
  48. package/dist/server/app-browser-navigation-controller.d.ts +4 -2
  49. package/dist/server/app-browser-navigation-controller.js +23 -2
  50. package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
  51. package/dist/server/app-browser-server-action-navigation.js +9 -0
  52. package/dist/server/app-browser-stream.js +86 -43
  53. package/dist/server/app-elements-wire.d.ts +6 -1
  54. package/dist/server/app-elements-wire.js +14 -4
  55. package/dist/server/app-elements.d.ts +2 -2
  56. package/dist/server/app-elements.js +2 -2
  57. package/dist/server/app-fallback-renderer.d.ts +1 -0
  58. package/dist/server/app-fallback-renderer.js +3 -1
  59. package/dist/server/app-optimistic-routing.js +2 -2
  60. package/dist/server/app-page-boundary-render.d.ts +1 -0
  61. package/dist/server/app-page-boundary-render.js +27 -14
  62. package/dist/server/app-page-cache-render.d.ts +53 -0
  63. package/dist/server/app-page-cache-render.js +91 -0
  64. package/dist/server/app-page-cache.d.ts +16 -2
  65. package/dist/server/app-page-cache.js +62 -1
  66. package/dist/server/app-page-dispatch.d.ts +26 -0
  67. package/dist/server/app-page-dispatch.js +149 -92
  68. package/dist/server/app-page-element-builder.d.ts +1 -0
  69. package/dist/server/app-page-element-builder.js +5 -2
  70. package/dist/server/app-page-execution.d.ts +6 -1
  71. package/dist/server/app-page-execution.js +21 -1
  72. package/dist/server/app-page-probe.d.ts +1 -0
  73. package/dist/server/app-page-probe.js +4 -0
  74. package/dist/server/app-page-render-observation.d.ts +3 -1
  75. package/dist/server/app-page-render-observation.js +17 -1
  76. package/dist/server/app-page-render.d.ts +12 -1
  77. package/dist/server/app-page-render.js +42 -4
  78. package/dist/server/app-page-request.d.ts +2 -0
  79. package/dist/server/app-page-request.js +2 -1
  80. package/dist/server/app-page-route-wiring.d.ts +3 -1
  81. package/dist/server/app-page-route-wiring.js +14 -5
  82. package/dist/server/app-page-stream.d.ts +15 -3
  83. package/dist/server/app-page-stream.js +11 -5
  84. package/dist/server/app-pages-bridge.d.ts +18 -0
  85. package/dist/server/app-pages-bridge.js +22 -5
  86. package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
  87. package/dist/server/app-ppr-fallback-shell-render.js +26 -0
  88. package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
  89. package/dist/server/app-ppr-fallback-shell.js +8 -1
  90. package/dist/server/app-route-handler-dispatch.js +9 -2
  91. package/dist/server/app-route-handler-policy.d.ts +1 -0
  92. package/dist/server/app-router-entry.js +5 -0
  93. package/dist/server/app-rsc-cache-busting.js +2 -0
  94. package/dist/server/app-rsc-handler.d.ts +25 -0
  95. package/dist/server/app-rsc-handler.js +154 -54
  96. package/dist/server/app-rsc-route-matching.d.ts +3 -0
  97. package/dist/server/app-rsc-route-matching.js +2 -0
  98. package/dist/server/app-segment-config.d.ts +9 -1
  99. package/dist/server/app-segment-config.js +12 -3
  100. package/dist/server/app-server-action-execution.d.ts +1 -0
  101. package/dist/server/app-server-action-execution.js +42 -13
  102. package/dist/server/app-ssr-entry.d.ts +2 -0
  103. package/dist/server/app-ssr-entry.js +83 -10
  104. package/dist/server/cache-control.js +4 -0
  105. package/dist/server/dev-server.d.ts +2 -2
  106. package/dist/server/dev-server.js +244 -51
  107. package/dist/server/hybrid-route-priority.d.ts +22 -0
  108. package/dist/server/hybrid-route-priority.js +33 -0
  109. package/dist/server/image-optimization.d.ts +18 -9
  110. package/dist/server/image-optimization.js +37 -23
  111. package/dist/server/implicit-tags.d.ts +2 -1
  112. package/dist/server/implicit-tags.js +4 -1
  113. package/dist/server/navigation-planner.d.ts +133 -30
  114. package/dist/server/navigation-planner.js +114 -0
  115. package/dist/server/navigation-trace.d.ts +8 -1
  116. package/dist/server/navigation-trace.js +8 -1
  117. package/dist/server/pages-api-route.d.ts +6 -0
  118. package/dist/server/pages-api-route.js +13 -2
  119. package/dist/server/pages-asset-tags.d.ts +2 -1
  120. package/dist/server/pages-asset-tags.js +6 -2
  121. package/dist/server/pages-data-route.d.ts +8 -1
  122. package/dist/server/pages-data-route.js +11 -2
  123. package/dist/server/pages-get-initial-props.d.ts +54 -4
  124. package/dist/server/pages-get-initial-props.js +43 -1
  125. package/dist/server/pages-node-compat.js +2 -2
  126. package/dist/server/pages-page-data.d.ts +11 -2
  127. package/dist/server/pages-page-data.js +204 -33
  128. package/dist/server/pages-page-handler.d.ts +4 -2
  129. package/dist/server/pages-page-handler.js +59 -22
  130. package/dist/server/pages-page-response.d.ts +2 -1
  131. package/dist/server/pages-page-response.js +7 -4
  132. package/dist/server/pages-request-pipeline.d.ts +1 -0
  133. package/dist/server/pages-request-pipeline.js +73 -36
  134. package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
  135. package/dist/server/pregenerated-concrete-paths.js +2 -19
  136. package/dist/server/prerender-manifest.d.ts +33 -0
  137. package/dist/server/prerender-manifest.js +54 -0
  138. package/dist/server/prerender-route-params.d.ts +1 -2
  139. package/dist/server/prod-server.js +9 -3
  140. package/dist/server/request-pipeline.d.ts +3 -15
  141. package/dist/server/request-pipeline.js +58 -47
  142. package/dist/server/rsc-stream-hints.d.ts +5 -1
  143. package/dist/server/rsc-stream-hints.js +6 -1
  144. package/dist/server/seed-cache.js +10 -18
  145. package/dist/shims/app-router-scroll-state.d.ts +3 -1
  146. package/dist/shims/app-router-scroll-state.js +14 -2
  147. package/dist/shims/app-router-scroll.d.ts +3 -0
  148. package/dist/shims/app-router-scroll.js +28 -18
  149. package/dist/shims/cache-runtime.js +3 -2
  150. package/dist/shims/cache.d.ts +1 -0
  151. package/dist/shims/cache.js +1 -1
  152. package/dist/shims/cdn-cache.d.ts +5 -5
  153. package/dist/shims/dynamic-preload-chunks.js +6 -4
  154. package/dist/shims/error-boundary.d.ts +2 -0
  155. package/dist/shims/error-boundary.js +7 -0
  156. package/dist/shims/error.js +3 -2
  157. package/dist/shims/error.react-server.d.ts +9 -0
  158. package/dist/shims/error.react-server.js +6 -0
  159. package/dist/shims/fetch-cache.d.ts +3 -1
  160. package/dist/shims/fetch-cache.js +45 -20
  161. package/dist/shims/hash-scroll.js +6 -1
  162. package/dist/shims/headers.js +29 -4
  163. package/dist/shims/internal/als-registry.js +28 -1
  164. package/dist/shims/internal/app-route-detection.js +8 -17
  165. package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
  166. package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
  167. package/dist/shims/internal/navigation-untracked.d.ts +35 -0
  168. package/dist/shims/internal/navigation-untracked.js +55 -0
  169. package/dist/shims/internal/pages-data-target.d.ts +7 -2
  170. package/dist/shims/internal/pages-data-target.js +17 -8
  171. package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
  172. package/dist/shims/internal/pages-router-accessor.js +13 -0
  173. package/dist/shims/internal/router-context.d.ts +2 -1
  174. package/dist/shims/internal/router-context.js +3 -1
  175. package/dist/shims/link.js +12 -5
  176. package/dist/shims/navigation.d.ts +8 -2
  177. package/dist/shims/navigation.js +61 -31
  178. package/dist/shims/ppr-fallback-shell.d.ts +5 -1
  179. package/dist/shims/ppr-fallback-shell.js +28 -7
  180. package/dist/shims/router.d.ts +13 -2
  181. package/dist/shims/router.js +419 -128
  182. package/dist/shims/server.d.ts +16 -1
  183. package/dist/shims/server.js +44 -12
  184. package/dist/shims/unified-request-context.js +1 -0
  185. package/dist/utils/built-asset-url.d.ts +4 -0
  186. package/dist/utils/built-asset-url.js +11 -0
  187. package/dist/utils/commonjs-loader.d.ts +16 -0
  188. package/dist/utils/commonjs-loader.js +100 -0
  189. package/dist/utils/deployment-id.d.ts +8 -0
  190. package/dist/utils/deployment-id.js +22 -0
  191. package/dist/utils/html-limited-bots.d.ts +18 -1
  192. package/dist/utils/html-limited-bots.js +23 -1
  193. package/dist/utils/parse-cookie.d.ts +13 -0
  194. package/dist/utils/parse-cookie.js +52 -0
  195. package/dist/utils/path.d.ts +7 -1
  196. package/dist/utils/path.js +9 -1
  197. package/package.json +2 -2
  198. package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
  199. package/dist/shims/internal/parse-cookie-header.js +0 -30
@@ -6,6 +6,7 @@ import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
6
6
  import { normalizeStaticPathname } from "../routing/route-pattern.js";
7
7
  import { importModule, reportRequestError } from "./instrumentation.js";
8
8
  import { buildCacheStateHeaders } from "./cache-headers.js";
9
+ import { isUnknownRecord } from "../utils/record.js";
9
10
  import { _runWithCacheState } from "../shims/cache.js";
10
11
  import { NEVER_CACHE_CONTROL, NO_STORE_CACHE_CONTROL } from "./cache-control.js";
11
12
  import { buildMissIsrCacheControl, decideIsr } from "./isr-decision.js";
@@ -28,7 +29,8 @@ import { resolvePagesPageMethodResponse } from "./pages-page-method.js";
28
29
  import { isSerializableProps } from "./pages-serializable-props.js";
29
30
  import { loadUserDocumentInitialProps, runDocumentRenderPage } from "./pages-document-initial-props.js";
30
31
  import { callDocumentGetInitialProps } from "./document-initial-head.js";
31
- import { loadPagesGetInitialProps } from "./pages-get-initial-props.js";
32
+ import { hasPagesGetInitialProps, loadDevAppInitialProps, loadPagesGetInitialProps } from "./pages-get-initial-props.js";
33
+ import { isBotUserAgent } from "../utils/html-limited-bots.js";
32
34
  import path from "node:path";
33
35
  import React from "react";
34
36
  import { renderToReadableStream } from "react-dom/server.edge";
@@ -60,7 +62,7 @@ async function renderIsrPassToStringAsync(element) {
60
62
  * (`buildPagesRedirectResponse`) and Next.js's `render.tsx` `__N_REDIRECT`
61
63
  * handling. See AGENTS.md "dev and prod server parity".
62
64
  */
63
- function writeGsspRedirect(res, redirect, isDataReq) {
65
+ function writeGsspRedirect(res, redirect, isDataReq, props) {
64
66
  const status = redirect.statusCode ?? (redirect.permanent ? 308 : 307);
65
67
  let dest = redirect.destination;
66
68
  if (!dest.startsWith("http://") && !dest.startsWith("https://")) dest = dest.replace(/^[\\/]+/, "/");
@@ -69,10 +71,14 @@ function writeGsspRedirect(res, redirect, isDataReq) {
69
71
  const dataHeaders = { "Content-Type": "application/json" };
70
72
  if (deploymentId) dataHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
71
73
  res.writeHead(200, dataHeaders);
72
- res.end(JSON.stringify({ pageProps: {
73
- __N_REDIRECT: dest,
74
- __N_REDIRECT_STATUS: status
75
- } }));
74
+ res.end(JSON.stringify({
75
+ ...props,
76
+ pageProps: {
77
+ ...isUnknownRecord(props.pageProps) ? props.pageProps : {},
78
+ __N_REDIRECT: dest,
79
+ __N_REDIRECT_STATUS: status
80
+ }
81
+ }));
76
82
  return;
77
83
  }
78
84
  res.writeHead(status, { Location: dest });
@@ -190,12 +196,12 @@ function parseCookieLocale(req, i18nConfig) {
190
196
  * 4. Render the component to HTML
191
197
  * 5. Wrap in _document shell and send response
192
198
  */
193
- function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata) {
199
+ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatcher, basePath = "", trailingSlash = false, hasMiddleware = false, hasRewrites = false, clientTraceMetadata, htmlLimitedBots) {
194
200
  const matcher = fileMatcher ?? createValidFileMatcher();
195
201
  const pagePatterns = routes.map((r) => patternToNextFormat(r.pattern));
196
202
  const _alsRegistration = Promise.all([runner.import("vinext/head-state"), runner.import("vinext/router-state")]);
197
203
  _alsRegistration.catch(() => {});
198
- return async (req, res, url, statusCode, isDataReq = false) => {
204
+ return async (req, res, url, statusCode, isDataReq = false, originalUrl = url) => {
199
205
  const _reqStart = now();
200
206
  let _compileEnd;
201
207
  let _renderEnd;
@@ -245,6 +251,11 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
245
251
  return;
246
252
  }
247
253
  const { route, params } = match;
254
+ req.url = originalUrl;
255
+ const parsedResolvedUrl = new URL(localeStrippedUrl, "http://vinext.local");
256
+ const originalRequestSearch = new URL(originalUrl, "http://vinext.local").search;
257
+ const gsspResolvedUrl = parsedResolvedUrl.pathname + originalRequestSearch;
258
+ const requestAsPath = isDataReq ? gsspResolvedUrl : originalUrl;
248
259
  const userFacingParams = route.isDynamic ? params : null;
249
260
  const query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
250
261
  return runWithRequestContext(createRequestContext(), async () => {
@@ -278,7 +289,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
278
289
  if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
279
290
  pathname: patternToNextFormat(route.pattern),
280
291
  query,
281
- asPath: url,
292
+ asPath: requestAsPath,
282
293
  navigationIsReady,
283
294
  nextData: pagesNextData,
284
295
  locale: locale ?? currentDefaultLocale,
@@ -312,8 +323,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
312
323
  }
313
324
  }
314
325
  let pageProps = {};
326
+ let renderProps = { pageProps };
315
327
  let isrRevalidateSeconds = null;
316
328
  let isFallbackRender = false;
329
+ let shouldPersistFallbackData = false;
317
330
  if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
318
331
  const pathsResult = await pageModule.getStaticPaths({
319
332
  locales: i18nConfig?.locales ?? [],
@@ -344,12 +357,15 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
344
357
  await renderErrorPage(server, runner, req, res, url, pagesDir, 404, routerShim.wrapWithRouterContext, matcher);
345
358
  return;
346
359
  }
347
- if (fallback === true && !isValidPath && !isDataReq) {
348
- isFallbackRender = true;
349
- if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
360
+ const userAgentHeader = req.headers["user-agent"];
361
+ const userAgent = Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;
362
+ const isBotRequest = !!userAgent && isBotUserAgent(userAgent, htmlLimitedBots);
363
+ if (fallback === true && !isValidPath && !isDataReq && !isBotRequest) {
364
+ isFallbackRender = (await isrGet(pagesIsrCacheKey(url.split("?")[0])))?.value.value?.kind !== "PAGES";
365
+ if (isFallbackRender && typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
350
366
  pathname: patternToNextFormat(route.pattern),
351
367
  query,
352
- asPath: url,
368
+ asPath: requestAsPath,
353
369
  navigationIsReady: false,
354
370
  locale: locale ?? currentDefaultLocale,
355
371
  locales: i18nConfig?.locales,
@@ -358,25 +374,71 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
358
374
  isFallback: true
359
375
  });
360
376
  }
377
+ shouldPersistFallbackData = fallback === true && !isValidPath && isDataReq;
361
378
  }
362
379
  const gsspExtraHeaders = {};
380
+ const hasAppGetInitialProps = hasPagesGetInitialProps(AppComponent);
381
+ async function loadAppInitialProps() {
382
+ if (!hasAppGetInitialProps) return false;
383
+ const appResult = await loadDevAppInitialProps({
384
+ appComponent: AppComponent,
385
+ appTree: (appTreeProps) => {
386
+ const appTree = React.createElement(AppComponent, {
387
+ ...appTreeProps,
388
+ Component: PageComponent,
389
+ pageProps: appTreeProps.pageProps,
390
+ router: routerShim.default
391
+ });
392
+ return typeof routerShim.wrapWithRouterContext === "function" ? routerShim.wrapWithRouterContext(appTree) : appTree;
393
+ },
394
+ component: PageComponent,
395
+ req,
396
+ res,
397
+ pathname: patternToNextFormat(route.pattern),
398
+ query,
399
+ asPath: requestAsPath,
400
+ locale: locale ?? currentDefaultLocale,
401
+ locales: i18nConfig?.locales,
402
+ defaultLocale: currentDefaultLocale
403
+ });
404
+ if (appResult.kind === "response-sent") return true;
405
+ if (appResult.kind === "render") {
406
+ pageProps = appResult.pageProps;
407
+ renderProps = appResult.renderProps;
408
+ }
409
+ return false;
410
+ }
363
411
  if (typeof pageModule.getServerSideProps === "function" && !isFallbackRender) {
412
+ if (await loadAppInitialProps()) return;
413
+ renderProps = {
414
+ ...renderProps,
415
+ __N_SSP: true
416
+ };
364
417
  const headersBeforeGSSP = new Set(Object.keys(res.getHeaders()));
365
418
  const context = {
366
419
  params: userFacingParams,
367
420
  req,
368
421
  res,
369
422
  query,
370
- resolvedUrl: localeStrippedUrl,
423
+ resolvedUrl: gsspResolvedUrl,
371
424
  locale: locale ?? currentDefaultLocale,
372
425
  locales: i18nConfig?.locales,
373
426
  defaultLocale: currentDefaultLocale
374
427
  };
375
428
  const result = await pageModule.getServerSideProps(context);
376
429
  if (res.writableEnded) return;
377
- if (result && "props" in result) pageProps = await Promise.resolve(result.props);
430
+ if (result && "props" in result) {
431
+ pageProps = {
432
+ ...pageProps,
433
+ ...await Promise.resolve(result.props)
434
+ };
435
+ renderProps = {
436
+ ...renderProps,
437
+ pageProps
438
+ };
439
+ }
378
440
  if (result && "redirect" in result) {
379
- writeGsspRedirect(res, result.redirect, isDataReq);
441
+ writeGsspRedirect(res, result.redirect, isDataReq, renderProps);
380
442
  return;
381
443
  }
382
444
  if (result && "notFound" in result && result.notFound) {
@@ -417,7 +479,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
417
479
  const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
418
480
  const cached = await isrGet(cacheKey);
419
481
  const isOnDemandRevalidate = isOnDemandRevalidateRequest(req.headers[PRERENDER_REVALIDATE_HEADER]);
420
- if (!isOnDemandRevalidate && cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !scriptNonce && !isDataReq) {
482
+ if (!isOnDemandRevalidate && cached && !cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq) {
421
483
  const cachedHtml = cached.value.value.html;
422
484
  const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
423
485
  const { cacheControl: hitCacheControl } = decideIsr({
@@ -435,12 +497,84 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
435
497
  res.end(transformedHtml);
436
498
  return;
437
499
  }
438
- if (!isOnDemandRevalidate && cached && cached.isStale && cached.value.value?.kind === "PAGES" && !scriptNonce && !isDataReq) {
500
+ if (!isOnDemandRevalidate && cached && cached.isStale && cached.value.value?.kind === "PAGES" && !cached.value.value.generatedFromDataRequest && !scriptNonce && !isDataReq) {
439
501
  const cachedHtml = cached.value.value.html;
440
502
  const transformedHtml = await server.transformIndexHtml(url, cachedHtml);
441
503
  triggerBackgroundRegeneration(cacheKey, async () => {
442
- return runWithRequestContext(createRequestContext({ executionContext: null }), async () => {
504
+ return runWithRequestContext(createRequestContext({
505
+ executionContext: null,
506
+ ssrContext: {
507
+ pathname: patternToNextFormat(route.pattern),
508
+ query,
509
+ asPath: requestAsPath,
510
+ navigationIsReady,
511
+ locale: locale ?? currentDefaultLocale,
512
+ locales: i18nConfig?.locales,
513
+ defaultLocale: currentDefaultLocale
514
+ },
515
+ i18nContext: i18nConfig ? {
516
+ locale: locale ?? currentDefaultLocale,
517
+ locales: i18nConfig.locales,
518
+ defaultLocale: currentDefaultLocale,
519
+ domainLocales,
520
+ hostname: req.headers.host?.split(":", 1)[0]
521
+ } : null
522
+ }), async () => {
443
523
  ensureFetchPatch();
524
+ let freshPageProps = {};
525
+ let freshRenderProps = { pageProps: freshPageProps };
526
+ let RegenApp = null;
527
+ const appPath = path.join(pagesDir, "_app");
528
+ if (findFileWithExtensions(appPath, matcher)) try {
529
+ RegenApp = (await runner.import(appPath)).default ?? null;
530
+ } catch {}
531
+ if (RegenApp && hasPagesGetInitialProps(RegenApp)) {
532
+ const regenReq = {
533
+ url: req.url,
534
+ headers: req.headers,
535
+ method: req.method
536
+ };
537
+ const regenRes = {
538
+ headersSent: false,
539
+ writableEnded: false,
540
+ statusCode: 200,
541
+ getHeaders() {
542
+ return {};
543
+ }
544
+ };
545
+ const initialProps = await loadPagesGetInitialProps(RegenApp, {
546
+ AppTree: (appTreeProps) => {
547
+ const appTree = React.createElement(RegenApp, {
548
+ ...appTreeProps,
549
+ Component: pageModule.default,
550
+ pageProps: appTreeProps.pageProps,
551
+ router: routerShim.default
552
+ });
553
+ return typeof routerShim.wrapWithRouterContext === "function" ? routerShim.wrapWithRouterContext(appTree) : appTree;
554
+ },
555
+ Component: pageModule.default,
556
+ router: {
557
+ pathname: patternToNextFormat(route.pattern),
558
+ query,
559
+ asPath: requestAsPath
560
+ },
561
+ ctx: {
562
+ req: regenReq,
563
+ res: regenRes,
564
+ pathname: patternToNextFormat(route.pattern),
565
+ query,
566
+ asPath: requestAsPath,
567
+ locale: locale ?? currentDefaultLocale,
568
+ locales: i18nConfig?.locales,
569
+ defaultLocale: currentDefaultLocale
570
+ }
571
+ });
572
+ if (regenRes.headersSent || regenRes.writableEnded) return;
573
+ if (initialProps) {
574
+ freshRenderProps = initialProps;
575
+ freshPageProps = isUnknownRecord(initialProps.pageProps) ? initialProps.pageProps : {};
576
+ }
577
+ }
444
578
  const freshResult = await pageModule.getStaticProps({
445
579
  params: userFacingParams,
446
580
  locale: locale ?? currentDefaultLocale,
@@ -449,13 +583,20 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
449
583
  revalidateReason: "stale"
450
584
  });
451
585
  if (freshResult && "props" in freshResult) {
452
- const revalidate = typeof freshResult.revalidate === "number" ? freshResult.revalidate : 0;
586
+ const revalidate = typeof freshResult.revalidate === "number" ? freshResult.revalidate : cached.value.cacheControl?.revalidate ?? 0;
453
587
  if (revalidate > 0) {
454
- const freshProps = freshResult.props;
588
+ freshPageProps = {
589
+ ...freshPageProps,
590
+ ...freshResult.props
591
+ };
592
+ freshRenderProps = {
593
+ ...freshRenderProps,
594
+ pageProps: freshPageProps
595
+ };
455
596
  if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext({
456
597
  pathname: patternToNextFormat(route.pattern),
457
598
  query,
458
- asPath: url,
599
+ asPath: requestAsPath,
459
600
  navigationIsReady,
460
601
  locale: locale ?? currentDefaultLocale,
461
602
  locales: i18nConfig?.locales,
@@ -473,15 +614,12 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
473
614
  hostname: req.headers.host?.split(":", 1)[0]
474
615
  });
475
616
  }
476
- let RegenApp = null;
477
- const appPath = path.join(pagesDir, "_app");
478
- if (findFileWithExtensions(appPath, matcher)) try {
479
- RegenApp = (await runner.import(appPath)).default ?? null;
480
- } catch {}
481
617
  let el = RegenApp ? React.createElement(RegenApp, {
618
+ ...freshRenderProps,
482
619
  Component: pageModule.default,
483
- pageProps: freshProps
484
- }) : React.createElement(pageModule.default, freshProps);
620
+ pageProps: freshRenderProps.pageProps,
621
+ router: routerShim.default
622
+ }) : React.createElement(pageModule.default, freshPageProps);
485
623
  if (routerShim.wrapWithRouterContext) el = routerShim.wrapWithRouterContext(el);
486
624
  const freshBody = await renderIsrPassToStringAsync(withScriptNonce(el, scriptNonce));
487
625
  const viteRoot = server.config?.root;
@@ -497,7 +635,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
497
635
  }
498
636
  };
499
637
  await isrSet(cacheKey, buildPagesCacheValue(`<!DOCTYPE html><html><head></head><body><div id="__next">${freshBody}</div>${`<script>window.__NEXT_DATA__ = ${safeJsonStringify({
500
- props: { pageProps: freshProps },
638
+ props: freshRenderProps,
501
639
  page: patternToNextFormat(route.pattern),
502
640
  query: params,
503
641
  buildId: process.env.__VINEXT_BUILD_ID,
@@ -507,7 +645,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
507
645
  defaultLocale: currentDefaultLocale,
508
646
  domainLocales,
509
647
  ...freshPagesNextData
510
- })}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}<\/script>`}\n ${cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/)?.[0] ?? ""}</body></html>`, freshProps), revalidate);
648
+ })}${i18nConfig ? `;window.__VINEXT_LOCALE__=${safeJsonStringify(locale ?? currentDefaultLocale)};window.__VINEXT_LOCALES__=${safeJsonStringify(i18nConfig.locales)};window.__VINEXT_DEFAULT_LOCALE__=${safeJsonStringify(currentDefaultLocale)}` : ""}<\/script>`}\n ${cachedHtml.match(/<script type="module">[\s\S]*?<\/script>/)?.[0] ?? ""}</body></html>`, freshRenderProps), revalidate);
511
649
  setRevalidateDuration(cacheKey, revalidate);
512
650
  }
513
651
  }
@@ -539,10 +677,25 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
539
677
  defaultLocale: currentDefaultLocale,
540
678
  revalidateReason: isOnDemandRevalidate ? "on-demand" : "stale"
541
679
  };
542
- const result = await pageModule.getStaticProps(context);
543
- if (result && "props" in result) pageProps = result.props;
680
+ const generatedPageData = !isOnDemandRevalidate && cached?.isStale === false && cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest && isUnknownRecord(cached.value.value.pageData) ? cached.value.value.pageData : null;
681
+ if (!generatedPageData && await loadAppInitialProps()) return;
682
+ const result = generatedPageData ? null : await pageModule.getStaticProps(context);
683
+ if (generatedPageData) {
684
+ renderProps = generatedPageData;
685
+ pageProps = isUnknownRecord(renderProps.pageProps) ? renderProps.pageProps : {};
686
+ }
687
+ if (result && "props" in result) {
688
+ pageProps = {
689
+ ...pageProps,
690
+ ...await Promise.resolve(result.props)
691
+ };
692
+ renderProps = {
693
+ ...renderProps,
694
+ pageProps
695
+ };
696
+ }
544
697
  if (result && "redirect" in result) {
545
- writeGsspRedirect(res, result.redirect, isDataReq);
698
+ writeGsspRedirect(res, result.redirect, isDataReq, renderProps);
546
699
  return;
547
700
  }
548
701
  if (result && "notFound" in result && result.notFound) {
@@ -559,25 +712,48 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
559
712
  }
560
713
  if (result && "props" in result) isSerializableProps(patternToNextFormat(route.pattern), "getStaticProps", pageProps);
561
714
  if (typeof result?.revalidate === "number" && result.revalidate > 0) isrRevalidateSeconds = result.revalidate;
715
+ else if (cached?.value.value?.kind === "PAGES" && cached.value.value.generatedFromDataRequest) isrRevalidateSeconds = cached.value.cacheControl?.revalidate ?? 31536e3;
562
716
  }
563
- if (typeof pageModule.getServerSideProps !== "function" && typeof pageModule.getStaticProps !== "function") {
717
+ if (typeof pageModule.getServerSideProps !== "function" && typeof pageModule.getStaticProps !== "function" && hasAppGetInitialProps) {
718
+ if (await loadAppInitialProps()) return;
719
+ }
720
+ if (typeof pageModule.getServerSideProps !== "function" && typeof pageModule.getStaticProps !== "function" && !hasAppGetInitialProps) {
564
721
  const initialProps = await loadPagesGetInitialProps(PageComponent, {
565
722
  req,
566
723
  res,
567
724
  pathname: patternToNextFormat(route.pattern),
568
725
  query,
569
- asPath: url,
726
+ asPath: requestAsPath,
570
727
  locale: locale ?? currentDefaultLocale,
571
728
  locales: i18nConfig?.locales,
572
729
  defaultLocale: currentDefaultLocale
573
730
  });
574
731
  if (res.headersSent || res.writableEnded) return;
575
- if (initialProps) pageProps = {
576
- ...pageProps,
577
- ...initialProps
578
- };
732
+ if (initialProps) {
733
+ pageProps = {
734
+ ...pageProps,
735
+ ...initialProps
736
+ };
737
+ renderProps = {
738
+ ...renderProps,
739
+ pageProps
740
+ };
741
+ }
579
742
  }
580
743
  if (isDataReq) {
744
+ if (shouldPersistFallbackData) {
745
+ const cacheKey = pagesIsrCacheKey(url.split("?")[0]);
746
+ const revalidateSeconds = isrRevalidateSeconds ?? 31536e3;
747
+ await isrSet(cacheKey, {
748
+ kind: "PAGES",
749
+ html: "",
750
+ pageData: renderProps,
751
+ generatedFromDataRequest: true,
752
+ headers: void 0,
753
+ status: void 0
754
+ }, revalidateSeconds);
755
+ setRevalidateDuration(cacheKey, revalidateSeconds);
756
+ }
581
757
  const dataHeaders = { "Content-Type": "application/json" };
582
758
  if (gsspExtraHeaders) for (const [k, v] of Object.entries(gsspExtraHeaders)) dataHeaders[k] = v;
583
759
  const dataRoutePattern = patternToNextFormat(route.pattern);
@@ -586,7 +762,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
586
762
  if (deploymentId) dataHeaders[NEXTJS_DEPLOYMENT_ID_HEADER] = deploymentId;
587
763
  }
588
764
  res.writeHead(statusCode ?? 200, dataHeaders);
589
- res.end(JSON.stringify({ pageProps }));
765
+ res.end(JSON.stringify(renderProps));
590
766
  _renderEnd = now();
591
767
  return;
592
768
  }
@@ -594,8 +770,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
594
770
  let element;
595
771
  const wrapWithRouterContext = routerShim.wrapWithRouterContext;
596
772
  if (AppComponent) element = createElement(AppComponent, {
773
+ ...renderProps,
597
774
  Component: PageComponent,
598
- pageProps
775
+ pageProps: renderProps.pageProps,
776
+ router: routerShim.default
599
777
  });
600
778
  else element = createElement(PageComponent, pageProps);
601
779
  if (wrapWithRouterContext) element = wrapWithRouterContext(element);
@@ -647,11 +825,14 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
647
825
  import "vinext/instrumentation-client";
648
826
  import React from "react";
649
827
  import { hydrateRoot } from "react-dom/client";
650
- import { installPagesRouterRuntime } from "vinext/pages-router-runtime";
651
- import { wrapWithRouterContext } from "next/router";
828
+ import Router, { wrapWithRouterContext } from "next/router";
652
829
 
653
830
  const nextData = window.__NEXT_DATA__;
654
- const { pageProps } = nextData.props;
831
+ const props = nextData.props && typeof nextData.props === "object" ? nextData.props : {};
832
+ const rawPageProps = props.pageProps;
833
+ const pageProps = rawPageProps && typeof rawPageProps === "object" ? rawPageProps : {};
834
+ window.__VINEXT_PAGE_LOADERS__ = { [nextData.page]: () => import("${pageModuleUrl}") };
835
+ window.__VINEXT_APP_LOADER__ = ${appModuleUrl ? `() => import("${appModuleUrl}")` : "undefined"};
655
836
 
656
837
  async function hydrate() {
657
838
  let hydrateRootOptions;
@@ -673,24 +854,34 @@ async function hydrate() {
673
854
  const appModule = await import("${appModuleUrl}");
674
855
  const AppComponent = appModule.default;
675
856
  window.__VINEXT_APP__ = AppComponent;
676
- element = React.createElement(AppComponent, { Component: PageComponent, pageProps });
857
+ element = React.createElement(AppComponent, {
858
+ ...props,
859
+ Component: PageComponent,
860
+ pageProps: rawPageProps,
861
+ router: Router,
862
+ });
677
863
  ` : `
678
864
  element = React.createElement(PageComponent, pageProps);
679
865
  `}
680
- element = wrapWithRouterContext(element);
866
+ let resolveHydrationCommit;
867
+ const hydrationCommitted = new Promise((resolve) => { resolveHydrationCommit = resolve; });
868
+ element = wrapWithRouterContext(element, resolveHydrationCommit);
681
869
  const root = hydrateRoot(document.getElementById("__next"), element, hydrateRootOptions);
682
870
  window.__VINEXT_ROOT__ = root;
683
- installPagesRouterRuntime();
871
+ await hydrationCommitted;
684
872
  const hydratedAt = performance.now();
685
873
  window.__VINEXT_HYDRATED_AT = hydratedAt;
686
874
  window.__NEXT_HYDRATED = true;
687
875
  window.__NEXT_HYDRATED_AT = hydratedAt;
688
876
  window.__NEXT_HYDRATED_CB?.();
877
+ if (nextData.isFallback) {
878
+ await Router.replace(window.location.pathname + window.location.search + window.location.hash, undefined, { _h: 1, scroll: false });
879
+ }
689
880
  }
690
881
  hydrate();
691
882
  <\/script>`;
692
883
  const nextDataScript = createInlineScriptTag(`window.__NEXT_DATA__ = ${safeJsonStringify({
693
- props: { pageProps },
884
+ props: renderProps,
694
885
  page: patternToNextFormat(route.pattern),
695
886
  query: params,
696
887
  buildId: process.env.__VINEXT_BUILD_ID,
@@ -726,7 +917,7 @@ hydrate();
726
917
  documentContext: {
727
918
  pathname: patternToNextFormat(route.pattern),
728
919
  query,
729
- asPath: url
920
+ asPath: requestAsPath
730
921
  },
731
922
  enhancePageElement: (renderPageOpts) => {
732
923
  let FinalApp = AppComponent;
@@ -735,6 +926,7 @@ hydrate();
735
926
  if (renderPageOpts && typeof renderPageOpts.enhanceComponent === "function") FinalComp = renderPageOpts.enhanceComponent(FinalComp);
736
927
  let enhancedElement;
737
928
  if (FinalApp) enhancedElement = createElement(FinalApp, {
929
+ ...renderProps,
738
930
  Component: FinalComp,
739
931
  pageProps
740
932
  });
@@ -753,6 +945,7 @@ hydrate();
753
945
  if (typeof routerShim.setSSRContext === "function") routerShim.setSSRContext(null);
754
946
  if (!scriptNonce && isrRevalidateSeconds !== null && isrRevalidateSeconds > 0) {
755
947
  let isrElement = AppComponent ? createElement(AppComponent, {
948
+ ...renderProps,
756
949
  Component: pageModule.default,
757
950
  pageProps
758
951
  }) : createElement(pageModule.default, pageProps);
@@ -767,7 +960,7 @@ hydrate();
767
960
  reportRequestError(e instanceof Error ? e : new Error(String(e)), {
768
961
  path: url,
769
962
  method: req.method ?? "GET",
770
- headers: Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v ?? "")]))
963
+ headers: Object.fromEntries(Object.entries(req.headers).filter(([k]) => !k.startsWith(":")).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v ?? "")]))
771
964
  }, {
772
965
  routerKind: "Pages Router",
773
966
  routePath: route.pattern,
@@ -0,0 +1,22 @@
1
+ //#region src/server/hybrid-route-priority.d.ts
2
+ type HybridRoutePriorityRoute = {
3
+ isDynamic: boolean;
4
+ pattern: string;
5
+ sourcePath?: string | null;
6
+ };
7
+ declare function validateHybridRouteConflicts(pagesRoutes: readonly HybridRoutePriorityRoute[], appRoutes: readonly HybridRoutePriorityRoute[]): void;
8
+ /**
9
+ * Return whether a matched Pages Router route should own the request instead
10
+ * of a matched App Router route.
11
+ *
12
+ * Next.js registers Pages providers before App providers, then sorts all
13
+ * dynamic route pathnames together in DefaultRouteMatcherManager. Vinext keeps
14
+ * separate route tries for each router, so the hybrid boundary needs to apply
15
+ * that same cross-router ordering after both routers have produced their best
16
+ * local match. The decision itself lives in
17
+ * `routing/utils.ts#compareHybridRoutePatterns` so the server and client
18
+ * always reach the same answer.
19
+ */
20
+ declare function pagesRouteHasPriorityOverAppRoute(pagesRoute: HybridRoutePriorityRoute, appRoute: HybridRoutePriorityRoute | null): boolean;
21
+ //#endregion
22
+ export { HybridRoutePriorityRoute, pagesRouteHasPriorityOverAppRoute, validateHybridRouteConflicts };
@@ -0,0 +1,33 @@
1
+ import { compareHybridRoutePatterns } from "../routing/utils.js";
2
+ import { validateRoutePatterns } from "../routing/route-validation.js";
3
+ //#region src/server/hybrid-route-priority.ts
4
+ function validateHybridRouteConflicts(pagesRoutes, appRoutes) {
5
+ const pagesByPattern = new Map(pagesRoutes.map((route) => [route.pattern, route]));
6
+ const conflicts = appRoutes.flatMap((appRoute) => {
7
+ const pagesRoute = pagesByPattern.get(appRoute.pattern);
8
+ return pagesRoute === void 0 ? [] : [[pagesRoute, appRoute]];
9
+ });
10
+ if (conflicts.length > 0) {
11
+ const message = `Conflicting app and page file${conflicts.length === 1 ? " was" : "s were"} found, please remove the conflicting files to continue:`;
12
+ throw new Error(`${message}\n${conflicts.map(([pagesRoute, appRoute]) => ` "${pagesRoute.sourcePath ?? pagesRoute.pattern}" - "${appRoute.sourcePath ?? appRoute.pattern}"`).join("\n")}`);
13
+ }
14
+ validateRoutePatterns([...pagesRoutes.map((route) => route.pattern), ...appRoutes.map((route) => route.pattern)]);
15
+ }
16
+ /**
17
+ * Return whether a matched Pages Router route should own the request instead
18
+ * of a matched App Router route.
19
+ *
20
+ * Next.js registers Pages providers before App providers, then sorts all
21
+ * dynamic route pathnames together in DefaultRouteMatcherManager. Vinext keeps
22
+ * separate route tries for each router, so the hybrid boundary needs to apply
23
+ * that same cross-router ordering after both routers have produced their best
24
+ * local match. The decision itself lives in
25
+ * `routing/utils.ts#compareHybridRoutePatterns` so the server and client
26
+ * always reach the same answer.
27
+ */
28
+ function pagesRouteHasPriorityOverAppRoute(pagesRoute, appRoute) {
29
+ if (appRoute === null) return true;
30
+ return compareHybridRoutePatterns(pagesRoute.pattern, pagesRoute.isDynamic, appRoute.pattern, appRoute.isDynamic) === "pages";
31
+ }
32
+ //#endregion
33
+ export { pagesRouteHasPriorityOverAppRoute, validateHybridRouteConflicts };
@@ -33,7 +33,15 @@ declare function isImageOptimizationPath(pathname: string): boolean;
33
33
  * Controls SVG handling and security headers for the image endpoint.
34
34
  */
35
35
  type ImageConfig = {
36
- /** Allow SVG through the image optimization endpoint. Default: false. */dangerouslyAllowSVG?: boolean;
36
+ /** Allowed device widths. Defaults to Next.js device sizes. */deviceSizes?: number[]; /** Allowed fixed-image widths. Defaults to Next.js image sizes. */
37
+ imageSizes?: number[];
38
+ /**
39
+ * Allowed output qualities. When unset, any quality from 1-100 is permitted
40
+ * (matches Next.js: an unset `images.qualities` is not restricted to a single
41
+ * value). When set, only the listed qualities are accepted.
42
+ */
43
+ qualities?: number[]; /** Allow SVG through the image optimization endpoint. Default: false. */
44
+ dangerouslyAllowSVG?: boolean;
37
45
  /**
38
46
  * Allow image optimization for hostnames that resolve to private IP addresses.
39
47
  * Default: false.
@@ -54,18 +62,19 @@ type ImageConfig = {
54
62
  */
55
63
  declare const DEFAULT_DEVICE_SIZES: number[];
56
64
  declare const DEFAULT_IMAGE_SIZES: number[];
65
+ type ParseImageParamsOptions = {
66
+ isDev?: boolean;
67
+ };
68
+ declare function resolveDevImageRedirect(requestUrl: URL, allowedWidths?: number[], allowedQualities?: number[], options?: ParseImageParamsOptions): string | null;
57
69
  /**
58
70
  * Parse and validate image optimization query parameters.
59
71
  * Returns null if the request is malformed.
60
72
  *
61
- * When `allowedWidths` is provided, the width must be 0 (no resize) or
62
- * exactly match one of the allowed values. This matches Next.js behavior
63
- * where only configured deviceSizes and imageSizes are accepted.
64
- *
65
- * When `allowedWidths` is not provided, any width from 0 to ABSOLUTE_MAX_WIDTH
66
- * is accepted (backwards-compatible fallback).
73
+ * Ported from Next.js:
74
+ * test/integration/image-optimizer/test/index.test.ts
75
+ * https://github.com/vercel/next.js/blob/canary/test/integration/image-optimizer/test/index.test.ts
67
76
  */
68
- declare function parseImageParams(url: URL, allowedWidths?: number[]): {
77
+ declare function parseImageParams(url: URL, allowedWidths?: number[], allowedQualities?: number[], options?: ParseImageParamsOptions): {
69
78
  imageUrl: string;
70
79
  width: number;
71
80
  quality: number;
@@ -113,4 +122,4 @@ type ImageHandlers = {
113
122
  */
114
123
  declare function handleImageOptimization(request: Request, handlers: ImageHandlers, allowedWidths?: number[], imageConfig?: ImageConfig): Promise<Response>;
115
124
  //#endregion
116
- export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, ImageConfig, ImageHandlers, VINEXT_IMAGE_OPTIMIZATION_PATH, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams };
125
+ export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, ImageConfig, ImageHandlers, ParseImageParamsOptions, VINEXT_IMAGE_OPTIMIZATION_PATH, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams, resolveDevImageRedirect };