diff --git a/.travis.yml b/.travis.yml index d0cf281..6aeb9f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,29 @@ language: node_js +dist: bionic cache: npm stages: - check - test - cov +branches: + only: + - master + - /^release\/.*$/ + node_js: - - '10' + - 'lts/*' + - 'node' os: - linux - osx - windows +before_install: + # modules with pre-built binaries may not have deployed versions for bleeding-edge node so this lets us fall back to building from source + - npm install -g @mapbox/node-pre-gyp + script: npx nyc -s npm run test:node -- --bail after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov @@ -20,7 +31,6 @@ jobs: include: - stage: check script: - - npx aegir commitlint --travis - npx aegir dep-check - npm run lint @@ -28,13 +38,14 @@ jobs: name: chrome addons: chrome: stable - script: npx aegir test -t browser -t webworker + script: + - npx aegir test -t browser -t webworker - stage: test name: firefox addons: firefox: latest - script: npx aegir test -t browser -t webworker -- --browsers FirefoxHeadless + script: npx aegir test -t browser -t webworker -- --browser firefox notifications: email: false diff --git a/package.json b/package.json index d6cc2b9..4e5d3ff 100644 --- a/package.json +++ b/package.json @@ -4,17 +4,18 @@ "description": "JavaScript Implementation of BlockService", "leadMaintainer": "Volker Mische ", "main": "src/index.js", + "types": "dist/src/index.d.ts", "scripts": { + "prepare": "aegir build --no-bundle", "lint": "aegir lint", - "build": "aegir build", + "prepublishOnly": "aegir build", "test": "aegir test", "test:node": "aegir test --target node", "test:browser": "aegir test --target browser", "release": "aegir release --docs", "release-minor": "aegir release --type minor --docs", "release-major": "aegir release --type major --docs", - "coverage": "aegir coverage", - "coverage-publish": "aegir coverage --provider coveralls", + "coverage": "aegir test -t node --cov && nyc report --reporter=html", "docs": "aegir docs" }, "repository": { @@ -30,24 +31,31 @@ }, "homepage": "https://github.com/ipfs/js-ipfs-block-service#readme", "devDependencies": { + "@types/fs-extra": "^9.0.8", + "@types/lodash.range": "^3.2.6", "abort-controller": "^3.0.0", - "aegir": "^22.0.0", + "aegir": "^31.0.4", + "assert": "^2.0.0", "cids": "^1.0.0", + "events": "^3.3.0", "fs-extra": "^9.0.0", - "ipfs-repo": "^6.0.0", - "ipld-block": "^0.10.0", + "ipfs-repo": "^9.0.0", + "ipld-block": "^0.11.1", + "it-all": "^1.0.5", "it-drain": "^1.0.1", - "lodash": "^4.17.11", + "lodash.range": "^3.2.0", "multihashing-async": "^2.0.1", - "uint8arrays": "^2.1.2" + "native-abort-controller": "^1.0.3", + "uint8arrays": "^2.1.2", + "util": "^0.12.3" }, "engines": { "node": ">=12.0.0", "npm": ">=3.0.0" }, "dependencies": { - "err-code": "^2.0.0", - "streaming-iterables": "^5.0.2" + "err-code": "^3.0.1", + "it-map": "^1.0.5" }, "contributors": [ "David Dias ", diff --git a/src/index.js b/src/index.js index 8b5369d..2ddadc7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,14 @@ 'use strict' -const { map } = require('streaming-iterables') +const map = require('it-map') const errcode = require('err-code') +/** + * @typedef {import('ipfs-repo')} IPFSRepo + * @typedef {import('ipld-block')} Block + * @typedef {import('cids')} CID + */ + /** * BlockService is a hybrid block datastore. It stores data in a local * datastore and may retrieve data from a remote Exchange. @@ -26,8 +32,7 @@ class BlockService { * If the node is online all requests for blocks first * check locally and afterwards ask the network for the blocks. * - * @param {Bitswap} bitswap - * @returns {void} + * @param {any} bitswap */ setExchange (bitswap) { this._bitswap = bitswap @@ -35,8 +40,6 @@ class BlockService { /** * Go offline, i.e. drop the reference to bitswap. - * - * @returns {void} */ unsetExchange () { this._bitswap = null @@ -44,8 +47,6 @@ class BlockService { /** * Is the blockservice online, i.e. is bitswap present. - * - * @returns {bool} */ hasExchange () { return this._bitswap != null @@ -55,9 +56,9 @@ class BlockService { * Put a block to the underlying datastore. * * @param {Block} block - * @param {Object} [options] - Options is an object with the following properties + * @param {object} [options] - Options is an object with the following properties * @param {AbortSignal} [options.signal] - A signal that can be used to abort any long-lived operations that are started as a result of this operation - * @returns {Promise} + * @returns {Promise} */ put (block, options) { if (this.hasExchange()) { @@ -70,10 +71,10 @@ class BlockService { /** * Put a multiple blocks to the underlying datastore. * - * @param {AsyncIterator} blocks - * @param {Object} [options] - Options is an object with the following properties + * @param {AsyncIterable | Iterable} blocks + * @param {object} [options] - Options is an object with the following properties * @param {AbortSignal} [options.signal] - A signal that can be used to abort any long-lived operations that are started as a result of this operation - * @returns {Promise} + * @returns {AsyncIterable} */ putMany (blocks, options) { if (this.hasExchange()) { @@ -87,7 +88,7 @@ class BlockService { * Get a block by cid. * * @param {CID} cid - * @param {Object} [options] - Options is an object with the following properties + * @param {object} [options] - Options is an object with the following properties * @param {AbortSignal} [options.signal] - A signal that can be used to abort any long-lived operations that are started as a result of this operation * @returns {Promise} */ @@ -102,10 +103,10 @@ class BlockService { /** * Get multiple blocks back from an array of cids. * - * @param {AsyncIterator} cids - * @param {Object} [options] - Options is an object with the following properties + * @param {AsyncIterable | Iterable} cids + * @param {object} [options] - Options is an object with the following properties * @param {AbortSignal} [options.signal] - A signal that can be used to abort any long-lived operations that are started as a result of this operation - * @returns {AsyncIterator} + * @returns {AsyncIterable} */ getMany (cids, options) { if (!Array.isArray(cids)) { @@ -115,8 +116,7 @@ class BlockService { if (this.hasExchange()) { return this._bitswap.getMany(cids, options) } else { - const getRepoBlocks = map((cid) => this._repo.blocks.get(cid, options)) - return getRepoBlocks(cids) + return map(cids, (cid) => this._repo.blocks.get(cid, options)) } } @@ -124,9 +124,8 @@ class BlockService { * Delete a block from the blockstore. * * @param {CID} cid - * @param {Object} [options] - Options is an object with the following properties + * @param {object} [options] - Options is an object with the following properties * @param {AbortSignal} [options.signal] - A signal that can be used to abort any long-lived operations that are started as a result of this operation - * @returns {Promise} */ async delete (cid, options) { if (!await this._repo.blocks.has(cid)) { @@ -139,10 +138,9 @@ class BlockService { /** * Delete multiple blocks from the blockstore. * - * @param {AsyncIterator} cids - * @param {Object} [options] - Options is an object with the following properties + * @param {AsyncIterable | Iterable} cids + * @param {object} [options] - Options is an object with the following properties * @param {AbortSignal} [options.signal] - A signal that can be used to abort any long-lived operations that are started as a result of this operation - * @returns {Promise} */ deleteMany (cids, options) { const repo = this._repo diff --git a/test/aborting-requests.spec.js b/test/aborting-requests.spec.js index d0bfce8..035531b 100644 --- a/test/aborting-requests.spec.js +++ b/test/aborting-requests.spec.js @@ -3,17 +3,26 @@ const { expect } = require('aegir/utils/chai') -const { collect } = require('streaming-iterables') -const AbortController = require('abort-controller') +const all = require('it-all') +const { AbortController } = require('native-abort-controller') const BlockService = require('../src') +/** + * @typedef {import('ipfs-repo')} IPFSRepo + */ + describe('aborting requests', () => { + /** @type {Error} */ let abortedErr + /** @type {BlockService} */ let r beforeEach(() => { abortedErr = new Error('Aborted!') + /** + * @param {...any} args + */ const abortOnSignal = (...args) => { const { signal } = args[args.length - 1] @@ -24,14 +33,17 @@ describe('aborting requests', () => { }) } + /** @type {IPFSRepo} */ const repo = { blocks: { put: abortOnSignal, + // @ts-ignore should return async iterable putMany: abortOnSignal, get: abortOnSignal, delete: abortOnSignal, + // @ts-ignore should return async iterable deleteMany: abortOnSignal, - has: () => true + has: () => Promise.resolve(true) } } r = new BlockService(repo) @@ -41,6 +53,7 @@ describe('aborting requests', () => { const controller = new AbortController() setTimeout(() => controller.abort(), 1) + // @ts-expect-error does not take string await expect(r.put('block', { signal: controller.signal })).to.eventually.rejectedWith(abortedErr) @@ -50,6 +63,7 @@ describe('aborting requests', () => { const controller = new AbortController() setTimeout(() => controller.abort(), 1) + // @ts-expect-error does not take string array await expect(r.putMany(['block'], { signal: controller.signal })).to.eventually.rejectedWith(abortedErr) @@ -59,6 +73,7 @@ describe('aborting requests', () => { const controller = new AbortController() setTimeout(() => controller.abort(), 1) + // @ts-expect-error does not take string await expect(r.get('cid', { signal: controller.signal })).to.eventually.rejectedWith(abortedErr) @@ -68,7 +83,8 @@ describe('aborting requests', () => { const controller = new AbortController() setTimeout(() => controller.abort(), 1) - await expect(collect(r.getMany(['cid'], { + // @ts-expect-error does not take string array + await expect(all(r.getMany(['cid'], { signal: controller.signal }))).to.eventually.rejectedWith(abortedErr) }) @@ -77,6 +93,7 @@ describe('aborting requests', () => { const controller = new AbortController() setTimeout(() => controller.abort(), 1) + // @ts-expect-error does not take string await expect(r.delete('cid', { signal: controller.signal })).to.eventually.rejectedWith(abortedErr) diff --git a/test/block-service-test.js b/test/block-service-test.js index 92f984a..198b559 100644 --- a/test/block-service-test.js +++ b/test/block-service-test.js @@ -4,18 +4,27 @@ const { expect } = require('aegir/utils/chai') const Block = require('ipld-block') -const _ = require('lodash') -const { collect } = require('streaming-iterables') +const range = require('lodash.range') +const all = require('it-all') const CID = require('cids') const multihashing = require('multihashing-async') const uint8ArrayFromString = require('uint8arrays/from-string') const drain = require('it-drain') +/** + * @typedef {import('ipfs-repo')} IPFSRepo + */ + const BlockService = require('../src') +/** + * @param {IPFSRepo} repo + */ module.exports = (repo) => { describe('block-service', () => { + /** @type {BlockService} */ let bs + /** @type {Block[]} */ let testBlocks before(async () => { @@ -72,7 +81,7 @@ module.exports = (repo) => { it('get many blocks through .getMany', async () => { const cids = testBlocks.map(b => b.cid) - const blocks = await collect(bs.getMany(cids)) + const blocks = await all(bs.getMany(cids)) expect(blocks).to.eql(testBlocks) }) @@ -121,7 +130,7 @@ module.exports = (repo) => { it('stores and gets lots of blocks', async function () { this.timeout(20 * 1000) - const data = _.range(1000).map((i) => { + const data = range(1000).map((i) => { return uint8ArrayFromString(`hello-${i}-${Math.random()}`) }) @@ -158,6 +167,9 @@ module.exports = (repo) => { it('retrieves a block through bitswap', async () => { // returns a block with a value equal to its key const bitswap = { + /** + * @param {CID} cid + */ get (cid) { return new Block(uint8ArrayFromString('secret'), cid) } @@ -174,8 +186,12 @@ module.exports = (repo) => { }) it('puts the block through bitswap', async () => { + /** @type {Block[]} */ const puts = [] const bitswap = { + /** + * @param {Block} block + */ put (block) { puts.push(block) } diff --git a/test/browser.js b/test/browser.js index fa97598..53222af 100644 --- a/test/browser.js +++ b/test/browser.js @@ -7,8 +7,11 @@ const IPFSRepo = require('ipfs-repo') const tests = require('./block-service-test') const idb = self.indexedDB || + // @ts-ignore self.mozIndexedDB || + // @ts-ignore self.webkitIndexedDB || + // @ts-ignore self.msIndexedDB idb.deleteDatabase('ipfs') diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7371337 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./node_modules/aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "test", + "src" + ] +}