Repository: vercel/next.js
Pull request: Allow URL schemes that include `+`, `-` or `.`

### What?
This is necessary for schemes such as `flatpak+https`.
An example url would look like `flatpak+https://dl.flathub.org/repo/appstream/net.krafting.Playlifin.flatpakref`

https://github.com/flathub-infra/website/pull/4792

### Why?
To enable schemes with `+` signs in them

### How?
Added the `+` sign to the regex

Changed files:
filename: packages/next/src/client/resolve-href.ts
patch: @@ -34,7 +34,8 @@ export function resolveHref(
 
   // repeated slashes and backslashes in the URL are considered
   // invalid and will never match a Next.js page/file
-  const urlProtoMatch = urlAsString.match(/^[a-zA-Z]{1,}:\/\//)
+  // https://www.rfc-editor.org/rfc/rfc3986.html#section-3.1
+  const urlProtoMatch = urlAsString.match(/^[a-z][a-z0-9+.-]*:\/\//i)
   const urlAsStringNoProto = urlProtoMatch
     ? urlAsString.slice(urlProtoMatch[0].length)
     : urlAsString

filename: test/integration/invalid-href/pages/exotic-href.js
patch: @@ -0,0 +1,14 @@
+import Link from 'next/link'
+
+export default function Page() {
+  return (
+    <>
+      <Link href="HTTPs://google.com">HTTPs</Link>
+      <Link href="flatpak+https://dl.flathub.org/repo/appstream/net.krafting.Playlifin.flatpakref">
+        flatpak+https
+      </Link>
+      <Link href="com.apple.tv://">com.apple.tv</Link>
+      <Link href="itms-apps://">itms-apps</Link>
+    </>
+  )
+}

filename: test/integration/invalid-href/pages/second.js
patch: @@ -12,16 +12,16 @@ export default function Page() {
       id="click-me"
       onClick={(e) => {
         e.preventDefault()
+        // this should throw an error on load since prefetch
+        // receives the invalid href
         router[method](invalidLink)
       }}
     >
       invalid link :o
     </a>
   ) : (
-    // this should throw an error on load since prefetch
-    // receives the invalid href
     <Link href={invalidLink} id="click-me">
-      invalid link :o
+      valid link :o
     </Link>
   )
 }

filename: test/integration/invalid-href/test/index.test.ts
patch: @@ -16,8 +16,8 @@ import cheerio from 'cheerio'
 import webdriver from 'next-webdriver'
 import { join } from 'path'
 
-let app
-let appPort
+let app: Awaited<ReturnType<typeof nextStart>>
+let appPort: number
 const appDir = join(__dirname, '..')
 
 // This test doesn't seem to benefit from retries, let's disable them until the test gets fixed
@@ -98,10 +98,18 @@ describe('Invalid hrefs', () => {
         await noError('/first')
       })
 
-      it('does not show error in production when https://google.com is used as href on Link', async () => {
+      it('does not show error in production when https:// is used in href on Link', async () => {
         await noError('/second')
       })
 
+      it('does not show error in production when exotic protocols are used in href in Link', async () => {
+        const browser = await webdriver(appPort, '/exotic-href')
+
+        expect(
+          (await browser.log()).filter((x) => x.source === 'error')
+        ).toEqual([])
+      })
+
       it('does not show error when internal href is used with external as', async () => {
         await noError('/invalid-relative', true)
       })
@@ -160,10 +168,18 @@ describe('Invalid hrefs', () => {
         await noError('/first')
       })
 
-      it('does not show error when https://google.com is used as href on Link', async () => {
+      it('does not show error when https:// is used as href in Link', async () => {
         await noError('/second')
       })
 
+      it('does not show error when exotic protocols are used in href in Link', async () => {
+        const browser = await webdriver(appPort, '/exotic-href')
+
+        expect(
+          (await browser.log()).filter((x) => x.source === 'error')
+        ).toEqual([])
+      })
+
       // eslint-disable-next-line jest/no-identical-title
       it('shows error when dynamic route mismatch is used on Link', async () => {
         await showsError(