{"id":13455199,"url":"https://github.com/maraisr/meros","last_synced_at":"2025-10-07T04:10:54.105Z","repository":{"id":40771576,"uuid":"277017877","full_name":"maraisr/meros","owner":"maraisr","description":"🪢 A fast utility that makes reading multipart responses simple","archived":false,"fork":false,"pushed_at":"2024-01-19T00:54:55.000Z","size":604,"stargazers_count":186,"open_issues_count":1,"forks_count":10,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-01T09:35:58.254Z","etag":null,"topics":["defer","fetch","graphql","multipart","multipart-mixed","relay","stream","streaming-data"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maraisr.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"maraisr"}},"created_at":"2020-07-04T01:37:02.000Z","updated_at":"2025-02-12T09:27:22.000Z","dependencies_parsed_at":"2024-06-18T13:38:57.098Z","dependency_job_id":"a4c4cde4-9f96-48d1-8801-9b447cd1ac30","html_url":"https://github.com/maraisr/meros","commit_stats":{"total_commits":158,"total_committers":6,"mean_commits":"26.333333333333332","dds":"0.22784810126582278","last_synced_commit":"e8d9efded13532f3ff3cef4db32b2f18a06c949b"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maraisr%2Fmeros","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maraisr%2Fmeros/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maraisr%2Fmeros/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maraisr%2Fmeros/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maraisr","download_url":"https://codeload.github.com/maraisr/meros/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247829511,"owners_count":21002997,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["defer","fetch","graphql","multipart","multipart-mixed","relay","stream","streaming-data"],"created_at":"2024-07-31T08:01:02.368Z","updated_at":"2025-10-07T04:10:54.100Z","avatar_url":"https://github.com/maraisr.png","language":"TypeScript","funding_links":["https://github.com/sponsors/maraisr","https://www.buymeacoffee.com/marais"],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cdiv align=\"left\"\u003e\n\n\u003csamp\u003e\n\n![meros](logo.svg)\n\n\u003c/samp\u003e\n\n**A utility that makes reading multipart responses simple**\n\n\u003ca href=\"https://npm-stat.com/charts.html?package=meros\"\u003e\n  \u003cimg src=\"https://badgen.net/npm/dm/meros?color=black\u0026label=npm%20downloads\" alt=\"js downloads\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://licenses.dev/npm/meros\"\u003e\n  \u003cimg src=\"https://licenses.dev/b/npm/meros?style=dark\" alt=\"licenses\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://unpkg.com/meros/browser/index.mjs\"\u003e\n  \u003cimg src=\"https://img.badgesize.io/https://unpkg.com/meros/browser/index.mjs?compression=gzip\u0026label=gzip\u0026color=black\" alt=\"gzip size\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://unpkg.com/meros/browser/index.mjs\"\u003e\n  \u003cimg src=\"https://img.badgesize.io/https://unpkg.com/meros/browser/index.mjs?compression=brotli\u0026label=brotli\u0026color=black\" alt=\"brotli size\" /\u003e\n\u003c/a\u003e\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n\u003csup\u003e\n\nThis is free to use software, but if you do like it, consider supporting me ❤️\n\n[![sponsor me](https://badgen.net/badge/icon/sponsor?icon=github\u0026label\u0026color=gray)](https://github.com/sponsors/maraisr)\n[![buy me a coffee](https://badgen.net/badge/icon/buymeacoffee?icon=buymeacoffee\u0026label\u0026color=gray)](https://www.buymeacoffee.com/marais)\n\n\u003c/sup\u003e\n\n\u003c/div\u003e\n\n## ⚡ Features\n\n- No dependencies\n- Seamless api\n- Super [performant](#-benchmark)\n- Supports _any_[^1] `content-type`\n- _preamble_ and _epilogue_ don't yield\n- Browser/Node Compatible\n- Plugs into existing libraries like Relay and rxjs\n\n[^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.\n\n## 🚀 Usage\n\n\u003e Visit [/examples](/examples) for more info!\n\n```ts\n// Relies on bundler/environment detection\nimport { meros } from 'meros';\n\nconst parts = await fetch('/api').then(meros);\n\n// As a simple Async Generator\nfor await (const part of parts) {\n  // Do something with this part\n}\n\n// Used with rxjs streams\nfrom(parts).subscribe((part) =\u003e {\n  // Do something with it\n});\n```\n\n## _Specific Environment_\n\n#### _Browser_\n\n```ts\nimport { meros } from 'meros/browser';\n// import { meros } from 'https://cdn.skypack.dev/meros';\n\nconst parts = await fetch('/api').then(meros);\n```\n\n#### _Node_\n\n```ts\nimport http from 'http';\nimport { meros } from 'meros/node';\n\nconst response = await new Promise((resolve) =\u003e {\n  const request = http.get(`http://example.com/api`, (response) =\u003e {\n    resolve(response);\n  });\n  request.end();\n});\n\nconst parts = await meros(response);\n```\n\n## 🔎 API\n\nMeros offers two flavours, both for the browser and for node; but their api's are fundamentally the same.\n\n\u003e **Note**: The type `Response` is used loosely here and simply alludes to Node's `IncomingMessage` or the browser's\n\u003e `Response` type.\n\n### `meros(response: Response, options?: Options)`\n\nReturns: `Promise\u003cResponse | AsyncGenerator\u003cPart | Part[]\u003e\u003e`\n\nMeros returns a promise that will resolve to an `AsyncGenerator` if the response is of `multipart/mixed` mime, or simply\nreturns the `Response` if something else; helpful for middlewares. The idea here being that you run meros as a chain off\nfetch.\n\n```ts\nfetch('/api').then(meros);\n```\n\n\u003e If the `content-type` is **NOT** a multipart, then meros will resolve with the response argument.\n\u003e\n\u003e \u003cdetails\u003e\n\u003e \u003csummary\u003eExample on how to handle this case\u003c/summary\u003e\n\u003e\n\u003e ```ts\n\u003e import { meros } from 'meros';\n\u003e\n\u003e const response = await fetch('/api'); // Assume this isnt multipart\n\u003e const parts = await meros(response);\n\u003e\n\u003e if (parts[Symbol.asyncIterator] \u003c 'u') {\n\u003e   for await (const part of parts) {\n\u003e     // Do something with this part\n\u003e   }\n\u003e } else {\n\u003e   const data = await parts.json();\n\u003e }\n\u003e ```\n\u003e\n\u003e \u003c/details\u003e\n\neach `Part` gives you access to:\n\n- `json: boolean` ~ Tells you the `body` would be a JavaScript object of your defined generic `T`.\n- `headers: object` ~ A key-value pair of all headers discovered from this part.\n- `body: T | Fallback` ~ Is the _body_ of the part, either as a JavaScript object (noted by `json`) _or_ the base type\n  of the environment (`Buffer | string`, for Node and Browser respectively).\n\n#### `options.multiple: boolean`\n\nDefault: `false`\n\nSetting this to `true` will yield once for all available parts of a chunk, rather than yielding once per part. This is\nan optimization technique for technologies like GraphQL where rather than commit the payload to the store, to be\nadded-to in the next process-tick we can simply do that synchronously.\n\n\u003e **Warning**: This will alter the behaviour and yield arrays—than yield payloads.\n\n```ts\nconst chunks = await fetch('/api').then((response) =\u003e meros(response, { multiple: true }));\n\n// As a simple Async Generator\nfor await (const parts of chunks) {\n  for (const part of parts) {\n    // Do something with this part, maybe aggregate?\n  }\n}\n```\n\n## 💨 Benchmark\n\n\u003e via the [`/bench`](/bench) directory with Node v18.0.0\n\n```\nNode\n✔ meros        ~ 1,271,218 ops/sec ± 0.84%\n✘ it-multipart ~   700,039 ops/sec ± 0.72%\n--\nit-multipart (FAILED @ \"should match reference patch set\")\n\nBrowser\n✔ meros                   ~ 800,941 ops/sec ± 1.06%\n✘ fetch-multipart-graphql ~ 502,175 ops/sec ± 0.75%\n--\nfetch-multipart-graphql (FAILED @ \"should match reference patch set\")\n```\n\n## 🎒 Notes\n\nWhy the name? _meros_ comes from Ancient Greek μέρος méros, meaning \"part\".\n\nThis library aims to implement [RFC1341] in its entirety, however we aren't there yet. That being said, you may very\nwell use this library in other scenarios like streaming in file form uploads.\n\nAnother goal here is to aide in being the defacto standard transport library to support\n[`@defer` and `@stream` GraphQL directives](https://foundation.graphql.org/news/2020/12/08/improving-latency-with-defer-and-stream-directives/)\n\n### _Caveats_\n\n- No support the `/alternative` , `/digest` _or_ `/parallel` subtype at this time.\n- No support for [nested multiparts](https://tools.ietf.org/html/rfc1341#appendix-C)\n\n## ❤ Thanks\n\nSpecial thanks to [Luke Edwards](https://github.com/lukeed) for performance guidance and high level api design.\n\n## 😇 Compassion\n\nThis 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\nall for the freedom of software. But please give credit where credit is due.\n\n## License\n\nMIT © [Marais Rossouw](https://marais.io)\n\n[rfc1341]: https://tools.ietf.org/html/rfc1341 'The Multipart Content-Type'\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaraisr%2Fmeros","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaraisr%2Fmeros","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaraisr%2Fmeros/lists"}