UNPKG

edge-core-js

Version:

Edge account & wallet management library

214 lines (193 loc) 6.3 kB
function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }import { makeSyncClient } from 'edge-sync-client' import { createStore } from 'redux' import { attachPixie, filterPixie, } from 'redux-pixies' import { emit } from 'yaob' import { validateServer } from '../util/validateServer' import { CLIENT_FILE_NAME, clientFile } from './context/client-file' import { INFO_CACHE_FILE_NAME, infoCacheFile } from './context/info-cache-file' import { filterLogs, makeLog } from './log/log' import { loadAirbitzStashes } from './login/airbitz-stashes' import { loadStashes } from './login/login-stash' import { watchPlugins } from './plugins/plugins-actions' import { rootPixie, } from './root-pixie' import { defaultLogSettings, reducer, } from './root-reducer' let allContexts = [] const enhancer = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION__ != null ? window.__REDUX_DEVTOOLS_EXTENSION__({ name: 'core' }) : undefined /** * Creates the root object for the entire core state machine. * This core object contains the `io` object, context options, * Redux store, and tree of background workers. */ export async function makeContext( ios, logBackend, opts ) { const { io } = ios const { airbitzSupport = false, apiSecret, appId = '', authServer, deviceDescription = null, changeServer, hideKeys = false, infoServer, loginServer, plugins: pluginsInit = {}, skipBlockHeight = false, syncServer } = opts let { apiKey } = opts if (apiKey == null || apiKey === '') { apiKey = '4248c1bf41e53b840a5fdb2c872dd3ade525e66d' } const authServers = toServerArray(authServer, [ 'https://login1.edge.app', 'https://login2.edge.app' ]) const changeServers = toServerArray(changeServer, ['wss://change1.edge.app']) const infoServers = toServerArray(infoServer, [ 'https://info1.edge.app', 'https://info2.edge.app' ]) const loginServers = toServerArray( loginServer, authServers.map(server => server.replace(/\/api$/, '')) ) const syncServers = toServerArray(syncServer, [ 'https://sync-us1.edge.app', 'https://sync-us2.edge.app', 'https://sync-us3.edge.app', 'https://sync-us4.edge.app', 'https://sync-us5.edge.app', 'https://sync-us6.edge.app', 'https://sync-eu.edge.app' ]) const logSettings = { ...defaultLogSettings, ...opts.logSettings } changeServers.map(server => validateServer(server)) infoServers.map(server => validateServer(server)) loginServers.map(server => validateServer(server)) syncServers.map(server => validateServer(server)) // Create a redux store: const redux = createStore(reducer, enhancer) // Create a log wrapper, using the settings from redux: logBackend = filterLogs(logBackend, () => { const state = redux.getState() return state.ready ? state.logSettings : logSettings }) const log = makeLog(logBackend, 'edge-core') // Load the login stashes from disk: let [clientInfo, infoCache = {}, stashes] = await Promise.all([ clientFile.load(io.disklet, CLIENT_FILE_NAME), infoCacheFile.load(io.disklet, INFO_CACHE_FILE_NAME), loadStashes(io.disklet, log) ]) // Load legacy stashes from disk if (airbitzSupport) { // Edge will write modern files to disk at login time, // but it won't delete the legacy Airbitz data. // Once this happens, we need to ignore the legacy files // and just use the new files: const avoidUsernames = new Set() for (const { username } of stashes) { if (username != null) avoidUsernames.add(username) } const airbitzStashes = await loadAirbitzStashes(io, avoidUsernames) stashes.push(...airbitzStashes) } // Save the initial client info if the client is new: if (clientInfo == null) { clientInfo = { clientId: io.random(16), duressEnabled: false, loginWaitTimestamps: {} } await clientFile.save(io.disklet, CLIENT_FILE_NAME, clientInfo) } // Write everything to redux: redux.dispatch({ type: 'INIT', payload: { apiKey, apiSecret, appId, changeServers, loginServers, infoCache, infoServers, syncServers, clientInfo, deviceDescription, hideKeys, logSettings, pluginsInit, skipBlockHeight, stashes } }) // Subscribe to new plugins: const closePlugins = watchPlugins( ios, infoCache, logBackend, pluginsInit, redux.dispatch ) // Create sync client: const syncClient = makeSyncClient({ log, fetch: (uri, opts) => io.fetch(uri, { ...opts, corsBypass: 'never' }), edgeServers: { infoServers, syncServers } }) // Start the pixie tree: const mirror = { output: {} } const closePixie = attachPixie( redux, filterPixie( rootPixie, (props) => ({ ...props, close() { closePixie() closePlugins() redux.dispatch({ type: 'CLOSE' }) }, io, log, logBackend, onError: error => { if (_optionalChain([mirror, 'access', _ => _.output, 'access', _2 => _2.context, 'optionalAccess', _3 => _3.api]) != null) { emit(mirror.output.context.api, 'error', error) } }, syncClient }) ), error => log.error(error), output => (mirror.output = output) ) const out = mirror.output.context.api allContexts.push(out) return out } function toServerArray( server, fallback ) { return typeof server === 'string' ? [server] : server != null && server.length > 0 ? server : fallback } /** * We use this for unit testing, to kill all core contexts. */ export function closeEdge() { for (const context of allContexts) context.close().catch(() => {}) allContexts = [] }