meros
Advanced tools
Comparing version 1.2.0 to 1.2.1
@@ -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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
8
15748
10
59
200
1