From d1914ef625953a9e9eccf2f696b5f7f559adc158 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 27 Jun 2022 21:27:25 -0400 Subject: [PATCH 01/12] monkey-patch formData to enable multipart parsing --- packages/kit/package.json | 1 + packages/kit/src/node/index.js | 13 ++++++++++++- pnpm-lock.yaml | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/kit/package.json b/packages/kit/package.json index cc72d674af55..aae4207b55f3 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -33,6 +33,7 @@ "locate-character": "^2.0.5", "marked": "^4.0.16", "mime": "^3.0.0", + "node-fetch": "^3.2.4", "port-authority": "^1.2.0", "rollup": "^2.75.3", "selfsigned": "^2.0.1", diff --git a/packages/kit/src/node/index.js b/packages/kit/src/node/index.js index 1b30bd113389..a28f721a02d2 100644 --- a/packages/kit/src/node/index.js +++ b/packages/kit/src/node/index.js @@ -1,4 +1,5 @@ import * as set_cookie_parser from 'set-cookie-parser'; +import { Request as NodeFetchRequest } from 'node-fetch'; /** @param {import('http').IncomingMessage} req */ function get_raw_body(req) { @@ -64,11 +65,21 @@ export async function getRequest(base, req) { delete headers[':scheme']; } - return new Request(base + req.url, { + const request = new Request(base + req.url, { method: req.method, headers, body: await get_raw_body(req) // TODO stream rather than buffer }); + + request.formData = async () => { + return new NodeFetchRequest(request.url, { + method: request.method, + headers: request.headers, + body: Buffer.from(await request.arrayBuffer()) + }).formData(); + }; + + return request; } /** @type {import('@sveltejs/kit/node').setResponse} */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f5a074f9c81..2cbe5597d0f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -271,6 +271,7 @@ importers: locate-character: ^2.0.5 marked: ^4.0.16 mime: ^3.0.0 + node-fetch: ^3.2.4 port-authority: ^1.2.0 rollup: ^2.75.3 sade: ^1.8.1 @@ -309,6 +310,7 @@ importers: locate-character: 2.0.5 marked: 4.0.16 mime: 3.0.0 + node-fetch: 3.2.4 port-authority: 1.2.0 rollup: 2.75.3 selfsigned: 2.0.1 From 0df582cf388d9dad1c8f1aea03ec129ae443d4bf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 13:33:46 -0400 Subject: [PATCH 02/12] convert ReadableStream to stream.Readable without buffering --- packages/kit/src/node/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/node/index.js b/packages/kit/src/node/index.js index a28f721a02d2..33a18065f956 100644 --- a/packages/kit/src/node/index.js +++ b/packages/kit/src/node/index.js @@ -1,5 +1,6 @@ import * as set_cookie_parser from 'set-cookie-parser'; import { Request as NodeFetchRequest } from 'node-fetch'; +import { Readable } from 'stream'; /** @param {import('http').IncomingMessage} req */ function get_raw_body(req) { @@ -75,7 +76,8 @@ export async function getRequest(base, req) { return new NodeFetchRequest(request.url, { method: request.method, headers: request.headers, - body: Buffer.from(await request.arrayBuffer()) + // @ts-expect-error TypeScript doesn't understand that ReadableStream implements Symbol.asyncIterator + body: request.body && Readable.from(request.body) }).formData(); }; From c9ee55609134f238a98310a2e3d11df8ccdbc74f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 14:09:06 -0400 Subject: [PATCH 03/12] remove obsolete code --- packages/kit/src/runtime/server/utils.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js index 6fddaa9009ae..69831be459c1 100644 --- a/packages/kit/src/runtime/server/utils.js +++ b/packages/kit/src/runtime/server/utils.js @@ -38,17 +38,13 @@ export function is_pojo(body) { if (body) { if (body instanceof Uint8Array) return false; + if (body instanceof ReadableStream) return false; // if body is a node Readable, throw an error // TODO remove this for 1.0 if (body._readableState && typeof body.pipe === 'function') { throw new Error('Node streams are no longer supported — use a ReadableStream instead'); } - - if (body instanceof ReadableStream) return false; - - // similarly, it could be a web ReadableStream - if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) return false; } return true; From e9683e637aa9b2f3a04b5596b29a6d4edea517c1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 14:18:51 -0400 Subject: [PATCH 04/12] bypass turbo cache --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eddd1f5b620f..e609cdeb081a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile - run: cd packages/kit && pnpm build - - run: pnpm turbo run lint check + - run: pnpm turbo run lint check --force Tests: runs-on: ${{ matrix.os }} timeout-minutes: 30 From c07b4534a73833b471ef5d910c360a55e347053a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 14:25:24 -0400 Subject: [PATCH 05/12] oops, wrong one --- .github/workflows/ci.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e609cdeb081a..eddd1f5b620f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile - run: cd packages/kit && pnpm build - - run: pnpm turbo run lint check --force + - run: pnpm turbo run lint check Tests: runs-on: ${{ matrix.os }} timeout-minutes: 30 diff --git a/package.json b/package.json index 96de779197b8..813c7836ab94 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "private": true, "scripts": { "build": "turbo run build", - "test": "turbo run test --filter=./packages/*", + "test": "turbo run test --filter=./packages/* --force", "check": "turbo run check", "lint": "turbo run lint", "format": "turbo run format", From ebd0a0307d2a06a73a38f0d4803b1fbeb4ec1221 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 14:25:45 -0400 Subject: [PATCH 06/12] belt and braces --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 813c7836ab94..89c7a3e91f3f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "monorepo for @sveltejs/kit and friends", "private": true, "scripts": { - "build": "turbo run build", + "build": "turbo run build --force", "test": "turbo run test --filter=./packages/* --force", "check": "turbo run check", "lint": "turbo run lint", From a68417c7449b75964868ada8a41f3de1931a106a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 14:29:34 -0400 Subject: [PATCH 07/12] well that wasnt it --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 89c7a3e91f3f..96de779197b8 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "monorepo for @sveltejs/kit and friends", "private": true, "scripts": { - "build": "turbo run build --force", - "test": "turbo run test --filter=./packages/* --force", + "build": "turbo run build", + "test": "turbo run test --filter=./packages/*", "check": "turbo run check", "lint": "turbo run lint", "format": "turbo run format", From d0d59765bda7b0ad337af1f9aab46da62d468618 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 14:31:38 -0400 Subject: [PATCH 08/12] trying something --- packages/kit/src/runtime/server/endpoint.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index 8ab8d2dd312a..7112419a5dff 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -112,6 +112,10 @@ export async function render_endpoint(event, mod) { if (is_pojo(body) && (!type || type.startsWith('application/json'))) { headers.set('content-type', 'application/json; charset=utf-8'); normalized_body = JSON.stringify(body); + + if (normalized_body === '{}' && body.getReader) { + throw new Error('ReadableStream was mistaken for a POJO'); + } } else { normalized_body = /** @type {import('types').StrictBody} */ (body); } From 5b9c1486e236d9dc0a27290404346b56b5bb3295 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 14:59:40 -0400 Subject: [PATCH 09/12] install polyfills unconditionally --- packages/kit/src/node/polyfills.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/kit/src/node/polyfills.js b/packages/kit/src/node/polyfills.js index 66ae54b155d8..ae1c8ff6c502 100644 --- a/packages/kit/src/node/polyfills.js +++ b/packages/kit/src/node/polyfills.js @@ -17,8 +17,6 @@ const globals = { // exported for dev/preview and node environments export function installPolyfills() { for (const name in globals) { - if (name in globalThis) continue; - Object.defineProperty(globalThis, name, { enumerable: true, configurable: true, From 6eb7e418e8eceeb1d563f23620ad38fbfe024bb2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 15:08:59 -0400 Subject: [PATCH 10/12] remove debugging code --- packages/kit/src/runtime/server/endpoint.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index 7112419a5dff..8ab8d2dd312a 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -112,10 +112,6 @@ export async function render_endpoint(event, mod) { if (is_pojo(body) && (!type || type.startsWith('application/json'))) { headers.set('content-type', 'application/json; charset=utf-8'); normalized_body = JSON.stringify(body); - - if (normalized_body === '{}' && body.getReader) { - throw new Error('ReadableStream was mistaken for a POJO'); - } } else { normalized_body = /** @type {import('types').StrictBody} */ (body); } From f1329ee4790a3ab5d36a76a44205c269d344ab49 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 16:20:24 -0400 Subject: [PATCH 11/12] add test --- .../kit/test/apps/basics/src/routes/shadowed/error-post.js | 6 ++++-- .../kit/test/apps/basics/src/routes/shadowed/index.svelte | 3 ++- packages/kit/test/apps/basics/test/test.js | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-post.js b/packages/kit/test/apps/basics/src/routes/shadowed/error-post.js index a4d616a831a4..fdf8aebba386 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/error-post.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/error-post.js @@ -6,11 +6,13 @@ export function get() { }; } -export function post() { +export async function post({ request }) { + const data = await request.formData(); + return { status: 400, body: { - post_message: 'hello from post' + post_message: `echo: ${data.get('message')}` } }; } diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/index.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/index.svelte index 8f5c46d2c5c4..3d37d71a7df4 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/shadowed/index.svelte @@ -14,6 +14,7 @@ -
+ +
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index a726480ac740..e619f4a03b63 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -543,7 +543,7 @@ test.describe.parallel('Shadowed pages', () => { test('Merges bodies for 4xx and 5xx responses from non-GET', async ({ page }) => { await page.goto('/shadowed'); await Promise.all([page.waitForNavigation(), page.click('#error-post')]); - expect(await page.textContent('h1')).toBe('hello from get / hello from post'); + expect(await page.textContent('h1')).toBe('hello from get / echo: posted data'); }); test('Responds from endpoint if Accept includes application/json but not text/html', async ({ From f90cd1acfeaf47db5c5a002fd7c707a74b7292cd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 29 Jun 2022 16:21:05 -0400 Subject: [PATCH 12/12] changeset --- .changeset/empty-roses-obey.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/empty-roses-obey.md diff --git a/.changeset/empty-roses-obey.md b/.changeset/empty-roses-obey.md new file mode 100644 index 000000000000..ec51eb768e68 --- /dev/null +++ b/.changeset/empty-roses-obey.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Enable multipart formdata parsing with node-fetch