diff --git a/.aegir.js b/.aegir.js index ab0e020c8c..9b5aefa98e 100644 --- a/.aegir.js +++ b/.aegir.js @@ -71,6 +71,7 @@ const after = (done) => { } module.exports = { + bundlesize: { maxSize: '215kB' }, hooks: { pre: before, post: after diff --git a/.travis.yml b/.travis.yml index 461d2aefc7..4b71d1a28d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ node_js: os: - linux - osx + script: npx nyc -s npm run test:node -- --bail after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov @@ -18,6 +19,7 @@ jobs: include: - stage: check script: + - npx aegir build --bundlesize - npx aegir commitlint --travis - npx aegir dep-check -- -i wrtc -i electron-webrtc - npm run lint diff --git a/README.md b/README.md index 2d07558882..afdcc98478 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,9 @@

- - + + +
diff --git a/package.json b/package.json index d538271d75..e60cbbcf9c 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ }, "homepage": "https://github.com/libp2p/js-libp2p", "browser": { - "joi": "joi-browser", "./test/utils/bundle-nodejs": "./test/utils/bundle-browser" }, "dependencies": { @@ -43,23 +42,23 @@ "debug": "^4.1.0", "err-code": "^1.1.2", "fsm-event": "^2.1.0", - "joi": "^14.0.6", - "joi-browser": "^13.4.0", + "kind-of": "^6.0.2", "libp2p-connection-manager": "~0.0.2", - "libp2p-floodsub": "~0.15.1", - "libp2p-ping": "~0.8.3", - "libp2p-switch": "~0.41.3", - "libp2p-websockets": "~0.12.0", - "mafmt": "^6.0.2", - "multiaddr": "^6.0.2", + "libp2p-floodsub": "~0.15.7", + "libp2p-ping": "~0.8.5", + "libp2p-switch": "~0.41.5", + "libp2p-websockets": "~0.12.1", + "mafmt": "^6.0.4", + "multiaddr": "^6.0.3", "once": "^1.4.0", - "peer-book": "~0.9.0", - "peer-id": "~0.12.0", - "peer-info": "~0.15.0" + "peer-book": "~0.9.1", + "peer-id": "~0.12.2", + "peer-info": "~0.15.1", + "superstruct": "~0.6.0" }, "devDependencies": { "@nodeutils/defaults-deep": "^1.1.0", - "aegir": "^18.1.0", + "aegir": "^18.2.0", "chai": "^4.2.0", "chai-checkmark": "^1.0.1", "cids": "~0.5.5", @@ -85,7 +84,7 @@ "pull-mplex": "~0.1.0", "pull-serializer": "~0.3.2", "pull-stream": "^3.6.9", - "sinon": "^7.1.1", + "sinon": "^7.2.4", "wrtc": "~0.3.2" }, "contributors": [ diff --git a/src/config.js b/src/config.js index aa38063b0f..0962784075 100644 --- a/src/config.js +++ b/src/config.js @@ -1,60 +1,89 @@ 'use strict' -const Joi = require('joi') +const { struct, superstruct } = require('superstruct') +const kind = require('kind-of') +const { optional, list } = struct -const ModuleSchema = Joi.alternatives().try(Joi.func(), Joi.object()) +const transports = ['tcp', 'utp', 'webrtcstar', 'webrtcdirect', 'websockets', 'websocketstar'] +// Define custom types +const s = superstruct({ + types: { + tcp: v => kind(v) === 'tcp', + utp: v => kind(v) === 'utp', + webrtcstar: v => kind(v) === 'webrtcstar', + webrtcdirect: v => kind(v) === 'webrtcdirect', + websockets: v => kind(v) === 'websockets', + websocketstar: v => kind(v) === 'websocketstar', + transport: value => { + const [error] = list([s.union([ ...transports, 'function' ])]).validate(value) + if (error) return error.message -const OptionsSchema = Joi.object({ - // TODO: create proper validators for the generics - connectionManager: Joi.object(), - datastore: Joi.object(), - peerInfo: Joi.object().required(), - peerBook: Joi.object(), - modules: Joi.object().keys({ - connEncryption: Joi.array().items(ModuleSchema).allow(null), - connProtector: Joi.object().keys({ - protect: Joi.func().required() - }).unknown(), - contentRouting: Joi.array().items(Joi.object()).allow(null), - dht: ModuleSchema.allow(null), - peerDiscovery: Joi.array().items(ModuleSchema).allow(null), - peerRouting: Joi.array().items(Joi.object()).allow(null), - streamMuxer: Joi.array().items(ModuleSchema).allow(null), - transport: Joi.array().items(ModuleSchema).min(1).required() - }).required(), - config: Joi.object().keys({ - peerDiscovery: Joi.object().allow(null), - relay: Joi.object().keys({ - enabled: Joi.boolean().default(true), - hop: Joi.object().keys({ - enabled: Joi.boolean().default(false), - active: Joi.boolean().default(false) - }) - }).default(), - dht: Joi.object().keys({ - kBucketSize: Joi.number().default(20), - enabled: Joi.boolean().default(true), - randomWalk: Joi.object().keys({ - enabled: Joi.boolean().default(true), - queriesPerPeriod: Joi.number().default(1), - interval: Joi.number().default(30000), - timeout: Joi.number().default(10000) - }).default(), - validators: Joi.object().allow(null), - selectors: Joi.object().allow(null) - }).default(), - EXPERIMENTAL: Joi.object().keys({ - pubsub: Joi.boolean().default(false) - }).default() - }).default() + return value.length > 0 + ? true + : 'You need to provide at least one transport.' + } + } }) -module.exports.validate = (options) => { - options = Joi.attempt(options, OptionsSchema) +const optionsSchema = s( + { + connectionManager: 'object?', + datastore: 'object?', + peerInfo: 'object', + peerBook: 'object?', + modules: s({ + connEncryption: optional(list([s('object|function')])), + // this is hacky to simulate optional because interface doesnt work correctly with it + // change to optional when fixed upstream + connProtector: s.union(['undefined', s.interface({ protect: 'function' })]), + contentRouting: optional(list(['object'])), + dht: optional(s('null|function|object')), + peerDiscovery: optional(list([s('object|function')])), + peerRouting: optional(list(['object'])), + streamMuxer: optional(list([s('object|function')])), + transport: 'transport' + }), + config: s({ + peerDiscovery: 'object?', + relay: s({ + enabled: 'boolean', + hop: optional(s({ + enabled: 'boolean', + active: 'boolean' + }, + { enabled: false, active: false })) + }, { enabled: true, hop: {} }), + dht: s({ + kBucketSize: 'number', + enabled: 'boolean?', + randomWalk: optional(s({ + enabled: 'boolean?', + queriesPerPeriod: 'number?', + interval: 'number?', + timeout: 'number?' + }, { enabled: true, queriesPerPeriod: 1, interval: 30000, timeout: 10000 })), + validators: 'object?', + selectors: 'object?' + }, { enabled: true, kBucketSize: 20, enabledDiscovery: true }), + EXPERIMENTAL: s({ + pubsub: 'boolean' + }, { pubsub: false }) + }, { relay: {}, dht: {}, EXPERIMENTAL: {} }) + }, + { config: {}, modules: {} } +) + +module.exports.validate = (opts) => { + const [error, options] = optionsSchema.validate(opts) - // Ensure dht is correct - if (options.config.dht.enabled) { - Joi.assert(options.modules.dht, ModuleSchema.required()) + // Improve errors throwed, reduce stack by throwing here and add reason to the message + if (error) { + throw new Error(`${error.message}${error.reason ? ' - ' + error.reason : ''}`) + } else { + // Throw when dht is enabled but no dht module provided + if (options.config.dht.enabled) { + s('function|object')(options.modules.dht) + } } return options diff --git a/test/config.spec.js b/test/config.spec.js index b8c00f0881..0d429f8edd 100644 --- a/test/config.spec.js +++ b/test/config.spec.js @@ -104,7 +104,11 @@ describe('configuration', () => { } }, relay: { - enabled: true + enabled: true, + hop: { + active: false, + enabled: false + } } } } @@ -185,7 +189,11 @@ describe('configuration', () => { pubsub: false }, relay: { - enabled: true + enabled: true, + hop: { + active: false, + enabled: false + } }, dht: { kBucketSize: 20,