JSPM

  • Created
  • Published
  • Downloads 3447513
  • Score
    100M100P100Q201020F
  • License MIT

A fast utility for reading streamed multipart responses.

Package Exports

  • meros
  • meros/browser
  • meros/node

Readme

meros · From Ancient Greek μέρος (méros, "part").

CI codecov

A fast utility for reading streamed multipart/mixed responses.

⚙️ Install

yarn add meros

🚀 Usage

// Rely on bundler/environment dection
import { meros } from 'meros';

const parts = await fetch('/fetch-multipart').then(meros);

// As a simple Async Generator
for await (const part of parts) {
    // Do something with this part
}

// Used with rxjs streams
from(parts).pipe(
    tap((part) => {
        // Do something with it
    }),
);

Specific Environment

// Browser
import { meros } from 'meros/browser';
// import { meros } from 'https://cdn.skypack.dev/meros';

const parts = await fetch('/fetch-multipart').then(meros);

// Node
import http from 'http';
import { meros } from 'meros/node';

const response = await new Promise((resolve) => {
    const request = http.get(`http://my-domain/mock-ep`, (response) => {
        resolve(response);
    });
    request.end();
});

const parts = await meros(response);

🎒 Notes

This library aims to implement RFC1341 in its entirety, however there have been some types left out as we aim to be on the consuming side, than the server side (but we do support Node clients).

  • content-type is assumed to stay consistent between parts, and therefore the "fall through" approach is recommended and to only be given at the start. Ie only give it content-type as a header once, and only for the first chunk.

Please note;

Because encapsulation boundaries must not appear in the body parts being encapsulated, a user agent must exercise care to choose a unique boundary.

~ RFC1341 7.2.1

So be sure to calculate a boundary that can be guaranteed to never exist in the body.

  • We do not support the /alternative , /digest or /parallel subtype at this time.
  • We also do not support nested multiparts

🔎 API

Browser ~ function meros<T=unknown>(response: Response): Promise<Response | AsyncGenerator<T>>;

Node ~ function meros<T=unknown>(response: IncomingMessage): Promise<IncomingMessage | AsyncGenerator<T>>;

Returns an async generator that yields on every part. Worth noting that if multiple parts are present in one chunk, each part will yield independently.

If the content-type is a multipart, then it will resolve with the response argument.

💨 Benchmark

Ran with Node v15.1.0

Validation :: node
✔ meros
✘ it-multipart (FAILED @ "should match reference patch set")

Benchmark :: node
  meros                     x 7,892 ops/sec ±0.92% (72 runs sampled)
  it-multipart              x 6,161 ops/sec ±1.77% (76 runs sampled)

Validation :: browser
✔ meros
✘ fetch-multipart-graphql (FAILED @ "should match reference patch set")

Benchmark :: browser
  meros                     x 13,641 ops/sec ±2.42% (74 runs sampled)
  fetch-multipart-graphql   x 8,406 ops/sec ±1.88% (74 runs sampled)
Reference patch set
content-type: "multipart/mixed; boundary=abc123"
preamble
--abc123
Content-Type: application/json
Content-Length: 17

{"hello":"world"}

--abc123
Content-Type: application/json
Content-Length: 17

{"other":"world"}

--abc123
Content-Type: application/json
Content-Length: 19

{"another":"world"}

--abc123
Content-Type: application/json
Content-Length: 39

{"massive":{"nested":{"world":"okay"}}}

--abc123
Content-Type: text/plain
Content-Length: 22

"should be plain text"

--abc123--
epilogue
--abc123
Content-Type: application/json
Content-Length: 19

{"shouldnt":"work"}

❤ Thanks

Special thanks to Luke Edwards for performance guidance and high level api design.

License

MIT © Marais Rossouw