Comparing version 4.0.1 to 5.0.0
@@ -0,1 +1,6 @@ | ||
# 5.0.0 | ||
- Scheduler dropped supports for ssb-friends lower than 5.0 | ||
- Scheduler dropped support for `config.conn.hops` | ||
# 4.0.0 | ||
@@ -2,0 +7,0 @@ |
export declare class ConnScheduler { | ||
private readonly ssb; | ||
private readonly config; | ||
private readonly hasSsbDb2; | ||
private pubDiscoveryPausable?; | ||
private intervalForUpdate?; | ||
private ssbDB2Subscription?; | ||
private closed; | ||
private isLoadingHops; | ||
private loadedSocialGraph; | ||
private lastMessageAt; | ||
private hasScheduledAnUpdate; | ||
private hops; | ||
private socialGraph; | ||
constructor(ssb: any, config: any); | ||
private loadHops; | ||
private loadSocialGraph; | ||
private isCurrentlyDownloading; | ||
@@ -15,0 +15,0 @@ private weBlockThem; |
@@ -93,22 +93,18 @@ "use strict"; | ||
} | ||
const minute = 60e3; | ||
const hour = 60 * 60e3; | ||
const MINUTED = 60e3; | ||
const HOUR = 60 * 60e3; | ||
let ConnScheduler = class ConnScheduler { | ||
constructor(ssb, config) { | ||
var _a, _b; | ||
this.weBlockThem = ([_addr, data]) => { | ||
if (!(data === null || data === void 0 ? void 0 : data.key)) | ||
return false; | ||
return this.hops[data.key] === -1; | ||
return this.socialGraph[data.key] === -1; | ||
}; | ||
this.weFollowThem = ([_addr, data]) => { | ||
var _a, _b; | ||
if (!(data === null || data === void 0 ? void 0 : data.key)) | ||
return false; | ||
const h = this.hops[data.key]; | ||
const maxHops = (_b = (_a = this.config.conn) === null || _a === void 0 ? void 0 : _a.hops) !== null && _b !== void 0 ? _b : 1; | ||
return h > 0 && h <= maxHops; | ||
return this.socialGraph[data.key] > 0; | ||
}; | ||
this.start = () => { | ||
var _a, _b; | ||
var _a, _b, _c; | ||
if (!this.closed) | ||
@@ -129,10 +125,15 @@ return; | ||
} | ||
this.loadHops(); | ||
this.ssbDB2Subscription = (_a = this.ssb.db) === null || _a === void 0 ? void 0 : _a.post((msg) => { | ||
if (msg.value.author !== this.ssb.id) { | ||
this.lastMessageAt = Date.now(); | ||
} | ||
}); | ||
this.loadSocialGraph(); | ||
this.populateWithSeeds(); | ||
this.setupPubDiscovery(); | ||
(_a = this.pubDiscoveryPausable) === null || _a === void 0 ? void 0 : _a.resume(); | ||
(_b = this.pubDiscoveryPausable) === null || _b === void 0 ? void 0 : _b.resume(); | ||
this.setupLanDiscovery(); | ||
this.setupBluetoothDiscovery(); | ||
this.intervalForUpdate = setInterval(() => this.updateSoon(), 2e3); | ||
if ((_b = this.intervalForUpdate) === null || _b === void 0 ? void 0 : _b.unref) | ||
if ((_c = this.intervalForUpdate) === null || _c === void 0 ? void 0 : _c.unref) | ||
this.intervalForUpdate.unref(); | ||
@@ -155,3 +156,4 @@ onWakeup(() => { | ||
(_a = this.pubDiscoveryPausable) === null || _a === void 0 ? void 0 : _a.pause(); | ||
(_c = (_b = this.ssb.lan) === null || _b === void 0 ? void 0 : _b.stop) === null || _c === void 0 ? void 0 : _c.call(_b); | ||
(_b = this.ssb.lan) === null || _b === void 0 ? void 0 : _b.stop(); | ||
(_c = this.ssbDB2Subscription) === null || _c === void 0 ? void 0 : _c.call(this); | ||
if (this.intervalForUpdate) { | ||
@@ -166,37 +168,23 @@ clearInterval(this.intervalForUpdate); | ||
this.config = config; | ||
this.hasSsbDb2 = !!((_a = this.ssb.db) === null || _a === void 0 ? void 0 : _a.post) && !!((_b = this.ssb.db) === null || _b === void 0 ? void 0 : _b.query); | ||
this.closed = true; | ||
this.lastMessageAt = 0; | ||
this.hasScheduledAnUpdate = false; | ||
this.isLoadingHops = false; | ||
this.hops = {}; | ||
if (this.hasSsbDb2) { | ||
this.ssb.db.post((msg) => { | ||
var _a; | ||
if (msg.value.author !== this.ssb.id) { | ||
this.lastMessageAt = Date.now(); | ||
} | ||
if (((_a = msg.value.content) === null || _a === void 0 ? void 0 : _a.type) === 'contact') { | ||
this.loadHops(() => this.updateNow()); | ||
} | ||
}); | ||
} | ||
this.loadedSocialGraph = false; | ||
this.socialGraph = {}; | ||
} | ||
loadHops(doneCallback) { | ||
var _a; | ||
if (!((_a = this.ssb.friends) === null || _a === void 0 ? void 0 : _a.hops)) { | ||
debug('Warning: ssb-friends is missing, scheduling will miss some info'); | ||
loadSocialGraph() { | ||
var _a, _b; | ||
if (!((_a = this.ssb.friends) === null || _a === void 0 ? void 0 : _a.graphStream)) { | ||
debug('Warning: ssb-friends@5 is missing, scheduling is degraded'); | ||
this.loadedSocialGraph = true; | ||
return; | ||
} | ||
this.isLoadingHops = true; | ||
this.ssb.friends.hops((err, hops) => { | ||
if (err) { | ||
debug('unable to call ssb.friends.hops: %s', err); | ||
return; | ||
pull((_b = this.ssb.friends) === null || _b === void 0 ? void 0 : _b.graphStream({ live: true, old: true }), pull.drain((g) => { | ||
if (g[this.ssb.id]) { | ||
const prev = this.socialGraph; | ||
const updates = g[this.ssb.id]; | ||
this.socialGraph = { ...prev, ...updates }; | ||
this.loadedSocialGraph = true; | ||
} | ||
this.hops = hops; | ||
this.isLoadingHops = false; | ||
if (doneCallback) | ||
doneCallback(); | ||
}); | ||
})); | ||
} | ||
@@ -277,3 +265,3 @@ isCurrentlyDownloading() { | ||
backoffStep: 2e3, | ||
backoffMax: 10 * minute, | ||
backoffMax: 10 * MINUTED, | ||
groupMin: 1e3, | ||
@@ -293,3 +281,3 @@ }); | ||
backoffStep: 5e3, | ||
backoffMax: 5 * minute, | ||
backoffMax: 5 * MINUTED, | ||
groupMin: 5e3, | ||
@@ -300,3 +288,3 @@ }); | ||
backoffStep: 10e3, | ||
backoffMax: 10 * minute, | ||
backoffMax: 10 * MINUTED, | ||
groupMin: 5e3, | ||
@@ -307,3 +295,3 @@ }); | ||
backoffStep: 30e3, | ||
backoffMax: 30 * minute, | ||
backoffMax: 30 * MINUTED, | ||
groupMin: 15e3, | ||
@@ -313,11 +301,11 @@ }); | ||
quota: 3, | ||
backoffStep: 1 * minute, | ||
backoffMax: 3 * hour, | ||
groupMin: 5 * minute, | ||
backoffStep: 1 * MINUTED, | ||
backoffMax: 3 * HOUR, | ||
groupMin: 5 * MINUTED, | ||
}); | ||
this.updateTheseConnections(p => notRoom(p) && isLegacy(p), { | ||
quota: 1, | ||
backoffStep: 4 * minute, | ||
backoffMax: 3 * hour, | ||
groupMin: 5 * minute, | ||
backoffStep: 4 * MINUTED, | ||
backoffMax: 3 * HOUR, | ||
groupMin: 5 * MINUTED, | ||
}); | ||
@@ -342,3 +330,3 @@ z(conn.query().peersConnectable('staging').filter(this.weFollowThem)) | ||
.filter(p => p[1].type !== 'bt' && p[1].type !== 'lan') | ||
.filter(p => p[1].stateChange + 0.5 * hour < Date.now()) | ||
.filter(p => p[1].stateChange + 0.5 * HOUR < Date.now()) | ||
.forEach(([addr]) => conn.disconnect(addr)); | ||
@@ -351,3 +339,3 @@ } | ||
return; | ||
if (this.isLoadingHops) | ||
if (!this.loadedSocialGraph) | ||
return; | ||
@@ -383,16 +371,18 @@ this.updateStagingNow(); | ||
setupPubDiscovery() { | ||
var _a; | ||
var _a, _b; | ||
if (((_a = this.config.conn) === null || _a === void 0 ? void 0 : _a.populatePubs) === false) | ||
return; | ||
if (!this.hasSsbDb2) { | ||
debug('Warning: ssb-db2 is missing, scheduling will miss some info'); | ||
if (!((_b = this.ssb.db) === null || _b === void 0 ? void 0 : _b.operators)) { | ||
debug('Warning: ssb-db2 is missing, scheduling is degraded'); | ||
return; | ||
} | ||
setTimeout(() => { | ||
var _a; | ||
var _a, _b; | ||
if (this.closed) | ||
return; | ||
if (!((_a = this.ssb.db) === null || _a === void 0 ? void 0 : _a.operators)) | ||
return; | ||
const MAX_STAGED_PUBS = 3; | ||
const { where, type, live, toPullStream } = this.ssb.db.operators; | ||
this.pubDiscoveryPausable = (_a = this.pubDiscoveryPausable) !== null && _a !== void 0 ? _a : Pausable(); | ||
this.pubDiscoveryPausable = (_b = this.pubDiscoveryPausable) !== null && _b !== void 0 ? _b : Pausable(); | ||
pull(this.ssb.db.query(where(type('pub')), live({ old: true }), toPullStream()), pull.filter((msg) => { var _a; return Ref.isAddress((_a = msg.value.content) === null || _a === void 0 ? void 0 : _a.address); }), pull.asyncMap((x, cb) => setTimeout(() => cb(null, x), 250)), this.pubDiscoveryPausable, pull.drain((msg) => { | ||
@@ -435,3 +425,3 @@ try { | ||
if (!((_a = this.ssb.bluetooth) === null || _a === void 0 ? void 0 : _a.nearbyScuttlebuttDevices)) { | ||
debug('Warning: ssb-bluetooth is missing, scheduling will miss some info'); | ||
debug('Warning: ssb-bluetooth is missing, scheduling is degraded'); | ||
return; | ||
@@ -463,3 +453,3 @@ } | ||
if (!((_a = this.ssb.lan) === null || _a === void 0 ? void 0 : _a.start) || !((_b = this.ssb.lan) === null || _b === void 0 ? void 0 : _b.discoveredPeers)) { | ||
debug('Warning: ssb-lan is missing, scheduling will miss some info'); | ||
debug('Warning: ssb-lan is missing, scheduling is degraded'); | ||
return; | ||
@@ -466,0 +456,0 @@ } |
@@ -0,1 +1,3 @@ | ||
import { FeedId } from 'ssb-typescript'; | ||
import { CONN } from '../conn'; | ||
export declare type Peer = { | ||
@@ -24,3 +26,3 @@ address?: string; | ||
}; | ||
export declare type Config = { | ||
export interface Config { | ||
seed?: boolean; | ||
@@ -30,6 +32,28 @@ seeds?: Array<string> | string; | ||
autostart: boolean; | ||
hops: number; | ||
populatePubs: boolean; | ||
}; | ||
}; | ||
} | ||
export interface SSB { | ||
readonly id: FeedId; | ||
readonly friends?: Readonly<{ | ||
graphStream: (opts: { | ||
old: boolean; | ||
live: boolean; | ||
}) => CallableFunction; | ||
}>; | ||
readonly bluetooth?: Readonly<{ | ||
nearbyScuttlebuttDevices: (x: number) => CallableFunction; | ||
}>; | ||
readonly lan?: Readonly<{ | ||
start: () => void; | ||
stop: () => void; | ||
discoveredPeers: () => CallableFunction; | ||
}>; | ||
readonly db?: Readonly<{ | ||
post: (cb: CallableFunction) => CallableFunction; | ||
query: (...args: Array<any>) => any; | ||
operators: Record<string, any>; | ||
}>; | ||
readonly conn: CONN; | ||
} | ||
export declare type Callback<T> = (err?: any, val?: T) => void; |
{ | ||
"name": "ssb-conn", | ||
"description": "SSB plugin for establishing and managing peer connections", | ||
"version": "4.0.1", | ||
"version": "5.0.0", | ||
"homepage": "https://github.com/staltz/ssb-conn", | ||
@@ -6,0 +6,0 @@ "main": "lib/index.js", |
113
README.md
@@ -59,4 +59,4 @@ <div style="text-align:center" align="center"> | ||
|-----|------|-------------| | ||
| **`ssb.conn.connect(addr, data?)`** | `async` | Connects to a peer known by its multiserver address `addr`, and stores additional optional `data` (as an object) during its connection lifespan. | | ||
| **`ssb.conn.disconnect(addr)`** | `async` | Disconnects a peer known by its multiserver address `addr`. | | ||
| **`ssb.conn.connect(addr, data?, cb)`** | `async` | Connects to a peer known by its multiserver address `addr`, and stores additional optional `data` (as an object) during its connection lifespan. | | ||
| **`ssb.conn.disconnect(addr, cb)`** | `async` | Disconnects a peer known by its multiserver address `addr`. | | ||
| **`ssb.conn.peers()`** | `source` | A pull-stream that emits an array of all ConnHub entries whenever any connection updates (i.e. changes it state: connecting, disconnecting, connected, etc). | | ||
@@ -147,5 +147,29 @@ | **`ssb.conn.remember(addr, data?)`** | `sync` | Stores (in cold storage) connection information about a new peer, known by its multiserver address `addr` and additional optional `data` (as an object). | | ||
## Config | ||
Some parameters in CONN can be configured by the user or by application code through the conventional [ssb-config](https://github.com/ssbc/ssb-config). The possible options are listed below: | ||
```typescript | ||
{ | ||
"conn": { | ||
/** | ||
* Whether the CONN scheduler should start automatically as soon as the | ||
* SSB app is initialized. Default is `true`. | ||
*/ | ||
"autostart": boolean, | ||
/** | ||
* Whether the CONN scheduler should look into the SSB database looking for | ||
* messages of type 'pub' and add them to CONN. Default is `true`. | ||
*/ | ||
"populatePubs": boolean, | ||
} | ||
} | ||
``` | ||
## Recipes | ||
**How can I get a pull stream of all currently connected peers?** | ||
<details> | ||
<summary>How can I get a pull stream of all currently connected peers? (click here)</summary> | ||
<p> | ||
@@ -178,4 +202,12 @@ You can use `ssb.conn.peers()` to get a stream of "all peers currently being processed" and then use Array `filter` to pick only peers that are strictly *connected*, ignoring those that are *connecting* or *disconnecting*: | ||
**How can I _immediately_ get all currently connected peers?** | ||
<ul></ul> | ||
</p> | ||
</details> | ||
<details> | ||
<summary>How can I <em>immediately</em> get all currently connected peers? (click here)</summary> | ||
<p> | ||
[ssb-conn-query](https://github.com/staltz/ssb-conn-query) has APIs for that and others, e.g. | ||
@@ -210,30 +242,7 @@ | ||
## Config | ||
<ul></ul> | ||
Some parameters in CONN can be configured by the user or by application code through the conventional [ssb-config](https://github.com/ssbc/ssb-config). The possible options are listed below: | ||
</p> | ||
</details> | ||
```typescript | ||
{ | ||
"conn": { | ||
/** | ||
* Whether the CONN scheduler should start automatically as soon as the | ||
* SSB app is initialized. Default is `true`. | ||
*/ | ||
"autostart": boolean, | ||
/** | ||
* How far in the social graph should a peer be automatically connected to | ||
* whenever possible. Default value (when this is unspecified) is `1`. | ||
*/ | ||
"hops": number, | ||
/** | ||
* Whether the CONN scheduler should look into the SSB database looking for | ||
* messages of type 'pub' and add them to CONN. Default is `true`. | ||
*/ | ||
"populatePubs": boolean, | ||
} | ||
} | ||
``` | ||
## Learn more | ||
@@ -279,2 +288,3 @@ | ||
- Read the SSB log and look for "pub" messages, and `remember` them | ||
- Listen to a stream of LAN peers (see [ssb-lan](https://github.com/staltz/ssb-lan)), and `stage` them | ||
- Listen to a stream of Bluetooth nearby devices, and `stage` them | ||
@@ -331,30 +341,29 @@ | ||
```javascript | ||
import {plugin, muxrpc} from 'secret-stack-decorators'; | ||
module.exports = { | ||
name: 'connScheduler', | ||
version: '1.0.0', | ||
manifest: { | ||
start: 'sync', | ||
stop: 'stop', | ||
}, | ||
init(ssb, config) { | ||
return { | ||
start() { | ||
// this is called when the scheduler should begin making connections | ||
@plugin('1.0.0') | ||
module.exports = class ConnScheduler { | ||
constructor(ssb, config) { | ||
// basic setup here | ||
this.ssb = ssb; | ||
} | ||
// You have access to CONN core here: | ||
ssb.conn.stage('some multiserver address'); | ||
ssb.conn.disconnect('another multiserver address'); | ||
// ... | ||
}, | ||
@muxrpc('sync') | ||
public start = () => { | ||
// this is called when the scheduler should begin scheduling connections | ||
// You have access to CONN core here: | ||
const query = this.ssb.conn.query(); | ||
this.ssb.conn.stage(addr); | ||
this.ssb.conn.disconnect(addr); | ||
// ... | ||
stop() { | ||
// this is called when the scheduler should cancel its jobs, if any | ||
} | ||
} | ||
} | ||
@muxrpc('sync') | ||
public stop = () => { | ||
// this is called when the scheduler should cancel its jobs | ||
} | ||
} | ||
``` | ||
Note that the name of the plugin must be **exactly `ConnScheduler`** (or `connScheduler`) and it **must have the methods start() and stop()**, because the CONN core will try to use your scheduler under those names. The rest of the contents of the ConnScheduler class are up to you, you can use private methods, etc. | ||
Note that the name of the plugin must be **exactly `connScheduler`** (or `connScheduler`) and it **must have the methods start() and stop()**, because the CONN core will try to use your scheduler under those names. The rest of the contents of the ConnScheduler class are up to you, you can use private methods, etc. | ||
@@ -361,0 +370,0 @@ When you're done building your scheduler, you can export it together with CONN core and the gossip compatibility plugin like this: |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
59521
981
406