diff --git a/dat/extensions.js b/dat/extensions.js index 32e77ef2..8bf9fba3 100644 --- a/dat/extensions.js +++ b/dat/extensions.js @@ -57,7 +57,7 @@ exports.getPeer = getPeer function broadcastEphemeralMessage (archive, payload) { datEphemeralExtMsg.broadcast(archive, encodeEphemeralMsg(payload)) } -exports.broadcastEphemeralMessage =broadcastEphemeralMessage +exports.broadcastEphemeralMessage = broadcastEphemeralMessage // impl for datPeers.send(peerId, msg) function sendEphemeralMessage (archive, peerId, payload) { @@ -186,4 +186,4 @@ function decodeSessionData (sessionData) { console.error('Failed to parse local session data', e, sessionData) return null } -} \ No newline at end of file +} diff --git a/dat/folder-sync.js b/dat/folder-sync.js index 54a757e3..cd9ce89b 100644 --- a/dat/folder-sync.js +++ b/dat/folder-sync.js @@ -56,8 +56,7 @@ const syncFolderToArchive = exports.syncFolderToArchive = function (archive, opt const ensureSyncFinished = exports.ensureSyncFinished = async function (archive) { var isFinished var release = await getArchiveSyncLock(archive) - try { isFinished = (archive._activeSyncs == 0) } - finally { release() } + try { isFinished = (archive._activeSyncs == 0) } finally { release() } if (!isFinished) { return ensureSyncFinished(archive) // check again } @@ -182,7 +181,7 @@ exports.configureFolderToArchiveWatcher = async function (archive) { // no need to setup watcher // just do an add-only sync from archive->folder await sync(archive, false, {shallow: false, addOnly: true}) - if (shouldAbort()) return + if (shouldAbort()) return // eslint-disable-line no-useless-return } else { // sync up try { diff --git a/dat/library.js b/dat/library.js index aaa98080..b0469765 100644 --- a/dat/library.js +++ b/dat/library.js @@ -456,7 +456,7 @@ const getArchive = exports.getArchive = function getArchive (key) { return archives[key] } -const getArchiveCheckout = exports.getArchiveCheckout = function getArchiveCheckout (archive, version) { +exports.getArchiveCheckout = function getArchiveCheckout (archive, version) { var isHistoric = false var isPreview = false var checkoutFS = archive diff --git a/dat/logging-utils.js b/dat/logging-utils.js index 17b8b984..efcd7678 100644 --- a/dat/logging-utils.js +++ b/dat/logging-utils.js @@ -34,7 +34,7 @@ function has (str, v) { return str.indexOf(v) !== -1 } -const addArchiveSwarmLogging = exports.addArchiveSwarmLogging = function ({archivesByDKey, log, archiveSwarm}) { +exports.addArchiveSwarmLogging = function ({archivesByDKey, log, archiveSwarm}) { archiveSwarm.on('listening', () => { archiveSwarm._discovery.dns.on('traffic', (type, details) => { let archive = archivesByDKey[getDNSMessageDiscoveryKey(archivesByDKey, details.message)] diff --git a/dat/storage.js b/dat/storage.js index d59509de..f9d4daab 100644 --- a/dat/storage.js +++ b/dat/storage.js @@ -20,7 +20,7 @@ var supportsSparseFiles = false exports.setup = async function () { await new Promise((resolve) => { - detectSparseFiles(function (err, yes) { + detectSparseFiles(function (_, yes) { supportsSparseFiles = yes if (!yes) { debug('Sparse-file support not detected. Falling back to indexed data files.') @@ -51,4 +51,4 @@ exports.create = function (folder) { metadata: createStorage(folder, 'metadata'), content: createStorage(folder, 'content') } -} \ No newline at end of file +} diff --git a/dbs/archive-drafts.js b/dbs/archive-drafts.js index f39974b8..6ecda788 100644 --- a/dbs/archive-drafts.js +++ b/dbs/archive-drafts.js @@ -8,7 +8,7 @@ exports.list = async function (profileId, masterKey) { // get draft list var records = await db.all(`SELECT draftKey as key FROM archive_drafts WHERE profileId = ? AND masterKey = ? ORDER BY createdAt`, [profileId, masterKey]) // fetch full info from archives db - return Promise.all(records.map(async ({key}) => archivesDb.query(profileId, {key, showHidden: true}))) + return Promise.all(records.map(({key}) => archivesDb.query(profileId, {key, showHidden: true}))) } exports.add = function (profileId, masterKey, draftKey) { @@ -27,4 +27,4 @@ exports.getMaster = async function (profileId, draftKey) { var record = await db.get(`SELECT masterKey as key FROM archive_drafts WHERE profileId = ? AND draftKey = ?`, [profileId, draftKey]) if (record) return record.key return draftKey -} \ No newline at end of file +} diff --git a/dbs/profile-data-db.js b/dbs/profile-data-db.js index 5b47f3b4..1bf97293 100644 --- a/dbs/profile-data-db.js +++ b/dbs/profile-data-db.js @@ -1,6 +1,5 @@ const sqlite3 = require('sqlite3') const path = require('path') -const fs = require('fs') const {cbPromise} = require('../lib/functions') const {setupSqliteDB} = require('../lib/db') diff --git a/dbs/sitedata.js b/dbs/sitedata.js index 9fcfe436..0df0cafa 100644 --- a/dbs/sitedata.js +++ b/dbs/sitedata.js @@ -4,7 +4,6 @@ const url = require('url') const { cbPromise } = require('../lib/functions') const { setupSqliteDB } = require('../lib/db') const datDns = require('../dat/dns') -const datLibrary = require('../dat/library') // globals // = diff --git a/hyperswarm/peer-socket-schemas.js b/hyperswarm/peer-socket-schemas.js new file mode 100644 index 00000000..105e8404 --- /dev/null +++ b/hyperswarm/peer-socket-schemas.js @@ -0,0 +1,119 @@ +// This file is auto generated by the protocol-buffers compiler + +/* eslint-disable quotes */ +/* eslint-disable indent */ +/* eslint-disable no-redeclare */ +/* eslint-disable camelcase */ + +// Remember to `npm install --save protocol-buffers-encodings` +var encodings = require('protocol-buffers-encodings') +var varint = encodings.varint +var skip = encodings.skip + +exports.PeerSocketMessageType = { + "MESSAGE": 0, + "SESSION_DATA": 1 +} + +var PeerSocketMessage = exports.PeerSocketMessage = { + buffer: true, + encodingLength: null, + encode: null, + decode: null +} + +definePeerSocketMessage() + +function definePeerSocketMessage () { + var enc = [ + encodings.enum, + encodings.bytes, + encodings.string + ] + + PeerSocketMessage.encodingLength = encodingLength + PeerSocketMessage.encode = encode + PeerSocketMessage.decode = decode + + function encodingLength (obj) { + var length = 0 + if (defined(obj.messageType)) { + var len = enc[0].encodingLength(obj.messageType) + length += 1 + len + } + if (!defined(obj.content)) throw new Error("content is required") + var len = enc[1].encodingLength(obj.content) + length += 1 + len + if (defined(obj.contentType)) { + var len = enc[2].encodingLength(obj.contentType) + length += 1 + len + } + return length + } + + function encode (obj, buf, offset) { + if (!offset) offset = 0 + if (!buf) buf = Buffer.allocUnsafe(encodingLength(obj)) + var oldOffset = offset + if (defined(obj.messageType)) { + buf[offset++] = 8 + enc[0].encode(obj.messageType, buf, offset) + offset += enc[0].encode.bytes + } + if (!defined(obj.content)) throw new Error("content is required") + buf[offset++] = 18 + enc[1].encode(obj.content, buf, offset) + offset += enc[1].encode.bytes + if (defined(obj.contentType)) { + buf[offset++] = 26 + enc[2].encode(obj.contentType, buf, offset) + offset += enc[2].encode.bytes + } + encode.bytes = offset - oldOffset + return buf + } + + function decode (buf, offset, end) { + if (!offset) offset = 0 + if (!end) end = buf.length + if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") + var oldOffset = offset + var obj = { + messageType: 0, + content: null, + contentType: "" + } + var found1 = false + while (true) { + if (end <= offset) { + if (!found1) throw new Error("Decoded message is not valid") + decode.bytes = offset - oldOffset + return obj + } + var prefix = varint.decode(buf, offset) + offset += varint.decode.bytes + var tag = prefix >> 3 + switch (tag) { + case 1: + obj.messageType = enc[0].decode(buf, offset) + offset += enc[0].decode.bytes + break + case 2: + obj.content = enc[1].decode(buf, offset) + offset += enc[1].decode.bytes + found1 = true + break + case 3: + obj.contentType = enc[2].decode(buf, offset) + offset += enc[2].decode.bytes + break + default: + offset = skip(prefix & 7, buf, offset) + } + } + } +} + +function defined (val) { + return val !== null && val !== undefined && (typeof val !== 'number' || !isNaN(val)) +} diff --git a/hyperswarm/peer-socket-schemas.proto b/hyperswarm/peer-socket-schemas.proto new file mode 100644 index 00000000..f60e4d46 --- /dev/null +++ b/hyperswarm/peer-socket-schemas.proto @@ -0,0 +1,9 @@ +enum PeerSocketMessageType { + MESSAGE = 0; + SESSION_DATA = 1; +} +message PeerSocketMessage { + optional PeerSocketMessageType messageType = 1 [default = MESSAGE]; + required bytes content = 2; + optional string contentType = 3; +} \ No newline at end of file diff --git a/hyperswarm/peer-socket.js b/hyperswarm/peer-socket.js new file mode 100644 index 00000000..d89d541c --- /dev/null +++ b/hyperswarm/peer-socket.js @@ -0,0 +1,269 @@ +const EventEmitter = require('events') +const createHyperswarmNetwork = require('@hyperswarm/network') +const lpstream = require('length-prefixed-stream') +const pump = require('pump') +const sodium = require('sodium-universal') +const schemas = require('./peer-socket-schemas') +const {extractOrigin} = require('../lib/strings') + +// constants +// = + +const {MESSAGE, SESSION_DATA} = schemas.PeerSocketMessageType +const MAX_SESSION_DATA_SIZE = 256 // bytes + +// globals +// = + +var swarms = new Map() // origin -> hyperswarm net instance + +// exported APIs +// = + +module.exports = { + getSwarm, + getOrCreateSwarm, + + getLobby, + getOrCreateLobby, + leaveLobby, + getLobbyConnection, + setLobbySessionData, + + sendMessage, + encodeMessage, + decodeMessage, + schemas +} + +function getSwarm (sender, tabIdentity) { + return swarms.get(getSwarmId(sender, tabIdentity)) +} + +function getOrCreateSwarm (sender, tabIdentity) { + var swarm = getSwarm(sender, tabIdentity) + if (!swarm) swarm = createSwarm(sender, tabIdentity) + return swarm +} + +function getLobby (sender, tabIdentity, lobbyType, lobbyName) { + var swarm = getSwarm(sender, tabIdentity) + if (swarm) { + var topic = createLobbyTopic(lobbyType, lobbyName) + return swarm.lobbies.get(topic.toString('hex')) + } +} + +function getOrCreateLobby (sender, tabIdentity, lobbyType, lobbyName) { + var swarm = getOrCreateSwarm(sender, tabIdentity) + if (swarm) { + var topic = createLobbyTopic(lobbyType, lobbyName) + if (!swarm.lobbies.has(topic.toString('hex'))) { + // create the lobby + var lobby = Object.assign(new EventEmitter(), { + topic, + name: lobbyName, + type: lobbyType, + self: { + sessionData: null + }, + connections: new Set(), + connIdCounter: 0 + }) + swarm.lobbies.set(topic.toString('hex'), lobby) + + // join the swarm topic + swarm.join(topic, {lookup: true, announce: true}) + } + return swarm.lobbies.get(topic.toString('hex')) + } +} + +function leaveLobby (sender, tabIdentity, lobbyType, lobbyName) { + var swarm = getSwarm(sender, tabIdentity) + if (swarm) { + var topic = createLobbyTopic(lobbyType, lobbyName) + var lobby = swarm.lobbies.get(topic.toString('hex')) + if (lobby) { + // leave the swarm topic and close all connections + lobby.connections.forEach(({socket}) => socket.close()) + swarm.leave(topic) + lobby.emit('leave') + swarm.lobbies.delete(topic.toString('hex')) + } + } +} + +function getLobbyConnection (sender, tabIdentity, lobbyType, lobbyName, socketId) { + var lobby = getLobby(sender, tabIdentity, lobbyType, lobbyName) + if (lobby) { + return Array.from(lobby.connections).find(({id}) => id === socketId) + } +} + +function setLobbySessionData (sender, tabIdentity, lobbyType, lobbyName, sessionData) { + var lobby = getLobby(sender, tabIdentity, lobbyType, lobbyName) + if (!lobby) throw new Error('Lobby is not active') + + // validate session data + var len = Buffer.length(Buffer.isBuffer(sessionData) ? sessionData : Buffer.from(JSON.stringify(sessionData), 'utf8')) + if (len > MAX_SESSION_DATA_SIZE) { + throw new Error(`Session data is too large. The total size must be no greater than ${MAX_SESSION_DATA_SIZE} bytes.`) + } + + // store & broadcast + lobby.self.sessionData = sessionData + lobby.connections.forEach(conn => sendSessionData(lobby, conn)) + lobby.emit('self-session-data', {sessionData}) + + return false +} + +function sendMessage (conn, message) { + return new Promise((resolve, reject) => { + conn.encoder.write(encodeMessage(message), err => { + if (err) { + console.error('Error writing to PeerSocket', err) + reject(new Error('Failed to send message')) + } else { + resolve() + } + }) + }) +} + +function encodeMessage (message) { + message.messageType = message.messageType || MESSAGE + if (message.content && !message.contentType) { + if (Buffer.isBuffer(message.content)) { + message.contentType = 'application/octet-stream' + } else { + message.contentType = 'application/json' + message.content = Buffer.from(JSON.stringify(message.content), 'utf8') + } + } + return schemas.PeerSocketMessage.encode(message) +} + +function decodeMessage (message) { + message = schemas.PeerSocketMessage.decode(message) + if (message.contentType === 'application/json') { + try { + message.content = JSON.parse(message.content.toString('utf8')) + } catch (e) { + console.error('Failed to parse PeerSocket message', e, message) + message.content = null + } + } + return message +} + +// internal methods +// = + +function createLobbyTopic (lobbyType, lobbyName) { + var out = Buffer.allocUnsafe(32) + sodium.crypto_generichash(out, Buffer.from(`peersocket-${lobbyType}-${lobbyName}`, 'utf8')) + return out +} + +function getSwarmId (sender, tabIdentity) { + var id = extractOrigin(sender.getURL()) + if (tabIdentity) { + id += '::' + tabIdentity + } + return id +} + +function createSwarm (sender, tabIdentity) { + let swarmId = getSwarmId(sender, tabIdentity) + var swarm = createHyperswarmNetwork({ephemeral: true}) + swarms.set(swarmId, swarm) + swarm.lobbies = new Map() + + // handle connection events + swarm.on('connection', (socket, details) => { + handleConnection(swarm, socket, details) + }) + return swarm +} + +function handleConnection (swarm, socket, details) { + var topic + if (!details.peer) { + // DEBUG HACK + // if no peer info is available, fallback to the first available lobby + // this MUST BE FIXED upstream prior to merging + // -prf + topic = Array.from(swarm.lobbies.keys())[0] + } else { + topic = details.peer.topic.toString('hex') + } + var lobby = swarm.lobbies.get(topic) + if (lobby) { + // create the connection + var id = ++lobby.connIdCounter + var encoder = lpstream.encode() + var decoder = lpstream.decode() + var conn = { + id, + socket, + details, + encoder, + decoder, + events: new EventEmitter(), + sessionData: null + } + lobby.connections.add(conn) + lobby.emit('connection', {socketInfo: {id}}) + + // send the handshake + // TODO- handshake message? + + // send current session data + if (lobby.self.sessionData) { + sendSessionData(lobby, conn) + } + + // wire up events + decoder.on('data', message => handleMessage(conn, message)) + + // wire up message-framers and handle close + pump(encoder, socket, decoder, err => { + if (err) console.log('PeerSocket connection error', err) + lobby.connections.remove(conn) + conn.events.emit('close') + }) + } +} + +function handleMessage (conn, message) { + try { + message = decodeMessage(message) + switch (message.messageType) { + case schemas.PeerSocketMessageType.MESSAGE: + conn.events.emit('message', {message: message.content}) + break + case schemas.PeerSocketMessageType.SESSION_DATA: + conn.sessionData = message.content + conn.events.emit('session-data', {sessionData: message.content}) + break + default: + throw new Error('Unknown message type: ' + message.messageType) + } + } catch (e) { + console.log('Failed to decode received PeerSocket message', e) + } +} + +async function sendSessionData (lobby, conn) { + try { + sendMessage(conn, { + messageType: SESSION_DATA, + content: lobby.self.sessionData + }) + } catch (e) { + console.log('Failed to send PeerSocket session data', e) + // TODO bubble error? + } +} diff --git a/lib/spell-checker.js b/lib/spell-checker.js index 37c4ced3..732c22b9 100644 --- a/lib/spell-checker.js +++ b/lib/spell-checker.js @@ -1,69 +1,71 @@ const osLocale = require('os-locale') const os = require('os') const semver = require('semver') -const spellchecker = exports.spellchecker = require('spellchecker') +const spellchecker = require('spellchecker') // exported api // = +exports.spellchecker = spellchecker + // Spellchecker thinks contractions are errors, silly spellchecker exports.SKIP_LIST = [ - 'ain', - 'couldn', - 'didn', - 'doesn', - 'hadn', - 'hasn', - 'mightn', - 'mustn', - 'needn', - 'oughtn', - 'shan', - 'shouldn', - 'wasn', - 'weren', - 'wouldn', + 'ain', + 'couldn', + 'didn', + 'doesn', + 'hadn', + 'hasn', + 'mightn', + 'mustn', + 'needn', + 'oughtn', + 'shan', + 'shouldn', + 'wasn', + 'weren', + 'wouldn', ] // Hunspell requires a fully-qualified locale const locale = exports.locale = osLocale.sync().replace('-', '_') exports.setup = function () { - // Need to set LANG env variable so node spellcheck can find its default language - if (!process.env.LANG) { - process.env.LANG = locale - } + // Need to set LANG env variable so node spellcheck can find its default language + if (!process.env.LANG) { + process.env.LANG = locale + } - if (process.platform === 'linux') { - setupLinux(locale) - } else if (process.platform === 'windows' && semver.lt(os.release(), '8.0.0')) { - setupWin7AndEarlier(locale) - } else { - // OSX and Windows 8+ have OS-level spellcheck APIs - console.info('Using OS spell check API') - } + if (process.platform === 'linux') { + setupLinux(locale) + } else if (process.platform === 'windows' && semver.lt(os.release(), '8.0.0')) { + setupWin7AndEarlier(locale) + } else { + // OSX and Windows 8+ have OS-level spellcheck APIs + console.info('Using OS spell check API') + } } function setupLinux (locale) { - // Load proper dictionary for locale - if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { - const location = process.env.HUNSPELL_DICTIONARIES || '/usr/share/hunspell' + // Load proper dictionary for locale + if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { + const location = process.env.HUNSPELL_DICTIONARIES || '/usr/share/hunspell' - console.info('Detected Linux. Setting up spell check for Linux.') - spellchecker.setDictionary(locale, location) - } else { - console.info('Detected Linux. Using default en_US spell check dictionary.') - } + console.info('Detected Linux. Setting up spell check for Linux.') + spellchecker.setDictionary(locale, location) + } else { + console.info('Detected Linux. Using default en_US spell check dictionary.') + } } function setupWin7AndEarlier (locale) { - // Load proper dictionary for locale - if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { - const location = process.env.HUNSPELL_DICTIONARIES - - console.info('Detected Windows 7 or earlier. Setting up spell-check for Windows 7 or earlier.') - spellchecker.setDictionary(locale, location) - } else { - console.info('Detected Windows 7 or earlier. Using default en_US spell check dictionary.') - } -} \ No newline at end of file + // Load proper dictionary for locale + if (process.env.HUNSPELL_DICTIONARIES || locale !== 'en_US') { + const location = process.env.HUNSPELL_DICTIONARIES + + console.info('Detected Windows 7 or earlier. Setting up spell-check for Windows 7 or earlier.') + spellchecker.setDictionary(locale, location) + } else { + console.info('Detected Windows 7 or earlier. Using default en_US spell check dictionary.') + } +} diff --git a/lib/strings.js b/lib/strings.js index 6e28ec50..f08cdcf9 100644 --- a/lib/strings.js +++ b/lib/strings.js @@ -51,3 +51,12 @@ exports.getHostname = function (str) { return str } } + +exports.extractOrigin = function (str) { + try { + const u = new URL(str) + return `${u.protocol}//${u.hostname}` + } catch (e) { + throw new Error('Invalid URL') + } +} diff --git a/package-lock.json b/package-lock.json index 880c4174..77fc163f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,26 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, "@beaker/dat-ephemeral-ext-msg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@beaker/dat-ephemeral-ext-msg/-/dat-ephemeral-ext-msg-1.0.1.tgz", @@ -22,6 +42,35 @@ "resolved": "https://registry.npmjs.org/@beaker/datignore/-/datignore-1.0.0.tgz", "integrity": "sha512-UMOvwf+efn01go0fV/JqAZIgrrObphXwdROgCoPZM+g6iCzeoA9G2vQwfULuw5W4XGHIj+Ro689zFNKfRXTHiw==" }, + "@hyperswarm/dht": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@hyperswarm/dht/-/dht-0.0.1.tgz", + "integrity": "sha512-ocwfEAXVuiuqaMPNUcBVCZn9pguRYBTjCYiI/fwbnEYInzkPGo/KG/aWBSkKqN9lpKAWXRFLYJrgo7VDnAMCpg==", + "requires": { + "dht-rpc": "^4.0.1", + "ipv4-peers": "^1.1.1", + "protocol-buffers-encodings": "^1.1.0", + "record-cache": "^1.1.0" + } + }, + "@hyperswarm/discovery": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@hyperswarm/discovery/-/discovery-1.1.0.tgz", + "integrity": "sha512-SSib8T/h6J1JoehhlU6S/6olnhZ+yyl1liqBIOFBuET9iBeUK+PpmjEHbbGsOy5qwVWhx+bl7eptdV+gbD/arg==", + "requires": { + "@hyperswarm/dht": "0.0.1", + "multicast-dns": "^7.2.0" + } + }, + "@hyperswarm/network": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@hyperswarm/network/-/network-0.0.3.tgz", + "integrity": "sha512-RV/dM41NC0qE6XN1KvEKVk0crZc/ms5Jo/mmu2vg0I1UA4QxgaAxg9RYaFUlkZZnKqDjQrY1yVnHd0yNiStsLg==", + "requires": { + "@hyperswarm/discovery": "^1.1.0", + "utp-native": "^2.0.1" + } + }, "@types/node": { "version": "10.11.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.3.tgz", @@ -40,20 +89,12 @@ "dev": true }, "acorn-jsx": { - "version": "3.0.1", - "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", + "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", "dev": true, "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } + "acorn": "^5.0.3" } }, "ajv": { @@ -68,9 +109,9 @@ } }, "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", "dev": true }, "ansi-escapes": { @@ -85,10 +126,13 @@ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } }, "any-promise": { "version": "1.3.0", @@ -243,32 +287,6 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - } - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -546,32 +564,12 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "chownr": { @@ -884,6 +882,31 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, + "dht-rpc": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/dht-rpc/-/dht-rpc-4.1.0.tgz", + "integrity": "sha512-f7mcLigsLWdL1zV7PgnwKKzRSk0fB6MwriCTJSVMNDz/4LmbvnxS8k8IXwTdz6W9LfQAsI7+WD714Cvnc5ro4g==", + "requires": { + "codecs": "^1.2.1", + "ipv4-peers": "^1.1.1", + "k-bucket": "^5.0.0", + "protocol-buffers-encodings": "^1.1.0", + "sodium-universal": "^2.0.0", + "stream-collector": "^1.0.1", + "time-ordered-set": "^1.0.1", + "xor-distance": "^1.0.0" + }, + "dependencies": { + "k-bucket": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.0.0.tgz", + "integrity": "sha512-r/q+wV/Kde62/tk+rqyttEJn6h0jR7x+incdMVSYTqK73zVxVrzJa70kJL49cIKen8XjIgUZKSvk8ktnrQbK4w==", + "requires": { + "randombytes": "^2.0.3" + } + } + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -1071,77 +1094,90 @@ "dev": true }, "eslint": { - "version": "4.19.1", - "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.1.tgz", + "integrity": "sha512-hgrDtGWz368b7Wqf+v1Z69O3ZebNR0+GA7PtDdbmuz4rInFVUV9uw7whjZEiWyLzCjVb5Rs5WRN1TAS6eo7AYA==", "dev": true, "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", + "@babel/code-frame": "^7.0.0", + "ajv": "^6.5.3", "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", + "eslint-scope": "^4.0.0", + "eslint-utils": "^1.3.1", "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", + "espree": "^4.0.0", + "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^2.0.0", "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", + "globals": "^11.7.0", + "ignore": "^4.0.6", "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", + "inquirer": "^6.1.0", + "is-resolvable": "^1.1.0", + "js-yaml": "^3.12.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", + "lodash": "^4.17.5", + "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", "path-is-inside": "^1.0.2", "pluralize": "^7.0.0", "progress": "^2.0.0", - "regexpp": "^1.0.1", + "regexpp": "^2.0.0", "require-uncached": "^1.0.3", - "semver": "^5.3.0", + "semver": "^5.5.1", "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" + "strip-json-comments": "^2.0.1", + "table": "^4.0.3", + "text-table": "^0.2.0" }, "dependencies": { + "ajv": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", + "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "debug": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", - "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", + "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", "dev": true, "requires": { "ms": "^2.1.1" } }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -1154,15 +1190,21 @@ } }, "eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", "dev": true, "requires": { "esrecurse": "^4.1.0", "estraverse": "^4.1.1" } }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "dev": true + }, "eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -1170,13 +1212,13 @@ "dev": true }, "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", + "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", "dev": true, "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" + "acorn": "^5.6.0", + "acorn-jsx": "^4.1.1" } }, "esprima": { @@ -1332,13 +1374,13 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, @@ -1604,6 +1646,24 @@ "wide-align": "^1.1.0" } }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "requires": { + "is-property": "^1.0.2" + } + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "^1.0.0" + } + }, "get-stream": { "version": "3.0.0", "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -1699,15 +1759,6 @@ "har-schema": "^2.0.0" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1907,9 +1958,9 @@ "integrity": "sha1-zm4pqnYvAxuChSojkqOBY/rXkOs=" }, "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "ignore-walk": { @@ -1946,22 +1997,21 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", "dev": true, "requires": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.0", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^2.0.4", + "external-editor": "^3.0.0", "figures": "^2.0.0", - "lodash": "^4.3.0", + "lodash": "^4.17.10", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", + "rxjs": "^6.1.0", "string-width": "^2.1.0", "strip-ansi": "^4.0.0", "through": "^2.3.6" @@ -2024,6 +2074,11 @@ "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, + "ipv4-peers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ipv4-peers/-/ipv4-peers-1.1.1.tgz", + "integrity": "sha1-hauGKg9SFFg+BRh2P34dd1riTXY=" + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -2187,6 +2242,12 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", @@ -2229,9 +2290,9 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { @@ -2376,6 +2437,16 @@ } } }, + "length-prefixed-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/length-prefixed-stream/-/length-prefixed-stream-1.6.0.tgz", + "integrity": "sha512-gsJvrb5giDqil/ScQ7fEoplsI2Ch4DwnvnfTW2EGl9KBW6Ekzn8JSNESObqNAeZD8HkSjEMvc5XjhuB66fsSZQ==", + "requires": { + "buffer-alloc-unsafe": "^1.0.0", + "readable-stream": "^2.0.0", + "varint": "^5.0.0" + } + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -3179,6 +3250,20 @@ "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", "dev": true }, + "protocol-buffers": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/protocol-buffers/-/protocol-buffers-4.1.0.tgz", + "integrity": "sha512-9erS5oyfb5vzLCO1pJfSujA03md3MzaR6zP77lcHobH3tb6Z3S/mMDzCxpRrGnmsPb/6o5RLZ5wpVEM2UMlgvw==", + "dev": true, + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.2.0", + "protocol-buffers-encodings": "^1.1.0", + "protocol-buffers-schema": "^3.1.1", + "signed-varint": "^2.0.0", + "varint": "^5.0.0" + } + }, "protocol-buffers-encodings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.1.0.tgz", @@ -3188,6 +3273,12 @@ "varint": "^5.0.0" } }, + "protocol-buffers-schema": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz", + "integrity": "sha512-Xdayp8sB/mU+sUV4G7ws8xtYMGdQnxbeIfLjyO9TZZRJdztBGhlmbI5x1qcY4TG5hBkIKGnc28i7nXxaugu88w==", + "dev": true + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -3301,6 +3392,11 @@ "util-deprecate": "~1.0.1" } }, + "record-cache": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/record-cache/-/record-cache-1.1.0.tgz", + "integrity": "sha512-u8rbtLEJV7HRacl/ZYwSBFD8NFyB3PfTTfGLP37IW3hftQCwu6z4Q2RLyxo1YJUNRTEzJfpLpGwVuEYdaIkG9Q==" + }, "recursive-watch": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/recursive-watch/-/recursive-watch-1.1.4.tgz", @@ -3327,9 +3423,9 @@ } }, "regexpp": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, "remove-trailing-separator": { @@ -3432,19 +3528,13 @@ "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.13.tgz", "integrity": "sha1-mghOe4YLF7/zAVuSxnpqM2GRUTo=" }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "rxjs": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "rx-lite": "*" + "tslib": "^1.9.0" } }, "safe-buffer": { @@ -3919,10 +4009,13 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } }, "supports-sparse-files": { "version": "1.0.2", @@ -3930,31 +4023,55 @@ "integrity": "sha512-45nB+UT0Tv06fTGkYCHMR+WfAL/gIgbdHqICw2vIb0Gb68QEmi1LX1s+ODA5NJbk4RS4ug2VBHBVO1tFywJzUw==" }, "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "version": "4.0.3", + "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", "chalk": "^2.1.0", "lodash": "^4.17.4", "slice-ansi": "1.0.0", "string-width": "^2.1.1" }, "dependencies": { + "ajv": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", + "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -4050,6 +4167,11 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-0.1.0.tgz", "integrity": "sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4=" }, + "time-ordered-set": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/time-ordered-set/-/time-ordered-set-1.0.2.tgz", + "integrity": "sha512-vGO99JkxvgX+u+LtOKQEpYf31Kj3i/GNwVstfnh4dyINakMgeZCpew1e3Aj+06hEslhtHEd52g7m5IV+o1K8Mw==" + }, "timeout-refresh": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-1.0.0.tgz", @@ -4116,6 +4238,12 @@ "punycode": "^1.4.1" } }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, "ttl": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/ttl/-/ttl-1.3.1.tgz", @@ -4261,6 +4389,23 @@ } } }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -4361,6 +4506,11 @@ "mkdirp": "^0.5.1" } }, + "xor-distance": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/xor-distance/-/xor-distance-1.0.0.tgz", + "integrity": "sha1-2nNdmyT8yo282bN00W0qAe6VQcY=" + }, "xsalsa20": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/xsalsa20/-/xsalsa20-1.0.2.tgz", diff --git a/package.json b/package.json index 11d8f391..97b112c2 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "lint": "eslint . --ignore-pattern 'node_modules/*' --fix", - "test": "ava ./*-test.js -s -T15000" + "protobuf": "protocol-buffers hyperswarm/peer-socket-schemas.proto -o hyperswarm/peer-socket-schemas.js" }, "pre-commit": [ "lint" @@ -28,6 +28,7 @@ "@beaker/dat-ephemeral-ext-msg": "^1.0.0", "@beaker/dat-session-data-ext-msg": "^1.1.0", "@beaker/datignore": "^1.0.0", + "@hyperswarm/network": "0.0.3", "anymatch": "^2.0.0", "await-lock": "^1.1.3", "beaker-error-constants": "^1.4.0", @@ -50,6 +51,7 @@ "hyperdrive-to-zip-stream": "^2.1.1", "identify-filetype": "^1.0.0", "into-stream": "^3.1.0", + "length-prefixed-stream": "^1.6.0", "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", @@ -65,6 +67,7 @@ "parse-dat-url": "^3.0.1", "pauls-dat-api": "^8.0.3", "pify": "^3.0.0", + "protocol-buffers-encodings": "^1.1.0", "pump": "^3.0.0", "random-access-file": "^2.0.1", "random-access-indexed-file": "^2.0.0", @@ -74,6 +77,7 @@ "slugify": "^1.3.1", "sodium-signatures": "^2.1.1", "spellchecker": "^3.5.0", + "sodium-universal": "^2.0.0", "split2": "^2.2.0", "sqlite3": "^4.0.1", "stream-throttle": "^0.1.3", @@ -83,7 +87,8 @@ "utp-native": "^2.0.1" }, "devDependencies": { - "eslint": "^4.19.1", - "pre-commit": "^1.2.2" + "eslint": "^5.6.1", + "pre-commit": "^1.2.2", + "protocol-buffers": "^4.1.0" } } diff --git a/web-apis/bg.js b/web-apis/bg.js index 9a14e0d5..6967da13 100644 --- a/web-apis/bg.js +++ b/web-apis/bg.js @@ -29,12 +29,14 @@ const experimentalCapturePageManifest = require('./manifests/external/experiment const experimentalDatPeersManifest = require('./manifests/external/experimental/dat-peers') const experimentalGlobalFetchManifest = require('./manifests/external/experimental/global-fetch') const experimentalLibraryManifest = require('./manifests/external/experimental/library') +const experimentalPeerSocketManifest = require('./manifests/external/experimental/peer-socket') // experimental apis const experimentalCapturePageAPI = require('./bg/experimental/capture-page') const experimentalDatPeersAPI = require('./bg/experimental/dat-peers') const experimentalGlobalFetchAPI = require('./bg/experimental/global-fetch') const experimentalLibraryAPI = require('./bg/experimental/library') +const experimentalPeerSocketAPI = require('./bg/experimental/peer-socket') // exported api // = @@ -57,6 +59,7 @@ exports.setup = function () { globals.rpcAPI.exportAPI('experimental-dat-peers', experimentalDatPeersManifest, experimentalDatPeersAPI, secureOnly) globals.rpcAPI.exportAPI('experimental-global-fetch', experimentalGlobalFetchManifest, experimentalGlobalFetchAPI, secureOnly) globals.rpcAPI.exportAPI('experimental-library', experimentalLibraryManifest, experimentalLibraryAPI, secureOnly) + globals.rpcAPI.exportAPI('experimental-peer-socket', experimentalPeerSocketManifest, experimentalPeerSocketAPI, secureOnly) } function internalOnly (event, methodName, args) { diff --git a/web-apis/bg/archives.js b/web-apis/bg/archives.js index 734bcc67..aaa78d44 100644 --- a/web-apis/bg/archives.js +++ b/web-apis/bg/archives.js @@ -10,7 +10,6 @@ const archivesDb = require('../../dbs/archives') const archiveDraftsDb = require('../../dbs/archive-drafts') const {cbPromise} = require('../../lib/functions') const {timer} = require('../../lib/time') -const lock = require('../../lib/lock') // exported api // = diff --git a/web-apis/bg/dat-archive.js b/web-apis/bg/dat-archive.js index e7a46110..a20b2988 100644 --- a/web-apis/bg/dat-archive.js +++ b/web-apis/bg/dat-archive.js @@ -9,7 +9,6 @@ const datDns = require('../../dat/dns') const datLibrary = require('../../dat/library') const archivesDb = require('../../dbs/archives') const {timer} = require('../../lib/time') -const scopedFSes = require('../../lib/scoped-fses') const { DAT_MANIFEST_FILENAME, DAT_CONFIGURABLE_FIELDS, diff --git a/web-apis/bg/experimental/peer-socket.js b/web-apis/bg/experimental/peer-socket.js new file mode 100644 index 00000000..36ed3cd5 --- /dev/null +++ b/web-apis/bg/experimental/peer-socket.js @@ -0,0 +1,72 @@ +const emitStream = require('emit-stream') +const globals = require('../../../globals') +const PeerSocket = require('../../../hyperswarm/peer-socket') + +// constants +// = + +const API_DOCS_URL = 'https://beakerbrowser.com/docs/apis/experimental-peersocket.html' +const API_PERM_ID = 'experimentalPeerSocket' +const LAB_API_ID = 'peerSocket' +const LAB_PERMS_OBJ = {perm: API_PERM_ID, labApi: LAB_API_ID, apiDocsUrl: API_DOCS_URL} + +// exported api +// = + +module.exports = { + async joinLobby (tabIdentity, lobbyType, lobbyName) { + await globals.permsAPI.checkLabsPerm(Object.assign({sender: this.sender}, LAB_PERMS_OBJ)) + var lobby = await PeerSocket.getOrCreateLobby(this.sender, tabIdentity, lobbyType, lobbyName) + return { + sessionData: lobby.self.sessionData + } + }, + + async leaveLobby (tabIdentity, lobbyType, lobbyName) { + await globals.permsAPI.checkLabsPerm(Object.assign({sender: this.sender}, LAB_PERMS_OBJ)) + PeerSocket.leaveLobby(this.sender, tabIdentity, lobbyType, lobbyName) + }, + + getActiveSocketsInLobby (tabIdentity, lobbyType, lobbyName) { + // NOTE + // this method isn't async so it can't await the perms check + // the frontend code is constructed so that `joinLobby` has to succeed prior to this function being available + // -prf + var lobby = PeerSocket.getLobby(this.sender, tabIdentity, lobbyType, lobbyName) + if (lobby) { + return Array.from(lobby.connections).map(({id}) => ({id})) // extract only the id + } + return [] + }, + + async setLobbySessionData (tabIdentity, lobbyType, lobbyName, sessionData) { + await globals.permsAPI.checkLabsPerm(Object.assign({sender: this.sender}, LAB_PERMS_OBJ)) + return PeerSocket.setLobbySessionData(this.sender, tabIdentity, lobbyType, lobbyName, sessionData) + }, + + async createLobbyEventStream (tabIdentity, lobbyType, lobbyName) { + await globals.permsAPI.checkLabsPerm(Object.assign({sender: this.sender}, LAB_PERMS_OBJ)) + var lobby = PeerSocket.getLobby(this.sender, tabIdentity, lobbyType, lobbyName) + if (lobby) { + return emitStream(lobby) + } + throw new Error('Lobby is not active') + }, + + async socketSend (tabIdentity, lobbyType, lobbyName, socketId, content) { + await globals.permsAPI.checkLabsPerm(Object.assign({sender: this.sender}, LAB_PERMS_OBJ)) + var conn = PeerSocket.getLobbyConnection(this.sender, tabIdentity, lobbyType, lobbyName, socketId) + if (conn) { + return PeerSocket.sendMessage(conn, {content}) + } + throw new Error('Socket is closed') + }, + + async createSocketEventStream (tabIdentity, lobbyType, lobbyName, socketId) { + await globals.permsAPI.checkLabsPerm(Object.assign({sender: this.sender}, LAB_PERMS_OBJ)) + var conn = PeerSocket.getLobbyConnection(this.sender, tabIdentity, lobbyType, lobbyName, socketId) + if (conn) { + return emitStream(conn.events) + } + } +} diff --git a/web-apis/bg/spell-checker.js b/web-apis/bg/spell-checker.js index cb719e90..bdf875a0 100644 --- a/web-apis/bg/spell-checker.js +++ b/web-apis/bg/spell-checker.js @@ -25,4 +25,4 @@ let self = module.exports = { add (text) { spellCheckerLib.spellchecker.add(text) } -} \ No newline at end of file +} diff --git a/web-apis/fg/experimental.js b/web-apis/fg/experimental.js index e8f98585..85cb6e79 100644 --- a/web-apis/fg/experimental.js +++ b/web-apis/fg/experimental.js @@ -8,6 +8,8 @@ const experimentalGlobalFetchManifest = require('../manifests/external/experimen const experimentalCapturePageManifest = require('../manifests/external/experimental/capture-page') const experimentalDatPeersManifest = require('../manifests/external/experimental/dat-peers') +const PeerSocketAPI = require('./peer-socket') + exports.setup = function (rpc) { const experimental = {} const opts = {timeout: false, errors} @@ -56,7 +58,7 @@ exports.setup = function (rpc) { datPeersRPC.send(this.id, data) } } - function prepDatPeersEvents (event, details) { + const prepDatPeersEvents = (event, details) => { var peer = new DatPeer(details.peerId, details.sessionData) delete details.peerId delete details.sessionData @@ -76,6 +78,9 @@ exports.setup = function (rpc) { experimental.datPeers.broadcast = datPeersRPC.broadcast experimental.datPeers.getSessionData = datPeersRPC.getSessionData experimental.datPeers.setSessionData = datPeersRPC.setSessionData + + // experimental.PeerSocket + experimental.PeerSocket = PeerSocketAPI.setup(rpc) } return experimental diff --git a/web-apis/fg/peer-socket.js b/web-apis/fg/peer-socket.js new file mode 100644 index 00000000..fc487183 --- /dev/null +++ b/web-apis/fg/peer-socket.js @@ -0,0 +1,159 @@ +/* globals ReadableStream WritableStream */ + +const errors = require('beaker-error-constants') +const {EventTarget, Event, fromEventStream} = require('./event-target') +const experimentalPeerSocketManifest = require('../manifests/external/experimental/peer-socket') + +exports.setup = function (rpc) { + var TAB_IDENT = 0 + var CAN_CHANGE_TAB_IDENT = true + const peerSocketRPC = rpc.importAPI('experimental-peer-socket', experimentalPeerSocketManifest, {timeout: false, errors}) + + class PeerSocketLobby extends EventTarget { + constructor (type, name, lobbyInitInfo) { + super() + setImmutableAttr(this, 'type', type) + setImmutableAttr(this, 'name', name) + this.self = { + sessionData: lobbyInitInfo.sessionData, + setSessionData: async (sessionData) => { + await peerSocketRPC.setLobbySessionData(TAB_IDENT, this.type, this.name, sessionData) + this.self.sessionData = sessionData + } + } + this.closed = false + + // wire up the events + var s = fromEventStream(peerSocketRPC.createLobbyEventStream(TAB_IDENT, this.type, this.name)) + s.addEventListener('connection', ({socketInfo}) => { + console.log('new connection', socketInfo) + this.dispatchEvent(new Event('connection', {target: this, socket: new PeerSocket(this, socketInfo)})) + }) + s.addEventListener('self-session-data', ({sessionData}) => { + this.self.sessionData = sessionData + }) + s.addEventListener('leave', () => { + this.closed = true + s.close() + this.dispatchEvent(new Event('leave')) + }) + } + + getSockets () { + return peerSocketRPC.getActiveSocketsInLobby(TAB_IDENT, this.type, this.name).map(si => new PeerSocket(this, si)) + } + + async leave () { + await peerSocketRPC.leaveLobby(TAB_IDENT, this.type, this.name) + } + + createSocketStream () { + var connectionEventHandler + return new ReadableStream({ + start: (controller) => { + // handle socket events + connectionEventHandler = e => controller.enqueue(e.socket) + this.addEventListener('connection', connectionEventHandler) + + // push all existing sockets + var sockets = this.getSockets() + sockets.forEach(socket => controller.enqueue(socket)) + }, + cancel: () => { + this.removeEventListener('connection', connectionEventHandler) + } + }) + } + } + + class PeerSocket extends EventTarget { + constructor (lobby, socketInfo) { + super() + setImmutableAttr(this, 'id', socketInfo.id) + setImmutableAttr(this, 'lobby', lobby) + this.sessionData = socketInfo.sessionData + + // wire up the events + var s = fromEventStream(peerSocketRPC.createSocketEventStream(TAB_IDENT, this.lobby.type, this.lobby.name, this.id)) + s.addEventListener('message', ({message}) => this.dispatchEvent(new Event('message', {target: this, message}))) + s.addEventListener('session-data', ({sessionData}) => { + this.sessionData = sessionData + this.dispatchEvent(new Event('session-data', {target: this, sessionData})) + }) + s.addEventListener('close', evt => { + this.dispatchEvent(new Event('close', {target: this})) + s.close() + }) + } + + // open lobby + static async joinOpenLobby (lobbyName) { + // tab identities are now locked + CAN_CHANGE_TAB_IDENT = false + + // join an instantiate + var lobbyInitInfo = await peerSocketRPC.joinLobby(TAB_IDENT, 'open', lobbyName) + return new PeerSocketLobby('open', lobbyName, lobbyInitInfo) + } + + // origin-specific lobby + static async joinSiteLobby () { + // tab identities are now locked + CAN_CHANGE_TAB_IDENT = false + + // join an instantiate + var lobbyInitInfo = await peerSocketRPC.joinLobby(TAB_IDENT, 'origin', window.location.origin) + return new PeerSocketLobby('origin', window.location.origin, lobbyInitInfo) + } + + static setDebugIdentity (n) { + if (!CAN_CHANGE_TAB_IDENT) { + throw new Error('setDebugIdentity must be called before joining any lobbies') + } + TAB_IDENT = +n || 0 + } + + async write (data) { + peerSocketRPC.socketSend(TAB_IDENT, this.lobby.type, this.lobby.name, this.id, data) + } + + createReadStream () { + var messageEventHandler + const cancel = () => this.removeEventListener('message', messageEventHandler) + return new ReadableStream({ + start: (controller) => { + messageEventHandler = e => controller.enqueue(e.message) + this.addEventListener('message', messageEventHandler) + this.addEventListener('close', () => { + controller.close() + cancel() + }) + }, + cancel + }) + } + + createWriteStream () { + return new WritableStream({ + write: (data) => this.write(data) + // NOTE + // currently not possible to close or cancel a peersocket + // -prf + }) + } + + createDuplexStream () { + return {readable: this.createReadStream(), writable: this.createWriteStream()} + } + } + + function setImmutableAttr (obj, name, value) { + Object.defineProperty(obj, name, { + value, + enumerable: true, + writable: false + }) + } + + return PeerSocket +} diff --git a/web-apis/fg/spell-checker.js b/web-apis/fg/spell-checker.js index 32d81e66..9edaa918 100644 --- a/web-apis/fg/spell-checker.js +++ b/web-apis/fg/spell-checker.js @@ -1,6 +1,6 @@ const spellCheckerManifest = require('../manifests/external/spell-checker') module.exports = function (rpc) { - // create the rpc apis - return rpc.importAPI('spell-checker', spellCheckerManifest) -} \ No newline at end of file + // create the rpc apis + return rpc.importAPI('spell-checker', spellCheckerManifest) +} diff --git a/web-apis/manifests/external/experimental/peer-socket.js b/web-apis/manifests/external/experimental/peer-socket.js new file mode 100644 index 00000000..358e28d4 --- /dev/null +++ b/web-apis/manifests/external/experimental/peer-socket.js @@ -0,0 +1,10 @@ +module.exports = { + joinLobby: 'promise', + leaveLobby: 'promise', + getActiveSocketsInLobby: 'sync', + setLobbySessionData: 'promise', + createLobbyEventStream: 'readable', + + socketSend: 'promise', + createSocketEventStream: 'readable' +}