meros
    TypeScript icon, indicating that this package has built-in type declarations

    1.2.0 • Public • Published

    meros



    npm add meros makes reading multipart responses simple

    downloads size

    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!

    // Relies on bundler/environment detection
    import { meros } from 'meros';
    
    const parts = await fetch('/api').then(meros);
    
    // As a simple Async Generator
    for await (const part of parts) {
      // Do something with this part
    }
    
    // Used with rxjs streams
    from(parts).subscribe((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('/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'); // Assume this isnt multipart
    const parts = await meros(response);
    
    if (parts[Symbol.asyncIterator] < 'u') {
      for await (const part of parts) {
        // Do something with this part
      }
    } 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.

    Important: This will alter the behaviour and yield arrays—than yield payloads.

    const chunks = await fetch('/api').then((response) => meros(response, { multiple: true }));
    
    // As a simple Async Generator
    for await (const parts of chunks) {
      for (const part of parts) {
        // Do something with this part, maybe aggregate?
      }
    }

    💨 Benchmark

    via the /bench directory with Node v16.11.0

    Validation :: node
    ✔ meros
    ✘ it-multipart (FAILED @ "should match reference patch set")
    
    Benchmark :: node
      meros                     x 262,515 ops/sec ±2.45% (77 runs sampled)
      it-multipart              x 165,562 ops/sec ±0.99% (79 runs sampled)
    
    Validation :: browser
    ✔ meros
    ✘ fetch-multipart-graphql (FAILED @ "should match reference patch set")
    
    Benchmark :: browser
      meros                     x 864,124 ops/sec ±0.77% (80 runs sampled)
      fetch-multipart-graphql   x 283,296 ops/sec ±0.96% (82 runs sampled)
    

    🎒 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.

    License

    MIT © Marais Rossouw

    Footnote

    1: By default, we'll look for JSON, and parse that for you. If not, we'll give you the body as what was streamed.

    Install

    npm i meros

    DownloadsWeekly Downloads

    2,030,606

    Version

    1.2.0

    License

    MIT

    Unpacked Size

    29.1 kB

    Total Files

    11

    Last publish

    Collaborators

    • marais