Skip to content

Commit

Permalink
Replacing form-data with native FormData of Node 18 (#1644)
Browse files Browse the repository at this point in the history
Needed for #1617 
Related to form-data/form-data#558

Using the native Node 18+ FormData (using it in tests).
It turned out that in version 18.0.0 (only), `FormData::set` and
`::append()` do not take the MIME type of the `Blob` into account, and
it's an empty string, which always lead to `application/octet-stream`
instead of actual MIME type.

`Blob::type` is readonly via getter-only approach, so there is no way to
hack it.
So I disabled the MIME type check for Node 18.0.0 test.
  • Loading branch information
RobinTail committed Mar 24, 2024
1 parent aadf5ad commit aff96b7
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 76 deletions.
7 changes: 1 addition & 6 deletions example/endpoints/upload-avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@ export const uploadAvatarEndpoint = taggedEndpointsFactory.build({
description: "Handles a file upload.",
input: z
.object({
avatar: ez
.upload()
.refine(
(file) => file.mimetype.match(/image\/.+/),
"Should be an image",
),
avatar: ez.upload(),
})
.passthrough(),
output: z.object({
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@
"eslint-plugin-unicorn": "^51.0.0",
"express": "^4.18.2",
"express-fileupload": "^1.4.3",
"form-data": "^4.0.0",
"http-errors": "^2.0.0",
"husky": "^9.0.5",
"make-coverage-badge": "^1.2.0",
Expand Down
23 changes: 0 additions & 23 deletions tests/system/__snapshots__/example.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -98,26 +98,3 @@ exports[`Example > Positive > Should send an image with a correct header 1`] = `
exports[`Example > Positive > Should serve static files 1`] = `"f39beeff92379dc935586d726211c2620be6f879"`;

exports[`Example > Positive > Should stream an image with a correct header 1`] = `"f39beeff92379dc935586d726211c2620be6f879"`;

exports[`Example > Positive > Should upload the file 1`] = `
{
"data": {
"hash": "f39beeff92379dc935586d726211c2620be6f879",
"mime": "image/svg+xml",
"name": "logo.svg",
"otherInputs": {
"arr": [
"456",
"789",
],
"num": "123",
"obj": {
"some": "thing",
},
"str": "test string value",
},
"size": 48687,
},
"status": "success",
}
`;
51 changes: 32 additions & 19 deletions tests/system/example.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import {
Implementation,
jsonEndpoints,
} from "../../example/example.client";
import { mimeMultipart } from "../../src/mime";
import { givePort, waitFor } from "../helpers";
import { createHash } from "node:crypto";
import FormData from "form-data";
import { readFile } from "node:fs/promises";
import { afterAll, afterEach, describe, expect, test } from "vitest";

Expand Down Expand Up @@ -196,24 +194,41 @@ describe("Example", async () => {
const filename = "logo.svg";
const logo = await readFile(filename, "utf-8");
const data = new FormData();
data.append("avatar", logo, { filename });
data.append(
"avatar",
new Blob([logo], { type: "image/svg+xml" }), // FormData mime is buggy in Node 18.0.0
filename,
);
data.append("str", "test string value");
data.append("num", 123);
data.append("arr[0]", 456);
data.append("arr[1]", 789);
data.append("obj[some]", "thing");
const response = await fetch(
`http://localhost:${port}/v1/avatar/upload`,
{
method: "POST",
headers: {
"Content-Type": `${mimeMultipart}; boundary=${data.getBoundary()}`,
},
body: data.getBuffer().toString("utf8"),
},
{ method: "POST", body: data },
);
const json = await response.json();
expect(json).toMatchSnapshot();
expect(json).toEqual({
data: {
hash: "f39beeff92379dc935586d726211c2620be6f879",
mime:
process.versions.node === "18.0.0"
? "application/octet-stream" // Node 18.0.0 FormData bug // @todo remove it when dropped
: "image/svg+xml",
name: "logo.svg",
otherInputs: {
arr: ["456", "789"],
num: "123",
obj: {
some: "thing",
},
str: "test string value",
},
size: 48687,
},
status: "success",
});
});

test.each([readFileSync("logo.svg"), createReadStream("logo.svg")])(
Expand Down Expand Up @@ -381,16 +396,14 @@ describe("Example", async () => {
const filename = "dataflow.svg";
const logo = await readFile(filename, "utf-8");
const data = new FormData();
data.append("avatar", logo, { filename });
data.append(
"avatar",
new Blob([logo], { type: "image/svg+xml" }),
filename,
);
const response = await fetch(
`http://localhost:${port}/v1/avatar/upload`,
{
method: "POST",
headers: {
"Content-Type": `${mimeMultipart}; boundary=${data.getBoundary()}`,
},
body: data.getBuffer().toString("utf8"),
},
{ method: "POST", body: data },
);
expect(response.status).toBe(413);
const json = await response.json();
Expand Down
28 changes: 1 addition & 27 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1101,11 +1101,6 @@ async@^3.2.3:
resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==

asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==

available-typed-arrays@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846"
Expand Down Expand Up @@ -1401,13 +1396,6 @@ colorspace@1.1.x:
color "^3.1.3"
text-hex "1.0.x"

combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"

commander@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
Expand Down Expand Up @@ -1563,11 +1551,6 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"

delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==

depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
Expand Down Expand Up @@ -2200,15 +2183,6 @@ foreground-child@^3.1.0:
cross-spawn "^7.0.0"
signal-exit "^4.0.1"

form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"

forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
Expand Down Expand Up @@ -3091,7 +3065,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==

mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
Expand Down

0 comments on commit aff96b7

Please sign in to comment.