Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/empty-roses-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Enable multipart formdata parsing with node-fetch
1 change: 1 addition & 0 deletions packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
15 changes: 14 additions & 1 deletion packages/kit/src/node/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +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) {
Expand Down Expand Up @@ -64,11 +66,22 @@ 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,
// @ts-expect-error TypeScript doesn't understand that ReadableStream implements Symbol.asyncIterator
body: request.body && Readable.from(request.body)
}).formData();
};

return request;
}

/** @type {import('@sveltejs/kit/node').setResponse} */
Expand Down
2 changes: 0 additions & 2 deletions packages/kit/src/node/polyfills.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 1 addition & 5 deletions packages/kit/src/runtime/server/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')}`
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<button type="submit" id="redirect-post-with-cookie">redirect</button>
</form>

<form action="/shadowed/error-post" method="post">
<form action="/shadowed/error-post" method="post" enctype="multipart/form-data">
<input name="message" value="posted data" />
<button type="submit" id="error-post">error</button>
</form>
2 changes: 1 addition & 1 deletion packages/kit/test/apps/basics/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 ({
Expand Down
2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.