diff --git a/.eslintrc.json b/.eslintrc.json index 4f3c9e2164f1..b2508a20109d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -71,6 +71,7 @@ "functions": false, "classes": false, "variables": false, + "enums": false, "typedefs": false } ], @@ -106,6 +107,16 @@ { "files": ["examples/**/*"], "rules": { + "@typescript-eslint/no-use-before-define": [ + "error", + { + "functions": true, + "classes": true, + "variables": true, + "enums": true, + "typedefs": true + } + ], "import/no-anonymous-default-export": [ "error", { diff --git a/docs/api-reference/next/future/image.md b/docs/api-reference/next/future/image.md index 437af9d77c3d..be66624adbb7 100644 --- a/docs/api-reference/next/future/image.md +++ b/docs/api-reference/next/future/image.md @@ -251,11 +251,37 @@ See also: ### sizes -A string that provides information about how wide the image will be at different breakpoints. +A string that provides information about how wide the image will be at different breakpoints. The value of `sizes` will greatly affect performance for images using [`fill`](#fill) or which are styled to have a responsive size. -It's important to assign `sizes` for responsive images that takes up less than the full viewport width. For example, when the parent element will constrain the image to always be less than half the viewport width, use `sizes="50vw"`. Without `sizes`, the image will be sent at twice the necessary resolution, decreasing performance. +The `sizes` property serves two important purposes related to image performance: -[Learn more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes). +First, the value of `sizes` is used by the browser to determine which size of the image to download, from `next/future/image`'s automatically-generated source set. When the browser chooses, it does not yet know the size of the image on the page, so it selects an image that is the same size or larger than the viewport. The `sizes` property allows you to tell the browser that the image will actually be smaller than full screen. If you don't specify a `sizes` value in an image with the `fill` property, a default value of `100vw` (full screen width) is used. + +Second, the `sizes` property configures how `next/future/image` automatically generates an image source set. If no `sizes` value is present, a small source set is generated, suitable for a fixed-size image. If `sizes` is defined, a large source set is generated, suitable for a responsive image. If the `sizes` property includes sizes such as `50vw`, which represent a percentage of the viewport width, then the source set is trimmed to not include any values which are too small to ever be necessary. + +For example, if you know your styling will cause an image to be full-width on mobile devices, in a 2-column layout on tablets, and a 3-column layout on desktop displays, you should include a sizes property such as the following: + +```js +import Image from 'next/image' +const Example = () => ( +
+ +
+) +``` + +This example `sizes` could have a dramatic effect on performance metrics. Without the `33vw` sizes, the image selected from the server would be 3 times as wide as it needs to be. Because file size is proportional to the square of the width, without `sizes` the user would download an image that's 9 times larger than necessary. + +Learn more about `srcset` and `sizes`: + +- [web.dev](https://web.dev/learn/design/responsive-images/#sizes) +- [mdn](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes) ### quality diff --git a/docs/api-reference/next/image.md b/docs/api-reference/next/image.md index 82e4830c8f28..b0b8a0ff3746 100644 --- a/docs/api-reference/next/image.md +++ b/docs/api-reference/next/image.md @@ -131,15 +131,37 @@ const MyImage = (props) => { ### sizes -A string that provides information about how wide the image will be at different breakpoints. Defaults to `100vw` (the full width of the screen) when using `layout="responsive"` or `layout="fill"`. +A string that provides information about how wide the image will be at different breakpoints. The value of `sizes` will greatly affect performance for images using `layout="responsive"` or `layout="fill"`. It will be ignored for images using `layout="intrinsic"` or `layout="fixed"`. -If you are using `layout="fill"` or `layout="responsive"` it's important to assign `sizes` for any image that takes up less than the full viewport width. +The `sizes` property serves two important purposes related to image performance: -For example, when the parent element will constrain the image to always be less than half the viewport width, use `sizes="50vw"`. Without `sizes`, the image will be sent at twice the necessary resolution, decreasing performance. +First, the value of `sizes` is used by the browser to determine which size of the image to download, from `next/image`'s automatically-generated source set. When the browser chooses, it does not yet know the size of the image on the page, so it selects an image that is the same size or larger than the viewport. The `sizes` property allows you to tell the browser that the image will actually be smaller than full screen. If you don't specify a `sizes` value, a default value of `100vw` (full screen width) is used. -If you are using `layout="intrinsic"` or `layout="fixed"`, then `sizes` is not needed because the upper bound width is constrained already. +Second, the `sizes` value is parsed and used to trim the values in the automatically-created source set. If the `sizes` property includes sizes such as `50vw`, which represent a percentage of the viewport width, then the source set is trimmed to not include any values which are too small to ever be necessary. -[Learn more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes). +For example, if you know your styling will cause an image to be full-width on mobile devices, in a 2-column layout on tablets, and a 3-column layout on desktop displays, you should include a sizes property such as the following: + +```js +import Image from 'next/image' +const Example = () => ( +
+ +
+) +``` + +This example `sizes` could have a dramatic effect on performance metrics. Without the `33vw` sizes, the image selected from the server would be 3 times as wide as it needs to be. Because file size is proportional to the square of the width, without `sizes` the user would download an image that's 9 times larger than necessary. + +Learn more about `srcset` and `sizes`: + +- [web.dev](https://web.dev/learn/design/responsive-images/#sizes) +- [mdn](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes) ### quality diff --git a/docs/api-reference/next/router.md b/docs/api-reference/next/router.md index f9f3ac0e602a..e068a85e479a 100644 --- a/docs/api-reference/next/router.md +++ b/docs/api-reference/next/router.md @@ -248,14 +248,16 @@ export default function Page() { Prefetch pages for faster client-side transitions. This method is only useful for navigations without [`next/link`](/docs/api-reference/next/link.md), as `next/link` takes care of prefetching pages automatically. -> This is a production only feature. Next.js doesn't prefetch pages on development. +> This is a production only feature. Next.js doesn't prefetch pages in development. ```jsx -router.prefetch(url, as) +router.prefetch(url, as, options) ``` - `url` - The URL to prefetch, including explicit routes (e.g. `/dashboard`) and dynamic routes (e.g. `/product/[id]`) - `as` - Optional decorator for `url`. Before Next.js 9.5.3 this was used to prefetch dynamic routes, check our [previous docs](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes) to see how it worked +- `options` - Optional object with the following allowed fields: + - `locale` - allows providing a different locale from the active one. If `false`, `url` has to include the locale as the active locale won't be used. #### Usage diff --git a/errors/invalid-getserversideprops-return-value.md b/errors/invalid-getserversideprops-value.md similarity index 100% rename from errors/invalid-getserversideprops-return-value.md rename to errors/invalid-getserversideprops-value.md diff --git a/errors/manifest.json b/errors/manifest.json index 44fcf7f3df62..3f013867de55 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -673,8 +673,14 @@ "path": "/errors/import-next.md" }, { - "title": "invalid-getserversideprops-return-value", - "path": "/errors/invalid-getserversideprops-return-value.md" + "path": "/errors/invalid-getserversideprops-return-value.md", + "redirect": { + "destination": "/docs/messages/invalid-getserversideprops-value" + } + }, + { + "title": "invalid-getserversideprops-value", + "path": "/errors/invalid-getserversideprops-value.md" }, { "title": "no-assign-module-variable", diff --git a/examples/amp-first/components/amp/AmpCustomElement.tsx b/examples/amp-first/components/amp/AmpCustomElement.tsx index 1a14221ea492..0c02ae55668c 100644 --- a/examples/amp-first/components/amp/AmpCustomElement.tsx +++ b/examples/amp-first/components/amp/AmpCustomElement.tsx @@ -319,6 +319,10 @@ export function AmpIncludeAmpLinkRewriter() { return } +export function AmpIncludeAmpMustache() { + return +} + export function AmpIncludeAmpList() { return ( <> @@ -350,10 +354,6 @@ export function AmpIncludeAmpMowplayer() { return } -export function AmpIncludeAmpMustache() { - return -} - export function AmpIncludeAmpNextPage() { return } diff --git a/examples/amp-first/components/amp/AmpScript.tsx b/examples/amp-first/components/amp/AmpScript.tsx index d8b2b4d2e00b..c884ec5ae83f 100644 --- a/examples/amp-first/components/amp/AmpScript.tsx +++ b/examples/amp-first/components/amp/AmpScript.tsx @@ -10,6 +10,13 @@ type AmpScriptProps = { src?: string } +const generateInlineScript = (script: Function | string) => { + if (typeof script === 'function') { + return `${script.toString()}()` + } + return String(script) +} + /** * Embeds an AMP Script by either linking to a TS `src` file or embedding inline * AMP Script via the `script` property. The inline script hash will automatically @@ -42,10 +49,3 @@ const AmpScript: React.FC = ({ } export default AmpScript - -const generateInlineScript = (script: Function | string) => { - if (typeof script === 'function') { - return `${script.toString()}()` - } - return String(script) -} diff --git a/examples/cms-agilitycms/lib/components/rich-text-area.tsx b/examples/cms-agilitycms/lib/components/rich-text-area.tsx index f747cf007653..7ff6f6f456db 100644 --- a/examples/cms-agilitycms/lib/components/rich-text-area.tsx +++ b/examples/cms-agilitycms/lib/components/rich-text-area.tsx @@ -1,3 +1,7 @@ +const setHTML = (textblob) => { + return { __html: textblob } +} + export default function RichTextArea({ fields }) { const { textblob } = fields return ( @@ -7,7 +11,3 @@ export default function RichTextArea({ fields }) { /> ) } - -const setHTML = (textblob) => { - return { __html: textblob } -} diff --git a/examples/cms-agilitycms/lib/dependancies.ts b/examples/cms-agilitycms/lib/dependancies.ts index 44ccead1baee..183fceba8ac3 100644 --- a/examples/cms-agilitycms/lib/dependancies.ts +++ b/examples/cms-agilitycms/lib/dependancies.ts @@ -3,6 +3,23 @@ const path = require('path') const userComponentsPath = path.resolve('./components') const libComponentsPath = path.resolve('./lib/components') +const requireComponent = (name) => { + let Component = null + + try { + //check the user path first (must be relative paths) + Component = require(`../components/${name}.tsx`).default + } catch {} + + if (!Component) + try { + //fallback to lib path (must be relative paths) + Component = require(`./components/${name}.tsx`).default + } catch {} + + return Component +} + //Bug: when dynamic imports are used within the module, it doest not get outputted server-side //let AgilityModule = dynamic(() => import ('../components/' + m.moduleName)); @@ -32,20 +49,3 @@ export const requireComponentDependancyByName = (name) => { return Component } - -const requireComponent = (name) => { - let Component = null - - try { - //check the user path first (must be relative paths) - Component = require(`../components/${name}.tsx`).default - } catch {} - - if (!Component) - try { - //fallback to lib path (must be relative paths) - Component = require(`./components/${name}.tsx`).default - } catch {} - - return Component -} diff --git a/examples/cms-agilitycms/lib/preview.ts b/examples/cms-agilitycms/lib/preview.ts index 7189f4ca398d..42e456df04b6 100644 --- a/examples/cms-agilitycms/lib/preview.ts +++ b/examples/cms-agilitycms/lib/preview.ts @@ -2,45 +2,27 @@ import crypto from 'crypto' import { getClient } from './api' import { CMS_LANG, CMS_CHANNEL } from './constants' -//Validates whether the incoming preview request is valid -export async function validatePreview({ agilityPreviewKey, slug, contentID }) { - //Validate the preview key - if (!agilityPreviewKey) { - return { - error: true, - message: `Missing agilitypreviewkey.`, - } - } - - //sanitize incoming key (replace spaces with '+') - if (agilityPreviewKey.indexOf(` `) > -1) { - agilityPreviewKey = agilityPreviewKey.split(` `).join(`+`) - } - - //compare the preview key being used - const correctPreviewKey = generatePreviewKey() +//Generates a preview key to compare against +export function generatePreviewKey() { + //the string we want to encode + const str = `-1_${process.env.AGILITY_CMS_SECURITY_KEY}_Preview` - if (agilityPreviewKey !== correctPreviewKey) { - return { - error: true, - message: `Invalid agilitypreviewkey.`, - //message: `Invalid agilitypreviewkey. Incoming key is=${agilityPreviewKey} compared to=${correctPreviewKey}...` - } + //build our byte array + let data = [] + for (var i = 0; i < str.length; ++i) { + data.push(str.charCodeAt(i)) + data.push(0) } - const validateSlugResponse = await validateSlugForPreview({ slug, contentID }) - - if (validateSlugResponse.error) { - //kickout - return validateSlugResponse - } + //convert byte array to buffer + const strBuffer = Buffer.from(data) + //encode it! + const previewKey = crypto + .createHash('sha512') + .update(strBuffer) + .digest('base64') - //return success - return { - error: false, - message: null, - slug: validateSlugResponse.slug, - } + return previewKey } //Checks that the requested page exists, if not return a 401 @@ -95,25 +77,43 @@ export async function validateSlugForPreview({ slug, contentID }) { } } -//Generates a preview key to compare against -export function generatePreviewKey() { - //the string we want to encode - const str = `-1_${process.env.AGILITY_CMS_SECURITY_KEY}_Preview` +//Validates whether the incoming preview request is valid +export async function validatePreview({ agilityPreviewKey, slug, contentID }) { + //Validate the preview key + if (!agilityPreviewKey) { + return { + error: true, + message: `Missing agilitypreviewkey.`, + } + } - //build our byte array - let data = [] - for (var i = 0; i < str.length; ++i) { - data.push(str.charCodeAt(i)) - data.push(0) + //sanitize incoming key (replace spaces with '+') + if (agilityPreviewKey.indexOf(` `) > -1) { + agilityPreviewKey = agilityPreviewKey.split(` `).join(`+`) } - //convert byte array to buffer - const strBuffer = Buffer.from(data) - //encode it! - const previewKey = crypto - .createHash('sha512') - .update(strBuffer) - .digest('base64') + //compare the preview key being used + const correctPreviewKey = generatePreviewKey() - return previewKey + if (agilityPreviewKey !== correctPreviewKey) { + return { + error: true, + message: `Invalid agilitypreviewkey.`, + //message: `Invalid agilitypreviewkey. Incoming key is=${agilityPreviewKey} compared to=${correctPreviewKey}...` + } + } + + const validateSlugResponse = await validateSlugForPreview({ slug, contentID }) + + if (validateSlugResponse.error) { + //kickout + return validateSlugResponse + } + + //return success + return { + error: false, + message: null, + slug: validateSlugResponse.slug, + } } diff --git a/examples/cms-builder-io/lib/api.js b/examples/cms-builder-io/lib/api.js index 6b80cf062d97..14b287ab1fb0 100644 --- a/examples/cms-builder-io/lib/api.js +++ b/examples/cms-builder-io/lib/api.js @@ -11,13 +11,6 @@ export function getAllPostsWithSlug() { }) } -export function getAllPostsForHome(preview) { - return searchPosts( - { 'data.slug': { $exists: true }, 'data.author': { $exists: true } }, - preview - ) -} - export function getDraftPost(id) { return fetch( `https://builder.io/api/v2/content/${BUILDER_CONFIG.postsModel}/${id}?apiKey=${BUILDER_CONFIG.apiKey}&preview=true&noCache=true&cachebust=tru&includeRefs=true` @@ -26,25 +19,6 @@ export function getDraftPost(id) { .then((res) => res || null) } -export async function getPost(mongoQuery, preview) { - let post = preview - ? (await searchPosts(mongoQuery, true))?.[0] - : await builder - .get(BUILDER_CONFIG.postsModel, { - includeRefs: true, - staleCacheSeconds: 20, - apiKey: BUILDER_CONFIG.apiKey, - preview: BUILDER_CONFIG.postsModel, - options: { - noTargeting: true, - }, - query: mongoQuery, - }) - .toPromise() - - return post || null -} - export async function searchPosts(query, preview, limit = 20, offset = 0) { let posts = await builder.getAll(BUILDER_CONFIG.postsModel, { limit, @@ -67,6 +41,32 @@ export async function searchPosts(query, preview, limit = 20, offset = 0) { return posts } +export function getAllPostsForHome(preview) { + return searchPosts( + { 'data.slug': { $exists: true }, 'data.author': { $exists: true } }, + preview + ) +} + +export async function getPost(mongoQuery, preview) { + let post = preview + ? (await searchPosts(mongoQuery, true))?.[0] + : await builder + .get(BUILDER_CONFIG.postsModel, { + includeRefs: true, + staleCacheSeconds: 20, + apiKey: BUILDER_CONFIG.apiKey, + preview: BUILDER_CONFIG.postsModel, + options: { + noTargeting: true, + }, + query: mongoQuery, + }) + .toPromise() + + return post || null +} + export async function getPostAndMorePosts(slug, preview, previewData) { const post = preview && previewData diff --git a/examples/cms-buttercms/lib/api.js b/examples/cms-buttercms/lib/api.js index 5913ae1277ef..b4a07b946fdf 100644 --- a/examples/cms-buttercms/lib/api.js +++ b/examples/cms-buttercms/lib/api.js @@ -16,6 +16,24 @@ try { const defaultPageSize = 100 const defaultPostCount = 10 +async function getLandingPagesData(page, pageSize = defaultPageSize) { + try { + const params = { + page, + page_size: pageSize, + } + const response = await butter.page.list('landing-page', params) + + return { + pages: response?.data?.data, + prevPage: response?.data?.meta.previous_page, + nextPage: response?.data?.meta.next_page, + } + } catch (e) { + throw e.response.data.detail + } +} + export async function getLandingPage(slug) { try { const page = await butter.page.retrieve('landing-page', slug) @@ -38,24 +56,6 @@ export async function getLandingPages() { return paginatedLandingPages } -async function getLandingPagesData(page, pageSize = defaultPageSize) { - try { - const params = { - page, - page_size: pageSize, - } - const response = await butter.page.list('landing-page', params) - - return { - pages: response?.data?.data, - prevPage: response?.data?.meta.previous_page, - nextPage: response?.data?.meta.next_page, - } - } catch (e) { - throw e.response.data.detail - } -} - export async function getPostsData( { page, pageSize, tag, category } = { page: 1, pageSize: defaultPostCount } ) { diff --git a/examples/cms-kontent/components/image.js b/examples/cms-kontent/components/image.js index 7b47e9176ad0..bbf5694d9330 100644 --- a/examples/cms-kontent/components/image.js +++ b/examples/cms-kontent/components/image.js @@ -1,10 +1,6 @@ import NextImage from 'next/image' import { transformImageUrl } from '@kentico/kontent-delivery' -const getLoader = (src) => { - return srcIsKontentAsset(src) ? kontentImageLoader : undefined -} - const srcIsKontentAsset = (src) => { try { const { hostname } = new URL(src) @@ -23,6 +19,10 @@ const kontentImageLoader = ({ src, width, quality = 75 }) => { .getUrl() } +const getLoader = (src) => { + return srcIsKontentAsset(src) ? kontentImageLoader : undefined +} + export default function Image(props) { const loader = getLoader(props.src) diff --git a/examples/with-aphrodite/pages/index.js b/examples/with-aphrodite/pages/index.js index 165a20546656..e13f3adddf11 100644 --- a/examples/with-aphrodite/pages/index.js +++ b/examples/with-aphrodite/pages/index.js @@ -7,14 +7,6 @@ if (typeof window !== 'undefined') { StyleSheet.rehydrate(window.__REHYDRATE_IDS) } -export default function Home() { - return ( -
-

My page

-
- ) -} - const styles = StyleSheet.create({ root: { width: 80, @@ -34,3 +26,11 @@ const styles = StyleSheet.create({ }, }, }) + +export default function Home() { + return ( +
+

My page

+
+ ) +} diff --git a/examples/with-apivideo-upload/components/Card/index.tsx b/examples/with-apivideo-upload/components/Card/index.tsx index 287da46ff84b..6f9d3a61f9e0 100644 --- a/examples/with-apivideo-upload/components/Card/index.tsx +++ b/examples/with-apivideo-upload/components/Card/index.tsx @@ -8,19 +8,6 @@ interface ICardProps { method: 'get' | 'post' } -const Card: React.FC = ({ content, url, method }): JSX.Element => ( - - {method.toUpperCase()} - {content} - - Sketch arrow -

Try it out with our API!

-
-
-) - -export default Card - const Container = styled.a` border: 1px solid rgb(215, 219, 236); border-radius: 0.25rem; @@ -59,3 +46,16 @@ const ImageContainer = styled.div` font-size: 0.5rem; } ` + +const Card: React.FC = ({ content, url, method }): JSX.Element => ( + + {method.toUpperCase()} + {content} + + Sketch arrow +

Try it out with our API!

+
+
+) + +export default Card diff --git a/examples/with-apivideo-upload/components/Loader/index.tsx b/examples/with-apivideo-upload/components/Loader/index.tsx index 0b0bd14a8f1f..9abbffdb189a 100644 --- a/examples/with-apivideo-upload/components/Loader/index.tsx +++ b/examples/with-apivideo-upload/components/Loader/index.tsx @@ -5,10 +5,6 @@ import styled, { keyframes } from 'styled-components' interface ILoaderProps { done: boolean } -const Loader: React.FC = ({ done }): JSX.Element => - done ? : - -export default Loader const spin = keyframes` 0% { @@ -18,6 +14,7 @@ const spin = keyframes` transform: rotate(360deg); } ` + const Spinner = styled.div` border: 3px solid #f3f3f3; border-top: 3px solid rgb(235, 137, 82); @@ -26,3 +23,8 @@ const Spinner = styled.div` height: 25px; animation: ${spin} 1s linear infinite; ` + +const Loader: React.FC = ({ done }): JSX.Element => + done ? : + +export default Loader diff --git a/examples/with-apivideo-upload/components/Status/index.tsx b/examples/with-apivideo-upload/components/Status/index.tsx index 90d494863c0f..d83f8fc12354 100644 --- a/examples/with-apivideo-upload/components/Status/index.tsx +++ b/examples/with-apivideo-upload/components/Status/index.tsx @@ -6,6 +6,13 @@ interface IStatusProps { done: boolean title: string } + +const Container = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; +` const Status: React.FC = ({ done, title }): JSX.Element => (

{title}

@@ -14,10 +21,3 @@ const Status: React.FC = ({ done, title }): JSX.Element => ( ) export default Status - -const Container = styled.div` - display: flex; - flex-direction: column; - align-items: center; - gap: 5px; -` diff --git a/examples/with-apivideo-upload/pages/index.tsx b/examples/with-apivideo-upload/pages/index.tsx index 24ec5d4edf99..63bb34361dff 100644 --- a/examples/with-apivideo-upload/pages/index.tsx +++ b/examples/with-apivideo-upload/pages/index.tsx @@ -42,6 +42,30 @@ const Home: NextPage = () => { fetcher ) + const fetchVideoStatus = async (videoId: string): Promise => { + const { status } = await fetcher(`/api/${videoId}`) + const { encoding, ingest } = status + setStatus({ + ingested: ingest.status === 'uploaded', + encoded: encoding.playable, + }) + if (ingest.status === 'uploaded' && encoding.playable) { + setSize({ + width: encoding.metadata.width, + height: encoding.metadata.height, + }) + setReady(true) + } + } + + const clearState = (): void => { + setReady(false) + setStatus({ ingested: false, encoded: false }) + setVideo(undefined) + setUploadProgress(undefined) + setSize(undefined) + } + useEffect(() => { if (video) { const intervalId = window.setInterval(() => { @@ -72,35 +96,11 @@ const Home: NextPage = () => { setVideo(video) } - const fetchVideoStatus = async (videoId: string): Promise => { - const { status } = await fetcher(`/api/${videoId}`) - const { encoding, ingest } = status - setStatus({ - ingested: ingest.status === 'uploaded', - encoded: encoding.playable, - }) - if (ingest.status === 'uploaded' && encoding.playable) { - setSize({ - width: encoding.metadata.width, - height: encoding.metadata.height, - }) - setReady(true) - } - } - const handleNavigate = (): void => { if (!video) return router.push(`/${video.videoId}?w=${size?.width}&h=${size?.height}`) } - const clearState = (): void => { - setReady(false) - setStatus({ ingested: false, encoded: false }) - setVideo(undefined) - setUploadProgress(undefined) - setSize(undefined) - } - return ( diff --git a/examples/with-apollo-and-redux/components/PostList.js b/examples/with-apollo-and-redux/components/PostList.js index d9b01d3ef1e6..a0eee23090ab 100644 --- a/examples/with-apollo-and-redux/components/PostList.js +++ b/examples/with-apollo-and-redux/components/PostList.js @@ -35,6 +35,8 @@ export default function PostList() { const loadingMorePosts = networkStatus === NetworkStatus.fetchMore + const { allPosts, _allPostsMeta } = data + const loadMorePosts = () => { fetchMore({ variables: { @@ -46,7 +48,6 @@ export default function PostList() { if (error) return if (loading && !loadingMorePosts) return
Loading
- const { allPosts, _allPostsMeta } = data const areMorePosts = allPosts.length < _allPostsMeta.count return ( diff --git a/examples/with-apollo/components/PostList.js b/examples/with-apollo/components/PostList.js index 4748ac8f35a6..381c156e8189 100644 --- a/examples/with-apollo/components/PostList.js +++ b/examples/with-apollo/components/PostList.js @@ -35,6 +35,7 @@ export default function PostList() { ) const loadingMorePosts = networkStatus === NetworkStatus.fetchMore + const { allPosts, _allPostsMeta } = data const loadMorePosts = () => { fetchMore({ @@ -47,7 +48,6 @@ export default function PostList() { if (error) return if (loading && !loadingMorePosts) return
Loading
- const { allPosts, _allPostsMeta } = data const areMorePosts = allPosts.length < _allPostsMeta.count return ( diff --git a/examples/with-babel-macros/pages/index.js b/examples/with-babel-macros/pages/index.js index dea28155a3da..0a0b2d5d6de9 100644 --- a/examples/with-babel-macros/pages/index.js +++ b/examples/with-babel-macros/pages/index.js @@ -5,9 +5,7 @@ const whoami = preval` module.exports = userInfo.username ` -export default WhoAmI - -function WhoAmI() { +export default function WhoAmI() { return (

diff --git a/examples/with-cerebral/components/Clock.js b/examples/with-cerebral/components/Clock.js index cfbddeae67ab..e2f4fcc8a3c3 100644 --- a/examples/with-cerebral/components/Clock.js +++ b/examples/with-cerebral/components/Clock.js @@ -1,3 +1,7 @@ +const pad = (n) => (n < 10 ? `0${n}` : n) +const format = (t) => + `${pad(t.getUTCHours())}:${pad(t.getUTCMinutes())}:${pad(t.getUTCSeconds())}` + export default function Clock(props) { return (
@@ -18,8 +22,3 @@ export default function Clock(props) {
) } - -const format = (t) => - `${pad(t.getUTCHours())}:${pad(t.getUTCMinutes())}:${pad(t.getUTCSeconds())}` - -const pad = (n) => (n < 10 ? `0${n}` : n) diff --git a/examples/with-cxs/pages/index.js b/examples/with-cxs/pages/index.js index bc7e660e5b23..fe0921355373 100644 --- a/examples/with-cxs/pages/index.js +++ b/examples/with-cxs/pages/index.js @@ -8,14 +8,6 @@ if (typeof window !== 'undefined') { cxs.rehydrate(serverCss) } -export default function Home() { - return ( -
-

My page

-
- ) -} - const cx = { root: cxs({ width: 80, @@ -35,3 +27,11 @@ const cx = { }, }), } + +export default function Home() { + return ( +
+

My page

+
+ ) +} diff --git a/examples/with-draft-js/pages/index.js b/examples/with-draft-js/pages/index.js index 950f5b4aceaa..3185ad29e701 100644 --- a/examples/with-draft-js/pages/index.js +++ b/examples/with-draft-js/pages/index.js @@ -7,6 +7,81 @@ import { convertFromRaw, } from 'draft-js' +const initialData = { + blocks: [ + { + key: '16d0k', + text: 'You can edit this text.', + type: 'unstyled', + depth: 0, + inlineStyleRanges: [{ offset: 0, length: 23, style: 'BOLD' }], + entityRanges: [], + data: {}, + }, + { + key: '98peq', + text: '', + type: 'unstyled', + depth: 0, + inlineStyleRanges: [], + entityRanges: [], + data: {}, + }, + { + key: 'ecmnc', + text: 'Luke Skywalker has vanished. In his absence, the sinister FIRST ORDER has risen from the ashes of the Empire and will not rest until Skywalker, the last Jedi, has been destroyed.', + type: 'unstyled', + depth: 0, + inlineStyleRanges: [ + { offset: 0, length: 14, style: 'BOLD' }, + { offset: 133, length: 9, style: 'BOLD' }, + ], + entityRanges: [], + data: {}, + }, + { + key: 'fe2gn', + text: '', + type: 'unstyled', + depth: 0, + inlineStyleRanges: [], + entityRanges: [], + data: {}, + }, + { + key: '4481k', + text: 'With the support of the REPUBLIC, General Leia Organa leads a brave RESISTANCE. She is desperate to find her brother Luke and gain his help in restoring peace and justice to the galaxy.', + type: 'unstyled', + depth: 0, + inlineStyleRanges: [ + { offset: 34, length: 19, style: 'BOLD' }, + { offset: 117, length: 4, style: 'BOLD' }, + { offset: 68, length: 10, style: 'ANYCUSTOMSTYLE' }, + ], + entityRanges: [], + data: {}, + }, + ], + entityMap: {}, +} + +// Custom overrides for each style +const styleMap = { + CODE: { + backgroundColor: 'rgba(0, 0, 0, 0.05)', + fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace', + fontSize: 16, + padding: 4, + }, + BOLD: { + color: '#395296', + fontWeight: 'bold', + }, + ANYCUSTOMSTYLE: { + color: '#00e400', + }, +} + export default class App extends Component { constructor(props) { super(props) @@ -223,23 +298,6 @@ export default class App extends Component { } } -// Custom overrides for each style -const styleMap = { - CODE: { - backgroundColor: 'rgba(0, 0, 0, 0.05)', - fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace', - fontSize: 16, - padding: 4, - }, - BOLD: { - color: '#395296', - fontWeight: 'bold', - }, - ANYCUSTOMSTYLE: { - color: '#00e400', - }, -} - class ToolbarButton extends Component { constructor() { super() @@ -285,61 +343,3 @@ const ToolBar = (props) => {

) } - -const initialData = { - blocks: [ - { - key: '16d0k', - text: 'You can edit this text.', - type: 'unstyled', - depth: 0, - inlineStyleRanges: [{ offset: 0, length: 23, style: 'BOLD' }], - entityRanges: [], - data: {}, - }, - { - key: '98peq', - text: '', - type: 'unstyled', - depth: 0, - inlineStyleRanges: [], - entityRanges: [], - data: {}, - }, - { - key: 'ecmnc', - text: 'Luke Skywalker has vanished. In his absence, the sinister FIRST ORDER has risen from the ashes of the Empire and will not rest until Skywalker, the last Jedi, has been destroyed.', - type: 'unstyled', - depth: 0, - inlineStyleRanges: [ - { offset: 0, length: 14, style: 'BOLD' }, - { offset: 133, length: 9, style: 'BOLD' }, - ], - entityRanges: [], - data: {}, - }, - { - key: 'fe2gn', - text: '', - type: 'unstyled', - depth: 0, - inlineStyleRanges: [], - entityRanges: [], - data: {}, - }, - { - key: '4481k', - text: 'With the support of the REPUBLIC, General Leia Organa leads a brave RESISTANCE. She is desperate to find her brother Luke and gain his help in restoring peace and justice to the galaxy.', - type: 'unstyled', - depth: 0, - inlineStyleRanges: [ - { offset: 34, length: 19, style: 'BOLD' }, - { offset: 117, length: 4, style: 'BOLD' }, - { offset: 68, length: 10, style: 'ANYCUSTOMSTYLE' }, - ], - entityRanges: [], - data: {}, - }, - ], - entityMap: {}, -} diff --git a/examples/with-edgedb/pages/api/post/[id].ts b/examples/with-edgedb/pages/api/post/[id].ts index 32aa946615b9..a1971a52bc0e 100644 --- a/examples/with-edgedb/pages/api/post/[id].ts +++ b/examples/with-edgedb/pages/api/post/[id].ts @@ -1,23 +1,6 @@ import type { NextApiRequest, NextApiResponse } from 'next' import { client, e } from '../../../client' -export default async function handle( - req: NextApiRequest, - res: NextApiResponse -) { - const postId = req.query.id as string - - if (req.method === 'DELETE') { - res.json(await deletePost(postId)) - } else if (req.method === 'PATCH') { - res.json(await updatePost(postId, req.body)) - } else { - throw new Error( - `The HTTP ${req.method} method is not supported at this route.` - ) - } -} - // PATCH /api/post/:id async function updatePost( postId: string, @@ -42,3 +25,20 @@ async function deletePost(postId: string) { })) .run(client) } + +export default async function handle( + req: NextApiRequest, + res: NextApiResponse +) { + const postId = req.query.id as string + + if (req.method === 'DELETE') { + res.json(await deletePost(postId)) + } else if (req.method === 'PATCH') { + res.json(await updatePost(postId, req.body)) + } else { + throw new Error( + `The HTTP ${req.method} method is not supported at this route.` + ) + } +} diff --git a/examples/with-expo-typescript/pages/index.tsx b/examples/with-expo-typescript/pages/index.tsx index a833b7b6d98a..61d3eba5202f 100644 --- a/examples/with-expo-typescript/pages/index.tsx +++ b/examples/with-expo-typescript/pages/index.tsx @@ -2,14 +2,6 @@ import React from 'react' import { StyleSheet, Text, View } from 'react-native' -export default function App() { - return ( - - Welcome to Expo + Next.js ๐Ÿ‘‹ - - ) -} - const styles = StyleSheet.create({ container: { flex: 1, @@ -20,3 +12,11 @@ const styles = StyleSheet.create({ fontSize: 16, }, }) + +export default function App() { + return ( + + Welcome to Expo + Next.js ๐Ÿ‘‹ + + ) +} diff --git a/examples/with-expo/pages/index.js b/examples/with-expo/pages/index.js index 7bf300c94990..90b9d18c7086 100644 --- a/examples/with-expo/pages/index.js +++ b/examples/with-expo/pages/index.js @@ -1,14 +1,6 @@ // @generated: @expo/next-adapter@2.1.5 import { StyleSheet, Text, View } from 'react-native' -export default function App() { - return ( - - Welcome to Expo + Next.js ๐Ÿ‘‹ - - ) -} - const styles = StyleSheet.create({ container: { flex: 1, @@ -19,3 +11,11 @@ const styles = StyleSheet.create({ fontSize: 16, }, }) + +export default function App() { + return ( + + Welcome to Expo + Next.js ๐Ÿ‘‹ + + ) +} diff --git a/examples/with-fingerprintjs-pro/components/Nav.tsx b/examples/with-fingerprintjs-pro/components/Nav.tsx index 3315c84c36a0..0e8ff28a3d78 100644 --- a/examples/with-fingerprintjs-pro/components/Nav.tsx +++ b/examples/with-fingerprintjs-pro/components/Nav.tsx @@ -3,15 +3,6 @@ import { useRouter } from 'next/router' import Link from 'next/link' import { RouterProps } from './types' -export const Nav: React.FC = () => { - return ( - - ) -} - const CustomLink: React.FC> = ({ to, children, @@ -35,3 +26,12 @@ const CustomLink: React.FC> = ({ ) } + +export const Nav: React.FC = () => { + return ( + + ) +} diff --git a/examples/with-fingerprintjs-pro/pages/home/[cacheStrategy].tsx b/examples/with-fingerprintjs-pro/pages/home/[cacheStrategy].tsx index 5f67e8eef36b..c9a786a6bcef 100644 --- a/examples/with-fingerprintjs-pro/pages/home/[cacheStrategy].tsx +++ b/examples/with-fingerprintjs-pro/pages/home/[cacheStrategy].tsx @@ -6,6 +6,14 @@ import { useVisitorData, } from '@fingerprintjs/fingerprintjs-pro-react' +function VisitorDataComponent() { + const { data, isLoading, error } = useVisitorData({ extendedResult: true }) + + return ( + + ) +} + function HomePage() { const { clearCache } = useContext(FpjsContext) @@ -27,12 +35,4 @@ function HomePage() { ) } -function VisitorDataComponent() { - const { data, isLoading, error } = useVisitorData({ extendedResult: true }) - - return ( - - ) -} - export default HomePage diff --git a/examples/with-graphql-hooks/components/submit.js b/examples/with-graphql-hooks/components/submit.js index 71e7fb8395a5..ca69203795bd 100644 --- a/examples/with-graphql-hooks/components/submit.js +++ b/examples/with-graphql-hooks/components/submit.js @@ -12,6 +12,22 @@ mutation createPost($title: String!, $url: String!) { } }` +async function handleSubmit(event, onSubmission, createPost) { + event.preventDefault() + const form = event.target + const formData = new window.FormData(form) + const title = formData.get('title') + const url = formData.get('url') + form.reset() + const result = await createPost({ + variables: { + title, + url, + }, + }) + onSubmission && onSubmission(result) +} + export default function Submit({ onSubmission }) { const [createPost, state] = useMutation(CREATE_POST) @@ -38,19 +54,3 @@ export default function Submit({ onSubmission }) { ) } - -async function handleSubmit(event, onSubmission, createPost) { - event.preventDefault() - const form = event.target - const formData = new window.FormData(form) - const title = formData.get('title') - const url = formData.get('url') - form.reset() - const result = await createPost({ - variables: { - title, - url, - }, - }) - onSubmission && onSubmission(result) -} diff --git a/examples/with-iron-session/lib/fetchJson.ts b/examples/with-iron-session/lib/fetchJson.ts index d8402eb86574..7165e84b92d3 100644 --- a/examples/with-iron-session/lib/fetchJson.ts +++ b/examples/with-iron-session/lib/fetchJson.ts @@ -1,26 +1,3 @@ -export default async function fetchJson( - input: RequestInfo, - init?: RequestInit -): Promise { - const response = await fetch(input, init) - - // if the server replies, there's always some data in json - // if there's a network error, it will throw at the previous line - const data = await response.json() - - // response.ok is true when res.status is 2xx - // https://developer.mozilla.org/en-US/docs/Web/API/Response/ok - if (response.ok) { - return data - } - - throw new FetchError({ - message: response.statusText, - response, - data, - }) -} - export class FetchError extends Error { response: Response data: { @@ -50,3 +27,26 @@ export class FetchError extends Error { this.data = data ?? { message: message } } } + +export default async function fetchJson( + input: RequestInfo, + init?: RequestInit +): Promise { + const response = await fetch(input, init) + + // if the server replies, there's always some data in json + // if there's a network error, it will throw at the previous line + const data = await response.json() + + // response.ok is true when res.status is 2xx + // https://developer.mozilla.org/en-US/docs/Web/API/Response/ok + if (response.ok) { + return data + } + + throw new FetchError({ + message: response.statusText, + response, + data, + }) +} diff --git a/examples/with-iron-session/pages/api/events.ts b/examples/with-iron-session/pages/api/events.ts index d946088e9277..91ed59a49471 100644 --- a/examples/with-iron-session/pages/api/events.ts +++ b/examples/with-iron-session/pages/api/events.ts @@ -10,8 +10,6 @@ export type Events = const octokit = new Octokit() -export default withIronSessionApiRoute(eventsRoute, sessionOptions) - async function eventsRoute(req: NextApiRequest, res: NextApiResponse) { const user = req.session.user @@ -31,3 +29,5 @@ async function eventsRoute(req: NextApiRequest, res: NextApiResponse) { res.status(200).json([]) } } + +export default withIronSessionApiRoute(eventsRoute, sessionOptions) diff --git a/examples/with-iron-session/pages/api/login.ts b/examples/with-iron-session/pages/api/login.ts index 9fa2e84cf3e1..c9f094ae429d 100644 --- a/examples/with-iron-session/pages/api/login.ts +++ b/examples/with-iron-session/pages/api/login.ts @@ -6,8 +6,6 @@ import { sessionOptions } from 'lib/session' import { NextApiRequest, NextApiResponse } from 'next' const octokit = new Octokit() -export default withIronSessionApiRoute(loginRoute, sessionOptions) - async function loginRoute(req: NextApiRequest, res: NextApiResponse) { const { username } = await req.body @@ -24,3 +22,5 @@ async function loginRoute(req: NextApiRequest, res: NextApiResponse) { res.status(500).json({ message: (error as Error).message }) } } + +export default withIronSessionApiRoute(loginRoute, sessionOptions) diff --git a/examples/with-iron-session/pages/api/logout.ts b/examples/with-iron-session/pages/api/logout.ts index cbbfb6a9ee21..e081136289bc 100644 --- a/examples/with-iron-session/pages/api/logout.ts +++ b/examples/with-iron-session/pages/api/logout.ts @@ -3,9 +3,9 @@ import { sessionOptions } from 'lib/session' import { NextApiRequest, NextApiResponse } from 'next' import type { User } from 'pages/api/user' -export default withIronSessionApiRoute(logoutRoute, sessionOptions) - function logoutRoute(req: NextApiRequest, res: NextApiResponse) { req.session.destroy() res.json({ isLoggedIn: false, login: '', avatarUrl: '' }) } + +export default withIronSessionApiRoute(logoutRoute, sessionOptions) diff --git a/examples/with-iron-session/pages/api/user.ts b/examples/with-iron-session/pages/api/user.ts index 7de3d3b21999..e7249a427e02 100644 --- a/examples/with-iron-session/pages/api/user.ts +++ b/examples/with-iron-session/pages/api/user.ts @@ -8,8 +8,6 @@ export type User = { avatarUrl: string } -export default withIronSessionApiRoute(userRoute, sessionOptions) - async function userRoute(req: NextApiRequest, res: NextApiResponse) { if (req.session.user) { // in a real world application you might read the user id from the session and then do a database request @@ -26,3 +24,5 @@ async function userRoute(req: NextApiRequest, res: NextApiResponse) { }) } } + +export default withIronSessionApiRoute(userRoute, sessionOptions) diff --git a/examples/with-mobx-react-lite/components/StoreProvider.js b/examples/with-mobx-react-lite/components/StoreProvider.js index 9f8cdd1be492..8484b5a3eb91 100644 --- a/examples/with-mobx-react-lite/components/StoreProvider.js +++ b/examples/with-mobx-react-lite/components/StoreProvider.js @@ -13,12 +13,6 @@ export function useStore() { return context } -export function StoreProvider({ children, initialState: initialData }) { - const store = initializeStore(initialData) - - return {children} -} - function initializeStore(initialData = null) { const _store = store ?? new Store() @@ -34,3 +28,9 @@ function initializeStore(initialData = null) { return _store } + +export function StoreProvider({ children, initialState: initialData }) { + const store = initializeStore(initialData) + + return {children} +} diff --git a/examples/with-mobx-state-tree-typescript/components/Clock.tsx b/examples/with-mobx-state-tree-typescript/components/Clock.tsx index ddf2dd6c5187..19ea8d84f668 100644 --- a/examples/with-mobx-state-tree-typescript/components/Clock.tsx +++ b/examples/with-mobx-state-tree-typescript/components/Clock.tsx @@ -1,6 +1,6 @@ +const pad = (n) => (n < 10 ? `0${n}` : n) const format = (t) => `${pad(t.getUTCHours())}:${pad(t.getUTCMinutes())}:${pad(t.getUTCSeconds())}` -const pad = (n) => (n < 10 ? `0${n}` : n) const Clock = (props) => { const divStyle = { diff --git a/examples/with-mongodb-mongoose/components/Form.js b/examples/with-mongodb-mongoose/components/Form.js index 968df84a86ba..03b754e7934c 100644 --- a/examples/with-mongodb-mongoose/components/Form.js +++ b/examples/with-mongodb-mongoose/components/Form.js @@ -83,16 +83,6 @@ const Form = ({ formId, petForm, forNewPet = true }) => { }) } - const handleSubmit = (e) => { - e.preventDefault() - const errs = formValidate() - if (Object.keys(errs).length === 0) { - forNewPet ? postData(form) : putData(form) - } else { - setErrors({ errs }) - } - } - /* Makes sure pet info is filled for pet name, owner name, species, and image url*/ const formValidate = () => { let err = {} @@ -103,6 +93,16 @@ const Form = ({ formId, petForm, forNewPet = true }) => { return err } + const handleSubmit = (e) => { + e.preventDefault() + const errs = formValidate() + if (Object.keys(errs).length === 0) { + forNewPet ? postData(form) : putData(form) + } else { + setErrors({ errs }) + } + } + return ( <>
diff --git a/examples/with-react-native-web/pages/alternate.js b/examples/with-react-native-web/pages/alternate.js index e8fc16ec8b69..1184ce94f2e6 100644 --- a/examples/with-react-native-web/pages/alternate.js +++ b/examples/with-react-native-web/pages/alternate.js @@ -1,19 +1,5 @@ import { StyleSheet, Text, View } from 'react-native' -export default function Alternate() { - return ( - - - Alternate Page - - - - Go Back - - - ) -} - const styles = StyleSheet.create({ container: { alignItems: 'center', @@ -29,3 +15,17 @@ const styles = StyleSheet.create({ color: 'blue', }, }) + +export default function Alternate() { + return ( + + + Alternate Page + + + + Go Back + + + ) +} diff --git a/examples/with-react-native-web/pages/index.js b/examples/with-react-native-web/pages/index.js index cac53ebcd5e7..3fa857406993 100644 --- a/examples/with-react-native-web/pages/index.js +++ b/examples/with-react-native-web/pages/index.js @@ -1,25 +1,5 @@ import { StyleSheet, Text, View } from 'react-native' -export default function App(props) { - return ( - - - React Native for Web & Next.js - - - - A universal link - - - - - Subheader - - - - ) -} - const styles = StyleSheet.create({ container: { alignItems: 'center', @@ -39,3 +19,23 @@ const styles = StyleSheet.create({ marginBottom: 24, }, }) + +export default function App(props) { + return ( + + + React Native for Web & Next.js + + + + A universal link + + + + + Subheader + + + + ) +} diff --git a/examples/with-redis/pages/api/subscribe.ts b/examples/with-redis/pages/api/subscribe.ts index 544558c36df3..6f3bac267bd7 100644 --- a/examples/with-redis/pages/api/subscribe.ts +++ b/examples/with-redis/pages/api/subscribe.ts @@ -2,6 +2,12 @@ import type { NextApiRequest, NextApiResponse } from 'next' import redis from '../../lib/redis' +function validateEmail(email: string) { + const re = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return re.test(String(email).toLowerCase()) +} + export default async function subscribe( req: NextApiRequest, res: NextApiResponse @@ -19,9 +25,3 @@ export default async function subscribe( }) } } - -function validateEmail(email: string) { - const re = - /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ - return re.test(String(email).toLowerCase()) -} diff --git a/examples/with-redux-wrapper/components/Clock.js b/examples/with-redux-wrapper/components/Clock.js index 6b13e9ebb4d6..57523ec17927 100644 --- a/examples/with-redux-wrapper/components/Clock.js +++ b/examples/with-redux-wrapper/components/Clock.js @@ -1,3 +1,7 @@ +const pad = (n) => (n < 10 ? `0${n}` : n) +const format = (t) => + `${pad(t.getUTCHours())}:${pad(t.getUTCMinutes())}:${pad(t.getUTCSeconds())}` + export default function Clock({ lastUpdate, light }) { return (
@@ -18,8 +22,3 @@ export default function Clock({ lastUpdate, light }) {
) } - -const format = (t) => - `${pad(t.getUTCHours())}:${pad(t.getUTCMinutes())}:${pad(t.getUTCSeconds())}` - -const pad = (n) => (n < 10 ? `0${n}` : n) diff --git a/lerna.json b/lerna.json index 3f4eb458a737..6d2f15175425 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "12.2.5-canary.0" + "version": "12.2.5-canary.1" } diff --git a/package.json b/package.json index efb10eda64d7..68e71f55f5dc 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@babel/plugin-proposal-object-rest-spread": "7.14.7", "@babel/preset-flow": "7.14.5", "@babel/preset-react": "7.14.5", - "@edge-runtime/jest-environment": "1.1.0-beta.25", + "@edge-runtime/jest-environment": "1.1.0-beta.26", "@fullhuman/postcss-purgecss": "1.3.0", "@mdx-js/loader": "0.18.0", "@next/bundle-analyzer": "workspace:*", diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 345c23d29fbc..16a734e607b5 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 1b5c9c445c0c..e4db98608445 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "description": "ESLint configuration used by NextJS.", "main": "index.js", "license": "MIT", @@ -9,7 +9,7 @@ "directory": "packages/eslint-config-next" }, "dependencies": { - "@next/eslint-plugin-next": "12.2.5-canary.0", + "@next/eslint-plugin-next": "12.2.5-canary.1", "@rushstack/eslint-patch": "^1.1.3", "@typescript-eslint/parser": "^5.21.0", "eslint-import-resolver-node": "^0.3.6", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 07e46332e89a..3de9527994d2 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "description": "ESLint plugin for NextJS.", "main": "lib/index.js", "license": "MIT", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 4104cce6aab4..bfd7f878c35c 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 74650e06108e..4162021d5f56 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 67df87ca4735..52ed696471e9 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index e7d78add2894..765e1015a618 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index b8c5a238ace1..cdc6074dcc0f 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 4aeeca2c7514..10ba73b237d5 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 921d8a19d6da..65643895c75d 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 112d219bb68d..b26fdf17afc3 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "private": true, "scripts": { "build-native": "napi build --platform -p next-swc-napi --cargo-name next_swc_napi native --features plugin", diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index d27ef9057b1b..af30649b7edc 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -4,7 +4,6 @@ import { extractExportedConstValue, UnsupportedValueError, } from './extract-const-value' -import { escapeStringRegexp } from '../../shared/lib/escape-regexp' import { parseModule } from './parse-module' import { promises as fs } from 'fs' import { tryToParsePath } from '../../lib/try-to-parse-path' @@ -185,6 +184,7 @@ function getMiddlewareRegExpStrings( getMiddlewareRegExpStrings(matcher, nextConfig) ) } + const { i18n } = nextConfig if (typeof matcherOrMatchers !== 'string') { throw new Error( @@ -197,20 +197,23 @@ function getMiddlewareRegExpStrings( if (!matcher.startsWith('/')) { throw new Error('`matcher`: path matcher must start with /') } + const isRoot = matcher === '/' - if (nextConfig.i18n?.locales) { - matcher = `/:nextInternalLocale(${nextConfig.i18n.locales - .map((locale) => escapeStringRegexp(locale)) - .join('|')})${ - matcher === '/' && !nextConfig.trailingSlash ? '' : matcher - }` + if (i18n?.locales) { + matcher = `/:nextInternalLocale([^/.]{1,})${isRoot ? '' : matcher}` } + matcher = `/:nextData(_next/data/[^/]{1,})?${matcher}${ + isRoot + ? `(${nextConfig.i18n ? '|\\.json|' : ''}/?index|/?index\\.json)?` + : '(.json)?' + }` + if (nextConfig.basePath) { - matcher = `${nextConfig.basePath}${matcher === '/' ? '' : matcher}` + matcher = `${nextConfig.basePath}${matcher}` } - const parsedPage = tryToParsePath(matcher) + if (parsedPage.error) { throw new Error(`Invalid path matcher: ${matcher}`) } diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index f224d2d33ea5..2d942a40070f 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -24,13 +24,12 @@ import { fileExists } from '../lib/file-exists' import { findPagesDir } from '../lib/find-pages-dir' import loadCustomRoutes, { CustomRoutes, - getRedirectStatus, - modifyRouteRegex, normalizeRouteRegex, Redirect, Rewrite, RouteType, } from '../lib/load-custom-routes' +import { getRedirectStatus, modifyRouteRegex } from '../lib/redirect-status' import { nonNullable } from '../lib/non-nullable' import { recursiveDelete } from '../lib/recursive-delete' import { verifyAndLint } from '../lib/verifyAndLint' diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts b/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts index 0dd70dbc25c4..90eaa23c39b0 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts @@ -8,7 +8,7 @@ import { renderToHTML } from '../../../../server/render' import { tryGetPreviewData } from '../../../../server/api-utils/node' import { denormalizePagePath } from '../../../../shared/lib/page-path/denormalize-page-path' import { setLazyProp, getCookieParser } from '../../../../server/api-utils' -import { getRedirectStatus } from '../../../../lib/load-custom-routes' +import { getRedirectStatus } from '../../../../lib/redirect-status' import getRouteNoAssetPath from '../../../../shared/lib/router/utils/get-route-from-asset-path' import { PERMANENT_REDIRECT_STATUS } from '../../../../shared/lib/constants' import RenderResult from '../../../../server/render-result' diff --git a/packages/next/build/webpack/plugins/build-manifest-plugin.ts b/packages/next/build/webpack/plugins/build-manifest-plugin.ts index b0802e0e20d7..6f3d2ca34269 100644 --- a/packages/next/build/webpack/plugins/build-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/build-manifest-plugin.ts @@ -1,3 +1,4 @@ +import type { Rewrite, CustomRoutes } from '../../../lib/load-custom-routes' import devalue from 'next/dist/compiled/devalue' import { webpack, sources } from 'next/dist/compiled/webpack/webpack' import { @@ -13,10 +14,8 @@ import { import { BuildManifest } from '../../../server/get-page-files' import getRouteFromEntrypoint from '../../../server/get-route-from-entrypoint' import { ampFirstEntryNamesMap } from './next-drop-client-page-plugin' -import { Rewrite } from '../../../lib/load-custom-routes' import { getSortedRoutes } from '../../../shared/lib/router/utils' import { spans } from './profiling-plugin' -import { CustomRoutes } from '../../../lib/load-custom-routes' type DeepMutable = { -readonly [P in keyof T]: DeepMutable } diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 5c4f482da59b..535584636276 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -468,7 +468,7 @@ async function render(renderingProps: RenderRouteInfo): Promise { // 404 and 500 errors are special kind of errors // and they are still handle via the main render method. function renderError(renderErrorProps: RenderErrorProps): Promise { - const { App, err } = renderErrorProps + let { App, err } = renderErrorProps // In development runtime errors are caught by our overlay // In production we catch runtime errors using componentDidCatch which will trigger renderError @@ -497,10 +497,18 @@ function renderError(renderErrorProps: RenderErrorProps): Promise { .loadPage('/_error') .then(({ page: ErrorComponent, styleSheets }) => { return lastAppProps?.Component === ErrorComponent - ? import('../pages/_error').then((m) => ({ - ErrorComponent: m.default as React.ComponentType<{}>, - styleSheets: [], - })) + ? import('../pages/_error') + .then((errorModule) => { + return import('../pages/_app').then((appModule) => { + App = appModule.default as any as AppComponent + renderErrorProps.App = App + return errorModule + }) + }) + .then((m) => ({ + ErrorComponent: m.default as React.ComponentType<{}>, + styleSheets: [], + })) : { ErrorComponent, styleSheets } }) .then(({ ErrorComponent, styleSheets }) => { diff --git a/packages/next/compiled/@edge-runtime/primitives/package.json b/packages/next/compiled/@edge-runtime/primitives/package.json index 835ca8cc3c16..f313ba0b2bba 100644 --- a/packages/next/compiled/@edge-runtime/primitives/package.json +++ b/packages/next/compiled/@edge-runtime/primitives/package.json @@ -1 +1 @@ -{"name":"@edge-runtime/primitives","version":"1.1.0-beta.25","main":"./index.js","license":"MPLv2"} +{"name":"@edge-runtime/primitives","version":"1.1.0-beta.26","main":"./index.js","license":"MPLv2"} diff --git a/packages/next/compiled/edge-runtime/index.js b/packages/next/compiled/edge-runtime/index.js index a042b3d70eb8..dd1b86049f7b 100644 --- a/packages/next/compiled/edge-runtime/index.js +++ b/packages/next/compiled/edge-runtime/index.js @@ -1 +1 @@ -(()=>{var __webpack_modules__={605:(__unused_webpack_module,exports,__nccwpck_require__)=>{"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EdgeVM=void 0;const buffer_1=__nccwpck_require__(300);const require_1=__nccwpck_require__(274);const vm_1=__nccwpck_require__(386);class EdgeVM extends vm_1.VM{constructor(e={}){super({...e,extend:t=>e.extend?e.extend(addPrimitives(t)):addPrimitives(t)})}}exports.EdgeVM=EdgeVM;function addPrimitives(context){defineProperty(context,"self",{enumerable:true,value:context});defineProperty(context,"globalThis",{value:context});defineProperty(context,"Symbol",{value:Symbol});defineProperty(context,"clearInterval",{value:clearInterval});defineProperty(context,"clearTimeout",{value:clearTimeout});defineProperty(context,"setInterval",{value:setInterval});defineProperty(context,"setTimeout",{value:setTimeout});defineProperties(context,{exports:(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/console"),scopedContext:{console:console}}),nonenumerable:["console"]});const encodings=(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/encoding"),scopedContext:{Buffer:buffer_1.Buffer,global:{ArrayBuffer:ArrayBuffer}}});defineProperties(context,{exports:encodings,nonenumerable:["atob","btoa","TextEncoder","TextDecoder"]});const streams=(0,require_1.requireWithCache)({path:require.resolve("next/dist/compiled/@edge-runtime/primitives/streams"),context:context});defineProperties(context,{exports:streams,nonenumerable:["ReadableStream","ReadableStreamBYOBReader","ReadableStreamDefaultReader","TransformStream","WritableStream","WritableStreamDefaultWriter"]});const abort=(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/abort-controller")});defineProperties(context,{exports:abort,nonenumerable:["AbortController","AbortSignal"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({cache:new Map([["punycode",{exports:__nccwpck_require__(477)}]]),context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/url"),scopedContext:{TextEncoder:encodings.TextEncoder,TextDecoder:encodings.TextDecoder}}),nonenumerable:["URL","URLSearchParams","URLPattern"]});const blob=(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/blob")});defineProperties(context,{exports:blob,nonenumerable:["Blob"]});const webFetch=(0,require_1.requireWithCache)({context:context,cache:new Map([["abort-controller",{exports:abort}],["assert",{exports:__nccwpck_require__(491)}],["buffer",{exports:__nccwpck_require__(300)}],["events",{exports:__nccwpck_require__(361)}],["http",{exports:__nccwpck_require__(685)}],["net",{exports:__nccwpck_require__(808)}],["perf_hooks",{exports:__nccwpck_require__(74)}],["stream",{exports:__nccwpck_require__(781)}],["tls",{exports:__nccwpck_require__(404)}],["util",{exports:__nccwpck_require__(837)}],["zlib",{exports:__nccwpck_require__(796)}],[require.resolve("next/dist/compiled/@edge-runtime/primitives/streams"),{exports:streams}],[require.resolve("next/dist/compiled/@edge-runtime/primitives/blob"),{exports:blob}]]),path:require.resolve("next/dist/compiled/@edge-runtime/primitives/fetch"),scopedContext:{Buffer:buffer_1.Buffer,FinalizationRegistry:function(){return{register:function(){}}},global:{},queueMicrotask:queueMicrotask,setImmediate:setImmediate}});defineProperties(context,{exports:webFetch,nonenumerable:["fetch","File","FormData","Headers","Request","Response"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({cache:new Map([[require.resolve("next/dist/compiled/@edge-runtime/primitives/fetch"),{exports:webFetch}]]),context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/cache"),scopedContext:{global:{}}}),enumerable:["caches"],nonenumerable:["Cache","CacheStorage"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({context:context,cache:new Map([["crypto",{exports:__nccwpck_require__(113)}],["process",{exports:__nccwpck_require__(282)}]]),path:require.resolve("next/dist/compiled/@edge-runtime/primitives/crypto"),scopedContext:{Buffer:buffer_1.Buffer}}),enumerable:["crypto"],nonenumerable:["Crypto","CryptoKey","SubtleCrypto"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/events")}),nonenumerable:["Event","EventTarget","FetchEvent","PromiseRejectionEvent"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/structured-clone")}),nonenumerable:["structuredClone"]});return context}function defineProperty(e,t,r){var n,o,s;Object.defineProperty(e,t,{configurable:(n=r.configurable)!==null&&n!==void 0?n:false,enumerable:(o=r.enumerable)!==null&&o!==void 0?o:false,value:r.value,writable:(s=r.writable)!==null&&s!==void 0?s:true})}function defineProperties(e,t){var r,n;for(const n of(r=t.enumerable)!==null&&r!==void 0?r:[]){if(!t.exports[n]){throw new Error(`Attempt to export a nullable value for "${n}"`)}defineProperty(e,n,{enumerable:true,value:t.exports[n]})}for(const r of(n=t.nonenumerable)!==null&&n!==void 0?n:[]){if(!t.exports[r]){throw new Error(`Attempt to export a nullable value for "${r}"`)}defineProperty(e,r,{value:t.exports[r]})}}},844:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.VM=t.EdgeVM=void 0;var n=r(605);Object.defineProperty(t,"EdgeVM",{enumerable:true,get:function(){return n.EdgeVM}});var o=r(386);Object.defineProperty(t,"VM",{enumerable:true,get:function(){return o.VM}})},274:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.requireWithCache=t.createRequire=t.requireDependencies=void 0;const n=r(147);const o=r(144);const s=r(17);function requireDependencies(e){const{context:t,requireCache:r,dependencies:n}=e;const o=createRequire(t,r);for(const{path:e,mapExports:r}of n){const n=o(e,e);for(const e of Object.keys(r)){t[r[e]]=n[e]}}}t.requireDependencies=requireDependencies;function createRequire(e,t,r,i={}){return function requireFn(a,E){const c=require.resolve(E,{paths:[(0,s.dirname)(a)]});const u=t.get(E)||t.get(c);if(u!==undefined&&u!==null){return u.exports}const _={exports:{},loaded:false,id:c};t.set(c,_);r===null||r===void 0?void 0:r.add(c);const d=(0,o.runInContext)(`(function(module,exports,require,__dirname,__filename,${Object.keys(i).join(",")}) {${(0,n.readFileSync)(c,"utf-8")}\n})`,e);try{d(_,_.exports,requireFn.bind(null,c),(0,s.dirname)(c),c,...Object.values(i))}catch(e){t.delete(c);throw e}_.loaded=true;return _.exports}}t.createRequire=createRequire;function requireWithCache(e){var t;return createRequire(e.context,(t=e.cache)!==null&&t!==void 0?t:new Map,e.references,e.scopedContext).call(null,e.path,e.path)}t.requireWithCache=requireWithCache},370:function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});t.tempFile=void 0;const o=r(277);const s=n(r(17));const i=n(r(147));const a=n(r(37));function tempFile(e){const t=s.default.join(a.default.tmpdir(),o.crypto.randomUUID());i.default.writeFileSync(t,e);return{path:t,remove:()=>i.default.unlinkSync(t)}}t.tempFile=tempFile},386:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.VM=void 0;const n=r(144);const o=r(274);const s=r(370);class VM{constructor(e={}){var t,r,s,i;const a=(0,n.createContext)({},{name:"Edge Runtime",codeGeneration:(t=e.codeGeneration)!==null&&t!==void 0?t:{strings:false,wasm:true}});this.requireCache=(r=e.requireCache)!==null&&r!==void 0?r:new Map;this.context=(i=(s=e.extend)===null||s===void 0?void 0:s.call(e,a))!==null&&i!==void 0?i:a;this.requireFn=(0,o.createRequire)(this.context,this.requireCache)}evaluate(e){return(0,n.runInContext)(e,this.context)}require(e){return this.requireFn(e,e)}requireInContext(e){const t=this.require(e);for(const[e,r]of Object.entries(t)){this.context[e]=r}}requireInlineInContext(e){const t=(0,s.tempFile)(e);this.requireInContext(t.path);t.remove()}}t.VM=VM},734:e=>{"use strict";e.exports=e=>{const t=e[0]*1e9+e[1];const r=t/1e6;const n=t/1e9;return{seconds:n,milliseconds:r,nanoseconds:t}}},663:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.EdgeRuntime=void 0;const n=r(844);let o;let s;class EdgeRuntime extends n.EdgeVM{constructor(e){super({...e,extend:t=>{var r,n;return(n=(r=e===null||e===void 0?void 0:e.extend)===null||r===void 0?void 0:r.call(e,t))!==null&&n!==void 0?n:t}});defineHandlerProps({object:this,setterName:"__onUnhandledRejectionHandler",setter:e=>o=e,getterName:"__rejectionHandlers",getter:()=>o});defineHandlerProps({object:this,setterName:"__onErrorHandler",setter:e=>s=e,getterName:"__errorHandlers",getter:()=>s});Object.defineProperty(this.context,"EdgeRuntime",{configurable:false,enumerable:false,writable:false,value:"edge-runtime"});this.evaluate(getDefineEventListenersCode());this.dispatchFetch=this.evaluate(getDispatchFetchCode());if(e===null||e===void 0?void 0:e.initialCode){this.evaluate(e.initialCode)}}}t.EdgeRuntime=EdgeRuntime;process.on("unhandledRejection",(function invokeRejectionHandlers(e,t){o===null||o===void 0?void 0:o.forEach((r=>r(e,t)))}));process.on("uncaughtException",(function invokeErrorHandlers(e){s===null||s===void 0?void 0:s.forEach((t=>t(e)))}));function getDefineEventListenersCode(){return`\n Object.defineProperty(self, '__listeners', {\n configurable: false,\n enumerable: false,\n value: {},\n writable: true,\n })\n\n function __conditionallyUpdatesHandlerList(eventType) {\n if (eventType === 'unhandledrejection') {\n self.__onUnhandledRejectionHandler = self.__listeners[eventType];\n } else if (eventType === 'error') {\n self.__onErrorHandler = self.__listeners[eventType];\n }\n }\n\n function addEventListener(type, handler) {\n const eventType = type.toLowerCase();\n if (eventType === 'fetch' && self.__listeners.fetch) {\n throw new TypeError('You can register just one "fetch" event listener');\n }\n\n self.__listeners[eventType] = self.__listeners[eventType] || [];\n self.__listeners[eventType].push(handler);\n __conditionallyUpdatesHandlerList(eventType);\n }\n\n function removeEventListener(type, handler) {\n const eventType = type.toLowerCase();\n if (self.__listeners[eventType]) {\n self.__listeners[eventType] = self.__listeners[eventType].filter(item => {\n return item !== handler;\n });\n\n if (self.__listeners[eventType].length === 0) {\n delete self.__listeners[eventType];\n }\n }\n __conditionallyUpdatesHandlerList(eventType);\n }\n `}function getDispatchFetchCode(){return`(async function dispatchFetch(input, init) {\n const request = new Request(input, init);\n const event = new FetchEvent(request);\n if (!self.__listeners.fetch) {\n throw new Error("No fetch event listeners found");\n }\n\n const getResponse = ({ response, error }) => {\n if (error || !response || !(response instanceof Response)) {\n console.error(error ? error : 'The event listener did not respond')\n response = new Response(null, {\n statusText: 'Internal Server Error',\n status: 500\n })\n }\n\n response.waitUntil = () => Promise.all(event.awaiting);\n return response;\n }\n\n try {\n await self.__listeners.fetch[0].call(event, event)\n } catch (error) {\n return getResponse({ error })\n }\n\n return Promise.resolve(event.response)\n .then(response => getResponse({ response }))\n .catch(error => getResponse({ error }))\n })`}function defineHandlerProps({object:e,setterName:t,setter:r,getterName:n,getter:o}){Object.defineProperty(e.context,t,{set:r,configurable:false,enumerable:false});Object.defineProperty(e,n,{get:o,configurable:false,enumerable:false})}},353:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.getClonableBodyStream=void 0;const n=r(781);function getClonableBodyStream(e,t){let r=null;return{finalize(){if(r){replaceRequestBody(e,bodyStreamToNodeStream(r))}},cloneBodyStream(){const n=r!==null&&r!==void 0?r:requestToBodyStream(e,t);const[o,s]=n.tee();r=o;return s}}}t.getClonableBodyStream=getClonableBodyStream;function requestToBodyStream(e,t){const r=new t({start(t){e.on("data",(e=>t.enqueue(e)));e.on("end",(()=>t.terminate()));e.on("error",(e=>t.error(e)))}});return r.readable}function bodyStreamToNodeStream(e){const t=e.getReader();return n.Readable.from(async function*(){while(true){const{done:e,value:r}=await t.read();if(e){return}yield r}}())}function replaceRequestBody(e,t){for(const r in t){let n=t[r];if(typeof n==="function"){n=n.bind(t)}e[r]=n}return e}},564:function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});t.createHandler=void 0;const o=r(353);const s=n(r(720));const i=n(r(242));const a=n(r(504));function createHandler(e){const t=new Set;return{handler:async(r,n)=>{var E,c;const u=(0,a.default)();const _=r.method!=="GET"&&r.method!=="HEAD"?(0,o.getClonableBodyStream)(r,e.runtime.context.TransformStream):undefined;const d=await e.runtime.dispatchFetch(String(getURL(r)),{headers:toRequestInitHeaders(r),method:r.method,body:_===null||_===void 0?void 0:_.cloneBodyStream()});const l=d.waitUntil();t.add(l);l.finally((()=>t.delete(l)));n.statusCode=d.status;n.statusMessage=d.statusText;for(const[e,t]of Object.entries(toNodeHeaders(d.headers))){if(e!=="content-encoding"&&t!==undefined){n.setHeader(e,t)}}if(d.body){for await(const e of d.body){n.write(e)}}const S=`${r.socket.remoteAddress} ${r.method} ${r.url}`;const R=`${(E=(0,s.default)(u()).match(/[a-zA-Z]+|[0-9]+/g))===null||E===void 0?void 0:E.join(" ")}`;const h=`${n.statusCode} ${i.default[n.statusCode]}`;(c=e.logger)===null||c===void 0?void 0:c.debug(`${S} โ†’ ${h} in ${R}`);n.end()},waitUntil:()=>Promise.all(t)}}t.createHandler=createHandler;function getURL(e){var t;const r=((t=e.socket)===null||t===void 0?void 0:t.encrypted)?"https":"http";return new URL(String(e.url),`${r}://${String(e.headers.host)}`)}function toRequestInitHeaders(e){return Object.keys(e.headers).map((t=>{const r=e.headers[t];return[t,Array.isArray(r)?r.join(", "):r!==null&&r!==void 0?r:""]}))}function toNodeHeaders(e){const t={};if(e){for(const[r,n]of e.entries()){t[r]=r.toLowerCase()==="set-cookie"?e.getAll("set-cookie"):n}}return t}},179:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.runServer=t.createHandler=void 0;var n=r(564);Object.defineProperty(t,"createHandler",{enumerable:true,get:function(){return n.createHandler}});var o=r(82);Object.defineProperty(t,"runServer",{enumerable:true,get:function(){return o.runServer}})},82:function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});t.runServer=void 0;const o=r(564);const s=r(361);const i=n(r(685));async function runServer(e){const{handler:t,waitUntil:r}=(0,o.createHandler)(e);const n=i.default.createServer(t);n.listen(e.port);try{await(0,s.once)(n,"listening")}catch(t){if((t===null||t===void 0?void 0:t.code)==="EADDRINUSE"){return runServer({...e,port:undefined})}throw t}const a=n.address();const E=typeof a==="string"||a==null?String(a):`http://localhost:${a.port}`;return{url:E,close:async()=>{await r();await new Promise(((e,t)=>n.close((r=>{if(r)t(r);e()}))))},waitUntil:r}}t.runServer=runServer},242:e=>{var t;t={"1xx":"Informational","1xx_NAME":"INFORMATIONAL","1xx_MESSAGE":"Indicates an interim response for communicating connection status or request progress prior to completing the requested action and sending a final response.",INFORMATIONAL:"1xx","2xx":"Successful","2xx_NAME":"SUCCESSFUL","2xx_MESSAGE":"Indicates that the client's request was successfully received, understood, and accepted.",SUCCESSFUL:"2xx","3xx":"Redirection","3xx_NAME":"REDIRECTION","3xx_MESSAGE":"Indicates that further action needs to be taken by the user agent in order to fulfill the request.",REDIRECTION:"3xx","4xx":"Client Error","4xx_NAME":"CLIENT_ERROR","4xx_MESSAGE":"Indicates that the client seems to have erred.",CLIENT_ERROR:"4xx","5xx":"Server Error","5xx_NAME":"SERVER_ERROR","5xx_MESSAGE":"Indicates that the server is aware that it has erred or is incapable of performing the requested method.",SERVER_ERROR:"5xx"};e.exports={classes:t,100:"Continue","100_NAME":"CONTINUE","100_MESSAGE":"The server has received the request headers and the client should proceed to send the request body.","100_CLASS":t.INFORMATIONAL,CONTINUE:100,101:"Switching Protocols","101_NAME":"SWITCHING_PROTOCOLS","101_MESSAGE":"The requester has asked the server to switch protocols and the server has agreed to do so.","101_CLASS":t.INFORMATIONAL,SWITCHING_PROTOCOLS:101,102:"Processing","102_NAME":"PROCESSING","102_MESSAGE":"A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request. This code indicates that the server has received and is processing the request, but no response is available yet.[7] This prevents the client from timing out and assuming the request was lost.","102_CLASS":t.INFORMATIONAL,PROCESSING:102,103:"Early Hints","103_NAME":"EARLY_HINTS","103_MESSAGE":"Used to return some response headers before final HTTP message.","103_CLASS":t.INFORMATIONAL,EARLY_HINTS:103,200:"OK","200_NAME":"OK","200_MESSAGE":"Standard response for successful HTTP requests.","200_CLASS":t.SUCCESSFUL,OK:200,201:"Created","201_NAME":"CREATED","201_MESSAGE":"The request has been fulfilled, resulting in the creation of a new resource.","201_CLASS":t.SUCCESSFUL,CREATED:201,202:"Accepted","202_NAME":"ACCEPTED","202_MESSAGE":"The request has been accepted for processing, but the processing has not been completed.","202_CLASS":t.SUCCESSFUL,ACCEPTED:202,203:"Non-Authoritative Information","203_NAME":"NON_AUTHORITATIVE_INFORMATION","203_MESSAGE":"The server is a transforming proxy (e.g. a Web accelerator) that received a 200 OK from its origin, but is returning a modified version of the origin's response.","203_CLASS":t.SUCCESSFUL,NON_AUTHORITATIVE_INFORMATION:203,204:"No Content","204_NAME":"NO_CONTENT","204_MESSAGE":"The server successfully processed the request and is not returning any content.","204_CLASS":t.SUCCESSFUL,NO_CONTENT:204,205:"Reset Content","205_NAME":"RESET_CONTENT","205_MESSAGE":"The server successfully processed the request, but is not returning any content. Unlike a 204 response, this response requires that the requester reset the document view.","205_CLASS":t.SUCCESSFUL,RESET_CONTENT:205,206:"Partial Content","206_NAME":"PARTIAL_CONTENT","206_MESSAGE":"The server is delivering only part of the resource (byte serving) due to a range header sent by the client.","206_CLASS":t.SUCCESSFUL,PARTIAL_CONTENT:206,207:"Multi Status","207_NAME":"MULTI_STATUS","207_MESSAGE":"The message body that follows is by default an XML message and can contain a number of separate response codes, depending on how many sub-requests were made.","207_CLASS":t.SUCCESSFUL,MULTI_STATUS:207,208:"Already Reported","208_NAME":"ALREADY_REPORTED","208_MESSAGE":"The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response, and are not being included again.","208_CLASS":t.SUCCESSFUL,ALREADY_REPORTED:208,226:"IM Used","226_NAME":"IM_USED","226_MESSAGE":"The server has fulfilled a request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance.","226_CLASS":t.SUCCESSFUL,IM_USED:226,300:"Multiple Choices","300_NAME":"MULTIPLE_CHOICES","300_MESSAGE":"Indicates multiple options for the resource from which the client may choose.","300_CLASS":t.REDIRECTION,MULTIPLE_CHOICES:300,301:"Moved Permanently","301_NAME":"MOVED_PERMANENTLY","301_MESSAGE":"This and all future requests should be directed to the given URI.","301_CLASS":t.REDIRECTION,MOVED_PERMANENTLY:301,302:"Found","302_NAME":"FOUND","302_MESSAGE":'This is an example of industry practice contradicting the standard. The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 to distinguish between the two behaviours.',"302_CLASS":t.REDIRECTION,FOUND:302,303:"See Other","303_NAME":"SEE_OTHER","303_MESSAGE":"The response to the request can be found under another URI using the GET method.","303_CLASS":t.REDIRECTION,SEE_OTHER:303,304:"Not Modified","304_NAME":"NOT_MODIFIED","304_MESSAGE":"Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match.","304_CLASS":t.REDIRECTION,NOT_MODIFIED:304,305:"Use Proxy","305_NAME":"USE_PROXY","305_MESSAGE":"The requested resource is available only through a proxy, the address for which is provided in the response.","305_CLASS":t.REDIRECTION,USE_PROXY:305,306:"Switch Proxy","306_NAME":"SWITCH_PROXY","306_MESSAGE":'No longer used. Originally meant "Subsequent requests should use the specified proxy.',"306_CLASS":t.REDIRECTION,SWITCH_PROXY:306,307:"Temporary Redirect","307_NAME":"TEMPORARY_REDIRECT","307_MESSAGE":"In this case, the request should be repeated with another URI; however, future requests should still use the original URI.","307_CLASS":t.REDIRECTION,TEMPORARY_REDIRECT:307,308:"Permanent Redirect","308_NAME":"PERMANENT_REDIRECT","308_MESSAGE":"The request and all future requests should be repeated using another URI.","308_CLASS":t.REDIRECTION,PERMANENT_REDIRECT:308,400:"Bad Request","400_NAME":"BAD_REQUEST","400_MESSAGE":"The server cannot or will not process the request due to an apparent client error.","400_CLASS":t.CLIENT_ERROR,BAD_REQUEST:400,401:"Unauthorized","401_NAME":"UNAUTHORIZED","401_MESSAGE":"Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided.","401_CLASS":t.CLIENT_ERROR,UNAUTHORIZED:401,402:"Payment Required","402_NAME":"PAYMENT_REQUIRED","402_MESSAGE":"Reserved for future use. The original intention was that this code might be used as part of some form of digital cash or micropayment scheme, as proposed for example by GNU Taler, but that has not yet happened, and this code is not usually used.","402_CLASS":t.CLIENT_ERROR,PAYMENT_REQUIRED:402,403:"Forbidden","403_NAME":"FORBIDDEN","403_MESSAGE":"The request was valid, but the server is refusing action.","403_CLASS":t.CLIENT_ERROR,FORBIDDEN:403,404:"Not Found","404_NAME":"NOT_FOUND","404_MESSAGE":"The requested resource could not be found but may be available in the future. Subsequent requests by the client are permissible.","404_CLASS":t.CLIENT_ERROR,NOT_FOUND:404,405:"Method Not Allowed","405_NAME":"METHOD_NOT_ALLOWED","405_MESSAGE":"A request method is not supported for the requested resource.","405_CLASS":t.CLIENT_ERROR,METHOD_NOT_ALLOWED:405,406:"Not Acceptable","406_NAME":"NOT_ACCEPTABLE","406_MESSAGE":"The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request.","406_CLASS":t.CLIENT_ERROR,NOT_ACCEPTABLE:406,407:"Proxy Authentication Required","407_NAME":"PROXY_AUTHENTICATION_REQUIRED","407_MESSAGE":"The client must first authenticate itself with the proxy.","407_CLASS":t.CLIENT_ERROR,PROXY_AUTHENTICATION_REQUIRED:407,408:"Request Time-out","408_NAME":"REQUEST_TIMEOUT","408_MESSAGE":"The server timed out waiting for the request.","408_CLASS":t.CLIENT_ERROR,REQUEST_TIMEOUT:408,409:"Conflict","409_NAME":"CONFLICT","409_MESSAGE":"Indicates that the request could not be processed because of conflict in the request, such as an edit conflict between multiple simultaneous updates.","409_CLASS":t.CLIENT_ERROR,CONFLICT:409,410:"Gone","410_NAME":"GONE","410_MESSAGE":"Indicates that the resource requested is no longer available and will not be available again.","410_CLASS":t.CLIENT_ERROR,GONE:410,411:"Length Required","411_NAME":"LENGTH_REQUIRED","411_MESSAGE":"The request did not specify the length of its content, which is required by the requested resource.","411_CLASS":t.CLIENT_ERROR,LENGTH_REQUIRED:411,412:"Precondition Failed","412_NAME":"PRECONDITION_FAILED","412_MESSAGE":"The server does not meet one of the preconditions that the requester put on the request.","412_CLASS":t.CLIENT_ERROR,PRECONDITION_FAILED:412,413:"Request Entity Too Large","413_NAME":"REQUEST_ENTITY_TOO_LARGE","413_MESSAGE":'The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large".',"413_CLASS":t.CLIENT_ERROR,REQUEST_ENTITY_TOO_LARGE:413,414:"Request-URI Too Large","414_NAME":"REQUEST_URI_TOO_LONG","414_MESSAGE":"The URI provided was too long for the server to process.","414_CLASS":t.CLIENT_ERROR,REQUEST_URI_TOO_LONG:414,415:"Unsupported Media Type","415_NAME":"UNSUPPORTED_MEDIA_TYPE","415_MESSAGE":"The request entity has a media type which the server or resource does not support.","415_CLASS":t.CLIENT_ERROR,UNSUPPORTED_MEDIA_TYPE:415,416:"Requested Range not Satisfiable","416_NAME":"REQUESTED_RANGE_NOT_SATISFIABLE","416_MESSAGE":"The client has asked for a portion of the file (byte serving), but the server cannot supply that portion.","416_CLASS":t.CLIENT_ERROR,REQUESTED_RANGE_NOT_SATISFIABLE:416,417:"Expectation Failed","417_NAME":"EXPECTATION_FAILED","417_MESSAGE":"The server cannot meet the requirements of the Expect request-header field.","417_CLASS":t.CLIENT_ERROR,EXPECTATION_FAILED:417,418:"I'm a teapot","418_NAME":"IM_A_TEAPOT","418_MESSAGE":'Any attempt to brew coffee with a teapot should result in the error code "418 I\'m a teapot". The resulting entity body MAY be short and stout.',"418_CLASS":t.CLIENT_ERROR,IM_A_TEAPOT:418,421:"Misdirected Request","421_NAME":"MISDIRECTED_REQUEST","421_MESSAGE":"The request was directed at a server that is not able to produce a response.","421_CLASS":t.CLIENT_ERROR,MISDIRECTED_REQUEST:421,422:"Unprocessable Entity","422_NAME":"UNPROCESSABLE_ENTITY","422_MESSAGE":"The request was well-formed but was unable to be followed due to semantic errors.","422_CLASS":t.CLIENT_ERROR,UNPROCESSABLE_ENTITY:422,423:"Locked","423_NAME":"LOCKED","423_MESSAGE":"The resource that is being accessed is locked.","423_CLASS":t.CLIENT_ERROR,LOCKED:423,424:"Failed Dependency","424_NAME":"FAILED_DEPENDENCY","424_MESSAGE":"The request failed because it depended on another request and that request failed.","424_CLASS":t.CLIENT_ERROR,FAILED_DEPENDENCY:424,426:"Upgrade Required","426_NAME":"UPGRADE_REQUIRED","426_MESSAGE":"The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field.","426_CLASS":t.CLIENT_ERROR,UPGRADE_REQUIRED:426,428:"Precondition Required","428_NAME":"PRECONDITION_REQUIRED","428_MESSAGE":"The origin server requires the request to be conditional.","428_CLASS":t.CLIENT_ERROR,PRECONDITION_REQUIRED:428,429:"Too Many Requests","429_NAME":"TOO_MANY_REQUESTS","429_MESSAGE":"The user has sent too many requests in a given amount of time.","429_CLASS":t.CLIENT_ERROR,TOO_MANY_REQUESTS:429,431:"Request Header Fields Too Large","431_NAME":"REQUEST_HEADER_FIELDS_TOO_LARGE","431_MESSAGE":"The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large.","431_CLASS":t.CLIENT_ERROR,REQUEST_HEADER_FIELDS_TOO_LARGE:431,451:"Unavailable For Legal Reasons","451_NAME":"UNAVAILABLE_FOR_LEGAL_REASONS","451_MESSAGE":"A server operator has received a legal demand to deny access to a resource or to a set of resources that includes the requested resource.","451_CLASS":t.CLIENT_ERROR,UNAVAILABLE_FOR_LEGAL_REASONS:451,500:"Internal Server Error","500_NAME":"INTERNAL_SERVER_ERROR","500_MESSAGE":"A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.","500_CLASS":t.SERVER_ERROR,INTERNAL_SERVER_ERROR:500,501:"Not Implemented","501_NAME":"NOT_IMPLEMENTED","501_MESSAGE":"The server either does not recognize the request method, or it lacks the ability to fulfil the request. Usually this implies future availability.","501_CLASS":t.SERVER_ERROR,NOT_IMPLEMENTED:501,502:"Bad Gateway","502_NAME":"BAD_GATEWAY","502_MESSAGE":"The server was acting as a gateway or proxy and received an invalid response from the upstream server.","502_CLASS":t.SERVER_ERROR,BAD_GATEWAY:502,503:"Service Unavailable","503_NAME":"SERVICE_UNAVAILABLE","503_MESSAGE":"The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state.","503_CLASS":t.SERVER_ERROR,SERVICE_UNAVAILABLE:503,504:"Gateway Time-out","504_NAME":"GATEWAY_TIMEOUT","504_MESSAGE":"The server was acting as a gateway or proxy and did not receive a timely response from the upstream server.","504_CLASS":t.SERVER_ERROR,GATEWAY_TIMEOUT:504,505:"HTTP Version not Supported","505_NAME":"HTTP_VERSION_NOT_SUPPORTED","505_MESSAGE":"The server does not support the HTTP protocol version used in the request.","505_CLASS":t.SERVER_ERROR,HTTP_VERSION_NOT_SUPPORTED:505,506:"Variant Also Negotiates","506_NAME":"VARIANT_ALSO_NEGOTIATES","506_MESSAGE":"Transparent content negotiation for the request results in a circular reference.","506_CLASS":t.SERVER_ERROR,VARIANT_ALSO_NEGOTIATES:506,507:"Insufficient Storage","507_NAME":"INSUFFICIENT_STORAGE","507_MESSAGE":"The server is unable to store the representation needed to complete the request.","507_CLASS":t.SERVER_ERROR,INSUFFICIENT_STORAGE:507,508:"Loop Detected","508_NAME":"LOOP_DETECTED","508_MESSAGE":"The server detected an infinite loop while processing the request.","508_CLASS":t.SERVER_ERROR,LOOP_DETECTED:508,510:"Not Extended","510_NAME":"NOT_EXTENDED","510_MESSAGE":"Further extensions to the request are required for the server to fulfil it.","510_CLASS":t.SERVER_ERROR,NOT_EXTENDED:510,511:"Network Authentication Required","511_NAME":"NETWORK_AUTHENTICATION_REQUIRED","511_MESSAGE":"The client needs to authenticate to gain network access. Intended for use by intercepting proxies used to control access to the network.","511_CLASS":t.SERVER_ERROR,NETWORK_AUTHENTICATION_REQUIRED:511,extra:{unofficial:{103:"Checkpoint","103_NAME":"CHECKPOINT","103_MESSAGE":"Used in the resumable requests proposal to resume aborted PUT or POST requests.","103_CLASS":t.INFORMATIONAL,CHECKPOINT:103,419:"Page Expired","419_NAME":"PAGE_EXPIRED","419_MESSAGE":"Used by the Laravel Framework when a CSRF Token is missing or expired.","419_CLASS":t.CLIENT_ERROR,PAGE_EXPIRED:419,218:"This is fine","218_NAME":"THIS_IS_FINE","218_MESSAGE":"Used as a catch-all error condition for allowing response bodies to flow through Apache when ProxyErrorOverride is enabled. When ProxyErrorOverride is enabled in Apache, response bodies that contain a status code of 4xx or 5xx are automatically discarded by Apache in favor of a generic response or a custom response specified by the ErrorDocument directive.","218_CLASS":t.SUCCESSFUL,THIS_IS_FINE:218,420:"Enhance Your Calm","420_NAME":"ENHANCE_YOUR_CALM","420_MESSAGE":"Returned by version 1 of the Twitter Search and Trends API when the client is being rate limited; versions 1.1 and later use the 429 Too Many Requests response code instead.","420_CLASS":t.CLIENT_ERROR,ENHANCE_YOUR_CALM:420,450:"Blocked by Windows Parental Controls","450_NAME":"BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS","450_MESSAGE":"The Microsoft extension code indicated when Windows Parental Controls are turned on and are blocking access to the requested webpage.","450_CLASS":t.CLIENT_ERROR,BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS:450,498:"Invalid Token","498_NAME":"INVALID_TOKEN","498_MESSAGE":"Returned by ArcGIS for Server. Code 498 indicates an expired or otherwise invalid token.","498_CLASS":t.CLIENT_ERROR,INVALID_TOKEN:498,499:"Token Required","499_NAME":"TOKEN_REQUIRED","499_MESSAGE":"Returned by ArcGIS for Server. Code 499 indicates that a token is required but was not submitted.","499_CLASS":t.CLIENT_ERROR,TOKEN_REQUIRED:499,509:"Bandwidth Limit Exceeded","509_NAME":"BANDWIDTH_LIMIT_EXCEEDED","509_MESSAGE":"The server has exceeded the bandwidth specified by the server administrator.","509_CLASS":t.SERVER_ERROR,BANDWIDTH_LIMIT_EXCEEDED:509,530:"Site is frozen","530_NAME":"SITE_IS_FROZEN","530_MESSAGE":"Used by the Pantheon web platform to indicate a site that has been frozen due to inactivity.","530_CLASS":t.SERVER_ERROR,SITE_IS_FROZEN:530,598:"Network read timeout error","598_NAME":"NETWORK_READ_TIMEOUT_ERROR","598_MESSAGE":"Used by some HTTP proxies to signal a network read timeout behind the proxy to a client in front of the proxy.","598_CLASS":t.SERVER_ERROR,NETWORK_READ_TIMEOUT_ERROR:598},iis:{440:"Login Time-out","440_NAME":"LOGIN_TIME_OUT","440_MESSAGE":"The client's session has expired and must log in again.","440_CLASS":t.CLIENT_ERROR,LOGIN_TIME_OUT:440,449:"Retry With","449_NAME":"RETRY_WITH","449_MESSAGE":"The server cannot honour the request because the user has not provided the required information.","449_CLASS":t.CLIENT_ERROR,RETRY_WITH:449,451:"Redirect","451_NAME":"REDIRECT","451_MESSAGE":"Used in Exchange ActiveSync when either a more efficient server is available or the server cannot access the users' mailbox.","451_CLASS":t.CLIENT_ERROR,REDIRECT:451},nginx:{444:"No Response","444_NAME":"NO_RESPONSE","444_MESSAGE":"Used internally to instruct the server to return no information to the client and close the connection immediately.","444_CLASS":t.CLIENT_ERROR,NO_RESPONSE:444,494:"Request header too large","494_NAME":"REQUEST_HEADER_TOO_LARGE","494_MESSAGE":"Client sent too large request or too long header line.","494_CLASS":t.CLIENT_ERROR,REQUEST_HEADER_TOO_LARGE:494,495:"SSL Certificate Error","495_NAME":"SSL_CERTIFICATE_ERROR","495_MESSAGE":"An expansion of the 400 Bad Request response code, used when the client has provided an invalid client certificate.","495_CLASS":t.CLIENT_ERROR,SSL_CERTIFICATE_ERROR:495,496:"SSL Certificate Required","496_NAME":"SSL_CERTIFICATE_REQUIRED","496_MESSAGE":"An expansion of the 400 Bad Request response code, used when a client certificate is required but not provided.","496_CLASS":t.CLIENT_ERROR,SSL_CERTIFICATE_REQUIRED:496,497:"HTTP Request Sent to HTTPS Port","497_NAME":"HTTP_REQUEST_SENT_TO_HTTPS_PORT","497_MESSAGE":"An expansion of the 400 Bad Request response code, used when the client has made a HTTP request to a port listening for HTTPS requests.","497_CLASS":t.CLIENT_ERROR,HTTP_REQUEST_SENT_TO_HTTPS_PORT:497,499:"Client Closed Request","499_NAME":"CLIENT_CLOSED_REQUEST","499_MESSAGE":"Used when the client has closed the request before the server could send a response.","499_CLASS":t.CLIENT_ERROR,CLIENT_CLOSED_REQUEST:499},cloudflare:{520:"Unknown Error","520_NAME":"UNKNOWN_ERROR","520_MESSAGE":'The 520 error is used as a "catch-all response for when the origin server returns something unexpected", listing connection resets, large headers, and empty or invalid responses as common triggers.',"520_CLASS":t.SERVER_ERROR,UNKNOWN_ERROR:520,521:"Web Server Is Down","521_NAME":"WEB_SERVER_IS_DOWN","521_MESSAGE":"The origin server has refused the connection from Cloudflare.","521_CLASS":t.SERVER_ERROR,WEB_SERVER_IS_DOWN:521,522:"Connection Timed Out","522_NAME":"CONNECTION_TIMED_OUT","522_MESSAGE":"Cloudflare could not negotiate a TCP handshake with the origin server.","522_CLASS":t.SERVER_ERROR,CONNECTION_TIMED_OUT:522,523:"Origin Is Unreachable","523_NAME":"ORIGIN_IS_UNREACHABLE","523_MESSAGE":"Cloudflare could not reach the origin server.","523_CLASS":t.SERVER_ERROR,ORIGIN_IS_UNREACHABLE:523,524:"A Timeout Occurred","524_NAME":"A_TIMEOUT_OCCURRED","524_MESSAGE":"Cloudflare was able to complete a TCP connection to the origin server, but did not receive a timely HTTP response.","524_CLASS":t.SERVER_ERROR,A_TIMEOUT_OCCURRED:524,525:"SSL Handshake Failed","525_NAME":"SSL_HANDSHAKE_FAILED","525_MESSAGE":"Cloudflare could not negotiate a SSL/TLS handshake with the origin server.","525_CLASS":t.SERVER_ERROR,SSL_HANDSHAKE_FAILED:525,526:"Invalid SSL Certificate","526_NAME":"INVALID_SSL_CERTIFICATE","526_MESSAGE":"Cloudflare could not validate the SSL/TLS certificate that the origin server presented.","526_CLASS":t.SERVER_ERROR,INVALID_SSL_CERTIFICATE:526,527:"Railgun Error","527_NAME":"RAILGUN_ERROR","527_MESSAGE":"Error 527 indicates that the request timed out or failed after the WAN connection had been established.","527_CLASS":t.SERVER_ERROR,RAILGUN_ERROR:527}}}},672:e=>{"use strict";e.exports=e=>{if(typeof e!=="number"){throw new TypeError("Expected a number")}const t=e>0?Math.floor:Math.ceil;return{days:t(e/864e5),hours:t(e/36e5)%24,minutes:t(e/6e4)%60,seconds:t(e/1e3)%60,milliseconds:t(e)%1e3,microseconds:t(e*1e3)%1e3,nanoseconds:t(e*1e6)%1e3}}},720:(e,t,r)=>{"use strict";const n=r(672);const pluralize=(e,t)=>t===1?e:`${e}s`;const o=1e-7;e.exports=(e,t={})=>{if(!Number.isFinite(e)){throw new TypeError("Expected a finite number")}if(t.colonNotation){t.compact=false;t.formatSubMilliseconds=false;t.separateMilliseconds=false;t.verbose=false}if(t.compact){t.secondsDecimalDigits=0;t.millisecondsDecimalDigits=0}const r=[];const floorDecimals=(e,t)=>{const r=Math.floor(e*10**t+o);const n=Math.round(r)/10**t;return n.toFixed(t)};const add=(e,n,o,s)=>{if((r.length===0||!t.colonNotation)&&e===0&&!(t.colonNotation&&o==="m")){return}s=(s||e||"0").toString();let i;let a;if(t.colonNotation){i=r.length>0?":":"";a="";const e=s.includes(".")?s.split(".")[0].length:s.length;const t=r.length>0?2:1;s="0".repeat(Math.max(0,t-e))+s}else{i="";a=t.verbose?" "+pluralize(n,e):o}r.push(i+s+a)};const s=n(e);add(Math.trunc(s.days/365),"year","y");add(s.days%365,"day","d");add(s.hours,"hour","h");add(s.minutes,"minute","m");if(t.separateMilliseconds||t.formatSubMilliseconds||!t.colonNotation&&e<1e3){add(s.seconds,"second","s");if(t.formatSubMilliseconds){add(s.milliseconds,"millisecond","ms");add(s.microseconds,"microsecond","ยตs");add(s.nanoseconds,"nanosecond","ns")}else{const e=s.milliseconds+s.microseconds/1e3+s.nanoseconds/1e6;const r=typeof t.millisecondsDecimalDigits==="number"?t.millisecondsDecimalDigits:0;const n=e>=1?Math.round(e):Math.ceil(e);const o=r?e.toFixed(r):n;add(Number.parseFloat(o,10),"millisecond","ms",o)}}else{const r=e/1e3%60;const n=typeof t.secondsDecimalDigits==="number"?t.secondsDecimalDigits:1;const o=floorDecimals(r,n);const s=t.keepDecimalsOnWholeSeconds?o:o.replace(/\.0+$/,"");add(Number.parseFloat(s,10),"second","s",s)}if(r.length===0){return"0"+(t.verbose?" milliseconds":"ms")}if(t.compact){return r[0]}if(typeof t.unitCount==="number"){const e=t.colonNotation?"":" ";return r.slice(0,Math.max(t.unitCount,1)).join(e)}return t.colonNotation?r.join(""):r.join(" ")}},504:(e,t,r)=>{"use strict";const n=r(734);e.exports=()=>{const e=process.hrtime();const end=t=>n(process.hrtime(e))[t];const returnValue=()=>end("milliseconds");returnValue.rounded=()=>Math.round(end("milliseconds"));returnValue.seconds=()=>end("seconds");returnValue.nanoseconds=()=>end("nanoseconds");return returnValue}},491:e=>{"use strict";e.exports=require("assert")},300:e=>{"use strict";e.exports=require("buffer")},113:e=>{"use strict";e.exports=require("crypto")},361:e=>{"use strict";e.exports=require("events")},147:e=>{"use strict";e.exports=require("fs")},685:e=>{"use strict";e.exports=require("http")},808:e=>{"use strict";e.exports=require("net")},277:e=>{"use strict";e.exports=require("next/dist/compiled/@edge-runtime/primitives/crypto")},37:e=>{"use strict";e.exports=require("os")},17:e=>{"use strict";e.exports=require("path")},74:e=>{"use strict";e.exports=require("perf_hooks")},282:e=>{"use strict";e.exports=require("process")},477:e=>{"use strict";e.exports=require("punycode")},781:e=>{"use strict";e.exports=require("stream")},404:e=>{"use strict";e.exports=require("tls")},837:e=>{"use strict";e.exports=require("util")},144:e=>{"use strict";e.exports=require("vm")},796:e=>{"use strict";e.exports=require("zlib")}};var __webpack_module_cache__={};function __nccwpck_require__(e){var t=__webpack_module_cache__[e];if(t!==undefined){return t.exports}var r=__webpack_module_cache__[e]={exports:{}};var n=true;try{__webpack_modules__[e].call(r.exports,r,r.exports,__nccwpck_require__);n=false}finally{if(n)delete __webpack_module_cache__[e]}return r.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var __webpack_exports__={};(()=>{"use strict";var e=__webpack_exports__;Object.defineProperty(e,"__esModule",{value:true});e.EdgeRuntime=e.runServer=e.createHandler=void 0;var t=__nccwpck_require__(179);Object.defineProperty(e,"createHandler",{enumerable:true,get:function(){return t.createHandler}});Object.defineProperty(e,"runServer",{enumerable:true,get:function(){return t.runServer}});var r=__nccwpck_require__(663);Object.defineProperty(e,"EdgeRuntime",{enumerable:true,get:function(){return r.EdgeRuntime}})})();module.exports=__webpack_exports__})(); \ No newline at end of file +(()=>{var __webpack_modules__={162:(__unused_webpack_module,exports,__nccwpck_require__)=>{"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EdgeVM=void 0;const buffer_1=__nccwpck_require__(300);const require_1=__nccwpck_require__(986);const vm_1=__nccwpck_require__(218);const vm_2=__nccwpck_require__(144);class EdgeVM extends vm_1.VM{constructor(e={}){super({...e,extend:t=>e.extend?e.extend(addPrimitives(t)):addPrimitives(t)})}}exports.EdgeVM=EdgeVM;function addPrimitives(context){defineProperty(context,"self",{enumerable:true,value:context});defineProperty(context,"globalThis",{value:context});defineProperty(context,"Symbol",{value:Symbol});defineProperty(context,"clearInterval",{value:clearInterval});defineProperty(context,"clearTimeout",{value:clearTimeout});defineProperty(context,"setInterval",{value:setInterval});defineProperty(context,"setTimeout",{value:setTimeout});defineProperty(context,"EdgeRuntime",{value:"edge-runtime"});defineProperties(context,{exports:(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/console"),scopedContext:{console:console}}),nonenumerable:["console"]});const encodings=(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/encoding"),scopedContext:{Buffer:buffer_1.Buffer,global:{ArrayBuffer:ArrayBuffer}}});defineProperties(context,{exports:encodings,nonenumerable:["atob","btoa","TextEncoder","TextDecoder"]});const streams=(0,require_1.requireWithCache)({path:require.resolve("next/dist/compiled/@edge-runtime/primitives/streams"),context:context});defineProperties(context,{exports:streams,nonenumerable:["ReadableStream","ReadableStreamBYOBReader","ReadableStreamDefaultReader","TransformStream","WritableStream","WritableStreamDefaultWriter"]});const abort=(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/abort-controller")});defineProperties(context,{exports:abort,nonenumerable:["AbortController","AbortSignal"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({cache:new Map([["punycode",{exports:__nccwpck_require__(477)}]]),context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/url"),scopedContext:{TextEncoder:encodings.TextEncoder,TextDecoder:encodings.TextDecoder}}),nonenumerable:["URL","URLSearchParams","URLPattern"]});const blob=(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/blob")});defineProperties(context,{exports:blob,nonenumerable:["Blob"]});const webFetch=(0,require_1.requireWithCache)({context:context,cache:new Map([["abort-controller",{exports:abort}],["assert",{exports:__nccwpck_require__(491)}],["buffer",{exports:__nccwpck_require__(300)}],["events",{exports:__nccwpck_require__(361)}],["http",{exports:__nccwpck_require__(685)}],["net",{exports:__nccwpck_require__(808)}],["perf_hooks",{exports:__nccwpck_require__(74)}],["stream",{exports:__nccwpck_require__(781)}],["tls",{exports:__nccwpck_require__(404)}],["util",{exports:__nccwpck_require__(837)}],["zlib",{exports:__nccwpck_require__(796)}],[require.resolve("next/dist/compiled/@edge-runtime/primitives/streams"),{exports:streams}],[require.resolve("next/dist/compiled/@edge-runtime/primitives/blob"),{exports:blob}]]),path:require.resolve("next/dist/compiled/@edge-runtime/primitives/fetch"),scopedContext:{Buffer:buffer_1.Buffer,FinalizationRegistry:function(){return{register:function(){}}},global:{},queueMicrotask:queueMicrotask,setImmediate:setImmediate}});defineProperties(context,{exports:webFetch,nonenumerable:["fetch","File","FormData","Headers","Request","Response"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({cache:new Map([[require.resolve("next/dist/compiled/@edge-runtime/primitives/fetch"),{exports:webFetch}]]),context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/cache"),scopedContext:{global:{}}}),enumerable:["caches"],nonenumerable:["Cache","CacheStorage"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({context:context,cache:new Map([["crypto",{exports:__nccwpck_require__(113)}],["process",{exports:__nccwpck_require__(282)}]]),path:require.resolve("next/dist/compiled/@edge-runtime/primitives/crypto"),scopedContext:{Buffer:buffer_1.Buffer,Uint8Array:new Proxy((0,vm_2.runInContext)("Uint8Array",context),{construct(e,t){const r=new e(...t);if(!(t[0]instanceof buffer_1.Buffer)){return r}return new e([...r])}})}}),enumerable:["crypto"],nonenumerable:["Crypto","CryptoKey","SubtleCrypto"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/events")}),nonenumerable:["Event","EventTarget","FetchEvent","PromiseRejectionEvent"]});defineProperties(context,{exports:(0,require_1.requireWithCache)({context:context,path:require.resolve("next/dist/compiled/@edge-runtime/primitives/structured-clone")}),nonenumerable:["structuredClone"]});return context}function defineProperty(e,t,r){var n,o,s;Object.defineProperty(e,t,{configurable:(n=r.configurable)!==null&&n!==void 0?n:false,enumerable:(o=r.enumerable)!==null&&o!==void 0?o:false,value:r.value,writable:(s=r.writable)!==null&&s!==void 0?s:true})}function defineProperties(e,t){var r,n;for(const n of(r=t.enumerable)!==null&&r!==void 0?r:[]){if(!t.exports[n]){throw new Error(`Attempt to export a nullable value for "${n}"`)}defineProperty(e,n,{enumerable:true,value:t.exports[n]})}for(const r of(n=t.nonenumerable)!==null&&n!==void 0?n:[]){if(!t.exports[r]){throw new Error(`Attempt to export a nullable value for "${r}"`)}defineProperty(e,r,{value:t.exports[r]})}}},135:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.VM=t.EdgeVM=void 0;var n=r(162);Object.defineProperty(t,"EdgeVM",{enumerable:true,get:function(){return n.EdgeVM}});var o=r(218);Object.defineProperty(t,"VM",{enumerable:true,get:function(){return o.VM}})},986:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.requireWithCache=t.createRequire=t.requireDependencies=void 0;const n=r(147);const o=r(144);const s=r(17);function requireDependencies(e){const{context:t,requireCache:r,dependencies:n}=e;const o=createRequire(t,r);for(const{path:e,mapExports:r}of n){const n=o(e,e);for(const e of Object.keys(r)){t[r[e]]=n[e]}}}t.requireDependencies=requireDependencies;function createRequire(e,t,r,i={}){return function requireFn(a,E){const c=require.resolve(E,{paths:[(0,s.dirname)(a)]});const u=t.get(E)||t.get(c);if(u!==undefined&&u!==null){return u.exports}const _={exports:{},loaded:false,id:c};t.set(c,_);r===null||r===void 0?void 0:r.add(c);const d=(0,o.runInContext)(`(function(module,exports,require,__dirname,__filename,${Object.keys(i).join(",")}) {${(0,n.readFileSync)(c,"utf-8")}\n})`,e);try{d(_,_.exports,requireFn.bind(null,c),(0,s.dirname)(c),c,...Object.values(i))}catch(e){t.delete(c);throw e}_.loaded=true;return _.exports}}t.createRequire=createRequire;function requireWithCache(e){var t;return createRequire(e.context,(t=e.cache)!==null&&t!==void 0?t:new Map,e.references,e.scopedContext).call(null,e.path,e.path)}t.requireWithCache=requireWithCache},266:function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});t.tempFile=void 0;const o=r(277);const s=n(r(17));const i=n(r(147));const a=n(r(37));function tempFile(e){const t=s.default.join(a.default.tmpdir(),o.crypto.randomUUID());i.default.writeFileSync(t,e);return{path:t,remove:()=>i.default.unlinkSync(t)}}t.tempFile=tempFile},218:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.VM=void 0;const n=r(144);const o=r(986);const s=r(266);class VM{constructor(e={}){var t,r,s,i;const a=(0,n.createContext)({},{name:"Edge Runtime",codeGeneration:(t=e.codeGeneration)!==null&&t!==void 0?t:{strings:false,wasm:true}});this.requireCache=(r=e.requireCache)!==null&&r!==void 0?r:new Map;this.context=(i=(s=e.extend)===null||s===void 0?void 0:s.call(e,a))!==null&&i!==void 0?i:a;this.requireFn=(0,o.createRequire)(this.context,this.requireCache)}evaluate(e){return(0,n.runInContext)(e,this.context)}require(e){return this.requireFn(e,e)}requireInContext(e){const t=this.require(e);for(const[e,r]of Object.entries(t)){this.context[e]=r}}requireInlineInContext(e){const t=(0,s.tempFile)(e);this.requireInContext(t.path);t.remove()}}t.VM=VM},734:e=>{"use strict";e.exports=e=>{const t=e[0]*1e9+e[1];const r=t/1e6;const n=t/1e9;return{seconds:n,milliseconds:r,nanoseconds:t}}},593:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.EdgeRuntime=void 0;const n=r(135);let o;let s;class EdgeRuntime extends n.EdgeVM{constructor(e){super({...e,extend:t=>{var r,n;return(n=(r=e===null||e===void 0?void 0:e.extend)===null||r===void 0?void 0:r.call(e,t))!==null&&n!==void 0?n:t}});defineHandlerProps({object:this,setterName:"__onUnhandledRejectionHandler",setter:e=>o=e,getterName:"__rejectionHandlers",getter:()=>o});defineHandlerProps({object:this,setterName:"__onErrorHandler",setter:e=>s=e,getterName:"__errorHandlers",getter:()=>s});this.evaluate(getDefineEventListenersCode());this.dispatchFetch=this.evaluate(getDispatchFetchCode());if(e===null||e===void 0?void 0:e.initialCode){this.evaluate(e.initialCode)}}}t.EdgeRuntime=EdgeRuntime;process.on("unhandledRejection",(function invokeRejectionHandlers(e,t){o===null||o===void 0?void 0:o.forEach((r=>r(e,t)))}));process.on("uncaughtException",(function invokeErrorHandlers(e){s===null||s===void 0?void 0:s.forEach((t=>t(e)))}));function getDefineEventListenersCode(){return`\n Object.defineProperty(self, '__listeners', {\n configurable: false,\n enumerable: false,\n value: {},\n writable: true,\n })\n\n function __conditionallyUpdatesHandlerList(eventType) {\n if (eventType === 'unhandledrejection') {\n self.__onUnhandledRejectionHandler = self.__listeners[eventType];\n } else if (eventType === 'error') {\n self.__onErrorHandler = self.__listeners[eventType];\n }\n }\n\n function addEventListener(type, handler) {\n const eventType = type.toLowerCase();\n if (eventType === 'fetch' && self.__listeners.fetch) {\n throw new TypeError('You can register just one "fetch" event listener');\n }\n\n self.__listeners[eventType] = self.__listeners[eventType] || [];\n self.__listeners[eventType].push(handler);\n __conditionallyUpdatesHandlerList(eventType);\n }\n\n function removeEventListener(type, handler) {\n const eventType = type.toLowerCase();\n if (self.__listeners[eventType]) {\n self.__listeners[eventType] = self.__listeners[eventType].filter(item => {\n return item !== handler;\n });\n\n if (self.__listeners[eventType].length === 0) {\n delete self.__listeners[eventType];\n }\n }\n __conditionallyUpdatesHandlerList(eventType);\n }\n `}function getDispatchFetchCode(){return`(async function dispatchFetch(input, init) {\n const request = new Request(input, init);\n const event = new FetchEvent(request);\n if (!self.__listeners.fetch) {\n throw new Error("No fetch event listeners found");\n }\n\n const getResponse = ({ response, error }) => {\n if (error || !response || !(response instanceof Response)) {\n console.error(error ? error : 'The event listener did not respond')\n response = new Response(null, {\n statusText: 'Internal Server Error',\n status: 500\n })\n }\n\n response.waitUntil = () => Promise.all(event.awaiting);\n return response;\n }\n\n try {\n await self.__listeners.fetch[0].call(event, event)\n } catch (error) {\n return getResponse({ error })\n }\n\n return Promise.resolve(event.response)\n .then(response => getResponse({ response }))\n .catch(error => getResponse({ error }))\n })`}function defineHandlerProps({object:e,setterName:t,setter:r,getterName:n,getter:o}){Object.defineProperty(e.context,t,{set:r,configurable:false,enumerable:false});Object.defineProperty(e,n,{get:o,configurable:false,enumerable:false})}},882:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.getClonableBodyStream=void 0;const n=r(781);function getClonableBodyStream(e,t){let r=null;return{finalize(){if(r){replaceRequestBody(e,bodyStreamToNodeStream(r))}},cloneBodyStream(){const n=r!==null&&r!==void 0?r:requestToBodyStream(e,t);const[o,s]=n.tee();r=o;return s}}}t.getClonableBodyStream=getClonableBodyStream;function requestToBodyStream(e,t){const r=new t({start(t){e.on("data",(e=>t.enqueue(e)));e.on("end",(()=>t.terminate()));e.on("error",(e=>t.error(e)))}});return r.readable}function bodyStreamToNodeStream(e){const t=e.getReader();return n.Readable.from(async function*(){while(true){const{done:e,value:r}=await t.read();if(e){return}yield r}}())}function replaceRequestBody(e,t){for(const r in t){let n=t[r];if(typeof n==="function"){n=n.bind(t)}e[r]=n}return e}},12:function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});t.createHandler=void 0;const o=r(882);const s=n(r(720));const i=n(r(242));const a=n(r(504));function createHandler(e){const t=new Set;return{handler:async(r,n)=>{var E,c;const u=(0,a.default)();const _=r.method!=="GET"&&r.method!=="HEAD"?(0,o.getClonableBodyStream)(r,e.runtime.context.TransformStream):undefined;const d=await e.runtime.dispatchFetch(String(getURL(r)),{headers:toRequestInitHeaders(r),method:r.method,body:_===null||_===void 0?void 0:_.cloneBodyStream()});const l=d.waitUntil();t.add(l);l.finally((()=>t.delete(l)));n.statusCode=d.status;n.statusMessage=d.statusText;for(const[e,t]of Object.entries(toNodeHeaders(d.headers))){if(e!=="content-encoding"&&t!==undefined){n.setHeader(e,t)}}if(d.body){for await(const e of d.body){n.write(e)}}const S=`${r.socket.remoteAddress} ${r.method} ${r.url}`;const R=`${(E=(0,s.default)(u()).match(/[a-zA-Z]+|[0-9]+/g))===null||E===void 0?void 0:E.join(" ")}`;const h=`${n.statusCode} ${i.default[n.statusCode]}`;(c=e.logger)===null||c===void 0?void 0:c.debug(`${S} โ†’ ${h} in ${R}`);n.end()},waitUntil:()=>Promise.all(t)}}t.createHandler=createHandler;function getURL(e){var t;const r=((t=e.socket)===null||t===void 0?void 0:t.encrypted)?"https":"http";return new URL(String(e.url),`${r}://${String(e.headers.host)}`)}function toRequestInitHeaders(e){return Object.keys(e.headers).map((t=>{const r=e.headers[t];return[t,Array.isArray(r)?r.join(", "):r!==null&&r!==void 0?r:""]}))}function toNodeHeaders(e){const t={};if(e){for(const[r,n]of e.entries()){t[r]=r.toLowerCase()==="set-cookie"?e.getAll("set-cookie"):n}}return t}},742:(e,t,r)=>{"use strict";Object.defineProperty(t,"__esModule",{value:true});t.runServer=t.createHandler=void 0;var n=r(12);Object.defineProperty(t,"createHandler",{enumerable:true,get:function(){return n.createHandler}});var o=r(653);Object.defineProperty(t,"runServer",{enumerable:true,get:function(){return o.runServer}})},653:function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:true});t.runServer=void 0;const o=r(12);const s=r(361);const i=n(r(685));async function runServer(e){const{handler:t,waitUntil:r}=(0,o.createHandler)(e);const n=i.default.createServer(t);n.listen(e.port);try{await(0,s.once)(n,"listening")}catch(t){if((t===null||t===void 0?void 0:t.code)==="EADDRINUSE"){return runServer({...e,port:undefined})}throw t}const a=n.address();const E=typeof a==="string"||a==null?String(a):`http://localhost:${a.port}`;return{url:E,close:async()=>{await r();await new Promise(((e,t)=>n.close((r=>{if(r)t(r);e()}))))},waitUntil:r}}t.runServer=runServer},242:e=>{var t;t={"1xx":"Informational","1xx_NAME":"INFORMATIONAL","1xx_MESSAGE":"Indicates an interim response for communicating connection status or request progress prior to completing the requested action and sending a final response.",INFORMATIONAL:"1xx","2xx":"Successful","2xx_NAME":"SUCCESSFUL","2xx_MESSAGE":"Indicates that the client's request was successfully received, understood, and accepted.",SUCCESSFUL:"2xx","3xx":"Redirection","3xx_NAME":"REDIRECTION","3xx_MESSAGE":"Indicates that further action needs to be taken by the user agent in order to fulfill the request.",REDIRECTION:"3xx","4xx":"Client Error","4xx_NAME":"CLIENT_ERROR","4xx_MESSAGE":"Indicates that the client seems to have erred.",CLIENT_ERROR:"4xx","5xx":"Server Error","5xx_NAME":"SERVER_ERROR","5xx_MESSAGE":"Indicates that the server is aware that it has erred or is incapable of performing the requested method.",SERVER_ERROR:"5xx"};e.exports={classes:t,100:"Continue","100_NAME":"CONTINUE","100_MESSAGE":"The server has received the request headers and the client should proceed to send the request body.","100_CLASS":t.INFORMATIONAL,CONTINUE:100,101:"Switching Protocols","101_NAME":"SWITCHING_PROTOCOLS","101_MESSAGE":"The requester has asked the server to switch protocols and the server has agreed to do so.","101_CLASS":t.INFORMATIONAL,SWITCHING_PROTOCOLS:101,102:"Processing","102_NAME":"PROCESSING","102_MESSAGE":"A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request. This code indicates that the server has received and is processing the request, but no response is available yet.[7] This prevents the client from timing out and assuming the request was lost.","102_CLASS":t.INFORMATIONAL,PROCESSING:102,103:"Early Hints","103_NAME":"EARLY_HINTS","103_MESSAGE":"Used to return some response headers before final HTTP message.","103_CLASS":t.INFORMATIONAL,EARLY_HINTS:103,200:"OK","200_NAME":"OK","200_MESSAGE":"Standard response for successful HTTP requests.","200_CLASS":t.SUCCESSFUL,OK:200,201:"Created","201_NAME":"CREATED","201_MESSAGE":"The request has been fulfilled, resulting in the creation of a new resource.","201_CLASS":t.SUCCESSFUL,CREATED:201,202:"Accepted","202_NAME":"ACCEPTED","202_MESSAGE":"The request has been accepted for processing, but the processing has not been completed.","202_CLASS":t.SUCCESSFUL,ACCEPTED:202,203:"Non-Authoritative Information","203_NAME":"NON_AUTHORITATIVE_INFORMATION","203_MESSAGE":"The server is a transforming proxy (e.g. a Web accelerator) that received a 200 OK from its origin, but is returning a modified version of the origin's response.","203_CLASS":t.SUCCESSFUL,NON_AUTHORITATIVE_INFORMATION:203,204:"No Content","204_NAME":"NO_CONTENT","204_MESSAGE":"The server successfully processed the request and is not returning any content.","204_CLASS":t.SUCCESSFUL,NO_CONTENT:204,205:"Reset Content","205_NAME":"RESET_CONTENT","205_MESSAGE":"The server successfully processed the request, but is not returning any content. Unlike a 204 response, this response requires that the requester reset the document view.","205_CLASS":t.SUCCESSFUL,RESET_CONTENT:205,206:"Partial Content","206_NAME":"PARTIAL_CONTENT","206_MESSAGE":"The server is delivering only part of the resource (byte serving) due to a range header sent by the client.","206_CLASS":t.SUCCESSFUL,PARTIAL_CONTENT:206,207:"Multi Status","207_NAME":"MULTI_STATUS","207_MESSAGE":"The message body that follows is by default an XML message and can contain a number of separate response codes, depending on how many sub-requests were made.","207_CLASS":t.SUCCESSFUL,MULTI_STATUS:207,208:"Already Reported","208_NAME":"ALREADY_REPORTED","208_MESSAGE":"The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response, and are not being included again.","208_CLASS":t.SUCCESSFUL,ALREADY_REPORTED:208,226:"IM Used","226_NAME":"IM_USED","226_MESSAGE":"The server has fulfilled a request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance.","226_CLASS":t.SUCCESSFUL,IM_USED:226,300:"Multiple Choices","300_NAME":"MULTIPLE_CHOICES","300_MESSAGE":"Indicates multiple options for the resource from which the client may choose.","300_CLASS":t.REDIRECTION,MULTIPLE_CHOICES:300,301:"Moved Permanently","301_NAME":"MOVED_PERMANENTLY","301_MESSAGE":"This and all future requests should be directed to the given URI.","301_CLASS":t.REDIRECTION,MOVED_PERMANENTLY:301,302:"Found","302_NAME":"FOUND","302_MESSAGE":'This is an example of industry practice contradicting the standard. The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 to distinguish between the two behaviours.',"302_CLASS":t.REDIRECTION,FOUND:302,303:"See Other","303_NAME":"SEE_OTHER","303_MESSAGE":"The response to the request can be found under another URI using the GET method.","303_CLASS":t.REDIRECTION,SEE_OTHER:303,304:"Not Modified","304_NAME":"NOT_MODIFIED","304_MESSAGE":"Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match.","304_CLASS":t.REDIRECTION,NOT_MODIFIED:304,305:"Use Proxy","305_NAME":"USE_PROXY","305_MESSAGE":"The requested resource is available only through a proxy, the address for which is provided in the response.","305_CLASS":t.REDIRECTION,USE_PROXY:305,306:"Switch Proxy","306_NAME":"SWITCH_PROXY","306_MESSAGE":'No longer used. Originally meant "Subsequent requests should use the specified proxy.',"306_CLASS":t.REDIRECTION,SWITCH_PROXY:306,307:"Temporary Redirect","307_NAME":"TEMPORARY_REDIRECT","307_MESSAGE":"In this case, the request should be repeated with another URI; however, future requests should still use the original URI.","307_CLASS":t.REDIRECTION,TEMPORARY_REDIRECT:307,308:"Permanent Redirect","308_NAME":"PERMANENT_REDIRECT","308_MESSAGE":"The request and all future requests should be repeated using another URI.","308_CLASS":t.REDIRECTION,PERMANENT_REDIRECT:308,400:"Bad Request","400_NAME":"BAD_REQUEST","400_MESSAGE":"The server cannot or will not process the request due to an apparent client error.","400_CLASS":t.CLIENT_ERROR,BAD_REQUEST:400,401:"Unauthorized","401_NAME":"UNAUTHORIZED","401_MESSAGE":"Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided.","401_CLASS":t.CLIENT_ERROR,UNAUTHORIZED:401,402:"Payment Required","402_NAME":"PAYMENT_REQUIRED","402_MESSAGE":"Reserved for future use. The original intention was that this code might be used as part of some form of digital cash or micropayment scheme, as proposed for example by GNU Taler, but that has not yet happened, and this code is not usually used.","402_CLASS":t.CLIENT_ERROR,PAYMENT_REQUIRED:402,403:"Forbidden","403_NAME":"FORBIDDEN","403_MESSAGE":"The request was valid, but the server is refusing action.","403_CLASS":t.CLIENT_ERROR,FORBIDDEN:403,404:"Not Found","404_NAME":"NOT_FOUND","404_MESSAGE":"The requested resource could not be found but may be available in the future. Subsequent requests by the client are permissible.","404_CLASS":t.CLIENT_ERROR,NOT_FOUND:404,405:"Method Not Allowed","405_NAME":"METHOD_NOT_ALLOWED","405_MESSAGE":"A request method is not supported for the requested resource.","405_CLASS":t.CLIENT_ERROR,METHOD_NOT_ALLOWED:405,406:"Not Acceptable","406_NAME":"NOT_ACCEPTABLE","406_MESSAGE":"The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request.","406_CLASS":t.CLIENT_ERROR,NOT_ACCEPTABLE:406,407:"Proxy Authentication Required","407_NAME":"PROXY_AUTHENTICATION_REQUIRED","407_MESSAGE":"The client must first authenticate itself with the proxy.","407_CLASS":t.CLIENT_ERROR,PROXY_AUTHENTICATION_REQUIRED:407,408:"Request Time-out","408_NAME":"REQUEST_TIMEOUT","408_MESSAGE":"The server timed out waiting for the request.","408_CLASS":t.CLIENT_ERROR,REQUEST_TIMEOUT:408,409:"Conflict","409_NAME":"CONFLICT","409_MESSAGE":"Indicates that the request could not be processed because of conflict in the request, such as an edit conflict between multiple simultaneous updates.","409_CLASS":t.CLIENT_ERROR,CONFLICT:409,410:"Gone","410_NAME":"GONE","410_MESSAGE":"Indicates that the resource requested is no longer available and will not be available again.","410_CLASS":t.CLIENT_ERROR,GONE:410,411:"Length Required","411_NAME":"LENGTH_REQUIRED","411_MESSAGE":"The request did not specify the length of its content, which is required by the requested resource.","411_CLASS":t.CLIENT_ERROR,LENGTH_REQUIRED:411,412:"Precondition Failed","412_NAME":"PRECONDITION_FAILED","412_MESSAGE":"The server does not meet one of the preconditions that the requester put on the request.","412_CLASS":t.CLIENT_ERROR,PRECONDITION_FAILED:412,413:"Request Entity Too Large","413_NAME":"REQUEST_ENTITY_TOO_LARGE","413_MESSAGE":'The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large".',"413_CLASS":t.CLIENT_ERROR,REQUEST_ENTITY_TOO_LARGE:413,414:"Request-URI Too Large","414_NAME":"REQUEST_URI_TOO_LONG","414_MESSAGE":"The URI provided was too long for the server to process.","414_CLASS":t.CLIENT_ERROR,REQUEST_URI_TOO_LONG:414,415:"Unsupported Media Type","415_NAME":"UNSUPPORTED_MEDIA_TYPE","415_MESSAGE":"The request entity has a media type which the server or resource does not support.","415_CLASS":t.CLIENT_ERROR,UNSUPPORTED_MEDIA_TYPE:415,416:"Requested Range not Satisfiable","416_NAME":"REQUESTED_RANGE_NOT_SATISFIABLE","416_MESSAGE":"The client has asked for a portion of the file (byte serving), but the server cannot supply that portion.","416_CLASS":t.CLIENT_ERROR,REQUESTED_RANGE_NOT_SATISFIABLE:416,417:"Expectation Failed","417_NAME":"EXPECTATION_FAILED","417_MESSAGE":"The server cannot meet the requirements of the Expect request-header field.","417_CLASS":t.CLIENT_ERROR,EXPECTATION_FAILED:417,418:"I'm a teapot","418_NAME":"IM_A_TEAPOT","418_MESSAGE":'Any attempt to brew coffee with a teapot should result in the error code "418 I\'m a teapot". The resulting entity body MAY be short and stout.',"418_CLASS":t.CLIENT_ERROR,IM_A_TEAPOT:418,421:"Misdirected Request","421_NAME":"MISDIRECTED_REQUEST","421_MESSAGE":"The request was directed at a server that is not able to produce a response.","421_CLASS":t.CLIENT_ERROR,MISDIRECTED_REQUEST:421,422:"Unprocessable Entity","422_NAME":"UNPROCESSABLE_ENTITY","422_MESSAGE":"The request was well-formed but was unable to be followed due to semantic errors.","422_CLASS":t.CLIENT_ERROR,UNPROCESSABLE_ENTITY:422,423:"Locked","423_NAME":"LOCKED","423_MESSAGE":"The resource that is being accessed is locked.","423_CLASS":t.CLIENT_ERROR,LOCKED:423,424:"Failed Dependency","424_NAME":"FAILED_DEPENDENCY","424_MESSAGE":"The request failed because it depended on another request and that request failed.","424_CLASS":t.CLIENT_ERROR,FAILED_DEPENDENCY:424,426:"Upgrade Required","426_NAME":"UPGRADE_REQUIRED","426_MESSAGE":"The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field.","426_CLASS":t.CLIENT_ERROR,UPGRADE_REQUIRED:426,428:"Precondition Required","428_NAME":"PRECONDITION_REQUIRED","428_MESSAGE":"The origin server requires the request to be conditional.","428_CLASS":t.CLIENT_ERROR,PRECONDITION_REQUIRED:428,429:"Too Many Requests","429_NAME":"TOO_MANY_REQUESTS","429_MESSAGE":"The user has sent too many requests in a given amount of time.","429_CLASS":t.CLIENT_ERROR,TOO_MANY_REQUESTS:429,431:"Request Header Fields Too Large","431_NAME":"REQUEST_HEADER_FIELDS_TOO_LARGE","431_MESSAGE":"The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large.","431_CLASS":t.CLIENT_ERROR,REQUEST_HEADER_FIELDS_TOO_LARGE:431,451:"Unavailable For Legal Reasons","451_NAME":"UNAVAILABLE_FOR_LEGAL_REASONS","451_MESSAGE":"A server operator has received a legal demand to deny access to a resource or to a set of resources that includes the requested resource.","451_CLASS":t.CLIENT_ERROR,UNAVAILABLE_FOR_LEGAL_REASONS:451,500:"Internal Server Error","500_NAME":"INTERNAL_SERVER_ERROR","500_MESSAGE":"A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.","500_CLASS":t.SERVER_ERROR,INTERNAL_SERVER_ERROR:500,501:"Not Implemented","501_NAME":"NOT_IMPLEMENTED","501_MESSAGE":"The server either does not recognize the request method, or it lacks the ability to fulfil the request. Usually this implies future availability.","501_CLASS":t.SERVER_ERROR,NOT_IMPLEMENTED:501,502:"Bad Gateway","502_NAME":"BAD_GATEWAY","502_MESSAGE":"The server was acting as a gateway or proxy and received an invalid response from the upstream server.","502_CLASS":t.SERVER_ERROR,BAD_GATEWAY:502,503:"Service Unavailable","503_NAME":"SERVICE_UNAVAILABLE","503_MESSAGE":"The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state.","503_CLASS":t.SERVER_ERROR,SERVICE_UNAVAILABLE:503,504:"Gateway Time-out","504_NAME":"GATEWAY_TIMEOUT","504_MESSAGE":"The server was acting as a gateway or proxy and did not receive a timely response from the upstream server.","504_CLASS":t.SERVER_ERROR,GATEWAY_TIMEOUT:504,505:"HTTP Version not Supported","505_NAME":"HTTP_VERSION_NOT_SUPPORTED","505_MESSAGE":"The server does not support the HTTP protocol version used in the request.","505_CLASS":t.SERVER_ERROR,HTTP_VERSION_NOT_SUPPORTED:505,506:"Variant Also Negotiates","506_NAME":"VARIANT_ALSO_NEGOTIATES","506_MESSAGE":"Transparent content negotiation for the request results in a circular reference.","506_CLASS":t.SERVER_ERROR,VARIANT_ALSO_NEGOTIATES:506,507:"Insufficient Storage","507_NAME":"INSUFFICIENT_STORAGE","507_MESSAGE":"The server is unable to store the representation needed to complete the request.","507_CLASS":t.SERVER_ERROR,INSUFFICIENT_STORAGE:507,508:"Loop Detected","508_NAME":"LOOP_DETECTED","508_MESSAGE":"The server detected an infinite loop while processing the request.","508_CLASS":t.SERVER_ERROR,LOOP_DETECTED:508,510:"Not Extended","510_NAME":"NOT_EXTENDED","510_MESSAGE":"Further extensions to the request are required for the server to fulfil it.","510_CLASS":t.SERVER_ERROR,NOT_EXTENDED:510,511:"Network Authentication Required","511_NAME":"NETWORK_AUTHENTICATION_REQUIRED","511_MESSAGE":"The client needs to authenticate to gain network access. Intended for use by intercepting proxies used to control access to the network.","511_CLASS":t.SERVER_ERROR,NETWORK_AUTHENTICATION_REQUIRED:511,extra:{unofficial:{103:"Checkpoint","103_NAME":"CHECKPOINT","103_MESSAGE":"Used in the resumable requests proposal to resume aborted PUT or POST requests.","103_CLASS":t.INFORMATIONAL,CHECKPOINT:103,419:"Page Expired","419_NAME":"PAGE_EXPIRED","419_MESSAGE":"Used by the Laravel Framework when a CSRF Token is missing or expired.","419_CLASS":t.CLIENT_ERROR,PAGE_EXPIRED:419,218:"This is fine","218_NAME":"THIS_IS_FINE","218_MESSAGE":"Used as a catch-all error condition for allowing response bodies to flow through Apache when ProxyErrorOverride is enabled. When ProxyErrorOverride is enabled in Apache, response bodies that contain a status code of 4xx or 5xx are automatically discarded by Apache in favor of a generic response or a custom response specified by the ErrorDocument directive.","218_CLASS":t.SUCCESSFUL,THIS_IS_FINE:218,420:"Enhance Your Calm","420_NAME":"ENHANCE_YOUR_CALM","420_MESSAGE":"Returned by version 1 of the Twitter Search and Trends API when the client is being rate limited; versions 1.1 and later use the 429 Too Many Requests response code instead.","420_CLASS":t.CLIENT_ERROR,ENHANCE_YOUR_CALM:420,450:"Blocked by Windows Parental Controls","450_NAME":"BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS","450_MESSAGE":"The Microsoft extension code indicated when Windows Parental Controls are turned on and are blocking access to the requested webpage.","450_CLASS":t.CLIENT_ERROR,BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS:450,498:"Invalid Token","498_NAME":"INVALID_TOKEN","498_MESSAGE":"Returned by ArcGIS for Server. Code 498 indicates an expired or otherwise invalid token.","498_CLASS":t.CLIENT_ERROR,INVALID_TOKEN:498,499:"Token Required","499_NAME":"TOKEN_REQUIRED","499_MESSAGE":"Returned by ArcGIS for Server. Code 499 indicates that a token is required but was not submitted.","499_CLASS":t.CLIENT_ERROR,TOKEN_REQUIRED:499,509:"Bandwidth Limit Exceeded","509_NAME":"BANDWIDTH_LIMIT_EXCEEDED","509_MESSAGE":"The server has exceeded the bandwidth specified by the server administrator.","509_CLASS":t.SERVER_ERROR,BANDWIDTH_LIMIT_EXCEEDED:509,530:"Site is frozen","530_NAME":"SITE_IS_FROZEN","530_MESSAGE":"Used by the Pantheon web platform to indicate a site that has been frozen due to inactivity.","530_CLASS":t.SERVER_ERROR,SITE_IS_FROZEN:530,598:"Network read timeout error","598_NAME":"NETWORK_READ_TIMEOUT_ERROR","598_MESSAGE":"Used by some HTTP proxies to signal a network read timeout behind the proxy to a client in front of the proxy.","598_CLASS":t.SERVER_ERROR,NETWORK_READ_TIMEOUT_ERROR:598},iis:{440:"Login Time-out","440_NAME":"LOGIN_TIME_OUT","440_MESSAGE":"The client's session has expired and must log in again.","440_CLASS":t.CLIENT_ERROR,LOGIN_TIME_OUT:440,449:"Retry With","449_NAME":"RETRY_WITH","449_MESSAGE":"The server cannot honour the request because the user has not provided the required information.","449_CLASS":t.CLIENT_ERROR,RETRY_WITH:449,451:"Redirect","451_NAME":"REDIRECT","451_MESSAGE":"Used in Exchange ActiveSync when either a more efficient server is available or the server cannot access the users' mailbox.","451_CLASS":t.CLIENT_ERROR,REDIRECT:451},nginx:{444:"No Response","444_NAME":"NO_RESPONSE","444_MESSAGE":"Used internally to instruct the server to return no information to the client and close the connection immediately.","444_CLASS":t.CLIENT_ERROR,NO_RESPONSE:444,494:"Request header too large","494_NAME":"REQUEST_HEADER_TOO_LARGE","494_MESSAGE":"Client sent too large request or too long header line.","494_CLASS":t.CLIENT_ERROR,REQUEST_HEADER_TOO_LARGE:494,495:"SSL Certificate Error","495_NAME":"SSL_CERTIFICATE_ERROR","495_MESSAGE":"An expansion of the 400 Bad Request response code, used when the client has provided an invalid client certificate.","495_CLASS":t.CLIENT_ERROR,SSL_CERTIFICATE_ERROR:495,496:"SSL Certificate Required","496_NAME":"SSL_CERTIFICATE_REQUIRED","496_MESSAGE":"An expansion of the 400 Bad Request response code, used when a client certificate is required but not provided.","496_CLASS":t.CLIENT_ERROR,SSL_CERTIFICATE_REQUIRED:496,497:"HTTP Request Sent to HTTPS Port","497_NAME":"HTTP_REQUEST_SENT_TO_HTTPS_PORT","497_MESSAGE":"An expansion of the 400 Bad Request response code, used when the client has made a HTTP request to a port listening for HTTPS requests.","497_CLASS":t.CLIENT_ERROR,HTTP_REQUEST_SENT_TO_HTTPS_PORT:497,499:"Client Closed Request","499_NAME":"CLIENT_CLOSED_REQUEST","499_MESSAGE":"Used when the client has closed the request before the server could send a response.","499_CLASS":t.CLIENT_ERROR,CLIENT_CLOSED_REQUEST:499},cloudflare:{520:"Unknown Error","520_NAME":"UNKNOWN_ERROR","520_MESSAGE":'The 520 error is used as a "catch-all response for when the origin server returns something unexpected", listing connection resets, large headers, and empty or invalid responses as common triggers.',"520_CLASS":t.SERVER_ERROR,UNKNOWN_ERROR:520,521:"Web Server Is Down","521_NAME":"WEB_SERVER_IS_DOWN","521_MESSAGE":"The origin server has refused the connection from Cloudflare.","521_CLASS":t.SERVER_ERROR,WEB_SERVER_IS_DOWN:521,522:"Connection Timed Out","522_NAME":"CONNECTION_TIMED_OUT","522_MESSAGE":"Cloudflare could not negotiate a TCP handshake with the origin server.","522_CLASS":t.SERVER_ERROR,CONNECTION_TIMED_OUT:522,523:"Origin Is Unreachable","523_NAME":"ORIGIN_IS_UNREACHABLE","523_MESSAGE":"Cloudflare could not reach the origin server.","523_CLASS":t.SERVER_ERROR,ORIGIN_IS_UNREACHABLE:523,524:"A Timeout Occurred","524_NAME":"A_TIMEOUT_OCCURRED","524_MESSAGE":"Cloudflare was able to complete a TCP connection to the origin server, but did not receive a timely HTTP response.","524_CLASS":t.SERVER_ERROR,A_TIMEOUT_OCCURRED:524,525:"SSL Handshake Failed","525_NAME":"SSL_HANDSHAKE_FAILED","525_MESSAGE":"Cloudflare could not negotiate a SSL/TLS handshake with the origin server.","525_CLASS":t.SERVER_ERROR,SSL_HANDSHAKE_FAILED:525,526:"Invalid SSL Certificate","526_NAME":"INVALID_SSL_CERTIFICATE","526_MESSAGE":"Cloudflare could not validate the SSL/TLS certificate that the origin server presented.","526_CLASS":t.SERVER_ERROR,INVALID_SSL_CERTIFICATE:526,527:"Railgun Error","527_NAME":"RAILGUN_ERROR","527_MESSAGE":"Error 527 indicates that the request timed out or failed after the WAN connection had been established.","527_CLASS":t.SERVER_ERROR,RAILGUN_ERROR:527}}}},672:e=>{"use strict";e.exports=e=>{if(typeof e!=="number"){throw new TypeError("Expected a number")}const t=e>0?Math.floor:Math.ceil;return{days:t(e/864e5),hours:t(e/36e5)%24,minutes:t(e/6e4)%60,seconds:t(e/1e3)%60,milliseconds:t(e)%1e3,microseconds:t(e*1e3)%1e3,nanoseconds:t(e*1e6)%1e3}}},720:(e,t,r)=>{"use strict";const n=r(672);const pluralize=(e,t)=>t===1?e:`${e}s`;const o=1e-7;e.exports=(e,t={})=>{if(!Number.isFinite(e)){throw new TypeError("Expected a finite number")}if(t.colonNotation){t.compact=false;t.formatSubMilliseconds=false;t.separateMilliseconds=false;t.verbose=false}if(t.compact){t.secondsDecimalDigits=0;t.millisecondsDecimalDigits=0}const r=[];const floorDecimals=(e,t)=>{const r=Math.floor(e*10**t+o);const n=Math.round(r)/10**t;return n.toFixed(t)};const add=(e,n,o,s)=>{if((r.length===0||!t.colonNotation)&&e===0&&!(t.colonNotation&&o==="m")){return}s=(s||e||"0").toString();let i;let a;if(t.colonNotation){i=r.length>0?":":"";a="";const e=s.includes(".")?s.split(".")[0].length:s.length;const t=r.length>0?2:1;s="0".repeat(Math.max(0,t-e))+s}else{i="";a=t.verbose?" "+pluralize(n,e):o}r.push(i+s+a)};const s=n(e);add(Math.trunc(s.days/365),"year","y");add(s.days%365,"day","d");add(s.hours,"hour","h");add(s.minutes,"minute","m");if(t.separateMilliseconds||t.formatSubMilliseconds||!t.colonNotation&&e<1e3){add(s.seconds,"second","s");if(t.formatSubMilliseconds){add(s.milliseconds,"millisecond","ms");add(s.microseconds,"microsecond","ยตs");add(s.nanoseconds,"nanosecond","ns")}else{const e=s.milliseconds+s.microseconds/1e3+s.nanoseconds/1e6;const r=typeof t.millisecondsDecimalDigits==="number"?t.millisecondsDecimalDigits:0;const n=e>=1?Math.round(e):Math.ceil(e);const o=r?e.toFixed(r):n;add(Number.parseFloat(o,10),"millisecond","ms",o)}}else{const r=e/1e3%60;const n=typeof t.secondsDecimalDigits==="number"?t.secondsDecimalDigits:1;const o=floorDecimals(r,n);const s=t.keepDecimalsOnWholeSeconds?o:o.replace(/\.0+$/,"");add(Number.parseFloat(s,10),"second","s",s)}if(r.length===0){return"0"+(t.verbose?" milliseconds":"ms")}if(t.compact){return r[0]}if(typeof t.unitCount==="number"){const e=t.colonNotation?"":" ";return r.slice(0,Math.max(t.unitCount,1)).join(e)}return t.colonNotation?r.join(""):r.join(" ")}},504:(e,t,r)=>{"use strict";const n=r(734);e.exports=()=>{const e=process.hrtime();const end=t=>n(process.hrtime(e))[t];const returnValue=()=>end("milliseconds");returnValue.rounded=()=>Math.round(end("milliseconds"));returnValue.seconds=()=>end("seconds");returnValue.nanoseconds=()=>end("nanoseconds");return returnValue}},491:e=>{"use strict";e.exports=require("assert")},300:e=>{"use strict";e.exports=require("buffer")},113:e=>{"use strict";e.exports=require("crypto")},361:e=>{"use strict";e.exports=require("events")},147:e=>{"use strict";e.exports=require("fs")},685:e=>{"use strict";e.exports=require("http")},808:e=>{"use strict";e.exports=require("net")},277:e=>{"use strict";e.exports=require("next/dist/compiled/@edge-runtime/primitives/crypto")},37:e=>{"use strict";e.exports=require("os")},17:e=>{"use strict";e.exports=require("path")},74:e=>{"use strict";e.exports=require("perf_hooks")},282:e=>{"use strict";e.exports=require("process")},477:e=>{"use strict";e.exports=require("punycode")},781:e=>{"use strict";e.exports=require("stream")},404:e=>{"use strict";e.exports=require("tls")},837:e=>{"use strict";e.exports=require("util")},144:e=>{"use strict";e.exports=require("vm")},796:e=>{"use strict";e.exports=require("zlib")}};var __webpack_module_cache__={};function __nccwpck_require__(e){var t=__webpack_module_cache__[e];if(t!==undefined){return t.exports}var r=__webpack_module_cache__[e]={exports:{}};var n=true;try{__webpack_modules__[e].call(r.exports,r,r.exports,__nccwpck_require__);n=false}finally{if(n)delete __webpack_module_cache__[e]}return r.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var __webpack_exports__={};(()=>{"use strict";var e=__webpack_exports__;Object.defineProperty(e,"__esModule",{value:true});e.EdgeRuntime=e.runServer=e.createHandler=void 0;var t=__nccwpck_require__(742);Object.defineProperty(e,"createHandler",{enumerable:true,get:function(){return t.createHandler}});Object.defineProperty(e,"runServer",{enumerable:true,get:function(){return t.runServer}});var r=__nccwpck_require__(593);Object.defineProperty(e,"EdgeRuntime",{enumerable:true,get:function(){return r.EdgeRuntime}})})();module.exports=__webpack_exports__})(); \ No newline at end of file diff --git a/packages/next/index.d.ts b/packages/next/index.d.ts index 5d7a7b9cd205..cba2b1dd4e05 100644 --- a/packages/next/index.d.ts +++ b/packages/next/index.d.ts @@ -1,5 +1,5 @@ /// -/// +/// /// /// /// diff --git a/packages/next/lib/load-custom-routes.ts b/packages/next/lib/load-custom-routes.ts index caa0e2f8307c..f9b6ee393c55 100644 --- a/packages/next/lib/load-custom-routes.ts +++ b/packages/next/lib/load-custom-routes.ts @@ -3,9 +3,8 @@ import type { Token } from 'next/dist/compiled/path-to-regexp' import chalk from './chalk' import { escapeStringRegexp } from '../shared/lib/escape-regexp' -import { PERMANENT_REDIRECT_STATUS } from '../shared/lib/constants' -import { TEMPORARY_REDIRECT_STATUS } from '../shared/lib/constants' import { tryToParsePath } from './try-to-parse-path' +import { allowedStatusCodes } from './redirect-status' export type RouteHas = | { @@ -53,41 +52,14 @@ export type Redirect = { } ) -export const allowedStatusCodes = new Set([301, 302, 303, 307, 308]) const allowedHasTypes = new Set(['header', 'cookie', 'query', 'host']) const namedGroupsRegex = /\(\?<([a-zA-Z][a-zA-Z0-9]*)>/g -export function getRedirectStatus(route: { - statusCode?: number - permanent?: boolean -}): number { - return ( - route.statusCode || - (route.permanent ? PERMANENT_REDIRECT_STATUS : TEMPORARY_REDIRECT_STATUS) - ) -} - export function normalizeRouteRegex(regex: string) { // clean up un-necessary escaping from regex.source which turns / into \\/ return regex.replace(/\\\//g, '/') } -// for redirects we restrict matching /_next and for all routes -// we add an optional trailing slash at the end for easier -// configuring between trailingSlash: true/false -export function modifyRouteRegex(regex: string, restrictedPaths?: string[]) { - if (restrictedPaths) { - regex = regex.replace( - /\^/, - `^(?!${restrictedPaths - .map((path) => path.replace(/\//g, '\\/')) - .join('|')})` - ) - } - regex = regex.replace(/\$$/, '(?:\\/)?$') - return regex -} - function checkRedirect(route: Redirect): { invalidParts: string[] hadInvalidStatus: boolean diff --git a/packages/next/lib/redirect-status.ts b/packages/next/lib/redirect-status.ts new file mode 100644 index 000000000000..54ecf4816491 --- /dev/null +++ b/packages/next/lib/redirect-status.ts @@ -0,0 +1,30 @@ +import { PERMANENT_REDIRECT_STATUS } from '../shared/lib/constants' +import { TEMPORARY_REDIRECT_STATUS } from '../shared/lib/constants' + +export const allowedStatusCodes = new Set([301, 302, 303, 307, 308]) + +export function getRedirectStatus(route: { + statusCode?: number + permanent?: boolean +}): number { + return ( + route.statusCode || + (route.permanent ? PERMANENT_REDIRECT_STATUS : TEMPORARY_REDIRECT_STATUS) + ) +} + +// for redirects we restrict matching /_next and for all routes +// we add an optional trailing slash at the end for easier +// configuring between trailingSlash: true/false +export function modifyRouteRegex(regex: string, restrictedPaths?: string[]) { + if (restrictedPaths) { + regex = regex.replace( + /\^/, + `^(?!${restrictedPaths + .map((path) => path.replace(/\//g, '\\/')) + .join('|')})` + ) + } + regex = regex.replace(/\$$/, '(?:\\/)?$') + return regex +} diff --git a/packages/next/package.json b/packages/next/package.json index 0acc58eedc28..bf094441346c 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -70,7 +70,7 @@ ] }, "dependencies": { - "@next/env": "12.2.5-canary.0", + "@next/env": "12.2.5-canary.1", "@swc/helpers": "0.4.3", "caniuse-lite": "^1.0.30001332", "postcss": "8.4.14", @@ -116,15 +116,15 @@ "@babel/runtime": "7.15.4", "@babel/traverse": "7.18.0", "@babel/types": "7.18.0", - "@edge-runtime/primitives": "1.1.0-beta.25", + "@edge-runtime/primitives": "1.1.0-beta.26", "@hapi/accept": "5.0.2", "@napi-rs/cli": "2.7.0", "@napi-rs/triples": "1.1.0", - "@next/polyfill-module": "12.2.5-canary.0", - "@next/polyfill-nomodule": "12.2.5-canary.0", - "@next/react-dev-overlay": "12.2.5-canary.0", - "@next/react-refresh-utils": "12.2.5-canary.0", - "@next/swc": "12.2.5-canary.0", + "@next/polyfill-module": "12.2.5-canary.1", + "@next/polyfill-nomodule": "12.2.5-canary.1", + "@next/react-dev-overlay": "12.2.5-canary.1", + "@next/react-refresh-utils": "12.2.5-canary.1", + "@next/swc": "12.2.5-canary.1", "@segment/ajv-human-errors": "2.1.2", "@taskr/clear": "1.1.0", "@taskr/esnext": "1.1.0", @@ -194,7 +194,7 @@ "debug": "4.1.1", "devalue": "2.0.1", "domain-browser": "4.19.0", - "edge-runtime": "1.1.0-beta.25", + "edge-runtime": "1.1.0-beta.26", "events": "3.3.0", "find-cache-dir": "3.3.1", "find-up": "4.1.0", @@ -257,7 +257,7 @@ "string-hash": "1.1.3", "string_decoder": "1.3.0", "strip-ansi": "6.0.0", - "styled-jsx": "5.0.2", + "styled-jsx": "5.0.3", "tar": "6.1.11", "taskr": "1.1.0", "terser": "5.14.1", diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 072d714c3161..ea34ac3d337e 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -1,4 +1,4 @@ -import { __ApiPreviewProps } from './api-utils' +import type { __ApiPreviewProps } from './api-utils' import type { CustomRoutes } from '../lib/load-custom-routes' import type { DomainLocale } from './config' import type { DynamicRoutes, PageChecker, Route } from './router' @@ -27,7 +27,7 @@ import type { PayloadOptions } from './send-payload' import { join, resolve } from '../shared/lib/isomorphic/path' import { parse as parseQs } from 'querystring' import { format as formatUrl, parse as parseUrl } from 'url' -import { getRedirectStatus } from '../lib/load-custom-routes' +import { getRedirectStatus } from '../lib/redirect-status' import { NEXT_BUILTIN_DOCUMENT, NEXT_CLIENT_SSR_ENTRY_SUFFIX, @@ -119,6 +119,10 @@ export interface Options { * The port the server is running behind */ port?: number + /** + * The HTTP Server that Next.js is running behind + */ + httpServer?: import('http').Server } export interface BaseRequestHandler { @@ -617,7 +621,7 @@ export default abstract class Server { } } - if (defaultLocale) { + if (!this.minimalMode && defaultLocale) { const redirect = getLocaleRedirect({ defaultLocale, domainLocale, @@ -665,6 +669,12 @@ export default abstract class Server { return this.handleRequest.bind(this) } + protected async handleUpgrade( + _req: BaseNextRequest, + _socket: any, + _head?: any + ): Promise {} + public setAssetPrefix(prefix?: string): void { this.renderOpts.assetPrefix = prefix ? prefix.replace(/\/$/, '') : '' } diff --git a/packages/next/server/config-shared.ts b/packages/next/server/config-shared.ts index 416fc7cb01da..a41f84c65a40 100644 --- a/packages/next/server/config-shared.ts +++ b/packages/next/server/config-shared.ts @@ -1,6 +1,6 @@ import os from 'os' import type { webpack5 } from 'next/dist/compiled/webpack/webpack' -import { Header, Redirect, Rewrite } from '../lib/load-custom-routes' +import type { Header, Redirect, Rewrite } from '../lib/load-custom-routes' import { ImageConfig, ImageConfigComplete, diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index 996eca68a0e3..ce3c86860b25 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -1,10 +1,12 @@ +import type { webpack5 } from 'next/dist/compiled/webpack/webpack' +import type { NextConfigComplete } from '../config-shared' +import type { CustomRoutes } from '../../lib/load-custom-routes' import { getOverlayMiddleware } from 'next/dist/compiled/@next/react-dev-overlay/dist/middleware' import { IncomingMessage, ServerResponse } from 'http' import { WebpackHotMiddleware } from './hot-middleware' import { join, relative, isAbsolute } from 'path' import { UrlObject } from 'url' import { webpack, StringXor } from 'next/dist/compiled/webpack/webpack' -import type { webpack5 } from 'next/dist/compiled/webpack/webpack' import { createEntrypoints, createPagesMapping, @@ -40,8 +42,6 @@ import { withoutRSCExtensions, isMiddlewareFilename, } from '../../build/utils' -import { NextConfigComplete } from '../config-shared' -import { CustomRoutes } from '../../lib/load-custom-routes' import { DecodeError } from '../../shared/lib/utils' import { Span, trace } from '../../trace' import { getProperError } from '../../lib/is-error' diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 20048ba4f86f..08057c149f69 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -83,10 +83,6 @@ const ReactDevOverlay = (props: any) => { } export interface Options extends ServerOptions { - /** - * The HTTP Server that Next.js is running behind - */ - httpServer?: HTTPServer /** * Tells of Next.js is running from the `next dev` command */ @@ -629,6 +625,8 @@ export default class DevServer extends Server { ) ) { this.hotReloader?.onHMR(req, socket, head) + } else { + this.handleUpgrade(req, socket, head) } }) } diff --git a/packages/next/server/lib/start-server.ts b/packages/next/server/lib/start-server.ts index f01432504f06..4308a93db34f 100644 --- a/packages/next/server/lib/start-server.ts +++ b/packages/next/server/lib/start-server.ts @@ -39,6 +39,14 @@ export function startServer(opts: StartServerOptions) { } }) + let upgradeHandler: any + + if (!opts.dev) { + server.on('upgrade', (req, socket, upgrade) => { + upgradeHandler(req, socket, upgrade) + }) + } + server.on('listening', () => { const addr = server.address() const hostname = @@ -55,6 +63,7 @@ export function startServer(opts: StartServerOptions) { }) requestHandler = app.getRequestHandler() + upgradeHandler = app.getUpgradeHandler() resolve(app) }) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 209e93d35890..7f0db17c2c92 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -54,6 +54,7 @@ import { apiResolver } from './api-utils/node' import { RenderOpts, renderToHTML } from './render' import { renderToHTMLOrFlight as appRenderToHTMLOrFlight } from './app-render' import { ParsedUrl, parseUrl } from '../shared/lib/router/utils/parse-url' +import { parse as nodeParseUrl } from 'url' import * as Log from '../build/output/log' import loadRequireHook from '../build/webpack/require-hook' @@ -501,10 +502,15 @@ export default class NextNodeServer extends BaseServer { } } + protected async handleUpgrade(req: NodeNextRequest, socket: any, head: any) { + await this.router.execute(req, socket, nodeParseUrl(req.url, true), head) + } + protected async proxyRequest( req: NodeNextRequest, res: NodeNextResponse, - parsedUrl: ParsedUrl + parsedUrl: ParsedUrl, + upgradeHead?: any ) { const { query } = parsedUrl delete (parsedUrl as any).query @@ -516,27 +522,46 @@ export default class NextNodeServer extends BaseServer { changeOrigin: true, ignorePath: true, xfwd: true, - proxyTimeout: 30_000, // limit proxying to 30 seconds + ws: true, + // we limit proxy requests to 30s by default, in development + // we don't time out WebSocket requests to allow proxying + proxyTimeout: upgradeHead && this.renderOpts.dev ? undefined : 30_000, }) await new Promise((proxyResolve, proxyReject) => { let finished = false - proxy.on('proxyReq', (proxyReq) => { - proxyReq.on('close', () => { - if (!finished) { - finished = true - proxyResolve(true) - } - }) - }) proxy.on('error', (err) => { + console.error(`Failed to proxy ${target}`, err) if (!finished) { finished = true proxyReject(err) } }) - proxy.web(req.originalRequest, res.originalResponse) + + // if upgrade head is present treat as WebSocket request + if (upgradeHead) { + proxy.on('proxyReqWs', (proxyReq) => { + proxyReq.on('close', () => { + if (!finished) { + finished = true + proxyResolve(true) + } + }) + }) + proxy.ws(req as any as IncomingMessage, res, upgradeHead) + proxyResolve(true) + } else { + proxy.on('proxyReq', (proxyReq) => { + proxyReq.on('close', () => { + if (!finished) { + finished = true + proxyResolve(true) + } + }) + }) + proxy.web(req.originalRequest, res.originalResponse) + } }) return { @@ -989,7 +1014,7 @@ export default class NextNodeServer extends BaseServer { matchesLocale: true, matchesLocaleAPIRoutes: true, matchesTrailingSlash: true, - fn: async (req, res, params, parsedUrl) => { + fn: async (req, res, params, parsedUrl, upgradeHead) => { const { newUrl, parsedDestination } = prepareDestination({ appendParamsToQuery: true, destination: rewriteRoute.destination, @@ -1002,7 +1027,8 @@ export default class NextNodeServer extends BaseServer { return this.proxyRequest( req as NodeNextRequest, res as NodeNextResponse, - parsedDestination + parsedDestination, + upgradeHead ) } diff --git a/packages/next/server/next.ts b/packages/next/server/next.ts index a6f3bbc0581b..643af922ab70 100644 --- a/packages/next/server/next.ts +++ b/packages/next/server/next.ts @@ -62,6 +62,15 @@ export class NextServer { } } + getUpgradeHandler() { + return async (req: IncomingMessage, socket: any, head: any) => { + const server = await this.getServer() + // @ts-expect-error we mark this as protected so it + // causes an error here + return server.handleUpgrade.apply(server, [req, socket, head]) + } + } + setAssetPrefix(assetPrefix: string) { if (this.server) { this.server.setAssetPrefix(assetPrefix) diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 42bf8bf58490..1bc29fbc3361 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -64,10 +64,7 @@ import { HtmlContext } from '../shared/lib/html-context' import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' import { getRequestMeta, NextParsedUrlQuery } from './request-meta' -import { - allowedStatusCodes, - getRedirectStatus, -} from '../lib/load-custom-routes' +import { allowedStatusCodes, getRedirectStatus } from '../lib/redirect-status' import RenderResult from './render-result' import isError from '../lib/is-error' import { diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index 1159d08e21d6..f06b4443249a 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -39,7 +39,8 @@ export type Route = { req: BaseNextRequest, res: BaseNextResponse, params: Params, - parsedUrl: NextUrlWithParsedQuery + parsedUrl: NextUrlWithParsedQuery, + upgradeHead?: any ) => Promise | RouteResult } @@ -130,7 +131,8 @@ export default class Router { async execute( req: BaseNextRequest, res: BaseNextResponse, - parsedUrl: NextUrlWithParsedQuery + parsedUrl: NextUrlWithParsedQuery, + upgradeHead?: any ): Promise { if (this.seenRequests.has(req)) { throw new Error( @@ -306,6 +308,11 @@ export default class Router { ] for (const testRoute of allRoutes) { + // only process rewrites for upgrade request + if (upgradeHead && testRoute.type !== 'rewrite') { + continue + } + const originalPathname = parsedUrlUpdated.pathname as string const pathnameInfo = getNextPathnameInfo(originalPathname, { nextConfig: this.nextConfig, @@ -388,7 +395,8 @@ export default class Router { req, res, newParams, - parsedUrlUpdated + parsedUrlUpdated, + upgradeHead ) if (result.finished) { diff --git a/packages/next/server/server-route-utils.ts b/packages/next/server/server-route-utils.ts index eecfcb14fff7..c21f07a98bcf 100644 --- a/packages/next/server/server-route-utils.ts +++ b/packages/next/server/server-route-utils.ts @@ -9,7 +9,7 @@ import type { Route } from './router' import type { BaseNextRequest } from './base-http' import type { ParsedUrlQuery } from 'querystring' -import { getRedirectStatus, modifyRouteRegex } from '../lib/load-custom-routes' +import { getRedirectStatus, modifyRouteRegex } from '../lib/redirect-status' import { getPathMatch } from '../shared/lib/router/utils/path-match' import { compileNonPath, diff --git a/packages/next/shared/lib/router/utils/resolve-rewrites.ts b/packages/next/shared/lib/router/utils/resolve-rewrites.ts index 6d612aaaa30a..66bd11d09e43 100644 --- a/packages/next/shared/lib/router/utils/resolve-rewrites.ts +++ b/packages/next/shared/lib/router/utils/resolve-rewrites.ts @@ -1,7 +1,7 @@ import type { ParsedUrlQuery } from 'querystring' +import type { Rewrite } from '../../../../lib/load-custom-routes' import { getPathMatch } from './path-match' import { matchHas, prepareDestination } from './prepare-destination' -import { Rewrite } from '../../../../lib/load-custom-routes' import { removeTrailingSlash } from './remove-trailing-slash' import { normalizeLocalePath } from '../../i18n/normalize-locale-path' import { removeBasePath } from '../../../../client/remove-base-path' diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index 198100f9a0cb..67f660129e6e 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -44,7 +44,7 @@ export async function copy_regenerator_runtime(task, opts) { // eslint-disable-next-line camelcase export async function copy_styled_jsx_assets(task, opts) { - // we copy the styled-jsx types so that we can reference them + // we copy the styled-jsx assets and types so that we can reference them // in the next-env.d.ts file so it doesn't matter if the styled-jsx // package is hoisted out of Next.js' node_modules or not const styledJsxPath = dirname(require.resolve('styled-jsx/package.json')) @@ -53,33 +53,22 @@ export async function copy_styled_jsx_assets(task, opts) { cwd: styledJsxPath, }) const outputDir = join(__dirname, 'dist/styled-jsx') - let typeReferences = `` + // Separate type files into different folders to avoid conflicts between + // dev dep `styled-jsx` and `next/dist/styled-jsx` for duplicated declare modules + const typesDir = join(outputDir, 'types') + let typeReferences = '' await fs.ensureDir(outputDir) + await fs.ensureDir(typesDir) for (const file of typeFiles) { const fileNoExt = file.replace(/\.d\.ts/, '') const content = await fs.readFile(join(styledJsxPath, file), 'utf8') - const exportsIndex = content.indexOf('export') - - let replacedContent = - `${content.substring(0, exportsIndex)}\n` + - `declare module 'styled-jsx${ - file === 'index.d.ts' ? '' : '/' + fileNoExt - }' { - ${content.substring(exportsIndex)} - }` - if (file === 'index.d.ts') { - replacedContent = replacedContent - .replace(/export function StyleRegistry/g, 'export function IRegistry') - .replace(/StyleRegistry/g, 'IStyleRegistry') - .replace(/IRegistry/g, 'Registry') - } - await fs.writeFile(join(outputDir, file), replacedContent) + await fs.writeFile(join(typesDir, file), content) typeReferences += `/// \n` } - await fs.writeFile(join(outputDir, 'global.d.ts'), typeReferences) + await fs.writeFile(join(typesDir, 'global.d.ts'), typeReferences) for (const file of jsFiles) { const content = await fs.readFile(join(styledJsxPath, file), 'utf8') @@ -1806,7 +1795,12 @@ export async function path_to_regexp(task, opts) { export async function precompile(task, opts) { await task.parallel( - ['browser_polyfills', 'path_to_regexp', 'copy_ncced'], + [ + 'browser_polyfills', + 'path_to_regexp', + 'copy_ncced', + 'copy_styled_jsx_assets', + ], opts ) } @@ -1965,7 +1959,6 @@ export async function compile(task, opts) { // we compile this each time so that fresh runtime data is pulled // before each publish 'ncc_amp_optimizer', - 'copy_styled_jsx_assets', ], opts ) diff --git a/packages/next/types/misc.d.ts b/packages/next/types/misc.d.ts index 02a3d342988c..e13257f7bee3 100644 --- a/packages/next/types/misc.d.ts +++ b/packages/next/types/misc.d.ts @@ -18,11 +18,6 @@ declare module 'next/dist/compiled/@next/react-refresh-utils/dist/ReactRefreshWe export = m } -declare module 'next/dist/styled-jsx' { - import m from 'styled-jsx' - export = m -} - declare module 'next/dist/compiled/node-html-parser' { export * from 'node-html-parser' } @@ -289,6 +284,11 @@ declare module 'next/dist/compiled/postcss-scss' { import m from 'postcss-scss' export = m } +declare module 'next/dist/styled-jsx' { + import m from 'styled-jsx' + export = m +} + declare module 'next/dist/compiled/text-table' { function textTable( rows: Array>, diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index 22ae6b6fb596..984a248eeb72 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-dev-overlay", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "description": "A development-only overlay for developing React applications.", "repository": { "url": "vercel/next.js", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 1ab4b07f0894..fccfb253a4a0 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "12.2.5-canary.0", + "version": "12.2.5-canary.1", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b1b29baf8d4b..03dc997f2ab2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: '@babel/plugin-proposal-object-rest-spread': 7.14.7 '@babel/preset-flow': 7.14.5 '@babel/preset-react': 7.14.5 - '@edge-runtime/jest-environment': 1.1.0-beta.25 + '@edge-runtime/jest-environment': 1.1.0-beta.26 '@fullhuman/postcss-purgecss': 1.3.0 '@mdx-js/loader': 0.18.0 '@next/bundle-analyzer': workspace:* @@ -173,7 +173,7 @@ importers: '@babel/plugin-proposal-object-rest-spread': 7.14.7_@babel+core@7.18.0 '@babel/preset-flow': 7.14.5_@babel+core@7.18.0 '@babel/preset-react': 7.14.5_@babel+core@7.18.0 - '@edge-runtime/jest-environment': 1.1.0-beta.25 + '@edge-runtime/jest-environment': 1.1.0-beta.26 '@fullhuman/postcss-purgecss': 1.3.0 '@mdx-js/loader': 0.18.0_uuaxwgga6hqycsez5ok7v2wg4i '@next/bundle-analyzer': link:packages/next-bundle-analyzer @@ -364,7 +364,7 @@ importers: packages/eslint-config-next: specifiers: - '@next/eslint-plugin-next': 12.2.5-canary.0 + '@next/eslint-plugin-next': 12.2.5-canary.1 '@rushstack/eslint-patch': ^1.1.3 '@typescript-eslint/parser': ^5.21.0 eslint-import-resolver-node: ^0.3.6 @@ -416,16 +416,16 @@ importers: '@babel/runtime': 7.15.4 '@babel/traverse': 7.18.0 '@babel/types': 7.18.0 - '@edge-runtime/primitives': 1.1.0-beta.25 + '@edge-runtime/primitives': 1.1.0-beta.26 '@hapi/accept': 5.0.2 '@napi-rs/cli': 2.7.0 '@napi-rs/triples': 1.1.0 - '@next/env': 12.2.5-canary.0 - '@next/polyfill-module': 12.2.5-canary.0 - '@next/polyfill-nomodule': 12.2.5-canary.0 - '@next/react-dev-overlay': 12.2.5-canary.0 - '@next/react-refresh-utils': 12.2.5-canary.0 - '@next/swc': 12.2.5-canary.0 + '@next/env': 12.2.5-canary.1 + '@next/polyfill-module': 12.2.5-canary.1 + '@next/polyfill-nomodule': 12.2.5-canary.1 + '@next/react-dev-overlay': 12.2.5-canary.1 + '@next/react-refresh-utils': 12.2.5-canary.1 + '@next/swc': 12.2.5-canary.1 '@segment/ajv-human-errors': 2.1.2 '@swc/helpers': 0.4.3 '@taskr/clear': 1.1.0 @@ -497,7 +497,7 @@ importers: debug: 4.1.1 devalue: 2.0.1 domain-browser: 4.19.0 - edge-runtime: 1.1.0-beta.25 + edge-runtime: 1.1.0-beta.26 events: 3.3.0 find-cache-dir: 3.3.1 find-up: 4.1.0 @@ -561,7 +561,7 @@ importers: string_decoder: 1.3.0 string-hash: 1.1.3 strip-ansi: 6.0.0 - styled-jsx: 5.0.2 + styled-jsx: 5.0.3 tar: 6.1.11 taskr: 1.1.0 terser: 5.14.1 @@ -609,7 +609,7 @@ importers: '@babel/runtime': 7.15.4 '@babel/traverse': 7.18.0 '@babel/types': 7.18.0 - '@edge-runtime/primitives': 1.1.0-beta.25 + '@edge-runtime/primitives': 1.1.0-beta.26 '@hapi/accept': 5.0.2 '@napi-rs/cli': 2.7.0 '@napi-rs/triples': 1.1.0 @@ -687,7 +687,7 @@ importers: debug: 4.1.1 devalue: 2.0.1 domain-browser: 4.19.0 - edge-runtime: 1.1.0-beta.25 + edge-runtime: 1.1.0-beta.26 events: 3.3.0 find-cache-dir: 3.3.1 find-up: 4.1.0 @@ -750,7 +750,7 @@ importers: string_decoder: 1.3.0 string-hash: 1.1.3 strip-ansi: 6.0.0 - styled-jsx: 5.0.2_@babel+core@7.18.0 + styled-jsx: 5.0.3_@babel+core@7.18.0 tar: 6.1.11 taskr: 1.1.0 terser: 5.14.1 @@ -3218,14 +3218,14 @@ packages: protobufjs: 6.11.2 dev: true - /@edge-runtime/format/1.1.0-beta.25: - resolution: {integrity: sha512-7fzTXgfVzcWs6T8ePVyogbVU67TbXpDHhgop9yP9stsRlejJjD2bDm/jDwX9XAfQdET5gVaKDiYc0wkp9E4gig==} + /@edge-runtime/format/1.1.0-beta.26: + resolution: {integrity: sha512-05wywSsBZVE1iMezvYXW1ZMak6y2riJPVmEDK9RW9BECazykJtYOOQ+GG+oVaicNfxh2YtQTQD/WaI/4K3yKWA==} dev: true - /@edge-runtime/jest-environment/1.1.0-beta.25: - resolution: {integrity: sha512-tOjxI0/8m0beqEiOXmDzPCqpycUiMLvIV1PoQ9zb5qcxJqF8AcmZMRqICfdqnWo+P3qJIr8YeZSG31lz5EaJfw==} + /@edge-runtime/jest-environment/1.1.0-beta.26: + resolution: {integrity: sha512-NnnYjF3Kaw83Vrkoy0lpuZCk49VjR5Ruo5XDjZ9dKqslA6lNVBkirTDvQrQBYSTaZM8lu3Ed39o50zH2Ua4lTw==} dependencies: - '@edge-runtime/vm': 1.1.0-beta.25 + '@edge-runtime/vm': 1.1.0-beta.26 '@jest/environment': 28.1.3 '@jest/fake-timers': 28.1.3 '@jest/types': 28.1.3 @@ -3233,14 +3233,14 @@ packages: jest-util: 28.1.3 dev: true - /@edge-runtime/primitives/1.1.0-beta.25: - resolution: {integrity: sha512-+lKore2sAuGD2AMa2GZviLMHKfFmw5k2BzhvyatKPuJ/frIFpb1OdluxGHfqmVBtNIJwnn7IJoJd9jm8r/6Flg==} + /@edge-runtime/primitives/1.1.0-beta.26: + resolution: {integrity: sha512-SsbXbw2+bAHbcjAmT+e5WoADmU/yDO5glNFkYd/971Zo26cJoW/1W3n4IMm5gOtyvLHbezSRKt8t5GteX0wYdA==} dev: true - /@edge-runtime/vm/1.1.0-beta.25: - resolution: {integrity: sha512-xDre6a3L0KXsxQxuZaXw5ukyGh5v7k1E7XL5ooxBbaHz+5GtvCidSZ3sPvFDzetlKq7eBT4ztg4RTkHbCUclDA==} + /@edge-runtime/vm/1.1.0-beta.26: + resolution: {integrity: sha512-hxWtmuO13zgNkM3zHvRENfMeavM+PAKSoHhvzt+sHjSothxGlA06XXN38t/NT6LD4ND8p8FmPJ70+fTptL4a/A==} dependencies: - '@edge-runtime/primitives': 1.1.0-beta.25 + '@edge-runtime/primitives': 1.1.0-beta.26 dev: true /@emotion/is-prop-valid/0.8.8: @@ -9980,12 +9980,12 @@ packages: safe-buffer: 5.2.0 dev: true - /edge-runtime/1.1.0-beta.25: - resolution: {integrity: sha512-2yC0hqnMis0t2zIFCWsV7uu8hEhUZDgVszZyXS/C4719r1km2FkL5kWk+6ktVD4A6fUTnzTrYPq+fzTxIt7X2A==} + /edge-runtime/1.1.0-beta.26: + resolution: {integrity: sha512-2JZlxpISnRv3p0yh6Oo9QZF06lWcgJH0RMxVbJ5Az0Grde7B1PLw34oCIcOu23Zu7Icn4Lh2wojA6riq3jyDqg==} hasBin: true dependencies: - '@edge-runtime/format': 1.1.0-beta.25 - '@edge-runtime/vm': 1.1.0-beta.25 + '@edge-runtime/format': 1.1.0-beta.26 + '@edge-runtime/vm': 1.1.0-beta.26 exit-hook: 2.2.1 http-status: 1.5.2 mri: 1.2.0 @@ -20560,8 +20560,8 @@ packages: postcss-load-plugins: 2.3.0 dev: true - /styled-jsx/5.0.2_@babel+core@7.18.0: - resolution: {integrity: sha512-LqPQrbBh3egD57NBcHET4qcgshPks+yblyhPlH2GY8oaDgKs8SK4C3dBh3oSJjgzJ3G5t1SYEZGHkP+QEpX9EQ==} + /styled-jsx/5.0.3_@babel+core@7.18.0: + resolution: {integrity: sha512-v82oihjxFj2WJtQiodZIDjJpnmVcE71HTCVylxdcQHU0ocnI0rGhJ0+5A3311NMQUx0KFJ+18RSHNlfIgcSU8g==} engines: {node: '>= 12.0.0'} peerDependencies: '@babel/core': '*' diff --git a/test/e2e/middleware-matcher/index.test.ts b/test/e2e/middleware-matcher/index.test.ts index d76a1b5d8ed0..812e19c69475 100644 --- a/test/e2e/middleware-matcher/index.test.ts +++ b/test/e2e/middleware-matcher/index.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable jest/no-identical-title */ import { createNext, FileRef } from 'e2e-utils' import { NextInstance } from 'test/lib/next-modes/base' import { check, fetchViaHTTP } from 'next-test-utils' @@ -27,7 +28,7 @@ describe('Middleware can set the matcher in its config', () => { expect(await response.text()).toContain('This should run the middleware') }) - it('adds the header for a matched data path', async () => { + it('adds the header for a matched data path (with header)', async () => { const response = await fetchViaHTTP( next.url, `/_next/data/${next.buildId}/with-middleware.json`, @@ -42,6 +43,19 @@ describe('Middleware can set the matcher in its config', () => { expect(response.headers.get('X-From-Middleware')).toBe('true') }) + it('adds the header for a matched data path (without header)', async () => { + const response = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/with-middleware.json` + ) + expect(await response.json()).toMatchObject({ + pageProps: { + message: 'Hello, cruel world.', + }, + }) + expect(response.headers.get('X-From-Middleware')).toBe('true') + }) + it('adds the header for another matched path', async () => { const response = await fetchViaHTTP(next.url, '/another-middleware') expect(response.headers.get('X-From-Middleware')).toBe('true') @@ -195,7 +209,7 @@ describe('using a single matcher', () => { expect(response.headers.get('X-From-Middleware')).toBe('true') }) - it('adds the headers for a matched data path', async () => { + it('adds the headers for a matched data path (with header)', async () => { const response = await fetchViaHTTP( next.url, `/_next/data/${next.buildId}/middleware/works.json`, @@ -210,6 +224,19 @@ describe('using a single matcher', () => { expect(response.headers.get('X-From-Middleware')).toBe('true') }) + it('adds the header for a matched data path (without header)', async () => { + const response = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/middleware/works.json` + ) + expect(await response.json()).toMatchObject({ + pageProps: { + message: 'Hello from /middleware/works', + }, + }) + expect(response.headers.get('X-From-Middleware')).toBe('true') + }) + it('does not add the header for an unmatched path', async () => { const response = await fetchViaHTTP(next.url, '/about/me') expect(await response.text()).toContain('Hello from /about/me') @@ -217,7 +244,88 @@ describe('using a single matcher', () => { }) }) -describe('using a single matcher with i18n', () => { +describe('using root matcher', () => { + let next: NextInstance + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` + export function getStaticProps() { + return { + props: { + message: 'hello world' + } + } + } + + export default function Home({ message }) { + return
Hi there!
+ } + `, + 'middleware.js': ` + import { NextResponse } from 'next/server' + export default (req) => { + const res = NextResponse.next(); + res.headers.set('X-From-Middleware', 'true'); + return res; + } + + export const config = { matcher: '/' }; + `, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('adds the header to the /', async () => { + const response = await fetchViaHTTP(next.url, '/') + expect(response.status).toBe(200) + expect(Object.fromEntries(response.headers)).toMatchObject({ + 'x-from-middleware': 'true', + }) + }) + + it('adds the header to the /index', async () => { + const response = await fetchViaHTTP(next.url, '/index') + expect(Object.fromEntries(response.headers)).toMatchObject({ + 'x-from-middleware': 'true', + }) + }) + + it('adds the header for a matched data path (with header)', async () => { + const response = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/index.json`, + undefined, + { headers: { 'x-nextjs-data': '1' } } + ) + expect(await response.json()).toMatchObject({ + pageProps: { + message: 'hello world', + }, + }) + expect(response.headers.get('X-From-Middleware')).toBe('true') + }) + + it('adds the header for a matched data path (without header)', async () => { + const response = await fetchViaHTTP( + next.url, + `/_next/data/${next.buildId}/index.json` + ) + expect(await response.json()).toMatchObject({ + pageProps: { + message: 'hello world', + }, + }) + expect(response.headers.get('X-From-Middleware')).toBe('true') + }) +}) + +describe.each([ + { title: '' }, + { title: ' and trailingSlash', trailingSlash: true }, +])('using a single matcher with i18n$title', ({ trailingSlash }) => { let next: NextInstance beforeAll(async () => { next = await createNext({ @@ -254,6 +362,7 @@ describe('using a single matcher with i18n', () => { `, 'next.config.js': ` module.exports = { + ${trailingSlash ? 'trailingSlash: true,' : ''} i18n: { localeDetection: false, locales: ['es', 'en'], @@ -276,6 +385,15 @@ describe('using a single matcher with i18n', () => { expect(res2.headers.get('X-From-Middleware')).toBe('true') }) + it('adds the header for a mathed root path with /index', async () => { + const res1 = await fetchViaHTTP(next.url, `/index`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/es/index`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') + }) + it(`adds the headers for a matched data path`, async () => { const res1 = await fetchViaHTTP( next.url, @@ -290,8 +408,7 @@ describe('using a single matcher with i18n', () => { const res2 = await fetchViaHTTP( next.url, `/_next/data/${next.buildId}/es.json`, - undefined, - { headers: { 'x-nextjs-data': '1' } } + undefined ) expect(await res2.json()).toMatchObject({ pageProps: { message: `(es) Hello from /` }, @@ -306,12 +423,17 @@ describe('using a single matcher with i18n', () => { }) }) -describe('using a single matcher with i18n and basePath', () => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` +describe.each([ + { title: '' }, + { title: ' and trailingSlash', trailingSlash: true }, +])( + 'using a single matcher with i18n and basePath$title', + ({ trailingSlash }) => { + let next: NextInstance + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/index.js': ` export default function Page({ message }) { return

root page

@@ -322,7 +444,7 @@ describe('using a single matcher with i18n and basePath', () => { props: { message: \`(\${locale}) Hello from /\` } }) `, - 'pages/[...route].js': ` + 'pages/[...route].js': ` export default function Page({ message }) { return

catchall page

@@ -333,7 +455,7 @@ describe('using a single matcher with i18n and basePath', () => { props: { message: \`(\${locale}) Hello from /\` + params.route.join("/") } }) `, - 'middleware.js': ` + 'middleware.js': ` import { NextResponse } from 'next/server' export const config = { matcher: '/' }; export default (req) => { @@ -342,8 +464,9 @@ describe('using a single matcher with i18n and basePath', () => { return res; } `, - 'next.config.js': ` + 'next.config.js': ` module.exports = { + ${trailingSlash ? 'trailingSlash: true,' : ''} basePath: '/root', i18n: { localeDetection: false, @@ -352,47 +475,57 @@ describe('using a single matcher with i18n and basePath', () => { } } `, - }, - dependencies: {}, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it(`adds the header for a matched path`, async () => { + const res1 = await fetchViaHTTP(next.url, `/root`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/root/es`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') }) - }) - afterAll(() => next.destroy()) - - it(`adds the header for a matched path`, async () => { - const res1 = await fetchViaHTTP(next.url, `/root`) - expect(await res1.text()).toContain(`(en) Hello from /`) - expect(res1.headers.get('X-From-Middleware')).toBe('true') - const res2 = await fetchViaHTTP(next.url, `/root/es`) - expect(await res2.text()).toContain(`(es) Hello from /`) - expect(res2.headers.get('X-From-Middleware')).toBe('true') - }) - it(`adds the headers for a matched data path`, async () => { - const res1 = await fetchViaHTTP( - next.url, - `/root/_next/data/${next.buildId}/en.json`, - undefined, - { headers: { 'x-nextjs-data': '1' } } - ) - expect(await res1.json()).toMatchObject({ - pageProps: { message: `(en) Hello from /` }, + it('adds the header for a mathed root path with /index', async () => { + const res1 = await fetchViaHTTP(next.url, `/root/index`) + expect(await res1.text()).toContain(`(en) Hello from /`) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP(next.url, `/root/es/index`) + expect(await res2.text()).toContain(`(es) Hello from /`) + expect(res2.headers.get('X-From-Middleware')).toBe('true') }) - expect(res1.headers.get('X-From-Middleware')).toBe('true') - const res2 = await fetchViaHTTP( - next.url, - `/root/_next/data/${next.buildId}/es.json`, - undefined, - { headers: { 'x-nextjs-data': '1' } } - ) - expect(await res2.json()).toMatchObject({ - pageProps: { message: `(es) Hello from /` }, + + it(`adds the headers for a matched data path`, async () => { + const res1 = await fetchViaHTTP( + next.url, + `/root/_next/data/${next.buildId}/en.json`, + undefined, + { headers: { 'x-nextjs-data': '1' } } + ) + expect(await res1.json()).toMatchObject({ + pageProps: { message: `(en) Hello from /` }, + }) + expect(res1.headers.get('X-From-Middleware')).toBe('true') + const res2 = await fetchViaHTTP( + next.url, + `/root/_next/data/${next.buildId}/es.json`, + undefined, + { headers: { 'x-nextjs-data': '1' } } + ) + expect(await res2.json()).toMatchObject({ + pageProps: { message: `(es) Hello from /` }, + }) + expect(res2.headers.get('X-From-Middleware')).toBe('true') }) - expect(res2.headers.get('X-From-Middleware')).toBe('true') - }) - it(`does not add the header for an unmatched path`, async () => { - const response = await fetchViaHTTP(next.url, `/root/about/me`) - expect(await response.text()).toContain('Hello from /about/me') - expect(response.headers.get('X-From-Middleware')).toBeNull() - }) -}) + it(`does not add the header for an unmatched path`, async () => { + const response = await fetchViaHTTP(next.url, `/root/about/me`) + expect(await response.text()).toContain('Hello from /about/me') + expect(response.headers.get('X-From-Middleware')).toBeNull() + }) + } +) diff --git a/test/integration/custom-routes/next.config.js b/test/integration/custom-routes/next.config.js index 3f9fc9bfcd23..1a06c9778588 100644 --- a/test/integration/custom-routes/next.config.js +++ b/test/integration/custom-routes/next.config.js @@ -12,6 +12,11 @@ module.exports = { }, ] : []), + { + source: '/to-websocket', + destination: + 'http://localhost:__EXTERNAL_PORT__/_next/webpack-hmr?page=/about', + }, { source: '/to-nowhere', destination: 'http://localhost:12233', diff --git a/test/integration/custom-routes/test/index.test.js b/test/integration/custom-routes/test/index.test.js index 55b60e85546a..cd5fc57f5ddb 100644 --- a/test/integration/custom-routes/test/index.test.js +++ b/test/integration/custom-routes/test/index.test.js @@ -5,6 +5,7 @@ import url from 'url' import stripAnsi from 'strip-ansi' import fs from 'fs-extra' import { join } from 'path' +import WebSocket from 'ws' import cheerio from 'cheerio' import webdriver from 'next-webdriver' import escapeRegex from 'escape-string-regexp' @@ -39,6 +40,29 @@ let appPort let app const runTests = (isDev = false) => { + it('should successfully rewrite a WebSocket request', async () => { + const messages = [] + const ws = await new Promise((resolve, reject) => { + let socket = new WebSocket(`ws://localhost:${appPort}/to-websocket`) + socket.on('message', (data) => { + messages.push(data.toString()) + }) + socket.on('open', () => resolve(socket)) + socket.on('error', (err) => { + console.error(err) + socket.close() + reject() + }) + }) + + await check( + () => (messages.length > 0 ? 'success' : JSON.stringify(messages)), + 'success' + ) + ws.close() + expect([...externalServerHits]).toEqual(['/_next/webpack-hmr?page=/about']) + }) + it('should not rewrite for _next/data route when a match is found', async () => { const initial = await fetchViaHTTP(appPort, '/overridden/first') expect(initial.status).toBe(200) @@ -1809,6 +1833,11 @@ const runTests = (isDev = false) => { }, ], afterFiles: [ + { + destination: `http://localhost:${externalServerPort}/_next/webpack-hmr?page=/about`, + regex: normalizeRegEx('^\\/to-websocket(?:\\/)?$'), + source: '/to-websocket', + }, { destination: 'http://localhost:12233', regex: normalizeRegEx('^\\/to-nowhere(?:\\/)?$'), @@ -2235,6 +2264,14 @@ describe('Custom routes', () => { const externalHost = req.headers['host'] res.end(`hi ${nextHost} from ${externalHost}`) }) + const wsServer = new WebSocket.Server({ noServer: true }) + + externalServer.on('upgrade', (req, socket, head) => { + externalServerHits.add(req.url) + wsServer.handleUpgrade(req, socket, head, (client) => { + client.send('hello world') + }) + }) await new Promise((resolve, reject) => { externalServer.listen(externalServerPort, (error) => { if (error) return reject(error) @@ -2244,7 +2281,7 @@ describe('Custom routes', () => { nextConfigRestoreContent = await fs.readFile(nextConfigPath, 'utf8') await fs.writeFile( nextConfigPath, - nextConfigRestoreContent.replace(/__EXTERNAL_PORT__/, externalServerPort) + nextConfigRestoreContent.replace(/__EXTERNAL_PORT__/g, externalServerPort) ) }) afterAll(async () => { diff --git a/test/production/fatal-render-errror/app/pages/_app.js b/test/production/fatal-render-errror/app/pages/_app.js new file mode 100644 index 000000000000..904b7838ceeb --- /dev/null +++ b/test/production/fatal-render-errror/app/pages/_app.js @@ -0,0 +1,15 @@ +export default function App({ Component, pageProps }) { + if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined') { + if (!window.renderAttempts) { + window.renderAttempts = 0 + } + window.renderAttempts++ + throw new Error('error in custom _app') + } + return ( + <> +

from _app

+ + + ) +} diff --git a/test/production/fatal-render-errror/app/pages/_error.js b/test/production/fatal-render-errror/app/pages/_error.js new file mode 100644 index 000000000000..9b8ff8ee0587 --- /dev/null +++ b/test/production/fatal-render-errror/app/pages/_error.js @@ -0,0 +1,6 @@ +export default function Error() { + if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined') { + throw new Error('error in custom _app') + } + return
Error encountered!
+} diff --git a/test/production/fatal-render-errror/app/pages/index.js b/test/production/fatal-render-errror/app/pages/index.js new file mode 100644 index 000000000000..08263e34c35f --- /dev/null +++ b/test/production/fatal-render-errror/app/pages/index.js @@ -0,0 +1,3 @@ +export default function Page() { + return

index page

+} diff --git a/test/production/fatal-render-errror/app/pages/with-error.js b/test/production/fatal-render-errror/app/pages/with-error.js new file mode 100644 index 000000000000..2a240431ba18 --- /dev/null +++ b/test/production/fatal-render-errror/app/pages/with-error.js @@ -0,0 +1,6 @@ +export default function Error() { + if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined') { + throw new Error('error in pages/with-error') + } + return
with-error
+} diff --git a/test/production/fatal-render-errror/index.test.ts b/test/production/fatal-render-errror/index.test.ts new file mode 100644 index 000000000000..1037cda593f2 --- /dev/null +++ b/test/production/fatal-render-errror/index.test.ts @@ -0,0 +1,55 @@ +import { createNext, FileRef } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { check, renderViaHTTP, waitFor } from 'next-test-utils' +import webdriver from 'next-webdriver' +import { join } from 'path' + +describe('fatal-render-errror', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: new FileRef(join(__dirname, 'app')), + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('should render page without error correctly', async () => { + const html = await renderViaHTTP(next.url, '/') + expect(html).toContain('index page') + expect(html).toContain('from _app') + }) + + it('should handle fatal error in _app and _error without loop on direct visit', async () => { + const browser = await webdriver(next.url, '/with-error') + + // wait a bit to see if we are rendering multiple times unexpectedly + await waitFor(500) + expect(await browser.eval('window.renderAttempts')).toBeLessThan(10) + + const html = await browser.eval('document.documentElement.innerHTML') + expect(html).not.toContain('from _app') + expect(html).toContain( + 'Application error: a client-side exception has occurred' + ) + }) + + it('should handle fatal error in _app and _error without loop on client-transition', async () => { + const browser = await webdriver(next.url, '/') + await browser.eval('window.renderAttempts = 0') + + await browser.eval('window.next.router.push("/with-error")') + await check(() => browser.eval('location.pathname'), '/with-error') + + // wait a bit to see if we are rendering multiple times unexpectedly + await waitFor(500) + expect(await browser.eval('window.renderAttempts')).toBeLessThan(10) + + const html = await browser.eval('document.documentElement.innerHTML') + expect(html).not.toContain('from _app') + expect(html).toContain( + 'Application error: a client-side exception has occurred' + ) + }) +}) diff --git a/test/production/required-server-files-i18n.test.ts b/test/production/required-server-files-i18n.test.ts index a8316fc530da..17c5df1c80df 100644 --- a/test/production/required-server-files-i18n.test.ts +++ b/test/production/required-server-files-i18n.test.ts @@ -135,6 +135,27 @@ describe('should set-up next', () => { if (server) await killApp(server) }) + it('should not apply locale redirect in minimal mode', async () => { + const res = await fetchViaHTTP(appPort, '/', undefined, { + redirect: 'manual', + headers: { + 'accept-language': 'fr', + }, + }) + expect(res.status).toBe(200) + expect(await res.text()).toContain('index page') + + const resCookie = await fetchViaHTTP(appPort, '/', undefined, { + redirect: 'manual', + headers: { + 'accept-language': 'en', + cookie: 'NEXT_LOCALE=fr', + }, + }) + expect(resCookie.status).toBe(200) + expect(await resCookie.text()).toContain('index page') + }) + it('should output required-server-files manifest correctly', async () => { expect(requiredFilesManifest.version).toBe(1) expect(Array.isArray(requiredFilesManifest.files)).toBe(true)