From c8a91f0e4bbf1cbfa69aefe5e881a5632e8bb430 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Thu, 9 Sep 2021 12:17:00 +0100 Subject: [PATCH] chore: switch to esm (#100) Also removes travis in favour of gh actions BREAKING CHANGE: now uses named exports --- .github/workflows/main.yml | 52 ++++++++++++++++++++ .gitignore | 1 + .travis.yml | 33 ------------- README.md | 6 +-- package.json | 43 ++++++++++------- src/index.js | 17 +++---- src/{types.d.ts => types.ts} | 2 +- src/utils.js | 20 +++----- test/index.spec.js | 93 +++++++++++++++++------------------- test/utils.js | 19 ++++---- tsconfig.json | 2 +- 11 files changed, 149 insertions(+), 139 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml rename src/{types.d.ts => types.ts} (69%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..05587f3 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,52 @@ +name: ci +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npx aegir lint + - uses: gozala/typescript-error-reporter-action@v1.0.8 + - run: npx aegir build --no-bundle + - run: npx aegir dep-check + test-node: + needs: check + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + node: [14, 16] + fail-fast: true + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + - run: npm install + - run: npm run pretest + - run: npx nyc --reporter=lcov aegir test -t node -- --bail + - uses: codecov/codecov-action@v1 + test-electron-main: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npm run pretest + - run: npx xvfb-maybe aegir test -t electron-main --bail -f dist/cjs/node-test/*js + test-electron-renderer: + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: npm install + - run: npm run pretest + - run: npx xvfb-maybe aegir test -t electron-renderer --bail -f dist/cjs/browser-test/*js diff --git a/.gitignore b/.gitignore index 85223e2..f21fe31 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,4 @@ typings/ # while testing npm5 package-lock.json yarn.lock +types diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 41b62b8..0000000 --- a/.travis.yml +++ /dev/null @@ -1,33 +0,0 @@ -language: node_js -cache: npm -dist: bionic - -branches: - only: - - master - - /^release\/.*$/ - -node_js: - - 'lts/*' - - 'node' - -stages: - - check - -os: - - linux - - osx - - windows - -script: npx nyc -s npm run test:node -- --bail -after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov - -jobs: - include: - - stage: check - script: - - npx aegir dep-check - - npm run lint - -notifications: - email: false diff --git a/README.md b/README.md index ab1a38c..c10b143 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ ## Usage ```js -const DatastorePubsub = require('datastore-pubsub') +import { PubsubDatastore } from 'datastore-pubsub' -const dsPubsub = new DatastorePubsub(pubsub, datastore, peerId, validator) +const dsPubsub = new PubsubDatastore(pubsub, datastore, peerId, validator) ``` ## API @@ -46,7 +46,7 @@ const dsPubsub = new DatastorePubsub(pubsub, datastore, peerId, validator) #### Setup ```js -new DatastorePubsub(pubsub, datastore, peerId, validator, subscriptionKeyFn) +new PubsubDatastore(pubsub, datastore, peerId, validator, subscriptionKeyFn) ``` Creates a DatastorePubsub instance. diff --git a/package.json b/package.json index 5cf372b..ab80c4f 100644 --- a/package.json +++ b/package.json @@ -4,20 +4,30 @@ "description": "Responsible for providing an interface-datastore compliant api to pubsub", "leadMaintainer": "Vasco Santos ", "main": "src/index.js", - "types": "dist/src/index.d.ts", + "type": "module", + "types": "types/src/index.d.ts", + "files": [ + "*", + "!**/*.tsbuildinfo" + ], + "eslintConfig": { + "extends": "ipfs", + "parserOptions": { + "sourceType": "module" + } + }, "scripts": { - "prepare": "npm run build", - "build": "aegir build --no-bundle", + "clean": "rimraf dist types", + "prepare": "aegir build --no-bundle && cp -R types dist", "lint": "aegir ts -p check && aegir lint", - "release": "aegir release --target node", - "release-minor": "aegir release --target node --type minor", - "release-major": "aegir release --target node --type major", - "test": "aegir test -t node", - "test:node": "aegir test -t node" + "build": "aegir build", + "release": "aegir release", + "release-minor": "aegir release --type minor", + "release-major": "aegir release --type major", + "pretest": "aegir build --esm-tests", + "test": "aegir test", + "dep-check": "aegir dep-check -i rimraf" }, - "pre-push": [ - "lint" - ], "repository": { "type": "git", "url": "git+https://github.com/ipfs/js-datastore-pubsub.git" @@ -27,10 +37,6 @@ "datastore", "pubsub" ], - "files": [ - "dist", - "src" - ], "author": "Vasco Santos ", "license": "MIT", "bugs": { @@ -38,9 +44,10 @@ }, "homepage": "https://github.com/ipfs/js-datastore-pubsub#readme", "dependencies": { + "datastore-core": "^6.0.7", "debug": "^4.2.0", "err-code": "^3.0.1", - "interface-datastore": "^5.1.1", + "interface-datastore": "^6.0.2", "uint8arrays": "^3.0.0" }, "devDependencies": { @@ -55,7 +62,9 @@ "libp2p-record": "^0.10.0", "p-wait-for": "^3.1.0", "peer-id": "^0.15.0", - "sinon": "^11.1.1" + "rimraf": "^3.0.2", + "sinon": "^11.1.1", + "util": "^0.12.4" }, "contributors": [ "Vasco Santos ", diff --git a/src/index.js b/src/index.js index 6eaa53b..b55bea1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,10 @@ -'use strict' +import { Key } from 'interface-datastore' +import { BaseDatastore } from 'datastore-core' +import { encodeBase32, keyToTopic, topicToKey } from './utils.js' +import { equals as uint8ArrayEquals } from 'uint8arrays/equals' +import errcode from 'err-code' +import debug from 'debug' -const { Key, Adapter } = require('interface-datastore') -const { encodeBase32, keyToTopic, topicToKey } = require('./utils') -const { equals: uint8ArrayEquals } = require('uint8arrays/equals') - -const errcode = require('err-code') -const debug = require('debug') const log = Object.assign(debug('datastore-pubsub:publisher'), { error: debug('datastore-pubsub:publisher:error') }) @@ -19,7 +18,7 @@ const log = Object.assign(debug('datastore-pubsub:publisher'), { // DatastorePubsub is responsible for providing an api for pubsub to be used as a datastore with // [TieredDatastore]{@link https://github.com/ipfs/js-datastore-core/blob/master/src/tiered.js} -class DatastorePubsub extends Adapter { +export class PubsubDatastore extends BaseDatastore { /** * Creates an instance of DatastorePubsub. * @@ -314,5 +313,3 @@ class DatastorePubsub extends Adapter { log(`record for ${keyToTopic(key)} was stored in the datastore`) } } - -exports = module.exports = DatastorePubsub diff --git a/src/types.d.ts b/src/types.ts similarity index 69% rename from src/types.d.ts rename to src/types.ts index 683efbf..6985746 100644 --- a/src/types.d.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { ValidateFn, SelectFn } from 'libp2p-interfaces/src/types' +import type { ValidateFn, SelectFn } from 'libp2p-interfaces/src/types' export interface SubscriptionKeyFn { (key: Uint8Array): Promise | Uint8Array } export interface Validator { diff --git a/src/utils.js b/src/utils.js index bf0a4cf..39b4152 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,8 +1,6 @@ -'use strict' - -const errcode = require('err-code') -const { toString: uint8ArrayToString } = require('uint8arrays/to-string') -const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') +import errcode from 'err-code' +import { toString as uint8ArrayToString } from 'uint8arrays/to-string' +import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' /** * @typedef {import('interface-datastore').Key} Key @@ -13,7 +11,7 @@ const namespace = '/record/' /** * @param {Uint8Array} buf */ -function encodeBase32 (buf) { +export function encodeBase32 (buf) { return uint8ArrayToString(buf, 'base32') } @@ -22,7 +20,7 @@ function encodeBase32 (buf) { * * @param {Uint8Array | string} key */ -function keyToTopic (key) { +export function keyToTopic (key) { // Record-store keys are arbitrary binary. However, pubsub requires UTF-8 string topic IDs // Encodes to "/record/base64url(key)" if (typeof key === 'string' || key instanceof String) { @@ -39,7 +37,7 @@ function keyToTopic (key) { * * @param {string} topic */ -function topicToKey (topic) { +export function topicToKey (topic) { if (topic.substring(0, namespace.length) !== namespace) { throw errcode(new Error('topic received is not from a record'), 'ERR_TOPIC_IS_NOT_FROM_RECORD_NAMESPACE') } @@ -48,9 +46,3 @@ function topicToKey (topic) { return uint8ArrayFromString(key, 'base64url') } - -module.exports = { - encodeBase32, - keyToTopic, - topicToKey -} diff --git a/test/index.spec.js b/test/index.spec.js index d7395c1..e801749 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -1,27 +1,22 @@ /* eslint-env mocha */ -'use strict' - -const { expect } = require('aegir/utils/chai') -const sinon = require('sinon') -const errcode = require('err-code') -const isNode = require('detect-node') -const { toString: uint8ArrayToString } = require('uint8arrays/to-string') -const { fromString: uint8ArrayFromString } = require('uint8arrays/from-string') - -const DatastorePubsub = require('../src') - -const { - Key, - MemoryDatastore -} = require('interface-datastore') -const { + +import { expect } from 'aegir/utils/chai.js' +import sinon from 'sinon' +import errcode from 'err-code' +import isNode from 'detect-node' +import { toString as uint8ArrayToString } from 'uint8arrays/to-string' +import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' +import { PubsubDatastore } from '../src/index.js' +import { Key } from 'interface-datastore' +import { MemoryDatastore } from 'datastore-core' +import { createPubsubNode, connectPubsubNodes, waitFor, waitForPeerToSubscribe -} = require('./utils') -const { Record } = require('libp2p-record') -const { keyToTopic, topicToKey } = require('../src/utils') +} from './utils.js' +import { Record } from 'libp2p-record' +import { keyToTopic, topicToKey } from '../src/utils.js' /** * @typedef {import('libp2p-interfaces/src/pubsub')} PubSub @@ -117,7 +112,7 @@ describe('datastore-pubsub', function () { }) it('should subscribe the topic, but receive error as no entry is stored locally', async () => { - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) const subsTopic = keyToTopic(`/${keyRef}`) let subscribers = await pubsubA.getTopics() @@ -135,8 +130,8 @@ describe('datastore-pubsub', function () { }) it('should put correctly to node A and node B should not receive it without subscribing', async () => { - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) - const dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, smoothValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, smoothValidator) const subsTopic = keyToTopic(`/${keyRef}`) const res = await pubsubB.getTopics() @@ -169,8 +164,8 @@ describe('datastore-pubsub', function () { return 0 } } - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) - const dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, customValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, customValidator) const subsTopic = keyToTopic(`/${keyRef}`) let receivedMessage = false @@ -199,8 +194,8 @@ describe('datastore-pubsub', function () { }) it('should put correctly to daemon A and daemon B should receive it as it tried to get it first and subscribed it', async () => { - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) - const dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, smoothValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, smoothValidator) const subsTopic = keyToTopic(`/${keyRef}`) let receivedMessage = false @@ -237,19 +232,19 @@ describe('datastore-pubsub', function () { expect(receivedRecord.value.toString()).to.equal(value) }) - it('should fail to create the DatastorePubsub if no validator is provided', () => { + it('should fail to create the PubsubDatastore if no validator is provided', () => { let dsPubsubB try { // @ts-expect-error no validator provided - dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB) - } catch (err) { + dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB) + } catch (/** @type {any} */ err) { expect(err.code).to.equal('ERR_INVALID_PARAMETERS') } expect(dsPubsubB).to.equal(undefined) }) - it('should fail to create the DatastorePubsub if no validate function is provided', () => { + it('should fail to create the PubsubDatastore if no validate function is provided', () => { const customValidator = { validate: undefined, select: () => { @@ -260,15 +255,15 @@ describe('datastore-pubsub', function () { let dsPubsubB try { // @ts-expect-error invalid validator provided - dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, customValidator) - } catch (err) { + dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, customValidator) + } catch (/** @type {any} */ err) { expect(err.code).to.equal('ERR_INVALID_PARAMETERS') } expect(dsPubsubB).to.equal(undefined) }) - it('should fail to create the DatastorePubsub if no select function is provided', () => { + it('should fail to create the PubsubDatastore if no select function is provided', () => { const customValidator = { validate: () => { return true @@ -279,8 +274,8 @@ describe('datastore-pubsub', function () { let dsPubsubB try { // @ts-expect-error invalid validator provided - dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, customValidator) - } catch (err) { + dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, customValidator) + } catch (/** @type {any} */ err) { expect(err.code).to.equal('ERR_INVALID_PARAMETERS') } @@ -296,8 +291,8 @@ describe('datastore-pubsub', function () { return 0 } } - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) - const dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, customValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, customValidator) const subsTopic = keyToTopic(`/${keyRef}`) let receivedMessage = false @@ -326,7 +321,7 @@ describe('datastore-pubsub', function () { // get from datastore await dsPubsubB.get(key) expect.fail('Should have disguarded invalid message') - } catch (err) { + } catch (/** @type {any} */ err) { // No record received, in spite of message received expect(err.code).to.equal('ERR_NOT_FOUND') } @@ -346,8 +341,8 @@ describe('datastore-pubsub', function () { const record = new Record(key, uint8ArrayFromString(newValue)) const newSerializedRecord = record.serialize() - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) - const dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, customValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, customValidator) const subsTopic = keyToTopic(`/${keyRef}`) let receivedMessage = false @@ -397,8 +392,8 @@ describe('datastore-pubsub', function () { const record = new Record(key, uint8ArrayFromString(newValue)) const newSerializedRecord = record.serialize() - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) - const dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, customValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, customValidator) const subsTopic = keyToTopic(`/${keyRef}`) let receivedMessage = false @@ -448,8 +443,8 @@ describe('datastore-pubsub', function () { expect(uint8ArrayToString(key)).to.equal(`/${keyRef}`) throw new Error('DISCARD MESSAGE') } - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) - const dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, smoothValidator, subscriptionKeyFn) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, smoothValidator, subscriptionKeyFn) const subsTopic = keyToTopic(`/${keyRef}`) let receivedMessage = false @@ -481,7 +476,7 @@ describe('datastore-pubsub', function () { try { await dsPubsubB.get(key) expect.fail('Should not have stored message') - } catch (err) { + } catch (/** @type {any} */ err) { // As message was discarded, it was not stored in the datastore expect(err.code).to.equal('ERR_NOT_FOUND') } @@ -493,8 +488,8 @@ describe('datastore-pubsub', function () { expect(uint8ArrayToString(key)).to.equal(`/${keyRef}`) return Promise.resolve(topicToKey(`${keyToTopic(key)}new`)) } - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) - const dsPubsubB = new DatastorePubsub(pubsubB, datastoreB, peerIdB, smoothValidator, subscriptionKeyFn) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubB = new PubsubDatastore(pubsubB, datastoreB, peerIdB, smoothValidator, subscriptionKeyFn) const subsTopic = keyToTopic(`/${keyRef}`) const keyNew = topicToKey(`${keyToTopic(key)}new`) let receivedMessage = false @@ -530,7 +525,7 @@ describe('datastore-pubsub', function () { }) it('should subscribe a topic only once', async () => { - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) sinon.spy(pubsubA, 'subscribe') @@ -553,7 +548,7 @@ describe('datastore-pubsub', function () { }) it('should handle a unexpected error properly when getting from the datastore', async () => { - const dsPubsubA = new DatastorePubsub(pubsubA, datastoreA, peerIdA, smoothValidator) + const dsPubsubA = new PubsubDatastore(pubsubA, datastoreA, peerIdA, smoothValidator) const stub = sinon.stub(dsPubsubA._datastore, 'get').throws(errcode(new Error('Wut'), 'RANDOM_ERR')) // causes pubsub b to become subscribed to the topic diff --git a/test/utils.js b/test/utils.js index e5faf6d..f772033 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,14 +1,11 @@ -'use strict' -const PeerId = require('peer-id') +import PeerId from 'peer-id' // @ts-ignore -const DuplexPair = require('it-pair/duplex') - -const Gossipsub = require('libp2p-gossipsub') +import DuplexPair from 'it-pair/duplex.js' +import pWaitFor from 'p-wait-for' +import Gossipsub from 'libp2p-gossipsub' const { multicodec } = Gossipsub -const pWaitFor = require('p-wait-for') - /** * @typedef {import('libp2p-interfaces/src/pubsub')} Pubsub */ @@ -51,7 +48,7 @@ const createMockRegistrar = (registrarRecord) => { * * @param {object} registrarRecord */ -exports.createPubsubNode = async (registrarRecord) => { +export const createPubsubNode = async (registrarRecord) => { const peerId = await PeerId.create({ bits: 1024 }) const libp2p = { @@ -93,7 +90,7 @@ const ConnectionPair = () => { * @param {Connectable} pubsubA * @param {Connectable} pubsubB */ -exports.connectPubsubNodes = async (pubsubA, pubsubB) => { +export const connectPubsubNodes = async (pubsubA, pubsubB) => { const onConnectA = pubsubA.registrar[multicodec].onConnect const onConnectB = pubsubB.registrar[multicodec].onConnect const handleA = pubsubA.registrar[multicodec].handler @@ -126,7 +123,7 @@ exports.connectPubsubNodes = async (pubsubA, pubsubB) => { * * @param {() => boolean} predicate */ -exports.waitFor = predicate => pWaitFor(predicate, { interval: 1000, timeout: 10000 }) +export const waitFor = predicate => pWaitFor(predicate, { interval: 1000, timeout: 10000 }) /** * Wait until a peer subscribes a topic @@ -135,7 +132,7 @@ exports.waitFor = predicate => pWaitFor(predicate, { interval: 1000, timeout: 10 * @param {PeerId} peer * @param {Pubsub} node */ -exports.waitForPeerToSubscribe = (topic, peer, node) => { +export const waitForPeerToSubscribe = (topic, peer, node) => { return pWaitFor(async () => { const peers = await node.getSubscribers(topic) diff --git a/tsconfig.json b/tsconfig.json index 13a3599..376ad55 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "outDir": "dist" + "outDir": "types" }, "include": [ "src",