Verified Commit bbd58918 authored by staltz's avatar staltz

WIP make threadsUtils plugin in the backend

This was a failed attempt. It seems like a correct implementation, but
in practice the app crashes (about 60s after startup) due to some
leveldown codec encoder receiving the wrong value. Too cryptic to
understand, I gave up.
parent c3f3cfca
This diff is collapsed.
......@@ -97,15 +97,13 @@
"remark-images-to-ssb-serve-blobs": "2.1.0-1",
"remark-linkify-regex": "1.0.0",
"remark-ssb-mentions": "~2.0.0",
"ssb-cached-about": "~1.0.0",
"ssb-conn-query": "~0.4.4",
"ssb-ref": "2.13.9",
"ssb-room": "~1.1.1",
"ssb-serve-blobs": "2.1.0",
"ssb-threads": "3.6.0",
"ssb-threads": "4.1.0",
"ssb-typescript": "2.0.0",
"xstream": "11.11.0",
"xstream-backoff": "1.0",
"xstream-between": "1.0",
"xstream-from-callback": "1.0",
"xstream-from-pull-stream": "1.1",
......@@ -145,4 +143,4 @@
"react-native": {
"os": "react-native-os-staltz"
}
}
\ No newline at end of file
}
......@@ -2231,9 +2231,9 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"promisify-tuple": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/promisify-tuple/-/promisify-tuple-1.0.0.tgz",
"integrity": "sha512-cLx3LIS6pjWJym+M2TWCc5Mvt6LFaZakGBaRQWpOQkrcobJ7PHFX7m+VXnbb9Ha7n4SULB9ajulWvasSdi5JHw=="
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/promisify-tuple/-/promisify-tuple-1.0.1.tgz",
"integrity": "sha512-st4Q1R6oAr8hzt1hj3uCYv87Pc5wtDkoq7ZqxWri2xR3x5Zvx7syH2DR4fgknVhMsZ6GV+kxEmhn2tVnwFMmJw=="
},
"promisize": {
"version": "1.1.2",
......@@ -2259,6 +2259,11 @@
"resolved": "https://registry.npmjs.org/pull-abortable/-/pull-abortable-4.0.0.tgz",
"integrity": "sha1-cBephMO4NN53usOMELd28i38GEM="
},
"pull-backoff": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pull-backoff/-/pull-backoff-1.0.0.tgz",
"integrity": "sha512-oIo1tP09Cl5BZpmKNlHPTn/weJBPPLGy019CjgLYxauANF72PPsAbKrQKKO2UJ68qh+7AG1EidCfIPnV5n3EYw=="
},
"pull-box-stream": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/pull-box-stream/-/pull-box-stream-1.0.13.tgz",
......@@ -2282,6 +2287,11 @@
"resolved": "https://registry.npmjs.org/pull-catch/-/pull-catch-1.0.1.tgz",
"integrity": "sha512-wrKbmEYySNETxOYXDTCJ8L/rcAFMayOifne2a+X9C0wSm6ttIWHHXwMYQh6k8iDRvtMM8itYkBlP4leKBJTiKA=="
},
"pull-combine-latest": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/pull-combine-latest/-/pull-combine-latest-1.1.2.tgz",
"integrity": "sha1-c4IWv20MKqrJYzeNEh1RpCTvaVE="
},
"pull-cont": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/pull-cont/-/pull-cont-0.1.1.tgz",
......@@ -2573,6 +2583,11 @@
"defined": "^1.0.0"
}
},
"pull-switch-map": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pull-switch-map/-/pull-switch-map-1.0.0.tgz",
"integrity": "sha512-gekejf7PG2uWrkbJRspvBWxGRC1tKYbIOiOeuTr5BZG6TwdzMuTZtI5sdByVMoG3LnvcEAZkJB1oEtwxJk+2gw=="
},
"pull-thenable": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pull-thenable/-/pull-thenable-1.0.0.tgz",
......@@ -2795,6 +2810,11 @@
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
},
"rfdc": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
"integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug=="
},
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
......@@ -3167,6 +3187,15 @@
"pull-pushable": "^2.2.0"
}
},
"ssb-cached-about": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ssb-cached-about/-/ssb-cached-about-1.1.0.tgz",
"integrity": "sha512-GXR7KggQZ5bJH1udSR6dZhPdWwEVxPUl58SMBSv23ypoTLMKAgT4hoZinE31suJ7wLBAqoYh3OBCw45Z9sXw4w==",
"requires": {
"quick-lru": "~4.0.1",
"ssb-typescript": "2.0.0"
}
},
"ssb-caps": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ssb-caps/-/ssb-caps-1.1.0.tgz",
......@@ -3230,6 +3259,11 @@
"version": "3.6.14",
"resolved": "https://registry.npmjs.org/pull-stream/-/pull-stream-3.6.14.tgz",
"integrity": "sha512-KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew=="
},
"ssb-typescript": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/ssb-typescript/-/ssb-typescript-1.7.0.tgz",
"integrity": "sha512-BuU8Buzm/TCA8WEUEX4mOO6AkgCPnrtoJQE7K4JY9eOvShEtA6qjvzKuNG4ZwivIwfGy+eF9fl5WFkaihqnj4A=="
}
}
},
......@@ -3386,6 +3420,11 @@
"version": "12.12.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.8.tgz",
"integrity": "sha512-XLla8N+iyfjvsa0KKV+BP/iGSoTmwxsu5Ci5sM33z9TjohF72DEz95iNvD6pPmemvbQgxAv/909G73gUn8QR7w=="
},
"promisify-tuple": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/promisify-tuple/-/promisify-tuple-1.0.0.tgz",
"integrity": "sha512-cLx3LIS6pjWJym+M2TWCc5Mvt6LFaZakGBaRQWpOQkrcobJ7PHFX7m+VXnbb9Ha7n4SULB9ajulWvasSdi5JHw=="
}
}
},
......@@ -3738,13 +3777,18 @@
"pull-stream": "^3.5.0",
"pull-write": "^1.1.1"
}
},
"ssb-typescript": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ssb-typescript/-/ssb-typescript-1.6.0.tgz",
"integrity": "sha512-SKX4ciRWZgbaPtEK5tGRnyorgAHt2N82+qXdLC1HihLWLmA1W5S5eY+xFx/pADsWo1r7rAfinEDWaEQN67rnrw=="
}
}
},
"ssb-typescript": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ssb-typescript/-/ssb-typescript-1.6.0.tgz",
"integrity": "sha512-SKX4ciRWZgbaPtEK5tGRnyorgAHt2N82+qXdLC1HihLWLmA1W5S5eY+xFx/pADsWo1r7rAfinEDWaEQN67rnrw=="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ssb-typescript/-/ssb-typescript-2.0.0.tgz",
"integrity": "sha512-lNv33Dg4Ru1GckWTojKrtIqpRbwB/GbJlZiJivWst4MbI4dsTfIZrFoMZaZ+ZqwlOQiR6xNEBtDd03OnJe6ALg=="
},
"ssb-validate": {
"version": "4.0.4",
......
......@@ -16,12 +16,17 @@
"leveldown-nodejs-mobile": "5.1.1-3",
"multiserver-rn-channel": "1.3.0",
"non-private-ip-android": "1.4.4-3",
"promisify-tuple": "1.0.1",
"pull-backoff": "1.0.0",
"pull-cat": "1.1.11",
"pull-combine-latest": "1.1.2",
"pull-file": "1.1.0",
"pull-pushable": "2.2.0",
"pull-pair": "~1.1.0",
"pull-stream": "3.6.13",
"pull-switch-map": "1.0.0",
"pull-thenable": "1.0.0",
"rfdc": "1.1.4",
"secret-stack": "6.3.1",
"secret-stack-decorators": "~1.0.0",
"sodium-chloride-native-nodejs-mobile": "1.1.0",
......@@ -30,6 +35,7 @@
"ssb-backlinks": "0.7.3",
"ssb-blobs": "1.2.2",
"ssb-bluetooth": "1.1.14",
"ssb-cached-about": "1.1.0",
"ssb-config": "3.3.2",
"ssb-conn": "0.16.2",
"ssb-db": "19.3.1",
......@@ -50,7 +56,7 @@
"ssb-serve-blobs": "2.1.0",
"ssb-suggest": "1.2.0",
"ssb-threads": "4.1.0",
"ssb-typescript": "1.6.0",
"ssb-typescript": "2.0.0",
"utf-8-validate": "5.0.2",
"utp-native-nodejs-mobile": "2.1.3-1"
},
......
......@@ -4,7 +4,55 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const pull = require('pull-stream');
const cat = require('pull-cat');
const backoff = require('pull-backoff');
const switchMap = require('pull-switch-map');
const combineLatest = require('pull-combine-latest');
import {Peer as PeerKV} from 'ssb-conn-query/lib/types';
import run = require('promisify-tuple');
import {imageToImageUrl} from './helpers/about';
import {Callback} from './helpers/types';
import {StagedPeerKV} from '../../shared-types';
type HostingDhtInvite = {seed: string; claimer: string; online: boolean};
function augmentPeerWithExtras(ssb: any) {
const getAbout = ssb.cachedAbout.socialValue;
return async ([addr, peer]: PeerKV, cb: Callback<[string, any]>) => {
// Fetch name
const nameOpts = {key: 'name', dest: peer.key};
const [e1, nameResult] = await run(getAbout)(nameOpts);
if (e1) return cb(e1);
const name = nameResult || undefined;
// Fetch avatar
const avatarOpts = {key: 'image', dest: peer.key};
const [e2, val] = await run(getAbout)(avatarOpts);
if (e2) return cb(e2);
const imageUrl = imageToImageUrl(val);
// Fetch 'isInDB' boolean
const isInDB: boolean = ssb.conn.db().has(addr);
cb(null, [addr, {name, imageUrl, isInDB, ...peer}]);
};
}
function augmentPeersWithExtras(ssb: any) {
return async (kvs: Array<PeerKV>, cb: Callback<Array<PeerKV>>) => {
const peers: Array<PeerKV> = [];
for (const kv of kvs) {
const [err, peer] = await run<any>(augmentPeerWithExtras(ssb))(kv);
if (err) {
cb(err);
return;
}
peers.push(peer);
}
cb(null, peers);
};
}
export = {
name: 'connUtils',
......@@ -12,11 +60,17 @@ export = {
manifest: {
persistentConnect: 'async',
persistentDisconnect: 'async',
isInDB: 'async',
peers: 'source',
stagedPeers: 'source',
},
permissions: {
master: {
allow: ['persistentConnect', 'persistentDisconnect', 'isInDB'],
allow: [
'persistentConnect',
'persistentDisconnect',
'peers',
'stagedPeers',
],
},
},
init: function init(ssb: any) {
......@@ -42,13 +96,53 @@ export = {
ssb.conn.disconnect(address, cb);
},
isInDB(address: string, cb: Callback<boolean>) {
try {
const result = ssb.conn.db().has(address);
cb(null, result);
} catch (err) {
cb(err);
}
peers() {
return pull(
ssb.conn.peers(),
switchMap((peers: Array<PeerKV>) =>
pull(
cat([pull.once(0), backoff(1e3, 2, 60e3)]),
pull.map(() => peers),
),
),
pull.through((peers: Array<PeerKV>) => {
for (const [, data] of peers) {
if (data.key) ssb.cachedAbout.invalidate(data.key);
}
}),
pull.asyncMap(augmentPeersWithExtras(ssb)),
);
},
stagedPeers() {
const connStagedPeers = pull(
ssb.conn.stagedPeers(),
pull.asyncMap(augmentPeersWithExtras(ssb)),
);
//#region DHT-related hacks (TODO ideally this should go through CONN)
const hostingDHT = pull(
cat([pull.values([[]]), ssb.dhtInvite.hostingInvites()]),
pull.map((invites: Array<HostingDhtInvite>) =>
invites
.filter(invite => !invite.online)
.map(
({seed}) =>
[
`dht:${seed}:${ssb.id}`,
{key: seed, type: 'dht', role: 'server'},
] as StagedPeerKV,
),
),
);
const stagedTotal = pull(
combineLatest(connStagedPeers, hostingDHT),
pull.map(([as, bs]: any) => [...as, ...bs]),
);
//#endregion
return stagedTotal;
},
};
},
......
/* Copyright (C) 2020 The Manyverse Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const blobIdToUrl = require('ssb-serve-blobs/id-to-url');
export function imageToImageUrl(val: any) {
let image: string | null = val;
if (!!val && typeof val === 'object' && val.link) image = val.link;
const imageUrl: string | undefined = image ? blobIdToUrl(image) : undefined;
return imageUrl;
}
/* Copyright (C) 2020 The Manyverse Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const pull = require('pull-stream');
const Ref = require('ssb-ref');
import {Thread as ThreadData} from 'ssb-threads/types';
import {Msg, Content, FeedId} from 'ssb-typescript';
import {
isMsg,
isRootPostMsg,
isPublic,
isReplyPostMsg,
} from 'ssb-typescript/utils';
import run = require('promisify-tuple');
import {
AnyThread,
MsgAndExtras,
ThreadAndExtras,
PrivateThreadAndExtras,
} from '../../shared-types';
import {imageToImageUrl} from './helpers/about';
import {Callback} from './helpers/types';
const clone = require('rfdc')();
function getRecipient(recp: string | Record<string, any>): string | undefined {
if (typeof recp === 'object' && Ref.isFeed(recp.link)) {
return recp.link;
}
if (typeof recp === 'string' && Ref.isFeed(recp)) {
return recp;
}
}
function mutateMsgWithLiveExtras(ssb: any) {
const getAbout = ssb.cachedAbout.socialValue;
return async (msg: Msg, cb: Callback<MsgAndExtras>) => {
if (!isMsg(msg) || !msg.value) return cb(null, msg as any);
// Fetch name
const nameOpts = {key: 'name', dest: msg.value.author};
const [e1, name] = await run<string | undefined>(getAbout)(nameOpts);
if (e1) return cb(e1);
// Fetch avatar
const avatarOpts = {key: 'image', dest: msg.value.author};
const [e2, val] = await run(getAbout)(avatarOpts);
if (e2) return cb(e2);
const imageUrl = imageToImageUrl(val);
// Get likes stream
// FIXME: do this in a frontend plugin
// - publicRawFeed()
// - publicFeed()
// x NOT for privateFeed() I guess?
// - selfPublicRoots()
// x NOT for selfPrivateRoots I guess?
// - selfReplies
// - profileFeed
// x NOT for threadUpdates, but maybe in the future we should, when this is used for public too
// - thread
// const likes = xsFromPullStream(ssb.votes.voterStream(msg.key)).startWith(
// [],
// );
// Create msg object
const m = msg as MsgAndExtras;
m.value._$manyverse$metadata = m.value._$manyverse$metadata || {
// likes, // FIXME: do this in a frontend plugin
about: {name, imageUrl},
};
// Add name of the target contact, if any
const content = msg.value.content;
if (!content || content.type !== 'contact' || !content.contact) {
return cb(null, m);
}
const dest: FeedId = content.contact;
const dOpts = {key: 'name', dest};
const [e3, destName] = await run<string | undefined>(getAbout)(dOpts);
if (e3) return cb(e3);
m.value._$manyverse$metadata.contact = {name: destName};
cb(null, m);
};
}
function mutateThreadWithLiveExtras(ssb: any) {
return async (thread: ThreadData, cb: Callback<ThreadAndExtras>) => {
for (const msg of thread.messages) {
await run(mutateMsgWithLiveExtras(ssb))(msg);
}
cb(null, thread as ThreadAndExtras);
};
}
function mutatePrivateThreadWithLiveExtras(ssb: any) {
const getAbout = ssb.cachedAbout.socialValue;
return async (thread: ThreadData, cb: Callback<PrivateThreadAndExtras>) => {
for (const msg of thread.messages) {
await run(mutateMsgWithLiveExtras(ssb))(msg);
}
const root: Msg<Content> | undefined = thread.messages[0];
const pvthread: PrivateThreadAndExtras = thread as any;
if (root && root?.value?.content?.recps) {
pvthread.recps = [];
for (const recp of root?.value?.content?.recps) {
const id = getRecipient(recp);
if (!id) continue;
// Fetch name
const nameOpts = {key: 'name', dest: id};
const [e1, name] = await run<string | undefined>(getAbout)(nameOpts);
if (e1) return cb(e1);
// Fetch avatar
const avatarOpts = {key: 'image', dest: id};
const [e2, val] = await run<string>(getAbout)(avatarOpts);
if (e2) return cb(e2);
const imageUrl = imageToImageUrl(val);
// Push
pvthread.recps.push({id, name, imageUrl});
}
}
cb(null, pvthread as PrivateThreadAndExtras);
};
}
export = {
name: 'threadsUtils',
version: '1.0.0',
manifest: {
publicRawFeed: 'source',
publicFeed: 'source',
privateFeed: 'source',
selfPublicRoots: 'source',
selfPrivateRoots: 'source',
selfReplies: 'source',
profileFeed: 'source',
threadUpdates: 'source',
thread: 'async',
},
permissions: {
master: {
allow: [
'publicRawFeed',
'publicFeed',
'privateFeed',
'selfPublicRoots',
'selfPrivateRoots',
'selfReplies',
'profileFeed',
'threadUpdates',
'thread',
],
},
},
init: function init(ssb: any, _config: any) {
return {
publicRawFeed(opts: any) {
return pull(
ssb.createFeedStream({reverse: true, live: false, ...opts}),
pull.asyncMap(mutateMsgWithLiveExtras(ssb)),
);
},
publicFeed(opts: any) {
return pull(
ssb.threads.public({
threadMaxSize: 3,
allowlist: ['post', 'contact'],
...opts,
}),
pull.map(clone),
pull.asyncMap(mutateThreadWithLiveExtras(ssb)),
);
},
privateFeed(opts: any) {
return pull(
ssb.threads.private({threadMaxSize: 1, allowlist: ['post'], ...opts}),
pull.map(clone),
pull.asyncMap(mutatePrivateThreadWithLiveExtras(ssb)),
);
},
selfPublicRoots(opts: any) {
return pull(
ssb.createUserStream({id: ssb.id, ...opts}),
pull.filter(isRootPostMsg),
pull.filter(isPublic),
pull.map((msg: Msg) => ({messages: [msg], full: true} as ThreadData)),
pull.asyncMap(mutateThreadWithLiveExtras(ssb)),
);
},
selfPrivateRoots() {
return pull(
ssb.threads.private({
threadMaxSize: 1,
allowlist: ['post'],
old: false,
live: true,
}),
pull.map((thread: ThreadData) => thread?.messages?.[0]),
pull.filter((msg: Msg) => msg?.value?.author === ssb.id),
);
},
selfReplies(opts: any) {
return pull(
ssb.createUserStream({id: ssb.id, ...opts}),
pull.filter(isReplyPostMsg),
pull.filter(isPublic),
pull.asyncMap(mutateMsgWithLiveExtras(ssb)),
);
},
profileFeed(id: FeedId, opts: any) {
return pull(
ssb.threads.profile({
id,
reverse: true,
live: false,
threadMaxSize: 3,
allowlist: ['post', 'contact'],
...opts,
}),
pull.asyncMap(mutateThreadWithLiveExtras(ssb)),
);
},
threadUpdates(opts: {root: FeedId; private: boolean}) {
return pull(
ssb.threads.threadUpdates(opts),
pull.asyncMap(mutateMsgWithLiveExtras(ssb)),
);
},
thread(opts: {root: FeedId; private: boolean}, cb: Callback<AnyThread>) {
pull(
ssb.threads.thread(opts),
pull.asyncMap((t: ThreadData, cb2: Callback<AnyThread>) => {
if (opts.private) {
mutatePrivateThreadWithLiveExtras(ssb)(t, cb2);
} else {
mutateThreadWithLiveExtras(ssb)(t, cb2);
}
}),
pull.take(1),
pull.drain(
(thread: AnyThread) => cb(null, thread),
(err: any) => (err ? cb(err) : void 0),
),
);
},
};
},
};
......@@ -67,6 +67,7 @@ SecretStack({appKey: require('ssb-caps').shs})
.use(require('ssb-private')) // needs: db
.use(require('ssb-backlinks')) // needs: db
.use(require('ssb-about')) // needs: db, backlinks
.use(require('ssb-cached-about').default()) // needs ssb-about
.use(require('ssb-suggest')) // needs: db, backlinks, about, friends
.use(require('ssb-threads')) // needs: db, backlinks, friends
// Blobs
......@@ -79,5 +80,6 @@ SecretStack({appKey: require('ssb-caps').shs})
.use(require('./plugins/friendsUtils')) // needs: db
.use(require('./plugins/keysUtils'))
.use(require('./plugins/syncing')) // needs: db
.use(require('./plugins/threadsUtils')) // needs: threads
.use(require('./plugins/votes')) // needs: backlinks
.call(null, config);
This diff is collapsed.
......@@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import ssbClient from 'react-native-ssb-client';
import cachedAbout from 'ssb-cached-about';
import manifest from './manifest';
import hooksPlugin from './plugins/hooks';
import publishUtilsPlugin from './plugins/publishUtils';
......@@ -16,10 +15,17 @@ function makeClient() {
return ssbClient(manifest)
.use(hooksPlugin())
.use(publishUtilsPlugin())
.use(cachedAbout())
.use(contactsPlugin())
.use(syncingNotifications())
.callPromise();
}
// type ClientAPIFromManifest = {
// [key in keyof typeof manifest]: typeof manifest[key] extends string
// ? CallableFunction
// : {
// [key2 in keyof typeof manifest[key]]: CallableFunction;
// };
// };
export default makeClient;
......@@ -142,6 +142,10 @@ export default {
endpoints: 'source',
ping: 'sync',
},
cachedAbout: {
socialValue: 'async',
invalidate: 'sync',
},
// This project's plugins
blobsUtils: {
......@@ -150,7 +154,8 @@ export default {
connUtils: {
persistentConnect: 'async',
persistentDisconnect: 'async',
isInDB: 'async',
peers: 'source',
stagedPeers: 'source',
},
publishUtilsBack: {
publish: 'async',
......@@ -165,6 +170,17 @@ export default {
syncing: {
stream: 'source',
},
threadsUtils: {
publicRawFeed: 'source',
publicFeed: 'source',
privateFeed: 'source',
selfPublicRoots: 'source',
selfPrivateRoots: 'source',
selfReplies: 'source',
profileFeed: 'source',
threadUpdates: 'source',
thread: 'async',
},
votes: {
voterStream: 'source',
},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment