{"id":19475487,"url":"https://github.com/artdecocode/catchment","last_synced_at":"2025-04-25T14:31:51.106Z","repository":{"id":57194972,"uuid":"88635216","full_name":"artdecocode/catchment","owner":"artdecocode","description":"Collect Node.js Stream Data Into Catchment: Stream With Promise Property Resolved On Finish.","archived":false,"fork":false,"pushed_at":"2019-04-10T13:52:11.000Z","size":118,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-24T10:47:08.603Z","etag":null,"topics":["nodejs","promise","stream"],"latest_commit_sha":null,"homepage":"https://catchment.page","language":"JavaScript","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/artdecocode.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2017-04-18T14:34:10.000Z","updated_at":"2019-04-10T13:52:13.000Z","dependencies_parsed_at":"2022-09-16T05:14:22.680Z","dependency_job_id":null,"html_url":"https://github.com/artdecocode/catchment","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artdecocode%2Fcatchment","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artdecocode%2Fcatchment/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artdecocode%2Fcatchment/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artdecocode%2Fcatchment/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/artdecocode","download_url":"https://codeload.github.com/artdecocode/catchment/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250834068,"owners_count":21494903,"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":["nodejs","promise","stream"],"created_at":"2024-11-10T19:33:10.602Z","updated_at":"2025-04-25T14:31:50.548Z","avatar_url":"https://github.com/artdecocode.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# catchment\n\n[![npm version](https://badge.fury.io/js/catchment.svg)](https://npmjs.org/package/catchment)\n\nA Node.JS package to collect stream data into a catchment and return it either as a buffer or a string.\n\n```\nyarn add -E catchment\n```\n\n\u003ctable\u003e\n  \u003ctr\u003e\u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003ca href=\"https://www.technation.sucks\"\u003e\n        \u003cimg src=\"https://raw.githubusercontent.com/artdecoweb/www.technation.sucks/master/anim.gif\"\n          alt=\"Tech Nation Visa\" /\u003e\n      \u003c/a\u003e\n      \u003cbr /\u003e\n      Sponsored by \n      \u003ca href=\"https://www.technation.sucks\"\u003eTech Nation Visa Sucks\u003c/a\u003e\n      .\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/0.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## Table of Contents\n\n- [Table of Contents](#table-of-contents)\n- [API](#api)\n- [`Catchment` Class](#catchment-class)\n  * [`constructor(options?: Options): Catchment`](#constructoroptions-options-catchment)\n  * [`Options`](#options)\n    * [Collect Buffer](#collect-buffer)\n    * [Pipe Readable](#pipe-readable)\n- [`async collect(readable: Readable, options?: CollectOptions): string|Buffer`](#async-collectreadable-readableoptions-collectoptions-stringbuffer)\n  * [`_catchment.CollectOptions`](#type-_catchmentcollectoptions)\n- [Errors Handling](#errors-handling)\n- [Proxy Error](#proxy-error)\n- [Copyright](#copyright)\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/1.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## API\n\nThe package exports the default _Catchment_ class, and the `collect` method.\n\n```js\nimport Catchment, { collect } from 'catchment'\n```\n\nThe types and [externs](externs.js) for _Google Closure Compiler_ via [**_Depack_**](https://github.com/dpck/depack) are defined in the `_catchment` namespace.\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/2.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## `Catchment` Class\n\n_Catchment_ extends `Writable`, and pushes incoming data into an internal array. When the stream finishes, a promise referenced in the `promise` property is fulfilled with concatenated data. If an error occurred, the promise is rejected.\n\nA new _Catchment_ can be created with a constructor, which accepts [optional options](#options).\n\n```js\n/* yarn example/catchment.js */\nimport { Readable } from 'stream'\nimport Catchment from 'catchment'\n\nconst DATA = 'test-data'\n\n// creating a readable stream to use in the example\nconst rs = new Readable({\n  read() {\n    for (let i = 0; i \u003c DATA.length; i++) {\n      const c = DATA.charAt(i)\n      this.push(c)\n    }\n    this.push(null)\n  },\n})\n\n;(async () =\u003e {\n  try {\n    const catchment = new Catchment()\n    rs.pipe(catchment)\n    const res = await catchment.promise\n    console.log(res)\n  } catch (err) {\n    console.log(err)\n  }\n})()\n```\n\n```\ntest-data\n```\n\n### `constructor(`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`options?: Options,`\u003cbr/\u003e`): Catchment`\n\n### `Options`\n\nAn optional options object can be passed to the constructor.\n\n[`import('stream').Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable) __\u003ca name=\"type-streamreadable\"\u003e`stream.Readable`\u003c/a\u003e__\n\n__\u003ca name=\"type-_catchmentoptions\"\u003e`_catchment.Options`\u003c/a\u003e__: Options to pass to the `Writable` super constructor, and others shown below.\n\n|  Name  |                    Type                    |                                                                          Description                                                                          | Default |\n| ------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| rs     | _[!stream.Readable](#type-streamreadable)_ | A readable stream to automatically pipe into the catchment. If an error occurs during reading of this stream, the catchment promise will be rejected with it. | -       |\n| binary | _boolean_                                  | Whether to return a raw buffer instead of a string. The string is created by joining all incoming chunks together with `.join('')` method.                    | `false` |\n\n#### Collect Buffer\n\nTo receive a buffer, the `binary` option should be set to `true`:\n\n```js\n/* yarn example/binary.js */\nimport Catchment from 'catchment'\nimport { createReadable } from './lib'\n\n(async () =\u003e {\n  try {\n    const rs = createReadable('test-data')\n    const catchment = new Catchment({ binary: true })\n    rs.pipe(catchment)\n\n    const res = await catchment.promise\n    console.log(res)\n  } catch (err) {\n    console.log(err)\n  }\n})()\n```\n\n```\n\u003cBuffer 74 65 73 74 2d 64 61 74 61\u003e\n```\n\n#### Pipe Readable\n\nTo automatically pipe a _Readable_, and reject the promise if an error occurs there, the `rs` option can be passed:\n\n```js\n/* yarn example/rs.js */\nimport Catchment from 'catchment'\nimport { createReadStream } from 'fs'\n\n(async () =\u003e {\n  try {\n    const rs = createReadStream('missing-file.txt')\n    const { promise } = new Catchment({ rs })\n\n    const res = await promise\n    console.log(res)\n  } catch ({ message }) {\n    console.log(message)\n  }\n})()\n```\n\n```\nENOENT: no such file or directory, open 'missing-file.txt'\n```\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/3.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n\n\n\n\n## `async collect(`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`readable: Readable,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`options?: CollectOptions,`\u003cbr/\u003e`): string|Buffer`\n\nThe collect method is a shorthand for creating a new catchment, and piping a readable stream into it. It will accumulate all data from the read stream, and asynchronously return when the stream finishes. If an error occurs in the stream, the promise will be rejected.\n\nSome options can be passed to the `collect` method. The `proxyError` option is described in the [Proxy Error](#proxy-error) section.\n\n__\u003ca name=\"type-_catchmentcollectoptions\"\u003e`_catchment.CollectOptions`\u003c/a\u003e__: Options when collecting data into a catchment. They can extend `Writable` options which will be passed to the `Catchment` constructor.\n\n|    Name    |   Type    |                                                                                                Description                                                                                                 | Default |\n| ---------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| binary     | _boolean_ | Whether to return a raw buffer instead of a string. The string is created by joining all incoming chunks together with `.join('')` method.                                                                 | `false` |\n| proxyError | _boolean_ | Sets whether an error emitted by the stream with have its stack start at the line where the `collect` was called rather than inside of the stream. In other words, hides the implementation of the stream. | `false` |\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/4.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## Errors Handling\n\nWhenever an error is encountered during reading a readable stream, either piped into a _Catchment_ via the `rs` option, or passed as an argument to the `collect` method, it will result in a rejected promise.\n\nIf the error has a stack, it will be modified to clean it from internal Node.js lines, such as `_module`.\n\n```js\nimport { Readable } from 'stream'\nimport Catchment from 'catchment'\n\nconst rs = new Readable({\n  read() {\n    const er = new Error('example-error')\n    this.emit('error', er) // emit an error to reject catchment\n    this.push(null)\n  },\n})\n\n;(async () =\u003e {\n  try {\n    const catchment = new Catchment({\n      rs,\n    })\n    rs.pipe(catchment)\n    await catchment.promise\n  } catch ({ stack }) {\n    console.log(stack)\n  }\n})()\n```\n\n```\nError: example-error\n    at Readable.read [as _read] (/Users/zavr/adc/catchment/example/error-catchment.js:6:16)\n```\n\nIf the error does not have the stack (which can happen when using `createReadStream` from the `fs` module), it will appear as thrown at the point of either creating an instance of _Catchment_, or calling the `collect` method.\n\n```js\nimport { createReadStream } from 'fs'\nimport { collect } from 'catchment'\n\n(async () =\u003e {\n  try {\n    const rs = createReadStream('missing-file.txt')\n    await collect(rs)\n  } catch ({ stack }) {\n    console.log(stack)\n  }\n})()\n```\n\n```\nError: ENOENT: no such file or directory, open 'missing-file.txt'\n    at /Users/zavr/adc/catchment/example/error-collect.js:7:11\n    at Object.\u003canonymous\u003e (/Users/zavr/adc/catchment/example/error-collect.js:11:3)\n```\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/5.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## Proxy Error\n\nThe `collect` method can throw an error with its stack updated to when it was called. This can be useful when using 3-rd party streams without the need to look into details of their internal stack. By setting the `proxyError` option, all internal lines of the stream will be hidden, and the error will appear to be thrown by the call to the `collect` method.\n\n```js\nimport { collect } from 'catchment'\nimport { createReadable } from './lib'\nimport frame from 'frame-of-mind'\n\n/** 0. Prepare a read function in a stream that emits an error. */\nfunction read() {\n  const err = new Error('Whatever error happens')\n  setTimeout(() =\u003e {\n    this.emit('error', err)\n    this.push(null)\n  }, 10)\n}\n\nconst Collect = async ({ proxyError } = {}) =\u003e {\n  try {\n    const rs = createReadable(read)\n    await collect(rs, { proxyError })\n  } catch ({ stack }) {\n    console.log('COLLECT %s \\n%s', proxyError ? 'WITH PROXY' : '', frame(stack))\n  }\n}\n\nconst Listeners = async () =\u003e {\n  try {\n    const rs = createReadable(read)\n    const p = collect(rs).catch(() =\u003e {})\n    await new Promise((r, j) =\u003e {\n      rs.on('finish', r)\n      rs.on('error', j)\n    })\n    await p\n  } catch ({ stack }) {\n    console.log('LISTENERS:\\n%s', frame(stack))\n  }\n}\n\n(async () =\u003e {\n  await Collect()\n  await Listeners()\n  await Collect({ proxyError: true })\n})()\n```\n\n```\nCOLLECT  \n┌────────────────────────────────────────────────────────────────────────────────────────┐\n│ Error: Whatever error happens                                                          │\n│     at Readable.read (/Users/zavr/adc/catchment/example/error-collect2.js:8:15)        │\n│     at Readable.read [as _read] (/Users/zavr/adc/catchment/example/lib/index.js:11:16) │\n└────────────────────────────────────────────────────────────────────────────────────────┘\nLISTENERS:\n┌────────────────────────────────────────────────────────────────────────────────────────┐\n│ Error: Whatever error happens                                                          │\n│     at Readable.read (/Users/zavr/adc/catchment/example/error-collect2.js:8:15)        │\n│     at Readable.read [as _read] (/Users/zavr/adc/catchment/example/lib/index.js:11:16) │\n└────────────────────────────────────────────────────────────────────────────────────────┘\nCOLLECT WITH PROXY \n┌────────────────────────────────────────────────────────────────────────────┐\n│ Error: Whatever error happens                                              │\n│     at Collect (/Users/zavr/adc/catchment/example/error-collect2.js:18:11) │\n│     at /Users/zavr/adc/catchment/example/error-collect2.js:41:9            │\n│     at \u003canonymous\u003e                                                         │\n└────────────────────────────────────────────────────────────────────────────┘\n```\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/6.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n## Copyright\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003e\n      \u003ca href=\"https://artd.eco\"\u003e\n        \u003cimg src=\"https://raw.githubusercontent.com/wrote/wrote/master/images/artdeco.png\" alt=\"Art Deco\" /\u003e\n      \u003c/a\u003e\n    \u003c/th\u003e\n    \u003cth\u003e© \u003ca href=\"https://artd.eco\"\u003eArt Deco\u003c/a\u003e   2019\u003c/th\u003e\n    \u003cth\u003e\n      \u003ca href=\"https://www.technation.sucks\" title=\"Tech Nation Visa\"\u003e\n        \u003cimg src=\"https://raw.githubusercontent.com/artdecoweb/www.technation.sucks/master/anim.gif\"\n          alt=\"Tech Nation Visa\" /\u003e\n      \u003c/a\u003e\n    \u003c/th\u003e\n    \u003cth\u003e\u003ca href=\"https://www.technation.sucks\"\u003eTech Nation Visa Sucks\u003c/a\u003e\u003c/th\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"#table-of-contents\"\u003e\u003cimg src=\".documentary/section-breaks/-1.svg?sanitize=true\"\u003e\u003c/a\u003e\u003c/p\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartdecocode%2Fcatchment","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fartdecocode%2Fcatchment","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartdecocode%2Fcatchment/lists"}