{"id":26758495,"url":"https://github.com/alessiofrittoli/stream-reader","last_synced_at":"2025-04-15T05:47:57.740Z","repository":{"id":269746037,"uuid":"907145883","full_name":"alessiofrittoli/stream-reader","owner":"alessiofrittoli","description":"Easly read pushed data from a Stream","archived":false,"fork":false,"pushed_at":"2025-03-21T12:04:30.000Z","size":371,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-15T05:47:48.273Z","etag":null,"topics":["stream-reader"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/@alessiofrittoli/stream-reader","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alessiofrittoli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"license.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["alessiofrittoli"]}},"created_at":"2024-12-22T23:42:12.000Z","updated_at":"2025-03-21T12:04:06.000Z","dependencies_parsed_at":"2024-12-25T20:27:24.301Z","dependency_job_id":"d75e17b8-685d-4620-b1f7-e9414775a529","html_url":"https://github.com/alessiofrittoli/stream-reader","commit_stats":null,"previous_names":["alessiofrittoli/stream-reader"],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fstream-reader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fstream-reader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fstream-reader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fstream-reader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alessiofrittoli","download_url":"https://codeload.github.com/alessiofrittoli/stream-reader/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249016316,"owners_count":21198832,"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":["stream-reader"],"created_at":"2025-03-28T16:29:16.351Z","updated_at":"2025-04-15T05:47:57.718Z","avatar_url":"https://github.com/alessiofrittoli.png","language":"TypeScript","funding_links":["https://github.com/sponsors/alessiofrittoli"],"categories":[],"sub_categories":[],"readme":"# Stream Reader 📚\n\n[![NPM Latest Version][version-badge]][npm-url] [![Coverage Status][coverage-badge]][coverage-url] [![Socket Status][socket-badge]][socket-url] [![NPM Monthly Downloads][downloads-badge]][npm-url] [![Dependencies][deps-badge]][deps-url]\n\n[![GitHub Sponsor][sponsor-badge]][sponsor-url]\n\n[version-badge]: https://img.shields.io/npm/v/%40alessiofrittoli%2Fstream-reader\n[npm-url]: https://npmjs.org/package/%40alessiofrittoli%2Fstream-reader\n[coverage-badge]: https://coveralls.io/repos/github/alessiofrittoli/stream-reader/badge.svg\n[coverage-url]: https://coveralls.io/github/alessiofrittoli/stream-reader\n[socket-badge]: https://socket.dev/api/badge/npm/package/@alessiofrittoli/stream-reader\n[socket-url]: https://socket.dev/npm/package/@alessiofrittoli/stream-reader/overview\n[downloads-badge]: https://img.shields.io/npm/dm/%40alessiofrittoli%2Fstream-reader.svg\n[deps-badge]: https://img.shields.io/librariesio/release/npm/%40alessiofrittoli%2Fstream-reader\n[deps-url]: https://libraries.io/npm/%40alessiofrittoli%2Fstream-reader\n\n[sponsor-badge]: https://img.shields.io/static/v1?label=Fund%20this%20package\u0026message=%E2%9D%A4\u0026logo=GitHub\u0026color=%23DB61A2\n[sponsor-url]: https://github.com/sponsors/alessiofrittoli\n\n## Easly read pushed data from a Stream\n\nThe `StreamReader` class is a utility for reading data from a `ReadableStream` in a structured and event-driven manner. It extends the `EventEmitter` class, providing events for stream lifecycle management and error handling.\n\n### Table of Contents\n\n- [Getting started](#getting-started)\n- [API Reference](#api-reference)\n  - [StreamReader Class API Reference](#streamreader-class-api-reference)\n    - [Constructor](#constructor)\n    - [Properties](#properties)\n    - [Methods](#methods)\n    - [Static Methods](#static-methods)\n    - [Listening Event](#listening-events)\n    - [Usage](#usage)\n    - [Error handling](#error-handling)\n  - [Types API Reference](#types-api-reference)\n- [Development](#development)\n  - [Install depenendencies](#install-depenendencies)\n  - [Build the source code](#build-the-source-code)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n- [Contributing](#contributing)\n- [Security](#security)\n- [Credits](#made-with-)\n\n---\n\n### Getting started\n\nRun the following command to start using `stream-reader` in your projects:\n\n```bash\nnpm i @alessiofrittoli/stream-reader\n```\n\nor using `pnpm`\n\n```bash\npnpm i @alessiofrittoli/stream-reader\n```\n\n---\n\n### API Reference\n\n#### Importing the library\n\n```ts\nimport { StreamReader } from '@alessiofrittoli/stream-reader'\nimport type { ... } from '@alessiofrittoli/stream-reader/types'\n```\n\n#### StreamReader Class API Reference\n\n##### Constructor\n\nThe `StreamReader` class constructor accepts a `ReadableStream` argument. You can optionally pass types arguments `I` and `O` to define the type of the streamed data being read and the type of the transformed output chunk.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eExample\u003c/summary\u003e\n\n```ts\nconst reader  = new StreamReader\u003cBuffer\u003e( ... )\nconst reader2 = new StreamReader\u003cBuffer, string\u003e( ... )\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary\u003eAutomatically inferred type\u003c/summary\u003e\n\n```ts\ntype Input    = Buffer\ntype Output   = Buffer\nconst stream  = new TransformStream\u003cInput, Output\u003e()\nconst reader  = new StreamReader( stream.readable ) // type of `StreamReader\u003cOutput, Output\u003e`\n```\n\n\u003c/details\u003e\n\n---\n\n##### Properties\n\nHere are listed the `StreamReader` class instance accessible properties:\n\n\u003cdetails\u003e\n\n\u003csummary\u003eProperties\u003c/summary\u003e\n\n| Parameter | Type                             | Description |\n|-----------|----------------------------------|-------------|\n| `reader`  | `ReadableStreamDefaultReader\u003cT\u003e` | The reader obtained from the input `ReadableStream\u003cT\u003e` |\n| `closed`  | `boolean`                        | Indicates whether the stream has been closed. |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Default   | Description |\n|-----------|-----------|-------------|\n| `I`       | `unknown` | The type of input data being read from the stream. |\n| `O`       | `I`       | The type of output data transformed after reading from the stream. Defaults to the same type of `I`. |\n\n\u003c/details\u003e\n\n---\n\n##### Methods\n\n###### `StreamReader.read()`\n\nThe `StreamReader.read()` method read the on-demand pushed data from the given stream.\n\nIt internally uses the `StreamReader.readChunks()` method to read the received chunks.\n\nIt emits usefull events such as:\n\n- `data` - Emitted when a chunk is received from the stream and processed by the optional transform function.\n- `close` - Emitted when the stream is closed.\n- `error` - Emitted when an error occurs while reading.\n\n---\n\n- See [Listening Events](#listening-events) section for further info.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eParameters\u003c/summary\u003e\n\n| Paramenter | Type | Description |\n| `transform` | `TransformChunk\u003cI, O\u003e` | (Optional) A function that transforms each chunk. |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eReturns\u003c/summary\u003e\n\nType: `Promise\u003cReadChunks\u003cO\u003e\u003e`\n\nA new Promise with an Array of read and eventually transformed chunks, resolved once the stream is closed.\n\n\u003c/details\u003e\n\n---\n\n###### `StreamReader.readChunks()`\n\nThe `StreamReader.readChunks()` method read the on-demand pushed data from the given stream.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eReturns\u003c/summary\u003e\n\nType: `AsyncGenerator\u003cReadChunk\u003cI\u003e\u003e`\n\nAn async iterable object for consuming chunks of data.\n\n\u003c/details\u003e\n\n---\n\n###### `StreamReader.cancel()`\n\nThe `StreamReader.cancel()` method it's pretty usefull when stream data reading is no longer needed, regardless of stream writer state.\n\nThis method will cancel the reader, release the lock, emit a 'cancel' event, and remove `data`, `close` and `cancel` event listeners.\n\nIt emits the `cancel` event.\n\n- See [Listening Events](#listening-events) section for further info.\n\n---\n\n##### Static Methods\n\n###### `StreamReader.generatorToReadableStream()`\n\nThe `StreamReader.generatorToReadableStream()` method is a utiliy function that converts a `Generator` or `AsyncGenerator` to a `ReadableStream`.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eParameters\u003c/summary\u003e\n\n| Parameter   | Type                 | Default                    | Description |\n|-------------|----------------------|----------------------------|-------------|\n| `generator` | `StreamGenerator\u003cT\u003e` | `StreamGenerator\u003cunknown\u003e` | The Generator or AsyncGenerator to convert. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary\u003eType Parameters\u003c/summary\u003e\n\n| Parameter | Type | Default   | Description                                |\n|-----------|------|-----------|--------------------------------------------|\n| `T`       | `T`  | `unknown` | The type of data produced by the iterator. |\n\n\u003c/details\u003e\n\n---\n\n##### Listening Events\n\nThe `StreamReader` class extends the `EventEmitter` class, providing events for stream lifecycle management and error handling.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eEvents list\u003c/summary\u003e\n\n| Event    | Arguments | Type            | Description |\n|----------|-----------|-----------------|-------------|\n| `data`   |           |                 | Emitted when a chunk of data is read from the stream and processed by the optional `transform` function. |\n|          | `chunk`   | `ReadChunk\u003cO\u003e`  | The chunk of data read from the stream. |\n| `close`  |           |                 | Emitted when the stream is closed. |\n|          | `chunks`  | `ReadChunks\u003cO\u003e` | An array of chunks read from the stream before it was closed. |\n| `error`  |           |                 | Emitted when an error occurs during reading. |\n|          | `error`   | `Error`         | The error that occurred during the reading process. |\n| `cancel` |           |                 | Emitted when the reading process is canceled. |\n|          | `reason`  | `DOMException`  | A DOMException explaing the reason for aborting the operation. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary\u003eExamples\u003c/summary\u003e\n\n###### `data` event\n\n```ts\nconst reader = new StreamReader( ... )\nreader.on( 'data', chunk =\u003e {\n  console.log( 'received chunk', chunk )\n} )\n```\n\n---\n\n###### `close` event\n\n```ts\nconst reader = new StreamReader( ... )\nreader.on( 'close', chunks =\u003e {\n  console.log( 'chunks', chunks )\n} )\n```\n\n---\n\n###### `error` event\n\n```ts\nconst reader = new StreamReader( ... )\nreader.on( 'error', error =\u003e {\n  console.error( error )\n} )\n```\n\n---\n\n###### `cancel` event\n\n```ts\nconst reader = new StreamReader( ... )\nreader.on( 'cancel', reason =\u003e {\n  console.log( 'reading cancelled', reason.message )\n} )\n```\n\n\u003c/details\u003e\n\n---\n\n##### Usage\n\nIn the following examples we reference `streamData` which is an async function that writes data and closes the stream once finished:\n\n```ts\nconst sleep = ( ms: number ) =\u003e new Promise\u003cvoid\u003e( resolve =\u003e setTimeout( resolve, ms ) )\n\nconst defaultChunks = [ 'data 1', 'data 2' ]\nconst erroredChunks = [ 'data 1', new Error( 'Test Error' ), 'data 2' ]\n    \nconst streamData = async (\n  { writer, chunks }: {\n    writer: WritableStreamDefaultWriter\u003cBuffer\u003e\n    chunks?: ( string | Error )[]\n  }\n) =\u003e {\n  chunks ||= defaultChunks\n  for await ( const chunk of chunks ) {\n    if ( chunk instanceof Error ) {\n      throw chunk\n    }\n    await writer.write( Buffer.from( chunk ) )\n    await sleep( 50 )\n  }\n  await writer.close()\n  writer.releaseLock()\n}\n```\n\n\u003cdetails\u003e\n\n\u003csummary\u003eBasic usage\u003c/summary\u003e\n\n```ts\nconst stream  = new TransformStream\u003cBuffer, Buffer\u003e()\nconst writer  = stream.writable.getWriter()\nconst reader  = new StreamReader( stream.readable )\n\nstreamData( { writer } )\n\nconst chunks = await reader.read()\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary\u003eReading chunk by chunk from a Response Body\u003c/summary\u003e\n\n```ts\nconst response = await fetch( ... )\nlet resourceSize = 0\n\nif ( response.body ) {\n    const reader  = new StreamReader( response.body )\n    const decoder = new TextDecoder()\n    reader.on( 'data', chunk =\u003e {\n        const decoded = decoder.decode( chunk, { stream: true } )\n        resourceSize += chunk.BYTES_PER_ELEMENT * chunk.length\n    } )\n    const chunks = await reader.read()\n}\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary\u003eTransforming read chunks\u003c/summary\u003e\n\n```ts\nconst stream  = new TransformStream\u003cBuffer, Buffer\u003e()\nconst writer  = stream.writable.getWriter()\nconst reader  = new StreamReader\u003cBuffer, string\u003e( stream.readable, {\n  transform( chunk ) {\n    return chunk.toString( 'base64url' )\n  }\n} )\n\nstreamData( { writer } )\n\nreader.on( 'data', chunk =\u003e {\n  console.log( chunk ) // chunk is now a base64url string\n} )\nconst chunks = await reader.read() // `string[]`\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary\u003eOpting-out from in-memory chunk collection\u003c/summary\u003e\n\n```ts\nconst inMemory  = false\nconst stream    = new TransformStream\u003cBuffer, Buffer\u003e()\nconst writer    = stream.writable.getWriter()\nconst reader    = new StreamReader( stream.readable, { inMemory } )\n\nstreamData( { writer } )\n\nreader.on( 'data', chunk =\u003e {\n  console.log( chunk )\n} )\nconst chunks = await reader.read() // empty `[]`\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary\u003eCancelling the reader before Stream is closed\u003c/summary\u003e\n\n```ts\nconst stream  = new TransformStream\u003cBuffer, Buffer\u003e()\nconst writer  = stream.writable.getWriter()\nconst reader  = new StreamReader( stream.readable )\n\nstreamData( { writer } )\n\nreader.read()\n\ncancelButton.addEventListener( 'click', () =\u003e {\n  reader.cancel( 'Reading no longer needed' )\n} )\n```\n\n\u003c/details\u003e\n\n---\n\n##### Error handling\n\nWhen an error occurs while reading stream data (such as unexpected stream abort), the `StreamReader` uses an internal `error` function which handles the thrown Error.\n\nBy default, if no listener is attached to the `error` event, the `StreamReader.read()` method will re-throw the caught error.\n\nIn that case, you need to await and wrap the `StreamReader.read()` method call in a `trycatch` block like so:\n\n```ts\ntry {\n  const chunks = await reader.read()\n} catch ( err ) {\n  const error = err as Error\n  console.error( 'An error occured', error.message )\n}\n```\n\nwith `error` event listener:\n\n```ts\nreader.read()\nreader.on( 'error', error =\u003e {\n  console.error( 'An error occured', error.message )\n} )\n```\n\n---\n\n#### Types API Reference\n\n##### `ReadChunk\u003cT\u003e`\n\nRepresents a chunk of data that can be read, which can either be of type `T` or a promise that resolves to `T`.\n\n- **Type Parameter:**\n  - `T`: The type of the data chunk. Defaults to `unknown` if not specified.\n\n---\n\n##### `ReadChunks\u003cT\u003e`\n\nRepresents an array of `ReadChunk` objects.\n\n- **Type Parameter:**\n  - `T`: The type of data contained in each `ReadChunk`.\n\n---\n\n##### `TransformChunk\u003cI, O\u003e`\n\nA function that transforms a chunk of data.\n\n- **Type Parameters:**\n  - `I`: The type of the input chunk. Defaults to `unknown`.\n  - `O`: The type of the output chunk. Defaults to `I`.\n\n- **Parameters:**\n  - `chunk`: The chunk of data to be transformed.\n\n- **Returns:**  \n  A transformed chunk of data, which can be either a synchronous result or a promise that resolves to the result.\n\n---\n\n##### `StreamReaderEvents\u003cO\u003e`\n\nDefines event types emitted by the `StreamReader`.\n\n- **Type Parameter:**\n  - `O`: The type of data being read from the stream and eventually transformed before the event is emitted.\n\n- **Event Types:**\n  - `data`: Emitted when a chunk of data is read.\n    - **Parameters:** `chunk` (`ReadChunk\u003cO\u003e`)\n  - `close`: Emitted when the stream is closed.\n    - **Parameters:** `chunks` (`ReadChunks\u003cO\u003e`)\n  - `error`: Emitted when an error occurs during reading.\n    - **Parameters:** `error` (`Error`)\n  - `cancel`: Emitted when the reading process is canceled.\n    - **Parameters:** `reason` (`DOMException`)\n\n---\n\n##### `Listener\u003cK, O\u003e`\n\nA listener function for events emitted by the `StreamReader`.\n\n- **Type Parameters:**\n  - `K`: The event type to listen for.\n  - `O`: The type of data being read from the stream.\n\n- **Parameters:**\n  - `...args`: The arguments emitted with the event, based on the event type `K`.\n\n---\n\n##### `OnDataEventListener\u003cO\u003e`\n\nListener for the `data` event.\n\n- **Type Parameter:**\n  - `O`: The type of data being read.\n\n- **Parameters:**\n  - `chunk` (`ReadChunk\u003cO\u003e`): The chunk of data that was read.\n\n---\n\n##### `OnCloseEventListener\u003cO\u003e`\n\nListener for the `close` event.\n\n- **Type Parameter:**\n  - `O`: The type of data being read.\n\n- **Parameters:**\n  - `chunks` (`ReadChunks\u003cO\u003e`): An array of chunks read before the stream was closed.\n\n---\n\n##### `OnCancelEventListener`\n\nListener for the `cancel` event.\n\n- **Parameters:**\n  - `reason` (`DOMException`): Explains the reason for aborting the operation.\n\n---\n\n##### `OnErrorEventListener`\n\nListener for the `error` event.\n\n- **Parameters:**\n  - `error` (`Error`): The error that occurred during reading.\n\n---\n\n##### `StreamGenerator\u003cT\u003e`\n\nA generator that produces chunks of data asynchronously. It can be either a regular generator or an async generator.\n\n- **Type Parameter:**\n  - `T`: The type of data produced by the generator.\n  \n---\n\n### Development\n\n#### Install depenendencies\n\n```bash\nnpm install\n```\n\nor using `pnpm`\n\n```bash\npnpm i\n```\n\n#### Build the source code\n\nRun the following command to test and build code for distribution.\n\n```bash\npnpm build\n```\n\n#### [ESLint](https://www.npmjs.com/package/eslint)\n\nwarnings / errors check.\n\n```bash\npnpm lint\n```\n\n#### [Jest](https://npmjs.com/package/jest)\n\nRun all the defined test suites by running the following:\n\n```bash\n# Run tests and watch file changes.\npnpm test:watch\n\n# Run tests in a CI environment.\npnpm test:ci\n```\n\n- See [`package.json`](./package.json) file scripts for more info.\n\nRun tests with coverage.\n\nAn HTTP server is then started to serve coverage files from `./coverage` folder.\n\n⚠️ You may see a blank page the first time you run this command. Simply refresh the browser to see the updates.\n\n```bash\ntest:coverage:serve\n```\n\n---\n\n### Contributing\n\nContributions are truly welcome!\n\nPlease refer to the [Contributing Doc](./CONTRIBUTING.md) for more information on how to start contributing to this project.\n\nHelp keep this project up to date with [GitHub Sponsor][sponsor-url].\n\n[![GitHub Sponsor][sponsor-badge]][sponsor-url]\n\n---\n\n### Security\n\nIf you believe you have found a security vulnerability, we encourage you to **_responsibly disclose this and NOT open a public issue_**. We will investigate all legitimate reports. Email `security@alessiofrittoli.it` to disclose any security vulnerabilities.\n\n### Made with ☕\n\n\u003ctable style='display:flex;gap:20px;'\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003cimg alt=\"avatar\" src='https://avatars.githubusercontent.com/u/35973186' style='width:60px;border-radius:50%;object-fit:contain;'\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n        \u003ctable style='display:flex;gap:2px;flex-direction:column;'\u003e\n          \u003ctbody\u003e\n              \u003ctr\u003e\n                \u003ctd\u003e\n                  \u003ca href='https://github.com/alessiofrittoli' target='_blank' rel='noopener'\u003eAlessio Frittoli\u003c/a\u003e\n                \u003c/td\u003e\n              \u003c/tr\u003e\n              \u003ctr\u003e\n                \u003ctd\u003e\n                  \u003csmall\u003e\n                    \u003ca href='https://alessiofrittoli.it' target='_blank' rel='noopener'\u003ehttps://alessiofrittoli.it\u003c/a\u003e |\n                    \u003ca href='mailto:info@alessiofrittoli.it' target='_blank' rel='noopener'\u003einfo@alessiofrittoli.it\u003c/a\u003e\n                  \u003c/small\u003e\n                \u003c/td\u003e\n              \u003c/tr\u003e\n          \u003c/tbody\u003e\n        \u003c/table\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falessiofrittoli%2Fstream-reader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falessiofrittoli%2Fstream-reader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falessiofrittoli%2Fstream-reader/lists"}