@le-space/ui 0.7.0 → 0.9.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/package.json +3 -3
- package/react/index.d.ts +62 -0
- package/react/index.js +907 -165
- package/shared/index.d.ts +72 -2
- package/shared/index.js +477 -41
- package/styles.css +6 -0
- package/svelte/SponsorRelayFab.svelte +286 -0
- package/svelte/styles/theme.css +6 -0
package/shared/index.js
CHANGED
|
@@ -42,7 +42,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
|
|
|
42
42
|
var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
|
|
43
43
|
|
|
44
44
|
// src/shared/package-version.ts
|
|
45
|
-
var UI_PACKAGE_VERSION = "0.
|
|
45
|
+
var UI_PACKAGE_VERSION = "0.9.0";
|
|
46
46
|
|
|
47
47
|
// src/shared/format.ts
|
|
48
48
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -140,6 +140,8 @@ var DEFAULT_ALEPH_API_HOSTS = [
|
|
|
140
140
|
];
|
|
141
141
|
var DEFAULT_ALEPH_API_HOST = DEFAULT_ALEPH_API_HOSTS[0] ?? "https://api2.aleph.im";
|
|
142
142
|
var DEFAULT_CRN_LIST_URL = "https://crns-list.aleph.sh/crns.json";
|
|
143
|
+
var DEFAULT_CRN_AGGREGATE_ADDRESS = "0xa1B3bb7d2332383D96b7796B908fB7f7F3c2Be10";
|
|
144
|
+
var DEFAULT_CRN_AGGREGATE_KEY = "corechannel";
|
|
143
145
|
var DEFAULT_ALEPH_SCHEDULER_API_HOST = "https://scheduler.api.aleph.cloud";
|
|
144
146
|
var DEFAULT_2N6_API_HOST = "https://api.2n6.me";
|
|
145
147
|
function normalizeMessageStatus(status) {
|
|
@@ -194,7 +196,7 @@ async function fetchBalance(address, apiHost = DEFAULT_ALEPH_API_HOST) {
|
|
|
194
196
|
if (!response.ok) throw new Error(`Balance request failed: ${response.status}`);
|
|
195
197
|
return await response.json();
|
|
196
198
|
}
|
|
197
|
-
async function
|
|
199
|
+
async function fetchCrnsFromList(url = DEFAULT_CRN_LIST_URL) {
|
|
198
200
|
const requestUrl = new URL(url);
|
|
199
201
|
requestUrl.searchParams.set("filter_inactive", "true");
|
|
200
202
|
const response = await fetchWithTimeout(requestUrl, { cache: "no-cache" });
|
|
@@ -202,6 +204,130 @@ async function fetchCrns(url = DEFAULT_CRN_LIST_URL) {
|
|
|
202
204
|
const payload = await response.json();
|
|
203
205
|
return payload.crns ?? [];
|
|
204
206
|
}
|
|
207
|
+
function asFiniteNumber(value) {
|
|
208
|
+
if (value == null || value === "") return null;
|
|
209
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
210
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
211
|
+
}
|
|
212
|
+
function normalizeCrnAddress(value) {
|
|
213
|
+
const address = asString(value);
|
|
214
|
+
if (!address) return null;
|
|
215
|
+
const withScheme = /^https?:\/\//i.test(address) ? address : `https://${address}`;
|
|
216
|
+
try {
|
|
217
|
+
return new URL(withScheme).toString().replace(/\/+$/, "");
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function mapResourceNodesToCrns(resourceNodes) {
|
|
223
|
+
if (!Array.isArray(resourceNodes)) return [];
|
|
224
|
+
const crns = [];
|
|
225
|
+
for (const node of resourceNodes) {
|
|
226
|
+
if (!node || typeof node !== "object") continue;
|
|
227
|
+
const entry = node;
|
|
228
|
+
const hash = asString(entry.hash);
|
|
229
|
+
const address = normalizeCrnAddress(entry.address);
|
|
230
|
+
const type = asString(entry.type)?.toLowerCase();
|
|
231
|
+
const status = asString(entry.status)?.toLowerCase();
|
|
232
|
+
if (!hash || !address) continue;
|
|
233
|
+
if (entry.inactive_since != null || entry.locked === true) continue;
|
|
234
|
+
if (type && type !== "compute") continue;
|
|
235
|
+
if (status && status !== "linked" && status !== "active") continue;
|
|
236
|
+
if (!asString(entry.parent)) continue;
|
|
237
|
+
const score = asFiniteNumber(entry.score) ?? asFiniteNumber(entry.scoreV1);
|
|
238
|
+
const performance = asFiniteNumber(entry.performance);
|
|
239
|
+
const decentralization = asFiniteNumber(entry.decentralization);
|
|
240
|
+
crns.push({
|
|
241
|
+
hash,
|
|
242
|
+
address,
|
|
243
|
+
name: asString(entry.name) ?? hash,
|
|
244
|
+
...score == null ? {} : { score },
|
|
245
|
+
...performance == null ? {} : { performance },
|
|
246
|
+
...decentralization == null ? {} : { decentralization }
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return crns;
|
|
250
|
+
}
|
|
251
|
+
async function fetchCrnsFromAggregate(apiHosts = DEFAULT_ALEPH_API_HOSTS, address = DEFAULT_CRN_AGGREGATE_ADDRESS) {
|
|
252
|
+
const hosts = apiHosts.length > 0 ? apiHosts : DEFAULT_ALEPH_API_HOSTS;
|
|
253
|
+
let lastError = null;
|
|
254
|
+
for (const apiHost of hosts) {
|
|
255
|
+
try {
|
|
256
|
+
const url = new URL(`/api/v0/aggregates/${address}.json`, apiHost);
|
|
257
|
+
url.searchParams.set("keys", DEFAULT_CRN_AGGREGATE_KEY);
|
|
258
|
+
const response = await fetchWithTimeout(url, { cache: "no-cache" });
|
|
259
|
+
if (!response.ok) {
|
|
260
|
+
lastError = new Error(`CRN aggregate request failed: ${response.status}`);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const payload = await response.json();
|
|
264
|
+
const crns = mapResourceNodesToCrns(payload?.data?.corechannel?.resource_nodes);
|
|
265
|
+
if (crns.length > 0) return crns;
|
|
266
|
+
lastError = new Error("CRN aggregate contained no deployable resource nodes");
|
|
267
|
+
} catch (error) {
|
|
268
|
+
lastError = error;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "CRN aggregate request failed"));
|
|
272
|
+
}
|
|
273
|
+
async function fetchCrnsWithSource(url = DEFAULT_CRN_LIST_URL, options = {}) {
|
|
274
|
+
const apiHosts = options.apiHosts ?? DEFAULT_ALEPH_API_HOSTS;
|
|
275
|
+
const source = options.source ?? "auto";
|
|
276
|
+
if (source === "aggregate") {
|
|
277
|
+
return {
|
|
278
|
+
crns: await fetchCrnsFromAggregate(apiHosts, options.aggregateAddress),
|
|
279
|
+
source: "aggregate"
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
let listError;
|
|
283
|
+
try {
|
|
284
|
+
const crns = await fetchCrnsFromList(url);
|
|
285
|
+
if (crns.length > 0) return { crns, source: "list" };
|
|
286
|
+
listError = new Error("CRN list returned no entries");
|
|
287
|
+
} catch (error) {
|
|
288
|
+
listError = error instanceof Error ? error : new Error(String(error));
|
|
289
|
+
}
|
|
290
|
+
if (source === "list") throw listError;
|
|
291
|
+
return {
|
|
292
|
+
crns: await fetchCrnsFromAggregate(apiHosts, options.aggregateAddress),
|
|
293
|
+
source: "aggregate",
|
|
294
|
+
listError
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async function fetchCrns(url = DEFAULT_CRN_LIST_URL, options = {}) {
|
|
298
|
+
return (await fetchCrnsWithSource(url, options)).crns;
|
|
299
|
+
}
|
|
300
|
+
async function probeCrnAvailability(crn) {
|
|
301
|
+
const address = String(crn.address ?? "").trim();
|
|
302
|
+
if (!address) return "unreachable";
|
|
303
|
+
const lookup = await fetchCrnExecutionMap(address);
|
|
304
|
+
if (lookup.payload !== null) return "reachable";
|
|
305
|
+
return lookup.blocked ? "unknown" : "unreachable";
|
|
306
|
+
}
|
|
307
|
+
async function filterReachableCrns(crns, options = {}) {
|
|
308
|
+
const limit = Math.max(1, Number(options.limit) || 5);
|
|
309
|
+
const probed = crns.slice(0, limit);
|
|
310
|
+
if (probed.length === 0) return [...crns];
|
|
311
|
+
const verdicts = await Promise.all(
|
|
312
|
+
probed.map(async (crn) => {
|
|
313
|
+
try {
|
|
314
|
+
return { crn, verdict: await probeCrnAvailability(crn) };
|
|
315
|
+
} catch {
|
|
316
|
+
return { crn, verdict: "unknown" };
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
);
|
|
320
|
+
const kept = verdicts.filter((entry) => entry.verdict !== "unreachable").map((entry) => entry.crn);
|
|
321
|
+
if (kept.length === 0) {
|
|
322
|
+
options.onDiagnostic?.(`No CRN answered its executions endpoint (${probed.length} probed); keeping the unverified order`);
|
|
323
|
+
return [...crns];
|
|
324
|
+
}
|
|
325
|
+
const dropped = probed.length - kept.length;
|
|
326
|
+
if (dropped > 0) {
|
|
327
|
+
options.onDiagnostic?.(`Skipping ${dropped} unreachable CRN${dropped === 1 ? "" : "s"} of ${probed.length} probed`);
|
|
328
|
+
}
|
|
329
|
+
return [...kept, ...crns.slice(limit)];
|
|
330
|
+
}
|
|
205
331
|
async function fetchInstances(address, apiHost = DEFAULT_ALEPH_API_HOST) {
|
|
206
332
|
const url = new URL("/api/v0/messages.json", apiHost);
|
|
207
333
|
url.searchParams.set("msgTypes", "INSTANCE");
|
|
@@ -856,18 +982,23 @@ function createAlephBrowserClient(options = {}) {
|
|
|
856
982
|
const apiHosts = options.apiHosts === void 0 && options.apiHost === void 0 ? [...DEFAULT_ALEPH_API_HOSTS] : normalizeApiHosts(options.apiHosts, options.apiHost ?? DEFAULT_ALEPH_API_HOST);
|
|
857
983
|
const apiHost = apiHosts[0] ?? DEFAULT_ALEPH_API_HOST;
|
|
858
984
|
const crnListUrl = options.crnListUrl ?? DEFAULT_CRN_LIST_URL;
|
|
985
|
+
const crnSource = options.crnSource ?? "auto";
|
|
859
986
|
const schedulerApiHost = options.schedulerApiHost ?? DEFAULT_ALEPH_SCHEDULER_API_HOST;
|
|
860
987
|
const twoN6ApiHost = options.twoN6ApiHost ?? DEFAULT_2N6_API_HOST;
|
|
861
988
|
return {
|
|
862
989
|
apiHost,
|
|
863
990
|
apiHosts,
|
|
864
991
|
crnListUrl,
|
|
992
|
+
crnSource,
|
|
865
993
|
schedulerApiHost,
|
|
866
994
|
fetchBalance(address) {
|
|
867
995
|
return withApiHostFallback(apiHosts, (host) => fetchBalance(address, host));
|
|
868
996
|
},
|
|
869
997
|
fetchCrns() {
|
|
870
|
-
return fetchCrns(crnListUrl);
|
|
998
|
+
return fetchCrns(crnListUrl, { apiHosts, source: crnSource });
|
|
999
|
+
},
|
|
1000
|
+
fetchCrnsWithSource(source) {
|
|
1001
|
+
return fetchCrnsWithSource(crnListUrl, { apiHosts, source: source ?? crnSource });
|
|
871
1002
|
},
|
|
872
1003
|
fetchInstances(address) {
|
|
873
1004
|
return withApiHostFallback(apiHosts, (host) => fetchInstances(address, host));
|
|
@@ -942,6 +1073,15 @@ var DEFAULT_ALEPH_API_HOST2 = "https://api.aleph.im";
|
|
|
942
1073
|
|
|
943
1074
|
// ../core/src/crns.ts
|
|
944
1075
|
var DEFAULT_CRN_LIST_URL2 = "https://crns-list.aleph.sh/crns.json";
|
|
1076
|
+
var DEFAULT_CRN_AGGREGATE_ADDRESS2 = "0xa1B3bb7d2332383D96b7796B908fB7f7F3c2Be10";
|
|
1077
|
+
var DEFAULT_CRN_AGGREGATE_KEY2 = "corechannel";
|
|
1078
|
+
var DEFAULT_CRN_AGGREGATE_API_HOSTS = [
|
|
1079
|
+
"https://api2.aleph.im",
|
|
1080
|
+
"https://api.aleph.im"
|
|
1081
|
+
];
|
|
1082
|
+
function asString2(value) {
|
|
1083
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1084
|
+
}
|
|
945
1085
|
function compatibleCrns(crns, excludedHashes = []) {
|
|
946
1086
|
const excluded = new Set(excludedHashes.filter(Boolean));
|
|
947
1087
|
return [...crns].filter((crn) => {
|
|
@@ -975,7 +1115,7 @@ function filterDeployableCrns(crns, options = {}) {
|
|
|
975
1115
|
compatibleCrns(crns, options.excludedHashes).filter((crn) => hasCrnCapacity(crn, options.spec))
|
|
976
1116
|
);
|
|
977
1117
|
}
|
|
978
|
-
async function
|
|
1118
|
+
async function fetchCrnsFromList2(options) {
|
|
979
1119
|
const requestUrl = new URL(options.url ?? DEFAULT_CRN_LIST_URL2);
|
|
980
1120
|
requestUrl.searchParams.set("filter_inactive", "true");
|
|
981
1121
|
const response = await options.fetch(requestUrl.toString(), {
|
|
@@ -987,6 +1127,101 @@ async function fetchCrns2(options) {
|
|
|
987
1127
|
const payload = await response.json();
|
|
988
1128
|
return Array.isArray(payload?.crns) ? payload.crns : [];
|
|
989
1129
|
}
|
|
1130
|
+
function normalizeCrnAddress2(value) {
|
|
1131
|
+
const address = asString2(value);
|
|
1132
|
+
if (!address) return null;
|
|
1133
|
+
const withScheme = /^https?:\/\//i.test(address) ? address : `https://${address}`;
|
|
1134
|
+
try {
|
|
1135
|
+
return new URL(withScheme).toString().replace(/\/+$/, "");
|
|
1136
|
+
} catch {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
function aggregateScore(node) {
|
|
1141
|
+
for (const candidate of [node.score, node.scoreV1]) {
|
|
1142
|
+
if (candidate == null || candidate === "") continue;
|
|
1143
|
+
const numeric = typeof candidate === "number" ? candidate : Number(candidate);
|
|
1144
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
1145
|
+
}
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
function isDeployableResourceNode(node) {
|
|
1149
|
+
if (!asString2(node.hash)) return false;
|
|
1150
|
+
if (!normalizeCrnAddress2(node.address)) return false;
|
|
1151
|
+
if (node.inactive_since != null) return false;
|
|
1152
|
+
if (node.locked === true) return false;
|
|
1153
|
+
const type = asString2(node.type)?.toLowerCase();
|
|
1154
|
+
if (type && type !== "compute") return false;
|
|
1155
|
+
const status = asString2(node.status)?.toLowerCase();
|
|
1156
|
+
if (status && status !== "linked" && status !== "active") return false;
|
|
1157
|
+
return Boolean(asString2(node.parent));
|
|
1158
|
+
}
|
|
1159
|
+
function mapResourceNodesToCrns2(resourceNodes) {
|
|
1160
|
+
if (!Array.isArray(resourceNodes)) return [];
|
|
1161
|
+
return resourceNodes.filter((node) => Boolean(node) && typeof node === "object").filter(isDeployableResourceNode).map((node) => {
|
|
1162
|
+
const record = {
|
|
1163
|
+
hash: asString2(node.hash),
|
|
1164
|
+
address: normalizeCrnAddress2(node.address)
|
|
1165
|
+
};
|
|
1166
|
+
const name = asString2(node.name);
|
|
1167
|
+
if (name) record.name = name;
|
|
1168
|
+
const score = aggregateScore(node);
|
|
1169
|
+
if (score != null) record.score = score;
|
|
1170
|
+
return record;
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
async function fetchCrnsFromAggregate2(options) {
|
|
1174
|
+
const apiHosts = options.apiHosts?.length ? options.apiHosts : DEFAULT_CRN_AGGREGATE_API_HOSTS;
|
|
1175
|
+
const address = options.address ?? DEFAULT_CRN_AGGREGATE_ADDRESS2;
|
|
1176
|
+
const aggregateKey = options.aggregateKey ?? DEFAULT_CRN_AGGREGATE_KEY2;
|
|
1177
|
+
let lastError = null;
|
|
1178
|
+
for (const apiHost of apiHosts) {
|
|
1179
|
+
try {
|
|
1180
|
+
const url = new URL(`/api/v0/aggregates/${address}.json`, apiHost);
|
|
1181
|
+
url.searchParams.set("keys", aggregateKey);
|
|
1182
|
+
const response = await options.fetch(url.toString(), { cache: "no-cache" });
|
|
1183
|
+
if (!response.ok) {
|
|
1184
|
+
lastError = new Error(`CRN aggregate request failed: ${response.status}`);
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
const payload = await response.json();
|
|
1188
|
+
const crns = mapResourceNodesToCrns2(payload?.data?.corechannel?.resource_nodes);
|
|
1189
|
+
if (crns.length > 0) return crns;
|
|
1190
|
+
lastError = new Error("CRN aggregate contained no deployable resource nodes");
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
lastError = error;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "CRN aggregate request failed"));
|
|
1196
|
+
}
|
|
1197
|
+
async function fetchCrnsWithSource2(options) {
|
|
1198
|
+
const aggregateOptions = {
|
|
1199
|
+
fetch: options.fetch,
|
|
1200
|
+
apiHosts: options.aggregateApiHosts,
|
|
1201
|
+
address: options.aggregateAddress
|
|
1202
|
+
};
|
|
1203
|
+
if (options.preferAggregate) {
|
|
1204
|
+
return { crns: await fetchCrnsFromAggregate2(aggregateOptions), source: "aggregate" };
|
|
1205
|
+
}
|
|
1206
|
+
let listError;
|
|
1207
|
+
try {
|
|
1208
|
+
const crns = await fetchCrnsFromList2({ url: options.url, fetch: options.fetch });
|
|
1209
|
+
if (crns.length > 0) return { crns, source: "list" };
|
|
1210
|
+
listError = new Error("CRN list returned no entries");
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
listError = error instanceof Error ? error : new Error(String(error));
|
|
1213
|
+
}
|
|
1214
|
+
options.onDiagnostic?.(`CRN list unavailable (${listError.message}); falling back to the ${DEFAULT_CRN_AGGREGATE_KEY2} aggregate`);
|
|
1215
|
+
try {
|
|
1216
|
+
return { crns: await fetchCrnsFromAggregate2(aggregateOptions), source: "aggregate", listError };
|
|
1217
|
+
} catch (aggregateError) {
|
|
1218
|
+
const message = aggregateError instanceof Error ? aggregateError.message : String(aggregateError);
|
|
1219
|
+
throw new Error(`${listError.message}; CRN aggregate fallback failed: ${message}`);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
async function fetchCrns2(options) {
|
|
1223
|
+
return (await fetchCrnsWithSource2(options)).crns;
|
|
1224
|
+
}
|
|
990
1225
|
|
|
991
1226
|
// ../core/src/port-forwarding.ts
|
|
992
1227
|
var DEFAULT_INSTANCE_PORT_FORWARDS = [
|
|
@@ -1076,7 +1311,7 @@ async function fetchPortForwardAggregate(address, options) {
|
|
|
1076
1311
|
}
|
|
1077
1312
|
|
|
1078
1313
|
// ../core/src/aleph-normalizers.ts
|
|
1079
|
-
function
|
|
1314
|
+
function asString3(value) {
|
|
1080
1315
|
return typeof value === "string" && value.trim() ? value : null;
|
|
1081
1316
|
}
|
|
1082
1317
|
function asNumber2(value) {
|
|
@@ -1091,7 +1326,7 @@ function normalizeMessageStatus2(status) {
|
|
|
1091
1326
|
return "unknown";
|
|
1092
1327
|
}
|
|
1093
1328
|
function normalizeProxyUrl2(value) {
|
|
1094
|
-
const stringValue =
|
|
1329
|
+
const stringValue = asString3(value);
|
|
1095
1330
|
if (!stringValue) return null;
|
|
1096
1331
|
if (/^https?:\/\//i.test(stringValue)) return stringValue;
|
|
1097
1332
|
return `https://${stringValue}`;
|
|
@@ -1186,22 +1421,22 @@ function normalizeExecution2(item, crnUrl) {
|
|
|
1186
1421
|
version: "v2",
|
|
1187
1422
|
running: typeof v2Item.running === "boolean" ? v2Item.running : void 0,
|
|
1188
1423
|
networking: {
|
|
1189
|
-
ipv4_network:
|
|
1190
|
-
host_ipv4:
|
|
1191
|
-
ipv6_network:
|
|
1192
|
-
ipv6_ip:
|
|
1193
|
-
ipv4_ip:
|
|
1424
|
+
ipv4_network: asString3(networking.ipv4_network),
|
|
1425
|
+
host_ipv4: asString3(networking.host_ipv4),
|
|
1426
|
+
ipv6_network: asString3(networking.ipv6_network),
|
|
1427
|
+
ipv6_ip: asString3(networking.ipv6_ip),
|
|
1428
|
+
ipv4_ip: asString3(networking.ipv4_ip),
|
|
1194
1429
|
proxy_url: extractProxyUrl2(v2Item, networking),
|
|
1195
1430
|
mapped_ports: mappedPorts2
|
|
1196
1431
|
},
|
|
1197
1432
|
status: v2Item.status ? {
|
|
1198
|
-
defined_at:
|
|
1199
|
-
preparing_at:
|
|
1200
|
-
prepared_at:
|
|
1201
|
-
starting_at:
|
|
1202
|
-
started_at:
|
|
1203
|
-
stopping_at:
|
|
1204
|
-
stopped_at:
|
|
1433
|
+
defined_at: asString3(v2Item.status.defined_at),
|
|
1434
|
+
preparing_at: asString3(v2Item.status.preparing_at),
|
|
1435
|
+
prepared_at: asString3(v2Item.status.prepared_at),
|
|
1436
|
+
starting_at: asString3(v2Item.status.starting_at),
|
|
1437
|
+
started_at: asString3(v2Item.status.started_at),
|
|
1438
|
+
stopping_at: asString3(v2Item.status.stopping_at),
|
|
1439
|
+
stopped_at: asString3(v2Item.status.stopped_at)
|
|
1205
1440
|
} : null
|
|
1206
1441
|
};
|
|
1207
1442
|
}
|
|
@@ -1209,8 +1444,8 @@ function normalizeExecution2(item, crnUrl) {
|
|
|
1209
1444
|
crnUrl,
|
|
1210
1445
|
version: "v1",
|
|
1211
1446
|
networking: {
|
|
1212
|
-
ipv4: networking && "ipv4" in networking ?
|
|
1213
|
-
ipv6: networking && "ipv6" in networking ?
|
|
1447
|
+
ipv4: networking && "ipv4" in networking ? asString3(networking.ipv4) : null,
|
|
1448
|
+
ipv6: networking && "ipv6" in networking ? asString3(networking.ipv6) : null
|
|
1214
1449
|
},
|
|
1215
1450
|
status: null
|
|
1216
1451
|
};
|
|
@@ -1372,7 +1607,7 @@ async function broadcastAlephMessage2(message, options) {
|
|
|
1372
1607
|
// ../core/src/runtime.ts
|
|
1373
1608
|
var DEFAULT_SCHEDULER_ALLOCATION_URL = "https://scheduler.api.aleph.cloud/api/v0/allocation";
|
|
1374
1609
|
var DEFAULT_TWO_N_SIX_HASH_URL = "https://api.2n6.me/api/hash";
|
|
1375
|
-
function
|
|
1610
|
+
function asString4(value) {
|
|
1376
1611
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1377
1612
|
}
|
|
1378
1613
|
function asNumber3(value) {
|
|
@@ -1414,17 +1649,17 @@ async function fetchSchedulerAllocation2(itemHash, options) {
|
|
|
1414
1649
|
const node = payload?.node;
|
|
1415
1650
|
return {
|
|
1416
1651
|
source: "scheduler",
|
|
1417
|
-
crnHash:
|
|
1418
|
-
crnUrl:
|
|
1652
|
+
crnHash: asString4(node?.node_id),
|
|
1653
|
+
crnUrl: asString4(node?.url),
|
|
1419
1654
|
node: node ? {
|
|
1420
|
-
node_id:
|
|
1421
|
-
url:
|
|
1422
|
-
ipv6:
|
|
1655
|
+
node_id: asString4(node.node_id) ?? void 0,
|
|
1656
|
+
url: asString4(node.url) ?? void 0,
|
|
1657
|
+
ipv6: asString4(node.ipv6),
|
|
1423
1658
|
supports_ipv6: typeof node.supports_ipv6 === "boolean" ? node.supports_ipv6 : void 0
|
|
1424
1659
|
} : null,
|
|
1425
|
-
vmIpv6:
|
|
1660
|
+
vmIpv6: asString4(payload?.vm_ipv6),
|
|
1426
1661
|
period: payload?.period ? {
|
|
1427
|
-
start_timestamp:
|
|
1662
|
+
start_timestamp: asString4(payload.period.start_timestamp) ?? void 0,
|
|
1428
1663
|
duration_seconds: asNumber3(payload.period.duration_seconds) ?? void 0
|
|
1429
1664
|
} : null
|
|
1430
1665
|
};
|
|
@@ -1439,7 +1674,7 @@ async function fetch2n6WebAccessUrl2(itemHash, options) {
|
|
|
1439
1674
|
return {
|
|
1440
1675
|
url: normalizeProxyUrl2(payload?.url ?? payload?.subdomain),
|
|
1441
1676
|
active: typeof payload?.active === "boolean" ? payload.active : null,
|
|
1442
|
-
subdomain:
|
|
1677
|
+
subdomain: asString4(payload?.subdomain)
|
|
1443
1678
|
};
|
|
1444
1679
|
}
|
|
1445
1680
|
async function fetchCrnExecutionMap2(crnUrl, options) {
|
|
@@ -1659,7 +1894,7 @@ function utf8Hex(value) {
|
|
|
1659
1894
|
function normalizeHexSignature(signature) {
|
|
1660
1895
|
return signature.startsWith("0x") ? signature : `0x${signature}`;
|
|
1661
1896
|
}
|
|
1662
|
-
function
|
|
1897
|
+
function asString5(value) {
|
|
1663
1898
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1664
1899
|
}
|
|
1665
1900
|
function trimTrailingSlashes(value) {
|
|
@@ -1736,14 +1971,14 @@ async function fetchInstanceNodeHash(args) {
|
|
|
1736
1971
|
apiHost: args.apiHost
|
|
1737
1972
|
});
|
|
1738
1973
|
const payload = envelope;
|
|
1739
|
-
return
|
|
1974
|
+
return asString5(payload?.content?.requirements?.node?.node_hash) ?? asString5(payload?.message?.content?.requirements?.node?.node_hash) ?? null;
|
|
1740
1975
|
}
|
|
1741
1976
|
async function resolveInstanceCrn(args) {
|
|
1742
|
-
const providedCrnUrl =
|
|
1977
|
+
const providedCrnUrl = asString5(args.crnUrl);
|
|
1743
1978
|
if (providedCrnUrl) {
|
|
1744
1979
|
return {
|
|
1745
1980
|
crnUrl: trimTrailingSlashes(providedCrnUrl),
|
|
1746
|
-
crnHash:
|
|
1981
|
+
crnHash: asString5(args.crnHash),
|
|
1747
1982
|
source: "provided"
|
|
1748
1983
|
};
|
|
1749
1984
|
}
|
|
@@ -1754,11 +1989,11 @@ async function resolveInstanceCrn(args) {
|
|
|
1754
1989
|
if (allocation?.crnUrl) {
|
|
1755
1990
|
return {
|
|
1756
1991
|
crnUrl: trimTrailingSlashes(allocation.crnUrl),
|
|
1757
|
-
crnHash:
|
|
1992
|
+
crnHash: asString5(allocation.crnHash),
|
|
1758
1993
|
source: "scheduler"
|
|
1759
1994
|
};
|
|
1760
1995
|
}
|
|
1761
|
-
const manualCrnHash =
|
|
1996
|
+
const manualCrnHash = asString5(args.crnHash) ?? await fetchInstanceNodeHash({
|
|
1762
1997
|
instanceHash: args.instanceHash,
|
|
1763
1998
|
fetch: args.fetch,
|
|
1764
1999
|
apiHost: args.apiHost ?? DEFAULT_ALEPH_API_HOST2
|
|
@@ -3824,6 +4059,7 @@ function defaultState(props = {}) {
|
|
|
3824
4059
|
},
|
|
3825
4060
|
balance: null,
|
|
3826
4061
|
crns: [],
|
|
4062
|
+
crnSource: null,
|
|
3827
4063
|
selectedCrn: null,
|
|
3828
4064
|
crnOptions: [],
|
|
3829
4065
|
crnPinnedByUser: false,
|
|
@@ -3831,7 +4067,8 @@ function defaultState(props = {}) {
|
|
|
3831
4067
|
bootstrapRegistrations: [],
|
|
3832
4068
|
orphanBootstrapRegistrations: [],
|
|
3833
4069
|
lastDeploymentHash: null,
|
|
3834
|
-
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS
|
|
4070
|
+
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS,
|
|
4071
|
+
instanceAddresses: {}
|
|
3835
4072
|
};
|
|
3836
4073
|
}
|
|
3837
4074
|
function manifestLoadErrorState(error) {
|
|
@@ -3852,6 +4089,17 @@ function isDebugEnabled(props) {
|
|
|
3852
4089
|
return false;
|
|
3853
4090
|
}
|
|
3854
4091
|
}
|
|
4092
|
+
function crnSourcePreference(props) {
|
|
4093
|
+
const fromStorage = (() => {
|
|
4094
|
+
try {
|
|
4095
|
+
return globalThis.localStorage?.getItem("LE_SPACE_CRN_SOURCE") ?? null;
|
|
4096
|
+
} catch {
|
|
4097
|
+
return null;
|
|
4098
|
+
}
|
|
4099
|
+
})();
|
|
4100
|
+
const raw = (fromStorage ?? props.crnSource ?? "auto").trim().toLowerCase();
|
|
4101
|
+
return raw === "aggregate" || raw === "list" ? raw : "auto";
|
|
4102
|
+
}
|
|
3855
4103
|
async function sha256Hex(payload) {
|
|
3856
4104
|
const digest = await globalThis.crypto.subtle.digest(
|
|
3857
4105
|
"SHA-256",
|
|
@@ -3859,6 +4107,83 @@ async function sha256Hex(payload) {
|
|
|
3859
4107
|
);
|
|
3860
4108
|
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
3861
4109
|
}
|
|
4110
|
+
function hostnameOfUrl(value) {
|
|
4111
|
+
if (!value) return null;
|
|
4112
|
+
try {
|
|
4113
|
+
return new URL(value).hostname || null;
|
|
4114
|
+
} catch {
|
|
4115
|
+
return null;
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
function emptyInstanceAddresses() {
|
|
4119
|
+
return {
|
|
4120
|
+
status: "idle",
|
|
4121
|
+
groups: [],
|
|
4122
|
+
pwaEnv: null,
|
|
4123
|
+
payload: null,
|
|
4124
|
+
peerId: null,
|
|
4125
|
+
version: null,
|
|
4126
|
+
fetchedAt: null,
|
|
4127
|
+
error: null,
|
|
4128
|
+
sourceUrl: null
|
|
4129
|
+
};
|
|
4130
|
+
}
|
|
4131
|
+
var TRANSPORT_LABELS = {
|
|
4132
|
+
tcp: "TCP",
|
|
4133
|
+
websocket: "WebSocket",
|
|
4134
|
+
webrtc: "WebRTC",
|
|
4135
|
+
direct_tcp: "Direct TCP",
|
|
4136
|
+
autotls_wss: "AutoTLS WSS",
|
|
4137
|
+
proxy_wss: "Proxy WSS",
|
|
4138
|
+
webtransport: "WebTransport",
|
|
4139
|
+
webrtc_direct: "WebRTC Direct",
|
|
4140
|
+
browser_bootstrap: "Browser bootstrap",
|
|
4141
|
+
probe: "Probe"
|
|
4142
|
+
};
|
|
4143
|
+
function transportLabel(key) {
|
|
4144
|
+
return TRANSPORT_LABELS[key] ?? key.replace(/[_-]+/g, " ").replace(/^./, (first) => first.toUpperCase());
|
|
4145
|
+
}
|
|
4146
|
+
function addressList(value) {
|
|
4147
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && Boolean(entry.trim())) : [];
|
|
4148
|
+
}
|
|
4149
|
+
function parseInstanceAddressPayload(payload) {
|
|
4150
|
+
const groups = [];
|
|
4151
|
+
const byTransport = payload.byTransport;
|
|
4152
|
+
if (byTransport && typeof byTransport === "object") {
|
|
4153
|
+
for (const [key, value] of Object.entries(
|
|
4154
|
+
byTransport
|
|
4155
|
+
)) {
|
|
4156
|
+
const addresses = addressList(value);
|
|
4157
|
+
if (addresses.length > 0) {
|
|
4158
|
+
groups.push({ key, label: transportLabel(key), addresses });
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
4163
|
+
if (!key.endsWith("_multiaddrs")) continue;
|
|
4164
|
+
const addresses = addressList(value);
|
|
4165
|
+
if (addresses.length === 0) continue;
|
|
4166
|
+
const groupKey = key.replace(/_multiaddrs$/, "");
|
|
4167
|
+
groups.push({
|
|
4168
|
+
key: groupKey,
|
|
4169
|
+
label: transportLabel(groupKey),
|
|
4170
|
+
addresses
|
|
4171
|
+
});
|
|
4172
|
+
}
|
|
4173
|
+
const pwaEnvRaw = payload.pwa_env;
|
|
4174
|
+
const pwaEnv = pwaEnvRaw && typeof pwaEnvRaw === "object" ? Object.fromEntries(
|
|
4175
|
+
Object.entries(pwaEnvRaw).filter(([, entry]) => typeof entry === "string").map(([entryKey, entry]) => [entryKey, entry])
|
|
4176
|
+
) : null;
|
|
4177
|
+
const peerId = typeof payload.peerId === "string" ? payload.peerId : typeof payload.peer_id === "string" ? payload.peer_id : null;
|
|
4178
|
+
return {
|
|
4179
|
+
...emptyInstanceAddresses(),
|
|
4180
|
+
groups,
|
|
4181
|
+
pwaEnv,
|
|
4182
|
+
payload,
|
|
4183
|
+
peerId,
|
|
4184
|
+
version: typeof payload.version === "string" ? payload.version : null
|
|
4185
|
+
};
|
|
4186
|
+
}
|
|
3862
4187
|
function mappedPorts(execution) {
|
|
3863
4188
|
return Object.entries(execution?.networking?.mapped_ports ?? {}).map(
|
|
3864
4189
|
([port, mapping]) => ({
|
|
@@ -4007,6 +4332,7 @@ async function inspectInstanceRuntime(args) {
|
|
|
4007
4332
|
vmIpv4: null,
|
|
4008
4333
|
webUrl: null,
|
|
4009
4334
|
sshCommand: null,
|
|
4335
|
+
proxyHostname: null,
|
|
4010
4336
|
mappedPorts: [],
|
|
4011
4337
|
execution: null,
|
|
4012
4338
|
error: null
|
|
@@ -4071,6 +4397,7 @@ async function inspectInstanceRuntime(args) {
|
|
|
4071
4397
|
details.ipv6 = execution.networking.ipv6_ip ?? execution.networking.ipv6 ?? details.ipv6;
|
|
4072
4398
|
details.vmIpv4 = execution.networking.ipv4_ip ?? null;
|
|
4073
4399
|
details.webUrl = execution.networking.proxy_url ?? details.webUrl;
|
|
4400
|
+
details.proxyHostname = hostnameOfUrl(details.webUrl);
|
|
4074
4401
|
details.mappedPorts = mappedPorts(execution);
|
|
4075
4402
|
details.sshCommand = buildSshCommand(details.hostIpv4, details.mappedPorts);
|
|
4076
4403
|
return { details, lookup };
|
|
@@ -4149,6 +4476,7 @@ var SponsorRelayController = class {
|
|
|
4149
4476
|
apiHost: props.apiHost,
|
|
4150
4477
|
apiHosts: props.apiHosts,
|
|
4151
4478
|
crnListUrl: props.crnListUrl,
|
|
4479
|
+
crnSource: crnSourcePreference(props),
|
|
4152
4480
|
schedulerApiHost: props.schedulerApiHost,
|
|
4153
4481
|
twoN6ApiHost: props.twoN6ApiHost
|
|
4154
4482
|
});
|
|
@@ -4420,10 +4748,14 @@ var SponsorRelayController = class {
|
|
|
4420
4748
|
detail: "Publishing the host port mapping into the guest relay configuration."
|
|
4421
4749
|
});
|
|
4422
4750
|
const tcpPort = mappedPorts2["9091"]?.host ?? 0;
|
|
4423
|
-
const wsPort = mappedPorts2["9092"]?.host ??
|
|
4751
|
+
const wsPort = mappedPorts2["9092"]?.host ?? 0;
|
|
4424
4752
|
if (!tcpPort || !wsPort) {
|
|
4753
|
+
const missing = [
|
|
4754
|
+
tcpPort ? null : "9091 (libp2p TCP)",
|
|
4755
|
+
wsPort ? null : "9092 (libp2p WebSocket)"
|
|
4756
|
+
].filter(Boolean);
|
|
4425
4757
|
throw new Error(
|
|
4426
|
-
|
|
4758
|
+
`OrbitDB relay runtime has no mapped host port for ${missing.join(" and ")}. Deploy a rootfs whose manifest requests these forwards.`
|
|
4427
4759
|
);
|
|
4428
4760
|
}
|
|
4429
4761
|
const configureResult = await configureOrbitdbRelaySetup2({
|
|
@@ -5367,10 +5699,18 @@ var SponsorRelayController = class {
|
|
|
5367
5699
|
statusText: currentProfile === "ucan-store" ? "Refreshing service deployment data" : "Refreshing relay deployment data"
|
|
5368
5700
|
});
|
|
5369
5701
|
try {
|
|
5370
|
-
const [pricingSummary,
|
|
5702
|
+
const [pricingSummary, crnList] = await Promise.all([
|
|
5371
5703
|
fetchInstancePricing(this.client.apiHost),
|
|
5372
|
-
this.client.
|
|
5704
|
+
this.client.fetchCrnsWithSource(crnSourcePreference(this.props))
|
|
5373
5705
|
]);
|
|
5706
|
+
const { crns, source: crnSource } = crnList;
|
|
5707
|
+
if (crnList.listError) {
|
|
5708
|
+
this.trace("crns:fallback", {
|
|
5709
|
+
source: crnSource,
|
|
5710
|
+
count: crns.length,
|
|
5711
|
+
listError: crnList.listError.message
|
|
5712
|
+
});
|
|
5713
|
+
}
|
|
5374
5714
|
const manifestState = await resolveManifest({
|
|
5375
5715
|
manifestUrl: this.state.manifestUrl,
|
|
5376
5716
|
manifestJson: this.state.manifestJson
|
|
@@ -5406,6 +5746,7 @@ var SponsorRelayController = class {
|
|
|
5406
5746
|
vmIpv4: null,
|
|
5407
5747
|
webUrl: null,
|
|
5408
5748
|
sshCommand: null,
|
|
5749
|
+
proxyHostname: null,
|
|
5409
5750
|
mappedPorts: [],
|
|
5410
5751
|
execution: null,
|
|
5411
5752
|
error: null
|
|
@@ -5519,6 +5860,7 @@ var SponsorRelayController = class {
|
|
|
5519
5860
|
},
|
|
5520
5861
|
balance,
|
|
5521
5862
|
crns,
|
|
5863
|
+
crnSource,
|
|
5522
5864
|
instances,
|
|
5523
5865
|
bootstrapRegistrations,
|
|
5524
5866
|
orphanBootstrapRegistrations,
|
|
@@ -5596,10 +5938,21 @@ var SponsorRelayController = class {
|
|
|
5596
5938
|
}
|
|
5597
5939
|
});
|
|
5598
5940
|
const preferredCrnHash = this.state.selectedCrn?.hash ?? null;
|
|
5599
|
-
const
|
|
5941
|
+
const rankedCrns = [
|
|
5600
5942
|
...compatibleCrns2.filter((crn) => crn.hash === preferredCrnHash),
|
|
5601
5943
|
...compatibleCrns2.filter((crn) => crn.hash !== preferredCrnHash)
|
|
5602
5944
|
].slice(0, 5);
|
|
5945
|
+
const orderedCrns = this.state.crnSource === "aggregate" ? await filterReachableCrns(rankedCrns, {
|
|
5946
|
+
limit: rankedCrns.length,
|
|
5947
|
+
onDiagnostic: (message) => this.trace("crns:probe", { message })
|
|
5948
|
+
}).then(
|
|
5949
|
+
(reachable) => preferredCrnHash && !reachable.some((crn) => crn.hash === preferredCrnHash) ? [
|
|
5950
|
+
...rankedCrns.filter(
|
|
5951
|
+
(crn) => crn.hash === preferredCrnHash
|
|
5952
|
+
),
|
|
5953
|
+
...reachable
|
|
5954
|
+
] : reachable
|
|
5955
|
+
) : rankedCrns;
|
|
5603
5956
|
if (orderedCrns.length === 0) {
|
|
5604
5957
|
throw new Error(
|
|
5605
5958
|
"No compatible CRN is currently available for this tier."
|
|
@@ -5821,6 +6174,9 @@ var SponsorRelayController = class {
|
|
|
5821
6174
|
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
5822
6175
|
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
5823
6176
|
sshCommand: runtime.sshCommand ?? null,
|
|
6177
|
+
proxyHostname: hostnameOfUrl(
|
|
6178
|
+
runtime.proxyUrl ?? runtime.webAccessUrl ?? runtime.execution?.networking?.proxy_url ?? null
|
|
6179
|
+
),
|
|
5824
6180
|
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
5825
6181
|
execution: runtime.execution ?? null,
|
|
5826
6182
|
error: null
|
|
@@ -5918,6 +6274,9 @@ var SponsorRelayController = class {
|
|
|
5918
6274
|
vmIpv4: latestRuntime.execution?.networking?.ipv4_ip ?? null,
|
|
5919
6275
|
webUrl: latestRuntime.webAccessUrl ?? latestRuntime.proxyUrl ?? latestRuntime.execution?.networking?.proxy_url ?? null,
|
|
5920
6276
|
sshCommand: latestRuntime.sshCommand ?? null,
|
|
6277
|
+
proxyHostname: hostnameOfUrl(
|
|
6278
|
+
latestRuntime.proxyUrl ?? latestRuntime.webAccessUrl ?? latestRuntime.execution?.networking?.proxy_url ?? null
|
|
6279
|
+
),
|
|
5921
6280
|
mappedPorts: latestRuntime.execution != null ? mappedPorts(latestRuntime.execution) : runtimeMappedPorts(latestRuntime.mappedPorts),
|
|
5922
6281
|
execution: latestRuntime.execution ?? null,
|
|
5923
6282
|
error: null
|
|
@@ -6098,6 +6457,82 @@ var SponsorRelayController = class {
|
|
|
6098
6457
|
});
|
|
6099
6458
|
}
|
|
6100
6459
|
}
|
|
6460
|
+
/**
|
|
6461
|
+
* Read one deployment's own address document over its HTTPS proxy.
|
|
6462
|
+
*
|
|
6463
|
+
* The mapped ports are plain HTTP, which a browser on an HTTPS page refuses to
|
|
6464
|
+
* fetch, so the proxy hostname is the only way in. Deliberately single-shot:
|
|
6465
|
+
* unlike the deploy-time readiness probe this backs a button someone pressed,
|
|
6466
|
+
* and a caller who wants newer data presses it again.
|
|
6467
|
+
*/
|
|
6468
|
+
async loadInstanceAddresses(instanceHash, options = {}) {
|
|
6469
|
+
const existing = this.state.instanceAddresses[instanceHash];
|
|
6470
|
+
if (!options.force && existing && existing.status !== "idle") return;
|
|
6471
|
+
const record = this.state.instances.find(
|
|
6472
|
+
(entry) => entry.instance.item_hash === instanceHash
|
|
6473
|
+
);
|
|
6474
|
+
const proxyHostname = record?.details.proxyHostname ?? null;
|
|
6475
|
+
if (!proxyHostname) {
|
|
6476
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6477
|
+
...emptyInstanceAddresses(),
|
|
6478
|
+
status: "unsupported",
|
|
6479
|
+
error: "This deployment has no HTTPS proxy hostname yet, so its addresses cannot be read from the browser."
|
|
6480
|
+
});
|
|
6481
|
+
return;
|
|
6482
|
+
}
|
|
6483
|
+
const sourceUrl = `https://${proxyHostname}/multiaddrs`;
|
|
6484
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6485
|
+
...existing ?? emptyInstanceAddresses(),
|
|
6486
|
+
status: "loading",
|
|
6487
|
+
sourceUrl,
|
|
6488
|
+
error: null
|
|
6489
|
+
});
|
|
6490
|
+
try {
|
|
6491
|
+
const abort = new AbortController();
|
|
6492
|
+
const timeout = globalThis.setTimeout(() => abort.abort(), 1e4);
|
|
6493
|
+
const response = await fetch(sourceUrl, {
|
|
6494
|
+
cache: "no-cache",
|
|
6495
|
+
signal: abort.signal
|
|
6496
|
+
});
|
|
6497
|
+
globalThis.clearTimeout(timeout);
|
|
6498
|
+
if (response.status === 404) {
|
|
6499
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6500
|
+
...emptyInstanceAddresses(),
|
|
6501
|
+
status: "unsupported",
|
|
6502
|
+
sourceUrl,
|
|
6503
|
+
fetchedAt: Date.now(),
|
|
6504
|
+
error: "This profile does not publish an address document."
|
|
6505
|
+
});
|
|
6506
|
+
return;
|
|
6507
|
+
}
|
|
6508
|
+
if (!response.ok) {
|
|
6509
|
+
throw new Error(`${sourceUrl} answered ${response.status}.`);
|
|
6510
|
+
}
|
|
6511
|
+
const payload = await response.json();
|
|
6512
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6513
|
+
...parseInstanceAddressPayload(payload),
|
|
6514
|
+
status: "ready",
|
|
6515
|
+
sourceUrl,
|
|
6516
|
+
fetchedAt: Date.now(),
|
|
6517
|
+
error: null
|
|
6518
|
+
});
|
|
6519
|
+
} catch (error) {
|
|
6520
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6521
|
+
...this.state.instanceAddresses[instanceHash] ?? emptyInstanceAddresses(),
|
|
6522
|
+
status: "error",
|
|
6523
|
+
sourceUrl,
|
|
6524
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6525
|
+
});
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6528
|
+
patchInstanceAddresses(instanceHash, value) {
|
|
6529
|
+
this.patch({
|
|
6530
|
+
instanceAddresses: {
|
|
6531
|
+
...this.state.instanceAddresses,
|
|
6532
|
+
[instanceHash]: value
|
|
6533
|
+
}
|
|
6534
|
+
});
|
|
6535
|
+
}
|
|
6101
6536
|
async deleteInstance(instanceHash) {
|
|
6102
6537
|
this.trace("delete:start", { instanceHash });
|
|
6103
6538
|
if (!this.state.wallet.address) {
|
|
@@ -6302,6 +6737,7 @@ export {
|
|
|
6302
6737
|
getEthereumProvider,
|
|
6303
6738
|
joinMappedPorts,
|
|
6304
6739
|
joinRequiredPortForwards,
|
|
6740
|
+
parseInstanceAddressPayload,
|
|
6305
6741
|
personalSign,
|
|
6306
6742
|
resolveManifestSource,
|
|
6307
6743
|
rootfsHealth,
|