mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-06-14 17:51:22 +00:00
fix: remove node global (#587)
* fix: use new libp2p-crypto
* fix: remove node globals and reduce size
- adds buffer require
- adds globalThis where needed
- streaming-iterables was remove and new utils created, this will be consolidated in `ipfs-utils` and backported here to normalize all these iterator helper functions
- latency-monitor was copied inside the repo
- iso-random-stream is now used instead of node crypto
- the test `should ignore self on discovery` was moved to node only
Size: 172.97KB
47.03KB below the 220KB limit.
`aegir build --node false` and `aegir test -t browser --node false` now work 🎉
* fix: fix require path
* fix: feedback
* fix: update deps and bundle size
* chore: bump interfaces
* chore: update size
This commit is contained in:
@ -45,7 +45,7 @@ const after = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
bundlesize: { maxSize: '220kB' },
|
bundlesize: { maxSize: '179kB' },
|
||||||
hooks: {
|
hooks: {
|
||||||
pre: before,
|
pre: before,
|
||||||
post: after
|
post: after
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* eslint no-console: ["off"] */
|
/* eslint no-console: ["off"] */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const { generate } = require('libp2p/src/pnet')
|
const { generate } = require('libp2p/src/pnet')
|
||||||
const privateLibp2pNode = require('./libp2p-node')
|
const privateLibp2pNode = require('./libp2p-node')
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ generate(otherSwarmKey)
|
|||||||
;(async () => {
|
;(async () => {
|
||||||
const node1 = await privateLibp2pNode(swarmKey)
|
const node1 = await privateLibp2pNode(swarmKey)
|
||||||
|
|
||||||
// TASK: switch the commented out line below so we're using a different key, to see the nodes fail to connect
|
// TASK: switch the commented out line below so we're using a different key, to see the nodes fail to connect
|
||||||
const node2 = await privateLibp2pNode(swarmKey)
|
const node2 = await privateLibp2pNode(swarmKey)
|
||||||
// const node2 = await privateLibp2pNode(otherSwarmKey)
|
// const node2 = await privateLibp2pNode(otherSwarmKey)
|
||||||
|
|
||||||
@ -47,4 +48,4 @@ generate(otherSwarmKey)
|
|||||||
['This message is sent on a private network'],
|
['This message is sent on a private network'],
|
||||||
stream
|
stream
|
||||||
)
|
)
|
||||||
})();
|
})()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const Libp2p = require('../../')
|
const Libp2p = require('../../')
|
||||||
const TCP = require('libp2p-tcp')
|
const TCP = require('libp2p-tcp')
|
||||||
const Mplex = require('libp2p-mplex')
|
const Mplex = require('libp2p-mplex')
|
||||||
@ -31,7 +32,7 @@ const createNode = async () => {
|
|||||||
|
|
||||||
const [node1, node2] = await Promise.all([
|
const [node1, node2] = await Promise.all([
|
||||||
createNode(),
|
createNode(),
|
||||||
createNode(),
|
createNode()
|
||||||
])
|
])
|
||||||
|
|
||||||
await node1.dial(node2.peerInfo)
|
await node1.dial(node2.peerInfo)
|
||||||
@ -48,4 +49,4 @@ const createNode = async () => {
|
|||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
|
node2.pubsub.publish(topic, Buffer.from('Bird bird bird, bird is the word!'))
|
||||||
}, 1000)
|
}, 1000)
|
||||||
})();
|
})()
|
||||||
|
35
package.json
35
package.json
@ -48,37 +48,39 @@
|
|||||||
"class-is": "^1.1.0",
|
"class-is": "^1.1.0",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
"err-code": "^2.0.0",
|
"err-code": "^2.0.0",
|
||||||
|
"events": "^3.1.0",
|
||||||
"hashlru": "^2.3.0",
|
"hashlru": "^2.3.0",
|
||||||
|
"ipfs-utils": "^2.2.0",
|
||||||
"it-all": "^1.0.1",
|
"it-all": "^1.0.1",
|
||||||
"it-buffer": "^0.1.1",
|
"it-buffer": "^0.1.2",
|
||||||
"it-handshake": "^1.0.1",
|
"it-handshake": "^1.0.1",
|
||||||
"it-length-prefixed": "^3.0.0",
|
"it-length-prefixed": "^3.0.1",
|
||||||
"it-pipe": "^1.1.0",
|
"it-pipe": "^1.1.0",
|
||||||
"it-protocol-buffers": "^0.2.0",
|
"it-protocol-buffers": "^0.2.0",
|
||||||
"latency-monitor": "~0.2.1",
|
"libp2p-crypto": "^0.17.6",
|
||||||
"libp2p-crypto": "^0.17.1",
|
"libp2p-interfaces": "^0.2.8",
|
||||||
"libp2p-interfaces": "^0.2.3",
|
|
||||||
"libp2p-utils": "^0.1.2",
|
"libp2p-utils": "^0.1.2",
|
||||||
"mafmt": "^7.0.0",
|
"mafmt": "^7.0.0",
|
||||||
"merge-options": "^2.0.0",
|
"merge-options": "^2.0.0",
|
||||||
"moving-average": "^1.0.0",
|
"moving-average": "^1.0.0",
|
||||||
"multiaddr": "^7.2.1",
|
"multiaddr": "^7.4.3",
|
||||||
"multistream-select": "^0.15.0",
|
"multistream-select": "^0.15.0",
|
||||||
"mutable-proxy": "^1.0.0",
|
"mutable-proxy": "^1.0.0",
|
||||||
"p-any": "^3.0.0",
|
"p-any": "^3.0.0",
|
||||||
"p-fifo": "^1.0.0",
|
"p-fifo": "^1.0.0",
|
||||||
"p-settle": "^4.0.0",
|
"p-settle": "^4.0.1",
|
||||||
"peer-id": "^0.13.4",
|
"peer-id": "^0.13.11",
|
||||||
"peer-info": "^0.17.0",
|
"peer-info": "^0.17.0",
|
||||||
"protons": "^1.0.1",
|
"protons": "^1.0.1",
|
||||||
"retimer": "^2.0.0",
|
"retimer": "^2.0.0",
|
||||||
|
"streaming-iterables": "^4.1.0",
|
||||||
"timeout-abort-controller": "^1.0.0",
|
"timeout-abort-controller": "^1.0.0",
|
||||||
"xsalsa20": "^1.0.2"
|
"xsalsa20": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nodeutils/defaults-deep": "^1.1.0",
|
"@nodeutils/defaults-deep": "^1.1.0",
|
||||||
"abortable-iterator": "^3.0.0",
|
"abortable-iterator": "^3.0.0",
|
||||||
"aegir": "^21.3.0",
|
"aegir": "^21.9.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"cids": "^0.8.0",
|
"cids": "^0.8.0",
|
||||||
@ -92,20 +94,19 @@
|
|||||||
"libp2p-delegated-content-routing": "^0.4.5",
|
"libp2p-delegated-content-routing": "^0.4.5",
|
||||||
"libp2p-delegated-peer-routing": "^0.4.3",
|
"libp2p-delegated-peer-routing": "^0.4.3",
|
||||||
"libp2p-floodsub": "^0.20.0",
|
"libp2p-floodsub": "^0.20.0",
|
||||||
"libp2p-gossipsub": "^0.2.0",
|
"libp2p-gossipsub": "^0.2.6",
|
||||||
"libp2p-kad-dht": "^0.18.2",
|
"libp2p-kad-dht": "^0.18.6",
|
||||||
"libp2p-mdns": "^0.13.0",
|
"libp2p-mdns": "^0.13.0",
|
||||||
"libp2p-mplex": "^0.9.1",
|
"libp2p-mplex": "^0.9.5",
|
||||||
"libp2p-secio": "^0.12.1",
|
"libp2p-secio": "^0.12.4",
|
||||||
"libp2p-tcp": "^0.14.1",
|
"libp2p-tcp": "^0.14.1",
|
||||||
"libp2p-webrtc-star": "^0.17.0",
|
"libp2p-webrtc-star": "^0.17.9",
|
||||||
"libp2p-websockets": "^0.13.1",
|
"libp2p-websockets": "^0.13.1",
|
||||||
"nock": "^12.0.0",
|
"nock": "^12.0.3",
|
||||||
"p-defer": "^3.0.0",
|
"p-defer": "^3.0.0",
|
||||||
"p-times": "^2.1.0",
|
"p-times": "^2.1.0",
|
||||||
"p-wait-for": "^3.1.0",
|
"p-wait-for": "^3.1.0",
|
||||||
"sinon": "^9.0.0",
|
"sinon": "^9.0.2",
|
||||||
"streaming-iterables": "^4.1.0",
|
|
||||||
"wrtc": "^0.4.1"
|
"wrtc": "^0.4.1"
|
||||||
},
|
},
|
||||||
"contributors": [
|
"contributors": [
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const errcode = require('err-code')
|
const errcode = require('err-code')
|
||||||
const mergeOptions = require('merge-options')
|
const mergeOptions = require('merge-options')
|
||||||
const LatencyMonitor = require('latency-monitor').default
|
const LatencyMonitor = require('./latency-monitor')
|
||||||
const debug = require('debug')('libp2p:connection-manager')
|
const debug = require('debug')('libp2p:connection-manager')
|
||||||
const retimer = require('retimer')
|
const retimer = require('retimer')
|
||||||
|
|
||||||
|
246
src/connection-manager/latency-monitor.js
Normal file
246
src/connection-manager/latency-monitor.js
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This code is based on `latency-monitor` (https://github.com/mlucool/latency-monitor) by `mlucool` (https://github.com/mlucool), available under Apache License 2.0 (https://github.com/mlucool/latency-monitor/blob/master/LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global window */
|
||||||
|
const globalThis = require('ipfs-utils/src/globalthis')
|
||||||
|
const EventEmitter = require('events')
|
||||||
|
const VisibilityChangeEmitter = require('./visibility-change-emitter')
|
||||||
|
const debug = require('debug')('latency-monitor:LatencyMonitor')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SummaryObject
|
||||||
|
* @property {Number} events How many events were called
|
||||||
|
* @property {Number} minMS What was the min time for a cb to be called
|
||||||
|
* @property {Number} maxMS What was the max time for a cb to be called
|
||||||
|
* @property {Number} avgMs What was the average time for a cb to be called
|
||||||
|
* @property {Number} lengthMs How long this interval was in ms
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to monitor latency of any async function which works in a browser or node. This works by periodically calling
|
||||||
|
* the asyncTestFn and timing how long it takes the callback to be called. It can also periodically emit stats about this.
|
||||||
|
* This can be disabled and stats can be pulled via setting dataEmitIntervalMs = 0.
|
||||||
|
*
|
||||||
|
* The default implementation is an event loop latency monitor. This works by firing periodic events into the event loop
|
||||||
|
* and timing how long it takes to get back.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const monitor = new LatencyMonitor();
|
||||||
|
* monitor.on('data', (summary) => console.log('Event Loop Latency: %O', summary));
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const monitor = new LatencyMonitor({latencyCheckIntervalMs: 1000, dataEmitIntervalMs: 60000, asyncTestFn:ping});
|
||||||
|
* monitor.on('data', (summary) => console.log('Ping Pong Latency: %O', summary));
|
||||||
|
*/
|
||||||
|
class LatencyMonitor extends EventEmitter {
|
||||||
|
/**
|
||||||
|
* @param {Number} [latencyCheckIntervalMs=500] How often to add a latency check event (ms)
|
||||||
|
* @param {Number} [dataEmitIntervalMs=5000] How often to summarize latency check events. null or 0 disables event firing
|
||||||
|
* @param {function} [asyncTestFn] What cb-style async function to use
|
||||||
|
* @param {Number} [latencyRandomPercentage=5] What percent (+/-) of latencyCheckIntervalMs should we randomly use? This helps avoid alignment to other events.
|
||||||
|
*/
|
||||||
|
constructor ({ latencyCheckIntervalMs, dataEmitIntervalMs, asyncTestFn, latencyRandomPercentage } = {}) {
|
||||||
|
super()
|
||||||
|
const that = this
|
||||||
|
|
||||||
|
// 0 isn't valid here, so its ok to use ||
|
||||||
|
that.latencyCheckIntervalMs = latencyCheckIntervalMs || 500 // 0.5s
|
||||||
|
that.latencyRandomPercentage = latencyRandomPercentage || 10
|
||||||
|
that._latecyCheckMultiply = 2 * (that.latencyRandomPercentage / 100.0) * that.latencyCheckIntervalMs
|
||||||
|
that._latecyCheckSubtract = that._latecyCheckMultiply / 2
|
||||||
|
|
||||||
|
that.dataEmitIntervalMs = (dataEmitIntervalMs === null || dataEmitIntervalMs === 0) ? undefined
|
||||||
|
: dataEmitIntervalMs || 5 * 1000 // 5s
|
||||||
|
debug('latencyCheckIntervalMs: %s dataEmitIntervalMs: %s',
|
||||||
|
that.latencyCheckIntervalMs, that.dataEmitIntervalMs)
|
||||||
|
if (that.dataEmitIntervalMs) {
|
||||||
|
debug('Expecting ~%s events per summary', that.latencyCheckIntervalMs / that.dataEmitIntervalMs)
|
||||||
|
} else {
|
||||||
|
debug('Not emitting summaries')
|
||||||
|
}
|
||||||
|
|
||||||
|
that.asyncTestFn = asyncTestFn // If there is no asyncFn, we measure latency
|
||||||
|
|
||||||
|
// If process: use high resolution timer
|
||||||
|
if (globalThis.process && globalThis.process.hrtime) {
|
||||||
|
debug('Using process.hrtime for timing')
|
||||||
|
that.now = globalThis.process.hrtime
|
||||||
|
that.getDeltaMS = (startTime) => {
|
||||||
|
const hrtime = that.now(startTime)
|
||||||
|
return (hrtime[0] * 1000) + (hrtime[1] / 1000000)
|
||||||
|
}
|
||||||
|
// Let's try for a timer that only monotonically increases
|
||||||
|
} else if (typeof window !== 'undefined' && window.performance && window.performance.now) {
|
||||||
|
debug('Using performance.now for timing')
|
||||||
|
that.now = window.performance.now.bind(window.performance)
|
||||||
|
that.getDeltaMS = (startTime) => Math.round(that.now() - startTime)
|
||||||
|
} else {
|
||||||
|
debug('Using Date.now for timing')
|
||||||
|
that.now = Date.now
|
||||||
|
that.getDeltaMS = (startTime) => that.now() - startTime
|
||||||
|
}
|
||||||
|
|
||||||
|
that._latencyData = that._initLatencyData()
|
||||||
|
|
||||||
|
// We check for isBrowser because of browsers set max rates of timeouts when a page is hidden,
|
||||||
|
// so we fall back to another library
|
||||||
|
// See: http://stackoverflow.com/questions/6032429/chrome-timeouts-interval-suspended-in-background-tabs
|
||||||
|
if (isBrowser()) {
|
||||||
|
that._visibilityChangeEmitter = new VisibilityChangeEmitter()
|
||||||
|
that._visibilityChangeEmitter.on('visibilityChange', (pageInFocus) => {
|
||||||
|
if (pageInFocus) {
|
||||||
|
that._startTimers()
|
||||||
|
} else {
|
||||||
|
that._emitSummary()
|
||||||
|
that._stopTimers()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!that._visibilityChangeEmitter || that._visibilityChangeEmitter.isVisible()) {
|
||||||
|
that._startTimers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start internal timers
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_startTimers () {
|
||||||
|
// Timer already started, ignore this
|
||||||
|
if (this._checkLatencyID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._checkLatency()
|
||||||
|
if (this.dataEmitIntervalMs) {
|
||||||
|
this._emitIntervalID = setInterval(() => this._emitSummary(), this.dataEmitIntervalMs)
|
||||||
|
if (typeof this._emitIntervalID.unref === 'function') {
|
||||||
|
this._emitIntervalID.unref() // Doesn't block exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop internal timers
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_stopTimers () {
|
||||||
|
if (this._checkLatencyID) {
|
||||||
|
clearTimeout(this._checkLatencyID)
|
||||||
|
this._checkLatencyID = undefined
|
||||||
|
}
|
||||||
|
if (this._emitIntervalID) {
|
||||||
|
clearInterval(this._emitIntervalID)
|
||||||
|
this._emitIntervalID = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit summary only if there were events. It might not have any events if it was forced via a page hidden/show
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_emitSummary () {
|
||||||
|
const summary = this.getSummary()
|
||||||
|
if (summary.events > 0) {
|
||||||
|
this.emit('data', summary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calling this function will end the collection period. If a timing event was already fired and somewhere in the queue,
|
||||||
|
* it will not count for this time period
|
||||||
|
* @returns {SummaryObject}
|
||||||
|
*/
|
||||||
|
getSummary () {
|
||||||
|
// We might want to adjust for the number of expected events
|
||||||
|
// Example: first 1 event it comes back, then such a long blocker that the next emit check comes
|
||||||
|
// Then this fires - looks like no latency!!
|
||||||
|
const latency = {
|
||||||
|
events: this._latencyData.events,
|
||||||
|
minMs: this._latencyData.minMs,
|
||||||
|
maxMs: this._latencyData.maxMs,
|
||||||
|
avgMs: this._latencyData.events ? this._latencyData.totalMs / this._latencyData.events
|
||||||
|
: Number.POSITIVE_INFINITY,
|
||||||
|
lengthMs: this.getDeltaMS(this._latencyData.startTime)
|
||||||
|
}
|
||||||
|
this._latencyData = this._initLatencyData() // Clear
|
||||||
|
|
||||||
|
debug('Summary: %O', latency)
|
||||||
|
return latency
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Randomly calls an async fn every roughly latencyCheckIntervalMs (plus some randomness). If no async fn is found,
|
||||||
|
* it will simply report on event loop latency.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_checkLatency () {
|
||||||
|
const that = this
|
||||||
|
// Randomness is needed to avoid alignment by accident to regular things in the event loop
|
||||||
|
const randomness = (Math.random() * that._latecyCheckMultiply) - that._latecyCheckSubtract
|
||||||
|
|
||||||
|
// We use this to ensure that in case some overlap somehow, we don't take the wrong startTime/offset
|
||||||
|
const localData = {
|
||||||
|
deltaOffset: Math.ceil(that.latencyCheckIntervalMs + randomness),
|
||||||
|
startTime: that.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
const cb = () => {
|
||||||
|
// We are already stopped, ignore this datapoint
|
||||||
|
if (!this._checkLatencyID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const deltaMS = that.getDeltaMS(localData.startTime) - localData.deltaOffset
|
||||||
|
that._checkLatency() // Start again ASAP
|
||||||
|
|
||||||
|
// Add the data point. If this gets complex, refactor it
|
||||||
|
that._latencyData.events++
|
||||||
|
that._latencyData.minMs = Math.min(that._latencyData.minMs, deltaMS)
|
||||||
|
that._latencyData.maxMs = Math.max(that._latencyData.maxMs, deltaMS)
|
||||||
|
that._latencyData.totalMs += deltaMS
|
||||||
|
debug('MS: %s Data: %O', deltaMS, that._latencyData)
|
||||||
|
}
|
||||||
|
debug('localData: %O', localData)
|
||||||
|
|
||||||
|
this._checkLatencyID = setTimeout(() => {
|
||||||
|
// This gets rid of including event loop
|
||||||
|
if (that.asyncTestFn) {
|
||||||
|
// Clear timing related things
|
||||||
|
localData.deltaOffset = 0
|
||||||
|
localData.startTime = that.now()
|
||||||
|
that.asyncTestFn(cb)
|
||||||
|
} else {
|
||||||
|
// setTimeout is not more accurate than 1ms, so this will ensure positive numbers. Add 1 to emitted data to remove.
|
||||||
|
// This is not the best, but for now it'll be just fine. This isn't meant to be sub ms accurate.
|
||||||
|
localData.deltaOffset -= 1
|
||||||
|
// If there is no function to test, we mean check latency which is a special case that is really cb => cb()
|
||||||
|
// We avoid that for the few extra function all overheads. Also, we want to keep the timers different
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
}, localData.deltaOffset)
|
||||||
|
|
||||||
|
if (typeof this._checkLatencyID.unref === 'function') {
|
||||||
|
this._checkLatencyID.unref() // Doesn't block exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_initLatencyData () {
|
||||||
|
return {
|
||||||
|
startTime: this.now(),
|
||||||
|
minMs: Number.POSITIVE_INFINITY,
|
||||||
|
maxMs: Number.NEGATIVE_INFINITY,
|
||||||
|
events: 0,
|
||||||
|
totalMs: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBrowser () {
|
||||||
|
return typeof window !== 'undefined'
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = LatencyMonitor
|
118
src/connection-manager/visibility-change-emitter.js
Normal file
118
src/connection-manager/visibility-change-emitter.js
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/* global document */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This code is based on `latency-monitor` (https://github.com/mlucool/latency-monitor) by `mlucool` (https://github.com/mlucool), available under Apache License 2.0 (https://github.com/mlucool/latency-monitor/blob/master/LICENSE)
|
||||||
|
*/
|
||||||
|
'use strict'
|
||||||
|
const EventEmitter = require('events')
|
||||||
|
|
||||||
|
const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to page visibility change events (i.e. when the page is focused / blurred) by an event emitter.
|
||||||
|
*
|
||||||
|
* Warning: This does not work on all browsers, but should work on all modern browsers
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* const myVisibilityEmitter = new VisibilityChangeEmitter();
|
||||||
|
*
|
||||||
|
* myVisibilityEmitter.on('visibilityChange', (pageInFocus) => {
|
||||||
|
* if ( pageInFocus ){
|
||||||
|
* // Page is in focus
|
||||||
|
* console.log('In focus');
|
||||||
|
* }
|
||||||
|
* else {
|
||||||
|
* // Page is blurred
|
||||||
|
* console.log('Out of focus');
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* // To access the visibility state directly, call:
|
||||||
|
* console.log('Am I focused now? ' + myVisibilityEmitter.isVisible());
|
||||||
|
*
|
||||||
|
* @class VisibilityChangeEmitter
|
||||||
|
*/
|
||||||
|
module.exports = class VisibilityChangeEmitter extends EventEmitter {
|
||||||
|
/**
|
||||||
|
* Creates a VisibilityChangeEmitter
|
||||||
|
*/
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
if (typeof document === 'undefined') {
|
||||||
|
debug('This is not a browser, no "document" found. Stopping.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._initializeVisibilityVarNames()
|
||||||
|
this._addVisibilityChangeListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* document.hidden and document.visibilityChange are the two variables we need to check for;
|
||||||
|
* Since these variables are named differently in different browsers, this function sets
|
||||||
|
* the appropriate name based on the browser being used. Once executed, tha actual names of
|
||||||
|
* document.hidden and document.visibilityChange are found in this._hidden and this._visibilityChange
|
||||||
|
* respectively
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_initializeVisibilityVarNames () {
|
||||||
|
let hidden
|
||||||
|
let visibilityChange
|
||||||
|
if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support
|
||||||
|
hidden = 'hidden'
|
||||||
|
visibilityChange = 'visibilitychange'
|
||||||
|
} else if (typeof document.mozHidden !== 'undefined') {
|
||||||
|
hidden = 'mozHidden'
|
||||||
|
visibilityChange = 'mozvisibilitychange'
|
||||||
|
} else if (typeof document.msHidden !== 'undefined') {
|
||||||
|
hidden = 'msHidden'
|
||||||
|
visibilityChange = 'msvisibilitychange'
|
||||||
|
} else if (typeof document.webkitHidden !== 'undefined') {
|
||||||
|
hidden = 'webkitHidden'
|
||||||
|
visibilityChange = 'webkitvisibilitychange'
|
||||||
|
}
|
||||||
|
this._hidden = hidden
|
||||||
|
this._visibilityChange = visibilityChange
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an event listener on the document that listens to changes in document.visibilityChange
|
||||||
|
* (or whatever name by which the visibilityChange variable is known in the browser)
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_addVisibilityChangeListener () {
|
||||||
|
if (typeof document.addEventListener === 'undefined' ||
|
||||||
|
typeof document[this._hidden] === 'undefined') {
|
||||||
|
debug('Checking page visibility requires a browser that supports the Page Visibility API.')
|
||||||
|
} else {
|
||||||
|
// Handle page visibility change
|
||||||
|
document.addEventListener(this._visibilityChange, this._handleVisibilityChange.bind(this), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function returns ```true``` if the page is visible or ```false``` if the page is not visible and
|
||||||
|
* ```undefined``` if the page visibility API is not supported by the browser.
|
||||||
|
* @returns {Boolean|void} whether the page is now visible or not (undefined is unknown)
|
||||||
|
*/
|
||||||
|
isVisible () {
|
||||||
|
if (this._hidden === undefined || document[this._hidden] === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return !document[this._hidden]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function that is called when document.visibilityChange has changed
|
||||||
|
* It emits an event called visibilityChange and sends the value of document.hidden as a
|
||||||
|
* parameter
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_handleVisibilityChange () {
|
||||||
|
const visible = !document[this._hidden]
|
||||||
|
debug(visible ? 'Page Visible' : 'Page Hidden')
|
||||||
|
// Emit the event
|
||||||
|
this.emit('visibilityChange', visible)
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const debug = require('debug')
|
const debug = require('debug')
|
||||||
const pb = require('it-protocol-buffers')
|
const pb = require('it-protocol-buffers')
|
||||||
const lp = require('it-length-prefixed')
|
const lp = require('it-length-prefixed')
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const { EventEmitter } = require('events')
|
const { EventEmitter } = require('events')
|
||||||
const debug = require('debug')
|
const debug = require('debug')
|
||||||
|
const globalThis = require('ipfs-utils/src/globalthis')
|
||||||
const log = debug('libp2p')
|
const log = debug('libp2p')
|
||||||
log.error = debug('libp2p:error')
|
log.error = debug('libp2p:error')
|
||||||
|
|
||||||
@ -142,7 +143,7 @@ class Libp2p extends EventEmitter {
|
|||||||
// Attach private network protector
|
// Attach private network protector
|
||||||
if (this._modules.connProtector) {
|
if (this._modules.connProtector) {
|
||||||
this.upgrader.protector = this._modules.connProtector
|
this.upgrader.protector = this._modules.connProtector
|
||||||
} else if (process.env.LIBP2P_FORCE_PNET) {
|
} else if (globalThis.process !== undefined && globalThis.process.env && globalThis.process.env.LIBP2P_FORCE_PNET) {
|
||||||
throw new Error('Private network is enforced, but no protector was provided')
|
throw new Error('Private network is enforced, but no protector was provided')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const debug = require('debug')
|
const debug = require('debug')
|
||||||
const Errors = require('./errors')
|
const Errors = require('./errors')
|
||||||
const xsalsa20 = require('xsalsa20')
|
const xsalsa20 = require('xsalsa20')
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const crypto = require('crypto')
|
const crypto = require('libp2p-crypto')
|
||||||
const KEY_LENGTH = 32
|
const KEY_LENGTH = 32
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const errCode = require('err-code')
|
const errCode = require('err-code')
|
||||||
const { messages, codes } = require('./errors')
|
const { messages, codes } = require('./errors')
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
/* eslint-env mocha */
|
/* eslint-env mocha */
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
chai.use(require('dirty-chai'))
|
chai.use(require('dirty-chai'))
|
||||||
const { expect } = chai
|
const { expect } = chai
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
/* eslint-env mocha */
|
/* eslint-env mocha */
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
chai.use(require('dirty-chai'))
|
chai.use(require('dirty-chai'))
|
||||||
chai.use(require('chai-as-promised'))
|
chai.use(require('chai-as-promised'))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
/* eslint-env mocha */
|
/* eslint-env mocha */
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
chai.use(require('dirty-chai'))
|
chai.use(require('dirty-chai'))
|
||||||
chai.use(require('chai-as-promised'))
|
chai.use(require('chai-as-promised'))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
/* eslint-env mocha */
|
/* eslint-env mocha */
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
chai.use(require('dirty-chai'))
|
chai.use(require('dirty-chai'))
|
||||||
const { expect } = chai
|
const { expect } = chai
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
chai.use(require('dirty-chai'))
|
chai.use(require('dirty-chai'))
|
||||||
|
const { expect } = chai
|
||||||
|
const sinon = require('sinon')
|
||||||
const defer = require('p-defer')
|
const defer = require('p-defer')
|
||||||
const mergeOptions = require('merge-options')
|
const mergeOptions = require('merge-options')
|
||||||
|
|
||||||
@ -32,6 +33,21 @@ describe('peer discovery scenarios', () => {
|
|||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
libp2p && await libp2p.stop()
|
libp2p && await libp2p.stop()
|
||||||
})
|
})
|
||||||
|
it('should ignore self on discovery', async () => {
|
||||||
|
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
||||||
|
peerInfo,
|
||||||
|
modules: {
|
||||||
|
peerDiscovery: [MulticastDNS]
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
await libp2p.start()
|
||||||
|
const discoverySpy = sinon.spy()
|
||||||
|
libp2p.on('peer:discovery', discoverySpy)
|
||||||
|
libp2p._discovery.get('mdns').emit('peer', libp2p.peerInfo)
|
||||||
|
|
||||||
|
expect(discoverySpy.called).to.eql(false)
|
||||||
|
})
|
||||||
|
|
||||||
it('bootstrap should discover all peers in the list', async () => {
|
it('bootstrap should discover all peers in the list', async () => {
|
||||||
const deferred = defer()
|
const deferred = defer()
|
||||||
|
@ -14,7 +14,6 @@ const WebRTCStar = require('libp2p-webrtc-star')
|
|||||||
const Libp2p = require('../../src')
|
const Libp2p = require('../../src')
|
||||||
const baseOptions = require('../utils/base-options.browser')
|
const baseOptions = require('../utils/base-options.browser')
|
||||||
const { createPeerInfo } = require('../utils/creators/peer')
|
const { createPeerInfo } = require('../utils/creators/peer')
|
||||||
const { EventEmitter } = require('events')
|
|
||||||
|
|
||||||
describe('peer discovery', () => {
|
describe('peer discovery', () => {
|
||||||
describe('basic functions', () => {
|
describe('basic functions', () => {
|
||||||
@ -50,27 +49,6 @@ describe('peer discovery', () => {
|
|||||||
expect(spy.getCall(0).args).to.eql([remotePeerInfo])
|
expect(spy.getCall(0).args).to.eql([remotePeerInfo])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should ignore self on discovery', async () => {
|
|
||||||
const mockDiscovery = new EventEmitter()
|
|
||||||
mockDiscovery.tag = 'mock'
|
|
||||||
mockDiscovery.start = () => {}
|
|
||||||
mockDiscovery.stop = () => {}
|
|
||||||
|
|
||||||
libp2p = new Libp2p(mergeOptions(baseOptions, {
|
|
||||||
peerInfo,
|
|
||||||
modules: {
|
|
||||||
peerDiscovery: [mockDiscovery]
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
await libp2p.start()
|
|
||||||
const discoverySpy = sinon.spy()
|
|
||||||
libp2p.on('peer:discovery', discoverySpy)
|
|
||||||
libp2p._discovery.get('mock').emit('peer', libp2p.peerInfo)
|
|
||||||
|
|
||||||
expect(discoverySpy.called).to.eql(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should stop discovery on libp2p start/stop', async () => {
|
it('should stop discovery on libp2p start/stop', async () => {
|
||||||
const mockDiscovery = {
|
const mockDiscovery = {
|
||||||
tag: 'mock',
|
tag: 'mock',
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* eslint-env mocha */
|
/* eslint-env mocha */
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
const dirtyChai = require('dirty-chai')
|
const dirtyChai = require('dirty-chai')
|
||||||
chai.use(dirtyChai)
|
chai.use(dirtyChai)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
/* eslint-env mocha */
|
/* eslint-env mocha */
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer')
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
chai.use(require('dirty-chai'))
|
chai.use(require('dirty-chai'))
|
||||||
const { expect } = chai
|
const { expect } = chai
|
||||||
|
Reference in New Issue
Block a user