Socket
Socket
Sign inDemoInstall

meros

Package Overview
Dependencies
2
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.2.0 to 1.2.1

41

browser/index.d.ts

@@ -1,23 +0,20 @@

interface Options {
/**
* Setting this to true will yield an array. In other words; instead of yielding once for every
* payload—we collect all complete payloads for a chunk and then yield.
*
* @default false
*/
multiple: boolean;
}
import type { Options, Part } from 'meros';
type Part<Body, Fallback> =
| { json: false; headers: Record<string, string>; body: Fallback }
| { json: true; headers: Record<string, string>; body: Body };
declare function meros<T = object>(response: Response, options: {
multiple: true;
}): Promise<Response | AsyncGenerator<ReadonlyArray<Part<T, string>>>>;
declare function meros<T = object>(response: Response, options?: {
multiple: false;
}): Promise<Response | AsyncGenerator<Part<T, string>>>;
declare function meros<T = object>(response: Response, options?: Options): Promise<Response | AsyncGenerator<Part<T, string>>>;
export { meros };
/**
* Yield immediately for every part made available on the response. If the `content-type` of the
* response isn't a multipart body, then we'll resolve with {@link Response}.
*
* @example
*
* ```js
* const parts = await fetch('/fetch-multipart')
* .then(meros);
*
* for await (const part of parts) {
* // do something with this part
* }
* ```
*/
export function meros<T = object>(response: Response, options: { multiple: true }): Promise<Response | AsyncGenerator<ReadonlyArray<Part<T, string>>>>;
export function meros<T = object>(response: Response, options?: { multiple: false }): Promise<Response | AsyncGenerator<Part<T, string>>>;
export function meros<T = object>(response: Response, options?: Options): Promise<Response | AsyncGenerator<Part<T, string>>>;

@@ -1,99 +0,1 @@

const separator = '\r\n\r\n';
const decoder = new TextDecoder;
async function* generate(stream, boundary, options) {
const reader = stream.getReader(), is_eager = !options || !options.multiple;
let buffer = '', is_preamble = true, payloads = [];
try {
let result;
outer: while (!(result = await reader.read()).done) {
const chunk = decoder.decode(result.value);
const idx_chunk = chunk.indexOf(boundary);
let idx_boundary = buffer.length;
buffer += chunk;
if (!!~idx_chunk) {
// chunk itself had `boundary` marker
idx_boundary += idx_chunk;
}
else {
// search combined (boundary can be across chunks)
idx_boundary = buffer.indexOf(boundary);
}
payloads = [];
while (!!~idx_boundary) {
const current = buffer.substring(0, idx_boundary);
const next = buffer.substring(idx_boundary + boundary.length);
if (is_preamble) {
is_preamble = false;
boundary = '\r\n' + boundary;
}
else {
const headers = {};
const idx_headers = current.indexOf(separator);
const arr_headers = buffer.slice(0, idx_headers).trim().split(/\r\n/);
// parse headers
let tmp;
while (tmp = arr_headers.shift()) {
tmp = tmp.split(': ');
headers[tmp.shift().toLowerCase()] = tmp.join(': ');
}
const last_idx = current.lastIndexOf('\r\n', idx_headers + separator.length);
let body = current.substring(idx_headers + separator.length, last_idx > -1 ? undefined : last_idx);
let is_json = false;
tmp = headers['content-type'];
if (tmp && !!~tmp.indexOf('application/json')) {
try {
body = JSON.parse(body);
is_json = true;
}
catch (_) {
}
}
tmp = { headers, body, json: is_json };
is_eager ? yield tmp : payloads.push(tmp);
// hit a tail boundary, break
if (next.substring(0, 2) === '--')
break outer;
}
buffer = next;
idx_boundary = buffer.indexOf(boundary);
}
if (payloads.length)
yield payloads;
}
}
finally {
if (payloads.length)
yield payloads;
reader.releaseLock();
}
}
/**
* Yield immediately for every part made available on the response. If the `content-type` of the
* response isn't a multipart body, then we'll resolve with {@link Response}.
*
* @example
*
* ```js
* const parts = await fetch('/fetch-multipart')
* .then(meros);
*
* for await (const part of parts) {
* // do something with this part
* }
* ```
*/
async function meros(response, options) {
if (!response.ok || !response.body || response.bodyUsed)
return response;
const ctype = response.headers.get('content-type');
if (!ctype || !~ctype.indexOf('multipart/mixed'))
return response;
const idx_boundary = ctype.indexOf('boundary=');
return generate(response.body, `--${!!~idx_boundary
? // +9 for 'boundary='.length
ctype.substring(idx_boundary + 9).trim().replace(/['"]/g, '')
: '-'}`, options);
}
exports.meros = meros;
var e=new TextDecoder;async function t(t,n){if(!t.ok||!t.body||t.bodyUsed)return t;const i=t.headers.get("content-type");if(!i||!~i.indexOf("multipart/mixed"))return t;const r=i.indexOf("boundary="),s=r+9,o=i.indexOf(";",s);return async function*(t,n,i){const r=t.getReader(),s=!i||!i.multiple;let o=n.length,d="",l=!0,a=[];try{let t;e:for(;!(t=await r.read()).done;){const i=e.decode(t.value),r=i.indexOf(n);let c=d.length;for(d+=i,~r?c+=r:c=d.indexOf(n),a=[];~c;){const e=d.substring(0,c),t=d.substring(c+o);if(l)l=!1,n="\r\n"+n,o+=2;else{const n={},i=e.indexOf("\r\n\r\n")+4,r=String(d.slice(0,i)).trim().split("\r\n");let o;for(;o=r.shift();)o=o.split(": "),n[o.shift().toLowerCase()]=o.join(": ");const l=e.lastIndexOf("\r\n",i);let c=e.substring(i,l>-1?void 0:l),f=!1;if(o=n["content-type"],o&&~o.indexOf("application/json"))try{c=JSON.parse(c),f=!0}catch(e){}if(o={headers:n,body:c,json:f},s?yield o:a.push(o),"--"===t.substring(0,2))break e}d=t,c=d.indexOf(n)}a.length&&(yield a)}}finally{a.length&&(yield a),await r.cancel()}}(t.body,`--${~r?i.substring(s,o>-1?o:void 0).replace(/['"]/g,"").trim():"-"}`,n)}exports.meros=t;

@@ -1,5 +0,2 @@

/// <reference types="node" />
import { IncomingMessage } from 'http';
interface Options {
export interface Options {
/**

@@ -14,80 +11,7 @@ * Setting this to true will yield an array. In other words; instead of yielding once for every payload—we collect

type Part<Body, Fallback> =
export type Part<Body, Fallback> =
| { json: false; headers: Record<string, string>; body: Fallback }
| { json: true; headers: Record<string, string>; body: Body };
// Browser~
declare function meros<T = object>(
response: Response,
options: {
multiple: true;
},
): Promise<Response | AsyncGenerator<ReadonlyArray<Part<T, Buffer>>>>;
declare function meros<T = object>(
response: Response,
options?: {
multiple: false;
},
): Promise<Response | AsyncGenerator<Part<T, Buffer>>>;
/**
* Yield immediately for every part made available on the response. If the `content-type` of the response isn't a
* multipart body, then we'll resolve with {@link Response}.
*
* @example
*
* ```js
* const parts = await fetch('/fetch-multipart')
* .then(meros);
*
* for await (const part of parts) {
* // do something with this part
* }
* ```
*/
declare function meros<T = object>(
response: Response,
options?: Options,
): Promise<Response | AsyncGenerator<Part<T, Buffer>>>;
// Node~
declare function meros<T = object>(
response: IncomingMessage,
options: {
multiple: true;
},
): Promise<IncomingMessage | AsyncGenerator<ReadonlyArray<Part<T, Buffer>>>>;
declare function meros<T = object>(
response: IncomingMessage,
options?: {
multiple: false;
},
): Promise<IncomingMessage | AsyncGenerator<Part<T, Buffer>>>;
/**
* Yield immediately for every part made available on the response. If the `content-type` of the response isn't a
* multipart body, then we'll resolve with {@link IncomingMessage}.
*
* @example
*
* ```js
* 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);
*
* for await (const part of parts) {
* // do something with this part
* }
* ```
*/
declare function meros<T = object>(
response: IncomingMessage,
options?: Options,
): Promise<IncomingMessage | AsyncGenerator<Part<T, Buffer>>>;
export { meros };
export * from 'meros/browser';
export * from 'meros/node';

@@ -1,25 +0,27 @@

import { IncomingMessage } from 'http';
import type { IncomingMessage } from 'node:http';
import type { Options, Part } from 'meros';
interface Options {
/**
* Setting this to true will yield an array. In other words; instead of yielding once for every
* payload—we collect all complete payloads for a chunk and then yield.
*
* @default false
*/
multiple: boolean;
}
type Part<Body, Fallback> =
| { json: false; headers: Record<string, string>; body: Fallback }
| { json: true; headers: Record<string, string>; body: Body };
declare function meros<T = object>(response: IncomingMessage, options: {
multiple: true;
}): Promise<IncomingMessage | AsyncGenerator<ReadonlyArray<Part<T, Buffer>>>>;
declare function meros<T = object>(response: IncomingMessage, options?: {
multiple: false;
}): Promise<IncomingMessage | AsyncGenerator<Part<T, Buffer>>>;
declare function meros<T = object>(response: IncomingMessage, options?: Options): Promise<IncomingMessage | AsyncGenerator<Part<T, Buffer>>>;
export { meros };
/**
* Yield immediately for every part made available on the response. If the `content-type` of the
* response isn't a multipart body, then we'll resolve with {@link IncomingMessage}.
*
* @example
*
* ```js
* 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);
*
* for await (const part of parts) {
* // do something with this part
* }
* ```
*/
export function meros<T = object>(response: IncomingMessage, options: { multiple: true }): Promise<IncomingMessage | AsyncGenerator<ReadonlyArray<Part<T, Buffer>>>>;
export function meros<T = object>(response: IncomingMessage, options?: { multiple: false }): Promise<IncomingMessage | AsyncGenerator<Part<T, Buffer>>>;
export function meros<T = object>(response: IncomingMessage, options?: Options): Promise<IncomingMessage | AsyncGenerator<Part<T, Buffer>>>;

@@ -1,96 +0,1 @@

const separator = '\r\n\r\n';
async function* generate(stream, boundary, options) {
const is_eager = !options || !options.multiple;
let len_boundary = Buffer.byteLength(boundary), buffer = Buffer.alloc(0), is_preamble = true, payloads = [];
outer: for await (const chunk of stream) {
let idx_boundary = buffer.byteLength;
buffer = Buffer.concat([buffer, chunk]);
const idx_chunk = chunk.indexOf(boundary);
if (!!~idx_chunk) {
// chunk itself had `boundary` marker
idx_boundary += idx_chunk;
}
else {
// search combined (boundary can be across chunks)
idx_boundary = buffer.indexOf(boundary);
}
payloads = [];
while (!!~idx_boundary) {
const current = buffer.slice(0, idx_boundary);
const next = buffer.slice(idx_boundary + len_boundary);
if (is_preamble) {
is_preamble = false;
boundary = '\r\n' + boundary;
len_boundary += 2;
}
else {
const headers = {};
const idx_headers = current.indexOf(separator);
const arr_headers = buffer.slice(0, idx_headers).toString().trim().split(/\r\n/);
// parse headers
let tmp;
while (tmp = arr_headers.shift()) {
tmp = tmp.split(': ');
headers[tmp.shift().toLowerCase()] = tmp.join(': ');
}
const last_idx = current.lastIndexOf('\r\n', idx_headers + separator.length);
let body = current.slice(idx_headers + separator.length, last_idx > -1 ? undefined : last_idx);
let is_json = false;
tmp = headers['content-type'];
if (tmp && !!~tmp.indexOf('application/json')) {
try {
body = JSON.parse(body.toString());
is_json = true;
}
catch (_) {
}
}
tmp = { headers, body, json: is_json };
is_eager ? yield tmp : payloads.push(tmp);
// hit a tail boundary, break
if (next.slice(0, 2).toString() === '--')
break outer;
}
buffer = next;
idx_boundary = buffer.indexOf(boundary);
}
if (payloads.length)
yield payloads;
}
if (payloads.length)
yield payloads;
}
/**
* Yield immediately for every part made available on the response. If the `content-type` of the
* response isn't a multipart body, then we'll resolve with {@link IncomingMessage}.
*
* @example
*
* ```js
* 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);
*
* for await (const part of parts) {
* // do something with this part
* }
* ```
*/
async function meros(response, options) {
const ctype = response.headers['content-type'];
if (!ctype || !~ctype.indexOf('multipart/mixed'))
return response;
const idx_boundary = ctype.indexOf('boundary=');
return generate(response, `--${!!~idx_boundary
? // +9 for 'boundary='.length
ctype.substring(idx_boundary + 9).trim().replace(/['"]/g, '')
: '-'}`, options);
}
exports.meros = meros;
async function e(e,t){const n=e.headers["content-type"];if(!n||!~n.indexOf("multipart/mixed"))return e;const i=n.indexOf("boundary="),r=i+9,o=n.indexOf(";",r);return async function*(e,t,n){const i=!n||!n.multiple;let r=Buffer.byteLength(t),o=Buffer.alloc(0),s=!0,f=[];e:for await(const n of e){const e=n.indexOf(t);let c=o.byteLength;for(o=Buffer.concat([o,n]),~e?c+=e:c=o.indexOf(t),f=[];~c;){const e=o.slice(0,c),n=o.slice(c+r);if(s)s=!1,t="\r\n"+t,r+=2;else{const t={},r=e.indexOf("\r\n\r\n")+4,s=String(o.slice(0,r)).trim().split("\r\n");let c;for(;c=s.shift();)c=c.split(": "),t[c.shift().toLowerCase()]=c.join(": ");const l=e.lastIndexOf("\r\n",r);let d=e.slice(r,l>-1?void 0:l),a=!1;if(c=t["content-type"],c&&~c.indexOf("application/json"))try{d=JSON.parse(String(d)),a=!0}catch(e){}if(c={headers:t,body:d,json:a},i?yield c:f.push(c),45===n[0]&&45===n[1])break e}o=n,c=o.indexOf(t)}f.length&&(yield f)}f.length&&(yield f)}(e,`--${~i?n.substring(r,o>-1?o:void 0).replace(/['"]/g,"").trim():"-"}`,t)}exports.meros=e;
{
"name": "meros",
"version": "1.2.0",
"version": "1.2.1",
"description": "A fast 626B utility that makes reading multipart responses simple",

@@ -26,2 +26,3 @@ "keywords": [

"browser": {
"types": "./browser/index.d.ts",
"import": "./browser/index.mjs",

@@ -31,2 +32,3 @@ "require": "./browser/index.js"

"node": {
"types": "./node/index.d.ts",
"import": "./node/index.mjs",

@@ -36,2 +38,3 @@ "require": "./node/index.js"

"default": {
"types": "./node/index.d.ts",
"import": "./node/index.mjs",

@@ -42,2 +45,3 @@ "require": "./node/index.js"

"./browser": {
"types": "./browser/index.d.ts",
"import": "./browser/index.mjs",

@@ -47,2 +51,3 @@ "require": "./browser/index.js"

"./node": {
"types": "./node/index.d.ts",
"import": "./node/index.mjs",

@@ -54,3 +59,2 @@ "require": "./node/index.js"

"main": "node/index.js",
"unpkg": "browser/index.min.js",
"module": "node/index.mjs",

@@ -61,3 +65,2 @@ "browser": "browser/index.mjs",

"*.d.ts",
"types",
"browser",

@@ -68,4 +71,4 @@ "node"

"bench": "node -r tsm -r ./test/_polyfill.ts bench/index.ts",
"build": "rollup -c",
"format": "prettier --write --list-different \"{*,examples/*/**,.github/**/*}.+(ts|tsx|json|yml|md)\"",
"build": "bundt --minify",
"format": "prettier --write --list-different \"{*,test/**/*,examples/*/**,.github/**/*}.+(ts|tsx|json|yml|md)\"",
"prepublishOnly": "pnpm run build",

@@ -76,16 +79,13 @@ "test": "uvu test \".spec.ts$\" -r tsm -r test/_polyfill.ts -i suites",

"devDependencies": {
"@marais/tsconfig": "0.0.2",
"@marais/tsconfig": "0.0.3",
"@n1ru4l/push-pull-async-iterable-iterator": "3.2.0",
"@rollup/plugin-node-resolve": "13.1.3",
"@types/node": "17.0.21",
"prettier": "2.5.1",
"rollup": "2.68.0",
"rollup-plugin-dts": "4.1.0",
"rollup-plugin-terser": "7.0.2",
"tsm": "2.2.1",
"typescript": "4.5.5",
"@types/node": "18.7.16",
"bundt": "2.0.0-next.5",
"prettier": "2.7.1",
"tsm": "2.2.2",
"typescript": "4.8.3",
"uvu": "0.5.3"
},
"peerDependencies": {
"@types/node": ">=12"
"@types/node": ">=13"
},

@@ -98,7 +98,7 @@ "peerDependenciesMeta": {

"engines": {
"node": ">=12"
"node": ">=13"
},
"volta": {
"node": "17.3.0"
"node": "18.0.0"
}
}
<img src="logo.svg" alt="meros">
<br />

@@ -6,16 +7,10 @@ <br />

<div align="right">
<p><code>npm add meros</code> makes reading multipart responses simple</p>
<span>
<a href="https://github.com/maraisr/meros/actions/workflows/ci.yml">
<img src="https://github.com/maraisr/meros/actions/workflows/ci.yml/badge.svg"/>
</a>
<a href="https://npm-stat.com/charts.html?package=meros">
<img src="https://badgen.net/npm/dw/meros?labelColor=black&color=black&cache=600" alt="downloads"/>
</a>
<a href="https://bundlephobia.com/result?p=meros">
<img src="https://badgen.net/bundlephobia/minzip/meros?labelColor=black&color=black" alt="size"/>
</a>
</span>
<br />
<br />
`npm add meros` makes reading multipart responses simple
[![npm stats](https://badgen.net/npm/dw/meros)](https://npm-stat.com/charts.html?package=meros)
[![bundle size](https://badgen.net/bundlephobia/minzip/meros)](https://bundlephobia.com/result?p=meros)
<br />
</div>

@@ -28,3 +23,3 @@

- Super [performant](#-benchmark)
- Supports _any_<sup>1</sup> `content-type`
- Supports _any_[^1] `content-type`
- _preamble_ and _epilogue_ don't yield

@@ -34,2 +29,4 @@ - Browser/Node Compatible

[^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.
## 🚀 Usage

@@ -87,3 +84,3 @@

> **Note:** The type `Response` is used loosely here and simply alludes to Node's `IncomingMessage` or the browser's
> **Note**: The type `Response` is used loosely here and simply alludes to Node's `IncomingMessage` or the browser's
> `Response` type.

@@ -140,3 +137,3 @@

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

@@ -156,3 +153,3 @@ ```ts

> via the [`/bench`](/bench) directory with Node v16.11.0
> via the [`/bench`](/bench) directory with Node v18.0.0

@@ -165,4 +162,4 @@ ```

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)
meros x 655,624 ops/sec ±0.29% (90 runs sampled)
it-multipart x 425,522 ops/sec ±0.29% (89 runs sampled)

@@ -174,4 +171,4 @@ Validation :: browser

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)
meros x 1,544,869 ops/sec ±0.15% (90 runs sampled)
fetch-multipart-graphql x 731,527 ops/sec ±0.44% (90 runs sampled)
```

@@ -198,13 +195,14 @@

## License
## 😇 Compassion
MIT © [Marais Rossouw](https://marais.io)
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.
<details>
<summary>Footnote</summary>
- [urql](https://github.com/FormidableLabs/urql/blob/78368cf5bfcdd04bd663d8775d8883962128376b/packages/core/src/internal/fetchSource.ts#L34-L35)
you guys rock 🫶
> 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.
## License
</details>
MIT © [Marais Rossouw](https://marais.io)
[rfc1341]: https://tools.ietf.org/html/rfc1341 'The Multipart Content-Type'

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc