Merge pull request #24 from shocknet/chat-improvements
Chat improvements
This commit is contained in:
commit
b4073bff1c
23 changed files with 2066 additions and 1640 deletions
|
|
@ -4,7 +4,6 @@
|
||||||
"rules": {
|
"rules": {
|
||||||
"prettier/prettier": "error",
|
"prettier/prettier": "error",
|
||||||
"strict": "off",
|
"strict": "off",
|
||||||
"id-length": ["error", { "exceptions": ["_"] }],
|
|
||||||
|
|
||||||
"no-console": "off",
|
"no-console": "off",
|
||||||
|
|
||||||
|
|
@ -65,7 +64,18 @@
|
||||||
"no-throw-literal": "off",
|
"no-throw-literal": "off",
|
||||||
|
|
||||||
// lightning has sync methods and this rule bans them
|
// lightning has sync methods and this rule bans them
|
||||||
"no-sync": "off"
|
"no-sync": "off",
|
||||||
|
|
||||||
|
"id-length": "off",
|
||||||
|
|
||||||
|
// typescript does this
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
|
||||||
|
// https://github.com/prettier/eslint-config-prettier/issues/132
|
||||||
|
"line-comment-position": "off",
|
||||||
|
|
||||||
|
// if someone does this it's probably intentional
|
||||||
|
"no-useless-concat": "off"
|
||||||
},
|
},
|
||||||
"parser": "babel-eslint",
|
"parser": "babel-eslint",
|
||||||
"env": {
|
"env": {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
"google-proto-files": "^1.0.3",
|
"google-proto-files": "^1.0.3",
|
||||||
"graphviz": "0.0.8",
|
"graphviz": "0.0.8",
|
||||||
"grpc": "^1.21.1",
|
"grpc": "^1.21.1",
|
||||||
"gun": "^0.2019.1211",
|
"gun": "git://github.com/amark/gun#c59e0e95f92779ce6bb3aab823d318bc16b20c33",
|
||||||
"husky": "^3.0.9",
|
"husky": "^3.0.9",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
"jsonwebtoken": "^8.3.0",
|
"jsonwebtoken": "^8.3.0",
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
"@types/express": "^4.17.1",
|
"@types/express": "^4.17.1",
|
||||||
"@types/gun": "^0.9.1",
|
"@types/gun": "^0.9.1",
|
||||||
"@types/jest": "^24.0.18",
|
"@types/jest": "^24.0.18",
|
||||||
|
"@types/jsonwebtoken": "^8.3.7",
|
||||||
"@types/lodash": "^4.14.141",
|
"@types/lodash": "^4.14.141",
|
||||||
"@types/socket.io": "^2.1.3",
|
"@types/socket.io": "^2.1.3",
|
||||||
"@types/socket.io-client": "^1.4.32",
|
"@types/socket.io-client": "^1.4.32",
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
const Gun = require('gun')
|
const Gun = require('gun')
|
||||||
|
// @ts-ignore
|
||||||
|
require('gun/lib/open')
|
||||||
const debounce = require('lodash/debounce')
|
const debounce = require('lodash/debounce')
|
||||||
const once = require('lodash/once')
|
|
||||||
const Encryption = require('../../../utils/encryptionStore')
|
const Encryption = require('../../../utils/encryptionStore')
|
||||||
|
const logger = require('winston')
|
||||||
|
|
||||||
/** @type {import('../contact-api/SimpleGUN').ISEA} */
|
/** @type {import('../contact-api/SimpleGUN').ISEA} */
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
@ -21,7 +23,10 @@ const IS_GUN_AUTH = 'IS_GUN_AUTH'
|
||||||
|
|
||||||
mySEA.encrypt = (msg, secret) => {
|
mySEA.encrypt = (msg, secret) => {
|
||||||
if (typeof msg !== 'string') {
|
if (typeof msg !== 'string') {
|
||||||
throw new TypeError('mySEA.encrypt() -> expected msg to be an string')
|
throw new TypeError(
|
||||||
|
'mySEA.encrypt() -> expected msg to be an string instead got: ' +
|
||||||
|
typeof msg
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.length === 0) {
|
if (msg.length === 0) {
|
||||||
|
|
@ -40,7 +45,10 @@ mySEA.encrypt = (msg, secret) => {
|
||||||
|
|
||||||
mySEA.decrypt = (encMsg, secret) => {
|
mySEA.decrypt = (encMsg, secret) => {
|
||||||
if (typeof encMsg !== 'string') {
|
if (typeof encMsg !== 'string') {
|
||||||
throw new TypeError('mySEA.encrypt() -> expected encMsg to be an string')
|
throw new TypeError(
|
||||||
|
'mySEA.encrypt() -> expected encMsg to be an string instead got: ' +
|
||||||
|
typeof encMsg
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encMsg.length === 0) {
|
if (encMsg.length === 0) {
|
||||||
|
|
@ -79,6 +87,12 @@ mySEA.decrypt = (encMsg, secret) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
mySEA.secret = (recipientOrSenderEpub, recipientOrSenderSEA) => {
|
mySEA.secret = (recipientOrSenderEpub, recipientOrSenderSEA) => {
|
||||||
|
if (typeof recipientOrSenderEpub !== 'string') {
|
||||||
|
throw new TypeError('epub has to be an string')
|
||||||
|
}
|
||||||
|
if (typeof recipientOrSenderSEA !== 'object') {
|
||||||
|
throw new TypeError('sea has to be an object')
|
||||||
|
}
|
||||||
if (recipientOrSenderEpub === recipientOrSenderSEA.pub) {
|
if (recipientOrSenderEpub === recipientOrSenderSEA.pub) {
|
||||||
throw new Error('Do not use pub for mysecret')
|
throw new Error('Do not use pub for mysecret')
|
||||||
}
|
}
|
||||||
|
|
@ -117,6 +131,7 @@ const Event = require('../event-constants')
|
||||||
* @typedef {object} SimpleSocket
|
* @typedef {object} SimpleSocket
|
||||||
* @prop {(eventName: string, data: Emission) => void} emit
|
* @prop {(eventName: string, data: Emission) => void} emit
|
||||||
* @prop {(eventName: string, handler: (data: any) => void) => void} on
|
* @prop {(eventName: string, handler: (data: any) => void) => void} on
|
||||||
|
* @prop {{ query: { 'x-shockwallet-device-id': string }}} handshake
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable init-declarations */
|
/* eslint-disable init-declarations */
|
||||||
|
|
@ -133,6 +148,11 @@ let user
|
||||||
let _currentAlias = ''
|
let _currentAlias = ''
|
||||||
let _currentPass = ''
|
let _currentPass = ''
|
||||||
|
|
||||||
|
let mySec = ''
|
||||||
|
|
||||||
|
/** @returns {string} */
|
||||||
|
const getMySecret = () => mySec
|
||||||
|
|
||||||
let _isAuthenticating = false
|
let _isAuthenticating = false
|
||||||
let _isRegistering = false
|
let _isRegistering = false
|
||||||
|
|
||||||
|
|
@ -179,19 +199,16 @@ const authenticate = async (alias, pass) => {
|
||||||
if (typeof ack.err === 'string') {
|
if (typeof ack.err === 'string') {
|
||||||
throw new Error(ack.err)
|
throw new Error(ack.err)
|
||||||
} else if (typeof ack.sea === 'object') {
|
} else if (typeof ack.sea === 'object') {
|
||||||
API.Jobs.onAcceptedRequests(user, mySEA)
|
mySec = await mySEA.secret(user._.sea.epub, user._.sea)
|
||||||
API.Jobs.onOrders(user, gun, mySEA)
|
|
||||||
|
|
||||||
const mySec = await mySEA.secret(user._.sea.epub, user._.sea)
|
|
||||||
if (typeof mySec !== 'string') {
|
|
||||||
throw new TypeError('mySec not an string')
|
|
||||||
}
|
|
||||||
|
|
||||||
_currentAlias = user.is ? user.is.alias : ''
|
_currentAlias = user.is ? user.is.alias : ''
|
||||||
_currentPass = await mySEA.encrypt(pass, mySec)
|
_currentPass = await mySEA.encrypt(pass, mySec)
|
||||||
|
|
||||||
await new Promise(res => setTimeout(res, 5000))
|
await new Promise(res => setTimeout(res, 5000))
|
||||||
|
|
||||||
|
API.Jobs.onAcceptedRequests(user, mySEA)
|
||||||
|
API.Jobs.onOrders(user, gun, mySEA)
|
||||||
|
|
||||||
return ack.sea.pub
|
return ack.sea.pub
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown error.')
|
throw new Error('Unknown error.')
|
||||||
|
|
@ -305,6 +322,7 @@ class Mediator {
|
||||||
this.socket.on(Action.SET_DISPLAY_NAME, this.setDisplayName)
|
this.socket.on(Action.SET_DISPLAY_NAME, this.setDisplayName)
|
||||||
this.socket.on(Action.SEND_PAYMENT, this.sendPayment)
|
this.socket.on(Action.SEND_PAYMENT, this.sendPayment)
|
||||||
this.socket.on(Action.SET_BIO, this.setBio)
|
this.socket.on(Action.SET_BIO, this.setBio)
|
||||||
|
this.socket.on(Action.DISCONNECT, this.disconnect)
|
||||||
|
|
||||||
this.socket.on(Event.ON_AVATAR, this.onAvatar)
|
this.socket.on(Event.ON_AVATAR, this.onAvatar)
|
||||||
this.socket.on(Event.ON_BLACKLIST, this.onBlacklist)
|
this.socket.on(Event.ON_BLACKLIST, this.onBlacklist)
|
||||||
|
|
@ -319,20 +337,26 @@ class Mediator {
|
||||||
this.socket.on(IS_GUN_AUTH, this.isGunAuth)
|
this.socket.on(IS_GUN_AUTH, this.isGunAuth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {SimpleSocket} socket */
|
||||||
encryptSocketInstance = socket => {
|
encryptSocketInstance = socket => {
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
* @type {SimpleSocket['on']}
|
||||||
|
*/
|
||||||
on: (eventName, cb) => {
|
on: (eventName, cb) => {
|
||||||
const deviceId = socket.handshake.query['x-shockwallet-device-id']
|
const deviceId = socket.handshake.query['x-shockwallet-device-id']
|
||||||
socket.on(eventName, data => {
|
socket.on(eventName, _data => {
|
||||||
try {
|
try {
|
||||||
if (Encryption.isNonEncrypted(eventName)) {
|
if (Encryption.isNonEncrypted(eventName)) {
|
||||||
return cb(data)
|
return cb(_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!_data) {
|
||||||
return cb(data)
|
return cb(_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let data = _data
|
||||||
|
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
const error = {
|
const error = {
|
||||||
field: 'deviceId',
|
field: 'deviceId',
|
||||||
|
|
@ -350,16 +374,9 @@ class Mediator {
|
||||||
console.error('Unknown Device', error)
|
console.error('Unknown Device', error)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
console.log('Emitting Data...', data)
|
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
data = JSON.parse(data)
|
data = JSON.parse(data)
|
||||||
}
|
}
|
||||||
console.log('Event:', eventName)
|
|
||||||
console.log('Data:', data)
|
|
||||||
console.log('Decrypt params:', {
|
|
||||||
deviceId,
|
|
||||||
message: data.encryptedKey
|
|
||||||
})
|
|
||||||
const decryptedKey = Encryption.decryptKey({
|
const decryptedKey = Encryption.decryptKey({
|
||||||
deviceId,
|
deviceId,
|
||||||
message: data.encryptedKey
|
message: data.encryptedKey
|
||||||
|
|
@ -377,6 +394,7 @@ class Mediator {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
/** @type {SimpleSocket['emit']} */
|
||||||
emit: (eventName, data) => {
|
emit: (eventName, data) => {
|
||||||
try {
|
try {
|
||||||
if (Encryption.isNonEncrypted(eventName)) {
|
if (Encryption.isNonEncrypted(eventName)) {
|
||||||
|
|
@ -392,8 +410,10 @@ class Mediator {
|
||||||
deviceId
|
deviceId
|
||||||
})
|
})
|
||||||
: data
|
: data
|
||||||
|
|
||||||
socket.emit(eventName, encryptedMessage)
|
socket.emit(eventName, encryptedMessage)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
logger.error(err.message)
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -436,29 +456,6 @@ class Mediator {
|
||||||
msg: null,
|
msg: null,
|
||||||
origBody: body
|
origBody: body
|
||||||
})
|
})
|
||||||
|
|
||||||
// refresh received requests
|
|
||||||
API.Events.onSimplerReceivedRequests(
|
|
||||||
debounce(
|
|
||||||
once(receivedRequests => {
|
|
||||||
if (Config.SHOW_LOG) {
|
|
||||||
console.log('---received requests---')
|
|
||||||
console.log(receivedRequests)
|
|
||||||
console.log('-----------------------')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.emit(Event.ON_RECEIVED_REQUESTS, {
|
|
||||||
msg: receivedRequests,
|
|
||||||
ok: true,
|
|
||||||
origBody: body
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
300
|
|
||||||
),
|
|
||||||
gun,
|
|
||||||
user,
|
|
||||||
mySEA
|
|
||||||
)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
this.socket.emit(Action.ACCEPT_REQUEST, {
|
this.socket.emit(Action.ACCEPT_REQUEST, {
|
||||||
|
|
@ -508,7 +505,7 @@ class Mediator {
|
||||||
|
|
||||||
await throwOnInvalidToken(token)
|
await throwOnInvalidToken(token)
|
||||||
|
|
||||||
await API.Actions.generateHandshakeAddress(user)
|
await API.Actions.generateHandshakeAddress()
|
||||||
|
|
||||||
this.socket.emit(Action.GENERATE_NEW_HANDSHAKE_NODE, {
|
this.socket.emit(Action.GENERATE_NEW_HANDSHAKE_NODE, {
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|
@ -562,22 +559,6 @@ class Mediator {
|
||||||
msg: null,
|
msg: null,
|
||||||
origBody: body
|
origBody: body
|
||||||
})
|
})
|
||||||
|
|
||||||
API.Events.onSimplerSentRequests(
|
|
||||||
debounce(
|
|
||||||
once(srs => {
|
|
||||||
this.socket.emit(Event.ON_SENT_REQUESTS, {
|
|
||||||
ok: true,
|
|
||||||
msg: srs,
|
|
||||||
origBody: body
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
350
|
|
||||||
),
|
|
||||||
gun,
|
|
||||||
user,
|
|
||||||
mySEA
|
|
||||||
)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (Config.SHOW_LOG) {
|
if (Config.SHOW_LOG) {
|
||||||
console.log('\n')
|
console.log('\n')
|
||||||
|
|
@ -815,24 +796,19 @@ class Mediator {
|
||||||
|
|
||||||
await throwOnInvalidToken(token)
|
await throwOnInvalidToken(token)
|
||||||
|
|
||||||
API.Events.onChats(
|
API.Events.onChats(chats => {
|
||||||
chats => {
|
if (Config.SHOW_LOG) {
|
||||||
if (Config.SHOW_LOG) {
|
console.log('---chats---')
|
||||||
console.log('---chats---')
|
console.log(chats)
|
||||||
console.log(chats)
|
console.log('-----------------------')
|
||||||
console.log('-----------------------')
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.emit(Event.ON_CHATS, {
|
this.socket.emit(Event.ON_CHATS, {
|
||||||
msg: chats,
|
msg: chats,
|
||||||
ok: true,
|
ok: true,
|
||||||
origBody: body
|
origBody: body
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
gun,
|
|
||||||
user,
|
|
||||||
mySEA
|
|
||||||
)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
this.socket.emit(Event.ON_CHATS, {
|
this.socket.emit(Event.ON_CHATS, {
|
||||||
|
|
@ -916,24 +892,19 @@ class Mediator {
|
||||||
|
|
||||||
await throwOnInvalidToken(token)
|
await throwOnInvalidToken(token)
|
||||||
|
|
||||||
API.Events.onSimplerReceivedRequests(
|
API.Events.onSimplerReceivedRequests(receivedRequests => {
|
||||||
receivedRequests => {
|
if (Config.SHOW_LOG) {
|
||||||
if (Config.SHOW_LOG) {
|
console.log('---receivedRequests---')
|
||||||
console.log('---receivedRequests---')
|
console.log(receivedRequests)
|
||||||
console.log(receivedRequests)
|
console.log('-----------------------')
|
||||||
console.log('-----------------------')
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.emit(Event.ON_RECEIVED_REQUESTS, {
|
this.socket.emit(Event.ON_RECEIVED_REQUESTS, {
|
||||||
msg: receivedRequests,
|
msg: receivedRequests,
|
||||||
ok: true,
|
ok: true,
|
||||||
origBody: body
|
origBody: body
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
gun,
|
|
||||||
user,
|
|
||||||
mySEA
|
|
||||||
)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
this.socket.emit(Event.ON_RECEIVED_REQUESTS, {
|
this.socket.emit(Event.ON_RECEIVED_REQUESTS, {
|
||||||
|
|
@ -944,6 +915,8 @@ class Mediator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSentRequestsSubbed = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Readonly<{ token: string }>} body
|
* @param {Readonly<{ token: string }>} body
|
||||||
*/
|
*/
|
||||||
|
|
@ -953,24 +926,22 @@ class Mediator {
|
||||||
|
|
||||||
await throwOnInvalidToken(token)
|
await throwOnInvalidToken(token)
|
||||||
|
|
||||||
await API.Events.onSimplerSentRequests(
|
if (!this.onSentRequestsSubbed) {
|
||||||
sentRequests => {
|
this.onSentRequestsSubbed = true
|
||||||
if (Config.SHOW_LOG) {
|
|
||||||
console.log('---sentRequests---')
|
|
||||||
console.log(sentRequests)
|
|
||||||
console.log('-----------------------')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.emit(Event.ON_SENT_REQUESTS, {
|
API.Events.onSimplerSentRequests(
|
||||||
msg: sentRequests,
|
debounce(sentRequests => {
|
||||||
ok: true,
|
console.log(
|
||||||
origBody: body
|
`new Reqss in mediator: ${JSON.stringify(sentRequests)}`
|
||||||
})
|
)
|
||||||
},
|
this.socket.emit(Event.ON_SENT_REQUESTS, {
|
||||||
gun,
|
msg: sentRequests,
|
||||||
user,
|
ok: true,
|
||||||
mySEA
|
origBody: body
|
||||||
)
|
})
|
||||||
|
}, 1000)
|
||||||
|
)
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
this.socket.emit(Event.ON_SENT_REQUESTS, {
|
this.socket.emit(Event.ON_SENT_REQUESTS, {
|
||||||
|
|
@ -1062,6 +1033,29 @@ class Mediator {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {Readonly<{ pub: string, token: string }>} body */
|
||||||
|
disconnect = async body => {
|
||||||
|
try {
|
||||||
|
const { pub, token } = body
|
||||||
|
|
||||||
|
await throwOnInvalidToken(token)
|
||||||
|
|
||||||
|
await API.Actions.disconnect(pub)
|
||||||
|
|
||||||
|
this.socket.emit(Action.DISCONNECT, {
|
||||||
|
ok: true,
|
||||||
|
msg: null,
|
||||||
|
origBody: body
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
this.socket.emit(Action.DISCONNECT, {
|
||||||
|
ok: false,
|
||||||
|
msg: err.message,
|
||||||
|
origBody: body
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1101,9 +1095,6 @@ const register = async (alias, pass) => {
|
||||||
_isRegistering = false
|
_isRegistering = false
|
||||||
|
|
||||||
const mySecret = await mySEA.secret(user._.sea.epub, user._.sea)
|
const mySecret = await mySEA.secret(user._.sea.epub, user._.sea)
|
||||||
if (typeof mySecret !== 'string') {
|
|
||||||
throw new Error('Could not generate secret for user.')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof ack.err === 'string') {
|
if (typeof ack.err === 'string') {
|
||||||
throw new Error(ack.err)
|
throw new Error(ack.err)
|
||||||
|
|
@ -1123,7 +1114,7 @@ const register = async (alias, pass) => {
|
||||||
|
|
||||||
return authenticate(alias, pass).then(async pub => {
|
return authenticate(alias, pass).then(async pub => {
|
||||||
await API.Actions.setDisplayName('anon' + pub.slice(0, 8), user)
|
await API.Actions.setDisplayName('anon' + pub.slice(0, 8), user)
|
||||||
await API.Actions.generateHandshakeAddress(user)
|
await API.Actions.generateHandshakeAddress()
|
||||||
await API.Actions.generateOrderAddress(user)
|
await API.Actions.generateOrderAddress(user)
|
||||||
return pub
|
return pub
|
||||||
})
|
})
|
||||||
|
|
@ -1163,5 +1154,6 @@ module.exports = {
|
||||||
instantiateGun,
|
instantiateGun,
|
||||||
getGun,
|
getGun,
|
||||||
getUser,
|
getUser,
|
||||||
mySEA
|
mySEA,
|
||||||
|
getMySecret
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ const Actions = {
|
||||||
SEND_PAYMENT: "SEND_PAYMENT",
|
SEND_PAYMENT: "SEND_PAYMENT",
|
||||||
SET_AVATAR: "SET_AVATAR",
|
SET_AVATAR: "SET_AVATAR",
|
||||||
SET_DISPLAY_NAME: "SET_DISPLAY_NAME",
|
SET_DISPLAY_NAME: "SET_DISPLAY_NAME",
|
||||||
SET_BIO: "SET_BIO"
|
SET_BIO: "SET_BIO",
|
||||||
|
DISCONNECT: "DISCONNECT"
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Actions;
|
module.exports = Actions;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ export type ListenerObj = Record<string, ListenerObjSoul | Primitive | null> & {
|
||||||
|
|
||||||
export type ListenerData = Primitive | null | ListenerObj | undefined
|
export type ListenerData = Primitive | null | ListenerObj | undefined
|
||||||
|
|
||||||
|
interface OpenListenerDataObj {
|
||||||
|
[k: string]: OpenListenerData
|
||||||
|
}
|
||||||
|
|
||||||
export type Listener = (data: ListenerData, key: string) => void
|
export type Listener = (data: ListenerData, key: string) => void
|
||||||
export type Callback = (ack: Ack) => void
|
export type Callback = (ack: Ack) => void
|
||||||
|
|
||||||
|
|
@ -31,14 +35,19 @@ export interface Soul {
|
||||||
put: Primitive | null | object | undefined
|
put: Primitive | null | object | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GUNNode {
|
export type OpenListenerData = Primitive | null | OpenListenerDataObj
|
||||||
|
export type OpenListener = (data: OpenListenerData, key: string) => void
|
||||||
|
|
||||||
|
export interface GUNNodeBase {
|
||||||
_: Soul
|
_: Soul
|
||||||
get(key: string): GUNNode
|
|
||||||
map(): GUNNode
|
map(): GUNNode
|
||||||
put(data: ValidDataValue | GUNNode, cb?: Callback): GUNNode
|
|
||||||
on(this: GUNNode, cb: Listener): void
|
on(this: GUNNode, cb: Listener): void
|
||||||
once(this: GUNNode, cb?: Listener): GUNNode
|
once(this: GUNNode, cb?: Listener): GUNNode
|
||||||
set(data: ValidDataValue | GUNNode, cb?: Callback): GUNNode
|
|
||||||
|
open(this: GUNNode, cb?: OpenListener): GUNNode
|
||||||
|
|
||||||
off(): void
|
off(): void
|
||||||
user(): UserGUNNode
|
user(): UserGUNNode
|
||||||
user(epub: string): GUNNode
|
user(epub: string): GUNNode
|
||||||
|
|
@ -47,6 +56,12 @@ export interface GUNNode {
|
||||||
then<T>(cb: (v: ListenerData) => T): Promise<ListenerData>
|
then<T>(cb: (v: ListenerData) => T): Promise<ListenerData>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GUNNode extends GUNNodeBase {
|
||||||
|
get(key: string): GUNNode
|
||||||
|
put(data: ValidDataValue | GUNNode, cb?: Callback): GUNNode
|
||||||
|
set(data: ValidDataValue | GUNNode, cb?: Callback): GUNNode
|
||||||
|
}
|
||||||
|
|
||||||
export interface CreateAck {
|
export interface CreateAck {
|
||||||
pub: string | undefined
|
pub: string | undefined
|
||||||
err: string | undefined
|
err: string | undefined
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ const ErrorCode = require('./errorCode')
|
||||||
const Getters = require('./getters')
|
const Getters = require('./getters')
|
||||||
const Key = require('./key')
|
const Key = require('./key')
|
||||||
const Utils = require('./utils')
|
const Utils = require('./utils')
|
||||||
|
// const { promisifyGunNode: p } = Utils
|
||||||
const { isHandshakeRequest } = require('./schema')
|
const { isHandshakeRequest } = require('./schema')
|
||||||
/**
|
/**
|
||||||
* @typedef {import('./SimpleGUN').GUNNode} GUNNode
|
* @typedef {import('./SimpleGUN').GUNNode} GUNNode
|
||||||
|
|
@ -28,14 +29,6 @@ const { isHandshakeRequest } = require('./schema')
|
||||||
*/
|
*/
|
||||||
const INITIAL_MSG = '$$__SHOCKWALLET__INITIAL__MESSAGE'
|
const INITIAL_MSG = '$$__SHOCKWALLET__INITIAL__MESSAGE'
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Message}
|
|
||||||
*/
|
|
||||||
const __createInitialMessage = () => ({
|
|
||||||
body: INITIAL_MSG,
|
|
||||||
timestamp: Date.now()
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a an outgoing feed. The feed will have an initial special acceptance
|
* Create a an outgoing feed. The feed will have an initial special acceptance
|
||||||
* message. Returns a promise that resolves to the id of the newly-created
|
* message. Returns a promise that resolves to the id of the newly-created
|
||||||
|
|
@ -57,13 +50,12 @@ const __createOutgoingFeed = async (withPublicKey, user, SEA) => {
|
||||||
throw new Error(ErrorCode.NOT_AUTH)
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
|
const mySecret = require('../Mediator').getMySecret()
|
||||||
if (typeof mySecret !== 'string') {
|
|
||||||
throw new TypeError(
|
|
||||||
"__createOutgoingFeed() -> typeof mySecret !== 'string'"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const encryptedForMeRecipientPub = await SEA.encrypt(withPublicKey, mySecret)
|
const encryptedForMeRecipientPub = await SEA.encrypt(withPublicKey, mySecret)
|
||||||
|
const ourSecret = await SEA.secret(
|
||||||
|
await Utils.pubToEpub(withPublicKey),
|
||||||
|
user._.sea
|
||||||
|
)
|
||||||
|
|
||||||
const maybeEncryptedForMeOutgoingFeedID = await Utils.tryAndWait(
|
const maybeEncryptedForMeOutgoingFeedID = await Utils.tryAndWait(
|
||||||
(_, user) =>
|
(_, user) =>
|
||||||
|
|
@ -103,12 +95,18 @@ const __createOutgoingFeed = async (withPublicKey, user, SEA) => {
|
||||||
throw new TypeError('typeof newOutgoingFeedID !== "string"')
|
throw new TypeError('typeof newOutgoingFeedID !== "string"')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {Message} */
|
||||||
|
const initialMsg = {
|
||||||
|
body: await SEA.encrypt(INITIAL_MSG, ourSecret),
|
||||||
|
timestamp: Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
user
|
user
|
||||||
.get(Key.OUTGOINGS)
|
.get(Key.OUTGOINGS)
|
||||||
.get(newOutgoingFeedID)
|
.get(newOutgoingFeedID)
|
||||||
.get(Key.MESSAGES)
|
.get(Key.MESSAGES)
|
||||||
.set(__createInitialMessage(), ack => {
|
.set(initialMsg, ack => {
|
||||||
if (ack.err) {
|
if (ack.err) {
|
||||||
rej(new Error(ack.err))
|
rej(new Error(ack.err))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -122,12 +120,6 @@ const __createOutgoingFeed = async (withPublicKey, user, SEA) => {
|
||||||
mySecret
|
mySecret
|
||||||
)
|
)
|
||||||
|
|
||||||
if (typeof encryptedForMeNewOutgoingFeedID === 'undefined') {
|
|
||||||
throw new TypeError(
|
|
||||||
"typeof encryptedForMeNewOutgoingFeedID === 'undefined'"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
user
|
user
|
||||||
.get(Key.RECIPIENT_TO_OUTGOING)
|
.get(Key.RECIPIENT_TO_OUTGOING)
|
||||||
|
|
@ -253,10 +245,7 @@ const acceptRequest = async (
|
||||||
SEA
|
SEA
|
||||||
)
|
)
|
||||||
|
|
||||||
const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
|
const mySecret = require('../Mediator').getMySecret()
|
||||||
if (typeof mySecret !== 'string') {
|
|
||||||
throw new TypeError("acceptRequest() -> typeof mySecret !== 'string'")
|
|
||||||
}
|
|
||||||
const encryptedForMeIncomingID = await SEA.encrypt(incomingID, mySecret)
|
const encryptedForMeIncomingID = await SEA.encrypt(incomingID, mySecret)
|
||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
|
|
@ -363,17 +352,15 @@ const blacklist = (publicKey, user) =>
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {UserGUNNode} user
|
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
const generateHandshakeAddress = user =>
|
const generateHandshakeAddress = async () => {
|
||||||
new Promise((res, rej) => {
|
const gun = require('../Mediator').getGun()
|
||||||
if (!user.is) {
|
const user = require('../Mediator').getUser()
|
||||||
throw new Error(ErrorCode.NOT_AUTH)
|
|
||||||
}
|
|
||||||
|
|
||||||
const address = uuidv1()
|
const address = uuidv1()
|
||||||
|
|
||||||
|
await new Promise((res, rej) => {
|
||||||
user.get(Key.CURRENT_HANDSHAKE_ADDRESS).put(address, ack => {
|
user.get(Key.CURRENT_HANDSHAKE_ADDRESS).put(address, ack => {
|
||||||
if (ack.err) {
|
if (ack.err) {
|
||||||
rej(new Error(ack.err))
|
rej(new Error(ack.err))
|
||||||
|
|
@ -383,6 +370,86 @@ const generateHandshakeAddress = user =>
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
gun
|
||||||
|
.get(Key.HANDSHAKE_NODES)
|
||||||
|
.get(address)
|
||||||
|
.put({ unused: 0 }, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} pub
|
||||||
|
* @throws {Error}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const cleanup = async pub => {
|
||||||
|
const user = require('../Mediator').getUser()
|
||||||
|
|
||||||
|
const outGoingID = await Utils.recipientToOutgoingID(pub)
|
||||||
|
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
user
|
||||||
|
.get(Key.USER_TO_INCOMING)
|
||||||
|
.get(pub)
|
||||||
|
.put(null, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
user
|
||||||
|
.get(Key.RECIPIENT_TO_OUTGOING)
|
||||||
|
.get(pub)
|
||||||
|
.put(null, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
user
|
||||||
|
.get(Key.USER_TO_LAST_REQUEST_SENT)
|
||||||
|
.get(pub)
|
||||||
|
.put(null, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (outGoingID) {
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
user
|
||||||
|
.get(Key.OUTGOINGS)
|
||||||
|
.get(outGoingID)
|
||||||
|
.put(null, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} recipientPublicKey
|
* @param {string} recipientPublicKey
|
||||||
* @param {GUNNode} gun
|
* @param {GUNNode} gun
|
||||||
|
|
@ -396,6 +463,8 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => {
|
||||||
throw new Error(ErrorCode.NOT_AUTH)
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await cleanup(recipientPublicKey)
|
||||||
|
|
||||||
if (typeof recipientPublicKey !== 'string') {
|
if (typeof recipientPublicKey !== 'string') {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
`recipientPublicKey is not string, got: ${typeof recipientPublicKey}`
|
`recipientPublicKey is not string, got: ${typeof recipientPublicKey}`
|
||||||
|
|
@ -406,6 +475,10 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => {
|
||||||
throw new TypeError('recipientPublicKey is an string of length 0')
|
throw new TypeError('recipientPublicKey is an string of length 0')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recipientPublicKey === user.is.pub) {
|
||||||
|
throw new Error('Do not send a request to yourself')
|
||||||
|
}
|
||||||
|
|
||||||
console.log('sendHR() -> before recipientEpub')
|
console.log('sendHR() -> before recipientEpub')
|
||||||
|
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
|
|
@ -413,7 +486,7 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => {
|
||||||
|
|
||||||
console.log('sendHR() -> before mySecret')
|
console.log('sendHR() -> before mySecret')
|
||||||
|
|
||||||
const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
|
const mySecret = require('../Mediator').getMySecret()
|
||||||
console.log('sendHR() -> before ourSecret')
|
console.log('sendHR() -> before ourSecret')
|
||||||
const ourSecret = await SEA.secret(recipientEpub, user._.sea)
|
const ourSecret = await SEA.secret(recipientEpub, user._.sea)
|
||||||
|
|
||||||
|
|
@ -509,6 +582,11 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => {
|
||||||
timestamp
|
timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const encryptedForMeRecipientPublicKey = await SEA.encrypt(
|
||||||
|
recipientPublicKey,
|
||||||
|
mySecret
|
||||||
|
)
|
||||||
|
|
||||||
console.log('sendHR() -> before newHandshakeRequestID')
|
console.log('sendHR() -> before newHandshakeRequestID')
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
const newHandshakeRequestID = await new Promise((res, rej) => {
|
const newHandshakeRequestID = await new Promise((res, rej) => {
|
||||||
|
|
@ -537,32 +615,8 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// save request id to REQUEST_TO_USER
|
|
||||||
|
|
||||||
const encryptedForMeRecipientPublicKey = await SEA.encrypt(
|
|
||||||
recipientPublicKey,
|
|
||||||
mySecret
|
|
||||||
)
|
|
||||||
|
|
||||||
// This needs to come before the write to sent requests. Because that write
|
// This needs to come before the write to sent requests. Because that write
|
||||||
// triggers Jobs.onAcceptedRequests and it in turn reads from request-to-user
|
// triggers Jobs.onAcceptedRequests and it in turn reads from request-to-user
|
||||||
// This also triggers Events.onSimplerSentRequests
|
|
||||||
await new Promise((res, rej) => {
|
|
||||||
user
|
|
||||||
.get(Key.REQUEST_TO_USER)
|
|
||||||
.get(newHandshakeRequestID)
|
|
||||||
.put(encryptedForMeRecipientPublicKey, ack => {
|
|
||||||
if (ack.err) {
|
|
||||||
rej(
|
|
||||||
new Error(
|
|
||||||
`Error saving recipient public key to request to user: ${ack.err}`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
res()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {StoredReq}
|
* @type {StoredReq}
|
||||||
|
|
@ -625,11 +679,7 @@ const sendMessage = async (recipientPublicKey, body, user, SEA) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const outgoingID = await Utils.recipientToOutgoingID(
|
const outgoingID = await Utils.recipientToOutgoingID(recipientPublicKey)
|
||||||
recipientPublicKey,
|
|
||||||
user,
|
|
||||||
SEA
|
|
||||||
)
|
|
||||||
|
|
||||||
if (outgoingID === null) {
|
if (outgoingID === null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -668,10 +718,9 @@ const sendMessage = async (recipientPublicKey, body, user, SEA) => {
|
||||||
* @param {string} recipientPub
|
* @param {string} recipientPub
|
||||||
* @param {string} msgID
|
* @param {string} msgID
|
||||||
* @param {UserGUNNode} user
|
* @param {UserGUNNode} user
|
||||||
* @param {ISEA} SEA
|
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
const deleteMessage = async (recipientPub, msgID, user, SEA) => {
|
const deleteMessage = async (recipientPub, msgID, user) => {
|
||||||
if (!user.is) {
|
if (!user.is) {
|
||||||
throw new Error(ErrorCode.NOT_AUTH)
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
}
|
}
|
||||||
|
|
@ -700,7 +749,7 @@ const deleteMessage = async (recipientPub, msgID, user, SEA) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const outgoingID = await Utils.recipientToOutgoingID(recipientPub, user, SEA)
|
const outgoingID = await Utils.recipientToOutgoingID(recipientPub)
|
||||||
|
|
||||||
if (outgoingID === null) {
|
if (outgoingID === null) {
|
||||||
throw new Error(`Could not fetch an outgoing id for user: ${recipientPub}`)
|
throw new Error(`Could not fetch an outgoing id for user: ${recipientPub}`)
|
||||||
|
|
@ -1037,7 +1086,7 @@ const saveSeedBackup = async (mnemonicPhrase, user, SEA) => {
|
||||||
throw new TypeError('expected mnemonicPhrase to be an string array')
|
throw new TypeError('expected mnemonicPhrase to be an string array')
|
||||||
}
|
}
|
||||||
|
|
||||||
const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
|
const mySecret = require('../Mediator').getMySecret()
|
||||||
const encryptedSeed = await SEA.encrypt(mnemonicPhrase.join(' '), mySecret)
|
const encryptedSeed = await SEA.encrypt(mnemonicPhrase.join(' '), mySecret)
|
||||||
|
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
|
|
@ -1051,8 +1100,21 @@ const saveSeedBackup = async (mnemonicPhrase, user, SEA) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} pub
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const disconnect = async pub => {
|
||||||
|
if (!(await Utils.successfulHandshakeAlreadyExists(pub))) {
|
||||||
|
throw new Error('No handshake exists for this pub')
|
||||||
|
}
|
||||||
|
|
||||||
|
await cleanup(pub)
|
||||||
|
|
||||||
|
await generateHandshakeAddress()
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
INITIAL_MSG,
|
|
||||||
__createOutgoingFeed,
|
__createOutgoingFeed,
|
||||||
acceptRequest,
|
acceptRequest,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
|
@ -1067,5 +1129,6 @@ module.exports = {
|
||||||
sendPayment,
|
sendPayment,
|
||||||
generateOrderAddress,
|
generateOrderAddress,
|
||||||
setBio,
|
setBio,
|
||||||
saveSeedBackup
|
saveSeedBackup,
|
||||||
|
disconnect
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
535
services/gunDB/contact-api/events/index.js
Normal file
535
services/gunDB/contact-api/events/index.js
Normal file
|
|
@ -0,0 +1,535 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
const debounce = require('lodash/debounce')
|
||||||
|
|
||||||
|
const ErrorCode = require('../errorCode')
|
||||||
|
const Key = require('../key')
|
||||||
|
const Schema = require('../schema')
|
||||||
|
const Streams = require('../streams')
|
||||||
|
const Utils = require('../utils')
|
||||||
|
/**
|
||||||
|
* @typedef {import('../SimpleGUN').UserGUNNode} UserGUNNode
|
||||||
|
* @typedef {import('../SimpleGUN').GUNNode} GUNNode
|
||||||
|
* @typedef {import('../SimpleGUN').ISEA} ISEA
|
||||||
|
* @typedef {import('../SimpleGUN').ListenerData} ListenerData
|
||||||
|
* @typedef {import('../schema').HandshakeRequest} HandshakeRequest
|
||||||
|
* @typedef {import('../schema').Message} Message
|
||||||
|
* @typedef {import('../schema').Outgoing} Outgoing
|
||||||
|
* @typedef {import('../schema').PartialOutgoing} PartialOutgoing
|
||||||
|
* @typedef {import('../schema').Chat} Chat
|
||||||
|
* @typedef {import('../schema').ChatMessage} ChatMessage
|
||||||
|
* @typedef {import('../schema').SimpleSentRequest} SimpleSentRequest
|
||||||
|
* @typedef {import('../schema').SimpleReceivedRequest} SimpleReceivedRequest
|
||||||
|
*/
|
||||||
|
|
||||||
|
const DEBOUNCE_WAIT_TIME = 500
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(userToIncoming: Record<string, string>) => void} cb
|
||||||
|
* @param {UserGUNNode} user Pass only for testing purposes.
|
||||||
|
* @param {ISEA} SEA
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const __onUserToIncoming = (cb, user, SEA) => {
|
||||||
|
if (!user.is) {
|
||||||
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
|
||||||
|
|
||||||
|
/** @type {Record<string, string>} */
|
||||||
|
const userToIncoming = {}
|
||||||
|
|
||||||
|
const mySecret = require('../../Mediator').getMySecret()
|
||||||
|
|
||||||
|
user
|
||||||
|
.get(Key.USER_TO_INCOMING)
|
||||||
|
.map()
|
||||||
|
.on(async (encryptedIncomingID, userPub) => {
|
||||||
|
if (typeof encryptedIncomingID !== 'string') {
|
||||||
|
if (encryptedIncomingID === null) {
|
||||||
|
// on disconnect
|
||||||
|
delete userToIncoming[userPub]
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
'got a non string non null value inside user to incoming'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptedIncomingID.length === 0) {
|
||||||
|
console.error('got an empty string value')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const incomingID = await SEA.decrypt(encryptedIncomingID, mySecret)
|
||||||
|
|
||||||
|
if (typeof incomingID === 'undefined') {
|
||||||
|
console.warn('could not decrypt incomingID inside __onUserToIncoming')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userToIncoming[userPub] = incomingID
|
||||||
|
|
||||||
|
callb(userToIncoming)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(avatar: string|null) => void} cb
|
||||||
|
* @param {UserGUNNode} user Pass only for testing purposes.
|
||||||
|
* @throws {Error} If user hasn't been auth.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const onAvatar = (cb, user) => {
|
||||||
|
if (!user.is) {
|
||||||
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
|
||||||
|
// Initial value if avvatar is undefined in gun
|
||||||
|
callb(null)
|
||||||
|
|
||||||
|
user
|
||||||
|
.get(Key.PROFILE)
|
||||||
|
.get(Key.AVATAR)
|
||||||
|
.on(avatar => {
|
||||||
|
if (typeof avatar === 'string' || avatar === null) {
|
||||||
|
callb(avatar)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(blacklist: string[]) => void} cb
|
||||||
|
* @param {UserGUNNode} user
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const onBlacklist = (cb, user) => {
|
||||||
|
/** @type {string[]} */
|
||||||
|
const blacklist = []
|
||||||
|
|
||||||
|
if (!user.is) {
|
||||||
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
|
||||||
|
|
||||||
|
// Initial value if no items are in blacklist in gun
|
||||||
|
callb(blacklist)
|
||||||
|
|
||||||
|
user
|
||||||
|
.get(Key.BLACKLIST)
|
||||||
|
.map()
|
||||||
|
.on(publicKey => {
|
||||||
|
if (typeof publicKey === 'string' && publicKey.length > 0) {
|
||||||
|
blacklist.push(publicKey)
|
||||||
|
callb(blacklist)
|
||||||
|
} else {
|
||||||
|
console.warn('Invalid public key received for blacklist')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(currentHandshakeAddress: string|null) => void} cb
|
||||||
|
* @param {UserGUNNode} user
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const onCurrentHandshakeAddress = (cb, user) => {
|
||||||
|
if (!user.is) {
|
||||||
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
|
||||||
|
|
||||||
|
// If undefined, callback below wont be called. Let's supply null as the
|
||||||
|
// initial value.
|
||||||
|
callb(null)
|
||||||
|
|
||||||
|
user.get(Key.CURRENT_HANDSHAKE_ADDRESS).on(addr => {
|
||||||
|
if (typeof addr !== 'string') {
|
||||||
|
console.error('expected handshake address to be an string')
|
||||||
|
|
||||||
|
callb(null)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
callb(addr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(displayName: string|null) => void} cb
|
||||||
|
* @param {UserGUNNode} user Pass only for testing purposes.
|
||||||
|
* @throws {Error} If user hasn't been auth.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const onDisplayName = (cb, user) => {
|
||||||
|
if (!user.is) {
|
||||||
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
|
||||||
|
|
||||||
|
// Initial value if display name is undefined in gun
|
||||||
|
callb(null)
|
||||||
|
|
||||||
|
user
|
||||||
|
.get(Key.PROFILE)
|
||||||
|
.get(Key.DISPLAY_NAME)
|
||||||
|
.on(displayName => {
|
||||||
|
if (typeof displayName === 'string' || displayName === null) {
|
||||||
|
callb(displayName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(messages: Record<string, Message>) => void} cb
|
||||||
|
* @param {string} userPK Public key of the user from whom the incoming
|
||||||
|
* messages will be obtained.
|
||||||
|
* @param {string} incomingFeedID ID of the outgoing feed from which the
|
||||||
|
* incoming messages will be obtained.
|
||||||
|
* @param {GUNNode} gun (Pass only for testing purposes)
|
||||||
|
* @param {UserGUNNode} user
|
||||||
|
* @param {ISEA} SEA
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const onIncomingMessages = (cb, userPK, incomingFeedID, gun, user, SEA) => {
|
||||||
|
if (!user.is) {
|
||||||
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
|
||||||
|
|
||||||
|
const otherUser = gun.user(userPK)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Record<string, Message>}
|
||||||
|
*/
|
||||||
|
const messages = {}
|
||||||
|
|
||||||
|
callb(messages)
|
||||||
|
|
||||||
|
otherUser
|
||||||
|
.get(Key.OUTGOINGS)
|
||||||
|
.get(incomingFeedID)
|
||||||
|
.get(Key.MESSAGES)
|
||||||
|
.map()
|
||||||
|
.on(async (data, key) => {
|
||||||
|
if (!Schema.isMessage(data)) {
|
||||||
|
console.warn('non-message received')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
const recipientEpub = await Utils.pubToEpub(userPK)
|
||||||
|
|
||||||
|
const secret = await SEA.secret(recipientEpub, user._.sea)
|
||||||
|
|
||||||
|
let { body } = data
|
||||||
|
body = await SEA.decrypt(body, secret)
|
||||||
|
|
||||||
|
messages[key] = {
|
||||||
|
body,
|
||||||
|
timestamp: data.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
callb(messages)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, Outgoing|null>} Outgoings
|
||||||
|
* @typedef {(outgoings: Outgoings) => void} OutgoingsListener
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Outgoings}
|
||||||
|
*/
|
||||||
|
let currentOutgoings = {}
|
||||||
|
|
||||||
|
const getCurrentOutgoings = () => currentOutgoings
|
||||||
|
|
||||||
|
/** @type {Set<OutgoingsListener>} */
|
||||||
|
const outgoingsListeners = new Set()
|
||||||
|
|
||||||
|
const notifyOutgoingsListeners = () => {
|
||||||
|
outgoingsListeners.forEach(l => l(currentOutgoings))
|
||||||
|
}
|
||||||
|
|
||||||
|
let outSubbed = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {OutgoingsListener} cb
|
||||||
|
* @returns {() => void}
|
||||||
|
*/
|
||||||
|
const onOutgoing = cb => {
|
||||||
|
outgoingsListeners.add(cb)
|
||||||
|
cb(currentOutgoings)
|
||||||
|
|
||||||
|
if (!outSubbed) {
|
||||||
|
const user = require('../../Mediator').getUser()
|
||||||
|
user.get(Key.OUTGOINGS).open(
|
||||||
|
debounce(async data => {
|
||||||
|
try {
|
||||||
|
if (typeof data !== 'object' || data === null) {
|
||||||
|
currentOutgoings = {}
|
||||||
|
notifyOutgoingsListeners()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Record<string, Outgoing|null>} */
|
||||||
|
const newOuts = {}
|
||||||
|
|
||||||
|
const SEA = require('../../Mediator').mySEA
|
||||||
|
const mySecret = await Utils.mySecret()
|
||||||
|
|
||||||
|
await Utils.asyncForEach(Object.entries(data), async ([id, out]) => {
|
||||||
|
if (typeof out !== 'object') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out === null) {
|
||||||
|
newOuts[id] = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { with: encPub, messages } = out
|
||||||
|
|
||||||
|
if (typeof encPub !== 'string') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const pub = await SEA.decrypt(encPub, mySecret)
|
||||||
|
|
||||||
|
if (!newOuts[id]) {
|
||||||
|
newOuts[id] = {
|
||||||
|
with: pub,
|
||||||
|
messages: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ourSec = await SEA.secret(
|
||||||
|
await Utils.pubToEpub(pub),
|
||||||
|
user._.sea
|
||||||
|
)
|
||||||
|
|
||||||
|
if (typeof messages === 'object' && messages !== null) {
|
||||||
|
await Utils.asyncForEach(
|
||||||
|
Object.entries(messages),
|
||||||
|
async ([mid, msg]) => {
|
||||||
|
if (typeof msg === 'object' && msg !== null) {
|
||||||
|
if (
|
||||||
|
typeof msg.body === 'string' &&
|
||||||
|
typeof msg.timestamp === 'number'
|
||||||
|
) {
|
||||||
|
const newOut = newOuts[id]
|
||||||
|
if (!newOut) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newOut.messages[mid] = {
|
||||||
|
body: await SEA.decrypt(msg.body, ourSec),
|
||||||
|
timestamp: msg.timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
currentOutgoings = newOuts
|
||||||
|
notifyOutgoingsListeners()
|
||||||
|
} catch (e) {
|
||||||
|
console.log('--------------------------')
|
||||||
|
console.log('Events -> onOutgoing')
|
||||||
|
console.log(e)
|
||||||
|
console.log('--------------------------')
|
||||||
|
}
|
||||||
|
}, 400)
|
||||||
|
)
|
||||||
|
|
||||||
|
outSubbed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
outgoingsListeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/**
|
||||||
|
* @typedef {(chats: Chat[]) => void} ChatsListener
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Chat[]} */
|
||||||
|
let currentChats = []
|
||||||
|
|
||||||
|
/** @type {Set<ChatsListener>} */
|
||||||
|
const chatsListeners = new Set()
|
||||||
|
|
||||||
|
const notifyChatsListeners = () => {
|
||||||
|
chatsListeners.forEach(l => l(currentChats))
|
||||||
|
}
|
||||||
|
|
||||||
|
const processChats = debounce(() => {
|
||||||
|
const pubToAvatar = Streams.getPubToAvatar()
|
||||||
|
const pubToDn = Streams.getPubToDn()
|
||||||
|
const existingOutgoings = /** @type {[string, Outgoing][]} */ (Object.entries(
|
||||||
|
getCurrentOutgoings()
|
||||||
|
).filter(([_, o]) => o !== null))
|
||||||
|
const pubToFeed = Streams.getPubToFeed()
|
||||||
|
|
||||||
|
/** @type {Chat[]} */
|
||||||
|
const newChats = []
|
||||||
|
|
||||||
|
for (const [outID, out] of existingOutgoings) {
|
||||||
|
if (typeof pubToAvatar[out.with] === 'undefined') {
|
||||||
|
// eslint-disable-next-line no-empty-function
|
||||||
|
Streams.onAvatar(() => {}, out.with)
|
||||||
|
}
|
||||||
|
if (typeof pubToDn[out.with] === 'undefined') {
|
||||||
|
// eslint-disable-next-line no-empty-function
|
||||||
|
Streams.onDisplayName(() => {}, out.with)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {ChatMessage[]} */
|
||||||
|
let msgs = Object.entries(out.messages).map(([mid, m]) => ({
|
||||||
|
id: mid,
|
||||||
|
outgoing: true,
|
||||||
|
body: m.body,
|
||||||
|
timestamp: m.timestamp
|
||||||
|
}))
|
||||||
|
|
||||||
|
const incoming = pubToFeed[out.with]
|
||||||
|
|
||||||
|
if (Array.isArray(incoming)) {
|
||||||
|
msgs = [...msgs, ...incoming]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Chat} */
|
||||||
|
const chat = {
|
||||||
|
recipientPublicKey: out.with,
|
||||||
|
didDisconnect: pubToFeed[out.with] === 'disconnected',
|
||||||
|
id: out.with + outID,
|
||||||
|
messages: msgs,
|
||||||
|
recipientAvatar: pubToAvatar[out.with] || null,
|
||||||
|
recipientDisplayName: pubToDn[out.with] || null
|
||||||
|
}
|
||||||
|
|
||||||
|
newChats.push(chat)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentChats = newChats.filter(
|
||||||
|
c =>
|
||||||
|
Array.isArray(pubToFeed[c.recipientPublicKey]) ||
|
||||||
|
pubToFeed[c.recipientPublicKey] === 'disconnected'
|
||||||
|
)
|
||||||
|
|
||||||
|
notifyChatsListeners()
|
||||||
|
}, 750)
|
||||||
|
|
||||||
|
let onChatsSubbed = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Massages all of the more primitive data structures into a more manageable
|
||||||
|
* 'Chat' paradigm.
|
||||||
|
* @param {ChatsListener} cb
|
||||||
|
* @returns {() => void}
|
||||||
|
*/
|
||||||
|
const onChats = cb => {
|
||||||
|
if (!chatsListeners.add(cb)) {
|
||||||
|
throw new Error('Tried to subscribe twice')
|
||||||
|
}
|
||||||
|
cb(currentChats)
|
||||||
|
|
||||||
|
if (!onChatsSubbed) {
|
||||||
|
onOutgoing(processChats)
|
||||||
|
Streams.onAvatar(processChats)
|
||||||
|
Streams.onDisplayName(processChats)
|
||||||
|
Streams.onPubToFeed(processChats)
|
||||||
|
|
||||||
|
onChatsSubbed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (!chatsListeners.delete(cb)) {
|
||||||
|
throw new Error('Tried to unsubscribe twice')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {string|null} */
|
||||||
|
let currentBio = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(bio: string|null) => void} cb
|
||||||
|
* @param {UserGUNNode} user Pass only for testing purposes.
|
||||||
|
* @throws {Error} If user hasn't been auth.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const onBio = (cb, user) => {
|
||||||
|
if (!user.is) {
|
||||||
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
|
||||||
|
// Initial value if avvatar is undefined in gun
|
||||||
|
callb(currentBio)
|
||||||
|
|
||||||
|
user.get(Key.BIO).on(bio => {
|
||||||
|
if (typeof bio === 'string' || bio === null) {
|
||||||
|
currentBio = bio
|
||||||
|
callb(bio)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {string|null} */
|
||||||
|
let currentSeedBackup = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(seedBackup: string|null) => void} cb
|
||||||
|
* @param {UserGUNNode} user
|
||||||
|
* @param {ISEA} SEA
|
||||||
|
* @throws {Error} If user hasn't been auth.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const onSeedBackup = (cb, user, SEA) => {
|
||||||
|
if (!user.is) {
|
||||||
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mySecret = require('../../Mediator').getMySecret()
|
||||||
|
|
||||||
|
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
|
||||||
|
callb(currentSeedBackup)
|
||||||
|
|
||||||
|
user.get(Key.SEED_BACKUP).on(async seedBackup => {
|
||||||
|
if (typeof seedBackup === 'string') {
|
||||||
|
currentSeedBackup = await SEA.decrypt(seedBackup, mySecret)
|
||||||
|
callb(currentSeedBackup)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
__onUserToIncoming,
|
||||||
|
onAvatar,
|
||||||
|
onBlacklist,
|
||||||
|
onCurrentHandshakeAddress,
|
||||||
|
onDisplayName,
|
||||||
|
onIncomingMessages,
|
||||||
|
onOutgoing,
|
||||||
|
getCurrentOutgoings,
|
||||||
|
onSimplerReceivedRequests: require('./onReceivedReqs').onReceivedReqs,
|
||||||
|
onSimplerSentRequests: require('./onSentReqs').onSentReqs,
|
||||||
|
getCurrentSentReqs: require('./onSentReqs').getCurrentSentReqs,
|
||||||
|
onBio,
|
||||||
|
onSeedBackup,
|
||||||
|
onChats
|
||||||
|
}
|
||||||
147
services/gunDB/contact-api/events/onReceivedReqs.js
Normal file
147
services/gunDB/contact-api/events/onReceivedReqs.js
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
/** @format */
|
||||||
|
const debounce = require('lodash/debounce')
|
||||||
|
|
||||||
|
const Key = require('../key')
|
||||||
|
const Schema = require('../schema')
|
||||||
|
const Streams = require('../streams')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Readonly<Schema.SimpleReceivedRequest>} SimpleReceivedRequest
|
||||||
|
* @typedef {(reqs: ReadonlyArray<SimpleReceivedRequest>) => void} Listener
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Set<Listener>} */
|
||||||
|
const listeners = new Set()
|
||||||
|
|
||||||
|
/** @type {string|null} */
|
||||||
|
let currentAddress = null
|
||||||
|
|
||||||
|
/** @type {Record<string, SimpleReceivedRequest>} */
|
||||||
|
let currReceivedReqsMap = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unprocessed requests in current handshake node.
|
||||||
|
* @type {Record<string, Schema.HandshakeRequest>}
|
||||||
|
*/
|
||||||
|
let currAddressData = {}
|
||||||
|
|
||||||
|
/** @returns {SimpleReceivedRequest[]} */
|
||||||
|
const getReceivedReqs = () => Object.values(currReceivedReqsMap)
|
||||||
|
/** @param {Record<string, SimpleReceivedRequest>} reqs */
|
||||||
|
const setReceivedReqsMap = reqs => {
|
||||||
|
currReceivedReqsMap = reqs
|
||||||
|
listeners.forEach(l => l(getReceivedReqs()))
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.add(() => {
|
||||||
|
console.log(
|
||||||
|
`new received reqs: ${JSON.stringify(getReceivedReqs(), null, 4)}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const react = debounce(() => {
|
||||||
|
/** @type {Record<string, SimpleReceivedRequest>} */
|
||||||
|
const newReceivedReqsMap = {}
|
||||||
|
|
||||||
|
const pubToFeed = Streams.getPubToFeed()
|
||||||
|
const pubToAvatar = Streams.getPubToAvatar()
|
||||||
|
const pubToDn = Streams.getPubToDn()
|
||||||
|
|
||||||
|
for (const [id, req] of Object.entries(currAddressData)) {
|
||||||
|
const inContact = Array.isArray(pubToFeed[req.from])
|
||||||
|
|
||||||
|
if (typeof pubToAvatar[req.from] === 'undefined') {
|
||||||
|
// eslint-disable-next-line no-empty-function
|
||||||
|
Streams.onAvatar(() => {}, req.from)()
|
||||||
|
}
|
||||||
|
if (typeof pubToDn[req.from] === 'undefined') {
|
||||||
|
// eslint-disable-next-line no-empty-function
|
||||||
|
Streams.onDisplayName(() => {}, req.from)()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inContact) {
|
||||||
|
newReceivedReqsMap[req.from] = {
|
||||||
|
id,
|
||||||
|
requestorAvatar: pubToAvatar[req.from] || null,
|
||||||
|
requestorDisplayName: pubToDn[req.from] || null,
|
||||||
|
requestorPK: req.from,
|
||||||
|
timestamp: req.timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setReceivedReqsMap(newReceivedReqsMap)
|
||||||
|
}, 750)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} addr
|
||||||
|
* @returns {(data: import('../SimpleGUN').OpenListenerData) => void}
|
||||||
|
*/
|
||||||
|
const listenerForAddr = addr => data => {
|
||||||
|
// did invalidate
|
||||||
|
if (addr !== currentAddress) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data !== 'object' || data === null) {
|
||||||
|
currAddressData = {}
|
||||||
|
} else {
|
||||||
|
for (const [id, req] of Object.entries(data)) {
|
||||||
|
// no need to update them just write them once
|
||||||
|
if (Schema.isHandshakeRequest(req) && !currAddressData[id]) {
|
||||||
|
currAddressData[id] = req
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('data for address: ' + addr)
|
||||||
|
console.log(JSON.stringify(data, null, 4))
|
||||||
|
|
||||||
|
react()
|
||||||
|
}
|
||||||
|
|
||||||
|
let subbed = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Listener} cb
|
||||||
|
* @returns {() => void}
|
||||||
|
*/
|
||||||
|
const onReceivedReqs = cb => {
|
||||||
|
listeners.add(cb)
|
||||||
|
cb(getReceivedReqs())
|
||||||
|
|
||||||
|
if (!subbed) {
|
||||||
|
require('./index').onCurrentHandshakeAddress(addr => {
|
||||||
|
if (currentAddress === addr) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAddress = addr
|
||||||
|
currAddressData = {}
|
||||||
|
setReceivedReqsMap({})
|
||||||
|
|
||||||
|
if (typeof addr === 'string') {
|
||||||
|
require('../../Mediator')
|
||||||
|
.getGun()
|
||||||
|
.get(Key.HANDSHAKE_NODES)
|
||||||
|
.get(addr)
|
||||||
|
.open(listenerForAddr(addr))
|
||||||
|
}
|
||||||
|
}, require('../../Mediator').getUser())
|
||||||
|
|
||||||
|
Streams.onAvatar(react)
|
||||||
|
Streams.onDisplayName(react)
|
||||||
|
Streams.onPubToFeed(react)
|
||||||
|
|
||||||
|
subbed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
listeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getReceivedReqs,
|
||||||
|
onReceivedReqs
|
||||||
|
}
|
||||||
124
services/gunDB/contact-api/events/onSentReqs.js
Normal file
124
services/gunDB/contact-api/events/onSentReqs.js
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
/** @format */
|
||||||
|
const debounce = require('lodash/debounce')
|
||||||
|
|
||||||
|
const Streams = require('../streams')
|
||||||
|
/**
|
||||||
|
* @typedef {import('../SimpleGUN').UserGUNNode} UserGUNNode
|
||||||
|
* @typedef {import('../SimpleGUN').GUNNode} GUNNode
|
||||||
|
* @typedef {import('../SimpleGUN').ISEA} ISEA
|
||||||
|
* @typedef {import('../SimpleGUN').ListenerData} ListenerData
|
||||||
|
* @typedef {import('../schema').HandshakeRequest} HandshakeRequest
|
||||||
|
* @typedef {import('../schema').Message} Message
|
||||||
|
* @typedef {import('../schema').Outgoing} Outgoing
|
||||||
|
* @typedef {import('../schema').PartialOutgoing} PartialOutgoing
|
||||||
|
* @typedef {import('../schema').Chat} Chat
|
||||||
|
* @typedef {import('../schema').ChatMessage} ChatMessage
|
||||||
|
* @typedef {import('../schema').SimpleSentRequest} SimpleSentRequest
|
||||||
|
* @typedef {import('../schema').SimpleReceivedRequest} SimpleReceivedRequest
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {(chats: SimpleSentRequest[]) => void} Listener
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Set<Listener>} */
|
||||||
|
const listeners = new Set()
|
||||||
|
|
||||||
|
/** @type {SimpleSentRequest[]} */
|
||||||
|
let currentReqs = []
|
||||||
|
|
||||||
|
listeners.add(() => {
|
||||||
|
console.log(`new sent reqs: ${JSON.stringify(currentReqs)}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getCurrentSentReqs = () => currentReqs
|
||||||
|
|
||||||
|
const react = debounce(() => {
|
||||||
|
/** @type {SimpleSentRequest[]} */
|
||||||
|
const newReqs = []
|
||||||
|
|
||||||
|
const pubToHAddr = Streams.getAddresses()
|
||||||
|
const storedReqs = Streams.getStoredReqs()
|
||||||
|
const pubToLastSentReqID = Streams.getSentReqIDs()
|
||||||
|
const pubToFeed = Streams.getPubToFeed()
|
||||||
|
const pubToAvatar = Streams.getPubToAvatar()
|
||||||
|
const pubToDN = Streams.getPubToDn()
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`pubToLastSentREqID: ${JSON.stringify(pubToLastSentReqID, null, 4)}`
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const storedReq of storedReqs) {
|
||||||
|
const { handshakeAddress, recipientPub, sentReqID, timestamp } = storedReq
|
||||||
|
const currAddress = pubToHAddr[recipientPub]
|
||||||
|
|
||||||
|
const lastReqID = pubToLastSentReqID[recipientPub]
|
||||||
|
const isStale = typeof lastReqID !== 'undefined' && lastReqID !== sentReqID
|
||||||
|
const isConnected = Array.isArray(pubToFeed[recipientPub])
|
||||||
|
|
||||||
|
if (isStale || isConnected) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof currAddress === 'undefined') {
|
||||||
|
// eslint-disable-next-line no-empty-function
|
||||||
|
Streams.onAddresses(() => {}, recipientPub)()
|
||||||
|
}
|
||||||
|
if (typeof pubToAvatar[recipientPub] === 'undefined') {
|
||||||
|
// eslint-disable-next-line no-empty-function
|
||||||
|
Streams.onAvatar(() => {}, recipientPub)()
|
||||||
|
}
|
||||||
|
if (typeof pubToDN[recipientPub] === 'undefined') {
|
||||||
|
// eslint-disable-next-line no-empty-function
|
||||||
|
Streams.onDisplayName(() => {}, recipientPub)()
|
||||||
|
}
|
||||||
|
|
||||||
|
newReqs.push({
|
||||||
|
id: sentReqID,
|
||||||
|
recipientAvatar: pubToAvatar[recipientPub] || null,
|
||||||
|
recipientChangedRequestAddress:
|
||||||
|
typeof currAddress !== 'undefined' && handshakeAddress !== currAddress,
|
||||||
|
recipientDisplayName: pubToDN[recipientPub] || null,
|
||||||
|
recipientPublicKey: recipientPub,
|
||||||
|
timestamp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
currentReqs = newReqs
|
||||||
|
|
||||||
|
listeners.forEach(l => l(currentReqs))
|
||||||
|
}, 750)
|
||||||
|
|
||||||
|
let subbed = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Massages all of the more primitive data structures into a more manageable
|
||||||
|
* 'Chat' paradigm.
|
||||||
|
* @param {Listener} cb
|
||||||
|
* @returns {() => void}
|
||||||
|
*/
|
||||||
|
const onSentReqs = cb => {
|
||||||
|
listeners.add(cb)
|
||||||
|
cb(currentReqs)
|
||||||
|
|
||||||
|
if (!subbed) {
|
||||||
|
Streams.onAddresses(react)
|
||||||
|
Streams.onStoredReqs(react)
|
||||||
|
Streams.onLastSentReqIDs(react)
|
||||||
|
Streams.onPubToFeed(react)
|
||||||
|
Streams.onAvatar(react)
|
||||||
|
Streams.onDisplayName(react)
|
||||||
|
|
||||||
|
subbed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
listeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
onSentReqs,
|
||||||
|
getCurrentSentReqs
|
||||||
|
}
|
||||||
|
|
@ -13,4 +13,17 @@ exports.currentOrderAddress = async (pub) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return currAddr
|
return currAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} pub
|
||||||
|
* @returns {Promise<string|null>}
|
||||||
|
*/
|
||||||
|
exports.userToIncomingID = async (pub) => {
|
||||||
|
const incomingID = await require('../Mediator').getUser().get(Key.USER_TO_INCOMING).get(pub).then()
|
||||||
|
|
||||||
|
if (typeof incomingID === 'string') return incomingID
|
||||||
|
|
||||||
|
return null
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -16,14 +16,14 @@ const Utils = require('../utils')
|
||||||
* @throws {Error} NOT_AUTH
|
* @throws {Error} NOT_AUTH
|
||||||
* @param {UserGUNNode} user
|
* @param {UserGUNNode} user
|
||||||
* @param {ISEA} SEA
|
* @param {ISEA} SEA
|
||||||
* @returns {Promise<void>}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
const onAcceptedRequests = async (user, SEA) => {
|
const onAcceptedRequests = (user, SEA) => {
|
||||||
if (!user.is) {
|
if (!user.is) {
|
||||||
throw new Error(ErrorCode.NOT_AUTH)
|
throw new Error(ErrorCode.NOT_AUTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
|
const mySecret = require('../../Mediator').getMySecret()
|
||||||
|
|
||||||
if (typeof mySecret !== 'string') {
|
if (typeof mySecret !== 'string') {
|
||||||
console.log("Jobs.onAcceptedRequests() -> typeof mySecret !== 'string'")
|
console.log("Jobs.onAcceptedRequests() -> typeof mySecret !== 'string'")
|
||||||
|
|
@ -33,7 +33,7 @@ const onAcceptedRequests = async (user, SEA) => {
|
||||||
user
|
user
|
||||||
.get(Key.STORED_REQS)
|
.get(Key.STORED_REQS)
|
||||||
.map()
|
.map()
|
||||||
.once(async storedReq => {
|
.once(async (storedReq, id) => {
|
||||||
try {
|
try {
|
||||||
if (!Schema.isStoredRequest(storedReq)) {
|
if (!Schema.isStoredRequest(storedReq)) {
|
||||||
throw new TypeError('Stored request not an StoredRequest')
|
throw new TypeError('Stored request not an StoredRequest')
|
||||||
|
|
@ -123,10 +123,31 @@ const onAcceptedRequests = async (user, SEA) => {
|
||||||
mySecret
|
mySecret
|
||||||
)
|
)
|
||||||
|
|
||||||
user
|
await new Promise((res, rej) => {
|
||||||
.get(Key.USER_TO_INCOMING)
|
user
|
||||||
.get(recipientPub)
|
.get(Key.USER_TO_INCOMING)
|
||||||
.put(encryptedForMeIncomingID)
|
.get(recipientPub)
|
||||||
|
.put(encryptedForMeIncomingID, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
user
|
||||||
|
.get(Key.STORED_REQS)
|
||||||
|
.get(id)
|
||||||
|
.put(null, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// ensure this listeners gets called at least once
|
// ensure this listeners gets called at least once
|
||||||
res()
|
res()
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ exports.RECIPIENT_TO_OUTGOING = 'recipientToOutgoing'
|
||||||
exports.USER_TO_INCOMING = 'userToIncoming'
|
exports.USER_TO_INCOMING = 'userToIncoming'
|
||||||
|
|
||||||
exports.STORED_REQS = 'storedReqs'
|
exports.STORED_REQS = 'storedReqs'
|
||||||
exports.REQUEST_TO_USER = 'requestToUser'
|
|
||||||
|
|
||||||
exports.BLACKLIST = 'blacklist'
|
exports.BLACKLIST = 'blacklist'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,13 @@ exports.isChatMessage = item => {
|
||||||
* outgoing/incoming feed paradigm. It combines both the outgoing and incoming
|
* outgoing/incoming feed paradigm. It combines both the outgoing and incoming
|
||||||
* messages into one data structure plus metada about the chat.
|
* messages into one data structure plus metada about the chat.
|
||||||
* @typedef {object} Chat
|
* @typedef {object} Chat
|
||||||
|
* @prop {string} id Chats now have IDs because of disconnect.
|
||||||
|
* RecipientPublicKey will no longer be unique.
|
||||||
* @prop {string|null} recipientAvatar Base64 encoded image.
|
* @prop {string|null} recipientAvatar Base64 encoded image.
|
||||||
* @prop {string} recipientPublicKey A way to uniquely identify each chat.
|
* @prop {string} recipientPublicKey A way to uniquely identify each chat.
|
||||||
* @prop {ChatMessage[]} messages Sorted from most recent to least recent.
|
* @prop {ChatMessage[]} messages Sorted from most recent to least recent.
|
||||||
* @prop {string|null} recipientDisplayName
|
* @prop {string|null} recipientDisplayName
|
||||||
|
* @prop {boolean} didDisconnect True if the recipient performed a disconnect.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,6 +105,14 @@ exports.isChat = item => {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof obj.didDisconnect !== 'boolean') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj.id !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return obj.messages.every(msg => exports.isChatMessage(msg))
|
return obj.messages.every(msg => exports.isChatMessage(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,6 +146,8 @@ exports.isStoredRequest = item => {
|
||||||
const obj = /** @type {StoredRequest} */ (item)
|
const obj = /** @type {StoredRequest} */ (item)
|
||||||
if (typeof obj.recipientPub !== 'string') return false
|
if (typeof obj.recipientPub !== 'string') return false
|
||||||
if (typeof obj.handshakeAddress !== 'string') return false
|
if (typeof obj.handshakeAddress !== 'string') return false
|
||||||
|
if (typeof obj.handshakeAddress !== 'string') return false
|
||||||
|
if (typeof obj.timestamp !== 'number') return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +213,6 @@ exports.isSimpleSentRequest = item => {
|
||||||
* @prop {string|null} requestorAvatar
|
* @prop {string|null} requestorAvatar
|
||||||
* @prop {string|null} requestorDisplayName
|
* @prop {string|null} requestorDisplayName
|
||||||
* @prop {string} requestorPK
|
* @prop {string} requestorPK
|
||||||
* @prop {string} response
|
|
||||||
* @prop {number} timestamp
|
* @prop {number} timestamp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -238,10 +250,6 @@ exports.isSimpleReceivedRequest = item => {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof obj.response !== 'string') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof obj.timestamp !== 'number') {
|
if (typeof obj.timestamp !== 'number') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
53
services/gunDB/contact-api/streams/addresses.js
Normal file
53
services/gunDB/contact-api/streams/addresses.js
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/** @format */
|
||||||
|
const Key = require('../key')
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, string|null|undefined>} Addresses
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Addresses} */
|
||||||
|
const pubToAddress = {}
|
||||||
|
|
||||||
|
/** @type {Set<() => void>} */
|
||||||
|
const listeners = new Set()
|
||||||
|
|
||||||
|
listeners.add(() => {
|
||||||
|
console.log(`pubToAddress: ${JSON.stringify(pubToAddress, null, 4)}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
const notify = () => listeners.forEach(l => l())
|
||||||
|
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
const subbedPublicKeys = new Set()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {() => void} cb
|
||||||
|
* @param {string=} pub
|
||||||
|
*/
|
||||||
|
const onAddresses = (cb, pub) => {
|
||||||
|
listeners.add(cb)
|
||||||
|
cb()
|
||||||
|
if (pub && subbedPublicKeys.add(pub)) {
|
||||||
|
require('../../Mediator')
|
||||||
|
.getGun()
|
||||||
|
.user(pub)
|
||||||
|
.get(Key.CURRENT_HANDSHAKE_ADDRESS)
|
||||||
|
.on(addr => {
|
||||||
|
if (typeof addr === 'string' || addr === null) {
|
||||||
|
pubToAddress[pub] = addr
|
||||||
|
} else {
|
||||||
|
pubToAddress[pub] = null
|
||||||
|
}
|
||||||
|
notify()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
listeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAddresses = () => pubToAddress
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
onAddresses,
|
||||||
|
getAddresses
|
||||||
|
}
|
||||||
191
services/gunDB/contact-api/streams/index.js
Normal file
191
services/gunDB/contact-api/streams/index.js
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
/** @format */
|
||||||
|
const Key = require('../key')
|
||||||
|
const Schema = require('../schema')
|
||||||
|
const Utils = require('../utils')
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, string|null|undefined>} Avatars
|
||||||
|
* @typedef {(avatars: Avatars) => void} AvatarListener
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Avatars} */
|
||||||
|
const pubToAvatar = {}
|
||||||
|
|
||||||
|
const getPubToAvatar = () => pubToAvatar
|
||||||
|
|
||||||
|
/** @type {Set<AvatarListener>} */
|
||||||
|
const avatarListeners = new Set()
|
||||||
|
|
||||||
|
const notifyAvatarListeners = () => {
|
||||||
|
avatarListeners.forEach(l => l(pubToAvatar))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
const pubsWithAvatarListeners = new Set()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AvatarListener} cb
|
||||||
|
* @param {string=} pub
|
||||||
|
*/
|
||||||
|
const onAvatar = (cb, pub) => {
|
||||||
|
avatarListeners.add(cb)
|
||||||
|
cb(pubToAvatar)
|
||||||
|
if (pub && pubsWithAvatarListeners.add(pub)) {
|
||||||
|
require('../../Mediator')
|
||||||
|
.getGun()
|
||||||
|
.user(pub)
|
||||||
|
.get(Key.PROFILE)
|
||||||
|
.get(Key.AVATAR)
|
||||||
|
.on(av => {
|
||||||
|
if (typeof av === 'string' || av === null) {
|
||||||
|
pubToAvatar[pub] = av || null
|
||||||
|
} else {
|
||||||
|
pubToAvatar[pub] = null
|
||||||
|
}
|
||||||
|
notifyAvatarListeners()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
avatarListeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, string|null|undefined>} DisplayNames
|
||||||
|
* @typedef {(avatars: Avatars) => void} DisplayNameListener
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {DisplayNames} */
|
||||||
|
const pubToDisplayName = {}
|
||||||
|
|
||||||
|
const getPubToDn = () => pubToDisplayName
|
||||||
|
|
||||||
|
/** @type {Set<DisplayNameListener>} */
|
||||||
|
const displayNameListeners = new Set()
|
||||||
|
|
||||||
|
const notifyDisplayNameListeners = () => {
|
||||||
|
displayNameListeners.forEach(l => l(pubToDisplayName))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
const pubsWithDisplayNameListeners = new Set()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {DisplayNameListener} cb
|
||||||
|
* @param {string=} pub
|
||||||
|
*/
|
||||||
|
const onDisplayName = (cb, pub) => {
|
||||||
|
displayNameListeners.add(cb)
|
||||||
|
cb(pubToDisplayName)
|
||||||
|
if (pub && pubsWithDisplayNameListeners.add(pub)) {
|
||||||
|
require('../../Mediator')
|
||||||
|
.getGun()
|
||||||
|
.user(pub)
|
||||||
|
.get(Key.PROFILE)
|
||||||
|
.get(Key.DISPLAY_NAME)
|
||||||
|
.on(dn => {
|
||||||
|
if (typeof dn === 'string' || dn === null) {
|
||||||
|
pubToDisplayName[pub] = dn || null
|
||||||
|
} else {
|
||||||
|
pubToDisplayName[pub] = null
|
||||||
|
}
|
||||||
|
notifyDisplayNameListeners()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
displayNameListeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('../schema').StoredRequest} StoredRequest
|
||||||
|
* @typedef {(reqs: StoredRequest[]) => void} StoredRequestsListener
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Set<StoredRequestsListener>} */
|
||||||
|
const storedRequestsListeners = new Set()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {StoredRequest[]}
|
||||||
|
*/
|
||||||
|
let encryptedStoredReqs = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {StoredRequest[]}
|
||||||
|
*/
|
||||||
|
let currentStoredReqs = []
|
||||||
|
|
||||||
|
const getStoredReqs = () => currentStoredReqs
|
||||||
|
|
||||||
|
const processStoredReqs = async () => {
|
||||||
|
const ereqs = encryptedStoredReqs
|
||||||
|
encryptedStoredReqs = []
|
||||||
|
const mySecret = await Utils.mySecret()
|
||||||
|
const SEA = require('../../Mediator').mySEA
|
||||||
|
const finalReqs = await Utils.asyncMap(ereqs, async er => {
|
||||||
|
/** @type {StoredRequest} */
|
||||||
|
const r = {
|
||||||
|
handshakeAddress: await SEA.decrypt(er.handshakeAddress, mySecret),
|
||||||
|
recipientPub: await SEA.decrypt(er.recipientPub, mySecret),
|
||||||
|
sentReqID: await SEA.decrypt(er.sentReqID, mySecret),
|
||||||
|
timestamp: er.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
currentStoredReqs = finalReqs
|
||||||
|
storedRequestsListeners.forEach(l => l(currentStoredReqs))
|
||||||
|
}
|
||||||
|
|
||||||
|
let storedReqsSubbed = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {StoredRequestsListener} cb
|
||||||
|
*/
|
||||||
|
const onStoredReqs = cb => {
|
||||||
|
storedRequestsListeners.add(cb)
|
||||||
|
|
||||||
|
if (!storedReqsSubbed) {
|
||||||
|
require('../../Mediator')
|
||||||
|
.getUser()
|
||||||
|
.get(Key.STORED_REQS)
|
||||||
|
.open(d => {
|
||||||
|
if (typeof d === 'object' && d !== null) {
|
||||||
|
encryptedStoredReqs = /** @type {StoredRequest[]} */ (Object.values(
|
||||||
|
d
|
||||||
|
).filter(i => Schema.isStoredRequest(i)))
|
||||||
|
}
|
||||||
|
|
||||||
|
processStoredReqs()
|
||||||
|
})
|
||||||
|
|
||||||
|
storedReqsSubbed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(currentStoredReqs)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
storedRequestsListeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
onAvatar,
|
||||||
|
getPubToAvatar,
|
||||||
|
onDisplayName,
|
||||||
|
getPubToDn,
|
||||||
|
|
||||||
|
onPubToIncoming: require('./pubToIncoming').onPubToIncoming,
|
||||||
|
getPubToIncoming: require('./pubToIncoming').getPubToIncoming,
|
||||||
|
setPubToIncoming: require('./pubToIncoming').setPubToIncoming,
|
||||||
|
|
||||||
|
onPubToFeed: require('./pubToFeed').onPubToFeed,
|
||||||
|
getPubToFeed: require('./pubToFeed').getPubToFeed,
|
||||||
|
|
||||||
|
onStoredReqs,
|
||||||
|
getStoredReqs,
|
||||||
|
onAddresses: require('./addresses').onAddresses,
|
||||||
|
getAddresses: require('./addresses').getAddresses,
|
||||||
|
onLastSentReqIDs: require('./lastSentReqID').onLastSentReqIDs,
|
||||||
|
getSentReqIDs: require('./lastSentReqID').getSentReqIDs,
|
||||||
|
PubToIncoming: require('./pubToIncoming')
|
||||||
|
}
|
||||||
50
services/gunDB/contact-api/streams/lastSentReqID.js
Normal file
50
services/gunDB/contact-api/streams/lastSentReqID.js
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/** @format */
|
||||||
|
const Key = require('../key')
|
||||||
|
|
||||||
|
/** @type {Record<string, string|null|undefined>} */
|
||||||
|
let pubToLastSentReqID = {}
|
||||||
|
|
||||||
|
/** @type {Set<() => void>} */
|
||||||
|
const listeners = new Set()
|
||||||
|
const notify = () => listeners.forEach(l => l())
|
||||||
|
|
||||||
|
let subbed = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {() => void} cb
|
||||||
|
*/
|
||||||
|
const onLastSentReqIDs = cb => {
|
||||||
|
listeners.add(cb)
|
||||||
|
cb()
|
||||||
|
|
||||||
|
if (!subbed) {
|
||||||
|
require('../../Mediator')
|
||||||
|
.getUser()
|
||||||
|
.get(Key.USER_TO_LAST_REQUEST_SENT)
|
||||||
|
.open(data => {
|
||||||
|
if (typeof data === 'object' && data !== null) {
|
||||||
|
for (const [pub, id] of Object.entries(data)) {
|
||||||
|
if (typeof id === 'string' || id === null) {
|
||||||
|
pubToLastSentReqID[pub] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pubToLastSentReqID = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
notify()
|
||||||
|
})
|
||||||
|
subbed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
listeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSentReqIDs = () => pubToLastSentReqID
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
onLastSentReqIDs,
|
||||||
|
getSentReqIDs
|
||||||
|
}
|
||||||
251
services/gunDB/contact-api/streams/pubToFeed.js
Normal file
251
services/gunDB/contact-api/streams/pubToFeed.js
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
/** @format */
|
||||||
|
const uuidv1 = require('uuid/v1')
|
||||||
|
const logger = require('winston')
|
||||||
|
const debounce = require('lodash/debounce')
|
||||||
|
|
||||||
|
const Schema = require('../schema')
|
||||||
|
const Key = require('../key')
|
||||||
|
const Utils = require('../utils')
|
||||||
|
/**
|
||||||
|
* @typedef {import('../schema').ChatMessage} Message
|
||||||
|
* @typedef {import('../SimpleGUN').OpenListenerData} OpenListenerData
|
||||||
|
*/
|
||||||
|
|
||||||
|
const PubToIncoming = require('./pubToIncoming')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, Message[]|null|undefined|'disconnected'>} Feeds
|
||||||
|
* @typedef {(feeds: Feeds) => void} FeedsListener
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Set<FeedsListener>} */
|
||||||
|
const feedsListeners = new Set()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Feeds}
|
||||||
|
*/
|
||||||
|
let pubToFeed = {}
|
||||||
|
|
||||||
|
const getPubToFeed = () => pubToFeed
|
||||||
|
|
||||||
|
feedsListeners.add(() => {
|
||||||
|
console.log(`new pubToFeed: ${JSON.stringify(getPubToFeed())}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
/** @param {Feeds} ptf */
|
||||||
|
const setPubToFeed = ptf => {
|
||||||
|
pubToFeed = ptf
|
||||||
|
feedsListeners.forEach(l => {
|
||||||
|
l(pubToFeed)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If at one point we subscribed to a feed, record it here. Keeps track of it
|
||||||
|
* for unsubbing.
|
||||||
|
*
|
||||||
|
* Since we can't really unsub in GUN, what we do is that each listener created
|
||||||
|
* checks the last incoming feed, if it was created for other feed that is not
|
||||||
|
* the latest, it becomes inactive.
|
||||||
|
* @type {Record<string, string|undefined|null>}
|
||||||
|
*/
|
||||||
|
const pubToLastIncoming = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any pub-feed pair listener will write its update id here when fired up. Avoid
|
||||||
|
* race conditions between different listeners and between different invocations
|
||||||
|
* of the same listener.
|
||||||
|
* @type {Record<string, string>}
|
||||||
|
*/
|
||||||
|
const pubToLastUpdate = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a sub to a pub feed pair that will only emit if it is the last
|
||||||
|
* subbed feed for that pub, according to `pubToLastIncoming`. This listener is
|
||||||
|
* not in charge of writing to the cache.
|
||||||
|
* @param {[ string , string ]} param0
|
||||||
|
* @returns {(data: OpenListenerData) => void}
|
||||||
|
*/
|
||||||
|
const onOpenForPubFeedPair = ([pub, feed]) =>
|
||||||
|
debounce(async data => {
|
||||||
|
// did invalidate
|
||||||
|
if (pubToLastIncoming[pub] !== feed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
// did disconnect
|
||||||
|
data === null ||
|
||||||
|
// interpret as disconnect
|
||||||
|
typeof data !== 'object'
|
||||||
|
) {
|
||||||
|
// invalidate this listener. If a reconnection happens it will be for a
|
||||||
|
// different pub-feed pair.
|
||||||
|
pubToLastIncoming[pub] = null
|
||||||
|
setImmediate(() => {
|
||||||
|
logger.info(
|
||||||
|
`onOpenForPubFeedPair -> didDisconnect -> pub: ${pub} - feed: ${feed}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// signal disconnect to listeners listeners should rely on pubToFeed for
|
||||||
|
// disconnect status instead of pub-to-incoming. Only the latter will
|
||||||
|
// detect remote disconnection
|
||||||
|
setPubToFeed({
|
||||||
|
...getPubToFeed(),
|
||||||
|
[pub]: /** @type {'disconnected'} */ ('disconnected')
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const incoming = /** @type {Schema.Outgoing} */ (data)
|
||||||
|
|
||||||
|
// incomplete data, let's not assume anything
|
||||||
|
if (
|
||||||
|
typeof incoming.with !== 'string' ||
|
||||||
|
typeof incoming.messages !== 'object'
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Schema.ChatMessage[]} */
|
||||||
|
const newMsgs = Object.entries(incoming.messages)
|
||||||
|
// filter out messages with incomplete data
|
||||||
|
.filter(([_, msg]) => Schema.isMessage(msg))
|
||||||
|
.map(([id, msg]) => {
|
||||||
|
/** @type {Schema.ChatMessage} */
|
||||||
|
const m = {
|
||||||
|
// we'll decrypt later
|
||||||
|
body: msg.body,
|
||||||
|
id,
|
||||||
|
outgoing: false,
|
||||||
|
timestamp: msg.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
})
|
||||||
|
|
||||||
|
if (newMsgs.length === 0) {
|
||||||
|
setPubToFeed({
|
||||||
|
...getPubToFeed(),
|
||||||
|
[pub]: []
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const thisUpdate = uuidv1()
|
||||||
|
pubToLastUpdate[pub] = thisUpdate
|
||||||
|
|
||||||
|
const user = require('../../Mediator').getUser()
|
||||||
|
const SEA = require('../../Mediator').mySEA
|
||||||
|
|
||||||
|
const ourSecret = await SEA.secret(await Utils.pubToEpub(pub), user._.sea)
|
||||||
|
|
||||||
|
const decryptedMsgs = await Utils.asyncMap(newMsgs, async m => {
|
||||||
|
/** @type {Schema.ChatMessage} */
|
||||||
|
const decryptedMsg = {
|
||||||
|
...m,
|
||||||
|
body: await SEA.decrypt(m.body, ourSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
return decryptedMsg
|
||||||
|
})
|
||||||
|
|
||||||
|
// this listener got invalidated while we were awaiting the async operations
|
||||||
|
// above.
|
||||||
|
if (pubToLastUpdate[pub] !== thisUpdate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setPubToFeed({
|
||||||
|
...getPubToFeed(),
|
||||||
|
[pub]: decryptedMsgs
|
||||||
|
})
|
||||||
|
}, 750)
|
||||||
|
|
||||||
|
const react = () => {
|
||||||
|
const pubToIncoming = PubToIncoming.getPubToIncoming()
|
||||||
|
|
||||||
|
const gun = require('../../Mediator').getGun()
|
||||||
|
|
||||||
|
/** @type {Feeds} */
|
||||||
|
const newPubToFeed = {}
|
||||||
|
|
||||||
|
for (const [pub, inc] of Object.entries(pubToIncoming)) {
|
||||||
|
/**
|
||||||
|
* empty string -> null
|
||||||
|
* @type {string|null}
|
||||||
|
*/
|
||||||
|
const newIncoming = inc || null
|
||||||
|
|
||||||
|
if (
|
||||||
|
// if disconnected, the same incoming feed will try to overwrite the
|
||||||
|
// nulled out pubToLastIncoming[pub] entry. Making the listener for that
|
||||||
|
// pub feed pair fire up again, etc. Now. When the user disconnects from
|
||||||
|
// this side of things. He will overwrite the pub to incoming with null.
|
||||||
|
// Let's allow that.
|
||||||
|
newIncoming === pubToLastIncoming[pub] &&
|
||||||
|
!(pubToFeed[pub] === 'disconnected' && newIncoming === null)
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// will invalidate stale listeners (a listener for an outdated incoming feed
|
||||||
|
// id)
|
||||||
|
pubToLastIncoming[pub] = newIncoming
|
||||||
|
// Invalidate pending writes from stale listener(s) for the old incoming
|
||||||
|
// address.
|
||||||
|
pubToLastUpdate[pub] = uuidv1()
|
||||||
|
newPubToFeed[pub] = newIncoming ? [] : null
|
||||||
|
|
||||||
|
// sub to this incoming feed
|
||||||
|
if (typeof newIncoming === 'string') {
|
||||||
|
// perform sub to pub-incoming_feed pair
|
||||||
|
// leave all of the sideffects from this for the next tick
|
||||||
|
setImmediate(() => {
|
||||||
|
gun
|
||||||
|
.user(pub)
|
||||||
|
.get(Key.OUTGOINGS)
|
||||||
|
.get(newIncoming)
|
||||||
|
.open(onOpenForPubFeedPair([pub, newIncoming]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(newPubToFeed).length > 0) {
|
||||||
|
setPubToFeed({
|
||||||
|
...getPubToFeed(),
|
||||||
|
...newPubToFeed
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let subbed = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array.isArray(pubToFeed[pub]) means a Handshake is in place, look for
|
||||||
|
* incoming messages here.
|
||||||
|
* pubToIncoming[pub] === null means a disconnection took place.
|
||||||
|
* typeof pubToIncoming[pub] === 'undefined' means none of the above.
|
||||||
|
* @param {FeedsListener} cb
|
||||||
|
* @returns {() => void}
|
||||||
|
*/
|
||||||
|
const onPubToFeed = cb => {
|
||||||
|
feedsListeners.add(cb)
|
||||||
|
cb(getPubToFeed())
|
||||||
|
|
||||||
|
if (!subbed) {
|
||||||
|
PubToIncoming.onPubToIncoming(react)
|
||||||
|
subbed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
feedsListeners.delete(cb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getPubToFeed,
|
||||||
|
setPubToFeed,
|
||||||
|
onPubToFeed
|
||||||
|
}
|
||||||
93
services/gunDB/contact-api/streams/pubToIncoming.js
Normal file
93
services/gunDB/contact-api/streams/pubToIncoming.js
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
/** @format */
|
||||||
|
const uuidv1 = require('uuid/v1')
|
||||||
|
const debounce = require('lodash/debounce')
|
||||||
|
|
||||||
|
const { USER_TO_INCOMING } = require('../key')
|
||||||
|
const { asyncForEach } = require('../utils')
|
||||||
|
/** @typedef {import('../SimpleGUN').OpenListenerData} OpenListenerData */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, string|null|undefined>} PubToIncoming
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {Set<() => void>} */
|
||||||
|
const listeners = new Set()
|
||||||
|
|
||||||
|
/** @type {PubToIncoming} */
|
||||||
|
let pubToIncoming = {}
|
||||||
|
|
||||||
|
const getPubToIncoming = () => pubToIncoming
|
||||||
|
/**
|
||||||
|
* @param {PubToIncoming} pti
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
const setPubToIncoming = pti => {
|
||||||
|
pubToIncoming = pti
|
||||||
|
listeners.forEach(l => l())
|
||||||
|
}
|
||||||
|
|
||||||
|
let latestUpdate = uuidv1()
|
||||||
|
|
||||||
|
const onOpen = debounce(async uti => {
|
||||||
|
const SEA = require('../../Mediator').mySEA
|
||||||
|
const mySec = require('../../Mediator').getMySecret()
|
||||||
|
const thisUpdate = uuidv1()
|
||||||
|
latestUpdate = thisUpdate
|
||||||
|
|
||||||
|
if (typeof uti !== 'object' || uti === null) {
|
||||||
|
setPubToIncoming({})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {PubToIncoming} */
|
||||||
|
const newPubToIncoming = {}
|
||||||
|
|
||||||
|
await asyncForEach(Object.entries(uti), async ([pub, encFeedID]) => {
|
||||||
|
if (encFeedID === null) {
|
||||||
|
newPubToIncoming[pub] = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof encFeedID === 'string') {
|
||||||
|
newPubToIncoming[pub] = await SEA.decrypt(encFeedID, mySec)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// avoid old data from overwriting new data if decrypting took longer to
|
||||||
|
// process for the older open() call than for the newer open() call
|
||||||
|
if (latestUpdate === thisUpdate) {
|
||||||
|
setPubToIncoming(newPubToIncoming)
|
||||||
|
}
|
||||||
|
}, 750)
|
||||||
|
|
||||||
|
let subbed = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {() => void} cb
|
||||||
|
* @returns {() => void}
|
||||||
|
*/
|
||||||
|
const onPubToIncoming = cb => {
|
||||||
|
if (!listeners.add(cb)) {
|
||||||
|
throw new Error('Tried to subscribe twice')
|
||||||
|
}
|
||||||
|
|
||||||
|
cb()
|
||||||
|
|
||||||
|
if (!subbed) {
|
||||||
|
const user = require('../../Mediator').getUser()
|
||||||
|
user.get(USER_TO_INCOMING).open(onOpen)
|
||||||
|
subbed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (!listeners.delete(cb)) {
|
||||||
|
throw new Error('Tried to unsubscribe twice')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getPubToIncoming,
|
||||||
|
setPubToIncoming,
|
||||||
|
onPubToIncoming
|
||||||
|
}
|
||||||
8
services/gunDB/contact-api/utils/PGUNNode.ts
Normal file
8
services/gunDB/contact-api/utils/PGUNNode.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/** @format */
|
||||||
|
import { GUNNode, GUNNodeBase, ValidDataValue } from '../SimpleGUN'
|
||||||
|
|
||||||
|
export interface PGUNNode extends GUNNodeBase {
|
||||||
|
get(key: string): PGUNNode
|
||||||
|
put(data: ValidDataValue | GUNNode): Promise<void>
|
||||||
|
set(data: ValidDataValue | GUNNode): Promise<void>
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
/**
|
/**
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
const ErrorCode = require('./errorCode')
|
const ErrorCode = require('../errorCode')
|
||||||
const Key = require('./key')
|
const Key = require('../key')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('./SimpleGUN').GUNNode} GUNNode
|
* @typedef {import('../SimpleGUN').GUNNode} GUNNode
|
||||||
* @typedef {import('./SimpleGUN').ISEA} ISEA
|
* @typedef {import('../SimpleGUN').ISEA} ISEA
|
||||||
* @typedef {import('./SimpleGUN').UserGUNNode} UserGUNNode
|
* @typedef {import('../SimpleGUN').UserGUNNode} UserGUNNode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -16,6 +16,11 @@ const Key = require('./key')
|
||||||
*/
|
*/
|
||||||
const delay = ms => new Promise(res => setTimeout(res, ms))
|
const delay = ms => new Promise(res => setTimeout(res, ms))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
const mySecret = () => Promise.resolve(require('../../Mediator').getMySecret())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {Promise<T>} promise
|
* @param {Promise<T>} promise
|
||||||
|
|
@ -41,8 +46,8 @@ const timeout10 = promise => {
|
||||||
const tryAndWait = promGen =>
|
const tryAndWait = promGen =>
|
||||||
timeout10(
|
timeout10(
|
||||||
promGen(
|
promGen(
|
||||||
require('../Mediator/index').getGun(),
|
require('../../Mediator/index').getGun(),
|
||||||
require('../Mediator/index').getUser()
|
require('../../Mediator/index').getUser()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -74,39 +79,11 @@ const pubToEpub = async pub => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} reqID
|
|
||||||
* @param {ISEA} SEA
|
|
||||||
* @param {string} mySecret
|
|
||||||
* @returns {Promise<string>}
|
|
||||||
*/
|
|
||||||
const reqToRecipientPub = async (reqID, SEA, mySecret) => {
|
|
||||||
const maybeEncryptedForMeRecipientPub = await tryAndWait(async (_, user) => {
|
|
||||||
const reqToUser = user.get(Key.REQUEST_TO_USER)
|
|
||||||
const data = await reqToUser.get(reqID).then()
|
|
||||||
|
|
||||||
if (typeof data !== 'string') {
|
|
||||||
throw new TypeError("typeof maybeEncryptedForMeRecipientPub !== 'string'")
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
})
|
|
||||||
|
|
||||||
const encryptedForMeRecipientPub = maybeEncryptedForMeRecipientPub
|
|
||||||
|
|
||||||
const recipientPub = await SEA.decrypt(encryptedForMeRecipientPub, mySecret)
|
|
||||||
|
|
||||||
if (typeof recipientPub !== 'string') {
|
|
||||||
throw new TypeError("typeof recipientPub !== 'string'")
|
|
||||||
}
|
|
||||||
|
|
||||||
return recipientPub
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should only be called with a recipient pub that has already been contacted.
|
* Should only be called with a recipient pub that has already been contacted.
|
||||||
|
* If returns null, a disconnect happened.
|
||||||
* @param {string} recipientPub
|
* @param {string} recipientPub
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string|null>}
|
||||||
*/
|
*/
|
||||||
const recipientPubToLastReqSentID = async recipientPub => {
|
const recipientPubToLastReqSentID = async recipientPub => {
|
||||||
const lastReqSentID = await tryAndWait(async (_, user) => {
|
const lastReqSentID = await tryAndWait(async (_, user) => {
|
||||||
|
|
@ -114,7 +91,7 @@ const recipientPubToLastReqSentID = async recipientPub => {
|
||||||
const data = await userToLastReqSent.get(recipientPub).then()
|
const data = await userToLastReqSent.get(recipientPub).then()
|
||||||
|
|
||||||
if (typeof data !== 'string') {
|
if (typeof data !== 'string') {
|
||||||
throw new TypeError("typeof latestReqSentID !== 'string'")
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
@ -134,31 +111,33 @@ const successfulHandshakeAlreadyExists = async recipientPub => {
|
||||||
return userToIncoming.get(recipientPub).then()
|
return userToIncoming.get(recipientPub).then()
|
||||||
})
|
})
|
||||||
|
|
||||||
return typeof maybeIncomingID === 'string'
|
const maybeOutgoingID = await tryAndWait((_, user) => {
|
||||||
|
const recipientToOutgoing = user.get(Key.RECIPIENT_TO_OUTGOING)
|
||||||
|
|
||||||
|
return recipientToOutgoing.get(recipientPub).then()
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
typeof maybeIncomingID === 'string' && typeof maybeOutgoingID === 'string'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} recipientPub
|
* @param {string} recipientPub
|
||||||
* @param {UserGUNNode} user
|
|
||||||
* @param {ISEA} SEA
|
|
||||||
* @returns {Promise<string|null>}
|
* @returns {Promise<string|null>}
|
||||||
*/
|
*/
|
||||||
const recipientToOutgoingID = async (recipientPub, user, SEA) => {
|
const recipientToOutgoingID = async recipientPub => {
|
||||||
const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
|
const maybeEncryptedOutgoingID = await require('../../Mediator/index')
|
||||||
|
.getUser()
|
||||||
if (typeof mySecret !== 'string') {
|
.get(Key.RECIPIENT_TO_OUTGOING)
|
||||||
throw new TypeError('could not get mySecret')
|
.get(recipientPub)
|
||||||
}
|
.then()
|
||||||
|
|
||||||
const maybeEncryptedOutgoingID = await tryAndWait((_, user) =>
|
|
||||||
user
|
|
||||||
.get(Key.RECIPIENT_TO_OUTGOING)
|
|
||||||
.get(recipientPub)
|
|
||||||
.then()
|
|
||||||
)
|
|
||||||
|
|
||||||
if (typeof maybeEncryptedOutgoingID === 'string') {
|
if (typeof maybeEncryptedOutgoingID === 'string') {
|
||||||
const outgoingID = await SEA.decrypt(maybeEncryptedOutgoingID, mySecret)
|
const outgoingID = await require('../../Mediator/index').mySEA.decrypt(
|
||||||
|
maybeEncryptedOutgoingID,
|
||||||
|
await mySecret()
|
||||||
|
)
|
||||||
|
|
||||||
return outgoingID || null
|
return outgoingID || null
|
||||||
}
|
}
|
||||||
|
|
@ -166,52 +145,6 @@ const recipientToOutgoingID = async (recipientPub, user, SEA) => {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} reqResponse
|
|
||||||
* @param {string} recipientPub
|
|
||||||
* @param {UserGUNNode} user
|
|
||||||
* @param {ISEA} SEA
|
|
||||||
* @returns {Promise<boolean>}
|
|
||||||
*/
|
|
||||||
const reqWasAccepted = async (reqResponse, recipientPub, user, SEA) => {
|
|
||||||
try {
|
|
||||||
const recipientEpub = await pubToEpub(recipientPub)
|
|
||||||
const ourSecret = await SEA.secret(recipientEpub, user._.sea)
|
|
||||||
if (typeof ourSecret !== 'string') {
|
|
||||||
throw new TypeError('typeof ourSecret !== "string"')
|
|
||||||
}
|
|
||||||
|
|
||||||
const decryptedResponse = await SEA.decrypt(reqResponse, ourSecret)
|
|
||||||
|
|
||||||
if (typeof decryptedResponse !== 'string') {
|
|
||||||
throw new TypeError('typeof decryptedResponse !== "string"')
|
|
||||||
}
|
|
||||||
|
|
||||||
const myFeedID = await recipientToOutgoingID(recipientPub, user, SEA)
|
|
||||||
|
|
||||||
if (typeof myFeedID === 'string' && decryptedResponse === myFeedID) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const recipientFeedID = decryptedResponse
|
|
||||||
|
|
||||||
const maybeFeed = await tryAndWait(gun =>
|
|
||||||
gun
|
|
||||||
.user(recipientPub)
|
|
||||||
.get(Key.OUTGOINGS)
|
|
||||||
.get(recipientFeedID)
|
|
||||||
.then()
|
|
||||||
)
|
|
||||||
|
|
||||||
const feedExistsOnRecipient =
|
|
||||||
typeof maybeFeed === 'object' && maybeFeed !== null
|
|
||||||
|
|
||||||
return feedExistsOnRecipient
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`reqWasAccepted() -> ${err.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} userPub
|
* @param {string} userPub
|
||||||
|
|
@ -228,6 +161,18 @@ const currHandshakeAddress = async userPub => {
|
||||||
return typeof maybeAddr === 'string' ? maybeAddr : null
|
return typeof maybeAddr === 'string' ? maybeAddr : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {T[]} arr
|
||||||
|
* @param {(item: T) => void} cb
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const asyncForEach = async (arr, cb) => {
|
||||||
|
const promises = arr.map(item => cb(item))
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @template U
|
* @template U
|
||||||
|
|
@ -266,8 +211,8 @@ const asyncFilter = async (arr, cb) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('./SimpleGUN').ListenerData} listenerData
|
* @param {import('../SimpleGUN').ListenerData} listenerData
|
||||||
* @returns {listenerData is import('./SimpleGUN').ListenerObj}
|
* @returns {listenerData is import('../SimpleGUN').ListenerObj}
|
||||||
*/
|
*/
|
||||||
const dataHasSoul = listenerData =>
|
const dataHasSoul = listenerData =>
|
||||||
typeof listenerData === 'object' && listenerData !== null
|
typeof listenerData === 'object' && listenerData !== null
|
||||||
|
|
@ -278,6 +223,22 @@ const dataHasSoul = listenerData =>
|
||||||
*/
|
*/
|
||||||
const defaultName = pub => 'anon' + pub.slice(0, 8)
|
const defaultName = pub => 'anon' + pub.slice(0, 8)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} pub
|
||||||
|
* @param {string} incomingID
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
const didDisconnect = async (pub, incomingID) => {
|
||||||
|
const feed = await require('../../Mediator/index')
|
||||||
|
.getGun()
|
||||||
|
.user(pub)
|
||||||
|
.get(Key.OUTGOINGS)
|
||||||
|
.get(incomingID)
|
||||||
|
.then()
|
||||||
|
|
||||||
|
return feed === null
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
asyncMap,
|
asyncMap,
|
||||||
asyncFilter,
|
asyncFilter,
|
||||||
|
|
@ -285,11 +246,13 @@ module.exports = {
|
||||||
defaultName,
|
defaultName,
|
||||||
delay,
|
delay,
|
||||||
pubToEpub,
|
pubToEpub,
|
||||||
reqToRecipientPub,
|
|
||||||
recipientPubToLastReqSentID,
|
recipientPubToLastReqSentID,
|
||||||
successfulHandshakeAlreadyExists,
|
successfulHandshakeAlreadyExists,
|
||||||
recipientToOutgoingID,
|
recipientToOutgoingID,
|
||||||
reqWasAccepted,
|
|
||||||
currHandshakeAddress,
|
currHandshakeAddress,
|
||||||
tryAndWait
|
tryAndWait,
|
||||||
|
mySecret,
|
||||||
|
promisifyGunNode: require('./promisifygun'),
|
||||||
|
didDisconnect,
|
||||||
|
asyncForEach
|
||||||
}
|
}
|
||||||
47
services/gunDB/contact-api/utils/promisifygun.js
Normal file
47
services/gunDB/contact-api/utils/promisifygun.js
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* @format
|
||||||
|
* @typedef {import("../SimpleGUN").ValidDataValue} ValidDataValue
|
||||||
|
* @typedef {import('../SimpleGUN').GUNNode} GUNNode
|
||||||
|
* @typedef {import('./PGUNNode').PGUNNode} PGUNNode
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GUNNode} node
|
||||||
|
* @returns {PGUNNode}
|
||||||
|
*/
|
||||||
|
const promisify = node => {
|
||||||
|
const oldPut = node.put.bind(node)
|
||||||
|
const oldSet = node.set.bind(node)
|
||||||
|
const oldGet = node.get.bind(node)
|
||||||
|
|
||||||
|
const _pnode = /** @type {unknown} */ (node)
|
||||||
|
const pnode = /** @type {PGUNNode} */ (_pnode)
|
||||||
|
|
||||||
|
pnode.put = data =>
|
||||||
|
new Promise((res, rej) => {
|
||||||
|
oldPut(data, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
pnode.set = data =>
|
||||||
|
new Promise((res, rej) => {
|
||||||
|
oldSet(data, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
pnode.get = key => promisify(oldGet(key))
|
||||||
|
|
||||||
|
return pnode
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = promisify
|
||||||
220
yarn.lock
220
yarn.lock
|
|
@ -385,24 +385,24 @@
|
||||||
asn1js "^2.0.22"
|
asn1js "^2.0.22"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@peculiar/json-schema@^1.1.5":
|
"@peculiar/json-schema@^1.1.6":
|
||||||
version "1.1.5"
|
version "1.1.9"
|
||||||
resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.5.tgz#376e0e978d2bd7132487a5679ab375a34313e73f"
|
resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.9.tgz#b746e046b787607a1b2804f64437fda2527b3e62"
|
||||||
integrity sha512-y5XYA3pf9+c+YKVpWnPtQbNmlNCs2ehNHyMLJvq4K5Fjwc1N64YGy7MNecKW3uYLga+sqbGTQSUdOdlnaRRbpA==
|
integrity sha512-F2ST2y/IQPgY+1QMw1Q33sqJbGDCeO3lGqI69SL3Hgo0++7iHqprUB1QyxB/A7bN3tuM65MBxoM2JLbwh42lsQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^1.9.3"
|
tslib "^1.10.0"
|
||||||
|
|
||||||
"@peculiar/webcrypto@^1.0.19":
|
"@peculiar/webcrypto@^1.0.22":
|
||||||
version "1.0.19"
|
version "1.0.22"
|
||||||
resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.0.19.tgz#45dd199c57ee655f9efd0332aca6fd53801d89f7"
|
resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.0.22.tgz#9dae652fce6bacd9df15bc91965797cee33adf67"
|
||||||
integrity sha512-+lF69A18LJBLp0/gJIQatCARLah6cTUmLwY0Cdab0zsk+Z53BcpjKQyyP4LIN8oW601ZXv28mWEQ4Cm7MllF6w==
|
integrity sha512-NP6H6ZGXUvJnQJCWzUgnRcQv+9nMCNwLUDhTwOxRUwPFvtHauMOl0oPTKUjbhInCMaE55gJqB4yc0YKbde6Exw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@peculiar/asn1-schema" "^1.0.3"
|
"@peculiar/asn1-schema" "^1.0.3"
|
||||||
"@peculiar/json-schema" "^1.1.5"
|
"@peculiar/json-schema" "^1.1.6"
|
||||||
asn1js "^2.0.26"
|
asn1js "^2.0.26"
|
||||||
pvtsutils "^1.0.6"
|
pvtsutils "^1.0.9"
|
||||||
tslib "^1.10.0"
|
tslib "^1.10.0"
|
||||||
webcrypto-core "^1.0.14"
|
webcrypto-core "^1.0.17"
|
||||||
|
|
||||||
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
|
|
@ -579,6 +579,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
|
||||||
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
|
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
|
||||||
|
|
||||||
|
"@types/jsonwebtoken@^8.3.7":
|
||||||
|
version "8.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.3.7.tgz#ab79ad55b9435834d24cca3112f42c08eedb1a54"
|
||||||
|
integrity sha512-B5SSifLkjB0ns7VXpOOtOUlynE78/hKcY8G8pOAhkLJZinwofIBYqz555nRj2W9iDWZqFhK5R+7NZDaRmKWAoQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/lodash@^4.14.141":
|
"@types/lodash@^4.14.141":
|
||||||
version "4.14.141"
|
version "4.14.141"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.141.tgz#d81f4d0c562abe28713406b571ffb27692a82ae6"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.141.tgz#d81f4d0c562abe28713406b571ffb27692a82ae6"
|
||||||
|
|
@ -604,11 +611,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.17.tgz#b96d4dd3e427382482848948041d3754d40fd5ce"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.17.tgz#b96d4dd3e427382482848948041d3754d40fd5ce"
|
||||||
integrity sha512-p/sGgiPaathCfOtqu2fx5Mu1bcjuP8ALFg4xpGgNkcin7LwRyzUKniEHBKdcE1RPsenq5JVPIpMTJSygLboygQ==
|
integrity sha512-p/sGgiPaathCfOtqu2fx5Mu1bcjuP8ALFg4xpGgNkcin7LwRyzUKniEHBKdcE1RPsenq5JVPIpMTJSygLboygQ==
|
||||||
|
|
||||||
"@types/node@^10.14.17":
|
|
||||||
version "10.14.19"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.19.tgz#f52742c7834a815dedf66edfc8a51547e2a67342"
|
|
||||||
integrity sha512-j6Sqt38ssdMKutXBUuAcmWF8QtHW1Fwz/mz4Y+Wd9mzpBiVFirjpNQf363hG5itkG+yGaD+oiLyb50HxJ36l9Q==
|
|
||||||
|
|
||||||
"@types/normalize-package-data@^2.4.0":
|
"@types/normalize-package-data@^2.4.0":
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
|
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
|
||||||
|
|
@ -680,6 +682,22 @@
|
||||||
lodash.unescape "4.0.1"
|
lodash.unescape "4.0.1"
|
||||||
semver "5.5.0"
|
semver "5.5.0"
|
||||||
|
|
||||||
|
"@unimodules/core@*":
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@unimodules/core/-/core-5.0.0.tgz#e1e3ca3f91f3d27dbc93c6eebc03a40c711da755"
|
||||||
|
integrity sha512-PswccfzFIviX61Lm8h6/QyC94bWe+6cARwhzgzTCKa6aR6azmi4732ExhX4VxfQjJNHB0szYVXGXVEDsFkj+tQ==
|
||||||
|
dependencies:
|
||||||
|
compare-versions "^3.4.0"
|
||||||
|
|
||||||
|
"@unimodules/react-native-adapter@*":
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@unimodules/react-native-adapter/-/react-native-adapter-5.0.0.tgz#af9835821a2bf38390b9f09f3231c0b7546ee510"
|
||||||
|
integrity sha512-qb5p5wUQoi3TRa/33aLLHSnS7sewV99oBxIo9gnzNI3VFzbOm3rsbTjOJNcR2hx0raUolTtnQT75VbgagVQx4w==
|
||||||
|
dependencies:
|
||||||
|
invariant "^2.2.4"
|
||||||
|
lodash "^4.5.0"
|
||||||
|
prop-types "^15.6.1"
|
||||||
|
|
||||||
abab@^2.0.0:
|
abab@^2.0.0:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.2.tgz#a2fba1b122c69a85caa02d10f9270c7219709a9d"
|
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.2.tgz#a2fba1b122c69a85caa02d10f9270c7219709a9d"
|
||||||
|
|
@ -873,7 +891,12 @@ ascli@~1:
|
||||||
colour "~0.7.1"
|
colour "~0.7.1"
|
||||||
optjs "~3.2.2"
|
optjs "~3.2.2"
|
||||||
|
|
||||||
asn1@^0.2.4, asn1@~0.2.3:
|
asmcrypto.js@^0.22.0:
|
||||||
|
version "0.22.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz#38fc1440884d802c7bd37d1d23c2b26a5cd5d2d2"
|
||||||
|
integrity sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA==
|
||||||
|
|
||||||
|
asn1@~0.2.3:
|
||||||
version "0.2.4"
|
version "0.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
|
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
|
||||||
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
|
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
|
||||||
|
|
@ -907,7 +930,7 @@ async-each@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
|
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
|
||||||
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
|
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
|
||||||
|
|
||||||
async-limiter@^1.0.0, async-limiter@~1.0.0:
|
async-limiter@~1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
||||||
|
|
@ -945,6 +968,20 @@ axios@0.19.0, axios@^0.19.0:
|
||||||
follow-redirects "1.5.10"
|
follow-redirects "1.5.10"
|
||||||
is-buffer "^2.0.2"
|
is-buffer "^2.0.2"
|
||||||
|
|
||||||
|
b64-lite@^1.3.1, b64-lite@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/b64-lite/-/b64-lite-1.4.0.tgz#e62442de11f1f21c60e38b74f111ac0242283d3d"
|
||||||
|
integrity sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w==
|
||||||
|
dependencies:
|
||||||
|
base-64 "^0.1.0"
|
||||||
|
|
||||||
|
b64u-lite@^1.0.1:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/b64u-lite/-/b64u-lite-1.1.0.tgz#a581b7df94cbd4bed7cbb19feae816654f0b1bf0"
|
||||||
|
integrity sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A==
|
||||||
|
dependencies:
|
||||||
|
b64-lite "^1.4.0"
|
||||||
|
|
||||||
babel-code-frame@^6.26.0:
|
babel-code-frame@^6.26.0:
|
||||||
version "6.26.0"
|
version "6.26.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||||
|
|
@ -1088,6 +1125,11 @@ balanced-match@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
|
base-64@^0.1.0:
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
|
||||||
|
integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs=
|
||||||
|
|
||||||
base-x@^3.0.2:
|
base-x@^3.0.2:
|
||||||
version "3.0.7"
|
version "3.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.7.tgz#1c5a7fafe8f66b4114063e8da102799d4e7c408f"
|
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.7.tgz#1c5a7fafe8f66b4114063e8da102799d4e7c408f"
|
||||||
|
|
@ -1100,6 +1142,11 @@ base64-arraybuffer@0.1.5:
|
||||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
||||||
integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
|
integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
|
||||||
|
|
||||||
|
base64-js@*, base64-js@^1.0.2, base64-js@^1.3.0:
|
||||||
|
version "1.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||||
|
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
||||||
|
|
||||||
base64id@1.0.0:
|
base64id@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
|
resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
|
||||||
|
|
@ -1275,6 +1322,14 @@ buffer-from@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||||
|
|
||||||
|
buffer@^5.4.3:
|
||||||
|
version "5.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115"
|
||||||
|
integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.0.2"
|
||||||
|
ieee754 "^1.1.4"
|
||||||
|
|
||||||
bytebuffer@~5:
|
bytebuffer@~5:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd"
|
resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd"
|
||||||
|
|
@ -1539,6 +1594,11 @@ commander@~2.20.0:
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.1.tgz#3863ce3ca92d0831dcf2a102f5fb4b5926afd0f9"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.1.tgz#3863ce3ca92d0831dcf2a102f5fb4b5926afd0f9"
|
||||||
integrity sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==
|
integrity sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==
|
||||||
|
|
||||||
|
compare-versions@^3.4.0:
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62"
|
||||||
|
integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==
|
||||||
|
|
||||||
component-bind@1.0.0:
|
component-bind@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
|
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
|
||||||
|
|
@ -2273,6 +2333,13 @@ expect@^24.9.0:
|
||||||
jest-message-util "^24.9.0"
|
jest-message-util "^24.9.0"
|
||||||
jest-regex-util "^24.9.0"
|
jest-regex-util "^24.9.0"
|
||||||
|
|
||||||
|
expo-random@*:
|
||||||
|
version "8.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-random/-/expo-random-8.0.0.tgz#bbcc7a189d29ae9b709b36a6cf84256c22cc16f6"
|
||||||
|
integrity sha512-ukDC3eGSEliBsnobX1bQRAwti9GE8ZEW53AHFf1fVy+JuAhhZm+M5HW7T0ptdbLjm46VpTDGIO4vq+qxeXAl7g==
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.3.0"
|
||||||
|
|
||||||
express-session@^1.15.1:
|
express-session@^1.15.1:
|
||||||
version "1.16.2"
|
version "1.16.2"
|
||||||
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.16.2.tgz#59f36d7770e94872d19b163b6708a2d16aa6848c"
|
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.16.2.tgz#59f36d7770e94872d19b163b6708a2d16aa6848c"
|
||||||
|
|
@ -2711,15 +2778,15 @@ grpc@^1.21.1:
|
||||||
node-pre-gyp "^0.13.0"
|
node-pre-gyp "^0.13.0"
|
||||||
protobufjs "^5.0.3"
|
protobufjs "^5.0.3"
|
||||||
|
|
||||||
gun@^0.2019.1211:
|
"gun@git://github.com/amark/gun#c59e0e95f92779ce6bb3aab823d318bc16b20c33":
|
||||||
version "0.2019.1211"
|
version "0.2020.116"
|
||||||
resolved "https://registry.yarnpkg.com/gun/-/gun-0.2019.1211.tgz#37aa58217f86256b5d6f126450c8860f7bca384e"
|
resolved "git://github.com/amark/gun#c59e0e95f92779ce6bb3aab823d318bc16b20c33"
|
||||||
integrity sha512-wueemUJBtNfVcZDeXK7wYbth+eu7tNFo5T54gAbA3N0VAN3zoPNE12saeekzrkVeMiKVxG8/kiR35BhykQ+CJg==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
ws "~>7.1.0"
|
buffer "^5.4.3"
|
||||||
|
ws "^7.1.2"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@peculiar/webcrypto" "^1.0.19"
|
|
||||||
emailjs "^2.2.0"
|
emailjs "^2.2.0"
|
||||||
|
isomorphic-webcrypto "^2.3.2"
|
||||||
text-encoding "^0.7.0"
|
text-encoding "^0.7.0"
|
||||||
|
|
||||||
handlebars@^4.1.2:
|
handlebars@^4.1.2:
|
||||||
|
|
@ -2902,6 +2969,11 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
safer-buffer ">= 2.1.2 < 3"
|
||||||
|
|
||||||
|
ieee754@^1.1.4:
|
||||||
|
version "1.1.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
|
||||||
|
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
|
||||||
|
|
||||||
ignore-by-default@^1.0.1:
|
ignore-by-default@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
|
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
|
||||||
|
|
@ -3297,6 +3369,24 @@ isobject@^3.0.0, isobject@^3.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||||
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
|
||||||
|
|
||||||
|
isomorphic-webcrypto@^2.3.2:
|
||||||
|
version "2.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/isomorphic-webcrypto/-/isomorphic-webcrypto-2.3.4.tgz#6d18ee7f795ab2f5e9fd7a5b489abaf9ee2e6af5"
|
||||||
|
integrity sha512-SnOCsm0Vls8jeWP4c26ItHFajzfDlRPcKK4YRUv6jukYGzJwl2tKNwSIAiCh1INdoRttaKhJrLc3HBer1om4HA==
|
||||||
|
dependencies:
|
||||||
|
"@peculiar/webcrypto" "^1.0.22"
|
||||||
|
asmcrypto.js "^0.22.0"
|
||||||
|
b64-lite "^1.3.1"
|
||||||
|
b64u-lite "^1.0.1"
|
||||||
|
msrcrypto "^1.5.6"
|
||||||
|
str2buf "^1.3.0"
|
||||||
|
webcrypto-shim "^0.1.4"
|
||||||
|
optionalDependencies:
|
||||||
|
"@unimodules/core" "*"
|
||||||
|
"@unimodules/react-native-adapter" "*"
|
||||||
|
expo-random "*"
|
||||||
|
react-native-securerandom "^0.1.1"
|
||||||
|
|
||||||
isstream@0.1.x, isstream@~0.1.2:
|
isstream@0.1.x, isstream@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||||
|
|
@ -4015,7 +4105,7 @@ lodash@=4.17.4:
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
|
||||||
integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=
|
integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=
|
||||||
|
|
||||||
lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5:
|
lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.5.0:
|
||||||
version "4.17.15"
|
version "4.17.15"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||||
|
|
@ -4030,7 +4120,7 @@ long@~3:
|
||||||
resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
|
resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
|
||||||
integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=
|
integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=
|
||||||
|
|
||||||
loose-envify@^1.0.0:
|
loose-envify@^1.0.0, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||||
|
|
@ -4232,6 +4322,11 @@ ms@^2.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
|
msrcrypto@^1.5.6:
|
||||||
|
version "1.5.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/msrcrypto/-/msrcrypto-1.5.8.tgz#be419be4945bf134d8af52e9d43be7fa261f4a1c"
|
||||||
|
integrity sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q==
|
||||||
|
|
||||||
mute-stream@0.0.8:
|
mute-stream@0.0.8:
|
||||||
version "0.0.8"
|
version "0.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
||||||
|
|
@ -4341,13 +4436,6 @@ node-pre-gyp@^0.13.0:
|
||||||
semver "^5.3.0"
|
semver "^5.3.0"
|
||||||
tar "^4"
|
tar "^4"
|
||||||
|
|
||||||
node-rsa@^1.0.7:
|
|
||||||
version "1.0.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/node-rsa/-/node-rsa-1.0.7.tgz#85b7a6d6fa8ee624be6402a6b41be49272d58055"
|
|
||||||
integrity sha512-idwRXma6scFufZmbaKkHpJoLL93yynRefP6yur13wZ5i9FR35ex451KCoF2OORDeJanyRVahmjjiwmUlCnTqJA==
|
|
||||||
dependencies:
|
|
||||||
asn1 "^0.2.4"
|
|
||||||
|
|
||||||
nodemon@^1.19.3:
|
nodemon@^1.19.3:
|
||||||
version "1.19.3"
|
version "1.19.3"
|
||||||
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.3.tgz#db71b3e62aef2a8e1283a9fa00164237356102c0"
|
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.3.tgz#db71b3e62aef2a8e1283a9fa00164237356102c0"
|
||||||
|
|
@ -4446,7 +4534,7 @@ oauth-sign@~0.9.0:
|
||||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
||||||
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
||||||
|
|
||||||
object-assign@^4, object-assign@^4.1.0:
|
object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||||
|
|
@ -4887,6 +4975,15 @@ prompts@^2.0.1:
|
||||||
kleur "^3.0.3"
|
kleur "^3.0.3"
|
||||||
sisteransi "^1.0.3"
|
sisteransi "^1.0.3"
|
||||||
|
|
||||||
|
prop-types@^15.6.1:
|
||||||
|
version "15.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||||
|
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
react-is "^16.8.1"
|
||||||
|
|
||||||
protobufjs@^5.0.3:
|
protobufjs@^5.0.3:
|
||||||
version "5.0.3"
|
version "5.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.3.tgz#e4dfe9fb67c90b2630d15868249bcc4961467a17"
|
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.3.tgz#e4dfe9fb67c90b2630d15868249bcc4961467a17"
|
||||||
|
|
@ -4957,12 +5054,11 @@ punycode@^2.1.0, punycode@^2.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||||
|
|
||||||
pvtsutils@^1.0.4, pvtsutils@^1.0.6:
|
pvtsutils@^1.0.9:
|
||||||
version "1.0.6"
|
version "1.0.9"
|
||||||
resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.0.6.tgz#e3883fd77abdd4c124131f6a49f3914cd9f21290"
|
resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.0.9.tgz#0eb6106f27878ccaa55e7dfbf6bd2c75af461dee"
|
||||||
integrity sha512-0yNrOdJyLE7FZzmeEHTKanwBr5XbmDAd020cKa4ZiTYuGMBYBZmq7vHOhcOqhVllh6gghDBbaz1lnVdOqiB7cw==
|
integrity sha512-/kDsuCKPqJuIzn37w6+iN+TiSrN+zrwPEd7FjT61oNbRvceGdsS94fMEWZ4/h6QZU5EZhBMiV+79IYedroP/Yw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "^10.14.17"
|
|
||||||
tslib "^1.10.0"
|
tslib "^1.10.0"
|
||||||
|
|
||||||
pvutils@latest:
|
pvutils@latest:
|
||||||
|
|
@ -5015,11 +5111,23 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
|
react-is@^16.8.1:
|
||||||
|
version "16.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
|
||||||
|
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
|
||||||
|
|
||||||
react-is@^16.8.4:
|
react-is@^16.8.4:
|
||||||
version "16.10.1"
|
version "16.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.10.1.tgz#0612786bf19df406502d935494f0450b40b8294f"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.10.1.tgz#0612786bf19df406502d935494f0450b40b8294f"
|
||||||
integrity sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw==
|
integrity sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw==
|
||||||
|
|
||||||
|
react-native-securerandom@^0.1.1:
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz#f130623a412c338b0afadedbc204c5cbb8bf2070"
|
||||||
|
integrity sha1-8TBiOkEsM4sK+t7bwgTFy7i/IHA=
|
||||||
|
dependencies:
|
||||||
|
base64-js "*"
|
||||||
|
|
||||||
read-pkg-up@^1.0.1:
|
read-pkg-up@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
||||||
|
|
@ -5697,6 +5805,11 @@ stealthy-require@^1.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
|
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
|
||||||
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
|
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
|
||||||
|
|
||||||
|
str2buf@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/str2buf/-/str2buf-1.3.0.tgz#a4172afff4310e67235178e738a2dbb573abead0"
|
||||||
|
integrity sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA==
|
||||||
|
|
||||||
string-length@^2.0.0:
|
string-length@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
||||||
|
|
@ -6221,14 +6334,19 @@ walker@^1.0.7, walker@~1.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
makeerror "1.0.x"
|
makeerror "1.0.x"
|
||||||
|
|
||||||
webcrypto-core@^1.0.14:
|
webcrypto-core@^1.0.17:
|
||||||
version "1.0.14"
|
version "1.0.17"
|
||||||
resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.0.14.tgz#c015088fbc9c235ebd8b35047a131c5ff58f7152"
|
resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.0.17.tgz#a9354bc0b1ba6735e882f4137ede2c4366e6ad9b"
|
||||||
integrity sha512-iGZQcH/o3Jv6mpvCbzan6uAcUcLTTnUCil6RVYakcNh5/QXIKRRC06EFxHru9lHgVKucZy3gG4OBiup0IsOr0g==
|
integrity sha512-7jxTLgtM+TahBPErx/Dd2XvxFDfWJrHxjVeTSvIa4LSgiYrmCPlC2INiAMAfb8MbtHiwJKKqF5sPS0AWNjBbXw==
|
||||||
dependencies:
|
dependencies:
|
||||||
pvtsutils "^1.0.4"
|
pvtsutils "^1.0.9"
|
||||||
tslib "^1.10.0"
|
tslib "^1.10.0"
|
||||||
|
|
||||||
|
webcrypto-shim@^0.1.4:
|
||||||
|
version "0.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/webcrypto-shim/-/webcrypto-shim-0.1.5.tgz#13e34a010ccc544edecfe8a2642204502841bcf0"
|
||||||
|
integrity sha512-mE+E00gulvbLjHaAwl0kph60oOLQRsKyivEFgV9DMM/3Y05F1vZvGq12hAcNzHRnYxyEOABBT/XMtwGSg5xA7A==
|
||||||
|
|
||||||
webidl-conversions@^4.0.2:
|
webidl-conversions@^4.0.2:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||||
|
|
@ -6383,6 +6501,11 @@ ws@^5.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
|
ws@^7.1.2:
|
||||||
|
version "7.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e"
|
||||||
|
integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A==
|
||||||
|
|
||||||
ws@~6.1.0:
|
ws@~6.1.0:
|
||||||
version "6.1.4"
|
version "6.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
|
||||||
|
|
@ -6390,13 +6513,6 @@ ws@~6.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
ws@~>7.1.0:
|
|
||||||
version "7.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.1.2.tgz#c672d1629de8bb27a9699eb599be47aeeedd8f73"
|
|
||||||
integrity sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==
|
|
||||||
dependencies:
|
|
||||||
async-limiter "^1.0.0"
|
|
||||||
|
|
||||||
xdg-basedir@^3.0.0:
|
xdg-basedir@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue