npm add meros
makes reading multipart responses simple
⚡ Features
- No dependencies
- Seemless api
- Super performant
- Supports any1
content-type
- preamble and epilogue don't yield
- Browser/Node Compatible
- Plugs into existing libraries like Relay and rxjs
🚀 Usage
Visit /examples for more info!
import { meros } from 'meros';
const parts = await fetch('/api').then(meros);
for await (const part of parts) {
}
from(parts).subscribe((part) => {
});
Specific Environment
Browser
import { meros } from 'meros/browser';
const parts = await fetch('/api').then(meros);
Node
import http from 'http';
import { meros } from 'meros/node';
const response = await new Promise((resolve) => {
const request = http.get(`http://example.com/api`, (response) => {
resolve(response);
});
request.end();
});
const parts = await meros(response);
🔎 API
Meros offers two flavours, both for the browser and for node; but their api's are fundamentally the same.
Note: The type Response
is used loosely here and simply alludes to Node's IncomingMessage
or the browser's
Response
type.
meros(response: Response, options?: Options)
Returns: Promise<Response | AsyncGenerator<Part | Part[]>
Meros returns a promise that will resolve to an AsyncGenerator
if the response is of multipart/mixed
mime, or simply
returns the Response
if something else; helpful for middlewares. The idea here being that you run meros as a chain off
fetch.
fetch('/api').then(meros);
If the content-type
is NOT a multipart, then meros will resolve with the response argument.
Example on how to handle this case
import { meros } from 'meros';
const response = await fetch('/api');
const parts = await meros(response);
if (parts[Symbol.asyncIterator] < 'u') {
for await (const part of parts) {
}
} else {
const data = await parts.json();
}
each Part
gives you access to:
json: boolean
~ Tells you the body
would be a JavaScript object of your defined generic T
.headers: object
~ A key-value pair of all headers discovered from this part.body: T | Fallback
~ Is the body of the part, either as a JavaScript object (noted by json
) or the base type
of the environment (Buffer | string
, for Node and Browser respectively).
options.multiple: boolean
Default: false
Setting this to true
will yield once for all available parts of a chunk, rather than yielding once per part. This is
an optimization technique for technologies like GraphQL where rather than commit the payload to the store, to be
added-to in the next process-tick we can simply do that synchronously.
Warning: This will alter the behaviour and yield arrays—than yield payloads.
const chunks = await fetch('/api').then((response) => meros(response, { multiple: true }));
for await (const parts of chunks) {
for (const part of parts) {
}
}
💨 Benchmark
via the /bench
directory with Node v18.0.0
Node
✔ meros ~ 1,271,218 ops/sec ± 0.84%
✘ it-multipart ~ 700,039 ops/sec ± 0.72%
--
it-multipart (FAILED @ "should match reference patch set")
Browser
✔ meros ~ 800,941 ops/sec ± 1.06%
✘ fetch-multipart-graphql ~ 502,175 ops/sec ± 0.75%
--
fetch-multipart-graphql (FAILED @ "should match reference patch set")
🎒 Notes
Why the name? meros comes from Ancient Greek μέρος méros, meaning "part".
This library aims to implement RFC1341 in its entirety, however we aren't there yet. That being said, you may very
well use this library in other scenarios like streaming in file form uploads.
Another goal here is to aide in being the defacto standard transport library to support
@defer
and @stream
GraphQL directives
Caveats
- No support the
/alternative
, /digest
or /parallel
subtype at this time. - No support for nested multiparts
❤ Thanks
Special thanks to Luke Edwards for performance guidance and high level api design.
😇 Compassion
This library is simple, a meer few hundred bytes. It's easy to copy, and easy to alter. If you do, that is fine ❤️ im
all for the freedom of software. But please give credit where credit is due.
License
MIT © Marais Rossouw