git ssb

3+

ev / decent



Commit 5a59ba682d902073cf69ba77bd1b7d0503920283

re-add viewer plugin

Ev Bogue committed on 8/31/2017, 7:33:29 PM
Parent: 4b72a4b3c3518eee46dfeef8be64a1ac1ac287af

Files changed

decent.jschanged
package.jsonchanged
plugins/viewer/README.mdadded
plugins/viewer/bin.jsadded
plugins/viewer/example.htmladded
plugins/viewer/index.jsadded
plugins/viewer/lib/about.jsadded
plugins/viewer/static/base.cssadded
plugins/viewer/static/nicer.cssadded
plugins/viewer/yarn.lockadded
yarn.lockchanged
decent.jsView
@@ -43,8 +43,9 @@
4343 .use(require('ssb-query'))
4444 .use(require('ssb-links'))
4545 .use(require('ssb-ws'))
4646 .use(require('ssb-ebt'))
47 + .use(require('./plugins/viewer'))
4748
4849 // add third-party plugins
4950 require('./plugins/plugins').loadUserPlugins(createSbot, config)
5051
package.jsonView
@@ -8,8 +8,9 @@
88 },
99 "author": "Ev Bogue",
1010 "license": "MIT",
1111 "dependencies": {
12 + "asyncmemo": "^1.0.0",
1213 "atomic-file": "0.0.1",
1314 "bash-color": "~0.0.3",
1415 "broadcast-stream": "~0.0.0",
1516 "cont": "~1.0.3",
@@ -18,8 +19,9 @@
1819 "deep-extend": "^0.5.0",
1920 "depject": "^3.0.0",
2021 "ecstatic": "^2.1.0",
2122 "emoji-named-characters": "^1.0.2",
23 + "emoji-server": "^1.0.0",
2224 "explain-error": "~1.0.1",
2325 "has-network": "0.0.0",
2426 "human-time": "0.0.1",
2527 "hypercombo": "0.1.0",
@@ -31,8 +33,9 @@
3133 "hypertabs-vertical": "^3.0.0",
3234 "ip": "^0.3.3",
3335 "is-visible": "^2.1.1",
3436 "kvgraph": "^0.1.0",
37 + "lrucache": "^1.0.3",
3538 "map-filter-reduce": "^3.0.1",
3639 "mdmanifest": "^1.0.4",
3740 "minimist": "^1.1.3",
3841 "mkdirp": "~0.5.0",
@@ -78,11 +81,13 @@
7881 "ssb-git": "^0.5.0",
7982 "ssb-keys": "^7.0.10",
8083 "ssb-links": "^2.0.0",
8184 "ssb-markdown": "^3.3.0",
85 + "ssb-marked": "^0.7.2",
8286 "ssb-mentions": "^0.4.0",
8387 "ssb-query": "^1.0.0",
8488 "ssb-ref": "^2.7.1",
89 + "ssb-sort": "^1.0.0",
8590 "ssb-ws": "^1.0.3",
8691 "statistics": "3.0.0",
8792 "stream-to-pull-stream": "^1.6.10",
8893 "suggest-box": "^2.2.1",
plugins/viewer/README.mdView
@@ -1,0 +1,108 @@
1 +# ssb-viewer
2 +
3 +HTTP server for read-only views of SSB content. Serves content as web pages or as scripts for embedding in other web pages.
4 +
5 +## Install & Run
6 +
7 +As a sbot plugin:
8 +```sh
9 +mkdir -p ~/.ssb/node_modules
10 +cd ~/.ssb/node_modules
11 +git clone ssb://%MeCTQrz9uszf9EZoTnKCeFeIedhnKWuB3JHW2l1g9NA=.sha256 ssb-viewer && cd ssb-viewer
12 +npm install
13 +sbot plugins.enable ssb-viewer
14 +# restart sbot
15 +```
16 +
17 +Or standalone:
18 +```sh
19 +git clone ssb://%MeCTQrz9uszf9EZoTnKCeFeIedhnKWuB3JHW2l1g9NA=.sha256 ssb-viewer && cd ssb-viewer
20 +npm install
21 +./bin.js
22 +```
23 +
24 +## Usage
25 +
26 +To view a thread as a web page, navigate to a url like `http://localhost:8807/%MSGID`.
27 +
28 +To embed a thread into another web page, load it as follows:
29 +
30 +```html
31 +<script src="http://localhost:8807/%MSGID.js"></script>
32 +```
33 +
34 +To add more than the base styles, you can also load `http://localhost:8807/static/nicer.css`.
35 +
36 +## Routes
37 +
38 +- `/%msgid`: web page showing a message thread
39 +- `/%msgid.js`: script to embed a message thread
40 +- `/%msgid.json`: message thread as JSON
41 +- `/&feedid`: web page showing a complete feed
42 +- `/user-feed/&feedid`: web page showing messages from followed users and channels of a feed
43 +- `/channel/#channel`: web page showing messages in a specific channel
44 +
45 +### Query options
46 +
47 +- `noroot`: don't include the root message in the thread
48 +- `base=...`: base url for links that ssb-viewer can handle
49 +- `msg_base=...`: base url for links to messages
50 +- `feed_base=...`: base url for links to feeds
51 +- `blob_base=...`: base url for links to blobs
52 +- `img_base=...`: base url for embedded blobs (images)
53 +- `emoji_base=...`: base url for emoji images
54 +
55 +The `*_base` query options overwrite the defaults set in the config.
56 +The `base` option is a fallback instead of specifying the URLs separately.
57 +The base options are mostly useful for embedding, where the script is embedded
58 +on a different origin than where ssb-viewer is running. However, you may not
59 +need them, as the ssb-viewer embed script will detect the base where it is
60 +included from.
61 +
62 +## Config
63 +
64 +To change `ssb-viewer`'s default options, edit your `~/.ssb/config`, to have
65 +properties like the following:
66 +```json
67 +{
68 + "viewer": {
69 + "port": 8807,
70 + "host": "::"
71 + }
72 +}
73 +```
74 +You can also pass these as command-line options to `./bin.js` or `sbot` as,
75 +e.g. `--viewer.port 8807`.
76 +
77 +- `viewer.port`: port for the server to listen on. default: `8807`
78 +- `viewer.host`: host address for the server to listen on. default: `::`
79 +- `viewer.base`: default base url for links that ssb-viewer can handle
80 +- `viewer.msg_base`: base url for links to ssb messages
81 +- `viewer.feed_base`: base url for links to ssb feeds
82 +- `viewer.blob_base`: base url for links to ssb blobs
83 +- `viewer.img_base`: base url for embedded blobs (images)
84 +- `viewer.emoji_base`: base url for emoji images
85 +
86 +## References
87 +
88 +- Concept: [ssb-porthole][]
89 +- UI ideas: [sdash][], [patchbay][]
90 +- Server techniques: [ssb-web-server][], [ssb-ws][], [git-ssb-web][]
91 +
92 +
93 +[ssb-porthole]: %cgkDJXsh6pO5m458B3ngEro+U0qUMGTY1TRGTZOP6lQ=.sha256
94 +[patchbay]: %s9mSFATE4RGyJx9wgH22lBrvD4CgUQW4yeguSWWjtqc=.sha256
95 +[sdash]: %qrU04j9vfUJKfq1rGZrQ5ihtSfA4ilfY3wLy7xFv0xk=.sha256
96 +[git-ssb-web]: %q5d5Du+9WkaSdjc8aJPZm+jMrqgo0tmfR+RcX5ZZ6H4=.sha256
97 +[ssb-web-server]: %gYctTCrA06BhAGGvQ6PJ0H2eCCQLj1iEsmfn8SD5+nk=.sha256
98 +[ssb-ws]: %tFjo5SoD+Y0SaB5vqZYppmoPmv9LKB5wMPl96qtu4qk=.sha256
99 +
100 +## License
101 +
102 +Copyright (c) 2016-2017 Secure Scuttlebutt Consortium
103 +
104 +Usage of the works is permitted provided that this instrument is
105 +retained with the works, so that any entity that uses the works is
106 +notified of this instrument.
107 +
108 +DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
plugins/viewer/bin.jsView
@@ -1,0 +1,6 @@
1 +#!/usr/bin/env node
2 +
3 +require('../ssb-client')(function (err, sbot, config) {
4 + if (err) throw err
5 + require('.').init(sbot, config)
6 +})
plugins/viewer/example.htmlView
@@ -1,0 +1,28 @@
1 +<!DOCTYPE html>
2 +<html>
3 +<head>
4 +<meta charset=utf-8>
5 +<title>ssb-viewer</title>
6 +<meta name=viewport content="width=device-width,initial-scale=1">
7 +<link rel="stylesheet" href="static/nicer.css">
8 +<style>
9 +body {
10 + background-color: #f3f3f3;
11 +}
12 +#demo .ssb-thread {
13 + background-color: white;
14 + padding: 1ex 2em;
15 +}
16 +</style>
17 +</head>
18 +<body id="demo">
19 + <center>
20 + <h1>Embed Example</h1>
21 + </center>
22 + <script src="http://localhost:8807/%fU0K0uC4orV6258+dGuAcA95TSfc9PrTtKIG9uL3rWI=.sha256.js"></script>
23 + <center>
24 + <p>the end</p>
25 + </center>
26 +</div>
27 +</body>
28 +</html>
plugins/viewer/index.jsView
@@ -1,0 +1,636 @@
1 +var fs = require('fs')
2 +var http = require('http')
3 +var qs = require('querystring')
4 +var path = require('path')
5 +var crypto = require('crypto')
6 +var cat = require('pull-cat')
7 +var pull = require('pull-stream')
8 +var paramap = require('pull-paramap')
9 +var marked = require('ssb-marked')
10 +var sort = require('ssb-sort')
11 +var toPull = require('stream-to-pull-stream')
12 +var memo = require('asyncmemo')
13 +var lru = require('lrucache')
14 +var htime = require('human-time')
15 +var emojis = require('emoji-named-characters')
16 +var serveEmoji = require('emoji-server')()
17 +
18 +var emojiDir = path.join(require.resolve('emoji-named-characters'), '../pngs')
19 +var appHash = hash([fs.readFileSync(__filename)])
20 +
21 +var urlIdRegex = /^(?:\/(([%&@]|%25|%26|%40)(?:[A-Za-z0-9\/+]|%2[Ff]|%2[Bb]){43}(?:=|%3[Dd])\.(?:sha256|ed25519))(?:\.([^?]*))?|(\/.*?))(?:\?(.*))?$/
22 +
23 +function MdRenderer(opts) {
24 + marked.Renderer.call(this, {})
25 + this.opts = opts
26 +}
27 +MdRenderer.prototype = new marked.Renderer()
28 +
29 +MdRenderer.prototype.urltransform = function (href) {
30 + if (!href) return false
31 + switch (href[0]) {
32 + case '#': return '/channel/' + href.slice(1)
33 + case '%': return this.opts.msg_base + encodeURIComponent(href)
34 + case '@': return this.opts.feed_base + encodeURIComponent(href)
35 + case '&': return this.opts.blob_base + encodeURIComponent(href)
36 + }
37 + if (href.indexOf('javascript:') === 0) return false
38 + return href
39 +}
40 +
41 +MdRenderer.prototype.image = function (href, title, text) {
42 + return '<img src="' + this.opts.img_base + escape(href) + '"'
43 + + ' alt="' + text + '"'
44 + + (title ? ' title="' + title + '"' : '')
45 + + (this.options.xhtml ? '/>' : '>')
46 +}
47 +
48 +function renderEmoji(emoji) {
49 + var opts = this.renderer.opts
50 + return emoji in emojis ?
51 + '<img src="' + opts.emoji_base + escape(emoji) + '.png"'
52 + + ' alt=":' + escape(emoji) + ':"'
53 + + ' title=":' + escape(emoji) + ':"'
54 + + ' class="ssb-emoji" height="16" width="16">'
55 + : ':' + emoji + ':'
56 +}
57 +
58 +exports.name = 'viewer'
59 +exports.manifest = {}
60 +exports.version = '0.0.0'
61 +
62 +exports.init = function (sbot, config) {
63 + var conf = config.viewer || {}
64 + var port = conf.port || 3307
65 + var host = conf.host || config.host || '::'
66 +
67 + var base = conf.base || '/'
68 + var defaultOpts = {
69 + msg_base: conf.msg_base || base,
70 + feed_base: conf.feed_base || base,
71 + blob_base: conf.blob_base || base,
72 + img_base: conf.img_base || base,
73 + emoji_base: conf.emoji_base || (base + 'emoji/'),
74 + }
75 +
76 + var getMsg = memo({cache: lru(100)}, getMsgWithValue, sbot)
77 + var getAbout = memo({cache: lru(100)}, require('./lib/about'), sbot)
78 +
79 + http.createServer(serve).listen(port, host, function () {
80 + console.log('[viewer] Listening on http://' + host + ':' + port)
81 + })
82 +
83 + function serve(req, res) {
84 + if (req.method !== 'GET' && req.method !== 'HEAD') {
85 + return respond(res, 405, 'Method must be GET or HEAD')
86 + }
87 +
88 + var m = urlIdRegex.exec(req.url)
89 +
90 + if (req.url.startsWith('/user-feed/')) return serveUserFeed(req, res, m[4])
91 + else if (req.url.startsWith('/channel/')) return serveChannel(req, res, m[4])
92 +
93 + if (m[2] && m[2].length === 3) {
94 + m[1] = decodeURIComponent(m[1])
95 + m[2] = m[1][0]
96 + }
97 + switch (m[2]) {
98 + case '%': return serveId(req, res, m[1], m[3], m[5])
99 + case '@': return serveFeed(req, res, m[1], m[3], m[5])
100 + case '&': return serveBlob(req, res, sbot, m[1])
101 + default: return servePath(req, res, m[4])
102 + }
103 + }
104 +
105 + function serveFeed(req, res, feedId) {
106 + console.log("serving feed: " + feedId)
107 +
108 + var opts = defaultOpts
109 +
110 + opts.marked = {
111 + gfm: true,
112 + mentions: true,
113 + tables: true,
114 + breaks: true,
115 + pedantic: false,
116 + sanitize: true,
117 + smartLists: true,
118 + smartypants: false,
119 + emoji: renderEmoji,
120 + renderer: new MdRenderer(opts)
121 + }
122 +
123 + getAbout(feedId, function (err, about) {
124 + if (err) return cb(err)
125 +
126 + pull(
127 + sbot.createUserStream({ id: feedId, reverse: true, limit: 100 }),
128 + // sbot.createUserStream({ id: feedId, reverse: true }),
129 + pull.collect(function (err, logs) {
130 + if (err) return respond(res, 500, err.stack || err)
131 + res.writeHead(200, {
132 + 'Content-Type': ctype("html")
133 + })
134 + pull(
135 + pull.values(logs),
136 + paramap(addAuthorAbout, 8),
137 + paramap(addFollowAbout, 8),
138 + paramap(addVoteMessage, 8),
139 + pull(renderThread(opts), wrapPage(about.name)),
140 + toPull(res, function (err) {
141 + if (err) console.error('[viewer]', err)
142 + })
143 + )
144 + })
145 + )
146 + })
147 + }
148 +
149 + function serveUserFeed(req, res, url) {
150 + var feedId = url.substring(url.lastIndexOf('user-feed/')+10, 100)
151 + console.log("serving user feed: " + feedId)
152 +
153 + var following = []
154 + var channelSubscriptions = []
155 +
156 + getAbout(feedId, function (err, about) {
157 + pull(
158 + sbot.createUserStream({ id: feedId }),
159 + pull.filter((msg) => {
160 + return !msg.value ||
161 + msg.value.content.type == 'contact'
162 + }),
163 + pull.collect(function (err, msgs) {
164 + msgs.forEach((msg) => {
165 + if (msg.value.content.type == 'contact')
166 + {
167 + if (msg.value.content.following)
168 + following[msg.value.content.contact] = 1
169 + else
170 + delete following[msg.value.content.contact]
171 + }
172 + })
173 +
174 + serveFeeds(req, res, following, channelSubscriptions, feedId, 'user feed ' + about.name)
175 + })
176 + )
177 + })
178 + }
179 +
180 + function serveFeeds(req, res, following, channelSubscriptions, feedId, name) {
181 + var opts = defaultOpts
182 +
183 + opts.marked = {
184 + gfm: true,
185 + mentions: true,
186 + tables: true,
187 + breaks: true,
188 + pedantic: false,
189 + sanitize: true,
190 + smartLists: true,
191 + smartypants: false,
192 + emoji: renderEmoji,
193 + renderer: new MdRenderer(opts)
194 + }
195 +
196 + pull(
197 + sbot.createLogStream({ reverse: true, limit: 2500 }),
198 + pull.filter((msg) => {
199 + return !msg.value ||
200 + (msg.value.author in following ||
201 + msg.value.content.channel in channelSubscriptions)
202 + }),
203 + pull.take(100),
204 + pull.collect(function (err, logs) {
205 + if (err) return respond(res, 500, err.stack || err)
206 + res.writeHead(200, {
207 + 'Content-Type': ctype("html")
208 + })
209 + pull(
210 + pull.values(logs),
211 + paramap(addAuthorAbout, 8),
212 + paramap(addFollowAbout, 8),
213 + paramap(addVoteMessage, 8),
214 + pull(renderThread(opts), wrapPage(name)),
215 + toPull(res, function (err) {
216 + if (err) console.error('[viewer]', err)
217 + })
218 + )
219 + })
220 + )
221 + }
222 +
223 + function serveChannel(req, res, url) {
224 + var channelId = url.substring(url.lastIndexOf('channel/')+8, 100)
225 + console.log("serving channel: " + channelId)
226 +
227 + var opts = defaultOpts
228 +
229 + opts.marked = {
230 + gfm: true,
231 + mentions: true,
232 + tables: true,
233 + breaks: true,
234 + pedantic: false,
235 + sanitize: true,
236 + smartLists: true,
237 + smartypants: false,
238 + emoji: renderEmoji,
239 + renderer: new MdRenderer(opts)
240 + }
241 +
242 + pull(
243 + sbot.query.read({ limit: 500, reverse: true, query: [{$filter: { value: { content: { channel: channelId }}}}]}),
244 + pull.collect(function (err, logs) {
245 + if (err) return respond(res, 500, err.stack || err)
246 + res.writeHead(200, {
247 + 'Content-Type': ctype("html")
248 + })
249 + pull(
250 + pull.values(logs),
251 + paramap(addAuthorAbout, 8),
252 + paramap(addVoteMessage, 8),
253 + pull(renderThread(opts), wrapPage('#' + channelId)),
254 + toPull(res, function (err) {
255 + if (err) console.error('[viewer]', err)
256 + })
257 + )
258 + })
259 + )
260 + }
261 +
262 + function addFollowAbout(msg, cb) {
263 + if (msg.value.content.contact)
264 + getAbout(msg.value.content.contact, function (err, about) {
265 + if (err) return cb(err)
266 + msg.value.content.contactAbout = about
267 + cb(null, msg)
268 + })
269 + else
270 + cb(null, msg)
271 + }
272 +
273 + function addVoteMessage(msg, cb) {
274 + if (msg.value.content.type == 'vote' && msg.value.content.vote.link[0] == '%')
275 + getMsg(msg.value.content.vote.link, function (err, linkedMsg) {
276 + if (linkedMsg)
277 + msg.value.content.vote.linkedText = linkedMsg.value.content.text
278 + cb(null, msg)
279 + })
280 + else
281 + cb(null, msg)
282 + }
283 +
284 + function serveId(req, res, id, ext, query) {
285 + var q = query ? qs.parse(query) : {}
286 + var includeRoot = !('noroot' in q)
287 + var base = q.base || conf.base
288 + var baseToken
289 + if (!base) {
290 + if (ext === 'js') base = baseToken = '__BASE_' + Math.random() + '_'
291 + else base = '/'
292 + }
293 + var opts = {
294 + base: base,
295 + base_token: baseToken,
296 + msg_base: q.msg_base || conf.msg_base || base,
297 + feed_base: q.feed_base || conf.feed_base || base,
298 + blob_base: q.blob_base || conf.blob_base || base,
299 + img_base: q.img_base || conf.img_base || base,
300 + emoji_base: q.emoji_base || conf.emoji_base || (base + 'emoji/'),
301 + }
302 + opts.marked = {
303 + gfm: true,
304 + mentions: true,
305 + tables: true,
306 + breaks: true,
307 + pedantic: false,
308 + sanitize: true,
309 + smartLists: true,
310 + smartypants: false,
311 + emoji: renderEmoji,
312 + renderer: new MdRenderer(opts)
313 + }
314 +
315 + var format = formatMsgs(id, ext, opts)
316 + if (format === null) return respond(res, 415, 'Invalid format')
317 +
318 + pull(
319 + sbot.links({dest: id, values: true, rel: 'root'}),
320 + includeRoot && prepend(getMsg, id),
321 + pull.unique('key'),
322 + pull.collect(function (err, links) {
323 + if (err) return respond(res, 500, err.stack || err)
324 + var etag = hash(sort.heads(links).concat(appHash, ext, qs))
325 + if (req.headers['if-none-match'] === etag) return respond(res, 304)
326 + res.writeHead(200, {
327 + 'Content-Type': ctype(ext),
328 + 'etag': etag
329 + })
330 + pull(
331 + pull.values(sort(links)),
332 + paramap(addAuthorAbout, 8),
333 + format,
334 + toPull(res, function (err) {
335 + if (err) console.error('[viewer]', err)
336 + })
337 + )
338 + })
339 + )
340 + }
341 +
342 + function addAuthorAbout(msg, cb) {
343 + getAbout(msg.value.author, function (err, about) {
344 + if (err) return cb(err)
345 + msg.author = about
346 + cb(null, msg)
347 + })
348 + }
349 +}
350 +
351 +function serveBlob(req, res, sbot, id) {
352 + if (req.headers['if-none-match'] === id) return respond(res, 304)
353 + sbot.blobs.has(id, function (err, has) {
354 + if (err) {
355 + if (/^invalid/.test(err.message)) return respond(res, 400, err.message)
356 + else return respond(res, 500, err.message || err)
357 + }
358 + if (!has) return respond(res, 404, 'Not found')
359 + res.writeHead(200, {
360 + 'Cache-Control': 'public, max-age=315360000',
361 + 'etag': id
362 + })
363 + pull(
364 + sbot.blobs.get(id),
365 + toPull(res, function (err) {
366 + if (err) console.error('[viewer]', err)
367 + })
368 + )
369 + })
370 +}
371 +
372 +function getMsgWithValue(sbot, id, cb) {
373 + sbot.get(id, function (err, value) {
374 + if (err) return cb(err)
375 + cb(null, {key: id, value: value})
376 + })
377 +}
378 +
379 +function escape(str) {
380 + return String(str)
381 + .replace(/&/g, '&amp;')
382 + .replace(/</g, '&lt;')
383 + .replace(/>/g, '&gt;')
384 + .replace(/"/g, '&quot;')
385 +}
386 +
387 +function respond(res, status, message) {
388 + res.writeHead(status)
389 + res.end(message)
390 +}
391 +
392 +function ctype(name) {
393 + switch (name && /[^.\/]*$/.exec(name)[0] || 'html') {
394 + case 'html': return 'text/html'
395 + case 'js': return 'text/javascript'
396 + case 'css': return 'text/css'
397 + case 'json': return 'application/json'
398 + }
399 +}
400 +
401 +function servePath(req, res, url) {
402 + switch (url) {
403 + case '/robots.txt': return res.end('User-agent: *')
404 + }
405 + var m = /^(\/?[^\/]*)(\/.*)?$/.exec(url)
406 + switch (m[1]) {
407 + case '/static': return serveStatic(req, res, m[2])
408 + case '/emoji': return serveEmoji(req, res, m[2])
409 + }
410 + return respond(res, 404, 'Not found')
411 +}
412 +
413 +function ifModified(req, lastMod) {
414 + var ifModSince = req.headers['if-modified-since']
415 + if (!ifModSince) return false
416 + var d = new Date(ifModSince)
417 + return d && Math.floor(d/1000) >= Math.floor(lastMod/1000)
418 +}
419 +
420 +function serveStatic(req, res, file) {
421 + serveFile(req, res, path.join(__dirname, 'static', file))
422 +}
423 +
424 +function serveFile(req, res, file) {
425 + fs.stat(file, function (err, stat) {
426 + if (err && err.code === 'ENOENT') return respond(res, 404, 'Not found')
427 + if (err) return respond(res, 500, err.stack || err)
428 + if (!stat.isFile()) return respond(res, 403, 'May only load files')
429 + if (ifModified(req, stat.mtime)) return respond(res, 304, 'Not modified')
430 + res.writeHead(200, {
431 + 'Content-Type': ctype(file),
432 + 'Content-Length': stat.size,
433 + 'Last-Modified': stat.mtime.toGMTString()
434 + })
435 + fs.createReadStream(file).pipe(res)
436 + })
437 +}
438 +
439 +function prepend(fn, arg) {
440 + return function (read) {
441 + return function (abort, cb) {
442 + if (fn && !abort) {
443 + var _fn = fn
444 + fn = null
445 + return _fn(arg, function (err, value) {
446 + if (err) return read(err, function (err) {
447 + cb(err || true)
448 + })
449 + cb(null, value)
450 + })
451 + }
452 + read(abort, cb)
453 + }
454 + }
455 +}
456 +
457 +function formatMsgs(id, ext, opts) {
458 + switch (ext || 'html') {
459 + case 'html': return pull(renderThread(opts), wrapPage(id))
460 + case 'js': return pull(renderThread(opts), wrapJSEmbed(opts))
461 + case 'json': return wrapJSON()
462 + default: return null
463 + }
464 +}
465 +
466 +function wrap(before, after) {
467 + return function (read) {
468 + return cat([pull.once(before), read, pull.once(after)])
469 + }
470 +}
471 +
472 +function renderThread(opts) {
473 + return pull(
474 + pull.map(renderMsg.bind(this, opts)),
475 + wrap('<div class="ssb-thread">', '</div>')
476 + )
477 +}
478 +
479 +function wrapPage(id) {
480 + return wrap('<!doctype html><html><head>'
481 + + '<meta charset=utf-8>'
482 + + '<title>' + id + ' | ssb-viewer</title>'
483 + + '<meta name=viewport content="width=device-width,initial-scale=1">'
484 + + '<link rel=stylesheet href="/static/base.css">'
485 + + '<link rel=stylesheet href="/static/nicer.css">'
486 + + '<link rel=stylesheet href="http://evbogue.com/style.css">'
487 + + '</head><body>',
488 + '</body></html>'
489 + )
490 +}
491 +
492 +function wrapJSON() {
493 + var first = true
494 + return pull(
495 + pull.map(JSON.stringify),
496 + join(','),
497 + wrap('[', ']')
498 + )
499 +}
500 +
501 +function wrapJSEmbed(opts) {
502 + return pull(
503 + wrap('<link rel=stylesheet href="' + opts.base + 'static/base.css">', ''),
504 + pull.map(docWrite),
505 + opts.base_token && rewriteBase(new RegExp(opts.base_token, 'g'))
506 + )
507 +}
508 +
509 +
510 +function rewriteBase(token) {
511 + // detect the origin of the script and rewrite the js/html to use it
512 + return pull(
513 + replace(token, '" + SSB_VIEWER_ORIGIN + "/'),
514 + wrap('var SSB_VIEWER_ORIGIN = (function () {'
515 + + 'var scripts = document.getElementsByTagName("script")\n'
516 + + 'var script = scripts[scripts.length-1]\n'
517 + + 'if (!script) return location.origin\n'
518 + + 'return script.src.replace(/\\/%.*$/, "")\n'
519 + + '}())\n', '')
520 + )
521 +}
522 +
523 +function join(delim) {
524 + var first = true
525 + return pull.map(function (val) {
526 + if (!first) return delim + String(val)
527 + first = false
528 + return val
529 + })
530 +}
531 +
532 +function replace(re, rep) {
533 + return pull.map(function (val) {
534 + return String(val).replace(re, rep)
535 + })
536 +}
537 +
538 +function docWrite(str) {
539 + return 'document.write(' + JSON.stringify(str) + ')\n'
540 +}
541 +
542 +function hash(arr) {
543 + return arr.reduce(function (hash, item) {
544 + return hash.update(String(item))
545 + }, crypto.createHash('sha256')).digest('base64')
546 +}
547 +
548 +function renderMsg(opts, msg) {
549 + var c = msg.value.content || {}
550 + var name = encodeURIComponent(msg.key)
551 + return '<div class="ssb-message" id="' + name + '">'
552 + + '<img class="ssb-avatar-image" alt=""'
553 + + ' src="' + opts.img_base + escape(msg.author.image) + '"'
554 + + ' height="32" width="32">'
555 + + '<a class="ssb-avatar-name"'
556 + + ' href="/' + escape(msg.value.author) + '"'
557 + + '>' + msg.author.name + '</a>'
558 + + msgTimestamp(msg, name)
559 + + render(opts, c)
560 + + '</div>'
561 +}
562 +
563 +function msgTimestamp(msg, name) {
564 + var date = new Date(msg.value.timestamp)
565 + return '<time class="ssb-timestamp" datetime="' + date.toISOString() + '">'
566 + + '<a href="#' + name + '">'
567 + + formatDate(date) + '</a></time>'
568 +}
569 +
570 +function formatDate(date) {
571 + // return date.toISOString().replace('T', ' ')
572 + return htime(date)
573 +}
574 +
575 +function render(opts, c)
576 +{
577 + if (c.type === 'post') {
578 + var channel = c.channel ? ' in <a href="/channel/' + c.channel + '">#' + c.channel + '</a>' : ''
579 + return channel + renderPost(opts, c)
580 + } else if (c.type == 'vote' && c.vote.expression == 'Dig') {
581 + var channel = c.channel ? ' in <a href="/channel/' + c.channel + '">#' + c.channel + '</a>' : ''
582 + var linkedText = 'this'
583 + if (typeof c.vote.linkedText != 'undefined')
584 + linkedText = c.vote.linkedText.substring(0, 75)
585 + return ' dug ' + '<a href="/' + c.vote.link + '">' + linkedText + '</a>' + channel
586 + }
587 + else if (c.type == 'vote') {
588 + var linkedText = 'this'
589 + if (typeof c.vote.linkedText != 'undefined')
590 + linkedText = c.vote.linkedText.substring(0, 75)
591 + return ' voted <a href="/' + c.vote.link + '">' + linkedText + '</a>'
592 + }
593 + else if (c.type == 'contact' && c.following) {
594 + var name = c.contact
595 + if (typeof c.contactAbout != 'undefined')
596 + name = c.contactAbout.name
597 + return ' followed <a href="/' + c.contact + '">' + name + "</a>"
598 + }
599 + else if (c.type == 'contact' && !c.following) {
600 + var name = c.contact
601 + if (typeof c.contactAbout != 'undefined')
602 + name = c.contactAbout.name
603 + return ' unfollowed <a href="/' + c.contact + '">' + name + "</a>"
604 + }
605 + else if (typeof c == 'string')
606 + return ' sent a private message '
607 + else if (c.type == 'about')
608 + return ' changed something in about'
609 + else if (c.type == 'issue')
610 + return ' created an issue'
611 + else if (c.type == 'git-update')
612 + return ' pushed to <a href="http://gitmx.com/'+ encodeURIComponent(c.repo) + '/">' + c.repo.substring(0, 10)+'...' + '</a><ul>' +
613 + (c.commits != undefined ?
614 + c.commits.map(com => { return '<li><code>' + com.sha1.substring(0, 10) + '</code> "' + com.title + '"</li>' }).join('') : "</ul>")
615 + else if (c.type == 'ssb-dns')
616 + return ' updated dns'
617 + else if (c.type == 'pub')
618 + return ' connected to a pub'
619 + else if (c.type == 'channel' && c.subscribed)
620 + return ' subscribed to channel <a href="/channel/' + c.channel + '">#' + c.channel + "</a>"
621 + else if (c.type == 'channel' && !c.subscribed)
622 + return ' unsubscribed from channel <a href="/channel/' + c.channel + '">#' + c.channel + "</a>"
623 + else
624 + return renderDefault(c)
625 +}
626 +
627 +function renderPost(opts, c) {
628 + if (c.root)
629 + return '<div class="ssb-post"> Re: <a href="/' + encodeURIComponent(c.root) + '">' + c.root.substring(0, 10) + '</a><br />' + marked(c.text, opts.marked) + '</div>'
630 + else return '<div class="ssb-post">' + marked(c.text, opts.marked) + '</div>'
631 +}
632 +
633 +function renderDefault(c) {
634 + return '<pre>' + JSON.stringify(c, 0, 2) + '</pre>'
635 +}
636 +
plugins/viewer/lib/about.jsView
@@ -1,0 +1,31 @@
1 +var pull = require('pull-stream')
2 +var sort = require('ssb-sort')
3 +
4 +function linkDest(val) {
5 + return typeof val === 'string' ? val : val && val.link
6 +}
7 +
8 +function reduceAbout(about, msg) {
9 + var c = msg.value.content
10 + if (!c) return about
11 + if (c.name) about.name = c.name.replace(/^@?/, '@')
12 + if (c.image) about.image = linkDest(c.image)
13 + return about
14 +}
15 +
16 +module.exports = function (sbot, id, cb) {
17 + var about = {}
18 + pull(
19 + sbot.links({
20 + rel: 'about',
21 + dest: id,
22 + values: true,
23 + }),
24 + pull.collect(function (err, msgs) {
25 + if (err) return cb(err)
26 + cb(null, sort(msgs).reduce(reduceAbout, {
27 + name: String(id).substr(0, 10) + '…',
28 + }))
29 + })
30 + )
31 +}
plugins/viewer/static/base.cssView
@@ -1,0 +1,15 @@
1 +.ssb-timestamp {
2 + float: right;
3 +}
4 +
5 +.ssb-avatar-image {
6 + vertical-align: top;
7 +}
8 +
9 +.ssb-avatar-name {
10 + margin-left: 1ex;
11 +}
12 +
13 +.ssb-post img {
14 + max-width: 100%;
15 +}
plugins/viewer/static/nicer.cssView
@@ -1,0 +1,47 @@
1 +* {
2 + word-wrap: break-word;
3 +}
4 +
5 +
6 +pre {
7 + height: auto;
8 + max-height: 200px;
9 + overflow: auto;
10 + background-color: #eeeeee;
11 + word-break: normal !important;
12 + word-wrap: normal !important;
13 + white-space: pre !important;
14 +}
15 +
16 +.ssb-message {
17 + border-bottom: 1px solid #ddd;
18 + margin: 1em 0;
19 +}
20 +
21 +h1, h2, h3, h4 {
22 + line-height: 1.2;
23 +}
24 +
25 +.ssb-thread {
26 + //width: 80ex;
27 + max-width: 100%;
28 + min-width: 57%;
29 + margin: 0 auto;
30 + line-height: 1.5;
31 + font-family: sans-serif;
32 +}
33 +
34 +.ssb-message:target {
35 + background-color: #fcfae8;
36 + padding: 1em 1em 0;
37 + margin: -1em -1em 0;
38 +}
39 +.ssb-message:target:first-child {
40 + margin-top: 0;
41 +}
42 +
43 +.ssb-emoji {
44 + height: 1em;
45 + width: 1em;
46 + vertical-align: top;
47 +}
plugins/viewer/yarn.lockView
@@ -1,0 +1,603 @@
1 +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 +# yarn lockfile v1
3 +
4 +
5 +asyncmemo@^1.0.0:
6 + version "1.0.0"
7 + resolved "https://registry.yarnpkg.com/asyncmemo/-/asyncmemo-1.0.0.tgz#ef249dc869d6c07e7dfd4a22c8a18850bb39d7f1"
8 +
9 +balanced-match@^1.0.0:
10 + version "1.0.0"
11 + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
12 +
13 +brace-expansion@^1.1.7:
14 + version "1.1.8"
15 + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
16 + dependencies:
17 + balanced-match "^1.0.0"
18 + concat-map "0.0.1"
19 +
20 +chloride-test@^1.1.0:
21 + version "1.2.2"
22 + resolved "https://registry.yarnpkg.com/chloride-test/-/chloride-test-1.2.2.tgz#178686a85e9278045112e96e8c791793f9a10aea"
23 + dependencies:
24 + json-buffer "^2.0.11"
25 +
26 +chloride@^2.2.1, chloride@^2.2.7:
27 + version "2.2.7"
28 + resolved "https://registry.yarnpkg.com/chloride/-/chloride-2.2.7.tgz#0e6a9d11894abe4a44911d3988da192e2208b786"
29 + dependencies:
30 + is-electron "^2.0.0"
31 + sodium-browserify "^1.0.3"
32 + sodium-browserify-tweetnacl "^0.2.0"
33 + sodium-chloride "^1.1.0"
34 + optionalDependencies:
35 + sodium-native "^1.10.0"
36 +
37 +concat-map@0.0.1:
38 + version "0.0.1"
39 + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
40 +
41 +deep-equal@~1.0.0, deep-equal@~1.0.1:
42 + version "1.0.1"
43 + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
44 +
45 +deep-extend@^0.4.0, deep-extend@~0.4.0:
46 + version "0.4.2"
47 + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
48 +
49 +define-properties@^1.1.2:
50 + version "1.1.2"
51 + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
52 + dependencies:
53 + foreach "^2.0.5"
54 + object-keys "^1.0.8"
55 +
56 +defined@~1.0.0:
57 + version "1.0.0"
58 + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
59 +
60 +ed2curve@^0.1.4:
61 + version "0.1.4"
62 + resolved "https://registry.yarnpkg.com/ed2curve/-/ed2curve-0.1.4.tgz#94a44248bb87da35db0eff7af0aa576168117f59"
63 + dependencies:
64 + tweetnacl "0.x.x"
65 +
66 +emoji-named-characters@^1.0.2, emoji-named-characters@~1.0.2:
67 + version "1.0.2"
68 + resolved "https://registry.yarnpkg.com/emoji-named-characters/-/emoji-named-characters-1.0.2.tgz#cdeb36d0e66002c4b9d7bf1dfbc3a199fb7d409b"
69 +
70 +emoji-server@^1.0.0:
71 + version "1.0.0"
72 + resolved "https://registry.yarnpkg.com/emoji-server/-/emoji-server-1.0.0.tgz#d063cfee9af118cc5aeefbc2e9b3dd5085815c63"
73 + dependencies:
74 + emoji-named-characters "~1.0.2"
75 +
76 +es-abstract@^1.5.0:
77 + version "1.7.0"
78 + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c"
79 + dependencies:
80 + es-to-primitive "^1.1.1"
81 + function-bind "^1.1.0"
82 + is-callable "^1.1.3"
83 + is-regex "^1.0.3"
84 +
85 +es-to-primitive@^1.1.1:
86 + version "1.1.1"
87 + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
88 + dependencies:
89 + is-callable "^1.1.1"
90 + is-date-object "^1.0.1"
91 + is-symbol "^1.0.1"
92 +
93 +explain-error@^1.0.1:
94 + version "1.0.4"
95 + resolved "https://registry.yarnpkg.com/explain-error/-/explain-error-1.0.4.tgz#a793d3ac0cad4c6ab571e9968fbbab6cb2532929"
96 +
97 +for-each@~0.3.2:
98 + version "0.3.2"
99 + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4"
100 + dependencies:
101 + is-function "~1.0.0"
102 +
103 +foreach@^2.0.5:
104 + version "2.0.5"
105 + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
106 +
107 +fs.realpath@^1.0.0:
108 + version "1.0.0"
109 + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
110 +
111 +function-bind@^1.0.2, function-bind@^1.1.0, function-bind@~1.1.0:
112 + version "1.1.0"
113 + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
114 +
115 +glob@~7.1.1:
116 + version "7.1.2"
117 + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
118 + dependencies:
119 + fs.realpath "^1.0.0"
120 + inflight "^1.0.4"
121 + inherits "2"
122 + minimatch "^3.0.4"
123 + once "^1.3.0"
124 + path-is-absolute "^1.0.0"
125 +
126 +has@^1.0.1, has@~1.0.1:
127 + version "1.0.1"
128 + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
129 + dependencies:
130 + function-bind "^1.0.2"
131 +
132 +human-time@^0.0.1:
133 + version "0.0.1"
134 + resolved "https://registry.yarnpkg.com/human-time/-/human-time-0.0.1.tgz#280d0336379199306b2e1518e3d5f6381cb8507d"
135 +
136 +increment-buffer@~1.0.0:
137 + version "1.0.1"
138 + resolved "https://registry.yarnpkg.com/increment-buffer/-/increment-buffer-1.0.1.tgz#65076d75189d808b39ad13ab5b958e05216f9e0d"
139 +
140 +inflight@^1.0.4:
141 + version "1.0.6"
142 + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
143 + dependencies:
144 + once "^1.3.0"
145 + wrappy "1"
146 +
147 +inherits@2, inherits@^2.0.1, inherits@~2.0.3:
148 + version "2.0.3"
149 + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
150 +
151 +ini@~1.3.0:
152 + version "1.3.4"
153 + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
154 +
155 +ip@^1.1.2, ip@^1.1.3:
156 + version "1.1.5"
157 + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
158 +
159 +ip@~0.3.2:
160 + version "0.3.3"
161 + resolved "https://registry.yarnpkg.com/ip/-/ip-0.3.3.tgz#8ee8309e92f0b040d287f72efaca1a21702d3fb4"
162 +
163 +is-callable@^1.1.1, is-callable@^1.1.3:
164 + version "1.1.3"
165 + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
166 +
167 +is-date-object@^1.0.1:
168 + version "1.0.1"
169 + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
170 +
171 +is-electron@^2.0.0:
172 + version "2.0.0"
173 + resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.0.0.tgz#c82d3599640f7df91c84eaaee76bc56713c6ac79"
174 +
175 +is-function@~1.0.0:
176 + version "1.0.1"
177 + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
178 +
179 +is-regex@^1.0.3:
180 + version "1.0.4"
181 + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
182 + dependencies:
183 + has "^1.0.1"
184 +
185 +is-symbol@^1.0.1:
186 + version "1.0.1"
187 + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
188 +
189 +is-valid-domain@~0.0.1:
190 + version "0.0.2"
191 + resolved "https://registry.yarnpkg.com/is-valid-domain/-/is-valid-domain-0.0.2.tgz#3e7a9423ff7c3b2fe11663afbd6d3837a251fb77"
192 +
193 +json-buffer@^2.0.11:
194 + version "2.0.11"
195 + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-2.0.11.tgz#3e441fda3098be8d1e3171ad591bc62a33e2d55f"
196 +
197 +libsodium-wrappers@^0.2.9:
198 + version "0.2.12"
199 + resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.2.12.tgz#51fb50774b8edc517927b307b812a46c3a467e1e"
200 + dependencies:
201 + libsodium "0.2.12"
202 +
203 +libsodium@0.2.12:
204 + version "0.2.12"
205 + resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.2.12.tgz#83083564dcf089cb82a5035be92ba5d224a2ccde"
206 +
207 +looper@^3.0.0, looper@~3.0.0:
208 + version "3.0.0"
209 + resolved "https://registry.yarnpkg.com/looper/-/looper-3.0.0.tgz#2efa54c3b1cbaba9b94aee2e5914b0be57fbb749"
210 +
211 +looper@^4.0.0:
212 + version "4.0.0"
213 + resolved "https://registry.yarnpkg.com/looper/-/looper-4.0.0.tgz#7706aded59a99edca06e6b54bb86c8ec19c95155"
214 +
215 +lrucache@^1.0.2:
216 + version "1.0.2"
217 + resolved "https://registry.yarnpkg.com/lrucache/-/lrucache-1.0.2.tgz#066e7072a2b3f8647993abc017d735a559b0ee5a"
218 +
219 +minimatch@^3.0.4:
220 + version "3.0.4"
221 + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
222 + dependencies:
223 + brace-expansion "^1.1.7"
224 +
225 +minimist@0.0.8:
226 + version "0.0.8"
227 + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
228 +
229 +minimist@^1.2.0, minimist@~1.2.0:
230 + version "1.2.0"
231 + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
232 +
233 +mkdirp@~0.5.0:
234 + version "0.5.1"
235 + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
236 + dependencies:
237 + minimist "0.0.8"
238 +
239 +multiserver@^1.7.0:
240 + version "1.9.1"
241 + resolved "https://registry.yarnpkg.com/multiserver/-/multiserver-1.9.1.tgz#f7dfa757aaaf6de2d64aa6853adbdb1ca884ab67"
242 + dependencies:
243 + pull-cat "~1.1.5"
244 + pull-stream "^3.4.3"
245 + pull-ws "^3.2.7"
246 + secret-handshake "^1.1.5"
247 + separator-escape "0.0.0"
248 + socks "1.1.9"
249 + stream-to-pull-stream "^1.7.2"
250 +
251 +muxrpc@^6.3.3:
252 + version "6.3.3"
253 + resolved "https://registry.yarnpkg.com/muxrpc/-/muxrpc-6.3.3.tgz#68ad940ef7f601df9da9ef2211b0a173d5286f9d"
254 + dependencies:
255 + explain-error "^1.0.1"
256 + packet-stream "~2.0.0"
257 + packet-stream-codec "^1.1.1"
258 + pull-goodbye "~0.0.1"
259 + pull-stream "^3.2.3"
260 +
261 +nan@^2.4.0:
262 + version "2.6.2"
263 + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
264 +
265 +node-gyp-build@^3.0.0:
266 + version "3.2.0"
267 + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.2.0.tgz#283be074924aabb9240ce3f880c7ca0c8e7a00dc"
268 +
269 +non-private-ip@^1.2.1:
270 + version "1.4.2"
271 + resolved "https://registry.yarnpkg.com/non-private-ip/-/non-private-ip-1.4.2.tgz#ed51fa7bf7e91a9c632394f10547b6a395e8bead"
272 + dependencies:
273 + ip "~0.3.2"
274 +
275 +object-inspect@~1.2.1:
276 + version "1.2.2"
277 + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.2.2.tgz#c82115e4fcc888aea14d64c22e4f17f6a70d5e5a"
278 +
279 +object-keys@^1.0.8:
280 + version "1.0.11"
281 + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
282 +
283 +once@^1.3.0:
284 + version "1.4.0"
285 + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
286 + dependencies:
287 + wrappy "1"
288 +
289 +options@>=0.0.5:
290 + version "0.0.6"
291 + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f"
292 +
293 +os-homedir@^1.0.1:
294 + version "1.0.2"
295 + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
296 +
297 +packet-stream-codec@^1.1.1:
298 + version "1.1.2"
299 + resolved "https://registry.yarnpkg.com/packet-stream-codec/-/packet-stream-codec-1.1.2.tgz#79b302fc144cdfbb4ab6feba7040e6a5d99c79c7"
300 + dependencies:
301 + pull-reader "^1.2.4"
302 + pull-through "^1.0.17"
303 +
304 +packet-stream@~2.0.0:
305 + version "2.0.2"
306 + resolved "https://registry.yarnpkg.com/packet-stream/-/packet-stream-2.0.2.tgz#b90b7f9bab4a962422cbc9cb24719c353e493267"
307 +
308 +path-is-absolute@^1.0.0:
309 + version "1.0.1"
310 + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
311 +
312 +private-box@^0.2.1:
313 + version "0.2.1"
314 + resolved "https://registry.yarnpkg.com/private-box/-/private-box-0.2.1.tgz#1df061afca5b3039c7feaadd0daf0f56f07e3ec0"
315 + dependencies:
316 + chloride "^2.2.1"
317 +
318 +pull-box-stream@^1.0.13:
319 + version "1.0.13"
320 + resolved "https://registry.yarnpkg.com/pull-box-stream/-/pull-box-stream-1.0.13.tgz#c3e240398eab3f5951b2ed1078c5988bf7a0a2b9"
321 + dependencies:
322 + chloride "^2.2.7"
323 + increment-buffer "~1.0.0"
324 + pull-reader "^1.2.5"
325 + pull-stream "^3.2.3"
326 + pull-through "^1.0.18"
327 + split-buffer "~1.0.0"
328 +
329 +pull-cat@^1.1.11, pull-cat@^1.1.9, pull-cat@~1.1.5:
330 + version "1.1.11"
331 + resolved "https://registry.yarnpkg.com/pull-cat/-/pull-cat-1.1.11.tgz#b642dd1255da376a706b6db4fa962f5fdb74c31b"
332 +
333 +pull-goodbye@~0.0.1:
334 + version "0.0.2"
335 + resolved "https://registry.yarnpkg.com/pull-goodbye/-/pull-goodbye-0.0.2.tgz#8d8357db55e22a710dfff0f16a8c90b45efe4171"
336 + dependencies:
337 + pull-stream "~3.5.0"
338 +
339 +pull-handshake@^1.1.1:
340 + version "1.1.4"
341 + resolved "https://registry.yarnpkg.com/pull-handshake/-/pull-handshake-1.1.4.tgz#6000a0fd018884cdfd737254f8cc60ab2a637791"
342 + dependencies:
343 + pull-cat "^1.1.9"
344 + pull-pair "~1.1.0"
345 + pull-pushable "^2.0.0"
346 + pull-reader "^1.2.3"
347 +
348 +pull-pair@~1.1.0:
349 + version "1.1.0"
350 + resolved "https://registry.yarnpkg.com/pull-pair/-/pull-pair-1.1.0.tgz#7ee427263fdf4da825397ac0a05e1ab4b74bd76d"
351 +
352 +pull-paramap@^1.2.1:
353 + version "1.2.2"
354 + resolved "https://registry.yarnpkg.com/pull-paramap/-/pull-paramap-1.2.2.tgz#51a4193ce9c8d7215d95adad45e2bcdb8493b23a"
355 + dependencies:
356 + looper "^4.0.0"
357 +
358 +pull-pushable@^2.0.0:
359 + version "2.1.1"
360 + resolved "https://registry.yarnpkg.com/pull-pushable/-/pull-pushable-2.1.1.tgz#86666abbe3f5402f1f7ead03eefd69b785eca5b8"
361 +
362 +pull-reader@^1.2.3, pull-reader@^1.2.4, pull-reader@^1.2.5:
363 + version "1.2.9"
364 + resolved "https://registry.yarnpkg.com/pull-reader/-/pull-reader-1.2.9.tgz#d2e9ad00bcfb54e62aa66d42c2dbbcb5eb6843b0"
365 +
366 +pull-stream@^3.2.3, pull-stream@^3.4.3, pull-stream@^3.4.5, pull-stream@^3.5.0:
367 + version "3.6.0"
368 + resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.6.0.tgz#59d033a6815d4e3097d47c3d2b1893a9e58a2351"
369 +
370 +pull-stream@~3.5.0:
371 + version "3.5.0"
372 + resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.5.0.tgz#1ee5b6f76fd3b3a49a5afb6ded5c0320acb3cfc7"
373 +
374 +pull-through@^1.0.17, pull-through@^1.0.18:
375 + version "1.0.18"
376 + resolved "https://registry.yarnpkg.com/pull-through/-/pull-through-1.0.18.tgz#8dd62314263e59cf5096eafbb127a2b6ef310735"
377 + dependencies:
378 + looper "~3.0.0"
379 +
380 +pull-ws@^3.2.7:
381 + version "3.2.9"
382 + resolved "https://registry.yarnpkg.com/pull-ws/-/pull-ws-3.2.9.tgz#8a90502a41d9058fcb3096ad4823a0f189d613ba"
383 + dependencies:
384 + relative-url "^1.0.2"
385 + ws "^1.1.0"
386 +
387 +rc@^1.1.6:
388 + version "1.2.1"
389 + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
390 + dependencies:
391 + deep-extend "~0.4.0"
392 + ini "~1.3.0"
393 + minimist "^1.2.0"
394 + strip-json-comments "~2.0.1"
395 +
396 +relative-url@^1.0.2:
397 + version "1.0.2"
398 + resolved "https://registry.yarnpkg.com/relative-url/-/relative-url-1.0.2.tgz#d21c52a72d6061018bcee9f9c9fc106bf7d65287"
399 +
400 +resolve@~1.1.7:
401 + version "1.1.7"
402 + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
403 +
404 +resumer@~0.0.0:
405 + version "0.0.0"
406 + resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759"
407 + dependencies:
408 + through "~2.3.4"
409 +
410 +secret-handshake@^1.1.5:
411 + version "1.1.11"
412 + resolved "https://registry.yarnpkg.com/secret-handshake/-/secret-handshake-1.1.11.tgz#239d613678f1e5c50f223f2605f36475cefd665e"
413 + dependencies:
414 + chloride "^2.2.7"
415 + deep-equal "~1.0.0"
416 + pull-box-stream "^1.0.13"
417 + pull-handshake "^1.1.1"
418 + pull-stream "^3.4.5"
419 +
420 +separator-escape@0.0.0:
421 + version "0.0.0"
422 + resolved "https://registry.yarnpkg.com/separator-escape/-/separator-escape-0.0.0.tgz#e433676932020454e3c14870c517ea1de56c2fa4"
423 +
424 +sha.js@2.4.5:
425 + version "2.4.5"
426 + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.5.tgz#27d171efcc82a118b99639ff581660242b506e7c"
427 + dependencies:
428 + inherits "^2.0.1"
429 +
430 +sha.js@^2.4.8:
431 + version "2.4.8"
432 + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f"
433 + dependencies:
434 + inherits "^2.0.1"
435 +
436 +smart-buffer@^1.0.4:
437 + version "1.1.15"
438 + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
439 +
440 +socks@1.1.9:
441 + version "1.1.9"
442 + resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691"
443 + dependencies:
444 + ip "^1.1.2"
445 + smart-buffer "^1.0.4"
446 +
447 +sodium-browserify-tweetnacl@^0.2.0:
448 + version "0.2.3"
449 + resolved "https://registry.yarnpkg.com/sodium-browserify-tweetnacl/-/sodium-browserify-tweetnacl-0.2.3.tgz#b5537ffcbb9f74ebc443b8b6a211b291e8fcbc8e"
450 + dependencies:
451 + chloride-test "^1.1.0"
452 + ed2curve "^0.1.4"
453 + sha.js "^2.4.8"
454 + tweetnacl "^0.14.1"
455 + tweetnacl-auth "^0.3.0"
456 +
457 +sodium-browserify@^1.0.3:
458 + version "1.2.1"
459 + resolved "https://registry.yarnpkg.com/sodium-browserify/-/sodium-browserify-1.2.1.tgz#b0b559ca36981679085214855e26645df67aaf1c"
460 + dependencies:
461 + libsodium-wrappers "^0.2.9"
462 + sha.js "2.4.5"
463 + tweetnacl "^0.14.1"
464 +
465 +sodium-chloride@^1.1.0:
466 + version "1.1.0"
467 + resolved "https://registry.yarnpkg.com/sodium-chloride/-/sodium-chloride-1.1.0.tgz#247a234b88867f6dff51332b605f193a65bf6839"
468 +
469 +sodium-native@^1.10.0:
470 + version "1.10.1"
471 + resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-1.10.1.tgz#95876693b336c51d4a9a4ea1ccc7a048bbb3e373"
472 + dependencies:
473 + nan "^2.4.0"
474 + node-gyp-build "^3.0.0"
475 +
476 +split-buffer@~1.0.0:
477 + version "1.0.0"
478 + resolved "https://registry.yarnpkg.com/split-buffer/-/split-buffer-1.0.0.tgz#b7e8e0ab51345158b72c1f6dbef2406d51f1d027"
479 +
480 +ssb-avatar@^0.2.0:
481 + version "0.2.0"
482 + resolved "https://registry.yarnpkg.com/ssb-avatar/-/ssb-avatar-0.2.0.tgz#06cd70795ee58d1462d100a45c660df3179d3b39"
483 + dependencies:
484 + pull-cat "^1.1.9"
485 + pull-stream "^3.4.3"
486 + ssb-msgs "^5.2.0"
487 + ssb-ref "^2.3.2"
488 +
489 +ssb-client@^4.4.0:
490 + version "4.4.1"
491 + resolved "https://registry.yarnpkg.com/ssb-client/-/ssb-client-4.4.1.tgz#aa0914c1ef0747fc360bc19defce2cebfaff4115"
492 + dependencies:
493 + explain-error "^1.0.1"
494 + multiserver "^1.7.0"
495 + muxrpc "^6.3.3"
496 + ssb-config "^2.0.0"
497 + ssb-keys "^7.0.9"
498 +
499 +ssb-config@^2.0.0:
500 + version "2.2.0"
501 + resolved "https://registry.yarnpkg.com/ssb-config/-/ssb-config-2.2.0.tgz#41cad038a8575af4062d3fd57d3b167be85b03bc"
502 + dependencies:
503 + deep-extend "^0.4.0"
504 + non-private-ip "^1.2.1"
505 + os-homedir "^1.0.1"
506 + rc "^1.1.6"
507 +
508 +ssb-keys@^7.0.9:
509 + version "7.0.9"
510 + resolved "https://registry.yarnpkg.com/ssb-keys/-/ssb-keys-7.0.9.tgz#06b6eb57784757266ced1a8c6d077cf81a0825f8"
511 + dependencies:
512 + chloride "^2.2.7"
513 + mkdirp "~0.5.0"
514 + private-box "^0.2.1"
515 +
516 +ssb-marked@^0.7.2:
517 + version "0.7.2"
518 + resolved "https://registry.yarnpkg.com/ssb-marked/-/ssb-marked-0.7.2.tgz#160e24113782a9ca5e806072aa7a65e7c865dbf2"
519 +
520 +ssb-msgs@^5.2.0:
521 + version "5.2.0"
522 + resolved "https://registry.yarnpkg.com/ssb-msgs/-/ssb-msgs-5.2.0.tgz#c681da5cd70c574c922dca4f03c521538135c243"
523 + dependencies:
524 + ssb-ref "^2.0.0"
525 +
526 +ssb-ref@^2.0.0, ssb-ref@^2.3.0, ssb-ref@^2.3.2, ssb-ref@^2.6.2:
527 + version "2.7.1"
528 + resolved "https://registry.yarnpkg.com/ssb-ref/-/ssb-ref-2.7.1.tgz#5d4effc545ec0ffd7fc15ba27829a640b8a2afba"
529 + dependencies:
530 + ip "^1.1.3"
531 + is-valid-domain "~0.0.1"
532 +
533 +ssb-sort@^1.0.0:
534 + version "1.0.0"
535 + resolved "https://registry.yarnpkg.com/ssb-sort/-/ssb-sort-1.0.0.tgz#8e9956f50752d2b158247b06e49c3f491c1cd27b"
536 + dependencies:
537 + ssb-ref "^2.3.0"
538 +
539 +stream-to-pull-stream@^1.7.2:
540 + version "1.7.2"
541 + resolved "https://registry.yarnpkg.com/stream-to-pull-stream/-/stream-to-pull-stream-1.7.2.tgz#757609ae1cebd33c7432d4afbe31ff78650b9dde"
542 + dependencies:
543 + looper "^3.0.0"
544 + pull-stream "^3.2.3"
545 +
546 +string.prototype.trim@~1.1.2:
547 + version "1.1.2"
548 + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea"
549 + dependencies:
550 + define-properties "^1.1.2"
551 + es-abstract "^1.5.0"
552 + function-bind "^1.0.2"
553 +
554 +strip-json-comments@~2.0.1:
555 + version "2.0.1"
556 + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
557 +
558 +tape@^4.6.2:
559 + version "4.6.3"
560 + resolved "https://registry.yarnpkg.com/tape/-/tape-4.6.3.tgz#637e77581e9ab2ce17577e9bd4ce4f575806d8b6"
561 + dependencies:
562 + deep-equal "~1.0.1"
563 + defined "~1.0.0"
564 + for-each "~0.3.2"
565 + function-bind "~1.1.0"
566 + glob "~7.1.1"
567 + has "~1.0.1"
568 + inherits "~2.0.3"
569 + minimist "~1.2.0"
570 + object-inspect "~1.2.1"
571 + resolve "~1.1.7"
572 + resumer "~0.0.0"
573 + string.prototype.trim "~1.1.2"
574 + through "~2.3.8"
575 +
576 +through@~2.3.4, through@~2.3.8:
577 + version "2.3.8"
578 + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
579 +
580 +tweetnacl-auth@^0.3.0:
581 + version "0.3.1"
582 + resolved "https://registry.yarnpkg.com/tweetnacl-auth/-/tweetnacl-auth-0.3.1.tgz#b75bc2df15649bb84e8b9aa3c0669c6c4bce0d25"
583 + dependencies:
584 + tweetnacl "0.x.x"
585 +
586 +tweetnacl@0.x.x, tweetnacl@^0.14.1:
587 + version "0.14.5"
588 + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
589 +
590 +ultron@1.0.x:
591 + version "1.0.2"
592 + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa"
593 +
594 +wrappy@1:
595 + version "1.0.2"
596 + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
597 +
598 +ws@^1.1.0:
599 + version "1.1.4"
600 + resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.4.tgz#57f40d036832e5f5055662a397c4de76ed66bf61"
601 + dependencies:
602 + options ">=0.0.5"
603 + ultron "1.0.x"
yarn.lockView
The diff is too large to show. Use a local git client to view these changes.
Old file size: 104384 bytes
New file size: 104890 bytes

Built with git-ssb-web