diff --git a/.gitignore b/.gitignore index 9bb01f4b..5448ee99 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ Thumbs.db .nyc_output coverage bin +packages/*/dist \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..a218c09d --- /dev/null +++ b/.npmignore @@ -0,0 +1,8 @@ +node_modules +*.log +.DS_Store +Thumbs.db +.vscode +.nyc_output +coverage +bin \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 45e0158f..d0765e9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,5 @@ language: node_js node_js: - '12' -script: npm run lint && npm run coverage +script: npm run lint && npm run check && npm run coverage after_success: coveralls-lerna diff --git a/package.json b/package.json index 11dc1640..7b009181 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "start": "NODE_ENV=development lerna run --parallel start", "clean": "lerna run --parallel clean", "lint": "lerna run --parallel lint", + "check": "lerna run --parallel check", "publish": "lerna publish" }, "devDependencies": { diff --git a/packages/it-all/README.md b/packages/it-all/README.md index 6d9ab2a9..f234c597 100644 --- a/packages/it-all/README.md +++ b/packages/it-all/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-all)](https://david-dm.org/achingbrain/it?path=packages/it-all) -> Collects all values from an async iterator and returns them as an array +> Collects all values from an (async) iterable into an array and returns it. For when you need a one-liner to collect iterable values. diff --git a/packages/it-all/index.js b/packages/it-all/index.js index deadd6c4..aa8544e6 100644 --- a/packages/it-all/index.js +++ b/packages/it-all/index.js @@ -1,9 +1,16 @@ 'use strict' -const all = async (iterator) => { +/** + * Collects all values from an (async) iterable into an array and returns it. + * + * @template T + * @param {AsyncIterable|Iterable} source + * @returns {Promise} + */ +const all = async (source) => { const arr = [] - for await (const entry of iterator) { + for await (const entry of source) { arr.push(entry) } diff --git a/packages/it-all/package.json b/packages/it-all/package.json index 33a19ff9..1a8e6626 100644 --- a/packages/it-all/package.json +++ b/packages/it-all/package.json @@ -10,13 +10,25 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", "devDependencies": { "ava": "^2.4.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-all/tsconfig.json b/packages/it-all/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-all/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-batch/README.md b/packages/it-batch/README.md index 205d9cc6..6017ebc4 100644 --- a/packages/it-batch/README.md +++ b/packages/it-batch/README.md @@ -2,7 +2,8 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-batch)](https://david-dm.org/achingbrain/it?path=packages/it-batch) -> Takes an async iterator that emits things and emits them as fixed-size batches +> Takes an (async) iterable that emits things and returns an async iterable that +> emits those things in fixed-sized batches. The final batch may be smaller than the max. diff --git a/packages/it-batch/index.js b/packages/it-batch/index.js index 46ca7680..8f4df5f4 100644 --- a/packages/it-batch/index.js +++ b/packages/it-batch/index.js @@ -1,12 +1,23 @@ 'use strict' +/** + * Takes an (async) iterable that emits things and returns an async iterable that + * emits those things in fixed-sized batches. + * + * @template T + * @param {AsyncIterable|Iterable} source + * @param {number|string} [size=1] + * @returns {AsyncIterable} + */ async function * batch (source, size) { + // @ts-ignore - parseInt expects string size = parseInt(size) if (isNaN(size) || size < 1) { size = 1 } + /** @type {T[]} */ let things = [] for await (const thing of source) { diff --git a/packages/it-batch/package.json b/packages/it-batch/package.json index 813ef384..9bffa0b6 100644 --- a/packages/it-batch/package.json +++ b/packages/it-batch/package.json @@ -10,7 +10,10 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", @@ -18,6 +21,15 @@ "ava": "^2.4.0", "it-all": "^1.0.2", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + "./", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-batch/tsconfig.json b/packages/it-batch/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-batch/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-buffer-stream/README.md b/packages/it-buffer-stream/README.md index b4371183..96bee008 100644 --- a/packages/it-buffer-stream/README.md +++ b/packages/it-buffer-stream/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-buffer-stream)](https://david-dm.org/achingbrain/it?path=packages/it-buffer-stream) -> An async iterator that emits buffers containing bytes up to a certain length +> An async iterable that emits buffers containing bytes up to a certain length ## Install diff --git a/packages/it-buffer-stream/index.js b/packages/it-buffer-stream/index.js index aaac56c6..d7b61ebb 100644 --- a/packages/it-buffer-stream/index.js +++ b/packages/it-buffer-stream/index.js @@ -1,13 +1,29 @@ 'use strict' +// @ts-ignore - untyped dependency const randomBytes = require('iso-random-stream/src/random') +/** + * @typedef {Object} Options + * @property {number} [chunkSize] + * @property {function(Buffer):void} [collector] + * @property {function(number):Promise|Buffer} [generator] + */ + +/** @type {Options} */ const defaultOptions = { chunkSize: 4096, collector: () => {}, generator: (size) => Promise.resolve(randomBytes(size)) } +/** + * An async iterable that emits buffers containing bytes up to a certain length. + * + * @param {number} limit + * @param {Options} [options] + * @returns {AsyncIterable} + */ async function * bufferStream (limit, options = {}) { options = Object.assign({}, defaultOptions, options) let emitted = 0 diff --git a/packages/it-buffer-stream/package.json b/packages/it-buffer-stream/package.json index 9cdccd82..2c21d16a 100644 --- a/packages/it-buffer-stream/package.json +++ b/packages/it-buffer-stream/package.json @@ -10,7 +10,10 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", @@ -18,9 +21,18 @@ "ava": "^2.4.0", "buffer": "^5.5.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" }, "dependencies": { "iso-random-stream": "^1.1.1" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-buffer-stream/tsconfig.json b/packages/it-buffer-stream/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-buffer-stream/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-drain/README.md b/packages/it-drain/README.md index a6a455ed..9404f4de 100644 --- a/packages/it-drain/README.md +++ b/packages/it-drain/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-drain)](https://david-dm.org/achingbrain/it?path=packages/it-drain) -> Drains an async iterator without returning anything +> Drains an (async) iterable discarding its' content and does not return anything. Mostly useful for tests or when you want to be explicit about consuming an iterable without doing anything with any yielded values. diff --git a/packages/it-drain/index.js b/packages/it-drain/index.js index cf13c3e2..2b819e4f 100644 --- a/packages/it-drain/index.js +++ b/packages/it-drain/index.js @@ -1,5 +1,13 @@ 'use strict' +/** + * Drains an (async) iterable discarding its' content and does not return + * anything. + * + * @template T + * @param {AsyncIterable|Iterable} iterator + * @returns {Promise} + */ const drain = async (iterator) => { for await (const _ of iterator) { } // eslint-disable-line no-unused-vars } diff --git a/packages/it-drain/package.json b/packages/it-drain/package.json index 49230046..7c2e08a0 100644 --- a/packages/it-drain/package.json +++ b/packages/it-drain/package.json @@ -10,13 +10,25 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", "devDependencies": { "ava": "^2.4.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-drain/tsconfig.json b/packages/it-drain/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-drain/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-filter/README.md b/packages/it-filter/README.md index 430e86ff..69f5d1cf 100644 --- a/packages/it-filter/README.md +++ b/packages/it-filter/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-all)](https://david-dm.org/achingbrain/it?path=packages/it-all) -> Filters the passed iterable by using the filter function +> Filters the passed (async) iterable by using the filter function ## Install diff --git a/packages/it-filter/index.js b/packages/it-filter/index.js index 8f5bb51b..bfbac0e6 100644 --- a/packages/it-filter/index.js +++ b/packages/it-filter/index.js @@ -1,7 +1,14 @@ 'use strict' -const filter = async function * (iterator, fn) { - for await (const entry of iterator) { +/** + * Filters the passed (async) iterable by using the filter function + * + * @template T + * @param {AsyncIterable|Iterable} source + * @param {function(T):boolean|Promise} fn + */ +const filter = async function * (source, fn) { + for await (const entry of source) { if (await fn(entry)) { yield entry } diff --git a/packages/it-filter/package.json b/packages/it-filter/package.json index 8a71fa25..62ef3e74 100644 --- a/packages/it-filter/package.json +++ b/packages/it-filter/package.json @@ -10,7 +10,10 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", @@ -18,6 +21,15 @@ "ava": "^2.4.0", "it-all": "^1.0.2", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-filter/tsconfig.json b/packages/it-filter/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-filter/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-first/README.md b/packages/it-first/README.md index 197a39a9..5dd02219 100644 --- a/packages/it-first/README.md +++ b/packages/it-first/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-first)](https://david-dm.org/achingbrain/it?path=packages/it-first) -> Returns the first result from an async iterator +> Returns the first result from an (async) iterable. Mostly useful for tests. diff --git a/packages/it-first/index.js b/packages/it-first/index.js index cd737f03..42bc08e4 100644 --- a/packages/it-first/index.js +++ b/packages/it-first/index.js @@ -1,7 +1,15 @@ 'use strict' -const first = async (iterator) => { - for await (const entry of iterator) { +/** + * Returns the first result from an (async) iterable, unless empty, in which + * case returns `void`. + * + * @template T + * @param {AsyncIterable|Iterable} source + * @returns {Promise} + */ +const first = async (source) => { + for await (const entry of source) { return entry } } diff --git a/packages/it-first/package.json b/packages/it-first/package.json index c1194c2a..8cf47d42 100644 --- a/packages/it-first/package.json +++ b/packages/it-first/package.json @@ -10,13 +10,25 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", "devDependencies": { "ava": "^2.4.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-first/tsconfig.json b/packages/it-first/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-first/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-flat-batch/README.md b/packages/it-flat-batch/README.md index 35a57f7a..c1ff7f51 100644 --- a/packages/it-flat-batch/README.md +++ b/packages/it-flat-batch/README.md @@ -2,9 +2,10 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-flat-batch)](https://david-dm.org/achingbrain/it?path=packages/it-flat-batch) -> Takes an async iterator that emits variable length arrays and emits them as fixed-size batches +> Takes an (async) iterable that emits variable length arrays of things and +> returns an async iterable that emits those thnigs in fixed-size batches. -The final batch may be smaller than the max. +The final batch may be smaller than requested batch size. ## Install diff --git a/packages/it-flat-batch/index.js b/packages/it-flat-batch/index.js index c81381e3..84fecf1b 100644 --- a/packages/it-flat-batch/index.js +++ b/packages/it-flat-batch/index.js @@ -1,12 +1,23 @@ 'use strict' -async function * batch (source, size) { - size = parseInt(size) +/** + * Takes an (async) iterable that emits variable length arrays of things and + * returns an async iterable that emits those thnigs in fixed-size batches. + * + * @template T + * @param {AsyncIterable|Iterable|AsyncIterable|Iterable} source + * @param {number|string} [batchSize=1] + * @returns {AsyncIterable} + */ +async function * batch (source, batchSize) { + // @ts-ignore - expects string not a number + let size = parseInt(batchSize) if (isNaN(size) || size < 1) { size = 1 } + /** @type {T[]} */ let things = [] for await (const set of source) { diff --git a/packages/it-flat-batch/package.json b/packages/it-flat-batch/package.json index e2bd6896..a908de36 100644 --- a/packages/it-flat-batch/package.json +++ b/packages/it-flat-batch/package.json @@ -10,7 +10,10 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", @@ -18,6 +21,15 @@ "ava": "^2.4.0", "it-all": "^1.0.2", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-flat-batch/tsconfig.json b/packages/it-flat-batch/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-flat-batch/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-glob/index.js b/packages/it-glob/index.js index d9049639..23628b90 100644 --- a/packages/it-glob/index.js +++ b/packages/it-glob/index.js @@ -1,9 +1,29 @@ 'use strict' +// @ts-ignore const fs = require('fs-extra') const path = require('path') const minimatch = require('minimatch') +/** + * @typedef {string} Glob + * @typedef {Object} OptionsExt + * @property {Glob[]} [ignore] - Glob patterns to ignore + * @property {string} [cwd=process.cwd()] + * @property {boolean} [absolute=false] - If true produces absolute paths + * @property {boolean} [nodir] - If true yields file paths and skip directories + * + * @typedef {OptionsExt & minimatch.IOptions} Options + */ + +/** + * Async iterable filename pattern matcher + * + * @param {string} dir + * @param {string} pattern + * @param {Options} [options] + * @returns {AsyncIterable} + */ module.exports = async function * glob (dir, pattern, options = {}) { const absoluteDir = path.resolve(dir) const relativeDir = path.relative(options.cwd || process.cwd(), dir) @@ -23,6 +43,13 @@ module.exports = async function * glob (dir, pattern, options = {}) { } } +/** + * @param {string} base + * @param {string} dir + * @param {Glob} pattern + * @param {Options} options + * @returns {AsyncIterable} + */ async function * _glob (base, dir, pattern, options) { for await (const entry of await fs.readdir(path.join(base, dir))) { const relativeEntryPath = path.join(dir, entry) diff --git a/packages/it-glob/package.json b/packages/it-glob/package.json index 8155133c..72c20af0 100644 --- a/packages/it-glob/package.json +++ b/packages/it-glob/package.json @@ -10,7 +10,10 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", @@ -22,9 +25,18 @@ "ava": "^2.4.0", "it-all": "^1.0.2", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" }, "browser": { "fs-extra": false + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-glob/tsconfig.json b/packages/it-glob/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-glob/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-last/README.md b/packages/it-last/README.md index 15ac7e87..78e620d0 100644 --- a/packages/it-last/README.md +++ b/packages/it-last/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-last)](https://david-dm.org/achingbrain/it?path=packages/it-last) -> Returns the last result from an async iterator +> Returns the last item of an (async) iterable Mostly useful for tests. diff --git a/packages/it-last/index.js b/packages/it-last/index.js index b5de053f..8f61f3cf 100644 --- a/packages/it-last/index.js +++ b/packages/it-last/index.js @@ -1,9 +1,18 @@ 'use strict' -const last = async (iterator) => { +/** + * Returns the last item of an (async) iterable, unless empty, in which case + * return `void`. + * + * @template T + * @param {AsyncIterable|Iterable} source + * @returns {Promise} + */ +const last = async (source) => { + /** @type {T|void} */ let res - for await (const entry of iterator) { + for await (const entry of source) { res = entry } diff --git a/packages/it-last/package.json b/packages/it-last/package.json index 229c6325..507c3fbe 100644 --- a/packages/it-last/package.json +++ b/packages/it-last/package.json @@ -10,13 +10,25 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", "devDependencies": { "ava": "^2.4.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-last/tsconfig.json b/packages/it-last/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-last/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-map/README.md b/packages/it-map/README.md index 64dde9eb..73a14b2a 100644 --- a/packages/it-map/README.md +++ b/packages/it-map/README.md @@ -3,7 +3,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-map)](https://david-dm.org/achingbrain/it?path=packages/it-map) -> Maps the values yielded by an async iterator +> Maps the values yielded by an (async) iterator ## Install diff --git a/packages/it-map/index.js b/packages/it-map/index.js index 0fd70f38..5baef902 100644 --- a/packages/it-map/index.js +++ b/packages/it-map/index.js @@ -1,7 +1,16 @@ 'use strict' -const map = async function * (iterator, func) { - for await (const val of iterator) { +/** + * Takes an (async) iterable and return one with each item mapped with given + * function. + * + * @template I,O + * @param {AsyncIterable|Iterable} source + * @param {function(I):O|Promise} func + * @returns {AsyncIterable} + */ +const map = async function * (source, func) { + for await (const val of source) { yield func(val) } } diff --git a/packages/it-map/package.json b/packages/it-map/package.json index 5bed85d6..ea1d4dd7 100644 --- a/packages/it-map/package.json +++ b/packages/it-map/package.json @@ -10,14 +10,26 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", "devDependencies": { "ava": "^2.4.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } }, "gitHead": "42faa5aeb9de0e07c956d73020bd74fd08e3029d" -} +} \ No newline at end of file diff --git a/packages/it-map/tsconfig.json b/packages/it-map/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-map/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-multipart/index.js b/packages/it-multipart/index.js index 95a71cd9..803ad467 100644 --- a/packages/it-multipart/index.js +++ b/packages/it-multipart/index.js @@ -1,15 +1,34 @@ 'use strict' const { Buffer } = require('buffer') +/** @type {(source:Buffer, search:Buffer, index?:number) => number} */ +// @ts-ignore const bIndexOf = require('buffer-indexof') +/** @typedef {function(string):IncomingHttpHeaders} */ +// @ts-ignore const parseHeaders = require('parse-headers') module.exports = multipart -async function * multipart (stream, boundary) { +/** + * @typedef {import('http').IncomingHttpHeaders} IncomingHttpHeaders + */ + +/** + * @template T + * @typedef {AsyncIterable & AsyncIterator} It + */ + +/** + * Streaming multipart HTTP message parser + * @param {import('http').IncomingMessage} source + * @param {string} [boundary] + * @returns {AsyncIterable<{headers:IncomingHttpHeaders, body:It}>} + */ +async function * multipart (source, boundary) { if (!boundary) { - if (stream && stream.headers && stream.headers['content-type'] && stream.headers['content-type'].includes('boundary')) { - boundary = stream.headers['content-type'].split('boundary=')[1].trim() + if (source && source.headers && source.headers['content-type'] && source.headers['content-type'].includes('boundary')) { + boundary = source.headers['content-type'].split('boundary=')[1].trim() } else { throw new Error('Not a multipart request') } @@ -19,7 +38,7 @@ async function * multipart (stream, boundary) { const headerEnd = Buffer.from('\r\n\r\n') // allow pushing data back into stream - stream = prefixStream(stream) + const stream = prefixStream(source) // consume initial boundary await consumeUntilAfter(stream, Buffer.from(boundary)) @@ -49,7 +68,13 @@ async function * multipart (stream, boundary) { } } -// yield chunks of buffer until a the needle is reached. consume the needle without yielding it +/** + * yield chunks of buffer until a the needle is reached. consume the needle + * without yielding it + * @param {PrefixStream} haystack + * @param {Buffer} needle + * @returns {AsyncIterable} + */ async function * yieldUntilAfter (haystack, needle) { let buffer = Buffer.alloc(0) @@ -85,14 +110,35 @@ async function * yieldUntilAfter (haystack, needle) { } } +/** + * @param {PrefixStream} haystack + * @param {Buffer} needle + * @returns {Promise} + */ async function consumeUntilAfter (haystack, needle) { - for await (const chunk of yieldUntilAfter(haystack, needle)) { // eslint-disable-line no-unused-vars + for await (const _chunk of yieldUntilAfter(haystack, needle)) { // eslint-disable-line no-unused-vars } } -// a stream that lets us push content back into it for consumption elsewhere +/** + * @template T + * @typedef {Object} PrefixPush + * @property {function(T):void} push + */ + +/** + * @template T + * @typedef {It & PrefixPush} PrefixStream + */ + +/** + * a stream that lets us push content back into it for consumption elsewhere + * @param {AsyncIterable} stream + * @returns {PrefixStream} + */ function prefixStream (stream) { + /** @type {Buffer[]} */ const buffer = [] const streamIterator = stream[Symbol.asyncIterator]() @@ -100,16 +146,20 @@ function prefixStream (stream) { [Symbol.asyncIterator]: () => { return iterator }, - next: () => { + next: async () => { if (buffer.length) { return { done: false, - value: buffer.shift() + /** @type {Buffer} */ + value: (buffer.shift()) } } return streamIterator.next() }, + /** + * @param {Buffer} buf + */ push: function (buf) { buffer.push(buf) } @@ -118,7 +168,12 @@ function prefixStream (stream) { return iterator } +/** + * @param {AsyncIterable} stream + * @returns {{complete:Promise, iterator:It}} + */ function waitForStreamToBeConsumed (stream) { + /** @type {{resolve():void, reject(error:Error):void}} */ let pending const complete = new Promise((resolve, reject) => { pending = { @@ -143,6 +198,7 @@ function waitForStreamToBeConsumed (stream) { return next } catch (err) { pending.reject(err) + throw err } } } @@ -153,6 +209,10 @@ function waitForStreamToBeConsumed (stream) { } } +/** + * @param {AsyncIterable} stream + * @returns {Promise} + */ const collect = async (stream) => { const buffers = [] let size = 0 diff --git a/packages/it-multipart/package.json b/packages/it-multipart/package.json index b7c9ac7c..1fd1be98 100644 --- a/packages/it-multipart/package.json +++ b/packages/it-multipart/package.json @@ -10,7 +10,10 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", @@ -24,6 +27,15 @@ "form-data": "^2.5.0", "node-fetch": "^2.6.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-multipart/test.js b/packages/it-multipart/test.js index d7de0d72..a555a804 100644 --- a/packages/it-multipart/test.js +++ b/packages/it-multipart/test.js @@ -1,17 +1,25 @@ import test from 'ava' import http from 'http' import handler from '.' +// @ts-ignore import fetch from 'node-fetch' import FormData from 'form-data' +/** @type {string} */ let port +/** @type {import('http').Server} */ let server test.before.cb((t) => { + /** + * @param {import('http').IncomingMessage} req + */ async function echo (req) { + /** @type {Record} */ const files = {} for await (const part of handler(req)) { + // @ts-ignore - header may not be present const name = part.headers['content-disposition'].match(/name="(.*)"/)[1] files[name] = '' @@ -26,7 +34,9 @@ test.before.cb((t) => { server = http.createServer((req, res) => { if (req.method === 'POST' && + // @ts-ignore - header may not be present req.headers['content-type'].includes('multipart/form-data') && + // @ts-ignore - header may not be present req.headers['content-type'].includes('boundary=') ) { echo(req) @@ -50,6 +60,7 @@ test.before.cb((t) => { res.writeHead(404) res.end() }).listen(() => { + // @ts-ignore - could be null port = server.address().port t.end() }) @@ -108,6 +119,7 @@ test('it parses loads of files from multipart requests', async (t) => { test('it throws if not handed a multipart request', async (t) => { await t.throwsAsync(async () => { + // @ts-ignore for await (const _ of handler()) { // eslint-disable-line no-unused-vars } diff --git a/packages/it-multipart/tsconfig.json b/packages/it-multipart/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-multipart/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-parallel-batch/README.md b/packages/it-parallel-batch/README.md index d33b50bd..513e93ed 100644 --- a/packages/it-parallel-batch/README.md +++ b/packages/it-parallel-batch/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-parallel-batch)](https://david-dm.org/achingbrain/it?path=packages/it-parallel-batch) -> Takes an async iterator that emits promise-returning functions, invokes them in parallel and emits the results as they become available but in the same order as the input +> Takes an (async) iterable that emits promise-returning functions, invokes them in parallel and emits the results as they become available but in the same order as the input The final batch may be smaller than the batch size. diff --git a/packages/it-parallel-batch/index.js b/packages/it-parallel-batch/index.js index c5306beb..ab05bd64 100644 --- a/packages/it-parallel-batch/index.js +++ b/packages/it-parallel-batch/index.js @@ -2,26 +2,43 @@ const batch = require('it-batch') +/** + * @template T + * @typedef {function():Promise} Task + */ + +/** + * Takes an (async) iterator that emits promise-returning functions, + * invokes them in parallel and emits the results as they become available but + * in the same order as the input + * + * @template T + * @param {AsyncIterable>} source + * @param {number|string} [size=1] + * @returns {AsyncIterable} + */ async function * parallelBatch (source, size) { + // @ts-ignore - expects string not a number size = parseInt(size) if (isNaN(size) || size < 1) { size = 1 } - for await (let things of batch(source, size)) { - things = things.map(p => { - return p().then(res => ({ res }), err => ({ err })) + for await (const tasks of batch(source, size)) { + /** @type {Promise<{ok:true, value:T}|{ok:false, err:Error}>[]} */ + const things = tasks.map(p => { + return p().then(value => ({ ok: true, value }), err => ({ ok: false, err })) }) for (let i = 0; i < things.length; i++) { - const { res, err } = await things[i] + const result = await things[i] - if (err) { - throw err + if (result.ok) { + yield result.value + } else { + throw result.err } - - yield res } } } diff --git a/packages/it-parallel-batch/package.json b/packages/it-parallel-batch/package.json index ccec9cfa..c5eeb60f 100644 --- a/packages/it-parallel-batch/package.json +++ b/packages/it-parallel-batch/package.json @@ -10,7 +10,10 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", @@ -19,9 +22,18 @@ "delay": "^4.3.0", "it-all": "^1.0.2", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" }, "dependencies": { "it-batch": "^1.0.4" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-parallel-batch/tsconfig.json b/packages/it-parallel-batch/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-parallel-batch/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-reduce/README.md b/packages/it-reduce/README.md index 31f28416..7646c839 100644 --- a/packages/it-reduce/README.md +++ b/packages/it-reduce/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/achingbrain/it.svg?branch=master)](https://travis-ci.org/achingbrain/it?branch=master) [![Coverage Status](https://coveralls.io/repos/github/achingbrain/it/badge.svg?branch=master)](https://coveralls.io/github/achingbrain/it?branch=master) [![Dependencies Status](https://david-dm.org/achingbrain/it/status.svg?path=packages/it-reduce)](https://david-dm.org/achingbrain/it?path=packages/it-reduce) -> Reduces the values yielded by an async iterator +> Reduces the values yielded by an (async) iterable Mostly useful for tests or when you want to be explicit about consuming an iterable without doing anything with any yielded values. diff --git a/packages/it-reduce/index.js b/packages/it-reduce/index.js index 12da8d62..69c66367 100644 --- a/packages/it-reduce/index.js +++ b/packages/it-reduce/index.js @@ -1,7 +1,15 @@ 'use strict' -const reduce = async (iterator, func, init) => { - for await (const val of iterator) { +/** + * Reduces the values yielded by an (async) iterable + * + * @template T, V + * @param {AsyncIterable|Iterable} source + * @param {function(V, T):V} func + * @param {V} init + */ +const reduce = async (source, func, init) => { + for await (const val of source) { init = func(init, val) } diff --git a/packages/it-reduce/package.json b/packages/it-reduce/package.json index 5855c8ed..9f1e8c12 100644 --- a/packages/it-reduce/package.json +++ b/packages/it-reduce/package.json @@ -10,14 +10,26 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", "devDependencies": { "ava": "^2.4.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } }, "gitHead": "42faa5aeb9de0e07c956d73020bd74fd08e3029d" -} +} \ No newline at end of file diff --git a/packages/it-reduce/tsconfig.json b/packages/it-reduce/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-reduce/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-to-browser-readablestream/index.js b/packages/it-to-browser-readablestream/index.js index 5bc89a3a..7cd96beb 100644 --- a/packages/it-to-browser-readablestream/index.js +++ b/packages/it-to-browser-readablestream/index.js @@ -1,9 +1,19 @@ 'use strict' +/** @type {typeof window} */ +// @ts-ignore const globalThis = require('@ungap/global-this') +/** + * @template T + * @param {AsyncIterator|Iterator} source + * @param {QueuingStrategy} [queuingStrategy] + * @returns {ReadableStream} + */ function itToBrowserReadableStream (source, queuingStrategy = {}) { - return new globalThis.ReadableStream({ + /** @type {UnderlyingSource & { _cancelled:boolean} } */ + const pump = { + _cancelled: false, async start () { this._cancelled = false }, @@ -28,7 +38,9 @@ function itToBrowserReadableStream (source, queuingStrategy = {}) { cancel () { this._cancelled = true } - }, queuingStrategy) + } + + return new globalThis.ReadableStream(pump, queuingStrategy) } module.exports = itToBrowserReadableStream diff --git a/packages/it-to-browser-readablestream/package.json b/packages/it-to-browser-readablestream/package.json index de2f3c08..9d363409 100644 --- a/packages/it-to-browser-readablestream/package.json +++ b/packages/it-to-browser-readablestream/package.json @@ -10,7 +10,10 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", @@ -21,5 +24,13 @@ }, "dependencies": { "@ungap/global-this": "^0.3.1" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-to-browser-readablestream/tsconfig.json b/packages/it-to-browser-readablestream/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-to-browser-readablestream/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/packages/it-to-buffer/index.js b/packages/it-to-buffer/index.js index 312c5898..c8fd1708 100644 --- a/packages/it-to-buffer/index.js +++ b/packages/it-to-buffer/index.js @@ -2,6 +2,12 @@ const { Buffer } = require('buffer') +/** + * Takes an (async) iterable that yields buffer-like-objects and concats them + * into one buffer + * @param {AsyncIterable|Iterable} stream + * @returns {Promise} + */ async function toBuffer (stream) { let buffer = Buffer.alloc(0) diff --git a/packages/it-to-buffer/package.json b/packages/it-to-buffer/package.json index 314ee418..90637c98 100644 --- a/packages/it-to-buffer/package.json +++ b/packages/it-to-buffer/package.json @@ -10,16 +10,28 @@ "test": "ava", "lint": "standard", "coverage": "nyc --reporter html --reporter lcov ava", - "clean": "rm -rf .nyc_output coverage" + "clean": "rm -rf .nyc_output coverage", + "check": "tsc --noEmit", + "build": "npm run build:types", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist" }, "author": "Alex Potsides ", "license": "ISC", "devDependencies": { "ava": "^2.4.0", "nyc": "^14.0.0", - "standard": "^14.3.1" + "standard": "^14.3.1", + "typescript": "^3.9.7" }, "dependencies": { "buffer": "^5.5.0" + }, + "typesVersions": { + "*": { + "*": [ + ".", + "dist/*" + ] + } } -} +} \ No newline at end of file diff --git a/packages/it-to-buffer/tsconfig.json b/packages/it-to-buffer/tsconfig.json new file mode 100644 index 00000000..f5e4524f --- /dev/null +++ b/packages/it-to-buffer/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../typescript.json", + "include": [ + "." + ] +} \ No newline at end of file diff --git a/typescript.json b/typescript.json new file mode 100644 index 00000000..35dbf017 --- /dev/null +++ b/typescript.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "strict": true, + "alwaysStrict": true, + "esModuleInterop": true, + "target": "ES2018", + "moduleResolution": "node", + "declaration": true, + "declarationMap": true, + "outDir": "dist" + }, + "include": [], + "baseUrl": ".", + "exclude": [ + "packages/*/dist", + "packages/*/test*.js", + "packages/*/node_modules", + "node_modules", + "node_modules/buffer" + ], + "compileOnSave": false +} \ No newline at end of file