From 6d34cba23f7084a6b9575442a0b027f5583d9ed7 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 6 Nov 2026 19:05:25 +0100 Subject: [PATCH 1/3] test(core): capture more `docusaurus serve` edge cases Co-Authored-By: Claude Opus 5.5 --- .../serve/__tests__/requestHandler.test.ts | 110 ++++++++++++++++++ .../src/commands/serve/__tests__/testUtils.ts | 5 +- 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts b/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts index 895a4a46f608..a26b37e564b6 100644 --- a/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts +++ b/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts @@ -38,14 +38,20 @@ const RequestPaths = [ // Folders with a dot, like a docs version or a package name '/docs/2.x', '/docs/2.x/', + '/docs/2.x/index', + '/docs/2.x/index.html', + '/docs/2.x?query=1', + '/docs/2%2Ex', // Encoded dot '/docs/lodash.debounce', '/docs/lodash.debounce/', // A file with a dot '/docs/1.x', '/docs/1.x/', + '/docs/1.x.html', '/assets/css/styles.css', '/assets/css/styles.css?v=1', + '/assets/css/styles.css/', '/assets/', // Extensions of 1, 3, 4, 5 and 11 characters '/assets/files/main.c', @@ -72,6 +78,7 @@ const RequestPaths = [ '//file', '/%E0%A4%A', // Malformed percent-encoding + '/%00', // Null byte // cspell:ignore Fdocusaurus '/..%2Fdocusaurus.config.js', // Path traversal ]; @@ -119,12 +126,18 @@ describe('createServeRequestHandler', () => { /both/index.html 301 /both/index → 301 /both → 200 both/index.html /docs/2.x 200 docs/2.x/index.html /docs/2.x/ 200 docs/2.x/index.html + /docs/2.x/index 301 /docs/2.x → 200 docs/2.x/index.html + /docs/2.x/index.html 301 /docs/2.x/index → 301 /docs/2.x → 200 docs/2.x/index.html + /docs/2.x?query=1 200 docs/2.x/index.html + /docs/2%2Ex 200 docs/2.x/index.html /docs/lodash.debounce 200 docs/lodash.debounce/index.html /docs/lodash.debounce/ 200 docs/lodash.debounce/index.html /docs/1.x 200 docs/1.x.html /docs/1.x/ 200 docs/1.x.html + /docs/1.x.html 301 /docs/1.x → 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css + /assets/css/styles.css/ 404 404.html /assets/ 404 404.html /assets/files/main.c 200 assets/files/main.c /assets/files/document.pdf 200 assets/files/document.pdf @@ -146,6 +159,7 @@ describe('createServeRequestHandler', () => { /café 200 café.html //file 301 /file → 200 file.html /%E0%A4%A 400 Bad Request + /%00 500 Internal Server Error /..%2Fdocusaurus.config.js 400 Bad Request" `); }); @@ -172,12 +186,18 @@ describe('createServeRequestHandler', () => { /both/index.html 301 /both/index → 301 /both → 301 /both/ → 200 both/index.html /docs/2.x 301 /docs/2.x/ → 200 docs/2.x/index.html /docs/2.x/ 200 docs/2.x/index.html + /docs/2.x/index 301 /docs/2.x → 301 /docs/2.x/ → 200 docs/2.x/index.html + /docs/2.x/index.html 301 /docs/2.x/index → 301 /docs/2.x → 301 /docs/2.x/ → 200 docs/2.x/index.html + /docs/2.x?query=1 301 /docs/2.x/?query=1 → 200 docs/2.x/index.html + /docs/2%2Ex 301 /docs/2%2Ex/ → 200 docs/2.x/index.html /docs/lodash.debounce 301 /docs/lodash.debounce/ → 200 docs/lodash.debounce/index.html /docs/lodash.debounce/ 200 docs/lodash.debounce/index.html /docs/1.x 301 /docs/1.x/ → 200 docs/1.x.html /docs/1.x/ 200 docs/1.x.html + /docs/1.x.html 301 /docs/1.x → 301 /docs/1.x/ → 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css + /assets/css/styles.css/ 404 404.html /assets/ 404 404.html /assets/files/main.c 200 assets/files/main.c /assets/files/document.pdf 200 assets/files/document.pdf @@ -199,6 +219,7 @@ describe('createServeRequestHandler', () => { /café 301 /caf%C3%A9/ → 200 café.html //file 301 /file → 301 /file/ → 200 file.html /%E0%A4%A 301 /%E0%A4%A/ → 400 Bad Request + /%00 301 /%00/ → 500 Internal Server Error /..%2Fdocusaurus.config.js 400 Bad Request" `); }); @@ -225,12 +246,18 @@ describe('createServeRequestHandler', () => { /both/index.html 301 /both/index → 301 /both → 200 both/index.html /docs/2.x 200 docs/2.x/index.html /docs/2.x/ 301 /docs/2.x → 200 docs/2.x/index.html + /docs/2.x/index 301 /docs/2.x → 200 docs/2.x/index.html + /docs/2.x/index.html 301 /docs/2.x/index → 301 /docs/2.x → 200 docs/2.x/index.html + /docs/2.x?query=1 200 docs/2.x/index.html + /docs/2%2Ex 200 docs/2.x/index.html /docs/lodash.debounce 200 docs/lodash.debounce/index.html /docs/lodash.debounce/ 301 /docs/lodash.debounce → 200 docs/lodash.debounce/index.html /docs/1.x 200 docs/1.x.html /docs/1.x/ 301 /docs/1.x → 200 docs/1.x.html + /docs/1.x.html 301 /docs/1.x → 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css + /assets/css/styles.css/ 301 /assets/css/styles.css → 200 assets/css/styles.css /assets/ 301 /assets → 404 404.html /assets/files/main.c 200 assets/files/main.c /assets/files/document.pdf 200 assets/files/document.pdf @@ -252,6 +279,7 @@ describe('createServeRequestHandler', () => { /café 200 café.html //file 301 /file → 200 file.html /%E0%A4%A 400 Bad Request + /%00 500 Internal Server Error /..%2Fdocusaurus.config.js 400 Bad Request" `); }); @@ -280,12 +308,18 @@ describe('createServeRequestHandler', () => { /base/both/index.html 301 /base/both/index → 301 /base/both → 200 both/index.html /base/docs/2.x 200 docs/2.x/index.html /base/docs/2.x/ 200 docs/2.x/index.html + /base/docs/2.x/index 301 /base/docs/2.x → 200 docs/2.x/index.html + /base/docs/2.x/index.html 301 /base/docs/2.x/index → 301 /base/docs/2.x → 200 docs/2.x/index.html + /base/docs/2.x?query=1 200 docs/2.x/index.html + /base/docs/2%2Ex 200 docs/2.x/index.html /base/docs/lodash.debounce 200 docs/lodash.debounce/index.html /base/docs/lodash.debounce/ 200 docs/lodash.debounce/index.html /base/docs/1.x 200 docs/1.x.html /base/docs/1.x/ 200 docs/1.x.html + /base/docs/1.x.html 301 /base/docs/1.x → 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css + /base/assets/css/styles.css/ 404 404.html /base/assets/ 404 404.html /base/assets/files/main.c 200 assets/files/main.c /base/assets/files/document.pdf 200 assets/files/document.pdf @@ -307,6 +341,7 @@ describe('createServeRequestHandler', () => { /base/café 200 café.html /base//file 301 /base/file → 200 file.html /base/%E0%A4%A 400 Bad Request + /base/%00 500 Internal Server Error /base/..%2Fdocusaurus.config.js 400 Bad Request" `); }); @@ -333,12 +368,18 @@ describe('createServeRequestHandler', () => { /base/both/index.html 301 /base/both/index → 301 /base/both → 301 /base/both/ → 200 both/index.html /base/docs/2.x 301 /base/docs/2.x/ → 200 docs/2.x/index.html /base/docs/2.x/ 200 docs/2.x/index.html + /base/docs/2.x/index 301 /base/docs/2.x → 301 /base/docs/2.x/ → 200 docs/2.x/index.html + /base/docs/2.x/index.html 301 /base/docs/2.x/index → 301 /base/docs/2.x → 301 /base/docs/2.x/ → 200 docs/2.x/index.html + /base/docs/2.x?query=1 301 /base/docs/2.x/?query=1 → 200 docs/2.x/index.html + /base/docs/2%2Ex 301 /base/docs/2%2Ex/ → 200 docs/2.x/index.html /base/docs/lodash.debounce 301 /base/docs/lodash.debounce/ → 200 docs/lodash.debounce/index.html /base/docs/lodash.debounce/ 200 docs/lodash.debounce/index.html /base/docs/1.x 301 /base/docs/1.x/ → 200 docs/1.x.html /base/docs/1.x/ 200 docs/1.x.html + /base/docs/1.x.html 301 /base/docs/1.x → 301 /base/docs/1.x/ → 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css + /base/assets/css/styles.css/ 404 404.html /base/assets/ 404 404.html /base/assets/files/main.c 200 assets/files/main.c /base/assets/files/document.pdf 200 assets/files/document.pdf @@ -360,6 +401,7 @@ describe('createServeRequestHandler', () => { /base/café 301 /base/caf%C3%A9/ → 200 café.html /base//file 301 /base/file → 301 /base/file/ → 200 file.html /base/%E0%A4%A 301 /base/%E0%A4%A/ → 400 Bad Request + /base/%00 301 /base/%00/ → 500 Internal Server Error /base/..%2Fdocusaurus.config.js 400 Bad Request" `); }); @@ -386,12 +428,18 @@ describe('createServeRequestHandler', () => { /base/both/index.html 301 /base/both/index → 301 /base/both → 200 both/index.html /base/docs/2.x 200 docs/2.x/index.html /base/docs/2.x/ 301 /base/docs/2.x → 200 docs/2.x/index.html + /base/docs/2.x/index 301 /base/docs/2.x → 200 docs/2.x/index.html + /base/docs/2.x/index.html 301 /base/docs/2.x/index → 301 /base/docs/2.x → 200 docs/2.x/index.html + /base/docs/2.x?query=1 200 docs/2.x/index.html + /base/docs/2%2Ex 200 docs/2.x/index.html /base/docs/lodash.debounce 200 docs/lodash.debounce/index.html /base/docs/lodash.debounce/ 301 /base/docs/lodash.debounce → 200 docs/lodash.debounce/index.html /base/docs/1.x 200 docs/1.x.html /base/docs/1.x/ 301 /base/docs/1.x → 200 docs/1.x.html + /base/docs/1.x.html 301 /base/docs/1.x → 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css + /base/assets/css/styles.css/ 301 /base/assets/css/styles.css → 200 assets/css/styles.css /base/assets/ 301 /base/assets → 404 404.html /base/assets/files/main.c 200 assets/files/main.c /base/assets/files/document.pdf 200 assets/files/document.pdf @@ -413,6 +461,7 @@ describe('createServeRequestHandler', () => { /base/café 200 café.html /base//file 301 /base/file → 200 file.html /base/%E0%A4%A 400 Bad Request + /base/%00 500 Internal Server Error /base/..%2Fdocusaurus.config.js 400 Bad Request" `); }); @@ -535,6 +584,20 @@ describe('createServeRequestHandler', () => { '//example.com/file.html', ]; + // Browsers read "\" as "/" in a Location header + // Not in a snapshot, which would print "\" as "/" + it('with a backslash', async () => { + await expect( + serveRequest( + {baseUrl: '/', trailingSlash: undefined}, + '/\\example.com/file.html', + {asIs: true}, + ), + ).resolves.toBe( + '301 /example.com/file.html → 301 /example.com/file → 404 404.html', + ); + }); + // See https://github.com/meta/docusaurus/pull/12879#discussion_r4493075460 describe('with a baseUrl', () => { it('trailingSlash undefined', async () => { @@ -698,6 +761,53 @@ describe('createServeRequestHandler', () => { serveRequest({baseUrl: '/', trailingSlash: true}, '/docs/2.x'), ).resolves.toBe('301 /docs/2.x/ → 200 docs/2.x/index.html'); }); + + // TODO fix: answer 400, like the malformed percent-encodings + it('answers 500 to null bytes', async () => { + await expect( + serveRequest( + {baseUrl: '/base/', trailingSlash: undefined}, + '/base/%00', + ), + ).resolves.toBe('500 Internal Server Error'); + }); + + // TODO fix: answer 400, like other ".." segments going above the root + it('redirects ".." segments that reach the name of the fake root', async () => { + await expect( + serveRequest( + {baseUrl: '/', trailingSlash: undefined}, + '/../__docusaurus_root__/file.html', + {asIs: true}, + ), + ).resolves.toBe('301 /file → 200 file.html'); + }); + + // Browsers send the baseUrl percent-encoded: "/caf%C3%A9/" + describe('baseUrl with non-ASCII characters', () => { + // TODO fix: serve the baseUrl, "200 index.html" + it('redirects to the baseUrl in a loop', async () => { + await expect( + serveRequest( + {baseUrl: '/café/', trailingSlash: undefined}, + '/caf%C3%A9/', + ), + ).resolves.toBe( + '302 /café/ → 302 /café/ → 302 /café/ → 302 /café/ → 302 /café/ → 302 /café/', + ); + }); + + // TODO fix: serve the baseUrl, "200 index.html" + // Characters outside of Latin-1 are invalid in a Location header + it('crashes with characters outside of Latin-1', async () => { + await expect( + serveRequest( + {baseUrl: '/文档/', trailingSlash: undefined}, + '/%E6%96%87%E6%A1%A3/', + ), + ).resolves.toBe('crash ERR_INVALID_CHAR'); + }); + }); }); describe('caching', () => { diff --git a/packages/docusaurus/src/commands/serve/__tests__/testUtils.ts b/packages/docusaurus/src/commands/serve/__tests__/testUtils.ts index 0afb52b93c3c..b1636a91929b 100644 --- a/packages/docusaurus/src/commands/serve/__tests__/testUtils.ts +++ b/packages/docusaurus/src/commands/serve/__tests__/testUtils.ts @@ -73,8 +73,11 @@ export async function describeCaching( export async function serveRequest( options: ServeOptions, requestPath: string, + requestOptions: RequestOptions = {}, ): Promise { - return withServer(options, (origin) => describeRequest(origin, requestPath)); + return withServer(options, (origin) => + describeRequest(origin, requestPath, requestOptions), + ); } // Requests a path again with the ETag of its first response, like a browser From da41f78ee877e4a6700f1fc412e4c904239a20fd Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 6 Nov 2026 19:12:57 +0100 Subject: [PATCH 2/3] test(core): capture the asset trailing slash outside of the snapshots, Windows serves it Co-Authored-By: Claude Opus 5.5 --- .../serve/__tests__/requestHandler.test.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts b/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts index a26b37e564b6..d704ab9d534b 100644 --- a/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts +++ b/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts @@ -51,7 +51,6 @@ const RequestPaths = [ '/assets/css/styles.css', '/assets/css/styles.css?v=1', - '/assets/css/styles.css/', '/assets/', // Extensions of 1, 3, 4, 5 and 11 characters '/assets/files/main.c', @@ -137,7 +136,6 @@ describe('createServeRequestHandler', () => { /docs/1.x.html 301 /docs/1.x → 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css - /assets/css/styles.css/ 404 404.html /assets/ 404 404.html /assets/files/main.c 200 assets/files/main.c /assets/files/document.pdf 200 assets/files/document.pdf @@ -197,7 +195,6 @@ describe('createServeRequestHandler', () => { /docs/1.x.html 301 /docs/1.x → 301 /docs/1.x/ → 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css - /assets/css/styles.css/ 404 404.html /assets/ 404 404.html /assets/files/main.c 200 assets/files/main.c /assets/files/document.pdf 200 assets/files/document.pdf @@ -257,7 +254,6 @@ describe('createServeRequestHandler', () => { /docs/1.x.html 301 /docs/1.x → 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css - /assets/css/styles.css/ 301 /assets/css/styles.css → 200 assets/css/styles.css /assets/ 301 /assets → 404 404.html /assets/files/main.c 200 assets/files/main.c /assets/files/document.pdf 200 assets/files/document.pdf @@ -319,7 +315,6 @@ describe('createServeRequestHandler', () => { /base/docs/1.x.html 301 /base/docs/1.x → 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css - /base/assets/css/styles.css/ 404 404.html /base/assets/ 404 404.html /base/assets/files/main.c 200 assets/files/main.c /base/assets/files/document.pdf 200 assets/files/document.pdf @@ -379,7 +374,6 @@ describe('createServeRequestHandler', () => { /base/docs/1.x.html 301 /base/docs/1.x → 301 /base/docs/1.x/ → 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css - /base/assets/css/styles.css/ 404 404.html /base/assets/ 404 404.html /base/assets/files/main.c 200 assets/files/main.c /base/assets/files/document.pdf 200 assets/files/document.pdf @@ -439,7 +433,6 @@ describe('createServeRequestHandler', () => { /base/docs/1.x.html 301 /base/docs/1.x → 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css - /base/assets/css/styles.css/ 301 /base/assets/css/styles.css → 200 assets/css/styles.css /base/assets/ 301 /base/assets → 404 404.html /base/assets/files/main.c 200 assets/files/main.c /base/assets/files/document.pdf 200 assets/files/document.pdf @@ -762,6 +755,20 @@ describe('createServeRequestHandler', () => { ).resolves.toBe('301 /docs/2.x/ → 200 docs/2.x/index.html'); }); + // Not in a snapshot: Windows ignores the trailing slash of file paths + it('serves no asset with a trailing slash', async () => { + await expect( + serveRequest( + {baseUrl: '/base/', trailingSlash: true}, + '/base/assets/css/styles.css/', + ), + ).resolves.toBe( + process.platform === 'win32' + ? '200 assets/css/styles.css' + : '404 404.html', + ); + }); + // TODO fix: answer 400, like the malformed percent-encodings it('answers 500 to null bytes', async () => { await expect( From 250f84a9757a805db2fe8eaef4cf5961e18d5649 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 6 Nov 2026 19:28:20 +0100 Subject: [PATCH 3/3] test(core): update the serve edge case snapshots, existing .html files are served as-is Co-Authored-By: Claude Opus 5.5 --- .../serve/__tests__/requestHandler.test.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts b/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts index 2348e39ee66f..dd94c4ae9f13 100644 --- a/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts +++ b/packages/docusaurus/src/commands/serve/__tests__/requestHandler.test.ts @@ -132,14 +132,14 @@ describe('createServeRequestHandler', () => { /docs/2.x 200 docs/2.x/index.html /docs/2.x/ 200 docs/2.x/index.html /docs/2.x/index 301 /docs/2.x → 200 docs/2.x/index.html - /docs/2.x/index.html 301 /docs/2.x/index → 301 /docs/2.x → 200 docs/2.x/index.html + /docs/2.x/index.html 200 docs/2.x/index.html /docs/2.x?query=1 200 docs/2.x/index.html /docs/2%2Ex 200 docs/2.x/index.html /docs/lodash.debounce 200 docs/lodash.debounce/index.html /docs/lodash.debounce/ 200 docs/lodash.debounce/index.html /docs/1.x 200 docs/1.x.html /docs/1.x/ 200 docs/1.x.html - /docs/1.x.html 301 /docs/1.x → 200 docs/1.x.html + /docs/1.x.html 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css /assets/ 404 404.html @@ -193,14 +193,14 @@ describe('createServeRequestHandler', () => { /docs/2.x 301 /docs/2.x/ → 200 docs/2.x/index.html /docs/2.x/ 200 docs/2.x/index.html /docs/2.x/index 301 /docs/2.x → 301 /docs/2.x/ → 200 docs/2.x/index.html - /docs/2.x/index.html 301 /docs/2.x/index → 301 /docs/2.x → 301 /docs/2.x/ → 200 docs/2.x/index.html + /docs/2.x/index.html 200 docs/2.x/index.html /docs/2.x?query=1 301 /docs/2.x/?query=1 → 200 docs/2.x/index.html /docs/2%2Ex 301 /docs/2%2Ex/ → 200 docs/2.x/index.html /docs/lodash.debounce 301 /docs/lodash.debounce/ → 200 docs/lodash.debounce/index.html /docs/lodash.debounce/ 200 docs/lodash.debounce/index.html /docs/1.x 301 /docs/1.x/ → 200 docs/1.x.html /docs/1.x/ 200 docs/1.x.html - /docs/1.x.html 301 /docs/1.x → 301 /docs/1.x/ → 200 docs/1.x.html + /docs/1.x.html 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css /assets/ 404 404.html @@ -254,14 +254,14 @@ describe('createServeRequestHandler', () => { /docs/2.x 200 docs/2.x/index.html /docs/2.x/ 301 /docs/2.x → 200 docs/2.x/index.html /docs/2.x/index 301 /docs/2.x → 200 docs/2.x/index.html - /docs/2.x/index.html 301 /docs/2.x/index → 301 /docs/2.x → 200 docs/2.x/index.html + /docs/2.x/index.html 200 docs/2.x/index.html /docs/2.x?query=1 200 docs/2.x/index.html /docs/2%2Ex 200 docs/2.x/index.html /docs/lodash.debounce 200 docs/lodash.debounce/index.html /docs/lodash.debounce/ 301 /docs/lodash.debounce → 200 docs/lodash.debounce/index.html /docs/1.x 200 docs/1.x.html /docs/1.x/ 301 /docs/1.x → 200 docs/1.x.html - /docs/1.x.html 301 /docs/1.x → 200 docs/1.x.html + /docs/1.x.html 200 docs/1.x.html /assets/css/styles.css 200 assets/css/styles.css /assets/css/styles.css?v=1 200 assets/css/styles.css /assets/ 301 /assets → 404 404.html @@ -317,14 +317,14 @@ describe('createServeRequestHandler', () => { /base/docs/2.x 200 docs/2.x/index.html /base/docs/2.x/ 200 docs/2.x/index.html /base/docs/2.x/index 301 /base/docs/2.x → 200 docs/2.x/index.html - /base/docs/2.x/index.html 301 /base/docs/2.x/index → 301 /base/docs/2.x → 200 docs/2.x/index.html + /base/docs/2.x/index.html 200 docs/2.x/index.html /base/docs/2.x?query=1 200 docs/2.x/index.html /base/docs/2%2Ex 200 docs/2.x/index.html /base/docs/lodash.debounce 200 docs/lodash.debounce/index.html /base/docs/lodash.debounce/ 200 docs/lodash.debounce/index.html /base/docs/1.x 200 docs/1.x.html /base/docs/1.x/ 200 docs/1.x.html - /base/docs/1.x.html 301 /base/docs/1.x → 200 docs/1.x.html + /base/docs/1.x.html 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css /base/assets/ 404 404.html @@ -378,14 +378,14 @@ describe('createServeRequestHandler', () => { /base/docs/2.x 301 /base/docs/2.x/ → 200 docs/2.x/index.html /base/docs/2.x/ 200 docs/2.x/index.html /base/docs/2.x/index 301 /base/docs/2.x → 301 /base/docs/2.x/ → 200 docs/2.x/index.html - /base/docs/2.x/index.html 301 /base/docs/2.x/index → 301 /base/docs/2.x → 301 /base/docs/2.x/ → 200 docs/2.x/index.html + /base/docs/2.x/index.html 200 docs/2.x/index.html /base/docs/2.x?query=1 301 /base/docs/2.x/?query=1 → 200 docs/2.x/index.html /base/docs/2%2Ex 301 /base/docs/2%2Ex/ → 200 docs/2.x/index.html /base/docs/lodash.debounce 301 /base/docs/lodash.debounce/ → 200 docs/lodash.debounce/index.html /base/docs/lodash.debounce/ 200 docs/lodash.debounce/index.html /base/docs/1.x 301 /base/docs/1.x/ → 200 docs/1.x.html /base/docs/1.x/ 200 docs/1.x.html - /base/docs/1.x.html 301 /base/docs/1.x → 301 /base/docs/1.x/ → 200 docs/1.x.html + /base/docs/1.x.html 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css /base/assets/ 404 404.html @@ -439,14 +439,14 @@ describe('createServeRequestHandler', () => { /base/docs/2.x 200 docs/2.x/index.html /base/docs/2.x/ 301 /base/docs/2.x → 200 docs/2.x/index.html /base/docs/2.x/index 301 /base/docs/2.x → 200 docs/2.x/index.html - /base/docs/2.x/index.html 301 /base/docs/2.x/index → 301 /base/docs/2.x → 200 docs/2.x/index.html + /base/docs/2.x/index.html 200 docs/2.x/index.html /base/docs/2.x?query=1 200 docs/2.x/index.html /base/docs/2%2Ex 200 docs/2.x/index.html /base/docs/lodash.debounce 200 docs/lodash.debounce/index.html /base/docs/lodash.debounce/ 301 /base/docs/lodash.debounce → 200 docs/lodash.debounce/index.html /base/docs/1.x 200 docs/1.x.html /base/docs/1.x/ 301 /base/docs/1.x → 200 docs/1.x.html - /base/docs/1.x.html 301 /base/docs/1.x → 200 docs/1.x.html + /base/docs/1.x.html 200 docs/1.x.html /base/assets/css/styles.css 200 assets/css/styles.css /base/assets/css/styles.css?v=1 200 assets/css/styles.css /base/assets/ 301 /base/assets → 404 404.html