{"id":16556302,"url":"https://github.com/miraclx/xresilient","last_synced_at":"2025-10-28T20:30:27.590Z","repository":{"id":57402209,"uuid":"231931864","full_name":"miraclx/xresilient","owner":"miraclx","description":"Build regenerative, resumable NodeJS streams","archived":false,"fork":false,"pushed_at":"2021-12-20T21:17:24.000Z","size":108,"stargazers_count":8,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-09-17T11:42:32.679Z","etag":null,"topics":["bytes","nodejs","pipe","readablestream","resumable-nodejs-streams"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/miraclx.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}},"created_at":"2020-01-05T14:46:24.000Z","updated_at":"2020-11-06T15:12:18.000Z","dependencies_parsed_at":"2022-09-17T06:43:00.452Z","dependency_job_id":null,"html_url":"https://github.com/miraclx/xresilient","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraclx%2Fxresilient","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraclx%2Fxresilient/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraclx%2Fxresilient/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miraclx%2Fxresilient/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/miraclx","download_url":"https://codeload.github.com/miraclx/xresilient/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219858902,"owners_count":16556039,"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":["bytes","nodejs","pipe","readablestream","resumable-nodejs-streams"],"created_at":"2024-10-11T20:04:07.579Z","updated_at":"2025-10-28T20:30:22.315Z","avatar_url":"https://github.com/miraclx.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xresilient\n\n\u003e Build regenerative, resumable NodeJS streams\n\n[![NPM Version][npm-image]][npm-url]\n[![NPM Downloads][downloads-image]][downloads-url]\n\n[![NPM][npm-image-url]][npm-url]\n\n## Installing\n\nVia [NPM][npm]:\n\n``` bash\nnpm install xresilient\n```\n\n## Usage\n\n``` javascript\n// Node CommonJS\nconst xresilient = require('xresilient');\n// Or ES6\nimport xresilient from 'xresilient';\n```\n\n## Examples\n\n``` javascript\nconst retriableStream = xresilient(\n  function returnStream({bytesRead, retryCount}) {\n    console.log(`Count = ${retryCount}`);\n    return request.get('https://website.com/image.png', {\n      headers: {\n        Range: `bytes=${bytesRead}-`,\n      },\n    });\n  },\n  {retries: 5},\n);\n\nretriableStream.pipe(fs.createWriteStream('image.png'));\n```\n\nThe above snippet of code would request an image file from the URL and pipe the result into the `image.png` file\n\nBased on the configuration to retry `5` times, it would recall the function specified with the number of bytes read before the error so you can resume the stream if need be\n\nFor instance, get requests interrupted by network errors can be resumed without breaking the active pipe.\n\n## API\n\n### xresilient(fn[, options])\n\n* `fn`: \u0026lt;[GenFn](#genfn)\u0026gt;\n* `options` \u003csub\u003e`extends`\u003c/sub\u003e [`stream.ReadableOptions`][stream.ReadableOptions]: [Object][object]\n  * `retries`: \u0026lt;[number][]\u0026gt; Number of times to retry the stream. **Default**: `5`.\n  * `destroyer`: \u0026lt;[function][]\u0026gt; Method with which to destroy the underlying stream.\n    * `source`: \u0026lt;[NodeJS.ReadableStream][]\u0026gt; Readable stream returned by the specified `fn` function.\n* Returns: \u0026lt;[ResilientStream](#resilientstream)\u0026gt;\n\nReturn a regenerative, persistent, resuming, resilient stream wrapped\nThat swaps underlying stream source without data loss.\n\nThe `fn` argument must be a function or async function taking two arguments returning a ResilientStream.\n\nReturning an ended readable stream forces the resilient stream to be ended.\nReturning a destroyed stream forces the resilient stream to retry the generator once until the limit is reached.\n\n#### Event: 'data'\n\n* `chunk`: \u0026lt;[Buffer][buffer]\u0026gt;\n\nThe `'data'` event is emitted whenever the active underlying stream pushes relinquishes ownership of a chunk to the resilient stream which in turn pushes the chunk out to a consumer.\n\n[See [stream.Readable (Event: 'data')](https://nodejs.org/api/stream.html#stream_event_data)]\n\n#### Event: 'end'\n\nThe `'end'` event is emitted whenever there is no more data within the resilient stream's underlying resources to be consumed.\n\n[See [stream.Readable (Event: 'end')](https://nodejs.org/api/stream.html#stream_event_end)]\n\n#### Event: 'retry'\n\n* `retrySlice`: \u0026lt;[RetrySlice](#retryslice)\u0026gt;\n\nThe `'retry'` event is emitted after an error is caught either within the active stream source or the generative function and the stream hasn't used up all of it's retries.\n\nThis event is emitted right before the [genFn](#genfn) is called.\n\n#### Event: 'error'\n\n* `err`: \u0026lt;[Error][]\u0026gt;\n\nThe `'error'` event is emitted either when the underlying readable stream encounters an `'error'`  event or the generative function throws an error while the resilient stream has maxed-out all possible retries.\ni.e\n\n```javascript\nself.getRetries() === self.getRetryCount()\n```\n\nAt this point, the resilient stream is destroyed and the specified [GenFn](#genfn) isn't called.\nThis, ends the resilient iteration.\n\n#### Event: 'readable'\n\nThe `'readable'` event is emitted when there is data available to be read from the stream or the end of the stream has been reached.\n\nEither way, the `retriableStream.read()` method would return the available data and reset the flow state so all buffered data flushes.\n\n[See [stream.Readable (Event: 'readable')](https://nodejs.org/api/stream.html#stream_event_readable)]\n\n### \u003ca id='genfn'\u003e\u003c/a\u003eGenFn: [`Function`][function]|[`AsyncFunction`][asyncfunction]\n\n* `storeSlice`: \u0026lt;[ResilientStore](#resilientstore)\u0026gt;\n* Returns: \u0026lt;[NodeJS.ReadableStream][]\u0026gt;\n\nGenerator function returning a readable stream.\nErrors / Promise rejections caught during this function's execution would be emitted through the `error` event.\n\nThis function can either be an async function or a normal function.\n\n### \u003ca id='resilientstream'\u003e\u003c/a\u003eResilientStream \u003csub\u003e`extends`\u003c/sub\u003e [stream.Readable][]\n\nThe Core resilient stream whose data is streamed off of the underlying streams gotten from the [GenFn](#genfn).\n\n### \u003ca id='retryslice'\u003e\u003c/a\u003eRetrySlice: [object][]\n\n* `retryCount`: \u0026lt;[number][]\u0026gt; The number of retry iterations so far. (starting from 0)\n* `maxRetries`: \u0026lt;[number][]\u0026gt; The maximum number of retries possible.\n* `bytesRead`: \u0026lt;[number][]\u0026gt; The number of bytes previously read (if any).\n* `lastErr`: \u0026lt;[Error][]\u0026gt; The error emitted either by the last generator function execution or previous stream.\n* `oldStream`: \u0026lt;[NodeJS.ReadableStream][]\u0026gt; The old stream that error-ed out (if any).\n\n### \u003ca id='resilientstore'\u003e\u003c/a\u003eResilientStore \u003csub\u003e`extends`\u003c/sub\u003e [RetrySlice](#retryslice): [object][]\n\n* `trialCount`: \u0026lt;[number][]\u0026gt; The active execution iteration count. (starting from 1)\n\n### \u003ca id='resilientstream_setretries'\u003e\u003c/a\u003eResilientStream.setRetries(retries)\n\n* `retries`: \u0026lt;[number][]\u0026gt; The retry value to update to.\n* Returns: \u0026lt;[boolean][]\u0026gt;\n\nThis method sets the number of possible retries.\nIf the `retryCount` is less than this value, returns `false`.\nelse, returns `true` on successful setting.\n\n### \u003ca id='resilientstream_getretries'\u003e\u003c/a\u003eResilientStream.getRetries()\n\n* Returns: \u0026lt;[number][]\u0026gt;\n\nReturns the value of max possible retries before error emit.\n\n### \u003ca id='resilientstream_getretrycount'\u003e\u003c/a\u003eResilientStream.getRetryCount()\n\n* Returns: \u0026lt;[number][]\u0026gt;\n\nReturns the number of the retries so-far.\n\n## Development\n\n### Building\n\nFeel free to clone, use in adherance to the [license](#license) and perhaps send pull requests\n\n``` bash\ngit clone https://github.com/miraclx/xresilient.git\ncd xresilient\nnpm install\n# hack on code\nnpm run build\n```\n\n## License\n\n[Apache 2.0][license] © **Miraculous Owonubi** ([@miraclx][author-url]) \u0026lt;omiraculous@gmail.com\u0026gt;\n\n[stream.Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable\n[NodeJS.ReadableStream]: https://nodejs.org/api/stream.html#stream_class_stream_readable\n[stream.ReadableOptions]: https://nodejs.org/api/stream.html#stream_new_stream_readable_options\n\n[npm]:  https://github.com/npm/cli \"The Node Package Manager\"\n[license]:  LICENSE \"Apache 2.0 License\"\n[author-url]: https://github.com/miraclx\n\n[npm-url]: https://npmjs.org/package/xresilient\n[npm-image]: https://badgen.net/npm/node/xresilient\n[npm-image-url]: https://nodei.co/npm/xresilient.png?stars\u0026downloads\n[downloads-url]: https://npmjs.org/package/xresilient\n[downloads-image]: https://badgen.net/npm/dm/xresilient\n\n[any]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Data_types\n[Error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type\n[buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer\n[object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object\n[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type\n[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function\n[asyncfunction]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiraclx%2Fxresilient","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmiraclx%2Fxresilient","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiraclx%2Fxresilient/lists"}