edge-core-js 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/android/src/main/assets/edge-core-js/edge-core.js +1 -1
- package/lib/core/account/account-api.js +2 -2
- package/lib/core/account/account-pixie.js +3 -6
- package/lib/core/account/lobby-api.js +1 -3
- package/lib/core/actions.js +6 -0
- package/lib/core/context/context-pixie.js +47 -2
- package/lib/core/context/info-cache-file.js +18 -0
- package/lib/core/currency/currency-pixie.js +32 -2
- package/lib/core/currency/wallet/currency-wallet-callbacks.js +3 -7
- package/lib/core/currency/wallet/currency-wallet-files.js +18 -16
- package/lib/core/currency/wallet/metadata.js +2 -1
- package/lib/core/fake/fake-db.js +3 -3
- package/lib/core/fake/fake-server.js +6 -6
- package/lib/core/fake/fake-world.js +5 -2
- package/lib/core/plugins/plugins-actions.js +6 -3
- package/lib/core/plugins/plugins-selectors.js +1 -1
- package/lib/core/root-reducer.js +21 -2
- package/lib/core/root.js +33 -16
- package/lib/core/storage/storage-actions.js +3 -3
- package/lib/core/swap/swap-api.js +5 -4
- package/lib/flow/types.js +6 -2
- package/lib/io/react-native/native-bridge.js +1 -0
- package/lib/node/index.js +179 -82
- package/lib/types/types.js +4 -0
- package/lib/util/match-json.js +47 -0
- package/package.json +1 -1
- package/src/types/types.ts +6 -2
- package/lib/core/context/context-reducer.js +0 -31
package/lib/node/index.js
CHANGED
|
@@ -20,7 +20,7 @@ var elliptic = require('elliptic');
|
|
|
20
20
|
var yavent = require('yavent');
|
|
21
21
|
var reduxKeto = require('redux-keto');
|
|
22
22
|
var crypto = require('crypto');
|
|
23
|
-
var fetch = require('node-fetch');
|
|
23
|
+
var fetch$1 = require('node-fetch');
|
|
24
24
|
|
|
25
25
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
26
26
|
|
|
@@ -30,7 +30,7 @@ var aesjs__default = /*#__PURE__*/_interopDefaultLegacy(aesjs);
|
|
|
30
30
|
var hashjs__default = /*#__PURE__*/_interopDefaultLegacy(hashjs);
|
|
31
31
|
var elliptic__default = /*#__PURE__*/_interopDefaultLegacy(elliptic);
|
|
32
32
|
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
|
|
33
|
-
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
33
|
+
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch$1);
|
|
34
34
|
|
|
35
35
|
function scrypt$2(data, salt, n, r, p, dklen) {
|
|
36
36
|
return new Promise((resolve, reject) => {
|
|
@@ -1843,7 +1843,7 @@ async function waitForPlugins(ai) {
|
|
|
1843
1843
|
swap
|
|
1844
1844
|
} = props.state.plugins;
|
|
1845
1845
|
const missingPlugins = [];
|
|
1846
|
-
for (const pluginId
|
|
1846
|
+
for (const pluginId of Object.keys(init)) {
|
|
1847
1847
|
const shouldLoad = init[pluginId] !== false && init[pluginId] != null;
|
|
1848
1848
|
if (shouldLoad && currency[pluginId] == null && swap[pluginId] == null) {
|
|
1849
1849
|
missingPlugins.push(pluginId);
|
|
@@ -3258,6 +3258,13 @@ const clientFile = makeJsonFile(cleaners.asObject({
|
|
|
3258
3258
|
clientId: asBase64
|
|
3259
3259
|
}));
|
|
3260
3260
|
|
|
3261
|
+
const INFO_CACHE_FILE_NAME = 'infoCache.json';
|
|
3262
|
+
const asInfoCacheFile = cleaners.asObject({
|
|
3263
|
+
corePlugins: cleaners.asObject(asJsonObject),
|
|
3264
|
+
syncServers: cleaners.asArray(cleaners.asString)
|
|
3265
|
+
});
|
|
3266
|
+
const infoCacheFile = makeJsonFile(asInfoCacheFile);
|
|
3267
|
+
|
|
3261
3268
|
const allPlugins = {};
|
|
3262
3269
|
let allPluginsLocked = false;
|
|
3263
3270
|
const onPluginsAdded = [];
|
|
@@ -3272,7 +3279,7 @@ function addEdgeCorePlugins(plugins) {
|
|
|
3272
3279
|
}
|
|
3273
3280
|
|
|
3274
3281
|
// Save the new plugins:
|
|
3275
|
-
for (const pluginId
|
|
3282
|
+
for (const pluginId of Object.keys(plugins)) {
|
|
3276
3283
|
allPlugins[pluginId] = plugins[pluginId];
|
|
3277
3284
|
}
|
|
3278
3285
|
|
|
@@ -3291,7 +3298,7 @@ function lockEdgeCorePlugins() {
|
|
|
3291
3298
|
/**
|
|
3292
3299
|
* Subscribes a context object to the core plugin list.
|
|
3293
3300
|
*/
|
|
3294
|
-
function watchPlugins(ios, logBackend, pluginsInit, dispatch) {
|
|
3301
|
+
function watchPlugins(ios, infoCache, logBackend, pluginsInit, dispatch) {
|
|
3295
3302
|
const {
|
|
3296
3303
|
io,
|
|
3297
3304
|
nativeIo
|
|
@@ -3302,7 +3309,7 @@ function watchPlugins(ios, logBackend, pluginsInit, dispatch) {
|
|
|
3302
3309
|
};
|
|
3303
3310
|
function pluginsAdded(plugins) {
|
|
3304
3311
|
const out = {};
|
|
3305
|
-
for (const pluginId
|
|
3312
|
+
for (const pluginId of Object.keys(plugins)) {
|
|
3306
3313
|
const plugin = plugins[pluginId];
|
|
3307
3314
|
const log = makeLog(logBackend, pluginId);
|
|
3308
3315
|
const initOptions = pluginsInit[pluginId];
|
|
@@ -3312,6 +3319,7 @@ function watchPlugins(ios, logBackend, pluginsInit, dispatch) {
|
|
|
3312
3319
|
try {
|
|
3313
3320
|
if (typeof plugin === 'function') {
|
|
3314
3321
|
const opts = {
|
|
3322
|
+
infoPayload: infoCache.corePlugins?.[pluginId] ?? {},
|
|
3315
3323
|
initOptions: typeof initOptions === 'object' ? initOptions : {},
|
|
3316
3324
|
io: legacyIo,
|
|
3317
3325
|
log,
|
|
@@ -3563,7 +3571,7 @@ async function addStorageWallet(ai, walletInfo) {
|
|
|
3563
3571
|
const {
|
|
3564
3572
|
lastHash
|
|
3565
3573
|
} = status;
|
|
3566
|
-
ai.props.log.error(`Could not sync ${syncKey} with last hash ${String(lastHash)}: ${String(error)}`);
|
|
3574
|
+
ai.props.log.error(`Could not sync ${String(syncKey)} with last hash ${String(lastHash)}: ${String(error)}`);
|
|
3567
3575
|
onError(error);
|
|
3568
3576
|
});
|
|
3569
3577
|
} else await syncPromise;
|
|
@@ -4049,8 +4057,9 @@ const asEdgeMetadata = raw => {
|
|
|
4049
4057
|
} = clean;
|
|
4050
4058
|
|
|
4051
4059
|
// Delete corrupt amounts that exceed the Javascript number range:
|
|
4052
|
-
for (const fiat of Object.keys(
|
|
4060
|
+
for (const fiat of Object.keys(exchangeAmount)) {
|
|
4053
4061
|
if (String(exchangeAmount[fiat]).includes('e')) {
|
|
4062
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
4054
4063
|
delete exchangeAmount[fiat];
|
|
4055
4064
|
}
|
|
4056
4065
|
}
|
|
@@ -5469,7 +5478,7 @@ const asAppIdInfo = cleaners.asObject({
|
|
|
5469
5478
|
*/
|
|
5470
5479
|
async function fetchAppIdInfo(ai, appId) {
|
|
5471
5480
|
try {
|
|
5472
|
-
const infoServerUri = shuffle(ai.props.state.
|
|
5481
|
+
const [infoServerUri] = shuffle(ai.props.state.infoServers);
|
|
5473
5482
|
const url = `${infoServerUri}/v1/appIdInfo/${appId}`;
|
|
5474
5483
|
const response = await ai.props.io.fetch(url);
|
|
5475
5484
|
if (response.status === 404) {
|
|
@@ -5820,21 +5829,23 @@ async function loadTokensFile(input) {
|
|
|
5820
5829
|
return;
|
|
5821
5830
|
}
|
|
5822
5831
|
const legacyCurrencyCodes = await legacyTokensFile.load(disklet, LEGACY_TOKENS_FILE);
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5832
|
+
if (legacyCurrencyCodes != null) {
|
|
5833
|
+
const {
|
|
5834
|
+
accountId,
|
|
5835
|
+
currencyInfo,
|
|
5836
|
+
pluginId
|
|
5837
|
+
} = input.props.walletState;
|
|
5838
|
+
const accountState = input.props.state.accounts[accountId];
|
|
5839
|
+
const tokenIds = currencyCodesToTokenIds(accountState.builtinTokens[pluginId], accountState.customTokens[pluginId], currencyInfo, legacyCurrencyCodes);
|
|
5840
|
+
dispatch({
|
|
5841
|
+
type: 'CURRENCY_WALLET_LOADED_TOKEN_FILE',
|
|
5842
|
+
payload: {
|
|
5843
|
+
walletId: input.props.walletId,
|
|
5844
|
+
detectedTokenIds: [],
|
|
5845
|
+
enabledTokenIds: tokenIds
|
|
5846
|
+
}
|
|
5847
|
+
});
|
|
5848
|
+
}
|
|
5838
5849
|
}
|
|
5839
5850
|
|
|
5840
5851
|
/**
|
|
@@ -6653,7 +6664,7 @@ function makeCurrencyWalletCallbacks(input) {
|
|
|
6653
6664
|
return;
|
|
6654
6665
|
}
|
|
6655
6666
|
pushUpdate({
|
|
6656
|
-
id: `${walletId}==${tokenId}`,
|
|
6667
|
+
id: `${walletId}==${String(tokenId)}`,
|
|
6657
6668
|
action: 'onTokenBalanceChanged',
|
|
6658
6669
|
updateFunc: () => {
|
|
6659
6670
|
input.props.dispatch({
|
|
@@ -8254,14 +8265,15 @@ async function fetchSwapQuotes(ai, accountId, request, opts = {}) {
|
|
|
8254
8265
|
slowResponseMs = 20000
|
|
8255
8266
|
} = opts;
|
|
8256
8267
|
const {
|
|
8257
|
-
log
|
|
8268
|
+
log,
|
|
8269
|
+
state
|
|
8258
8270
|
} = ai.props;
|
|
8259
|
-
const account =
|
|
8271
|
+
const account = state.accounts[accountId];
|
|
8260
8272
|
const {
|
|
8261
8273
|
swapSettings,
|
|
8262
8274
|
userSettings
|
|
8263
8275
|
} = account;
|
|
8264
|
-
const swapPlugins =
|
|
8276
|
+
const swapPlugins = state.plugins.swap;
|
|
8265
8277
|
log.warn('Requesting swap quotes for: ', {
|
|
8266
8278
|
...request,
|
|
8267
8279
|
fromWallet: request.fromWallet.id,
|
|
@@ -8283,6 +8295,7 @@ async function fetchSwapQuotes(ai, accountId, request, opts = {}) {
|
|
|
8283
8295
|
// Start request:
|
|
8284
8296
|
pendingIds.add(pluginId);
|
|
8285
8297
|
promises.push(swapPlugins[pluginId].fetchSwapQuote(request, userSettings[pluginId], {
|
|
8298
|
+
infoPayload: state.infoCache.corePlugins?.[pluginId] ?? {},
|
|
8286
8299
|
promoCode: promoCodes[pluginId]
|
|
8287
8300
|
}).then(quote => {
|
|
8288
8301
|
const {
|
|
@@ -9241,8 +9254,8 @@ function makeAccountApi(ai, accountId) {
|
|
|
9241
9254
|
} = walletOutput;
|
|
9242
9255
|
if (engine == null) throw new Error(`Invalid wallet: ${activateWalletId} not found`);
|
|
9243
9256
|
if (engine.engineActivateWallet == null) throw new Error(`activateWallet unsupported by walletId ${activateWalletId}`);
|
|
9244
|
-
const walletId = paymentInfo?.walletId;
|
|
9245
|
-
const wallet = currencyWallets[walletId
|
|
9257
|
+
const walletId = paymentInfo?.walletId ?? '';
|
|
9258
|
+
const wallet = currencyWallets[walletId];
|
|
9246
9259
|
if (wallet == null) {
|
|
9247
9260
|
throw new Error(`No wallet for walletId ${walletId}`);
|
|
9248
9261
|
}
|
|
@@ -9702,14 +9715,7 @@ const accountPixie = reduxPixies.combinePixies({
|
|
|
9702
9715
|
});
|
|
9703
9716
|
return {
|
|
9704
9717
|
update() {
|
|
9705
|
-
|
|
9706
|
-
accountOutput
|
|
9707
|
-
} = input.props;
|
|
9708
|
-
if (accountOutput == null) return;
|
|
9709
|
-
const {
|
|
9710
|
-
accountApi
|
|
9711
|
-
} = accountOutput;
|
|
9712
|
-
if (accountApi == null) return;
|
|
9718
|
+
if (input.props.accountOutput?.accountApi == null) return;
|
|
9713
9719
|
|
|
9714
9720
|
// Start once the EdgeAccount API exists:
|
|
9715
9721
|
dataTask.start({
|
|
@@ -10118,16 +10124,110 @@ const context = reduxPixies.combinePixies({
|
|
|
10118
10124
|
}
|
|
10119
10125
|
}
|
|
10120
10126
|
};
|
|
10121
|
-
}
|
|
10127
|
+
},
|
|
10128
|
+
infoFetcher: reduxPixies.filterPixie(input => {
|
|
10129
|
+
async function doInfoSync() {
|
|
10130
|
+
const {
|
|
10131
|
+
dispatch,
|
|
10132
|
+
io
|
|
10133
|
+
} = input.props;
|
|
10134
|
+
const [infoServerUri] = shuffle(input.props.state.infoServers);
|
|
10135
|
+
const response = await fetch(`${infoServerUri}/v1/coreRollup`, {
|
|
10136
|
+
headers: {
|
|
10137
|
+
accept: 'application/json'
|
|
10138
|
+
}
|
|
10139
|
+
});
|
|
10140
|
+
if (!response.ok) return;
|
|
10141
|
+
const json = await response.json();
|
|
10142
|
+
const infoCache = asInfoCacheFile(json);
|
|
10143
|
+
dispatch({
|
|
10144
|
+
type: 'INFO_CACHE_FETCHED',
|
|
10145
|
+
payload: infoCache
|
|
10146
|
+
});
|
|
10147
|
+
await infoCacheFile.save(io.disklet, INFO_CACHE_FILE_NAME, infoCache);
|
|
10148
|
+
}
|
|
10149
|
+
const infoTask = makePeriodicTask(doInfoSync, 10 * 60 * 1000);
|
|
10150
|
+
infoTask.start();
|
|
10151
|
+
return {
|
|
10152
|
+
update() {},
|
|
10153
|
+
destroy() {
|
|
10154
|
+
infoTask.stop();
|
|
10155
|
+
}
|
|
10156
|
+
};
|
|
10157
|
+
}, props => props.state.paused ? undefined : props)
|
|
10122
10158
|
});
|
|
10123
10159
|
|
|
10160
|
+
/**
|
|
10161
|
+
* Compares two JSON-like objects, returning false if they differ.
|
|
10162
|
+
*/
|
|
10163
|
+
function matchJson(a, b) {
|
|
10164
|
+
// Use simple equality, unless a and b are proper objects:
|
|
10165
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a == null || b == null) {
|
|
10166
|
+
return a === b;
|
|
10167
|
+
}
|
|
10168
|
+
|
|
10169
|
+
// These must either be both arrays or both objects:
|
|
10170
|
+
const aIsArray = Array.isArray(a);
|
|
10171
|
+
const bIsArray = Array.isArray(b);
|
|
10172
|
+
if (aIsArray !== bIsArray) return false;
|
|
10173
|
+
|
|
10174
|
+
// Compare arrays in order:
|
|
10175
|
+
if (aIsArray) {
|
|
10176
|
+
if (a.length !== b.length) return false;
|
|
10177
|
+
for (let i = 0; i < a.length; ++i) {
|
|
10178
|
+
if (!matchJson(a[i], b[i])) return false;
|
|
10179
|
+
}
|
|
10180
|
+
return true;
|
|
10181
|
+
}
|
|
10182
|
+
|
|
10183
|
+
// These are both regular objects, so grab the keys,
|
|
10184
|
+
// ignoring entries where the value is `undefined`:
|
|
10185
|
+
const aKeys = Object.getOwnPropertyNames(a).filter(key => a[key] !== undefined);
|
|
10186
|
+
const bKeys = Object.getOwnPropertyNames(b).filter(key => b[key] !== undefined);
|
|
10187
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
10188
|
+
|
|
10189
|
+
// We know that both objects have the same number of properties,
|
|
10190
|
+
// so if every property in `a` has a matching property in `b`,
|
|
10191
|
+
// the objects must be identical, regardless of key order.
|
|
10192
|
+
for (const key of aKeys) {
|
|
10193
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
|
|
10194
|
+
if (!matchJson(a[key], b[key])) return false;
|
|
10195
|
+
}
|
|
10196
|
+
return true;
|
|
10197
|
+
}
|
|
10198
|
+
|
|
10124
10199
|
const currency$1 = reduxPixies.combinePixies({
|
|
10125
10200
|
wallets: reduxPixies.mapPixie(walletPixie, props => props.state.currency.currencyWalletIds, (props, walletId) => ({
|
|
10126
10201
|
...props,
|
|
10127
10202
|
walletId,
|
|
10128
10203
|
walletState: props.state.currency.wallets[walletId],
|
|
10129
10204
|
walletOutput: props.output.currency.wallets[walletId]
|
|
10130
|
-
}))
|
|
10205
|
+
})),
|
|
10206
|
+
pluginUpdater(input) {
|
|
10207
|
+
let lastInfo;
|
|
10208
|
+
return async () => {
|
|
10209
|
+
const {
|
|
10210
|
+
infoCache,
|
|
10211
|
+
plugins
|
|
10212
|
+
} = input.props.state;
|
|
10213
|
+
|
|
10214
|
+
// Bail out quickly if nothing has changed:
|
|
10215
|
+
if (lastInfo === infoCache) return;
|
|
10216
|
+
|
|
10217
|
+
// Update plugins after the first run:
|
|
10218
|
+
if (lastInfo != null) {
|
|
10219
|
+
for (const pluginId of Object.keys(plugins.currency)) {
|
|
10220
|
+
const plugin = plugins.currency[pluginId];
|
|
10221
|
+
const newPayload = infoCache.corePlugins?.[pluginId] ?? {};
|
|
10222
|
+
const oldPayload = lastInfo.corePlugins?.[pluginId] ?? {};
|
|
10223
|
+
if (plugin.updateInfoPayload != null && !matchJson(oldPayload, newPayload)) {
|
|
10224
|
+
await plugin.updateInfoPayload(newPayload);
|
|
10225
|
+
}
|
|
10226
|
+
}
|
|
10227
|
+
}
|
|
10228
|
+
lastInfo = infoCache;
|
|
10229
|
+
};
|
|
10230
|
+
}
|
|
10131
10231
|
});
|
|
10132
10232
|
|
|
10133
10233
|
/**
|
|
@@ -10280,26 +10380,6 @@ const rootPixie = reduxPixies.combinePixies({
|
|
|
10280
10380
|
scrypt
|
|
10281
10381
|
});
|
|
10282
10382
|
|
|
10283
|
-
const defaultEdgeServers = {
|
|
10284
|
-
infoServers: [],
|
|
10285
|
-
syncServers: []
|
|
10286
|
-
};
|
|
10287
|
-
const contextConfig = reduxKeto.buildReducer({
|
|
10288
|
-
edgeServers(state = defaultEdgeServers, action) {
|
|
10289
|
-
if (action.type === 'INIT') {
|
|
10290
|
-
const {
|
|
10291
|
-
infoServers,
|
|
10292
|
-
syncServers
|
|
10293
|
-
} = action.payload;
|
|
10294
|
-
return {
|
|
10295
|
-
infoServers,
|
|
10296
|
-
syncServers
|
|
10297
|
-
};
|
|
10298
|
-
}
|
|
10299
|
-
return state;
|
|
10300
|
-
}
|
|
10301
|
-
});
|
|
10302
|
-
|
|
10303
10383
|
const currency = reduxKeto.buildReducer({
|
|
10304
10384
|
currencyWalletIds(state, action, next) {
|
|
10305
10385
|
// Optimize the common case:
|
|
@@ -10554,6 +10634,18 @@ const reducer = reduxKeto.buildReducer({
|
|
|
10554
10634
|
hideKeys(state = true, action) {
|
|
10555
10635
|
return action.type === 'INIT' ? action.payload.hideKeys : state;
|
|
10556
10636
|
},
|
|
10637
|
+
infoCache(state = {}, action) {
|
|
10638
|
+
switch (action.type) {
|
|
10639
|
+
case 'INIT':
|
|
10640
|
+
return action.payload.infoCache;
|
|
10641
|
+
case 'INFO_CACHE_FETCHED':
|
|
10642
|
+
return action.payload;
|
|
10643
|
+
}
|
|
10644
|
+
return state;
|
|
10645
|
+
},
|
|
10646
|
+
infoServers(state = [], action) {
|
|
10647
|
+
return action.type === 'INIT' ? action.payload.infoServers : state;
|
|
10648
|
+
},
|
|
10557
10649
|
lastAccountId(state, action, next) {
|
|
10558
10650
|
return `login${next.accountCount}`;
|
|
10559
10651
|
},
|
|
@@ -10575,7 +10667,9 @@ const reducer = reduxKeto.buildReducer({
|
|
|
10575
10667
|
skipBlockHeight(state = false, action) {
|
|
10576
10668
|
return action.type === 'INIT' ? action.payload.skipBlockHeight : state;
|
|
10577
10669
|
},
|
|
10578
|
-
|
|
10670
|
+
syncServers(state = [], action) {
|
|
10671
|
+
return action.type === 'INIT' ? action.payload.syncServers : state;
|
|
10672
|
+
},
|
|
10579
10673
|
currency,
|
|
10580
10674
|
login,
|
|
10581
10675
|
plugins,
|
|
@@ -10600,15 +10694,15 @@ async function makeContext(ios, logBackend, opts) {
|
|
|
10600
10694
|
apiKey,
|
|
10601
10695
|
appId = '',
|
|
10602
10696
|
authServer = 'https://login.edge.app/api',
|
|
10603
|
-
infoServer
|
|
10604
|
-
syncServer
|
|
10697
|
+
infoServer,
|
|
10698
|
+
syncServer,
|
|
10605
10699
|
deviceDescription = null,
|
|
10606
10700
|
hideKeys = false,
|
|
10607
10701
|
plugins: pluginsInit = {},
|
|
10608
10702
|
skipBlockHeight = false
|
|
10609
10703
|
} = opts;
|
|
10610
|
-
const infoServers =
|
|
10611
|
-
const syncServers =
|
|
10704
|
+
const infoServers = typeof infoServer === 'string' ? [infoServer] : infoServer != null && infoServer.length > 0 ? infoServer : ['https://info-eu1.edge.app', 'https://info-us1.edge.app'];
|
|
10705
|
+
const syncServers = typeof syncServer === 'string' ? [syncServer] : syncServer != null && syncServer.length > 0 ? 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'];
|
|
10612
10706
|
const logSettings = {
|
|
10613
10707
|
...defaultLogSettings,
|
|
10614
10708
|
...opts.logSettings
|
|
@@ -10629,9 +10723,9 @@ async function makeContext(ios, logBackend, opts) {
|
|
|
10629
10723
|
return state.ready ? state.logSettings : logSettings;
|
|
10630
10724
|
});
|
|
10631
10725
|
const log = makeLog(logBackend, 'edge-core');
|
|
10726
|
+
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)]);
|
|
10632
10727
|
|
|
10633
|
-
//
|
|
10634
|
-
let clientInfo = await clientFile.load(io.disklet, CLIENT_FILE_NAME);
|
|
10728
|
+
// Save the clientId if we don't have one:
|
|
10635
10729
|
if (clientInfo == null) {
|
|
10636
10730
|
clientInfo = {
|
|
10637
10731
|
clientId: io.random(16)
|
|
@@ -10640,13 +10734,13 @@ async function makeContext(ios, logBackend, opts) {
|
|
|
10640
10734
|
}
|
|
10641
10735
|
|
|
10642
10736
|
// Load the login stashes from disk:
|
|
10643
|
-
const stashes = await loadStashes(io.disklet, log);
|
|
10644
10737
|
redux$1.dispatch({
|
|
10645
10738
|
type: 'INIT',
|
|
10646
10739
|
payload: {
|
|
10647
10740
|
apiKey,
|
|
10648
10741
|
appId,
|
|
10649
10742
|
authServer,
|
|
10743
|
+
infoCache,
|
|
10650
10744
|
infoServers,
|
|
10651
10745
|
syncServers,
|
|
10652
10746
|
clientId: clientInfo.clientId,
|
|
@@ -10660,10 +10754,10 @@ async function makeContext(ios, logBackend, opts) {
|
|
|
10660
10754
|
});
|
|
10661
10755
|
|
|
10662
10756
|
// Subscribe to new plugins:
|
|
10663
|
-
watchPlugins(ios, logBackend, pluginsInit, redux$1.dispatch);
|
|
10757
|
+
watchPlugins(ios, infoCache, logBackend, pluginsInit, redux$1.dispatch);
|
|
10664
10758
|
|
|
10665
10759
|
// Create sync client:
|
|
10666
|
-
const syncClient =
|
|
10760
|
+
const syncClient = edgeSyncClient.makeSyncClient({
|
|
10667
10761
|
log,
|
|
10668
10762
|
fetch: io.fetch,
|
|
10669
10763
|
edgeServers: {
|
|
@@ -10720,9 +10814,9 @@ function closeEdge() {
|
|
|
10720
10814
|
*/
|
|
10721
10815
|
class FakeDb {
|
|
10722
10816
|
constructor() {
|
|
10723
|
-
this.lobbies =
|
|
10817
|
+
this.lobbies = new Map();
|
|
10724
10818
|
this.logins = [];
|
|
10725
|
-
this.repos =
|
|
10819
|
+
this.repos = new Map();
|
|
10726
10820
|
}
|
|
10727
10821
|
getLoginById(loginId) {
|
|
10728
10822
|
return this.logins.find(login => verifyData(login.loginId, loginId));
|
|
@@ -10757,7 +10851,7 @@ class FakeDb {
|
|
|
10757
10851
|
}
|
|
10758
10852
|
}
|
|
10759
10853
|
setupRepo(syncKey, repo) {
|
|
10760
|
-
this.repos
|
|
10854
|
+
this.repos.set(syncKey, repo);
|
|
10761
10855
|
}
|
|
10762
10856
|
dumpLogin(login) {
|
|
10763
10857
|
const makeTree = login => ({
|
|
@@ -11224,7 +11318,7 @@ function createLogin(request, login) {
|
|
|
11224
11318
|
keyBoxes: []
|
|
11225
11319
|
}))(body.data);
|
|
11226
11320
|
for (const syncKey of keys.newSyncKeys) {
|
|
11227
|
-
db.repos
|
|
11321
|
+
db.repos.set(syncKey, {});
|
|
11228
11322
|
}
|
|
11229
11323
|
|
|
11230
11324
|
// Start building the new database row:
|
|
@@ -11267,7 +11361,7 @@ const addKeysRoute = withLogin2(request => {
|
|
|
11267
11361
|
|
|
11268
11362
|
// Set up repos:
|
|
11269
11363
|
for (const syncKey of clean.newSyncKeys) {
|
|
11270
|
-
db.repos
|
|
11364
|
+
db.repos.set(syncKey, {});
|
|
11271
11365
|
}
|
|
11272
11366
|
login.keyBoxes = softCat(login.keyBoxes, clean.keyBoxes);
|
|
11273
11367
|
return statusResponse();
|
|
@@ -11526,7 +11620,7 @@ const withLobby = (server, fallback = handleMissingLobby) => request => {
|
|
|
11526
11620
|
path
|
|
11527
11621
|
} = request;
|
|
11528
11622
|
const lobbyId = path.split('/')[4];
|
|
11529
|
-
const lobby = db.lobbies
|
|
11623
|
+
const lobby = db.lobbies.get(lobbyId);
|
|
11530
11624
|
return lobby != null ? server({
|
|
11531
11625
|
...request,
|
|
11532
11626
|
lobby,
|
|
@@ -11550,11 +11644,11 @@ const createLobbyRoute = withLobby(request => statusResponse(statusCodes.account
|
|
|
11550
11644
|
timeout = 600
|
|
11551
11645
|
} = clean;
|
|
11552
11646
|
const expires = new Date(Date.now() + 1000 * timeout).toISOString();
|
|
11553
|
-
db.lobbies
|
|
11647
|
+
db.lobbies.set(lobbyId, {
|
|
11554
11648
|
request: clean,
|
|
11555
11649
|
replies: [],
|
|
11556
11650
|
expires
|
|
11557
|
-
};
|
|
11651
|
+
});
|
|
11558
11652
|
return statusResponse();
|
|
11559
11653
|
});
|
|
11560
11654
|
const updateLobbyRoute = withLobby(request => {
|
|
@@ -11580,7 +11674,7 @@ const deleteLobbyRoute = withLobby(request => {
|
|
|
11580
11674
|
db,
|
|
11581
11675
|
lobbyId
|
|
11582
11676
|
} = request;
|
|
11583
|
-
|
|
11677
|
+
db.lobbies.delete(lobbyId);
|
|
11584
11678
|
return statusResponse();
|
|
11585
11679
|
});
|
|
11586
11680
|
|
|
@@ -11623,7 +11717,7 @@ const withRepo = server => request => {
|
|
|
11623
11717
|
const syncKey = elements[4];
|
|
11624
11718
|
// const hash = elements[5]
|
|
11625
11719
|
|
|
11626
|
-
const repo = db.repos
|
|
11720
|
+
const repo = db.repos.get(syncKey);
|
|
11627
11721
|
if (repo == null) {
|
|
11628
11722
|
// This is not the auth server, so we have a different format:
|
|
11629
11723
|
return jsonResponse({
|
|
@@ -11867,7 +11961,9 @@ function makeFakeWorld(ios, logBackend, users) {
|
|
|
11867
11961
|
|
|
11868
11962
|
// Find the data on the server:
|
|
11869
11963
|
const login = fakeDb.getLoginById(loginId);
|
|
11870
|
-
if (login == null)
|
|
11964
|
+
if (login == null) {
|
|
11965
|
+
throw new Error(`Cannot find user ${account.rootLoginId}`);
|
|
11966
|
+
}
|
|
11871
11967
|
|
|
11872
11968
|
// Figure out which repos to use:
|
|
11873
11969
|
const syncKeys = [];
|
|
@@ -11878,7 +11974,8 @@ function makeFakeWorld(ios, logBackend, users) {
|
|
|
11878
11974
|
}
|
|
11879
11975
|
const repos = {};
|
|
11880
11976
|
for (const syncKey of syncKeys) {
|
|
11881
|
-
|
|
11977
|
+
const repo = fakeDb.repos.get(syncKey);
|
|
11978
|
+
if (repo != null) repos[syncKey] = wasEdgeRepoDump(repo);
|
|
11882
11979
|
}
|
|
11883
11980
|
return {
|
|
11884
11981
|
lastLogin: account.lastLogin,
|
package/lib/types/types.js
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compares two JSON-like objects, returning false if they differ.
|
|
3
|
+
*/
|
|
4
|
+
export function matchJson(a, b) {
|
|
5
|
+
// Use simple equality, unless a and b are proper objects:
|
|
6
|
+
if (
|
|
7
|
+
typeof a !== 'object' ||
|
|
8
|
+
typeof b !== 'object' ||
|
|
9
|
+
a == null ||
|
|
10
|
+
b == null
|
|
11
|
+
) {
|
|
12
|
+
return a === b
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// These must either be both arrays or both objects:
|
|
16
|
+
const aIsArray = Array.isArray(a)
|
|
17
|
+
const bIsArray = Array.isArray(b)
|
|
18
|
+
if (aIsArray !== bIsArray) return false
|
|
19
|
+
|
|
20
|
+
// Compare arrays in order:
|
|
21
|
+
if (aIsArray) {
|
|
22
|
+
if (a.length !== b.length) return false
|
|
23
|
+
for (let i = 0; i < a.length; ++i) {
|
|
24
|
+
if (!matchJson(a[i], b[i])) return false
|
|
25
|
+
}
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// These are both regular objects, so grab the keys,
|
|
30
|
+
// ignoring entries where the value is `undefined`:
|
|
31
|
+
const aKeys = Object.getOwnPropertyNames(a).filter(
|
|
32
|
+
key => a[key] !== undefined
|
|
33
|
+
)
|
|
34
|
+
const bKeys = Object.getOwnPropertyNames(b).filter(
|
|
35
|
+
key => b[key] !== undefined
|
|
36
|
+
)
|
|
37
|
+
if (aKeys.length !== bKeys.length) return false
|
|
38
|
+
|
|
39
|
+
// We know that both objects have the same number of properties,
|
|
40
|
+
// so if every property in `a` has a matching property in `b`,
|
|
41
|
+
// the objects must be identical, regardless of key order.
|
|
42
|
+
for (const key of aKeys) {
|
|
43
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) return false
|
|
44
|
+
if (!matchJson(a[key], b[key])) return false
|
|
45
|
+
}
|
|
46
|
+
return true
|
|
47
|
+
}
|
package/package.json
CHANGED
package/src/types/types.ts
CHANGED
|
@@ -163,9 +163,12 @@ export interface EdgeNativeIo {
|
|
|
163
163
|
* All core plugins receive these options at creation time.
|
|
164
164
|
*/
|
|
165
165
|
export interface EdgeCorePluginOptions {
|
|
166
|
-
|
|
166
|
+
/** Load-time options (like API keys) passed into the context */
|
|
167
167
|
initOptions: JsonObject
|
|
168
168
|
|
|
169
|
+
/** Data provided by the info server */
|
|
170
|
+
infoPayload: JsonObject
|
|
171
|
+
|
|
169
172
|
// Access to the world outside the plugin:
|
|
170
173
|
io: EdgeIo
|
|
171
174
|
log: EdgeLog // Plugin-scoped logging
|
|
@@ -1034,6 +1037,7 @@ export interface EdgeCurrencyPlugin {
|
|
|
1034
1037
|
walletInfo: EdgeWalletInfo,
|
|
1035
1038
|
opts: EdgeCurrencyEngineOptions
|
|
1036
1039
|
) => Promise<EdgeCurrencyEngine>
|
|
1040
|
+
readonly updateInfoPayload?: (infoPayload: JsonObject) => Promise<void>
|
|
1037
1041
|
|
|
1038
1042
|
// Escape hatch:
|
|
1039
1043
|
readonly otherMethods?: EdgeOtherMethods
|
|
@@ -1311,7 +1315,7 @@ export interface EdgeSwapPlugin {
|
|
|
1311
1315
|
readonly fetchSwapQuote: (
|
|
1312
1316
|
request: EdgeSwapRequest,
|
|
1313
1317
|
userSettings: JsonObject | undefined,
|
|
1314
|
-
opts: { promoCode?: string }
|
|
1318
|
+
opts: { infoPayload: JsonObject; promoCode?: string }
|
|
1315
1319
|
) => Promise<EdgeSwapQuote>
|
|
1316
1320
|
}
|
|
1317
1321
|
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { buildReducer } from 'redux-keto'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const defaultEdgeServers = {
|
|
15
|
-
infoServers: [],
|
|
16
|
-
syncServers: []
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const contextConfig = buildReducer
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
({
|
|
24
|
-
edgeServers(state = defaultEdgeServers, action) {
|
|
25
|
-
if (action.type === 'INIT') {
|
|
26
|
-
const { infoServers, syncServers } = action.payload
|
|
27
|
-
return { infoServers, syncServers }
|
|
28
|
-
}
|
|
29
|
-
return state
|
|
30
|
-
}
|
|
31
|
-
})
|