@1delta/data-sdk 0.0.55 → 0.0.57
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/dist/index.js +126 -28
- package/dist/index.mjs +123 -29
- package/dist/lending/dolomite.d.ts +25 -0
- package/dist/remoteCache.d.ts +82 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -298,6 +298,7 @@ function parseGearboxV3LenderKey(lenderKey) {
|
|
|
298
298
|
|
|
299
299
|
// src/lending/dolomite.ts
|
|
300
300
|
var dolomiteConfigs = () => getGlobalData2()?.dolomiteConfigs;
|
|
301
|
+
var dolomiteOdosExecutorShim = (chainId) => getGlobalData2()?.dolomiteConfigs?.[chainId]?.odosExecutor;
|
|
301
302
|
var dolomiteChains = () => Object.keys(getGlobalData2()?.dolomiteConfigs ?? {});
|
|
302
303
|
var dolomiteEmode = () => getGlobalData2()?.dolomiteEmode;
|
|
303
304
|
var dolomiteMarketIdByToken = (chainId, token) => {
|
|
@@ -1033,17 +1034,32 @@ var parseFraxlendPairKey = (lender) => {
|
|
|
1033
1034
|
var FRESH_TTL_MS = 5 * 60 * 1e3;
|
|
1034
1035
|
var STALE_WHILE_REVALIDATE_MS = 12 * 60 * 60 * 1e3;
|
|
1035
1036
|
var REMOTE_CACHE_STORE_TTL_S = 7 * 24 * 60 * 60;
|
|
1037
|
+
var STUCK_REVALIDATE_MS = 60 * 1e3;
|
|
1038
|
+
var MISSING_TTL_MS = FRESH_TTL_MS;
|
|
1036
1039
|
var STATE_KEY = "__1delta_remote_cache__";
|
|
1037
1040
|
function getState() {
|
|
1038
1041
|
const g = globalThis;
|
|
1039
|
-
if (!g[STATE_KEY])
|
|
1042
|
+
if (!g[STATE_KEY]) {
|
|
1043
|
+
g[STATE_KEY] = {
|
|
1044
|
+
memo: /* @__PURE__ */ new Map(),
|
|
1045
|
+
inflight: /* @__PURE__ */ new Map(),
|
|
1046
|
+
revalidating: /* @__PURE__ */ new Map(),
|
|
1047
|
+
missing: /* @__PURE__ */ new Map(),
|
|
1048
|
+
loaders: /* @__PURE__ */ new Map()
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1040
1051
|
const state = g[STATE_KEY];
|
|
1041
|
-
if (!state.revalidating) state.revalidating = /* @__PURE__ */ new
|
|
1052
|
+
if (!(state.revalidating instanceof Map)) state.revalidating = /* @__PURE__ */ new Map();
|
|
1053
|
+
if (!state.missing) state.missing = /* @__PURE__ */ new Map();
|
|
1054
|
+
if (!state.loaders) state.loaders = /* @__PURE__ */ new Map();
|
|
1042
1055
|
return state;
|
|
1043
1056
|
}
|
|
1044
1057
|
function setRemoteCacheStore(next) {
|
|
1045
1058
|
getState().store = next;
|
|
1046
1059
|
}
|
|
1060
|
+
function setRemoteCacheWaitUntil(next) {
|
|
1061
|
+
getState().waitUntil = next;
|
|
1062
|
+
}
|
|
1047
1063
|
function setRemoteCacheGithubToken(token) {
|
|
1048
1064
|
getState().githubToken = token;
|
|
1049
1065
|
}
|
|
@@ -1051,7 +1067,11 @@ function clearRemoteCache() {
|
|
|
1051
1067
|
const state = getState();
|
|
1052
1068
|
state.memo.clear();
|
|
1053
1069
|
state.inflight.clear();
|
|
1070
|
+
state.revalidating.clear();
|
|
1071
|
+
state.missing.clear();
|
|
1072
|
+
state.loaders.clear();
|
|
1054
1073
|
}
|
|
1074
|
+
var tokenListCacheKey = (chainId) => `token-list:${chainId}`;
|
|
1055
1075
|
var RAW_HOST = "https://raw.githubusercontent.com/";
|
|
1056
1076
|
function githubApiMirror(url) {
|
|
1057
1077
|
if (!url.startsWith(RAW_HOST)) return void 0;
|
|
@@ -1099,11 +1119,21 @@ async function readStore(key, isValid) {
|
|
|
1099
1119
|
const { store } = getState();
|
|
1100
1120
|
if (!store) return void 0;
|
|
1101
1121
|
try {
|
|
1102
|
-
|
|
1122
|
+
let raw;
|
|
1123
|
+
let metaAt;
|
|
1124
|
+
if (store.getWithMetadata) {
|
|
1125
|
+
const got = await store.getWithMetadata(storeKey(key));
|
|
1126
|
+
raw = got.value;
|
|
1127
|
+
if (typeof got.metadata?.at === "number") metaAt = got.metadata.at;
|
|
1128
|
+
} else {
|
|
1129
|
+
raw = await store.get(storeKey(key));
|
|
1130
|
+
}
|
|
1103
1131
|
if (!raw) return void 0;
|
|
1104
1132
|
const parsed = JSON.parse(raw);
|
|
1105
|
-
if (
|
|
1106
|
-
|
|
1133
|
+
if (!isValid(parsed?.value)) return void 0;
|
|
1134
|
+
const at = metaAt ?? parsed?.at;
|
|
1135
|
+
if (typeof at !== "number") return void 0;
|
|
1136
|
+
return { at, value: parsed.value };
|
|
1107
1137
|
} catch {
|
|
1108
1138
|
return void 0;
|
|
1109
1139
|
}
|
|
@@ -1112,50 +1142,106 @@ async function writeStore(key, entry) {
|
|
|
1112
1142
|
const { store } = getState();
|
|
1113
1143
|
if (!store) return;
|
|
1114
1144
|
try {
|
|
1115
|
-
|
|
1145
|
+
const body = JSON.stringify(entry);
|
|
1146
|
+
if (store.putWithMetadata) {
|
|
1147
|
+
await store.putWithMetadata(storeKey(key), body, REMOTE_CACHE_STORE_TTL_S, { at: entry.at });
|
|
1148
|
+
} else {
|
|
1149
|
+
await store.put(storeKey(key), body, REMOTE_CACHE_STORE_TTL_S);
|
|
1150
|
+
}
|
|
1116
1151
|
} catch {
|
|
1117
1152
|
}
|
|
1118
1153
|
}
|
|
1154
|
+
function background(promise) {
|
|
1155
|
+
const { waitUntil } = getState();
|
|
1156
|
+
if (!waitUntil) return;
|
|
1157
|
+
try {
|
|
1158
|
+
waitUntil(promise);
|
|
1159
|
+
} catch {
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
function adopt(key, entry) {
|
|
1163
|
+
const { memo } = getState();
|
|
1164
|
+
const current = memo.get(key);
|
|
1165
|
+
if (!current || current.at <= entry.at) memo.set(key, entry);
|
|
1166
|
+
}
|
|
1167
|
+
var negativeCacheError = (entry) => new RemoteFetchError(entry.message, entry.status);
|
|
1168
|
+
function revalidate(key, loader, preferStore) {
|
|
1169
|
+
const state = getState();
|
|
1170
|
+
const existing = state.revalidating.get(key);
|
|
1171
|
+
if (existing && Date.now() - existing.startedAt < STUCK_REVALIDATE_MS) return existing.promise;
|
|
1172
|
+
const entry = { startedAt: Date.now(), promise: Promise.resolve(false) };
|
|
1173
|
+
entry.promise = (async () => {
|
|
1174
|
+
try {
|
|
1175
|
+
const mine = state.memo.get(key);
|
|
1176
|
+
if (preferStore && state.store) {
|
|
1177
|
+
const stored = await readStore(key, loader.isValid);
|
|
1178
|
+
if (stored && stored.at > (mine?.at ?? 0) && Date.now() - stored.at < FRESH_TTL_MS) {
|
|
1179
|
+
adopt(key, stored);
|
|
1180
|
+
return true;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
const value = await loader.load();
|
|
1184
|
+
const fresh = { at: Date.now(), value };
|
|
1185
|
+
adopt(key, fresh);
|
|
1186
|
+
state.missing.delete(key);
|
|
1187
|
+
await writeStore(key, fresh);
|
|
1188
|
+
return true;
|
|
1189
|
+
} catch {
|
|
1190
|
+
return false;
|
|
1191
|
+
} finally {
|
|
1192
|
+
if (state.revalidating.get(key) === entry) state.revalidating.delete(key);
|
|
1193
|
+
}
|
|
1194
|
+
})();
|
|
1195
|
+
state.revalidating.set(key, entry);
|
|
1196
|
+
return entry.promise;
|
|
1197
|
+
}
|
|
1119
1198
|
async function loadCached(key, load, isValid) {
|
|
1120
|
-
const
|
|
1199
|
+
const state = getState();
|
|
1200
|
+
const { memo, inflight } = state;
|
|
1201
|
+
const loader = { load, isValid };
|
|
1202
|
+
state.loaders.set(key, loader);
|
|
1121
1203
|
const cached = memo.get(key);
|
|
1122
|
-
if (cached
|
|
1204
|
+
if (cached) {
|
|
1205
|
+
const age = Date.now() - cached.at;
|
|
1206
|
+
if (age < FRESH_TTL_MS) return cached.value;
|
|
1207
|
+
if (age < STALE_WHILE_REVALIDATE_MS) {
|
|
1208
|
+
background(revalidate(key, loader, true));
|
|
1209
|
+
return cached.value;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
const missing = state.missing.get(key);
|
|
1213
|
+
if (missing && Date.now() - missing.at < MISSING_TTL_MS) throw negativeCacheError(missing);
|
|
1123
1214
|
const pending = inflight.get(key);
|
|
1124
1215
|
if (pending) return pending;
|
|
1125
1216
|
const task = (async () => {
|
|
1126
1217
|
const stored = await readStore(key, isValid);
|
|
1127
1218
|
if (stored && Date.now() - stored.at < FRESH_TTL_MS) {
|
|
1128
|
-
|
|
1219
|
+
adopt(key, stored);
|
|
1129
1220
|
return stored.value;
|
|
1130
1221
|
}
|
|
1131
1222
|
if (stored && Date.now() - stored.at < STALE_WHILE_REVALIDATE_MS) {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
revalidating.add(key);
|
|
1135
|
-
void (async () => {
|
|
1136
|
-
try {
|
|
1137
|
-
const value = await load();
|
|
1138
|
-
const entry = { at: Date.now(), value };
|
|
1139
|
-
const current = memo.get(key);
|
|
1140
|
-
if (!current || current.at <= entry.at) memo.set(key, entry);
|
|
1141
|
-
await writeStore(key, entry);
|
|
1142
|
-
} catch {
|
|
1143
|
-
} finally {
|
|
1144
|
-
revalidating.delete(key);
|
|
1145
|
-
}
|
|
1146
|
-
})();
|
|
1223
|
+
adopt(key, stored);
|
|
1224
|
+
background(revalidate(key, loader, false));
|
|
1147
1225
|
return stored.value;
|
|
1148
1226
|
}
|
|
1149
1227
|
try {
|
|
1150
1228
|
const value = await load();
|
|
1151
1229
|
const entry = { at: Date.now(), value };
|
|
1152
|
-
|
|
1153
|
-
|
|
1230
|
+
adopt(key, entry);
|
|
1231
|
+
state.missing.delete(key);
|
|
1232
|
+
const put = writeStore(key, entry);
|
|
1233
|
+
if (state.waitUntil) background(put);
|
|
1234
|
+
else await put;
|
|
1154
1235
|
return value;
|
|
1155
1236
|
} catch (error) {
|
|
1156
1237
|
const stale = memo.get(key) ?? stored;
|
|
1157
|
-
if (!stale)
|
|
1158
|
-
|
|
1238
|
+
if (!stale) {
|
|
1239
|
+
if (error instanceof RemoteFetchError && error.isMissing) {
|
|
1240
|
+
state.missing.set(key, { at: Date.now(), status: error.status, message: error.message });
|
|
1241
|
+
}
|
|
1242
|
+
throw error;
|
|
1243
|
+
}
|
|
1244
|
+
adopt(key, stale);
|
|
1159
1245
|
return stale.value;
|
|
1160
1246
|
}
|
|
1161
1247
|
})().finally(() => {
|
|
@@ -1164,6 +1250,14 @@ async function loadCached(key, load, isValid) {
|
|
|
1164
1250
|
inflight.set(key, task);
|
|
1165
1251
|
return task;
|
|
1166
1252
|
}
|
|
1253
|
+
async function refreshRemoteCache(key, opts = {}) {
|
|
1254
|
+
const state = getState();
|
|
1255
|
+
const loader = state.loaders.get(key);
|
|
1256
|
+
if (!loader) return "unknown";
|
|
1257
|
+
const cached = state.memo.get(key);
|
|
1258
|
+
if (cached && opts.maxAgeMs !== void 0 && Date.now() - cached.at < opts.maxAgeMs) return "fresh";
|
|
1259
|
+
return await revalidate(key, loader, false) ? "refreshed" : "failed";
|
|
1260
|
+
}
|
|
1167
1261
|
async function loadCachedJson(key, url, isValid, label = key) {
|
|
1168
1262
|
return loadCached(key, () => fetchRemoteJson(url, label), isValid);
|
|
1169
1263
|
}
|
|
@@ -1256,6 +1350,7 @@ exports.dolomiteChains = dolomiteChains;
|
|
|
1256
1350
|
exports.dolomiteConfigs = dolomiteConfigs;
|
|
1257
1351
|
exports.dolomiteEmode = dolomiteEmode;
|
|
1258
1352
|
exports.dolomiteMarketIdByToken = dolomiteMarketIdByToken;
|
|
1353
|
+
exports.dolomiteOdosExecutorShim = dolomiteOdosExecutorShim;
|
|
1259
1354
|
exports.dssChainData = dssChainData;
|
|
1260
1355
|
exports.dssConfigFor = dssConfigFor;
|
|
1261
1356
|
exports.dssDebtJoin = dssDebtJoin;
|
|
@@ -1370,6 +1465,7 @@ exports.parseMidnightRef = parseMidnightRef;
|
|
|
1370
1465
|
exports.parseSiloV2LenderKey = parseSiloV2LenderKey;
|
|
1371
1466
|
exports.parseSiloV3LenderKey = parseSiloV3LenderKey;
|
|
1372
1467
|
exports.parseTermMaxLenderKey = parseTermMaxLenderKey;
|
|
1468
|
+
exports.refreshRemoteCache = refreshRemoteCache;
|
|
1373
1469
|
exports.resolveMidnightApiBase = resolveMidnightApiBase;
|
|
1374
1470
|
exports.resolveTenorApiUrl = resolveTenorApiUrl;
|
|
1375
1471
|
exports.resolveTermApiBase = resolveTermApiBase;
|
|
@@ -1386,6 +1482,7 @@ exports.riverMarkets = riverMarkets;
|
|
|
1386
1482
|
exports.setMidnightApiBase = setMidnightApiBase;
|
|
1387
1483
|
exports.setRemoteCacheGithubToken = setRemoteCacheGithubToken;
|
|
1388
1484
|
exports.setRemoteCacheStore = setRemoteCacheStore;
|
|
1485
|
+
exports.setRemoteCacheWaitUntil = setRemoteCacheWaitUntil;
|
|
1389
1486
|
exports.setTenorApiUrl = setTenorApiUrl;
|
|
1390
1487
|
exports.setTermApiBase = setTermApiBase;
|
|
1391
1488
|
exports.setTermOrderStoreBase = setTermOrderStoreBase;
|
|
@@ -1417,6 +1514,7 @@ exports.termMaxConfig = termMaxConfig;
|
|
|
1417
1514
|
exports.termMaxConfigByChain = termMaxConfigByChain;
|
|
1418
1515
|
exports.termMaxLenderKey = termMaxLenderKey;
|
|
1419
1516
|
exports.termOrderStoreBaseOverride = termOrderStoreBaseOverride;
|
|
1517
|
+
exports.tokenListCacheKey = tokenListCacheKey;
|
|
1420
1518
|
exports.twyneBaseKey = twyneBaseKey;
|
|
1421
1519
|
exports.twyneBeaconFor = twyneBeaconFor;
|
|
1422
1520
|
exports.twyneChainData = twyneChainData;
|
package/dist/index.mjs
CHANGED
|
@@ -296,6 +296,7 @@ function parseGearboxV3LenderKey(lenderKey) {
|
|
|
296
296
|
|
|
297
297
|
// src/lending/dolomite.ts
|
|
298
298
|
var dolomiteConfigs = () => getGlobalData2()?.dolomiteConfigs;
|
|
299
|
+
var dolomiteOdosExecutorShim = (chainId) => getGlobalData2()?.dolomiteConfigs?.[chainId]?.odosExecutor;
|
|
299
300
|
var dolomiteChains = () => Object.keys(getGlobalData2()?.dolomiteConfigs ?? {});
|
|
300
301
|
var dolomiteEmode = () => getGlobalData2()?.dolomiteEmode;
|
|
301
302
|
var dolomiteMarketIdByToken = (chainId, token) => {
|
|
@@ -1031,17 +1032,32 @@ var parseFraxlendPairKey = (lender) => {
|
|
|
1031
1032
|
var FRESH_TTL_MS = 5 * 60 * 1e3;
|
|
1032
1033
|
var STALE_WHILE_REVALIDATE_MS = 12 * 60 * 60 * 1e3;
|
|
1033
1034
|
var REMOTE_CACHE_STORE_TTL_S = 7 * 24 * 60 * 60;
|
|
1035
|
+
var STUCK_REVALIDATE_MS = 60 * 1e3;
|
|
1036
|
+
var MISSING_TTL_MS = FRESH_TTL_MS;
|
|
1034
1037
|
var STATE_KEY = "__1delta_remote_cache__";
|
|
1035
1038
|
function getState() {
|
|
1036
1039
|
const g = globalThis;
|
|
1037
|
-
if (!g[STATE_KEY])
|
|
1040
|
+
if (!g[STATE_KEY]) {
|
|
1041
|
+
g[STATE_KEY] = {
|
|
1042
|
+
memo: /* @__PURE__ */ new Map(),
|
|
1043
|
+
inflight: /* @__PURE__ */ new Map(),
|
|
1044
|
+
revalidating: /* @__PURE__ */ new Map(),
|
|
1045
|
+
missing: /* @__PURE__ */ new Map(),
|
|
1046
|
+
loaders: /* @__PURE__ */ new Map()
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1038
1049
|
const state = g[STATE_KEY];
|
|
1039
|
-
if (!state.revalidating) state.revalidating = /* @__PURE__ */ new
|
|
1050
|
+
if (!(state.revalidating instanceof Map)) state.revalidating = /* @__PURE__ */ new Map();
|
|
1051
|
+
if (!state.missing) state.missing = /* @__PURE__ */ new Map();
|
|
1052
|
+
if (!state.loaders) state.loaders = /* @__PURE__ */ new Map();
|
|
1040
1053
|
return state;
|
|
1041
1054
|
}
|
|
1042
1055
|
function setRemoteCacheStore(next) {
|
|
1043
1056
|
getState().store = next;
|
|
1044
1057
|
}
|
|
1058
|
+
function setRemoteCacheWaitUntil(next) {
|
|
1059
|
+
getState().waitUntil = next;
|
|
1060
|
+
}
|
|
1045
1061
|
function setRemoteCacheGithubToken(token) {
|
|
1046
1062
|
getState().githubToken = token;
|
|
1047
1063
|
}
|
|
@@ -1049,7 +1065,11 @@ function clearRemoteCache() {
|
|
|
1049
1065
|
const state = getState();
|
|
1050
1066
|
state.memo.clear();
|
|
1051
1067
|
state.inflight.clear();
|
|
1068
|
+
state.revalidating.clear();
|
|
1069
|
+
state.missing.clear();
|
|
1070
|
+
state.loaders.clear();
|
|
1052
1071
|
}
|
|
1072
|
+
var tokenListCacheKey = (chainId) => `token-list:${chainId}`;
|
|
1053
1073
|
var RAW_HOST = "https://raw.githubusercontent.com/";
|
|
1054
1074
|
function githubApiMirror(url) {
|
|
1055
1075
|
if (!url.startsWith(RAW_HOST)) return void 0;
|
|
@@ -1097,11 +1117,21 @@ async function readStore(key, isValid) {
|
|
|
1097
1117
|
const { store } = getState();
|
|
1098
1118
|
if (!store) return void 0;
|
|
1099
1119
|
try {
|
|
1100
|
-
|
|
1120
|
+
let raw;
|
|
1121
|
+
let metaAt;
|
|
1122
|
+
if (store.getWithMetadata) {
|
|
1123
|
+
const got = await store.getWithMetadata(storeKey(key));
|
|
1124
|
+
raw = got.value;
|
|
1125
|
+
if (typeof got.metadata?.at === "number") metaAt = got.metadata.at;
|
|
1126
|
+
} else {
|
|
1127
|
+
raw = await store.get(storeKey(key));
|
|
1128
|
+
}
|
|
1101
1129
|
if (!raw) return void 0;
|
|
1102
1130
|
const parsed = JSON.parse(raw);
|
|
1103
|
-
if (
|
|
1104
|
-
|
|
1131
|
+
if (!isValid(parsed?.value)) return void 0;
|
|
1132
|
+
const at = metaAt ?? parsed?.at;
|
|
1133
|
+
if (typeof at !== "number") return void 0;
|
|
1134
|
+
return { at, value: parsed.value };
|
|
1105
1135
|
} catch {
|
|
1106
1136
|
return void 0;
|
|
1107
1137
|
}
|
|
@@ -1110,50 +1140,106 @@ async function writeStore(key, entry) {
|
|
|
1110
1140
|
const { store } = getState();
|
|
1111
1141
|
if (!store) return;
|
|
1112
1142
|
try {
|
|
1113
|
-
|
|
1143
|
+
const body = JSON.stringify(entry);
|
|
1144
|
+
if (store.putWithMetadata) {
|
|
1145
|
+
await store.putWithMetadata(storeKey(key), body, REMOTE_CACHE_STORE_TTL_S, { at: entry.at });
|
|
1146
|
+
} else {
|
|
1147
|
+
await store.put(storeKey(key), body, REMOTE_CACHE_STORE_TTL_S);
|
|
1148
|
+
}
|
|
1114
1149
|
} catch {
|
|
1115
1150
|
}
|
|
1116
1151
|
}
|
|
1152
|
+
function background(promise) {
|
|
1153
|
+
const { waitUntil } = getState();
|
|
1154
|
+
if (!waitUntil) return;
|
|
1155
|
+
try {
|
|
1156
|
+
waitUntil(promise);
|
|
1157
|
+
} catch {
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
function adopt(key, entry) {
|
|
1161
|
+
const { memo } = getState();
|
|
1162
|
+
const current = memo.get(key);
|
|
1163
|
+
if (!current || current.at <= entry.at) memo.set(key, entry);
|
|
1164
|
+
}
|
|
1165
|
+
var negativeCacheError = (entry) => new RemoteFetchError(entry.message, entry.status);
|
|
1166
|
+
function revalidate(key, loader, preferStore) {
|
|
1167
|
+
const state = getState();
|
|
1168
|
+
const existing = state.revalidating.get(key);
|
|
1169
|
+
if (existing && Date.now() - existing.startedAt < STUCK_REVALIDATE_MS) return existing.promise;
|
|
1170
|
+
const entry = { startedAt: Date.now(), promise: Promise.resolve(false) };
|
|
1171
|
+
entry.promise = (async () => {
|
|
1172
|
+
try {
|
|
1173
|
+
const mine = state.memo.get(key);
|
|
1174
|
+
if (preferStore && state.store) {
|
|
1175
|
+
const stored = await readStore(key, loader.isValid);
|
|
1176
|
+
if (stored && stored.at > (mine?.at ?? 0) && Date.now() - stored.at < FRESH_TTL_MS) {
|
|
1177
|
+
adopt(key, stored);
|
|
1178
|
+
return true;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
const value = await loader.load();
|
|
1182
|
+
const fresh = { at: Date.now(), value };
|
|
1183
|
+
adopt(key, fresh);
|
|
1184
|
+
state.missing.delete(key);
|
|
1185
|
+
await writeStore(key, fresh);
|
|
1186
|
+
return true;
|
|
1187
|
+
} catch {
|
|
1188
|
+
return false;
|
|
1189
|
+
} finally {
|
|
1190
|
+
if (state.revalidating.get(key) === entry) state.revalidating.delete(key);
|
|
1191
|
+
}
|
|
1192
|
+
})();
|
|
1193
|
+
state.revalidating.set(key, entry);
|
|
1194
|
+
return entry.promise;
|
|
1195
|
+
}
|
|
1117
1196
|
async function loadCached(key, load, isValid) {
|
|
1118
|
-
const
|
|
1197
|
+
const state = getState();
|
|
1198
|
+
const { memo, inflight } = state;
|
|
1199
|
+
const loader = { load, isValid };
|
|
1200
|
+
state.loaders.set(key, loader);
|
|
1119
1201
|
const cached = memo.get(key);
|
|
1120
|
-
if (cached
|
|
1202
|
+
if (cached) {
|
|
1203
|
+
const age = Date.now() - cached.at;
|
|
1204
|
+
if (age < FRESH_TTL_MS) return cached.value;
|
|
1205
|
+
if (age < STALE_WHILE_REVALIDATE_MS) {
|
|
1206
|
+
background(revalidate(key, loader, true));
|
|
1207
|
+
return cached.value;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
const missing = state.missing.get(key);
|
|
1211
|
+
if (missing && Date.now() - missing.at < MISSING_TTL_MS) throw negativeCacheError(missing);
|
|
1121
1212
|
const pending = inflight.get(key);
|
|
1122
1213
|
if (pending) return pending;
|
|
1123
1214
|
const task = (async () => {
|
|
1124
1215
|
const stored = await readStore(key, isValid);
|
|
1125
1216
|
if (stored && Date.now() - stored.at < FRESH_TTL_MS) {
|
|
1126
|
-
|
|
1217
|
+
adopt(key, stored);
|
|
1127
1218
|
return stored.value;
|
|
1128
1219
|
}
|
|
1129
1220
|
if (stored && Date.now() - stored.at < STALE_WHILE_REVALIDATE_MS) {
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
revalidating.add(key);
|
|
1133
|
-
void (async () => {
|
|
1134
|
-
try {
|
|
1135
|
-
const value = await load();
|
|
1136
|
-
const entry = { at: Date.now(), value };
|
|
1137
|
-
const current = memo.get(key);
|
|
1138
|
-
if (!current || current.at <= entry.at) memo.set(key, entry);
|
|
1139
|
-
await writeStore(key, entry);
|
|
1140
|
-
} catch {
|
|
1141
|
-
} finally {
|
|
1142
|
-
revalidating.delete(key);
|
|
1143
|
-
}
|
|
1144
|
-
})();
|
|
1221
|
+
adopt(key, stored);
|
|
1222
|
+
background(revalidate(key, loader, false));
|
|
1145
1223
|
return stored.value;
|
|
1146
1224
|
}
|
|
1147
1225
|
try {
|
|
1148
1226
|
const value = await load();
|
|
1149
1227
|
const entry = { at: Date.now(), value };
|
|
1150
|
-
|
|
1151
|
-
|
|
1228
|
+
adopt(key, entry);
|
|
1229
|
+
state.missing.delete(key);
|
|
1230
|
+
const put = writeStore(key, entry);
|
|
1231
|
+
if (state.waitUntil) background(put);
|
|
1232
|
+
else await put;
|
|
1152
1233
|
return value;
|
|
1153
1234
|
} catch (error) {
|
|
1154
1235
|
const stale = memo.get(key) ?? stored;
|
|
1155
|
-
if (!stale)
|
|
1156
|
-
|
|
1236
|
+
if (!stale) {
|
|
1237
|
+
if (error instanceof RemoteFetchError && error.isMissing) {
|
|
1238
|
+
state.missing.set(key, { at: Date.now(), status: error.status, message: error.message });
|
|
1239
|
+
}
|
|
1240
|
+
throw error;
|
|
1241
|
+
}
|
|
1242
|
+
adopt(key, stale);
|
|
1157
1243
|
return stale.value;
|
|
1158
1244
|
}
|
|
1159
1245
|
})().finally(() => {
|
|
@@ -1162,6 +1248,14 @@ async function loadCached(key, load, isValid) {
|
|
|
1162
1248
|
inflight.set(key, task);
|
|
1163
1249
|
return task;
|
|
1164
1250
|
}
|
|
1251
|
+
async function refreshRemoteCache(key, opts = {}) {
|
|
1252
|
+
const state = getState();
|
|
1253
|
+
const loader = state.loaders.get(key);
|
|
1254
|
+
if (!loader) return "unknown";
|
|
1255
|
+
const cached = state.memo.get(key);
|
|
1256
|
+
if (cached && opts.maxAgeMs !== void 0 && Date.now() - cached.at < opts.maxAgeMs) return "fresh";
|
|
1257
|
+
return await revalidate(key, loader, false) ? "refreshed" : "failed";
|
|
1258
|
+
}
|
|
1165
1259
|
async function loadCachedJson(key, url, isValid, label = key) {
|
|
1166
1260
|
return loadCached(key, () => fetchRemoteJson(url, label), isValid);
|
|
1167
1261
|
}
|
|
@@ -1210,4 +1304,4 @@ async function fetchMainTokensMap() {
|
|
|
1210
1304
|
}
|
|
1211
1305
|
}
|
|
1212
1306
|
|
|
1213
|
-
export { COOLER_ETHEREUM_CONFIG, CURVANCE_MONAD_CONFIG, FRAXLEND_ETHEREUM_CONFIG, GEARBOX_ACCOUNT_COMPRESSOR_V310, REMOTE_CACHE_STORE_TTL_S, RESUPPLY_ETHEREUM_CONFIG, RemoteFetchError, aaveOracles, aaveOraclesConfig, aavePools, aaveReserves, aaveTokens, aaveV4Oracles, aaveV4Peripherals, aaveV4SpokeLenderKey, aaveV4Spokes, aaveWethGateway, assertTwyneKeysUnique, chainLogoURI, chains, clearRemoteCache, compoundV2Oracles, compoundV2OraclesConfig, compoundV2Pools, compoundV2Reserves, compoundV2TokenArray, compoundV2Tokens, compoundV3BaseData, compoundV3Bulker, compoundV3OraclesData, compoundV3Pools, compoundV3Reserves, coolerConfig, coolerConfigFor, coolerLendersByChain, curvanceConfig, curvanceConfigFor, curvanceLendersByChain, curvancePositionManager, curvancePositionManagersFor, dolomiteChains, dolomiteConfigs, dolomiteEmode, dolomiteMarketIdByToken, dssChainData, dssConfigFor, dssDebtJoin, dssDebtToken, dssMarketByIlk, eulerConfigs, eulerVaults, exactlyConfig, exactlyMarketByAddress, exactlyMarketByAsset, exactlyMarkets, exactlyMarketsByChain, fetchMainTokensMap, fetchRemoteJson, fetchTokenList, fetchTokenListFile, fetchTokenLists, findAaveV4PositionManager, fluidChains, fluidResolvers, fluidVaultEntries, fluidVaults, flyingTulipAssets, flyingTulipAssetsFor, flyingTulipConfig, flyingTulipConfigFor, flyingTulipLendersByChain, frankencoinChainData, frankencoinConfig, frankencoinConfigFor, frankencoinLendersByChain, frankencoinMarketByPosition, frankencoinMarkets, fraxlendConfig, fraxlendConfigFor, fraxlendLendersByChain, fraxlendPairKey, fraxlendPairsFor, gearboxAccountCompressor, gearboxAddressProviderV310, gearboxBotListV3, gearboxChains, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxResolvers, gearboxV3LenderKey, getAaveV4GatewayAddresses, getAaveV4PositionManagers, getAaveV4SpokeAccess, getAaveV4SpokeEntry, getAaveV4SpokePeripherals, getChainEntries, getSiloV2MarketEntry, getSiloV3MarketEntry, initConfig, initializeChainData, initializeLenderData, inverseChainData, inverseConfig, inverseConfigFor, inverseLendersByChain, inverseMarketByAddress, inverseMarkets, isAaveV4OperationRestricted, isListaSmartLpAsset, isMidnightCollateralRef, liquityBranchByIndex, liquityBranchByTroveManager, liquityBranchesByChain, liquityConfig, liquityConfigFor, liquityLendersByChain, liquityMarkets, listaCollateralProvider, listaCollateralProviders, listaNativeProvider, listaSmartLpByCollateral, llamaLendApiBaseUrl, llamaLendChainData, llamaLendConfig, llamaLendConfigFor, llamaLendLenderKey, llamaLendLendersByChain, llamaLendLeverageZap, llamaLendMarketByController, llamaLendMarketByVault, llamaLendMarkets, llamaLendSupportsDelegation, llamaLendSupportsLeverage, loadCached, loadCachedJson, midnightApiBaseOverride, midnightCollateralRef, midnightConfig, midnightLoanRef, midnightMarketById, midnightMarketIsEnterable, midnightMarkets, midnightMarketsByChain, midnightNotEnterableReason, morphoBundler3, morphoOracles, morphoPools, morphoTypeMarkets, morphoTypeOracles, morphoTypeVaults, parseAaveV4SpokeLenderKey, parseFraxlendPairKey, parseGearboxV3LenderKey, parseLlamaLendLenderKey, parseMidnightRef, parseSiloV2LenderKey, parseSiloV3LenderKey, parseTermMaxLenderKey, resolveMidnightApiBase, resolveTenorApiUrl, resolveTermApiBase, resolveTermOrderStoreBase, resupplyConfig, resupplyConfigFor, resupplyLendersByChain, riverChainData, riverConfig, riverConfigFor, riverLendersByChain, riverMarketByIndex, riverMarkets, setMidnightApiBase, setRemoteCacheGithubToken, setRemoteCacheStore, setTenorApiUrl, setTermApiBase, setTermOrderStoreBase, siloMarkets, siloMarketsV3, siloPeripherals, siloPeripheralsV3, siloV2LenderKey, siloV3LenderKey, skyChainData, skyConfig, skyConfigFor, skyLendersByChain, skyMarkets, tellerConfig, tellerConfigByChain, tellerPoolByAddress, tellerPools, tellerPoolsByChain, tenorApiUrlOverride, termApiBaseOverride, termConfig, termMarketById, termMarkets, termMarketsByChain, termMaxApiBaseUrl, termMaxChainIds, termMaxConfig, termMaxConfigByChain, termMaxLenderKey, termOrderStoreBaseOverride, twyneBaseKey, twyneBeaconFor, twyneChainData, twyneConfig, twyneConfigFor, twyneLendersByChain, twyneMarketByKey, twyneMarketFor, twyneMarketKey, twyneMarkets, twyneMarketsForIntermediateVault, twyneOperator, usddChainData, usddConfig, usddConfigFor, usddLendersByChain, usddMarketByIlk, usddMarkets };
|
|
1307
|
+
export { COOLER_ETHEREUM_CONFIG, CURVANCE_MONAD_CONFIG, FRAXLEND_ETHEREUM_CONFIG, GEARBOX_ACCOUNT_COMPRESSOR_V310, REMOTE_CACHE_STORE_TTL_S, RESUPPLY_ETHEREUM_CONFIG, RemoteFetchError, aaveOracles, aaveOraclesConfig, aavePools, aaveReserves, aaveTokens, aaveV4Oracles, aaveV4Peripherals, aaveV4SpokeLenderKey, aaveV4Spokes, aaveWethGateway, assertTwyneKeysUnique, chainLogoURI, chains, clearRemoteCache, compoundV2Oracles, compoundV2OraclesConfig, compoundV2Pools, compoundV2Reserves, compoundV2TokenArray, compoundV2Tokens, compoundV3BaseData, compoundV3Bulker, compoundV3OraclesData, compoundV3Pools, compoundV3Reserves, coolerConfig, coolerConfigFor, coolerLendersByChain, curvanceConfig, curvanceConfigFor, curvanceLendersByChain, curvancePositionManager, curvancePositionManagersFor, dolomiteChains, dolomiteConfigs, dolomiteEmode, dolomiteMarketIdByToken, dolomiteOdosExecutorShim, dssChainData, dssConfigFor, dssDebtJoin, dssDebtToken, dssMarketByIlk, eulerConfigs, eulerVaults, exactlyConfig, exactlyMarketByAddress, exactlyMarketByAsset, exactlyMarkets, exactlyMarketsByChain, fetchMainTokensMap, fetchRemoteJson, fetchTokenList, fetchTokenListFile, fetchTokenLists, findAaveV4PositionManager, fluidChains, fluidResolvers, fluidVaultEntries, fluidVaults, flyingTulipAssets, flyingTulipAssetsFor, flyingTulipConfig, flyingTulipConfigFor, flyingTulipLendersByChain, frankencoinChainData, frankencoinConfig, frankencoinConfigFor, frankencoinLendersByChain, frankencoinMarketByPosition, frankencoinMarkets, fraxlendConfig, fraxlendConfigFor, fraxlendLendersByChain, fraxlendPairKey, fraxlendPairsFor, gearboxAccountCompressor, gearboxAddressProviderV310, gearboxBotListV3, gearboxChains, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxResolvers, gearboxV3LenderKey, getAaveV4GatewayAddresses, getAaveV4PositionManagers, getAaveV4SpokeAccess, getAaveV4SpokeEntry, getAaveV4SpokePeripherals, getChainEntries, getSiloV2MarketEntry, getSiloV3MarketEntry, initConfig, initializeChainData, initializeLenderData, inverseChainData, inverseConfig, inverseConfigFor, inverseLendersByChain, inverseMarketByAddress, inverseMarkets, isAaveV4OperationRestricted, isListaSmartLpAsset, isMidnightCollateralRef, liquityBranchByIndex, liquityBranchByTroveManager, liquityBranchesByChain, liquityConfig, liquityConfigFor, liquityLendersByChain, liquityMarkets, listaCollateralProvider, listaCollateralProviders, listaNativeProvider, listaSmartLpByCollateral, llamaLendApiBaseUrl, llamaLendChainData, llamaLendConfig, llamaLendConfigFor, llamaLendLenderKey, llamaLendLendersByChain, llamaLendLeverageZap, llamaLendMarketByController, llamaLendMarketByVault, llamaLendMarkets, llamaLendSupportsDelegation, llamaLendSupportsLeverage, loadCached, loadCachedJson, midnightApiBaseOverride, midnightCollateralRef, midnightConfig, midnightLoanRef, midnightMarketById, midnightMarketIsEnterable, midnightMarkets, midnightMarketsByChain, midnightNotEnterableReason, morphoBundler3, morphoOracles, morphoPools, morphoTypeMarkets, morphoTypeOracles, morphoTypeVaults, parseAaveV4SpokeLenderKey, parseFraxlendPairKey, parseGearboxV3LenderKey, parseLlamaLendLenderKey, parseMidnightRef, parseSiloV2LenderKey, parseSiloV3LenderKey, parseTermMaxLenderKey, refreshRemoteCache, resolveMidnightApiBase, resolveTenorApiUrl, resolveTermApiBase, resolveTermOrderStoreBase, resupplyConfig, resupplyConfigFor, resupplyLendersByChain, riverChainData, riverConfig, riverConfigFor, riverLendersByChain, riverMarketByIndex, riverMarkets, setMidnightApiBase, setRemoteCacheGithubToken, setRemoteCacheStore, setRemoteCacheWaitUntil, setTenorApiUrl, setTermApiBase, setTermOrderStoreBase, siloMarkets, siloMarketsV3, siloPeripherals, siloPeripheralsV3, siloV2LenderKey, siloV3LenderKey, skyChainData, skyConfig, skyConfigFor, skyLendersByChain, skyMarkets, tellerConfig, tellerConfigByChain, tellerPoolByAddress, tellerPools, tellerPoolsByChain, tenorApiUrlOverride, termApiBaseOverride, termConfig, termMarketById, termMarkets, termMarketsByChain, termMaxApiBaseUrl, termMaxChainIds, termMaxConfig, termMaxConfigByChain, termMaxLenderKey, termOrderStoreBaseOverride, tokenListCacheKey, twyneBaseKey, twyneBeaconFor, twyneChainData, twyneConfig, twyneConfigFor, twyneLendersByChain, twyneMarketByKey, twyneMarketFor, twyneMarketKey, twyneMarkets, twyneMarketsForIntermediateVault, twyneOperator, usddChainData, usddConfig, usddConfigFor, usddLendersByChain, usddMarketByIlk, usddMarkets };
|
|
@@ -17,6 +17,25 @@ export type DolomiteConfigEntry = {
|
|
|
17
17
|
aggregatorTraders?: {
|
|
18
18
|
[K in DolomiteAggregator]?: string;
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Our `OdosExecutor` (`contracts-delegation`,
|
|
22
|
+
* `contracts/1delta/composer/generic/OdosExecutor.sol`) — a contract carrying
|
|
23
|
+
* the Odos executor selector (`executePath(bytes,uint256[],address)`) that
|
|
24
|
+
* simply forwards to another aggregator's router. Sibling of the same repo's
|
|
25
|
+
* `AggregationExecutor` (1inch) and `AllowanceHolderExecutor` (0x).
|
|
26
|
+
*
|
|
27
|
+
* It exists because Dolomite swaps must route through a Dolomite-DEPLOYED
|
|
28
|
+
* `IExchangeWrapper` and we cannot add one, so a chain is limited to the
|
|
29
|
+
* wrappers Dolomite happens to have deployed. `OdosRouterV2` takes a
|
|
30
|
+
* caller-supplied `executor` and `pathDefinition` and validates NEITHER (its
|
|
31
|
+
* only mapping is `referralLookup`), and `OdosAggregatorTrader` forwards both
|
|
32
|
+
* verbatim — so pointing `executor` at this shim runs a KyberSwap/0x/1inch
|
|
33
|
+
* route through the `odos` wrapper without Odos's API being involved.
|
|
34
|
+
*
|
|
35
|
+
* Absent = the shim is not deployed on this chain and the passthrough route
|
|
36
|
+
* must not be offered (fail closed).
|
|
37
|
+
*/
|
|
38
|
+
odosExecutor?: string;
|
|
20
39
|
};
|
|
21
40
|
export type DolomiteConfigsType = {
|
|
22
41
|
[chainId: string]: DolomiteConfigEntry;
|
|
@@ -49,6 +68,12 @@ export type DolomiteEmodeType = {
|
|
|
49
68
|
[chainId: string]: DolomiteEmodeChain;
|
|
50
69
|
};
|
|
51
70
|
export declare const dolomiteConfigs: () => DolomiteConfigsType;
|
|
71
|
+
/**
|
|
72
|
+
* The `OdosExecutor` deployed on a chain, or `undefined` when the
|
|
73
|
+
* passthrough route is unavailable there. Callers MUST treat `undefined` as
|
|
74
|
+
* "not supported" — never substitute a default address.
|
|
75
|
+
*/
|
|
76
|
+
export declare const dolomiteOdosExecutorShim: (chainId: string) => string | undefined;
|
|
52
77
|
export declare const dolomiteChains: () => string[];
|
|
53
78
|
export declare const dolomiteEmode: () => DolomiteEmodeType;
|
|
54
79
|
export declare const dolomiteMarketIdByToken: (chainId: string, token: string) => string | undefined;
|
package/dist/remoteCache.d.ts
CHANGED
|
@@ -11,21 +11,70 @@
|
|
|
11
11
|
* These files change on the order of days, so serving a stale copy is strictly
|
|
12
12
|
* better than failing — hence the stale-on-error fallback, which is the part
|
|
13
13
|
* that actually survives an outage.
|
|
14
|
+
*
|
|
15
|
+
* Read order, and what each layer is for:
|
|
16
|
+
*
|
|
17
|
+
* memory — served IMMEDIATELY whenever a copy exists and is inside the SWR
|
|
18
|
+
* window; older than `FRESH_TTL_MS` it also kicks a background
|
|
19
|
+
* refresh. The request path never awaits the store or the network
|
|
20
|
+
* while memory is warm — a 6.66 MB token list re-read from KV every
|
|
21
|
+
* five minutes used to land on whichever user request crossed the
|
|
22
|
+
* boundary.
|
|
23
|
+
* store — a KV namespace in the workers. Consulted only when memory has
|
|
24
|
+
* nothing usable (a cold isolate), so a fresh isolate never touches
|
|
25
|
+
* GitHub at all.
|
|
26
|
+
* network — raw.githubusercontent.com, then the api.github.com mirror.
|
|
14
27
|
*/
|
|
15
28
|
/** How long a persisted copy is kept around purely as the stale fallback. */
|
|
16
29
|
export declare const REMOTE_CACHE_STORE_TTL_S: number;
|
|
30
|
+
/** What the store carries next to a value, when it can carry anything. */
|
|
31
|
+
export interface RemoteCacheEntryMetadata {
|
|
32
|
+
/** Epoch ms of the upstream fetch that produced the value. */
|
|
33
|
+
at: number;
|
|
34
|
+
}
|
|
17
35
|
/**
|
|
18
36
|
* Durable backing store — a Cloudflare KV namespace in the workers, absent
|
|
19
37
|
* everywhere else. Without one the cache is per-process, which still collapses
|
|
20
38
|
* N requests into one upstream fetch but cannot help a cold start; with one, a
|
|
21
39
|
* fresh isolate never touches GitHub at all.
|
|
40
|
+
*
|
|
41
|
+
* The two `*WithMetadata` members are optional. When present, the fetch
|
|
42
|
+
* timestamp is written as store METADATA as well as inside the value, so a
|
|
43
|
+
* store that can list keys with their metadata (KV's `list()` can) can answer
|
|
44
|
+
* "how old is this entry?" without transferring a multi-MB value. Note that
|
|
45
|
+
* KV's `getWithMetadata` still returns the value — only `list` is
|
|
46
|
+
* metadata-only — so reads through this interface do not get cheaper; what
|
|
47
|
+
* the metadata buys is an inspectable freshness signal for operators and for
|
|
48
|
+
* a future probe that stays off the value read.
|
|
22
49
|
*/
|
|
23
50
|
export interface RemoteCacheStore {
|
|
24
51
|
get(key: string): Promise<string | null>;
|
|
25
52
|
put(key: string, value: string, ttlSeconds: number): Promise<void>;
|
|
53
|
+
getWithMetadata?(key: string): Promise<{
|
|
54
|
+
value: string | null;
|
|
55
|
+
metadata: RemoteCacheEntryMetadata | null;
|
|
56
|
+
}>;
|
|
57
|
+
putWithMetadata?(key: string, value: string, ttlSeconds: number, metadata: RemoteCacheEntryMetadata): Promise<void>;
|
|
26
58
|
}
|
|
59
|
+
/** `ctx.waitUntil`-shaped hook: keeps a background promise alive post-response. */
|
|
60
|
+
export type RemoteCacheWaitUntil = (promise: Promise<unknown>) => void;
|
|
27
61
|
/** Installs (or with `undefined`, removes) the durable store. */
|
|
28
62
|
export declare function setRemoteCacheStore(next: RemoteCacheStore | undefined): void;
|
|
63
|
+
/**
|
|
64
|
+
* Installs (or with `undefined`, removes) the hook that keeps background work
|
|
65
|
+
* alive after a response — `ctx.waitUntil` on Cloudflare Workers.
|
|
66
|
+
*
|
|
67
|
+
* The LATEST hook wins. Workers hand each request its own context, and a
|
|
68
|
+
* module-level slot can only hold one, so a refresh kicked by request A may be
|
|
69
|
+
* registered against request B's context if B arrived in between. That is
|
|
70
|
+
* harmless in the direction that matters (B's context keeps A's refresh alive
|
|
71
|
+
* at least as long as B runs) and the stuck guard covers the other direction
|
|
72
|
+
* (a context that closed before the refresh settled). The hook is called under
|
|
73
|
+
* try/catch for the same reason: a closed context may throw.
|
|
74
|
+
*
|
|
75
|
+
* Without a hook (Node, tests) background work is simply detached.
|
|
76
|
+
*/
|
|
77
|
+
export declare function setRemoteCacheWaitUntil(next: RemoteCacheWaitUntil | undefined): void;
|
|
29
78
|
/**
|
|
30
79
|
* Optional GitHub token for the API mirror below. Unauthenticated it allows 60
|
|
31
80
|
* requests/hour per IP, which is thin for a shared Worker egress; a token
|
|
@@ -34,6 +83,12 @@ export declare function setRemoteCacheStore(next: RemoteCacheStore | undefined):
|
|
|
34
83
|
export declare function setRemoteCacheGithubToken(token: string | undefined): void;
|
|
35
84
|
/** Drops every cached value. Intended for tests. */
|
|
36
85
|
export declare function clearRemoteCache(): void;
|
|
86
|
+
/**
|
|
87
|
+
* The remote-cache key of a chain's token list. `tokens.ts` builds the same
|
|
88
|
+
* string inline; this is exported so a pre-warm can name the key without
|
|
89
|
+
* importing the loader.
|
|
90
|
+
*/
|
|
91
|
+
export declare const tokenListCacheKey: (chainId: string) => string;
|
|
37
92
|
/**
|
|
38
93
|
* A non-2xx from the remote. Carries the ORIGIN status so a caller can tell a
|
|
39
94
|
* settled answer (404/410 — the file is not published) from a transient one
|
|
@@ -57,6 +112,10 @@ export declare function fetchRemoteJson<T>(url: string, label: string): Promise<
|
|
|
57
112
|
* failed `load()` falls back to whatever stale copy either layer holds, only
|
|
58
113
|
* rethrowing when there is nothing to fall back to.
|
|
59
114
|
*
|
|
115
|
+
* Memory is served WITHOUT awaiting anything for as long as the copy is inside
|
|
116
|
+
* the SWR window; past `FRESH_TTL_MS` a refresh runs behind it. The store is
|
|
117
|
+
* consulted only when memory has nothing inside that window.
|
|
118
|
+
*
|
|
60
119
|
* Concurrent callers for the same key share one in-flight load.
|
|
61
120
|
*
|
|
62
121
|
* `isValid` guards the durable layer: a truncated or shape-changed entry is
|
|
@@ -64,5 +123,28 @@ export declare function fetchRemoteJson<T>(url: string, label: string): Promise<
|
|
|
64
123
|
* mis-decimals every balance built from it.
|
|
65
124
|
*/
|
|
66
125
|
export declare function loadCached<T>(key: string, load: () => Promise<T>, isValid: (value: any) => boolean): Promise<T>;
|
|
126
|
+
/** Outcome of {@link refreshRemoteCache}. */
|
|
127
|
+
export type RemoteCacheRefreshResult =
|
|
128
|
+
/** Upstream was fetched and the store written. */
|
|
129
|
+
'refreshed'
|
|
130
|
+
/** The memory copy was younger than `maxAgeMs`; nothing was done. */
|
|
131
|
+
| 'fresh'
|
|
132
|
+
/** Upstream failed; the previous copy (if any) is still served. */
|
|
133
|
+
| 'failed'
|
|
134
|
+
/** Nothing has loaded this key in this process, so there is no loader to run. */
|
|
135
|
+
| 'unknown';
|
|
136
|
+
/**
|
|
137
|
+
* Forces a refresh of `key` from UPSTREAM (never the store), and awaits the
|
|
138
|
+
* store write. For a pre-warm job: the routine path only refreshes once a
|
|
139
|
+
* copy is `FRESH_TTL_MS` old, so a job that runs more often than that would
|
|
140
|
+
* otherwise never write anything.
|
|
141
|
+
*
|
|
142
|
+
* `maxAgeMs` skips the work when the memory copy is younger than that — so a
|
|
143
|
+
* job that has just loaded the key cold does not immediately fetch it again.
|
|
144
|
+
* Joins an in-flight refresh rather than starting a second one.
|
|
145
|
+
*/
|
|
146
|
+
export declare function refreshRemoteCache(key: string, opts?: {
|
|
147
|
+
maxAgeMs?: number;
|
|
148
|
+
}): Promise<RemoteCacheRefreshResult>;
|
|
67
149
|
/** `loadCached` over a JSON URL, failing on any non-2xx so stale can take over. */
|
|
68
150
|
export declare function loadCachedJson<T>(key: string, url: string, isValid: (value: any) => boolean, label?: string): Promise<T>;
|