-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: dropping support for non-native FormData implementations #199
Conversation
@@ -24,24 +24,25 @@ export interface CustomFixture { | |||
|
|||
export const runCustomFixtures = ({ targetId, clientId, tests }: CustomFixture) => { | |||
describe(`custom fixtures for ${targetId}:${clientId}`, () => { | |||
tests.forEach(({ it: title, expected: fixtureFile, options, input: request }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to restructure all of these tests into formal describe.each
and it.each
blocks because with the async change Vitest wasn't picking them up as they were getting invoked after Vitest was expecting them to because it was running an async test inside of a non-async foreach loop.
src/helpers/form-data.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extracted this from node-fetch
core a couple years ago when I overhauled the multpart handling in this library. Really glad I can now delete this crap.
form.append(name, value, { | ||
// @ts-expect-error TODO | ||
filename, | ||
// @ts-expect-error TODO | ||
contentType: param.contentType || null, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hit da bricks non-native formdata
@@ -8,6 +8,6 @@ struct curl_slist *headers = NULL; | |||
headers = curl_slist_append(headers, "content-type: multipart/form-data; boundary=---011000010111000001101001"); | |||
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); | |||
|
|||
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\nHello World\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"bar\"\r\n\r\nBonjour le monde\r\n-----011000010111000001101001--\r\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fun side effect of the quirky non-native FormData
work we had set up was that sometimes the filename
in a HAR would get to basenemed from src/fixtures/files/hello.txt
to hello.txt
. This isn't happening anymore anywhere, and I think that's okay. The HARs we ingest for this library in the browser will always have a basenamed filename thanks to browser security not giving us full paths.
It's also good, as far as integration testing goes, for these filenames to be the full path in snippets everywhere so when we execute these snippets in the Docker integrations suite it's able to send along the file when necessary (like some JS snippets in here that use fs.readFileStream()
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked the golang source to confirm that the trailing newlines are not required, and it agrees
@@ -8,6 +8,6 @@ struct curl_slist *headers = NULL; | |||
headers = curl_slist_append(headers, "Content-Type: multipart/form-data; boundary=---011000010111000001101001"); | |||
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); | |||
|
|||
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These multipart payloads no longer have trailing \r\n
's and that's also okay.
src/index.ts
Outdated
request.postData.boundary = boundary; | ||
request.postData.text = await formDataToString(form).then(str => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This formdata-to-string
library I published last week from extracted undici
code is a goddamn dream. Give it a FormData
object and it just works without any fuss.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be cool if this library accepted an optional flag where you could say whether your not you want the undici-specific boundary, feels a li'l weird to be doing this here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonder why undici doesnt publish that as a util function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure but if they did publish it we'd be importing a whole bunch of junk as their core function has handling for all sorts of body payloads that we don't need to worry about. https://github.com/nodejs/undici/blob/e39a6324c4474c6614cac98b8668e3d036aa6b18/lib/fetch/body.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kanadgupta I added a custom boundary option in v1.1.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! This is a breaking change right?
// @ts-expect-error TODO | ||
const isNativeFormData = typeof form[Symbol.iterator] === 'function'; | ||
|
||
// TODO: THIS ABSOLUTELY MUST BE REMOVED. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😬 good job we're getting rid of this.
src/index.ts
Outdated
request.postData.boundary = boundary; | ||
request.postData.text = await formDataToString(form).then(str => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonder why undici doesnt publish that as a util function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly small comments, LGTM! Even though there's as chance they'll decline, I wouldn't be opposed to offering this to the upstream fork since you're basically a maintainer at this point lol (and mostly everything you're doing here isn't all that controversial, plus it contains a lot of great modernizations)
.vscode/settings.json
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this file here because they use it upstream? This feels like something that should just be in the prettier config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah these snippets have all sorts of frustrating line endings, that are handled in .editorconfig
, that i fought the upstream maintainers on but they didn't relent.
src/index.ts
Outdated
request.postData.boundary = boundary; | ||
request.postData.text = await formDataToString(form).then(str => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be cool if this library accepted an optional flag where you could say whether your not you want the undici-specific boundary, feels a li'l weird to be doing this here
// easter egg | ||
const boundary = '---011000010111000001101001'; // this is binary for "api". yep. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:buzzfeed-cute: TIL!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR doesn't bump the version in package.json - should this be v8?
It should do when merged in! Normally you'd do |
## 🧰 Changes In September of last year I reworked this library[^1] off `node-fetch` and the funky non-native `FormData` object it ships to only support native `FormData` -- in the process making code snippet generation asynchronous. Unfortunately this async work is causing us some headaches on integrating it into our API Explorer. I started to dig in to trying to make `formdata-to-string` synchronous and realized... why bother? We have the HAR, we can generate everything we need for a multipart boundary from that. * [x] Completely removes the `FormData` and `Blob` API dependencies from this library. * [x] Made `.convert()` a synchronous operation again. [^1]: #199 --------- Co-authored-by: Kanad Gupta <[email protected]>
🧰 Changes
Now that Node 16 is EOL'd and native
fetch
andFormData
are available everywhere this library needs to run we can now drop support for the legacy, non-nativeFormData
handling we had for the form-data library.Late last week I packed up some undici, the library that Node 18+ uses for native
fetch
, internals into a new package named formdata-to-string that we can use to easily transform aFormData
object into a payload string. The unfortunate result of this work is that because that library is async then this library also needs to be async.The main benefit of this new native
FormData
work however is that we can delete a lot of code I had written two years ago here and upstream for handling these funky non-nativeFormData
instances that the form-data library, and node-fetch@2 utilized.And because this work fundamentally changes how the library works I am not planning on upstreaming it.