Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@datastream/fetch

Package Overview
Dependencies
Maintainers
1
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@datastream/fetch - npm Package Compare versions

Comparing version
0.0.42
to
0.1.4
+56
index.d.ts
// Copyright 2026 will Farrell, and datastream contributors.
// SPDX-License-Identifier: MIT
import type { StreamOptions, StreamResult } from "@datastream/core";
export interface FetchOptions {
url?: string;
method?: string;
headers?: Record<string, string>;
body?: unknown;
mode?: RequestMode;
credentials?: RequestCredentials;
cache?: RequestCache;
redirect?: RequestRedirect;
referrer?: string;
referrerPolicy?: ReferrerPolicy;
integrity?: string;
keepalive?: boolean;
duplex?: string;
rateLimit?: number;
dataPath?: string | string[];
nextPath?: string | string[];
qs?: Record<string, string | number>;
offsetParam?: string;
offsetAmount?: number;
rateLimitTimestamp?: number;
}
export function fetchSetDefaults(options: Partial<FetchOptions>): void;
export function fetchWritableStream(
options: FetchOptions,
streamOptions?: StreamOptions,
): Promise<
unknown & {
result: () => StreamResult<Response>;
}
>;
export { fetchWritableStream as fetchRequestStream };
export function fetchReadableStream(
fetchOptions: FetchOptions | FetchOptions[],
streamOptions?: StreamOptions,
): unknown;
export { fetchReadableStream as fetchResponseStream };
export function fetchRateLimit(
options: FetchOptions,
streamOptions?: StreamOptions,
): Promise<Response>;
declare const _default: {
setDefaults: typeof fetchSetDefaults;
readableStream: typeof fetchReadableStream;
responseStream: typeof fetchReadableStream;
};
export default _default;
<div align="center">
<h1>&lt;datastream&gt; `fetch`</h1>
<img alt="datastream logo" src="https://raw.githubusercontent.com/willfarrell/datastream/main/docs/img/datastream-logo.svg"/>
<p><strong>HTTP fetch streams.</strong></p>
<p>
<a href="https://github.com/willfarrell/datastream/actions/workflows/test-unit.yml"><img src="https://github.com/willfarrell/datastream/actions/workflows/test-unit.yml/badge.svg" alt="GitHub Actions unit test status"></a>
<a href="https://github.com/willfarrell/datastream/actions/workflows/test-dast.yml"><img src="https://github.com/willfarrell/datastream/actions/workflows/test-dast.yml/badge.svg" alt="GitHub Actions dast test status"></a>
<a href="https://github.com/willfarrell/datastream/actions/workflows/test-perf.yml"><img src="https://github.com/willfarrell/datastream/actions/workflows/test-perf.yml/badge.svg" alt="GitHub Actions perf test status"></a>
<a href="https://github.com/willfarrell/datastream/actions/workflows/test-sast.yml"><img src="https://github.com/willfarrell/datastream/actions/workflows/test-sast.yml/badge.svg" alt="GitHub Actions SAST test status"></a>
<a href="https://github.com/willfarrell/datastream/actions/workflows/test-lint.yml"><img src="https://github.com/willfarrell/datastream/actions/workflows/test-lint.yml/badge.svg" alt="GitHub Actions lint test status"></a>
<br/>
<a href="https://www.npmjs.com/package/@datastream/fetch"><img alt="npm version" src="https://img.shields.io/npm/v/@datastream/fetch.svg"></a>
<a href="https://packagephobia.com/result?p=@datastream/fetch"><img src="https://packagephobia.com/badge?p=@datastream/fetch" alt="npm install size"></a>
<a href="https://www.npmjs.com/package/@datastream/fetch">
<img alt="npm weekly downloads" src="https://img.shields.io/npm/dw/@datastream/fetch.svg"></a>
<a href="https://www.npmjs.com/package/@datastream/fetch#provenance">
<img alt="npm provenance" src="https://img.shields.io/badge/provenance-Yes-brightgreen"></a>
<br/>
<a href="https://scorecard.dev/viewer/?uri=github.com/willfarrell/datastream"><img src="https://api.scorecard.dev/projects/github.com/willfarrell/datastream/badge" alt="Open Source Security Foundation (OpenSSF) Scorecard"></a>
<a href="https://slsa.dev"><img src="https://slsa.dev/images/gh-badge-level3.svg" alt="SLSA 3"></a>
<a href="https://github.com/willfarrell/datastream/blob/main/docs/CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg"></a>
<a href="https://biomejs.dev"><img alt="Checked with Biome" src="https://img.shields.io/badge/Checked_with-Biome-60a5fa?style=flat&logo=biome"></a>
<a href="https://conventionalcommits.org"><img alt="Conventional Commits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white"></a>
<a href="https://github.com/willfarrell/datastream/blob/main/package.json#L32">
<img alt="code coverage" src="https://img.shields.io/badge/code%20coverage-95%25-brightgreen"></a>
</p>
<p>You can read the documentation at: <a href="https://datastream.js.org">https://datastream.js.org</a></p>
</div>
## Install
To install datastream you can use NPM:
```bash
npm install --save @datastream/fetch
```
## Documentation and examples
For documentation and examples, refer to the main [datastream monorepo on GitHub](https://github.com/willfarrell/datastream) or [datastream official website](https://datastream.js.org).
## Contributing
Everyone is very welcome to contribute to this repository. Feel free to [raise issues](https://github.com/willfarrell/datastream/issues) or to [submit Pull Requests](https://github.com/willfarrell/datastream/pulls).
## License
Licensed under [MIT License](LICENSE). Copyright (c) 2026 [will Farrell](https://github.com/willfarrell), and [datastream contributors](https://github.com/willfarrell/datastream/graphs/contributors).
+41
-11

@@ -140,13 +140,46 @@ import {

options = mergeOptions(options);
const response = await fetch(options.url, {
...options,
const {
method,
headers,
body,
mode,
credentials,
cache,
redirect,
referrer,
referrerPolicy,
integrity,
keepalive,
duplex
} = options;
const fetchInit = {
method,
headers,
body,
mode,
credentials,
cache,
redirect,
referrer,
referrerPolicy,
integrity,
keepalive,
duplex,
signal: streamOptions.signal
});
};
const response = await fetch(options.url, fetchInit);
if (!response.ok) {
if (response.statusCode === 429) {
if (response.status === 429) {
return fetchRateLimit(options, streamOptions);
}
throw new Error("fetch", {
cause: { request: options, response }
});
throw new Error(
`fetch ${response.status} ${options.method} ${options.url}`,
{
cause: {
status: response.status,
url: options.url,
method: options.method
}
}
);
}

@@ -158,5 +191,3 @@ return response;

if (!Array.isArray(path)) path = path.split(".");
return path.slice(0).reduce((a, b) => {
return a[b];
}, obj);
return path.reduce((a, b) => a?.[b], obj);
};

@@ -167,3 +198,2 @@ var index_default = {

responseStream: fetchReadableStream
// writableStream: fetchRequestStream,
};

@@ -170,0 +200,0 @@ export {

{
"version": 3,
"sources": ["index.js"],
"sourcesContent": ["/* global fetch */\nimport {\n\tcreateReadableStream,\n\tcreateWritableStream,\n\ttimeout,\n} from \"@datastream/core\";\n\nconst defaults = {\n\t// custom\n\trateLimit: 0.01, // 100 per sec\n\tdataPath: undefined, // for json response, where the data is to return form body root\n\tnextPath: undefined, // for json pagination, body root\n\tqs: {}, // object to convert to query string\n\toffsetParam: undefined, // offset query parameter to use for pagination\n\toffsetAmount: undefined, // offset amount to use for pagination\n\n\t// fetch\n\tmethod: \"GET\",\n\theaders: {\n\t\tAccept: \"application/json\",\n\t\t\"Accept-Encoding\": \"br, gzip, deflate\",\n\t},\n};\n\nconst mergeOptions = (options = {}) => {\n\treturn {\n\t\t...defaults,\n\t\t...options,\n\t\theaders: { ...defaults.headers, ...options.headers },\n\t\tqs: { ...defaults.qs, ...options.qs },\n\t};\n};\n\nexport const fetchSetDefaults = (options) => {\n\tObject.assign(defaults, mergeOptions(options));\n};\n\n// Note: requires EncodeStream to ensure it's Uint8Array\n// Poor browser support - https://github.com/Fyrd/caniuse/issues/6375\n// TODO needs testing\n// TODO mulit-part upload\nexport const fetchWritableStream = async (options, streamOptions = {}) => {\n\tconst body = createReadableStream();\n\t// Duplex: half - For browser compatibility - https://developer.chrome.com/articles/fetch-streaming-requests/#half-duplex\n\toptions = mergeOptions(options);\n\tconst value = await fetchRateLimit({\n\t\t...options,\n\t\tbody,\n\t\tduplex: \"half\",\n\t\tsignal: streamOptions.signal,\n\t});\n\tconst write = (chunk) => {\n\t\tbody.push(chunk);\n\t};\n\tconst stream = createWritableStream(write, streamOptions);\n\tstream.result = () => ({ key: \"output\", value });\n\treturn stream;\n};\nexport const fetchRequestStream = fetchWritableStream;\n\nexport const fetchReadableStream = (fetchOptions, streamOptions = {}) => {\n\treturn createReadableStream(\n\t\tfetchGenerator(fetchOptions, streamOptions),\n\t\tstreamOptions,\n\t);\n};\nexport const fetchResponseStream = fetchReadableStream;\n\nasync function* fetchGenerator(fetchOptions, streamOptions) {\n\tlet rateLimitTimestamp = 0;\n\tif (!Array.isArray(fetchOptions)) fetchOptions = [fetchOptions];\n\tfor (let options of fetchOptions) {\n\t\toptions = mergeOptions(options);\n\t\toptions.rateLimitTimestamp ??= rateLimitTimestamp;\n\n\t\tif (options.offsetParam) {\n\t\t\toptions.qs[options.offsetParam] ??= 0;\n\t\t}\n\n\t\tif (Object.keys(options.qs).length) {\n\t\t\toptions.url += `?${new URLSearchParams(options.qs)}`.replaceAll(\n\t\t\t\t\"+\",\n\t\t\t\t\"%20\",\n\t\t\t);\n\t\t}\n\t\tconst response = await fetchUnknown(options, streamOptions);\n\t\tfor await (const chunk of response) {\n\t\t\tyield chunk;\n\t\t}\n\t\t// ensure there is rate limiting between req with different options\n\t\trateLimitTimestamp = options.rateLimitTimestamp;\n\t}\n}\n\nconst jsonContentTypeRegExp = /^application\\/(.+\\+)?json($|;.+)/;\nconst fetchUnknown = async (options, streamOptions) => {\n\tconst response = await fetchRateLimit(options, streamOptions);\n\tif (jsonContentTypeRegExp.test(response.headers.get(\"Content-Type\"))) {\n\t\toptions.prefetchResponse = response; // hack\n\t\treturn fetchJson(options, streamOptions);\n\t}\n\treturn response.body;\n};\n\nconst nextLinkRegExp = /<(.*?)>; rel=\"next\"/;\n\nasync function* fetchJson(options, streamOptions) {\n\tconst { dataPath, nextPath } = options;\n\tlet { url } = options;\n\n\twhile (options.url) {\n\t\tconst response =\n\t\t\toptions.prefetchResponse ??\n\t\t\t(await fetchRateLimit(options, streamOptions));\n\t\tdelete options.prefetchResponse;\n\t\tconst body = await response.json();\n\t\turl = parseLinkFromHeader(response.headers);\n\t\turl ??= parseNextPath(body, nextPath);\n\t\turl ??= paginateUsingQuery(options);\n\t\toptions.url = url;\n\t\tconst data = pickPath(body, dataPath);\n\t\tif (Array.isArray(data)) {\n\t\t\tfor (const item of data) {\n\t\t\t\tyield item;\n\t\t\t}\n\n\t\t\tif (options.offsetParam && !data.length) break;\n\t\t} else {\n\t\t\tyield data;\n\t\t}\n\t}\n}\n\nconst paginateUsingQuery = (options) => {\n\tif (!options.offsetParam || !options.offsetAmount) return undefined;\n\n\tconst url = new URL(options.url);\n\tlet offset = url.searchParams.get(options.offsetParam);\n\tif (!offset) return null;\n\n\toffset = Number.parseInt(offset, 10) + options.offsetAmount;\n\turl.searchParams.delete(options.offsetParam);\n\turl.searchParams.set(options.offsetParam, offset);\n\treturn url.toString();\n};\n\nconst parseNextPath = (body, nextPath) => {\n\treturn nextPath ? pickPath(body, nextPath) : undefined;\n};\n\nconst parseLinkFromHeader = (headers) => {\n\tconst link = headers.get(\"Link\");\n\treturn link?.match(nextLinkRegExp)?.[1];\n};\n\nexport const fetchRateLimit = async (options, streamOptions = {}) => {\n\tconst now = Date.now();\n\tif (now < (options.rateLimitTimestamp ?? 0)) {\n\t\tawait timeout(options.rateLimitTimestamp - now, streamOptions);\n\t}\n\toptions.rateLimitTimestamp = Date.now() + 1000 * options.rateLimit;\n\toptions = mergeOptions(options); // for when called directly\n\n\tconst response = await fetch(options.url, {\n\t\t...options,\n\t\tsignal: streamOptions.signal,\n\t});\n\tif (!response.ok) {\n\t\t// 429 Too Many Requests\n\t\tif (response.statusCode === 429) {\n\t\t\treturn fetchRateLimit(options, streamOptions);\n\t\t}\n\t\tthrow new Error(\"fetch\", {\n\t\t\tcause: { request: options, response },\n\t\t});\n\t}\n\treturn response;\n};\n\nconst pickPath = (obj, path = \"\") => {\n\tif (path === \"\") return obj;\n\tif (!Array.isArray(path)) path = path.split(\".\");\n\treturn path\n\t\t.slice(0) // clone\n\t\t.reduce((a, b) => {\n\t\t\treturn a[b];\n\t\t}, obj);\n};\n\nexport default {\n\tsetDefaults: fetchSetDefaults,\n\treadableStream: fetchReadableStream,\n\tresponseStream: fetchReadableStream,\n\t// writableStream: fetchRequestStream,\n};\n"],
"mappings": "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",
"sourcesContent": ["// Copyright 2026 will Farrell, and datastream contributors.\n// SPDX-License-Identifier: MIT\n/* global fetch */\nimport {\n\tcreateReadableStream,\n\tcreateWritableStream,\n\ttimeout,\n} from \"@datastream/core\";\n\nconst defaults = {\n\t// custom\n\trateLimit: 0.01, // 100 per sec\n\tdataPath: undefined, // for json response, where the data is to return form body root\n\tnextPath: undefined, // for json pagination, body root\n\tqs: {}, // object to convert to query string\n\toffsetParam: undefined, // offset query parameter to use for pagination\n\toffsetAmount: undefined, // offset amount to use for pagination\n\n\t// fetch\n\tmethod: \"GET\",\n\theaders: {\n\t\tAccept: \"application/json\",\n\t\t\"Accept-Encoding\": \"br, gzip, deflate\",\n\t},\n};\n\nconst mergeOptions = (options = {}) => {\n\treturn {\n\t\t...defaults,\n\t\t...options,\n\t\theaders: { ...defaults.headers, ...options.headers },\n\t\tqs: { ...defaults.qs, ...options.qs },\n\t};\n};\n\nexport const fetchSetDefaults = (options) => {\n\tObject.assign(defaults, mergeOptions(options));\n};\n\n// Note: requires EncodeStream to ensure it's Uint8Array\n// Poor browser support - https://github.com/Fyrd/caniuse/issues/6375\nexport const fetchWritableStream = async (options, streamOptions = {}) => {\n\tconst body = createReadableStream();\n\t// Duplex: half - For browser compatibility - https://developer.chrome.com/articles/fetch-streaming-requests/#half-duplex\n\toptions = mergeOptions(options);\n\tconst value = await fetchRateLimit({\n\t\t...options,\n\t\tbody,\n\t\tduplex: \"half\",\n\t\tsignal: streamOptions.signal,\n\t});\n\tconst write = (chunk) => {\n\t\tbody.push(chunk);\n\t};\n\tconst stream = createWritableStream(write, streamOptions);\n\tstream.result = () => ({ key: \"output\", value });\n\treturn stream;\n};\nexport const fetchRequestStream = fetchWritableStream;\n\nexport const fetchReadableStream = (fetchOptions, streamOptions = {}) => {\n\treturn createReadableStream(\n\t\tfetchGenerator(fetchOptions, streamOptions),\n\t\tstreamOptions,\n\t);\n};\nexport const fetchResponseStream = fetchReadableStream;\n\nasync function* fetchGenerator(fetchOptions, streamOptions) {\n\tlet rateLimitTimestamp = 0;\n\tif (!Array.isArray(fetchOptions)) fetchOptions = [fetchOptions];\n\tfor (let options of fetchOptions) {\n\t\toptions = mergeOptions(options);\n\t\toptions.rateLimitTimestamp ??= rateLimitTimestamp;\n\n\t\tif (options.offsetParam) {\n\t\t\toptions.qs[options.offsetParam] ??= 0;\n\t\t}\n\n\t\tif (Object.keys(options.qs).length) {\n\t\t\toptions.url += `?${new URLSearchParams(options.qs)}`.replaceAll(\n\t\t\t\t\"+\",\n\t\t\t\t\"%20\",\n\t\t\t);\n\t\t}\n\t\tconst response = await fetchUnknown(options, streamOptions);\n\t\tfor await (const chunk of response) {\n\t\t\tyield chunk;\n\t\t}\n\t\t// ensure there is rate limiting between req with different options\n\t\trateLimitTimestamp = options.rateLimitTimestamp;\n\t}\n}\n\nconst jsonContentTypeRegExp = /^application\\/(.+\\+)?json($|;.+)/;\nconst fetchUnknown = async (options, streamOptions) => {\n\tconst response = await fetchRateLimit(options, streamOptions);\n\tif (jsonContentTypeRegExp.test(response.headers.get(\"Content-Type\"))) {\n\t\toptions.prefetchResponse = response; // hack\n\t\treturn fetchJson(options, streamOptions);\n\t}\n\treturn response.body;\n};\n\nconst nextLinkRegExp = /<(.*?)>; rel=\"next\"/;\n\nasync function* fetchJson(options, streamOptions) {\n\tconst { dataPath, nextPath } = options;\n\tlet { url } = options;\n\n\twhile (options.url) {\n\t\tconst response =\n\t\t\toptions.prefetchResponse ??\n\t\t\t(await fetchRateLimit(options, streamOptions));\n\t\tdelete options.prefetchResponse;\n\t\tconst body = await response.json();\n\t\turl = parseLinkFromHeader(response.headers);\n\t\turl ??= parseNextPath(body, nextPath);\n\t\turl ??= paginateUsingQuery(options);\n\t\toptions.url = url;\n\t\tconst data = pickPath(body, dataPath);\n\t\tif (Array.isArray(data)) {\n\t\t\tfor (const item of data) {\n\t\t\t\tyield item;\n\t\t\t}\n\n\t\t\tif (options.offsetParam && !data.length) break;\n\t\t} else {\n\t\t\tyield data;\n\t\t}\n\t}\n}\n\nconst paginateUsingQuery = (options) => {\n\tif (!options.offsetParam || !options.offsetAmount) return undefined;\n\n\tconst url = new URL(options.url);\n\tlet offset = url.searchParams.get(options.offsetParam);\n\tif (!offset) return null;\n\n\toffset = Number.parseInt(offset, 10) + options.offsetAmount;\n\turl.searchParams.delete(options.offsetParam);\n\turl.searchParams.set(options.offsetParam, offset);\n\treturn url.toString();\n};\n\nconst parseNextPath = (body, nextPath) => {\n\treturn nextPath ? pickPath(body, nextPath) : undefined;\n};\n\nconst parseLinkFromHeader = (headers) => {\n\tconst link = headers.get(\"Link\");\n\treturn link?.match(nextLinkRegExp)?.[1];\n};\n\nexport const fetchRateLimit = async (options, streamOptions = {}) => {\n\tconst now = Date.now();\n\tif (now < (options.rateLimitTimestamp ?? 0)) {\n\t\tawait timeout(options.rateLimitTimestamp - now, streamOptions);\n\t}\n\toptions.rateLimitTimestamp = Date.now() + 1000 * options.rateLimit;\n\toptions = mergeOptions(options); // for when called directly\n\n\tconst {\n\t\tmethod,\n\t\theaders,\n\t\tbody,\n\t\tmode,\n\t\tcredentials,\n\t\tcache,\n\t\tredirect,\n\t\treferrer,\n\t\treferrerPolicy,\n\t\tintegrity,\n\t\tkeepalive,\n\t\tduplex,\n\t} = options;\n\tconst fetchInit = {\n\t\tmethod,\n\t\theaders,\n\t\tbody,\n\t\tmode,\n\t\tcredentials,\n\t\tcache,\n\t\tredirect,\n\t\treferrer,\n\t\treferrerPolicy,\n\t\tintegrity,\n\t\tkeepalive,\n\t\tduplex,\n\t\tsignal: streamOptions.signal,\n\t};\n\tconst response = await fetch(options.url, fetchInit);\n\tif (!response.ok) {\n\t\t// 429 Too Many Requests\n\t\tif (response.status === 429) {\n\t\t\treturn fetchRateLimit(options, streamOptions);\n\t\t}\n\t\tthrow new Error(\n\t\t\t`fetch ${response.status} ${options.method} ${options.url}`,\n\t\t\t{\n\t\t\t\tcause: {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\turl: options.url,\n\t\t\t\t\tmethod: options.method,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\treturn response;\n};\n\nconst pickPath = (obj, path = \"\") => {\n\tif (path === \"\") return obj;\n\tif (!Array.isArray(path)) path = path.split(\".\");\n\treturn path.reduce((a, b) => a?.[b], obj);\n};\n\nexport default {\n\tsetDefaults: fetchSetDefaults,\n\treadableStream: fetchReadableStream,\n\tresponseStream: fetchReadableStream,\n};\n"],
"mappings": "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",
"names": []
}

@@ -140,13 +140,46 @@ import {

options = mergeOptions(options);
const response = await fetch(options.url, {
...options,
const {
method,
headers,
body,
mode,
credentials,
cache,
redirect,
referrer,
referrerPolicy,
integrity,
keepalive,
duplex
} = options;
const fetchInit = {
method,
headers,
body,
mode,
credentials,
cache,
redirect,
referrer,
referrerPolicy,
integrity,
keepalive,
duplex,
signal: streamOptions.signal
});
};
const response = await fetch(options.url, fetchInit);
if (!response.ok) {
if (response.statusCode === 429) {
if (response.status === 429) {
return fetchRateLimit(options, streamOptions);
}
throw new Error("fetch", {
cause: { request: options, response }
});
throw new Error(
`fetch ${response.status} ${options.method} ${options.url}`,
{
cause: {
status: response.status,
url: options.url,
method: options.method
}
}
);
}

@@ -158,5 +191,3 @@ return response;

if (!Array.isArray(path)) path = path.split(".");
return path.slice(0).reduce((a, b) => {
return a[b];
}, obj);
return path.reduce((a, b) => a?.[b], obj);
};

@@ -167,3 +198,2 @@ var index_default = {

responseStream: fetchReadableStream
// writableStream: fetchRequestStream,
};

@@ -170,0 +200,0 @@ export {

{
"version": 3,
"sources": ["index.js"],
"sourcesContent": ["/* global fetch */\nimport {\n\tcreateReadableStream,\n\tcreateWritableStream,\n\ttimeout,\n} from \"@datastream/core\";\n\nconst defaults = {\n\t// custom\n\trateLimit: 0.01, // 100 per sec\n\tdataPath: undefined, // for json response, where the data is to return form body root\n\tnextPath: undefined, // for json pagination, body root\n\tqs: {}, // object to convert to query string\n\toffsetParam: undefined, // offset query parameter to use for pagination\n\toffsetAmount: undefined, // offset amount to use for pagination\n\n\t// fetch\n\tmethod: \"GET\",\n\theaders: {\n\t\tAccept: \"application/json\",\n\t\t\"Accept-Encoding\": \"br, gzip, deflate\",\n\t},\n};\n\nconst mergeOptions = (options = {}) => {\n\treturn {\n\t\t...defaults,\n\t\t...options,\n\t\theaders: { ...defaults.headers, ...options.headers },\n\t\tqs: { ...defaults.qs, ...options.qs },\n\t};\n};\n\nexport const fetchSetDefaults = (options) => {\n\tObject.assign(defaults, mergeOptions(options));\n};\n\n// Note: requires EncodeStream to ensure it's Uint8Array\n// Poor browser support - https://github.com/Fyrd/caniuse/issues/6375\n// TODO needs testing\n// TODO mulit-part upload\nexport const fetchWritableStream = async (options, streamOptions = {}) => {\n\tconst body = createReadableStream();\n\t// Duplex: half - For browser compatibility - https://developer.chrome.com/articles/fetch-streaming-requests/#half-duplex\n\toptions = mergeOptions(options);\n\tconst value = await fetchRateLimit({\n\t\t...options,\n\t\tbody,\n\t\tduplex: \"half\",\n\t\tsignal: streamOptions.signal,\n\t});\n\tconst write = (chunk) => {\n\t\tbody.push(chunk);\n\t};\n\tconst stream = createWritableStream(write, streamOptions);\n\tstream.result = () => ({ key: \"output\", value });\n\treturn stream;\n};\nexport const fetchRequestStream = fetchWritableStream;\n\nexport const fetchReadableStream = (fetchOptions, streamOptions = {}) => {\n\treturn createReadableStream(\n\t\tfetchGenerator(fetchOptions, streamOptions),\n\t\tstreamOptions,\n\t);\n};\nexport const fetchResponseStream = fetchReadableStream;\n\nasync function* fetchGenerator(fetchOptions, streamOptions) {\n\tlet rateLimitTimestamp = 0;\n\tif (!Array.isArray(fetchOptions)) fetchOptions = [fetchOptions];\n\tfor (let options of fetchOptions) {\n\t\toptions = mergeOptions(options);\n\t\toptions.rateLimitTimestamp ??= rateLimitTimestamp;\n\n\t\tif (options.offsetParam) {\n\t\t\toptions.qs[options.offsetParam] ??= 0;\n\t\t}\n\n\t\tif (Object.keys(options.qs).length) {\n\t\t\toptions.url += `?${new URLSearchParams(options.qs)}`.replaceAll(\n\t\t\t\t\"+\",\n\t\t\t\t\"%20\",\n\t\t\t);\n\t\t}\n\t\tconst response = await fetchUnknown(options, streamOptions);\n\t\tfor await (const chunk of response) {\n\t\t\tyield chunk;\n\t\t}\n\t\t// ensure there is rate limiting between req with different options\n\t\trateLimitTimestamp = options.rateLimitTimestamp;\n\t}\n}\n\nconst jsonContentTypeRegExp = /^application\\/(.+\\+)?json($|;.+)/;\nconst fetchUnknown = async (options, streamOptions) => {\n\tconst response = await fetchRateLimit(options, streamOptions);\n\tif (jsonContentTypeRegExp.test(response.headers.get(\"Content-Type\"))) {\n\t\toptions.prefetchResponse = response; // hack\n\t\treturn fetchJson(options, streamOptions);\n\t}\n\treturn response.body;\n};\n\nconst nextLinkRegExp = /<(.*?)>; rel=\"next\"/;\n\nasync function* fetchJson(options, streamOptions) {\n\tconst { dataPath, nextPath } = options;\n\tlet { url } = options;\n\n\twhile (options.url) {\n\t\tconst response =\n\t\t\toptions.prefetchResponse ??\n\t\t\t(await fetchRateLimit(options, streamOptions));\n\t\tdelete options.prefetchResponse;\n\t\tconst body = await response.json();\n\t\turl = parseLinkFromHeader(response.headers);\n\t\turl ??= parseNextPath(body, nextPath);\n\t\turl ??= paginateUsingQuery(options);\n\t\toptions.url = url;\n\t\tconst data = pickPath(body, dataPath);\n\t\tif (Array.isArray(data)) {\n\t\t\tfor (const item of data) {\n\t\t\t\tyield item;\n\t\t\t}\n\n\t\t\tif (options.offsetParam && !data.length) break;\n\t\t} else {\n\t\t\tyield data;\n\t\t}\n\t}\n}\n\nconst paginateUsingQuery = (options) => {\n\tif (!options.offsetParam || !options.offsetAmount) return undefined;\n\n\tconst url = new URL(options.url);\n\tlet offset = url.searchParams.get(options.offsetParam);\n\tif (!offset) return null;\n\n\toffset = Number.parseInt(offset, 10) + options.offsetAmount;\n\turl.searchParams.delete(options.offsetParam);\n\turl.searchParams.set(options.offsetParam, offset);\n\treturn url.toString();\n};\n\nconst parseNextPath = (body, nextPath) => {\n\treturn nextPath ? pickPath(body, nextPath) : undefined;\n};\n\nconst parseLinkFromHeader = (headers) => {\n\tconst link = headers.get(\"Link\");\n\treturn link?.match(nextLinkRegExp)?.[1];\n};\n\nexport const fetchRateLimit = async (options, streamOptions = {}) => {\n\tconst now = Date.now();\n\tif (now < (options.rateLimitTimestamp ?? 0)) {\n\t\tawait timeout(options.rateLimitTimestamp - now, streamOptions);\n\t}\n\toptions.rateLimitTimestamp = Date.now() + 1000 * options.rateLimit;\n\toptions = mergeOptions(options); // for when called directly\n\n\tconst response = await fetch(options.url, {\n\t\t...options,\n\t\tsignal: streamOptions.signal,\n\t});\n\tif (!response.ok) {\n\t\t// 429 Too Many Requests\n\t\tif (response.statusCode === 429) {\n\t\t\treturn fetchRateLimit(options, streamOptions);\n\t\t}\n\t\tthrow new Error(\"fetch\", {\n\t\t\tcause: { request: options, response },\n\t\t});\n\t}\n\treturn response;\n};\n\nconst pickPath = (obj, path = \"\") => {\n\tif (path === \"\") return obj;\n\tif (!Array.isArray(path)) path = path.split(\".\");\n\treturn path\n\t\t.slice(0) // clone\n\t\t.reduce((a, b) => {\n\t\t\treturn a[b];\n\t\t}, obj);\n};\n\nexport default {\n\tsetDefaults: fetchSetDefaults,\n\treadableStream: fetchReadableStream,\n\tresponseStream: fetchReadableStream,\n\t// writableStream: fetchRequestStream,\n};\n"],
"mappings": "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",
"sourcesContent": ["// Copyright 2026 will Farrell, and datastream contributors.\n// SPDX-License-Identifier: MIT\n/* global fetch */\nimport {\n\tcreateReadableStream,\n\tcreateWritableStream,\n\ttimeout,\n} from \"@datastream/core\";\n\nconst defaults = {\n\t// custom\n\trateLimit: 0.01, // 100 per sec\n\tdataPath: undefined, // for json response, where the data is to return form body root\n\tnextPath: undefined, // for json pagination, body root\n\tqs: {}, // object to convert to query string\n\toffsetParam: undefined, // offset query parameter to use for pagination\n\toffsetAmount: undefined, // offset amount to use for pagination\n\n\t// fetch\n\tmethod: \"GET\",\n\theaders: {\n\t\tAccept: \"application/json\",\n\t\t\"Accept-Encoding\": \"br, gzip, deflate\",\n\t},\n};\n\nconst mergeOptions = (options = {}) => {\n\treturn {\n\t\t...defaults,\n\t\t...options,\n\t\theaders: { ...defaults.headers, ...options.headers },\n\t\tqs: { ...defaults.qs, ...options.qs },\n\t};\n};\n\nexport const fetchSetDefaults = (options) => {\n\tObject.assign(defaults, mergeOptions(options));\n};\n\n// Note: requires EncodeStream to ensure it's Uint8Array\n// Poor browser support - https://github.com/Fyrd/caniuse/issues/6375\nexport const fetchWritableStream = async (options, streamOptions = {}) => {\n\tconst body = createReadableStream();\n\t// Duplex: half - For browser compatibility - https://developer.chrome.com/articles/fetch-streaming-requests/#half-duplex\n\toptions = mergeOptions(options);\n\tconst value = await fetchRateLimit({\n\t\t...options,\n\t\tbody,\n\t\tduplex: \"half\",\n\t\tsignal: streamOptions.signal,\n\t});\n\tconst write = (chunk) => {\n\t\tbody.push(chunk);\n\t};\n\tconst stream = createWritableStream(write, streamOptions);\n\tstream.result = () => ({ key: \"output\", value });\n\treturn stream;\n};\nexport const fetchRequestStream = fetchWritableStream;\n\nexport const fetchReadableStream = (fetchOptions, streamOptions = {}) => {\n\treturn createReadableStream(\n\t\tfetchGenerator(fetchOptions, streamOptions),\n\t\tstreamOptions,\n\t);\n};\nexport const fetchResponseStream = fetchReadableStream;\n\nasync function* fetchGenerator(fetchOptions, streamOptions) {\n\tlet rateLimitTimestamp = 0;\n\tif (!Array.isArray(fetchOptions)) fetchOptions = [fetchOptions];\n\tfor (let options of fetchOptions) {\n\t\toptions = mergeOptions(options);\n\t\toptions.rateLimitTimestamp ??= rateLimitTimestamp;\n\n\t\tif (options.offsetParam) {\n\t\t\toptions.qs[options.offsetParam] ??= 0;\n\t\t}\n\n\t\tif (Object.keys(options.qs).length) {\n\t\t\toptions.url += `?${new URLSearchParams(options.qs)}`.replaceAll(\n\t\t\t\t\"+\",\n\t\t\t\t\"%20\",\n\t\t\t);\n\t\t}\n\t\tconst response = await fetchUnknown(options, streamOptions);\n\t\tfor await (const chunk of response) {\n\t\t\tyield chunk;\n\t\t}\n\t\t// ensure there is rate limiting between req with different options\n\t\trateLimitTimestamp = options.rateLimitTimestamp;\n\t}\n}\n\nconst jsonContentTypeRegExp = /^application\\/(.+\\+)?json($|;.+)/;\nconst fetchUnknown = async (options, streamOptions) => {\n\tconst response = await fetchRateLimit(options, streamOptions);\n\tif (jsonContentTypeRegExp.test(response.headers.get(\"Content-Type\"))) {\n\t\toptions.prefetchResponse = response; // hack\n\t\treturn fetchJson(options, streamOptions);\n\t}\n\treturn response.body;\n};\n\nconst nextLinkRegExp = /<(.*?)>; rel=\"next\"/;\n\nasync function* fetchJson(options, streamOptions) {\n\tconst { dataPath, nextPath } = options;\n\tlet { url } = options;\n\n\twhile (options.url) {\n\t\tconst response =\n\t\t\toptions.prefetchResponse ??\n\t\t\t(await fetchRateLimit(options, streamOptions));\n\t\tdelete options.prefetchResponse;\n\t\tconst body = await response.json();\n\t\turl = parseLinkFromHeader(response.headers);\n\t\turl ??= parseNextPath(body, nextPath);\n\t\turl ??= paginateUsingQuery(options);\n\t\toptions.url = url;\n\t\tconst data = pickPath(body, dataPath);\n\t\tif (Array.isArray(data)) {\n\t\t\tfor (const item of data) {\n\t\t\t\tyield item;\n\t\t\t}\n\n\t\t\tif (options.offsetParam && !data.length) break;\n\t\t} else {\n\t\t\tyield data;\n\t\t}\n\t}\n}\n\nconst paginateUsingQuery = (options) => {\n\tif (!options.offsetParam || !options.offsetAmount) return undefined;\n\n\tconst url = new URL(options.url);\n\tlet offset = url.searchParams.get(options.offsetParam);\n\tif (!offset) return null;\n\n\toffset = Number.parseInt(offset, 10) + options.offsetAmount;\n\turl.searchParams.delete(options.offsetParam);\n\turl.searchParams.set(options.offsetParam, offset);\n\treturn url.toString();\n};\n\nconst parseNextPath = (body, nextPath) => {\n\treturn nextPath ? pickPath(body, nextPath) : undefined;\n};\n\nconst parseLinkFromHeader = (headers) => {\n\tconst link = headers.get(\"Link\");\n\treturn link?.match(nextLinkRegExp)?.[1];\n};\n\nexport const fetchRateLimit = async (options, streamOptions = {}) => {\n\tconst now = Date.now();\n\tif (now < (options.rateLimitTimestamp ?? 0)) {\n\t\tawait timeout(options.rateLimitTimestamp - now, streamOptions);\n\t}\n\toptions.rateLimitTimestamp = Date.now() + 1000 * options.rateLimit;\n\toptions = mergeOptions(options); // for when called directly\n\n\tconst {\n\t\tmethod,\n\t\theaders,\n\t\tbody,\n\t\tmode,\n\t\tcredentials,\n\t\tcache,\n\t\tredirect,\n\t\treferrer,\n\t\treferrerPolicy,\n\t\tintegrity,\n\t\tkeepalive,\n\t\tduplex,\n\t} = options;\n\tconst fetchInit = {\n\t\tmethod,\n\t\theaders,\n\t\tbody,\n\t\tmode,\n\t\tcredentials,\n\t\tcache,\n\t\tredirect,\n\t\treferrer,\n\t\treferrerPolicy,\n\t\tintegrity,\n\t\tkeepalive,\n\t\tduplex,\n\t\tsignal: streamOptions.signal,\n\t};\n\tconst response = await fetch(options.url, fetchInit);\n\tif (!response.ok) {\n\t\t// 429 Too Many Requests\n\t\tif (response.status === 429) {\n\t\t\treturn fetchRateLimit(options, streamOptions);\n\t\t}\n\t\tthrow new Error(\n\t\t\t`fetch ${response.status} ${options.method} ${options.url}`,\n\t\t\t{\n\t\t\t\tcause: {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\turl: options.url,\n\t\t\t\t\tmethod: options.method,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\treturn response;\n};\n\nconst pickPath = (obj, path = \"\") => {\n\tif (path === \"\") return obj;\n\tif (!Array.isArray(path)) path = path.split(\".\");\n\treturn path.reduce((a, b) => a?.[b], obj);\n};\n\nexport default {\n\tsetDefaults: fetchSetDefaults,\n\treadableStream: fetchReadableStream,\n\tresponseStream: fetchReadableStream,\n};\n"],
"mappings": "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",
"names": []
}
{
"name": "@datastream/fetch",
"version": "0.0.42",
"description": "",
"version": "0.1.4",
"description": "HTTP fetch-based readable and writable streams with pagination and rate limiting",
"type": "module",

@@ -13,3 +13,3 @@ "engines": {

},
"main": "./index.web.mjs",
"main": "./index.node.mjs",
"module": "./index.web.mjs",

@@ -22,6 +22,2 @@ "exports": {

"default": "./index.node.mjs"
},
"__require": {
"types": "./index.d.ts",
"default": "./index.node.cjs"
}

@@ -42,3 +38,2 @@ },

"*.mjs",
"*.cjs",
"*.map",

@@ -70,6 +65,5 @@ "*.d.ts"

"homepage": "https://datastream.js.org",
"gitHead": "6ddc0fadabf5f3702a51aebae1fc6b252c6ae8d4",
"dependencies": {
"@datastream/core": "0.0.42"
"@datastream/core": "0.1.4"
}
}