{"_id":"@cjser/fetch-extras__v1_1_0","name":"@cjser/fetch-extras__v1_1_0","dist-tags":{"latest":"1.1.0-cjser.2"},"versions":{"1.1.0-cjser.2":{"name":"@cjser/fetch-extras__v1_1_0","version":"1.1.0-cjser.2","description":"Useful utilities for working with Fetch","license":"MIT","repository":{"type":"git","url":"https://code.moenext.com/3rdeye/cjser.git"},"funding":"https://github.com/sponsors/sindresorhus","author":{"name":"Sindre Sorhus","email":"sindresorhus@gmail.com","url":"https://sindresorhus.com"},"type":"module","exports":{"types":"./source/index.d.ts","require":"./dist-cjser/index.cjs","default":"./source/index.js"},"sideEffects":false,"engines":{"node":">=18.18"},"scripts":{"test":"xo && ava && tsc source/index.d.ts"},"keywords":["fetch","whatwg","api","request","response","http","client","httperror","utilities","wrapper","ky","got","axios"],"devDependencies":{"ava":"^6.2.0","typescript":"^5.7.3","xo":"^0.60.0"},"xo":{"rules":{"n/no-unsupported-features/node-builtins":"off"}},"types":"./source/index.d.ts","main":"./dist-cjser/index.cjs","cjser":{"sourceVersion":"1.1.0","cjserVersion":2,"original":{"name":"fetch-extras","version":"1.1.0","exports":{"types":"./source/index.d.ts","default":"./source/index.js"},"repository":"sindresorhus/fetch-extras","files":["source"],"scripts":{"test":"xo && ava && tsc source/index.d.ts"}}},"_id":"@cjser/fetch-extras__v1_1_0@1.1.0-cjser.2","gitHead":"a4bef54d7d22d3d9a6b84528457938d84cd65fe7","_nodeVersion":"20.14.0","_npmVersion":"10.7.0","dist":{"integrity":"sha512-NVwgY659YCwIGio29RqN7tCAafhMLO5+ZeEvAGl16CXV2/28WZAzYOsLW+HCY+vc83GR6gI3/fBCv5nTlNqSeQ==","shasum":"9bed1777b470b8ba60cf54b8cf2763a7adf0655f","tarball":"https://registry.npmjs.org/@cjser/fetch-extras__v1_1_0/-/fetch-extras__v1_1_0-1.1.0-cjser.2.tgz","fileCount":9,"unpackedSize":60743,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICXDHmJ7XaWTVAha+apLLT9Iu+XTpQkst3BMADsBvx+fAiB5iTgJn1N6iqrbmGpT+y8VvcM1GuRZtuR1kzjW+TtXBw=="}]},"_npmUser":{"name":"nanahira","email":"nanahira@momobako.com"},"directories":{},"maintainers":[{"name":"nanahira","email":"nanahira@momobako.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fetch-extras__v1_1_0_1.1.0-cjser.2_1778158031727_0.30349178063594495"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-07T12:47:11.624Z","1.1.0-cjser.2":"2026-05-07T12:47:11.902Z","modified":"2026-05-07T12:47:12.462Z"},"maintainers":[{"name":"nanahira","email":"nanahira@momobako.com"}],"description":"Useful utilities for working with Fetch","keywords":["fetch","whatwg","api","request","response","http","client","httperror","utilities","wrapper","ky","got","axios"],"repository":{"type":"git","url":"https://code.moenext.com/3rdeye/cjser.git"},"author":{"name":"Sindre Sorhus","email":"sindresorhus@gmail.com","url":"https://sindresorhus.com"},"license":"MIT","readme":"<h1 align=\"center\" title=\"fetch-extras\">\n\t<img src=\"media/logo.jpg\" alt=\"fetch-extras logo\">\n</h1>\n\n> Useful utilities for working with [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)\n\n*For more features and conveniences on top of Fetch, check out my [`ky`](https://github.com/sindresorhus/ky) package.*\n\n## Install\n\n```sh\nnpm install fetch-extras\n```\n\n## Usage\n\n```js\nimport {withHttpError, withTimeout} from 'fetch-extras';\n\n// Create an enhanced reusable fetch function that:\n// - Throws errors for non-2xx responses\n// - Times out after 5 seconds\nconst enhancedFetch = withHttpError(withTimeout(fetch, 5000));\n\nconst response = await enhancedFetch('/api');\nconst data = await response.json();\n```\n\n## API\n\n### HttpError\n\nError class thrown when a response has a non-2xx status code.\n\n```js\nimport {HttpError, throwIfHttpError} from 'fetch-extras';\n\ntry {\n\tawait throwIfHttpError(fetch('/api'));\n} catch (error) {\n\tif (error instanceof HttpError) {\n\t\tconsole.log(error.response.status); // 404\n\t}\n}\n```\n\n### throwIfHttpError(response)\n\nThrows an `HttpError` if the response is not ok. Can also accept a promise that resolves to a response.\n\n```js\nimport {throwIfHttpError} from 'fetch-extras';\n\nconst response = await throwIfHttpError(fetch('/api'));\nconst data = await response.json();\n```\n\n### withHttpError(fetchFunction)\n\nReturns a wrapped fetch function that automatically throws `HttpError` for non-2xx responses.\n\n```js\nimport {withHttpError} from 'fetch-extras';\n\nconst fetchWithError = withHttpError(fetch);\nconst response = await fetchWithError('/api');\n```\n\n### withTimeout(fetchFunction, timeout)\n\nReturns a wrapped fetch function with timeout functionality.\n\n```js\nimport {withTimeout} from 'fetch-extras';\n\nconst fetchWithTimeout = withTimeout(fetch, 5000);\nconst response = await fetchWithTimeout('/api');\n```\n\n### paginate(input, options?)\n\nPaginate through API responses using async iteration. By default, it automatically follows RFC 5988 `Link` headers with `rel=\"next\"`.\n\nReturns an async iterator that yields items from each page.\n\n```js\nimport {paginate} from 'fetch-extras';\n\n// Basic usage with Link headers (GitHub API)\nfor await (const commit of paginate('https://api.github.com/repos/sindresorhus/ky/commits')) {\n\tconsole.log(commit.sha);\n}\n```\n\n#### options\n\nType: `object`\n\n##### pagination\n\nType: `object`\n\n###### transform\n\nType: `(response: Response) => Promise<unknown[]>`\\\nDefault: `response => response.json()`\n\nTransform the response into an array of items.\n\n```js\nfor await (const user of paginate('https://api.example.com/users', {\n\tpagination: {\n\t\ttransform: async response => {\n\t\t\tconst data = await response.json();\n\t\t\treturn data.users; // Extract from nested property\n\t\t}\n\t}\n})) {\n\tconsole.log(user);\n}\n```\n\n###### paginate\n\nType: `(data: {response, currentUrl, currentItems, allItems}) => Promise<PaginationNextPage | false>`\\\nDefault: Parses RFC 5988 `Link` header\n\nDetermine the next page to fetch. Return an object with fetch options for the next request, or `false` to stop pagination.\n\n> [!IMPORTANT]\n> The response body has already been consumed by the `transform` function. Do NOT call `response.json()` or other body methods here. Extract pagination info from headers, the URL, or share data from the transform function through closure.\n\n> [!NOTE]\n> Returning `headers` replaces all inherited headers, consistent with standard Fetch API behavior. If you need to add headers while keeping existing ones, read them from the response and include them in the returned object.\n> Setting `body` to `undefined` will strip body-related headers (`Content-Type`, `Content-Length`, etc.) from the request, consistent with HTTP semantics for bodyless requests.\n\n```js\n// Cursor-based pagination using headers (recommended)\nfor await (const item of paginate('https://api.example.com/items', {\n\tpagination: {\n\t\tpaginate: ({response}) => {\n\t\t\tconst cursor = response.headers.get('X-Next-Cursor');\n\t\t\treturn cursor\n\t\t\t\t? {url: new URL(`https://api.example.com/items?cursor=${cursor}`)}\n\t\t\t\t: false;\n\t\t}\n\t}\n})) {\n\tconsole.log(item);\n}\n```\n\n```js\n// Sharing data between transform and paginate via closure\nlet nextCursor;\n\nfor await (const item of paginate('https://api.example.com/items', {\n\tpagination: {\n\t\ttransform: async (response) => {\n\t\t\tconst data = await response.json();\n\t\t\tnextCursor = data.nextCursor;\n\t\t\treturn data.items;\n\t\t},\n\t\tpaginate: () => {\n\t\t\treturn nextCursor\n\t\t\t\t? {url: new URL(`https://api.example.com/items?cursor=${nextCursor}`)}\n\t\t\t\t: false;\n\t\t}\n\t}\n})) {\n\tconsole.log(item);\n}\n```\n\n###### filter\n\nType: `(data: {item, currentItems, allItems}) => boolean`\\\nDefault: `() => true`\n\nFilter items before yielding them.\n\n```js\n// Only get active users\nfor await (const user of paginate('https://api.example.com/users', {\n\tpagination: {\n\t\tfilter: ({item}) => item.status === 'active'\n\t}\n})) {\n\tconsole.log(user);\n}\n```\n\n###### shouldContinue\n\nType: `(data: {item, currentItems, allItems}) => boolean`\\\nDefault: `() => true`\n\nCheck if pagination should continue after yielding an item. This is called after `filter` returns `true`. Useful for stopping pagination based on item values.\n\n```js\n// Stop when we reach items older than one week\nconst oneWeekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);\n\nfor await (const commit of paginate('https://api.github.com/repos/user/repo/commits', {\n\tpagination: {\n\t\tshouldContinue: ({item}) => new Date(item.date).getTime() >= oneWeekAgo\n\t}\n})) {\n\tconsole.log(commit);\n}\n```\n\n###### countLimit\n\nType: `number`\\\nDefault: `Infinity`\n\nMaximum number of items to yield.\n\n```js\nconst items = await paginate.all('https://api.example.com/items', {\n\tpagination: {\n\t\tcountLimit: 100 // Stop after 100 items\n\t}\n});\n```\n\n###### requestLimit\n\nType: `number`\\\nDefault: `10000`\n\nMaximum number of requests to make. This prevents infinite loops if your `paginate` function has bugs. Ensure your `paginate` function eventually returns `false` or the iteration will continue until this limit is reached.\n\n###### backoff\n\nType: `number`\\\nDefault: `0`\n\nDelay in milliseconds between requests. Useful for rate limiting.\n\n```js\nfor await (const item of paginate('https://api.example.com/items', {\n\tpagination: {\n\t\tbackoff: 1000 // Wait 1 second between requests\n\t}\n})) {\n\tconsole.log(item);\n}\n```\n\n###### stackAllItems\n\nType: `boolean`\\\nDefault: `false`\n\nWhether to keep all yielded items in memory. When `true`, the `allItems` array passed to callbacks will contain all previously yielded items. When `false`, `allItems` will always be empty to save memory.\n\n##### fetchFunction\n\nType: `(input: RequestInfo | URL, init?: any) => Promise<Response>`\\\nDefault: `globalThis.fetch`\n\nCustom fetch function to use for requests. This allows you to use a custom fetch implementation, such as [`ky`](https://github.com/sindresorhus/ky), or a fetch function wrapped with `withHttpError` or `withTimeout`.\n\n```js\nimport {paginate} from 'fetch-extras';\nimport ky from 'ky';\n\nconst url = 'https://api.github.com/repos/sindresorhus/ky/commits';\n\nfor await (const commit of paginate(url, {fetchFunction: ky})) {\n\tconsole.log(commit.sha);\n}\n```\n\n### paginate.all(input, options?)\n\nGet all paginated items as an array. This is a convenience method that collects all items into memory. For large datasets, prefer using the async iterator directly.\n\n```js\nimport {paginate} from 'fetch-extras';\n\nconst commits = await paginate.all('https://api.github.com/repos/sindresorhus/ky/commits', {\n\tpagination: {\n\t\tcountLimit: 50\n\t}\n});\n\nconsole.log(`Fetched ${commits.length} commits`);\n```\n\n## Related\n\n- [is-network-error](https://github.com/sindresorhus/is-network-error) - Check if a value is a Fetch network error\n- [ky](https://github.com/sindresorhus/ky) - HTTP client based on Fetch\n- [parse-sse](https://www.npmjs.com/package/parse-sse) - Parse Server-Sent Events (SSE) from a Response\n\n## cjser\n\nThis package is a CommonJS-compatible build generated by cjser for projects that still need `require()` support. The source version matches the original npm package version, with a cjser prerelease suffix for this generated build.\nOriginal repository: https://github.com/sindresorhus/fetch-extras\n","readmeFilename":"readme.md","_rev":"1-ff8f79a1e35ba1d655d9d2f131d415fe"}