• Stars
    star
    185
  • Rank 208,271 (Top 5 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🪢 A fast utility that makes reading multipart responses simple

meros

A utility that makes reading multipart responses simple

js downloads gzip size brotli size

This is free to use software, but if you do like it, consisder supporting me ❤️

sponsor me buy me a coffee

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

Warning: 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 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 mere few hundred bytes. It's easy to copy, and easy to alter. If you do, that is fine ❤️ I'm all for the freedom of software. But please give credit where credit is due.

License

MIT © Marais Rossouw

Footnotes

  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.

More Repositories

1

diary

📑 Zero-dependency, fast logging library for Node, Browser and Workers
TypeScript
250
star
2

workers-logger

🪵 Fast effective logging for Cloudflare Workers
TypeScript
50
star
3

rian

👣 Effective tracing for the edge and origins
TypeScript
33
star
4

relay-sentry

⚛ Relay log function that enriches Sentry with Relay life cycles and GraphQL data
TypeScript
29
star
5

ports-list

28
star
6

piecemeal

🪀 Send your data piecemeal
TypeScript
24
star
7

swrr

⏩ Stale While Revalidated Resources — keeps data fast
TypeScript
24
star
8

mosaic.js

Delaunay triangulation shaded, from a light source based on mouse cursor.
TypeScript
24
star
9

dldr

🧩 A tiny/fast dataloader implementation
TypeScript
20
star
10

storybook-addon-grid

⚜️ Column guides that help you align your stories
TypeScript
20
star
11

configs-webpack-plugin

Runtime Config mangement for webpack projects
TypeScript
15
star
12

tctx

Blazing fast traceparents for use in w3c Trace Context
TypeScript
12
star
13

async-boundary

🏃‍♂️ A React async-boundary that couples an error-boundary as well as a suspense container
TypeScript
12
star
14

graphql-workers

⚛️ graphql-workers puts graphql on the edge
TypeScript
7
star
15

object-identity

#️⃣ Object hashing for structural equality
TypeScript
7
star
16

fgs

Fetch GraphQL Schema from a GraphQL HTTP Endpoint from grphql-config that is Relay (rust) aware.
TypeScript
6
star
17

rollup-plugin-treat

TypeScript
4
star
18

tabling

Let's table this object till a later date
TypeScript
3
star
19

apollo-link-multipart

TypeScript
3
star
20

web-vitals

Cloudflare Workers enabled web-vitals
TypeScript
3
star
21

git.ski

TypeScript
2
star
22

build-header

🙃 Logs a pretty header to your build scripts
JavaScript
2
star
23

uni-days

Bits n bobs from my software written whilst studying at QUT
HTML
2
star
24

git-remote-fetcher

A tool to help me keep my git remote's synced
Rust
2
star
25

dotfiles

⚡ my env
Shell
2
star
26

.github

🩺 The community health files and GitHub shared configuration files
2
star
27

posthtml-obfuscate

PostHTML plugin that obfuscates emails.
JavaScript
2
star
28

website

🌏 personal brand, and guinea pig
HTML
2
star
29

obs-count-to-timer

An OBS script to count to a specified time
Lua
1
star
30

configs

my shared config files — prettier, tsconfig, ...
1
star
31

posthtml-json

PostHTML plugin that does stuff with JSON.
JavaScript
1
star
32

mar.fyi

⚙️ My home brand url shortening service
Rust
1
star
33

treat-repro

JavaScript
1
star
34

auth0-repro

JavaScript
1
star