@le-space/ui 0.6.3 → 0.6.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/ui",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "Floating Aleph sponsor relay UI components for Svelte and React.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -44,9 +44,9 @@
44
44
  "url": "https://github.com/NiKrause/relay-button/issues"
45
45
  },
46
46
  "dependencies": {
47
- "@le-space/browser": "0.6.3",
48
- "@le-space/core": "0.6.3",
49
- "viem": "^2.38.5"
47
+ "@le-space/browser": "0.6.5",
48
+ "@le-space/core": "0.6.5",
49
+ "viem": "^2.53.1"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "react": ">=18",
package/react/index.js CHANGED
@@ -43,7 +43,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
43
43
  var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
44
44
 
45
45
  // src/shared/package-version.ts
46
- var UI_PACKAGE_VERSION = "0.6.3";
46
+ var UI_PACKAGE_VERSION = "0.6.5";
47
47
 
48
48
  // src/shared/format.ts
49
49
  function shortHash(value, head = 8, tail = 6) {
@@ -1298,7 +1298,9 @@ async function postBroadcastPayload2(body, options) {
1298
1298
  }
1299
1299
  async function broadcastAlephMessage2(message, options) {
1300
1300
  const requestBody = { sync: options.sync ?? false, message };
1301
- const maxAttempts = 3;
1301
+ const maxAttempts = Math.max(1, Number(options.attempts ?? 3));
1302
+ const retryDelayMs = Math.max(0, Number(options.retryDelayMs ?? 0));
1303
+ const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1302
1304
  for (let index = 0; index < maxAttempts; index += 1) {
1303
1305
  const result = await postBroadcastPayload2(requestBody, {
1304
1306
  apiHost: options.apiHost,
@@ -1311,6 +1313,9 @@ async function broadcastAlephMessage2(message, options) {
1311
1313
  if (!canRetry) {
1312
1314
  throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response ?? {})}`);
1313
1315
  }
1316
+ if (retryDelayMs > 0) {
1317
+ await sleep2(retryDelayMs);
1318
+ }
1314
1319
  }
1315
1320
  throw new Error("Broadcast failed: retry budget exhausted");
1316
1321
  }
@@ -2118,7 +2123,10 @@ async function deployInstance(args) {
2118
2123
  const { response, httpStatus } = await broadcastAlephMessage2(message, {
2119
2124
  apiHost: args.apiHost,
2120
2125
  sync: args.sync,
2121
- fetch: args.fetch
2126
+ fetch: args.fetch,
2127
+ attempts: args.broadcastAttempts,
2128
+ retryDelayMs: args.broadcastRetryDelayMs,
2129
+ sleep: args.broadcastSleep
2122
2130
  });
2123
2131
  const status = httpStatus === 202 ? "pending" : typeof response.message_status === "string" ? response.message_status : "unknown";
2124
2132
  args.onProgress?.({
@@ -2323,9 +2331,13 @@ async function configureUcanStore(args) {
2323
2331
  public_ipv4: args.hostIpv4,
2324
2332
  public_ipv6: args.publicIpv6 ?? void 0,
2325
2333
  proxy_url: args.proxyUrl ?? void 0,
2334
+ service_did: args.serviceDid ?? void 0,
2335
+ service_origin: args.serviceOrigin ?? void 0,
2336
+ public_storage_origin: args.publicStorageOrigin ?? void 0,
2326
2337
  webauthn_origin: args.webauthnOrigin ?? void 0,
2327
2338
  webauthn_origin_fallbacks: args.webauthnOriginFallbacks ?? void 0,
2328
2339
  admin_did: args.adminDid ?? void 0,
2340
+ admin_api_token: args.adminApiToken ?? void 0,
2329
2341
  bootstrap_package: args.bootstrapPackage ?? void 0,
2330
2342
  no_start: args.noStart === true ? true : void 0
2331
2343
  };
@@ -2424,13 +2436,15 @@ async function fetchUcGoPeerMetadata(args) {
2424
2436
  }
2425
2437
 
2426
2438
  // ../aleph-bootstrap/src/index.ts
2427
- import { recoverMessageAddress } from "viem";
2439
+ import { keccak256, recoverMessageAddress, stringToHex } from "viem";
2428
2440
  var DEFAULT_ALEPH_API_HOST3 = "https://api.aleph.im";
2429
2441
  var DEFAULT_ALEPH_BOOTSTRAP_CHANNEL = "simple-todo";
2430
2442
  var DEFAULT_ALEPH_BOOTSTRAP_REF = "simple-todo-bootstrap";
2431
2443
  var DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap";
2444
+ var DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE = "relay-bootstrap-v2";
2432
2445
  var DEFAULT_BOOTSTRAP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
2433
2446
  var DEFAULT_BOOTSTRAP_PAGINATION = 50;
2447
+ var DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT = 3;
2434
2448
  var RELAY_BOOTSTRAP_SIGNATURE_SCHEME = "personal_sign";
2435
2449
  function serializeOwnerAuthorizationPayload(payload) {
2436
2450
  return JSON.stringify({
@@ -2448,14 +2462,41 @@ function serializeOwnerAuthorizationPayload(payload) {
2448
2462
  function serializeRelayProofPayload(payload) {
2449
2463
  return JSON.stringify({
2450
2464
  peerId: payload.peerId,
2451
- multiaddrs: dedupeMultiaddrs(payload.multiaddrs),
2465
+ multiaddrs: payload.multiaddrs ? dedupeMultiaddrs(payload.multiaddrs) : void 0,
2452
2466
  browserMultiaddrs: payload.browserMultiaddrs ? dedupeMultiaddrs(payload.browserMultiaddrs) : void 0,
2467
+ multiaddrsHash: payload.multiaddrsHash,
2468
+ browserMultiaddrsHash: payload.browserMultiaddrsHash,
2453
2469
  registrationId: payload.registrationId,
2454
2470
  profile: payload.profile,
2455
2471
  version: payload.version,
2456
2472
  updatedAt: payload.updatedAt
2457
2473
  });
2458
2474
  }
2475
+ function relayBootstrapMultiaddrsHash(addrs) {
2476
+ return keccak256(stringToHex(JSON.stringify(dedupeMultiaddrs(addrs))));
2477
+ }
2478
+ function compactPostType(postType) {
2479
+ return (postType ?? DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE) === DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
2480
+ }
2481
+ function browserMultiaddrPreference(addr) {
2482
+ const normalized = addr.toLowerCase();
2483
+ if (normalized.includes("/tcp/443/tls/ws/")) return 0;
2484
+ if (normalized.includes("/tls/ws/")) return 1;
2485
+ if (normalized.includes("/webtransport/")) return 2;
2486
+ if (normalized.includes("/webrtc-direct/")) return 3;
2487
+ if (normalized.includes("/ws")) return 4;
2488
+ return 5;
2489
+ }
2490
+ function selectCompactRelayBootstrapMultiaddrs(addrs, limit = DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT) {
2491
+ const entries = filterPublicMultiaddrs(addrs, {
2492
+ browserDialableOnly: true
2493
+ }).map((addr, index) => ({ addr, index }));
2494
+ entries.sort((left, right) => {
2495
+ const preference = browserMultiaddrPreference(left.addr) - browserMultiaddrPreference(right.addr);
2496
+ return preference !== 0 ? preference : left.index - right.index;
2497
+ });
2498
+ return entries.map((entry) => entry.addr).slice(0, Math.max(1, Math.floor(limit)));
2499
+ }
2459
2500
  function asTrimmedString(value) {
2460
2501
  return typeof value === "string" && value.trim() ? value.trim() : null;
2461
2502
  }
@@ -2611,8 +2652,10 @@ function normalizeRelayBootstrapProofPayload(value) {
2611
2652
  const browserMultiaddrs = Array.isArray(payload.browserMultiaddrs) ? payload.browserMultiaddrs.filter((entry) => typeof entry === "string") : void 0;
2612
2653
  return {
2613
2654
  peerId,
2614
- multiaddrs: dedupeMultiaddrs(multiaddrs),
2655
+ multiaddrs: multiaddrs.length > 0 ? dedupeMultiaddrs(multiaddrs) : void 0,
2615
2656
  browserMultiaddrs: browserMultiaddrs ? dedupeMultiaddrs(browserMultiaddrs) : void 0,
2657
+ multiaddrsHash: asTrimmedString(payload.multiaddrsHash) ?? void 0,
2658
+ browserMultiaddrsHash: asTrimmedString(payload.browserMultiaddrsHash) ?? void 0,
2616
2659
  registrationId: asTrimmedString(payload.registrationId) ?? void 0,
2617
2660
  profile: asTrimmedString(payload.profile) ?? void 0,
2618
2661
  version: asTrimmedString(payload.version) ?? void 0,
@@ -2644,16 +2687,24 @@ function normalizeRelayBootstrapPostRecord(value) {
2644
2687
  function buildRelayBootstrapPostContent(args) {
2645
2688
  const now = args.now ?? Date.now() / 1e3;
2646
2689
  const updatedAt = args.now ?? Date.now();
2690
+ const postType = args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE;
2691
+ const compact = args.compact ?? compactPostType(postType);
2692
+ const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
2693
+ const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
2694
+ browserDialableOnly: true
2695
+ }) : void 0;
2696
+ const compactMultiaddrs = selectCompactRelayBootstrapMultiaddrs(
2697
+ fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
2698
+ args.compactMultiaddrLimit
2699
+ );
2647
2700
  return {
2648
- type: args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
2701
+ type: postType,
2649
2702
  address: args.sender,
2650
2703
  ...args.ref ? { ref: args.ref } : {},
2651
2704
  content: {
2652
2705
  peerId: args.peerId,
2653
- multiaddrs: filterPublicMultiaddrs(args.multiaddrs),
2654
- browserMultiaddrs: args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
2655
- browserDialableOnly: true
2656
- }) : void 0,
2706
+ multiaddrs: compact ? compactMultiaddrs : fullMultiaddrs,
2707
+ browserMultiaddrs: compact ? void 0 : fullBrowserMultiaddrs,
2657
2708
  registrationId: args.registrationId,
2658
2709
  profile: args.profile,
2659
2710
  version: args.version,
@@ -2683,6 +2734,8 @@ async function createRelayBootstrapPost(args) {
2683
2734
  publisherAddress: args.publisherAddress,
2684
2735
  authorization: args.authorization,
2685
2736
  relayProof: args.relayProof,
2737
+ compact: args.compact,
2738
+ compactMultiaddrLimit: args.compactMultiaddrLimit,
2686
2739
  now: nowMillis
2687
2740
  });
2688
2741
  itemContent.time = nowSeconds;
@@ -2720,12 +2773,20 @@ async function signRelayBootstrapAuthorization(args) {
2720
2773
  };
2721
2774
  }
2722
2775
  async function signRelayBootstrapProof(args) {
2776
+ const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
2777
+ const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
2778
+ browserDialableOnly: true
2779
+ }) : void 0;
2780
+ const compactMultiaddrs = selectCompactRelayBootstrapMultiaddrs(
2781
+ fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
2782
+ args.compactMultiaddrLimit
2783
+ );
2784
+ const compact = args.compact ?? false;
2723
2785
  const payload = {
2724
2786
  peerId: args.peerId,
2725
- multiaddrs: filterPublicMultiaddrs(args.multiaddrs),
2726
- browserMultiaddrs: args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
2727
- browserDialableOnly: true
2728
- }) : void 0,
2787
+ multiaddrs: compact ? void 0 : fullMultiaddrs,
2788
+ browserMultiaddrs: compact ? void 0 : fullBrowserMultiaddrs,
2789
+ multiaddrsHash: compact ? relayBootstrapMultiaddrsHash(compactMultiaddrs) : void 0,
2729
2790
  registrationId: args.registrationId,
2730
2791
  profile: args.profile,
2731
2792
  version: args.version,
@@ -2772,7 +2833,7 @@ async function fetchAlephBootstrapPosts(options = {}) {
2772
2833
  throw new Error(`Aleph bootstrap post lookup failed: ${response.status}`);
2773
2834
  }
2774
2835
  const payload = await response.json();
2775
- return (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null);
2836
+ return (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
2776
2837
  }
2777
2838
  function compareRelayBootstrapPostRecency(left, right) {
2778
2839
  const leftUpdatedAt = left.content?.updatedAt ?? 0;
@@ -2819,12 +2880,14 @@ async function waitForRelayBootstrapRegistration(args) {
2819
2880
  const expectedSender = args.sender?.trim().toLowerCase() || null;
2820
2881
  const expectedRegistrationId = args.registrationId?.trim() || null;
2821
2882
  const expectedPeerId = args.peerId?.trim() || null;
2883
+ const postType = args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
2884
+ const sleepImpl = args.sleep ?? sleep;
2822
2885
  for (let attempt = 0; attempt < attempts; attempt += 1) {
2823
2886
  const posts = await fetchAlephBootstrapPosts({
2824
2887
  apiHost: args.apiHost ?? DEFAULT_ALEPH_API_HOST3,
2825
2888
  channel: args.channel,
2826
2889
  ref: args.ref,
2827
- postType: args.postType,
2890
+ postType,
2828
2891
  fetch: args.fetch
2829
2892
  });
2830
2893
  const currentPosts = selectCurrentRelayBootstrapPosts(posts);
@@ -2855,7 +2918,7 @@ async function waitForRelayBootstrapRegistration(args) {
2855
2918
  }
2856
2919
  args.onAttempt?.(null, attempt + 1, attempts);
2857
2920
  if (attempt < attempts - 1) {
2858
- await sleep(delayMs);
2921
+ await sleepImpl(delayMs);
2859
2922
  }
2860
2923
  }
2861
2924
  return null;
@@ -2863,6 +2926,8 @@ async function waitForRelayBootstrapRegistration(args) {
2863
2926
  async function publishRelayBootstrapRegistration(args) {
2864
2927
  const publisherAddress = args.publisherAddress ?? args.sender;
2865
2928
  const ownerAddress = args.ownerAddress;
2929
+ const postType = args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
2930
+ const compact = postType === DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
2866
2931
  let ownerAuthorization = args.ownerAuthorization;
2867
2932
  let relayProof = args.relayProof;
2868
2933
  if (!ownerAuthorization && ownerAddress && args.ownerSigner) {
@@ -2889,6 +2954,8 @@ async function publishRelayBootstrapRegistration(args) {
2889
2954
  profile: args.profile,
2890
2955
  version: args.version,
2891
2956
  updatedAt: args.now ?? Date.now(),
2957
+ compact,
2958
+ compactMultiaddrLimit: args.compactMultiaddrLimit,
2892
2959
  signer: args.publisherSigner
2893
2960
  });
2894
2961
  }
@@ -2900,13 +2967,15 @@ async function publishRelayBootstrapRegistration(args) {
2900
2967
  registrationId: args.registrationId,
2901
2968
  channel: args.channel,
2902
2969
  ref: args.ref,
2903
- postType: args.postType,
2970
+ postType,
2904
2971
  profile: args.profile,
2905
2972
  version: args.version,
2906
2973
  ownerAddress,
2907
2974
  publisherAddress,
2908
2975
  authorization: ownerAuthorization,
2909
2976
  relayProof,
2977
+ compact,
2978
+ compactMultiaddrLimit: args.compactMultiaddrLimit,
2910
2979
  now: args.now,
2911
2980
  hasher: args.hasher
2912
2981
  });
@@ -2928,16 +2997,33 @@ async function publishRelayBootstrapRegistration(args) {
2928
2997
  let forgottenHashes = [];
2929
2998
  let forgetResult;
2930
2999
  if (args.forgetPrevious && args.registrationId) {
2931
- const previousPosts = await fetchAlephBootstrapPosts({
2932
- apiHost: args.apiHost ?? DEFAULT_ALEPH_API_HOST3,
2933
- channel: args.channel,
2934
- ref: args.ref,
2935
- postType: args.postType,
2936
- fetch: args.fetch
2937
- });
2938
- forgottenHashes = previousPosts.filter(
2939
- (post) => post.address?.toLowerCase() === args.sender.toLowerCase() && post.content?.registrationId === args.registrationId && (post.itemHash ?? post.hash) != null && (post.itemHash ?? post.hash) !== unsigned.item_hash
2940
- ).map((post) => post.itemHash ?? post.hash ?? "").filter(Boolean);
3000
+ const previousPostTypes = compact ? [postType, DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE] : [postType];
3001
+ const previousPosts = (await Promise.all(
3002
+ previousPostTypes.map(
3003
+ (previousPostType) => fetchAlephBootstrapPosts({
3004
+ apiHost: args.apiHost ?? DEFAULT_ALEPH_API_HOST3,
3005
+ channel: args.channel,
3006
+ ref: args.ref,
3007
+ postType: previousPostType,
3008
+ fetch: args.fetch
3009
+ })
3010
+ )
3011
+ )).flat();
3012
+ const forgottenHashSet = new Set(
3013
+ previousPosts.filter((post) => {
3014
+ const hash = post.itemHash ?? post.hash;
3015
+ if (!hash || hash === unsigned.item_hash) return false;
3016
+ if (post.address?.toLowerCase() !== args.sender.toLowerCase()) {
3017
+ return false;
3018
+ }
3019
+ const content2 = post.content;
3020
+ if (!content2) return false;
3021
+ if (content2.registrationId === args.registrationId) return true;
3022
+ if (content2.peerId !== args.peerId) return false;
3023
+ return !args.profile || content2.profile === args.profile;
3024
+ }).map((post) => post.itemHash ?? post.hash ?? "").filter(Boolean)
3025
+ );
3026
+ forgottenHashes = [...forgottenHashSet];
2941
3027
  if (forgottenHashes.length > 0) {
2942
3028
  forgetResult = await forgetAlephMessages({
2943
3029
  sender: args.sender,
@@ -2958,7 +3044,7 @@ async function publishRelayBootstrapRegistration(args) {
2958
3044
  response,
2959
3045
  httpStatus,
2960
3046
  publishedMultiaddrs: content.content?.multiaddrs ?? [],
2961
- publishedBrowserMultiaddrs: content.content?.browserMultiaddrs ?? [],
3047
+ publishedBrowserMultiaddrs: content.content?.browserMultiaddrs ?? content.content?.multiaddrs ?? [],
2962
3048
  forgottenHashes,
2963
3049
  forgetResult
2964
3050
  };
@@ -4108,6 +4194,8 @@ var SponsorRelayController = class {
4108
4194
  setupPort,
4109
4195
  proxyUrl: serviceProxyUrl,
4110
4196
  webauthnOrigin: serviceProxyUrl,
4197
+ serviceDid: bootstrapValidation.bootstrapPackage.serviceDid,
4198
+ serviceOrigin: bootstrapValidation.bootstrapPackage.serviceOrigin,
4111
4199
  adminDid: bootstrapValidation.bootstrapPackage.adminDid,
4112
4200
  bootstrapPackage: bootstrapValidation.bootstrapPackage,
4113
4201
  fetch: (url, init) => fetch(url, init),
@@ -5010,10 +5098,18 @@ var SponsorRelayController = class {
5010
5098
  const [nextBalance, rawInstances, rawBootstrapPosts] = await Promise.all([
5011
5099
  this.client.fetchBalance(this.state.wallet.address),
5012
5100
  this.state.showInstances ? this.client.fetchInstances(this.state.wallet.address) : Promise.resolve([]),
5013
- bootstrapEnabled ? fetchAlephBootstrapPosts({
5014
- apiHost: this.client.apiHost,
5015
- fetch
5016
- }).catch(() => []) : Promise.resolve([])
5101
+ bootstrapEnabled ? Promise.all(
5102
+ [
5103
+ DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE,
5104
+ DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE
5105
+ ].map(
5106
+ (postType) => fetchAlephBootstrapPosts({
5107
+ apiHost: this.client.apiHost,
5108
+ postType,
5109
+ fetch
5110
+ }).catch(() => [])
5111
+ )
5112
+ ).then((pages) => pages.flat()) : Promise.resolve([])
5017
5113
  ]);
5018
5114
  balance = nextBalance;
5019
5115
  instances = await Promise.all(
package/shared/index.d.ts CHANGED
@@ -330,7 +330,7 @@ declare const STALE_INSTANCE_ALLOCATION_COOLDOWN_MS: number;
330
330
  declare const RECENT_INSTANCE_RUNTIME_GRACE_MS: number;
331
331
  declare const DEPLOYMENT_PENDING_WARNING_MS: number;
332
332
 
333
- declare const UI_PACKAGE_VERSION = "0.6.3";
333
+ declare const UI_PACKAGE_VERSION = "0.6.5";
334
334
 
335
335
  interface RootfsRequiredPortForward {
336
336
  port: number;
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.6.3";
45
+ var UI_PACKAGE_VERSION = "0.6.5";
46
46
 
47
47
  // src/shared/format.ts
48
48
  function shortHash(value, head = 8, tail = 6) {
@@ -1297,7 +1297,9 @@ async function postBroadcastPayload2(body, options) {
1297
1297
  }
1298
1298
  async function broadcastAlephMessage2(message, options) {
1299
1299
  const requestBody = { sync: options.sync ?? false, message };
1300
- const maxAttempts = 3;
1300
+ const maxAttempts = Math.max(1, Number(options.attempts ?? 3));
1301
+ const retryDelayMs = Math.max(0, Number(options.retryDelayMs ?? 0));
1302
+ const sleep2 = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1301
1303
  for (let index = 0; index < maxAttempts; index += 1) {
1302
1304
  const result = await postBroadcastPayload2(requestBody, {
1303
1305
  apiHost: options.apiHost,
@@ -1310,6 +1312,9 @@ async function broadcastAlephMessage2(message, options) {
1310
1312
  if (!canRetry) {
1311
1313
  throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response ?? {})}`);
1312
1314
  }
1315
+ if (retryDelayMs > 0) {
1316
+ await sleep2(retryDelayMs);
1317
+ }
1313
1318
  }
1314
1319
  throw new Error("Broadcast failed: retry budget exhausted");
1315
1320
  }
@@ -2117,7 +2122,10 @@ async function deployInstance(args) {
2117
2122
  const { response, httpStatus } = await broadcastAlephMessage2(message, {
2118
2123
  apiHost: args.apiHost,
2119
2124
  sync: args.sync,
2120
- fetch: args.fetch
2125
+ fetch: args.fetch,
2126
+ attempts: args.broadcastAttempts,
2127
+ retryDelayMs: args.broadcastRetryDelayMs,
2128
+ sleep: args.broadcastSleep
2121
2129
  });
2122
2130
  const status = httpStatus === 202 ? "pending" : typeof response.message_status === "string" ? response.message_status : "unknown";
2123
2131
  args.onProgress?.({
@@ -2322,9 +2330,13 @@ async function configureUcanStore(args) {
2322
2330
  public_ipv4: args.hostIpv4,
2323
2331
  public_ipv6: args.publicIpv6 ?? void 0,
2324
2332
  proxy_url: args.proxyUrl ?? void 0,
2333
+ service_did: args.serviceDid ?? void 0,
2334
+ service_origin: args.serviceOrigin ?? void 0,
2335
+ public_storage_origin: args.publicStorageOrigin ?? void 0,
2325
2336
  webauthn_origin: args.webauthnOrigin ?? void 0,
2326
2337
  webauthn_origin_fallbacks: args.webauthnOriginFallbacks ?? void 0,
2327
2338
  admin_did: args.adminDid ?? void 0,
2339
+ admin_api_token: args.adminApiToken ?? void 0,
2328
2340
  bootstrap_package: args.bootstrapPackage ?? void 0,
2329
2341
  no_start: args.noStart === true ? true : void 0
2330
2342
  };
@@ -2423,13 +2435,15 @@ async function fetchUcGoPeerMetadata(args) {
2423
2435
  }
2424
2436
 
2425
2437
  // ../aleph-bootstrap/src/index.ts
2426
- import { recoverMessageAddress } from "viem";
2438
+ import { keccak256, recoverMessageAddress, stringToHex } from "viem";
2427
2439
  var DEFAULT_ALEPH_API_HOST3 = "https://api.aleph.im";
2428
2440
  var DEFAULT_ALEPH_BOOTSTRAP_CHANNEL = "simple-todo";
2429
2441
  var DEFAULT_ALEPH_BOOTSTRAP_REF = "simple-todo-bootstrap";
2430
2442
  var DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE = "relay-bootstrap";
2443
+ var DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE = "relay-bootstrap-v2";
2431
2444
  var DEFAULT_BOOTSTRAP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
2432
2445
  var DEFAULT_BOOTSTRAP_PAGINATION = 50;
2446
+ var DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT = 3;
2433
2447
  var RELAY_BOOTSTRAP_SIGNATURE_SCHEME = "personal_sign";
2434
2448
  function serializeOwnerAuthorizationPayload(payload) {
2435
2449
  return JSON.stringify({
@@ -2447,14 +2461,41 @@ function serializeOwnerAuthorizationPayload(payload) {
2447
2461
  function serializeRelayProofPayload(payload) {
2448
2462
  return JSON.stringify({
2449
2463
  peerId: payload.peerId,
2450
- multiaddrs: dedupeMultiaddrs(payload.multiaddrs),
2464
+ multiaddrs: payload.multiaddrs ? dedupeMultiaddrs(payload.multiaddrs) : void 0,
2451
2465
  browserMultiaddrs: payload.browserMultiaddrs ? dedupeMultiaddrs(payload.browserMultiaddrs) : void 0,
2466
+ multiaddrsHash: payload.multiaddrsHash,
2467
+ browserMultiaddrsHash: payload.browserMultiaddrsHash,
2452
2468
  registrationId: payload.registrationId,
2453
2469
  profile: payload.profile,
2454
2470
  version: payload.version,
2455
2471
  updatedAt: payload.updatedAt
2456
2472
  });
2457
2473
  }
2474
+ function relayBootstrapMultiaddrsHash(addrs) {
2475
+ return keccak256(stringToHex(JSON.stringify(dedupeMultiaddrs(addrs))));
2476
+ }
2477
+ function compactPostType(postType) {
2478
+ return (postType ?? DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE) === DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
2479
+ }
2480
+ function browserMultiaddrPreference(addr) {
2481
+ const normalized = addr.toLowerCase();
2482
+ if (normalized.includes("/tcp/443/tls/ws/")) return 0;
2483
+ if (normalized.includes("/tls/ws/")) return 1;
2484
+ if (normalized.includes("/webtransport/")) return 2;
2485
+ if (normalized.includes("/webrtc-direct/")) return 3;
2486
+ if (normalized.includes("/ws")) return 4;
2487
+ return 5;
2488
+ }
2489
+ function selectCompactRelayBootstrapMultiaddrs(addrs, limit = DEFAULT_BOOTSTRAP_COMPACT_MULTIADDR_LIMIT) {
2490
+ const entries = filterPublicMultiaddrs(addrs, {
2491
+ browserDialableOnly: true
2492
+ }).map((addr, index) => ({ addr, index }));
2493
+ entries.sort((left, right) => {
2494
+ const preference = browserMultiaddrPreference(left.addr) - browserMultiaddrPreference(right.addr);
2495
+ return preference !== 0 ? preference : left.index - right.index;
2496
+ });
2497
+ return entries.map((entry) => entry.addr).slice(0, Math.max(1, Math.floor(limit)));
2498
+ }
2458
2499
  function asTrimmedString(value) {
2459
2500
  return typeof value === "string" && value.trim() ? value.trim() : null;
2460
2501
  }
@@ -2610,8 +2651,10 @@ function normalizeRelayBootstrapProofPayload(value) {
2610
2651
  const browserMultiaddrs = Array.isArray(payload.browserMultiaddrs) ? payload.browserMultiaddrs.filter((entry) => typeof entry === "string") : void 0;
2611
2652
  return {
2612
2653
  peerId,
2613
- multiaddrs: dedupeMultiaddrs(multiaddrs),
2654
+ multiaddrs: multiaddrs.length > 0 ? dedupeMultiaddrs(multiaddrs) : void 0,
2614
2655
  browserMultiaddrs: browserMultiaddrs ? dedupeMultiaddrs(browserMultiaddrs) : void 0,
2656
+ multiaddrsHash: asTrimmedString(payload.multiaddrsHash) ?? void 0,
2657
+ browserMultiaddrsHash: asTrimmedString(payload.browserMultiaddrsHash) ?? void 0,
2615
2658
  registrationId: asTrimmedString(payload.registrationId) ?? void 0,
2616
2659
  profile: asTrimmedString(payload.profile) ?? void 0,
2617
2660
  version: asTrimmedString(payload.version) ?? void 0,
@@ -2643,16 +2686,24 @@ function normalizeRelayBootstrapPostRecord(value) {
2643
2686
  function buildRelayBootstrapPostContent(args) {
2644
2687
  const now = args.now ?? Date.now() / 1e3;
2645
2688
  const updatedAt = args.now ?? Date.now();
2689
+ const postType = args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE;
2690
+ const compact = args.compact ?? compactPostType(postType);
2691
+ const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
2692
+ const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
2693
+ browserDialableOnly: true
2694
+ }) : void 0;
2695
+ const compactMultiaddrs = selectCompactRelayBootstrapMultiaddrs(
2696
+ fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
2697
+ args.compactMultiaddrLimit
2698
+ );
2646
2699
  return {
2647
- type: args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE,
2700
+ type: postType,
2648
2701
  address: args.sender,
2649
2702
  ...args.ref ? { ref: args.ref } : {},
2650
2703
  content: {
2651
2704
  peerId: args.peerId,
2652
- multiaddrs: filterPublicMultiaddrs(args.multiaddrs),
2653
- browserMultiaddrs: args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
2654
- browserDialableOnly: true
2655
- }) : void 0,
2705
+ multiaddrs: compact ? compactMultiaddrs : fullMultiaddrs,
2706
+ browserMultiaddrs: compact ? void 0 : fullBrowserMultiaddrs,
2656
2707
  registrationId: args.registrationId,
2657
2708
  profile: args.profile,
2658
2709
  version: args.version,
@@ -2682,6 +2733,8 @@ async function createRelayBootstrapPost(args) {
2682
2733
  publisherAddress: args.publisherAddress,
2683
2734
  authorization: args.authorization,
2684
2735
  relayProof: args.relayProof,
2736
+ compact: args.compact,
2737
+ compactMultiaddrLimit: args.compactMultiaddrLimit,
2685
2738
  now: nowMillis
2686
2739
  });
2687
2740
  itemContent.time = nowSeconds;
@@ -2719,12 +2772,20 @@ async function signRelayBootstrapAuthorization(args) {
2719
2772
  };
2720
2773
  }
2721
2774
  async function signRelayBootstrapProof(args) {
2775
+ const fullMultiaddrs = filterPublicMultiaddrs(args.multiaddrs);
2776
+ const fullBrowserMultiaddrs = args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
2777
+ browserDialableOnly: true
2778
+ }) : void 0;
2779
+ const compactMultiaddrs = selectCompactRelayBootstrapMultiaddrs(
2780
+ fullBrowserMultiaddrs && fullBrowserMultiaddrs.length > 0 ? fullBrowserMultiaddrs : fullMultiaddrs,
2781
+ args.compactMultiaddrLimit
2782
+ );
2783
+ const compact = args.compact ?? false;
2722
2784
  const payload = {
2723
2785
  peerId: args.peerId,
2724
- multiaddrs: filterPublicMultiaddrs(args.multiaddrs),
2725
- browserMultiaddrs: args.browserMultiaddrs ? filterPublicMultiaddrs(args.browserMultiaddrs, {
2726
- browserDialableOnly: true
2727
- }) : void 0,
2786
+ multiaddrs: compact ? void 0 : fullMultiaddrs,
2787
+ browserMultiaddrs: compact ? void 0 : fullBrowserMultiaddrs,
2788
+ multiaddrsHash: compact ? relayBootstrapMultiaddrsHash(compactMultiaddrs) : void 0,
2728
2789
  registrationId: args.registrationId,
2729
2790
  profile: args.profile,
2730
2791
  version: args.version,
@@ -2771,7 +2832,7 @@ async function fetchAlephBootstrapPosts(options = {}) {
2771
2832
  throw new Error(`Aleph bootstrap post lookup failed: ${response.status}`);
2772
2833
  }
2773
2834
  const payload = await response.json();
2774
- return (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null);
2835
+ return (payload.posts ?? []).map((entry) => normalizeRelayBootstrapPostRecord(entry)).filter((entry) => entry != null).sort((left, right) => compareRelayBootstrapPostRecency(right, left));
2775
2836
  }
2776
2837
  function compareRelayBootstrapPostRecency(left, right) {
2777
2838
  const leftUpdatedAt = left.content?.updatedAt ?? 0;
@@ -2818,12 +2879,14 @@ async function waitForRelayBootstrapRegistration(args) {
2818
2879
  const expectedSender = args.sender?.trim().toLowerCase() || null;
2819
2880
  const expectedRegistrationId = args.registrationId?.trim() || null;
2820
2881
  const expectedPeerId = args.peerId?.trim() || null;
2882
+ const postType = args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
2883
+ const sleepImpl = args.sleep ?? sleep;
2821
2884
  for (let attempt = 0; attempt < attempts; attempt += 1) {
2822
2885
  const posts = await fetchAlephBootstrapPosts({
2823
2886
  apiHost: args.apiHost ?? DEFAULT_ALEPH_API_HOST3,
2824
2887
  channel: args.channel,
2825
2888
  ref: args.ref,
2826
- postType: args.postType,
2889
+ postType,
2827
2890
  fetch: args.fetch
2828
2891
  });
2829
2892
  const currentPosts = selectCurrentRelayBootstrapPosts(posts);
@@ -2854,7 +2917,7 @@ async function waitForRelayBootstrapRegistration(args) {
2854
2917
  }
2855
2918
  args.onAttempt?.(null, attempt + 1, attempts);
2856
2919
  if (attempt < attempts - 1) {
2857
- await sleep(delayMs);
2920
+ await sleepImpl(delayMs);
2858
2921
  }
2859
2922
  }
2860
2923
  return null;
@@ -2862,6 +2925,8 @@ async function waitForRelayBootstrapRegistration(args) {
2862
2925
  async function publishRelayBootstrapRegistration(args) {
2863
2926
  const publisherAddress = args.publisherAddress ?? args.sender;
2864
2927
  const ownerAddress = args.ownerAddress;
2928
+ const postType = args.postType ?? DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
2929
+ const compact = postType === DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE;
2865
2930
  let ownerAuthorization = args.ownerAuthorization;
2866
2931
  let relayProof = args.relayProof;
2867
2932
  if (!ownerAuthorization && ownerAddress && args.ownerSigner) {
@@ -2888,6 +2953,8 @@ async function publishRelayBootstrapRegistration(args) {
2888
2953
  profile: args.profile,
2889
2954
  version: args.version,
2890
2955
  updatedAt: args.now ?? Date.now(),
2956
+ compact,
2957
+ compactMultiaddrLimit: args.compactMultiaddrLimit,
2891
2958
  signer: args.publisherSigner
2892
2959
  });
2893
2960
  }
@@ -2899,13 +2966,15 @@ async function publishRelayBootstrapRegistration(args) {
2899
2966
  registrationId: args.registrationId,
2900
2967
  channel: args.channel,
2901
2968
  ref: args.ref,
2902
- postType: args.postType,
2969
+ postType,
2903
2970
  profile: args.profile,
2904
2971
  version: args.version,
2905
2972
  ownerAddress,
2906
2973
  publisherAddress,
2907
2974
  authorization: ownerAuthorization,
2908
2975
  relayProof,
2976
+ compact,
2977
+ compactMultiaddrLimit: args.compactMultiaddrLimit,
2909
2978
  now: args.now,
2910
2979
  hasher: args.hasher
2911
2980
  });
@@ -2927,16 +2996,33 @@ async function publishRelayBootstrapRegistration(args) {
2927
2996
  let forgottenHashes = [];
2928
2997
  let forgetResult;
2929
2998
  if (args.forgetPrevious && args.registrationId) {
2930
- const previousPosts = await fetchAlephBootstrapPosts({
2931
- apiHost: args.apiHost ?? DEFAULT_ALEPH_API_HOST3,
2932
- channel: args.channel,
2933
- ref: args.ref,
2934
- postType: args.postType,
2935
- fetch: args.fetch
2936
- });
2937
- forgottenHashes = previousPosts.filter(
2938
- (post) => post.address?.toLowerCase() === args.sender.toLowerCase() && post.content?.registrationId === args.registrationId && (post.itemHash ?? post.hash) != null && (post.itemHash ?? post.hash) !== unsigned.item_hash
2939
- ).map((post) => post.itemHash ?? post.hash ?? "").filter(Boolean);
2999
+ const previousPostTypes = compact ? [postType, DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE] : [postType];
3000
+ const previousPosts = (await Promise.all(
3001
+ previousPostTypes.map(
3002
+ (previousPostType) => fetchAlephBootstrapPosts({
3003
+ apiHost: args.apiHost ?? DEFAULT_ALEPH_API_HOST3,
3004
+ channel: args.channel,
3005
+ ref: args.ref,
3006
+ postType: previousPostType,
3007
+ fetch: args.fetch
3008
+ })
3009
+ )
3010
+ )).flat();
3011
+ const forgottenHashSet = new Set(
3012
+ previousPosts.filter((post) => {
3013
+ const hash = post.itemHash ?? post.hash;
3014
+ if (!hash || hash === unsigned.item_hash) return false;
3015
+ if (post.address?.toLowerCase() !== args.sender.toLowerCase()) {
3016
+ return false;
3017
+ }
3018
+ const content2 = post.content;
3019
+ if (!content2) return false;
3020
+ if (content2.registrationId === args.registrationId) return true;
3021
+ if (content2.peerId !== args.peerId) return false;
3022
+ return !args.profile || content2.profile === args.profile;
3023
+ }).map((post) => post.itemHash ?? post.hash ?? "").filter(Boolean)
3024
+ );
3025
+ forgottenHashes = [...forgottenHashSet];
2940
3026
  if (forgottenHashes.length > 0) {
2941
3027
  forgetResult = await forgetAlephMessages({
2942
3028
  sender: args.sender,
@@ -2957,7 +3043,7 @@ async function publishRelayBootstrapRegistration(args) {
2957
3043
  response,
2958
3044
  httpStatus,
2959
3045
  publishedMultiaddrs: content.content?.multiaddrs ?? [],
2960
- publishedBrowserMultiaddrs: content.content?.browserMultiaddrs ?? [],
3046
+ publishedBrowserMultiaddrs: content.content?.browserMultiaddrs ?? content.content?.multiaddrs ?? [],
2961
3047
  forgottenHashes,
2962
3048
  forgetResult
2963
3049
  };
@@ -4107,6 +4193,8 @@ var SponsorRelayController = class {
4107
4193
  setupPort,
4108
4194
  proxyUrl: serviceProxyUrl,
4109
4195
  webauthnOrigin: serviceProxyUrl,
4196
+ serviceDid: bootstrapValidation.bootstrapPackage.serviceDid,
4197
+ serviceOrigin: bootstrapValidation.bootstrapPackage.serviceOrigin,
4110
4198
  adminDid: bootstrapValidation.bootstrapPackage.adminDid,
4111
4199
  bootstrapPackage: bootstrapValidation.bootstrapPackage,
4112
4200
  fetch: (url, init) => fetch(url, init),
@@ -5009,10 +5097,18 @@ var SponsorRelayController = class {
5009
5097
  const [nextBalance, rawInstances, rawBootstrapPosts] = await Promise.all([
5010
5098
  this.client.fetchBalance(this.state.wallet.address),
5011
5099
  this.state.showInstances ? this.client.fetchInstances(this.state.wallet.address) : Promise.resolve([]),
5012
- bootstrapEnabled ? fetchAlephBootstrapPosts({
5013
- apiHost: this.client.apiHost,
5014
- fetch
5015
- }).catch(() => []) : Promise.resolve([])
5100
+ bootstrapEnabled ? Promise.all(
5101
+ [
5102
+ DEFAULT_ALEPH_BOOTSTRAP_COMPACT_POST_TYPE,
5103
+ DEFAULT_ALEPH_BOOTSTRAP_POST_TYPE
5104
+ ].map(
5105
+ (postType) => fetchAlephBootstrapPosts({
5106
+ apiHost: this.client.apiHost,
5107
+ postType,
5108
+ fetch
5109
+ }).catch(() => [])
5110
+ )
5111
+ ).then((pages) => pages.flat()) : Promise.resolve([])
5016
5112
  ]);
5017
5113
  balance = nextBalance;
5018
5114
  instances = await Promise.all(