@glyphteck/veyl 0.32.1 → 0.35.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.
Files changed (3) hide show
  1. package/dist/cli.js +585 -353
  2. package/dist/index.js +585 -353
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6216,7 +6216,7 @@ var init_seed = __esm(() => {
6216
6216
  });
6217
6217
 
6218
6218
  // ../../shared/config.js
6219
- var MS_PER_SECOND = 1000, MINUTE_MS, HOUR_MS, DAY_MS, KIB_BYTES = 1024, MIB_BYTES, SATS_PER_BITCOIN = 100000000n, USERNAME_MAX_CHARS = 12, PASSWORD_MIN_CHARS = 12, PASSWORD_MAX_CHARS = 64, AUTOLOCK_MIN_MINUTES = 1, AUTOLOCK_MAX_MINUTES = 60, LOCAL_CACHE_WRITE_DELAY_MS = 350, LOCAL_MEDIA_CACHE_MAX_BYTES, LOCAL_MEDIA_CACHE_MAX_ITEMS = 1000, LOCAL_MEDIA_ACCESS_TOUCH_MIN_MS, LOCAL_PROFILE_CACHE_MAX_ITEMS = 500, LOCAL_PROFILE_CACHE_MAX_AGE_MS, LOCAL_CHAT_CACHE_MAX_ITEMS = 1000, LOCAL_CHAT_MESSAGE_CACHE_MAX_VISIBLE = 1000, LOCAL_AVATAR_CACHE_MAX_BYTES, LOCAL_AVATAR_CACHE_MAX_AGE_MS, AVATAR_IMAGE_MAX_BYTES, CHAT_READY_DOWNLOAD_MAX_BYTES, CHAT_MESSAGE_FILE_CACHE_MAX_BYTES, IDLE_CALLBACK_MIN_TIMEOUT_MS = 50, ATTACHMENT_CACHE_IDLE_TIMEOUT_MS = 2500, ATTACHMENT_CACHE_FALLBACK_DELAY_MS = 250, CHAT_UNSAVED_TTL_DAYS = 21, CHAT_UNSAVED_TTL_MS, CHAT_AFTER_SEEN_MS, CHAT_MEDIA_TTL_DAYS, CHAT_MEDIA_TTL_MS, CHAT_UPLOAD_MAX_BYTES, CHAT_AUDIO_TRANSCODE_BITRATE_BPS = 128000, CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS = 768000, CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS, CHAT_MESSAGE_BATCH_SIZE = 40, CHAT_MESSAGE_QUERY_MAX_DOCS = 120, CHAT_TTL_CLIENT_DELETE_GRACE_MS, CHAT_TTL_DELETE_CHUNK_SIZE = 40, CHAT_DELETE_SCAN_BATCH_SIZE = 200, CHAT_TTL_WRITE_BATCH_SIZE = 400, CHAT_DELETE_WRITE_BATCH_SIZE = 400, CHAT_BATCH_CLEANUP_IDLE_TIMEOUT_MS = 1500, CHAT_BATCH_CLEANUP_IDLE_DELAY_MS = 250, CHAT_LIST_PAGE_SIZE = 20, CHAT_LIST_LIVE_COUNT, CHAT_INBOX_PING_PAGE_SIZE = 25, CHAT_LIST_CACHE_WRITE_DELAY_MS = 1500, CHAT_LIST_SNAPSHOT_COALESCE_MS = 80, CHAT_LIST_STATUS_CACHE_MS, CHAT_LIST_LISTENER_RETRY_MS = 1000, CHAT_TOP_WARM_COUNT = 0, CHAT_EAGER_WARM_COUNT = 0, CHAT_WARM_DELAY_MS = 3000, CHAT_WARM_BATCH_SIZE, CHAT_MESSAGE_VIEW_CACHE_SIZE = 30, CHAT_MEDIA_WARM_MESSAGES_PER_CHAT, CHAT_MEDIA_WARM_START_DELAY_MS = 600, CHAT_MEDIA_WARM_STEP_DELAY_MS = 120, CHAT_MEDIA_WARM_TYPES, CHAT_MEDIA_WARM_MAX_BYTES = 0, CHAT_READ_WRITE_INTERVAL_MS, CHAT_SEND_QUEUE_RATE_LIMIT_COUNT = 12, CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS, CHAT_MESSAGE_BODY_MAX_BYTES, CHAT_LAST_MESSAGE_BODY_MAX_BYTES, CHAT_SETTINGS_BODY_MAX_BYTES, BOT_WALLET_CLAIM_POLL_MS, BOT_WALLET_IDLE_CLAIM_POLL_MS, CHAT_MAX_TEXT_CHARS = 2048, CHAT_MAX_REACTIONS = 2, CHAT_PAIR_CACHE_LIMIT = 256, SEARCH_DEBOUNCE_MS = 300, SEARCH_USERNAME_LIMIT = 15, SEARCH_ROLE_LIMIT = 15, RECENT_PEER_REFRESH_LIMIT = 50, RECENT_PEER_REFRESH_DELAY_MS = 250, RECENT_PEER_REFRESH_INTERVAL_MS, RECENT_PEER_REFRESH_THROTTLE_MS = 120, BAN_REFRESH_GRACE_MS = 50, REQUEST_MONEY_MAX_SATS, WALLET_TRANSFER_POLL_MS, WALLET_ACTIVE_TRANSFER_REFRESH_MS, WALLET_IDLE_ACTIVE_TRANSFER_REFRESH_MS, WALLET_VISIBLE_TRANSFER_DISCOVERY_WINDOW_MS, WALLET_ACTIVE_TRANSFER_EMPTY_SCAN_LIMIT = 8, WALLET_CACHE_HYDRATE_DELAY_MS = 0, WALLET_BOOT_REFRESH_DELAY_MS = 500, WALLET_BOOT_CACHED_REFRESH_DELAY_MS = 2500, WALLET_REGTEST_DEPOSIT_CLAIM_POLL_MS, WALLET_MAINNET_DEPOSIT_CLAIM_POLL_MS, WALLET_BALANCE_EVENT_COALESCE_MS, WALLET_INCOMING_UPDATE_COALESCE_MS = 250, WALLET_AUTO_CLAIM_MAX_FEE_SATS = 5000, WALLET_CLAIM_PAGE_SIZE = 100, WALLET_PENDING_TRANSFER_CLAIM_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_STATUS_BATCH_SIZE = 2, WALLET_PENDING_TRANSFER_COLD_REFRESH_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_COLD_REFRESH_INTERVAL_MS, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_QUEUE = 3, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_QUEUE = 10, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_SIZE = 2, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_SIZE = 3, WALLET_PENDING_TRANSFER_SLOW_CLAIM_MS, WALLET_PENDING_TRANSFER_CLAIM_COOLDOWN_MS, WALLET_SDK_BACKGROUND_QUIET_MS, WALLET_RECENT_TRANSFER_LIMIT = 100, WALLET_TRANSFER_CACHE_LIMIT = 1e4, WALLET_TRANSFER_PAGE_LIMIT = 100, WALLET_TRANSFER_FETCH_THROTTLE_MS = 150, WALLET_PENDING_TRANSFER_CLAIM_RETRY_MS, WALLET_SENT_TRANSFER_REFRESH_DELAY_MS, WALLET_PENDING_TRANSFER_BOOT_GRACE_MS, WALLET_PENDING_TRANSFER_HOT_AGE_MS, WALLET_PENDING_TRANSFER_WARM_AGE_MS, WALLET_PENDING_TRANSFER_WARM_RETRY_MS, WALLET_PENDING_TRANSFER_STALE_RETRY_MS, WALLET_PENDING_TRANSFER_STUCK_RETRY_MS, WALLET_PENDING_TRANSFER_DORMANT_RETRY_MS, WALLET_TRANSFER_CACHE_WRITE_DELAY_MS;
6219
+ var MS_PER_SECOND = 1000, MINUTE_MS, HOUR_MS, DAY_MS, KIB_BYTES = 1024, MIB_BYTES, SATS_PER_BITCOIN = 100000000n, USERNAME_MAX_CHARS = 12, PASSWORD_MIN_CHARS = 12, PASSWORD_MAX_CHARS = 64, AUTOLOCK_MIN_MINUTES = 1, AUTOLOCK_MAX_MINUTES = 60, LOCAL_CACHE_WRITE_DELAY_MS = 350, LOCAL_MEDIA_CACHE_MAX_BYTES, LOCAL_MEDIA_CACHE_MAX_ITEMS = 1000, LOCAL_MEDIA_ACCESS_TOUCH_MIN_MS, LOCAL_PROFILE_CACHE_MAX_ITEMS = 500, LOCAL_PROFILE_CACHE_MAX_AGE_MS, LOCAL_CHAT_CACHE_MAX_ITEMS = 1000, LOCAL_CHAT_MESSAGE_CACHE_MAX_VISIBLE = 1000, LOCAL_AVATAR_CACHE_MAX_BYTES, LOCAL_AVATAR_CACHE_MAX_AGE_MS, AVATAR_IMAGE_MAX_BYTES, CHAT_READY_DOWNLOAD_MAX_BYTES, CHAT_MESSAGE_FILE_CACHE_MAX_BYTES, IDLE_CALLBACK_MIN_TIMEOUT_MS = 50, ATTACHMENT_CACHE_IDLE_TIMEOUT_MS = 2500, ATTACHMENT_CACHE_FALLBACK_DELAY_MS = 250, CHAT_UNSAVED_TTL_DAYS = 21, CHAT_UNSAVED_TTL_MS, CHAT_AFTER_SEEN_MS, CHAT_MEDIA_TTL_DAYS, CHAT_MEDIA_TTL_MS, CHAT_UPLOAD_MAX_BYTES, CHAT_AUDIO_TRANSCODE_BITRATE_BPS = 128000, CHAT_VIDEO_TRANSCODE_VIDEO_BITRATE_BPS = 3000000, CHAT_VIDEO_TRANSCODE_AUDIO_BITRATE_BPS, CHAT_MESSAGE_BATCH_SIZE = 40, CHAT_MESSAGE_QUERY_MAX_DOCS = 120, CHAT_TTL_CLIENT_DELETE_GRACE_MS, CHAT_TTL_DELETE_CHUNK_SIZE = 40, CHAT_DELETE_SCAN_BATCH_SIZE = 200, CHAT_TTL_WRITE_BATCH_SIZE = 400, CHAT_DELETE_WRITE_BATCH_SIZE = 400, CHAT_BATCH_CLEANUP_IDLE_TIMEOUT_MS = 1500, CHAT_BATCH_CLEANUP_IDLE_DELAY_MS = 250, CHAT_LIST_PAGE_SIZE = 20, CHAT_LIST_LIVE_COUNT, CHAT_INBOX_PING_PAGE_SIZE = 25, CHAT_LIST_CACHE_WRITE_DELAY_MS = 1500, CHAT_LIST_SNAPSHOT_COALESCE_MS = 80, CHAT_LIST_STATUS_CACHE_MS, CHAT_LIST_LISTENER_RETRY_MS = 1000, CHAT_TOP_WARM_COUNT = 0, CHAT_EAGER_WARM_COUNT = 0, CHAT_WARM_DELAY_MS = 3000, CHAT_WARM_BATCH_SIZE, CHAT_MESSAGE_VIEW_CACHE_SIZE = 30, CHAT_MEDIA_WARM_MESSAGES_PER_CHAT, CHAT_MEDIA_WARM_START_DELAY_MS = 600, CHAT_MEDIA_WARM_STEP_DELAY_MS = 120, CHAT_MEDIA_WARM_TYPES, CHAT_MEDIA_WARM_MAX_BYTES = 0, CHAT_READ_WRITE_INTERVAL_MS, CHAT_SEND_QUEUE_RATE_LIMIT_COUNT = 12, CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS, CHAT_MESSAGE_BODY_MAX_BYTES, CHAT_LAST_MESSAGE_BODY_MAX_BYTES, CHAT_SETTINGS_BODY_MAX_BYTES, BOT_WALLET_CLAIM_POLL_MS, BOT_WALLET_IDLE_CLAIM_POLL_MS, CHAT_MAX_TEXT_CHARS = 2048, CHAT_MAX_REACTIONS = 2, CHAT_PAIR_CACHE_LIMIT = 256, SEARCH_DEBOUNCE_MS = 300, SEARCH_USERNAME_LIMIT = 15, SEARCH_ROLE_LIMIT = 15, RECENT_PEER_REFRESH_LIMIT = 50, RECENT_PEER_REFRESH_DELAY_MS = 250, RECENT_PEER_REFRESH_INTERVAL_MS, RECENT_PEER_REFRESH_THROTTLE_MS = 120, BAN_REFRESH_GRACE_MS = 50, REQUEST_MONEY_MAX_SATS, WALLET_TRANSFER_POLL_MS, WALLET_ACTIVE_TRANSFER_REFRESH_MS, WALLET_IDLE_ACTIVE_TRANSFER_REFRESH_MS, WALLET_VISIBLE_TRANSFER_DISCOVERY_WINDOW_MS, WALLET_ACTIVE_TRANSFER_EMPTY_SCAN_LIMIT = 8, WALLET_CACHE_HYDRATE_DELAY_MS = 0, WALLET_BOOT_REFRESH_DELAY_MS = 500, WALLET_BOOT_CACHED_REFRESH_DELAY_MS = 2500, WALLET_REGTEST_DEPOSIT_CLAIM_POLL_MS, WALLET_MAINNET_DEPOSIT_CLAIM_POLL_MS, WALLET_BALANCE_EVENT_COALESCE_MS, WALLET_INCOMING_UPDATE_COALESCE_MS = 250, WALLET_AUTO_CLAIM_MAX_FEE_SATS = 5000, WALLET_CLAIM_PAGE_SIZE = 100, WALLET_PENDING_TRANSFER_CLAIM_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_STATUS_BATCH_SIZE = 2, WALLET_PENDING_TRANSFER_COLD_REFRESH_BATCH_SIZE = 50, WALLET_PENDING_TRANSFER_COLD_REFRESH_INTERVAL_MS, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_QUEUE = 3, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_QUEUE = 10, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_SMALL_SIZE = 2, WALLET_PENDING_TRANSFER_ADAPTIVE_BATCH_LARGE_SIZE = 3, WALLET_PENDING_TRANSFER_SLOW_CLAIM_MS, WALLET_PENDING_TRANSFER_CLAIM_COOLDOWN_MS, WALLET_SDK_BACKGROUND_QUIET_MS, WALLET_RECENT_TRANSFER_LIMIT = 100, WALLET_TRANSFER_CACHE_LIMIT = 1e4, WALLET_TRANSFER_PAGE_LIMIT = 100, WALLET_TRANSFER_FETCH_THROTTLE_MS = 150, WALLET_PENDING_TRANSFER_CLAIM_RETRY_MS, WALLET_SENT_TRANSFER_REFRESH_DELAY_MS, WALLET_PENDING_TRANSFER_BOOT_GRACE_MS, WALLET_PENDING_TRANSFER_HOT_AGE_MS, WALLET_PENDING_TRANSFER_WARM_AGE_MS, WALLET_PENDING_TRANSFER_WARM_RETRY_MS, WALLET_PENDING_TRANSFER_STALE_RETRY_MS, WALLET_PENDING_TRANSFER_STUCK_RETRY_MS, WALLET_PENDING_TRANSFER_DORMANT_RETRY_MS, WALLET_TRANSFER_CACHE_WRITE_DELAY_MS;
6220
6220
  var init_config = __esm(() => {
6221
6221
  MINUTE_MS = 60 * MS_PER_SECOND;
6222
6222
  HOUR_MS = 60 * MINUTE_MS;
@@ -7743,20 +7743,17 @@ function expectedSigningKeys(pair, signingKeysByChatKey) {
7743
7743
  }
7744
7744
  return values;
7745
7745
  }
7746
- async function verifyExpectedSignatureBeforeOpen(pair, sig, bytes, options = {}) {
7746
+ async function preverifyExpectedSigner(pair, sig, bytes, options = {}) {
7747
7747
  if (!options?.crypto?.preverifyBeforeOpen) {
7748
- return true;
7749
- }
7750
- const signingKeysByChatKey = expectedSigningKeys(pair, options?.signingKeysByChatKey);
7751
- if (signingKeysByChatKey.length < 2) {
7752
- return true;
7748
+ return "";
7753
7749
  }
7754
- for (const signingPublicKey of signingKeysByChatKey) {
7750
+ const signingPublicKeys = expectedSigningKeys(pair, options?.signingKeysByChatKey);
7751
+ for (const signingPublicKey of signingPublicKeys) {
7755
7752
  if (await verifyMsgSignature(signingPublicKey, sig, bytes, options)) {
7756
- return true;
7753
+ return signingPublicKey;
7757
7754
  }
7758
7755
  }
7759
- return false;
7756
+ return null;
7760
7757
  }
7761
7758
  async function sealMsg(pair, message, options = {}) {
7762
7759
  const head = getHead(pair, message);
@@ -7777,14 +7774,16 @@ async function openMsg(pair, data, options = {}) {
7777
7774
  const { suite, flags, nonce, sig, ct } = unpackMsgBody(data.body);
7778
7775
  const aad = getMsgAad(pair, head, suite, flags);
7779
7776
  const signatureInput = getSignatureInput(aad, nonce, ct);
7780
- if (!await verifyExpectedSignatureBeforeOpen(pair, sig, signatureInput, options)) {
7777
+ const preverifiedSigner = await preverifyExpectedSigner(pair, sig, signatureInput, options);
7778
+ if (preverifiedSigner == null) {
7781
7779
  throw new Error("invalid chat action signature");
7782
7780
  }
7783
7781
  const plaintext = await openMsgPlaintext(pair, nonce, ct, aad, options);
7784
7782
  try {
7785
7783
  const action = JSON.parse(decoder.decode(plaintext));
7786
7784
  const normalized = normalizeOpenedAction(pair, head, action, options);
7787
- if (!await verifyMsgSignature(normalized.signingPublicKey, sig, signatureInput, options)) {
7785
+ const validSignature = preverifiedSigner ? normalized.signingPublicKey === preverifiedSigner : await verifyMsgSignature(normalized.signingPublicKey, sig, signatureInput, options);
7786
+ if (!validSignature) {
7788
7787
  throw new Error("invalid chat action signature");
7789
7788
  }
7790
7789
  return {
@@ -7858,40 +7857,120 @@ var init_protocol = __esm(() => {
7858
7857
  });
7859
7858
 
7860
7859
  // ../../shared/chat/pairs.js
7860
+ function accountGeneration(chatPK) {
7861
+ return accountGenerations.get(cleanText(chatPK)) || 0;
7862
+ }
7861
7863
  function getChatPairKey(chatPK, peerChatPK, chatId = "") {
7862
7864
  if (!chatPK || !peerChatPK)
7863
7865
  return null;
7864
7866
  return `${cleanText(chatPK)}|${cleanText(peerChatPK)}|${cleanText(chatId)}`;
7865
7867
  }
7866
- function clearChatPairCache() {
7867
- for (const pair of pairCache.values()) {
7868
- closeChatPair(pair);
7868
+ function closeEntry(entry) {
7869
+ if (!entry || entry.closed || entry.leases > 0) {
7870
+ return;
7871
+ }
7872
+ entry.closed = true;
7873
+ closeChatPair(entry.pair);
7874
+ }
7875
+ function retireEntry(entry) {
7876
+ if (!entry || entry.retired) {
7877
+ return;
7878
+ }
7879
+ entry.retired = true;
7880
+ closeEntry(entry);
7881
+ }
7882
+ function releaseEntry(entry) {
7883
+ if (!entry || entry.leases <= 0) {
7884
+ return;
7885
+ }
7886
+ entry.leases -= 1;
7887
+ if (entry.retired) {
7888
+ closeEntry(entry);
7869
7889
  }
7870
- pairCache.clear();
7871
7890
  }
7872
- async function getCachedPair(chatPK, chatPrivKey, peerChatPK, options = {}) {
7891
+ function evictOverflow() {
7892
+ while (pairCache.size > MAX_PAIR_CACHE) {
7893
+ const firstKey = pairCache.keys().next().value;
7894
+ if (!firstKey) {
7895
+ return;
7896
+ }
7897
+ const entry = pairCache.get(firstKey);
7898
+ pairCache.delete(firstKey);
7899
+ retireEntry(entry);
7900
+ }
7901
+ }
7902
+ function clearChatPairCache(chatPK = "") {
7903
+ const account = cleanText(chatPK);
7904
+ if (!account) {
7905
+ cacheGeneration += 1;
7906
+ accountGenerations.clear();
7907
+ for (const entry of pairCache.values()) {
7908
+ retireEntry(entry);
7909
+ }
7910
+ pairCache.clear();
7911
+ return;
7912
+ }
7913
+ accountGenerations.set(account, accountGeneration(account) + 1);
7914
+ for (const [key, entry] of pairCache) {
7915
+ if (entry.account !== account) {
7916
+ continue;
7917
+ }
7918
+ pairCache.delete(key);
7919
+ retireEntry(entry);
7920
+ }
7921
+ }
7922
+ async function acquireCachedPair(chatPK, chatPrivKey, peerChatPK, options = {}) {
7923
+ const account = cleanText(chatPK);
7873
7924
  const chatId = cleanText(options?.chatId);
7874
7925
  const key = getChatPairKey(chatPK, peerChatPK, chatId);
7926
+ const generation = cacheGeneration;
7927
+ const accountVersion = accountGeneration(account);
7875
7928
  if (!key) {
7876
- return openChatPair(chatPK, chatPrivKey, peerChatPK, { chatId });
7929
+ const pair = await openChatPair(chatPK, chatPrivKey, peerChatPK, { chatId });
7930
+ const entry2 = { pair, leases: 1, retired: true, closed: false };
7931
+ return {
7932
+ pair,
7933
+ release: () => releaseEntry(entry2)
7934
+ };
7877
7935
  }
7878
- const cached = pairCache.get(key);
7879
- if (cached)
7880
- return cached;
7881
- const pair = await openChatPair(chatPK, chatPrivKey, peerChatPK, { chatId });
7882
- pairCache.set(key, pair);
7883
- if (pairCache.size > MAX_PAIR_CACHE) {
7884
- const firstKey = pairCache.keys().next().value;
7885
- if (firstKey) {
7886
- closeChatPair(pairCache.get(firstKey));
7887
- pairCache.delete(firstKey);
7936
+ let entry = pairCache.get(key);
7937
+ if (!entry) {
7938
+ const pair = await openChatPair(chatPK, chatPrivKey, peerChatPK, { chatId });
7939
+ if (generation !== cacheGeneration || accountVersion !== accountGeneration(account)) {
7940
+ const staleEntry = { pair, leases: 1, retired: true, closed: false };
7941
+ return {
7942
+ pair,
7943
+ release: () => releaseEntry(staleEntry)
7944
+ };
7888
7945
  }
7946
+ entry = pairCache.get(key);
7947
+ if (entry) {
7948
+ closeChatPair(pair);
7949
+ } else {
7950
+ entry = { account, pair, leases: 0, retired: false, closed: false };
7951
+ pairCache.set(key, entry);
7952
+ evictOverflow();
7953
+ }
7954
+ } else {
7955
+ pairCache.delete(key);
7956
+ pairCache.set(key, entry);
7957
+ }
7958
+ entry.leases += 1;
7959
+ return {
7960
+ pair: entry.pair,
7961
+ release: () => releaseEntry(entry)
7962
+ };
7963
+ }
7964
+ async function withCachedPair(chatPK, chatPrivKey, peerChatPK, options, runWithPair) {
7965
+ const { pair, release } = await acquireCachedPair(chatPK, chatPrivKey, peerChatPK, options);
7966
+ try {
7967
+ return await runWithPair(pair);
7968
+ } finally {
7969
+ release();
7889
7970
  }
7890
- return pair;
7891
7971
  }
7892
7972
  async function resolveLinkId(chatPK, chatPrivKey, peerChatPK) {
7893
- const pair = await getCachedPair(chatPK, chatPrivKey, peerChatPK);
7894
- return pair?.linkId || null;
7973
+ return withCachedPair(chatPK, chatPrivKey, peerChatPK, {}, (pair) => pair?.linkId || null);
7895
7974
  }
7896
7975
  async function resolvePeerChat(cloud, chatPK, chatPrivateKey, peerChatPK, options = {}) {
7897
7976
  const existingChatId = cleanText(options?.chatId);
@@ -7922,13 +8001,14 @@ async function resolvePeerChat(cloud, chatPK, chatPrivateKey, peerChatPK, option
7922
8001
  exists: linkChat?.exists === true
7923
8002
  };
7924
8003
  }
7925
- var pairCache, MAX_PAIR_CACHE;
8004
+ var pairCache, MAX_PAIR_CACHE, cacheGeneration = 0, accountGenerations;
7926
8005
  var init_pairs = __esm(() => {
7927
8006
  init_config();
7928
8007
  init_chat();
7929
8008
  init_protocol();
7930
8009
  pairCache = new Map;
7931
8010
  MAX_PAIR_CACHE = CHAT_PAIR_CACHE_LIMIT;
8011
+ accountGenerations = new Map;
7932
8012
  });
7933
8013
 
7934
8014
  // ../../shared/utils/diagnostics.js
@@ -8081,14 +8161,16 @@ function lockWallet(wallet) {
8081
8161
  cleanup?.catch?.(() => {});
8082
8162
  } catch {}
8083
8163
  }
8084
- function lockChat(chatPrivateKey) {
8164
+ function lockChat(chatPrivateKey, chatPK = "") {
8085
8165
  try {
8086
8166
  if (chatPrivateKey) {
8087
8167
  chatPrivateKey.fill(0);
8088
8168
  }
8089
8169
  } catch {}
8090
8170
  try {
8091
- clearChatPairCache();
8171
+ if (chatPK) {
8172
+ clearChatPairCache(chatPK);
8173
+ }
8092
8174
  } catch {}
8093
8175
  }
8094
8176
  var init_vault = __esm(() => {
@@ -8108,7 +8190,7 @@ function walletPubkey(value) {
8108
8190
  }
8109
8191
  return key;
8110
8192
  }
8111
- function closeSessionResources({ wallet, chatPrivateKey, localCache, vaultAccess, vaultSigner } = {}) {
8193
+ function closeSessionResources({ wallet, chatPK, chatPrivateKey, localCache, vaultAccess, vaultSigner } = {}) {
8112
8194
  try {
8113
8195
  vaultAccess?.close?.();
8114
8196
  } catch {}
@@ -8119,7 +8201,7 @@ function closeSessionResources({ wallet, chatPrivateKey, localCache, vaultAccess
8119
8201
  localCache?.close?.();
8120
8202
  } catch {}
8121
8203
  lockWallet(wallet);
8122
- lockChat(chatPrivateKey);
8204
+ lockChat(chatPrivateKey, chatPK);
8123
8205
  }
8124
8206
  function createAccountSession(resources = {}) {
8125
8207
  let closed = false;
@@ -8210,6 +8292,32 @@ function notifySeedDecrypted(callback, markDiag2, source) {
8210
8292
  failed(error);
8211
8293
  }
8212
8294
  }
8295
+ function startWalletBoot(bootWallet2, walletMnemonic, user, options) {
8296
+ let closed = false;
8297
+ let handedOff = false;
8298
+ let wallet = null;
8299
+ const result = Promise.resolve().then(() => bootWallet2(walletMnemonic, user, options)).then((identity) => {
8300
+ wallet = identity?.wallet || null;
8301
+ if (closed && wallet) {
8302
+ lockWallet(wallet);
8303
+ }
8304
+ return identity;
8305
+ });
8306
+ return {
8307
+ result,
8308
+ handoff() {
8309
+ handedOff = true;
8310
+ },
8311
+ close() {
8312
+ if (closed)
8313
+ return;
8314
+ closed = true;
8315
+ if (wallet && !handedOff) {
8316
+ lockWallet(wallet);
8317
+ }
8318
+ }
8319
+ };
8320
+ }
8213
8321
  async function openVaultAccountSession(vault, password, options = {}) {
8214
8322
  const {
8215
8323
  uid,
@@ -8251,6 +8359,7 @@ async function openVaultAccountSession(vault, password, options = {}) {
8251
8359
  throw new Error("unlockSettings missing");
8252
8360
  const { decryptSeed: decryptSeed2, unpackVaultSeedData } = vaultCrypto;
8253
8361
  let wallet = null;
8362
+ let chatPK = "";
8254
8363
  let chatPrivateKey = null;
8255
8364
  let localCache = null;
8256
8365
  let masterSeed = null;
@@ -8261,6 +8370,7 @@ async function openVaultAccountSession(vault, password, options = {}) {
8261
8370
  let vaultSigner = null;
8262
8371
  let vaultAccess = null;
8263
8372
  let vaultAccessResult = null;
8373
+ let walletBoot = null;
8264
8374
  let network;
8265
8375
  try {
8266
8376
  onStage?.("unlocking");
@@ -8329,46 +8439,18 @@ async function openVaultAccountSession(vault, password, options = {}) {
8329
8439
  onStage?.("wallet");
8330
8440
  const walletStartedAt = Date.now();
8331
8441
  mark(diag, "vault.unlock.wallet.start", { source });
8332
- const walletIdentity = await bootWallet2(walletMnemonic, user, { network, diag });
8333
- wallet = walletIdentity?.wallet;
8334
- const walletPK = walletPubkey(walletIdentity?.walletPK);
8335
- mark(diag, "vault.unlock.wallet.done", { elapsedMs: Date.now() - walletStartedAt, source });
8336
- requireCurrent(isCurrent);
8442
+ walletBoot = startWalletBoot(bootWallet2, walletMnemonic, user, { network, diag });
8337
8443
  onStage?.("chat");
8338
8444
  const chatStartedAt = Date.now();
8339
8445
  mark(diag, "vault.unlock.chat.start", { source });
8340
8446
  const chatIdentity = await bootChat2(chatSeed, user, { diag });
8341
8447
  chatPrivateKey = chatIdentity?.chatPrivateKey;
8342
- const chatPK = cleanText(chatIdentity?.chatPK);
8448
+ chatPK = cleanText(chatIdentity?.chatPK);
8343
8449
  if (!chatPrivateKey || !chatPK)
8344
8450
  throw new Error("chat identity missing");
8345
8451
  chatSeed = null;
8346
8452
  mark(diag, "vault.unlock.chat.done", { elapsedMs: Date.now() - chatStartedAt, source });
8347
8453
  requireCurrent(isCurrent);
8348
- const identityPayload = {};
8349
- if (walletIdentity?.walletPKNeedsWrite) {
8350
- identityPayload.walletPK = walletPK;
8351
- identityPayload.network = network;
8352
- }
8353
- if (chatIdentity?.chatPKNeedsWrite) {
8354
- identityPayload.chatPK = chatPK;
8355
- }
8356
- const identityFields = Object.keys(identityPayload);
8357
- let identityResult = null;
8358
- if (identityFields.length) {
8359
- const identitiesStartedAt = Date.now();
8360
- mark(diag, "vault.unlock.identities.start", { source, fields: identityFields });
8361
- identityResult = cloud.user.profile.identity.write(identityPayload).then(() => {
8362
- mark(diag, "vault.unlock.identities.done", {
8363
- elapsedMs: Date.now() - identitiesStartedAt,
8364
- source,
8365
- fields: identityFields
8366
- });
8367
- return { error: null };
8368
- }, (error) => ({ error }));
8369
- } else {
8370
- mark(diag, "vault.unlock.identities.skip", { source });
8371
- }
8372
8454
  const cacheStartedAt = Date.now();
8373
8455
  mark(diag, "vault.unlock.localCache.start", { source });
8374
8456
  localCache = await openLocalCache(cacheKey, { uid, network });
@@ -8376,20 +8458,16 @@ async function openVaultAccountSession(vault, password, options = {}) {
8376
8458
  cleanBytes(cacheKey);
8377
8459
  cacheKey = null;
8378
8460
  requireCurrent(isCurrent);
8379
- const identityWrite = await identityResult;
8380
- if (identityWrite?.error)
8381
- throw identityWrite.error;
8382
- requireCurrent(isCurrent);
8383
8461
  const signatureResult = await vaultAccessResult;
8384
8462
  if (signatureResult.error)
8385
8463
  throw signatureResult.error;
8386
8464
  vaultAccess = signatureResult.access;
8387
8465
  requireCurrent(isCurrent);
8388
- return createAccountSession({
8466
+ const session = createAccountSession({
8389
8467
  uid,
8390
8468
  network,
8391
- wallet,
8392
- walletPK,
8469
+ wallet: null,
8470
+ walletPK: null,
8393
8471
  chatPK,
8394
8472
  chatPrivateKey,
8395
8473
  localCache,
@@ -8397,12 +8475,56 @@ async function openVaultAccountSession(vault, password, options = {}) {
8397
8475
  vaultAccess,
8398
8476
  vaultPK: vaultSigner.publicKey
8399
8477
  });
8478
+ session.own(walletBoot);
8479
+ const walletReady = walletBoot.result.then(async (walletIdentity) => {
8480
+ if (session.closed)
8481
+ throw new Error("account session closed");
8482
+ const readyWallet = walletIdentity?.wallet;
8483
+ const walletPK = walletPubkey(walletIdentity?.walletPK);
8484
+ if (!readyWallet)
8485
+ throw new Error("wallet missing");
8486
+ mark(diag, "vault.unlock.wallet.done", { elapsedMs: Date.now() - walletStartedAt, source });
8487
+ const identityPayload = {};
8488
+ if (walletIdentity?.walletPKNeedsWrite) {
8489
+ identityPayload.walletPK = walletPK;
8490
+ identityPayload.network = network;
8491
+ }
8492
+ if (chatIdentity?.chatPKNeedsWrite) {
8493
+ identityPayload.chatPK = chatPK;
8494
+ }
8495
+ const identityFields = Object.keys(identityPayload);
8496
+ if (identityFields.length) {
8497
+ const identitiesStartedAt = Date.now();
8498
+ mark(diag, "vault.unlock.identities.start", { source, fields: identityFields });
8499
+ await cloud.user.profile.identity.write(identityPayload);
8500
+ mark(diag, "vault.unlock.identities.done", {
8501
+ elapsedMs: Date.now() - identitiesStartedAt,
8502
+ source,
8503
+ fields: identityFields
8504
+ });
8505
+ } else {
8506
+ mark(diag, "vault.unlock.identities.skip", { source });
8507
+ }
8508
+ if (session.closed)
8509
+ throw new Error("account session closed");
8510
+ walletBoot.handoff();
8511
+ session.wallet = readyWallet;
8512
+ session.walletPK = walletPK;
8513
+ return { wallet: readyWallet, walletPK };
8514
+ }).catch((error) => {
8515
+ walletBoot.close();
8516
+ throw error;
8517
+ });
8518
+ walletReady.catch(() => {});
8519
+ session.walletReady = walletReady;
8520
+ return session;
8400
8521
  } catch (error) {
8522
+ walletBoot?.close();
8401
8523
  if (vaultAccessResult && !vaultAccess) {
8402
8524
  const signatureResult = await vaultAccessResult;
8403
8525
  vaultAccess = signatureResult.access;
8404
8526
  }
8405
- closeSessionResources({ wallet, chatPrivateKey, localCache, vaultAccess, vaultSigner });
8527
+ closeSessionResources({ wallet, chatPK, chatPrivateKey, localCache, vaultAccess, vaultSigner });
8406
8528
  throw error;
8407
8529
  } finally {
8408
8530
  cleanBytes(masterSeed, walletEntropy, chatSeed, cacheKey, settingsKey);
@@ -8417,6 +8539,7 @@ async function openAccountSessionFromSecrets(walletEntropy, chatSeed, masterSeed
8417
8539
  }
8418
8540
  const walletMnemonic = mnemonicFromWalletEntropy(walletEntropy);
8419
8541
  let wallet = null;
8542
+ let chatPK = "";
8420
8543
  let chatPrivateKey = null;
8421
8544
  let chatPubKey = null;
8422
8545
  let vaultSigner = null;
@@ -8445,10 +8568,11 @@ async function openAccountSessionFromSecrets(walletEntropy, chatSeed, masterSeed
8445
8568
  throw new Error("vault signature uid missing");
8446
8569
  vaultAccess = await cloud.auth.vaultSignature.open(vaultSigner);
8447
8570
  }
8571
+ chatPK = toHex(chatPubKey);
8448
8572
  return createAccountSession({
8449
8573
  wallet,
8450
8574
  walletPK,
8451
- chatPK: toHex(chatPubKey),
8575
+ chatPK,
8452
8576
  chatPrivateKey,
8453
8577
  localCache,
8454
8578
  network,
@@ -8457,7 +8581,7 @@ async function openAccountSessionFromSecrets(walletEntropy, chatSeed, masterSeed
8457
8581
  vaultPK: vaultSigner.publicKey
8458
8582
  });
8459
8583
  } catch (error) {
8460
- closeSessionResources({ wallet, chatPrivateKey, localCache, vaultAccess, vaultSigner });
8584
+ closeSessionResources({ wallet, chatPK, chatPrivateKey, localCache, vaultAccess, vaultSigner });
8461
8585
  throw error;
8462
8586
  } finally {
8463
8587
  cleanBytes(chatSeed, chatPubKey);
@@ -8484,9 +8608,10 @@ var init_session = __esm(() => {
8484
8608
  });
8485
8609
 
8486
8610
  // ../../shared/community.js
8487
- var COMMUNITY_RULES_VERSION = "2026-05-11.1", COMMUNITY_SECTIONS;
8611
+ var COMMUNITY_RULES_VERSION = "2026-05-11.1", COMMUNITY_SUPPORT_EMAIL, COMMUNITY_SECTIONS;
8488
8612
  var init_community = __esm(() => {
8489
8613
  init_links();
8614
+ COMMUNITY_SUPPORT_EMAIL = links.contact.replace("mailto:", "");
8490
8615
  COMMUNITY_SECTIONS = [
8491
8616
  {
8492
8617
  title: "Use veyl directly and lawfully",
@@ -8526,7 +8651,7 @@ var init_community = __esm(() => {
8526
8651
  {
8527
8652
  title: "Reporting and support",
8528
8653
  body: [
8529
- `If someone is abusing veyl, contact us at ${links.contact.replace("mailto:", "")} and include the username, profile details, screenshots, or any evidence you choose to provide from your own device.`,
8654
+ `If someone is abusing veyl, contact us at ${COMMUNITY_SUPPORT_EMAIL} and include the username, profile details, screenshots, or any evidence you choose to provide from your own device.`,
8530
8655
  "Because veyl uses end-to-end encryption, we may rely on reports, account-level signals, and evidence you submit when reviewing abuse."
8531
8656
  ]
8532
8657
  }
@@ -107147,7 +107272,7 @@ function getSubtleCrypto() {
107147
107272
  async function importAesKey(key, usages) {
107148
107273
  return getSubtleCrypto().importKey("raw", toBytes32(key, "file key"), { name: "AES-GCM" }, false, usages);
107149
107274
  }
107150
- async function sealFile(_pair, key, bytes, scope) {
107275
+ async function sealFile(key, bytes, scope) {
107151
107276
  const fileKey = new Uint8Array(toBytes32(key, "file key"));
107152
107277
  try {
107153
107278
  const cryptoKey = await importAesKey(fileKey, ["encrypt"]);
@@ -107310,20 +107435,22 @@ async function toUploadBytes(data) {
107310
107435
  }
107311
107436
  return toBytes(data, "upload bytes");
107312
107437
  }
107313
- async function makeChatFileUploadPayload(pair, cid, data, { cacheControl = "private, max-age=0, no-transform" } = {}) {
107314
- const chatId = cleanMediaChatId(pair?.chatId);
107438
+ async function makeChatFileUploadPayload(chatId, cid, data, { cacheControl = "private, max-age=0, no-transform" } = {}) {
107439
+ const nextChatId = cleanMediaChatId(chatId);
107315
107440
  const mediaId = makeChatMediaId();
107316
- const path = mediaFilePath(chatId, mediaId);
107441
+ const path = mediaFilePath(nextChatId, mediaId);
107317
107442
  const expiresAt = Date.now() + CHAT_MEDIA_TTL_MS2;
107318
107443
  const key = createFileKey();
107319
107444
  try {
107320
107445
  const uploadBytes = await toUploadBytes(data);
107321
107446
  assertChatUploadByteSize(uploadBytes);
107447
+ const body = await sealFile(key, uploadBytes, path);
107448
+ assertChatUploadByteSize(body);
107322
107449
  return {
107323
- chatId,
107450
+ chatId: nextChatId,
107324
107451
  mediaId,
107325
107452
  path,
107326
- body: await sealFile(pair, key, uploadBytes, path),
107453
+ body,
107327
107454
  metadata: {
107328
107455
  contentType: "application/octet-stream",
107329
107456
  cacheControl
@@ -107352,10 +107479,12 @@ async function makeSharedFileUploadPayload(data, { contentType = "application/oc
107352
107479
  try {
107353
107480
  const uploadBytes = await toUploadBytes(data);
107354
107481
  assertChatUploadByteSize(uploadBytes);
107482
+ const body = await sealFile(key, uploadBytes, path);
107483
+ assertChatUploadByteSize(body);
107355
107484
  return {
107356
107485
  sharedId,
107357
107486
  path,
107358
- body: await sealFile(null, key, uploadBytes, path),
107487
+ body,
107359
107488
  metadata: {
107360
107489
  contentType,
107361
107490
  cacheControl
@@ -109281,10 +109410,19 @@ function sortedUniqueValues(items) {
109281
109410
  }
109282
109411
 
109283
109412
  // ../../shared/chat/chats.js
109413
+ function sameSigningKeys(left, right) {
109414
+ if (left === right)
109415
+ return true;
109416
+ const leftKeys = Object.keys(left || {});
109417
+ const rightKeys = Object.keys(right || {});
109418
+ if (leftKeys.length !== rightKeys.length)
109419
+ return false;
109420
+ return leftKeys.every((key) => left[key] === right[key]);
109421
+ }
109284
109422
  function sameChatShape(a, b) {
109285
109423
  if (!a || !b)
109286
109424
  return a === b;
109287
- return a.id === b.id && a.protocol === b.protocol && a.linkId === b.linkId && a.ts === b.ts && a.startMs === b.startMs && a.unseen === b.unseen && a.settings?.retention === b.settings?.retention && sameChatPreview(a.preview, b.preview);
109425
+ return a.id === b.id && a.protocol === b.protocol && a.linkId === b.linkId && a.entryId === b.entryId && a.peerChatPK === b.peerChatPK && a.peerUid === b.peerUid && sameSigningKeys(a.signingKeysByChatKey, b.signingKeysByChatKey) && a.ts === b.ts && a.startMs === b.startMs && a.unseen === b.unseen && a.settings?.retention === b.settings?.retention && sameChatPreview(a.preview, b.preview);
109288
109426
  }
109289
109427
  function sameChats(prev, next) {
109290
109428
  if (prev.length !== next.length)
@@ -110847,8 +110985,14 @@ function mediaAad(uid, network, id) {
110847
110985
  function partAad(uid, network, part) {
110848
110986
  return encoder.encode(JSON.stringify(["veyl-local-cache-part", CACHE_PART_ENVELOPE_VERSION, String(uid || ""), String(network || ""), String(part || "")]));
110849
110987
  }
110988
+ function encodePayloadData(payload) {
110989
+ return encoder.encode(JSON.stringify({ ...normalizePayload(payload), savedAt: Date.now() }));
110990
+ }
110991
+ function decodePayloadData(raw) {
110992
+ return JSON.parse(decoder.decode(toBytes(raw, "cache payload")));
110993
+ }
110850
110994
  async function sealPayload(key, payload, uid, network) {
110851
- const body = encoder.encode(JSON.stringify({ ...normalizePayload(payload), savedAt: Date.now() }));
110995
+ const body = encodePayloadData(payload);
110852
110996
  try {
110853
110997
  const { iv, ct } = await sealAes(key, body, cacheAad(uid, network));
110854
110998
  return JSON.stringify({
@@ -110870,7 +111014,7 @@ async function openPayloadData(key, raw, uid, network) {
110870
111014
  }
110871
111015
  const plain = await openAes(key, fromHexBytes(envelope.iv, "cache iv"), fromHexBytes(envelope.ct, "cache ciphertext"), cacheAad(uid, network));
110872
111016
  try {
110873
- return JSON.parse(decoder.decode(plain));
111017
+ return decodePayloadData(plain);
110874
111018
  } finally {
110875
111019
  cleanBytes(plain);
110876
111020
  }
@@ -110931,6 +111075,14 @@ function mediaCrypto(uid, network, id, keyBytes) {
110931
111075
  ivBytes: AES_IV_BYTES
110932
111076
  };
110933
111077
  }
111078
+ function payloadCrypto(uid, network, keyBytes) {
111079
+ return {
111080
+ key: keyBytes,
111081
+ aad: cacheAad(uid, network),
111082
+ version: LOCAL_DATA_CACHE_VERSION,
111083
+ ivBytes: AES_IV_BYTES
111084
+ };
111085
+ }
110934
111086
  var MEDIA_ENVELOPE_VERSION = 1, CACHE_PART_ENVELOPE_VERSION = 1;
110935
111087
  var init_crypto = __esm(() => {
110936
111088
  init_aes2();
@@ -110966,11 +111118,32 @@ async function openVaultCache({ key, storage, uid, network }) {
110966
111118
  let writeTimer = null;
110967
111119
  let writeTask = Promise.resolve();
110968
111120
  const partWriteTasks = new Map;
111121
+ let storedPayload = null;
110969
111122
  try {
110970
- const rawPayload = await openPayloadData(cacheKey, await storage.read(), uid, network);
110971
- payload = normalizePayload(rawPayload);
111123
+ storedPayload = await storage.read();
111124
+ if (!storedPayload) {
111125
+ payload = emptyPayload();
111126
+ } else if (typeof storage.openPayload === "function") {
111127
+ const readKey = new Uint8Array(cacheKey);
111128
+ let plain;
111129
+ try {
111130
+ plain = await storage.openPayload(storedPayload, payloadCrypto(uid, network, readKey));
111131
+ payload = normalizePayload(decodePayloadData(plain));
111132
+ } finally {
111133
+ cleanBytes(plain, readKey);
111134
+ }
111135
+ } else {
111136
+ payload = normalizePayload(await openPayloadData(cacheKey, storedPayload, uid, network));
111137
+ }
110972
111138
  } catch {
110973
111139
  payload = emptyPayload();
111140
+ if (storedPayload) {
111141
+ await Promise.all([
111142
+ storage.remove().catch(() => {}),
111143
+ storage.removeAllMedia().catch(() => {}),
111144
+ storage.removeAllParts().catch(() => {})
111145
+ ]);
111146
+ }
110974
111147
  }
110975
111148
  const writeNow = () => {
110976
111149
  if (closed) {
@@ -110988,10 +111161,12 @@ async function openVaultCache({ key, storage, uid, network }) {
110988
111161
  return;
110989
111162
  }
110990
111163
  const writeKey = new Uint8Array(cacheKey);
111164
+ let body;
110991
111165
  try {
110992
- await storage.write(await sealPayload(writeKey, payload, uid, network));
111166
+ const raw = typeof storage.sealPayload === "function" ? await storage.sealPayload(body = encodePayloadData(payload), payloadCrypto(uid, network, writeKey)) : await sealPayload(writeKey, payload, uid, network);
111167
+ await storage.write(raw);
110993
111168
  } finally {
110994
- cleanBytes(writeKey);
111169
+ cleanBytes(body, writeKey);
110995
111170
  }
110996
111171
  }).catch(() => {});
110997
111172
  return writeTask;
@@ -111794,9 +111969,9 @@ function setErrorStage(error, stage, extra = {}) {
111794
111969
  Object.assign(error, extra);
111795
111970
  return error;
111796
111971
  }
111797
- async function makeChatFileUpload(pair, cid, data, { cacheControl = "private, max-age=0, no-transform" } = {}) {
111972
+ async function makeChatFileUpload(chatId, cid, data, { cacheControl = "private, max-age=0, no-transform" } = {}) {
111798
111973
  try {
111799
- return await makeChatFileUploadPayload(pair, cid, data, {
111974
+ return await makeChatFileUploadPayload(chatId, cid, data, {
111800
111975
  cacheControl
111801
111976
  });
111802
111977
  } catch (error) {
@@ -111806,8 +111981,8 @@ async function makeChatFileUpload(pair, cid, data, { cacheControl = "private, ma
111806
111981
  });
111807
111982
  }
111808
111983
  }
111809
- async function putChatFile(pair, cid, data, options) {
111810
- const upload = await makeChatFileUpload(pair, cid, data, options);
111984
+ async function putChatFile(chatId, cid, data, options) {
111985
+ const upload = await makeChatFileUpload(chatId, cid, data, options);
111811
111986
  try {
111812
111987
  if (typeof options?.uploadChatMedia !== "function") {
111813
111988
  throw new Error("chat media upload required");
@@ -111842,7 +112017,7 @@ async function putSharedFile(data, options = {}) {
111842
112017
  throw setErrorStage(error, "upload", { path: upload.path });
111843
112018
  }
111844
112019
  }
111845
- async function readChatFile(readChatMedia, _pair, file) {
112020
+ async function readChatFile(readChatMedia, file) {
111846
112021
  let body;
111847
112022
  try {
111848
112023
  if (typeof readChatMedia !== "function") {
@@ -111900,8 +112075,8 @@ function buildAttachmentMsg(type, file) {
111900
112075
  return makeAttachment(type, file);
111901
112076
  }
111902
112077
  }
111903
- async function putAttachment(pair, cid, type, data, meta = {}) {
111904
- const file = await putChatFile(pair, cid, data, meta);
112078
+ async function putAttachment(chatId, cid, type, data, meta = {}) {
112079
+ const file = await putChatFile(chatId, cid, data, meta);
111905
112080
  return buildAttachmentMsg(type, {
111906
112081
  ...file,
111907
112082
  ...pickAttachmentMeta(meta)
@@ -111914,26 +112089,26 @@ async function putSharedAttachment(type, data, meta = {}) {
111914
112089
  ...pickAttachmentMeta(meta)
111915
112090
  });
111916
112091
  }
111917
- async function putImg(pair, cid, data, meta = {}) {
111918
- return putAttachment(pair, cid, "img", data, meta);
112092
+ async function putImg(chatId, cid, data, meta = {}) {
112093
+ return putAttachment(chatId, cid, "img", data, meta);
111919
112094
  }
111920
- async function putGif(pair, cid, data, meta = {}) {
111921
- return putAttachment(pair, cid, "gif", data, meta);
112095
+ async function putGif(chatId, cid, data, meta = {}) {
112096
+ return putAttachment(chatId, cid, "gif", data, meta);
111922
112097
  }
111923
- async function putM4a(pair, cid, data, meta = {}) {
111924
- return putAttachment(pair, cid, "m4a", data, meta);
112098
+ async function putM4a(chatId, cid, data, meta = {}) {
112099
+ return putAttachment(chatId, cid, "m4a", data, meta);
111925
112100
  }
111926
- async function putMp4(pair, cid, data, meta = {}) {
111927
- return putAttachment(pair, cid, "mp4", data, meta);
112101
+ async function putMp4(chatId, cid, data, meta = {}) {
112102
+ return putAttachment(chatId, cid, "mp4", data, meta);
111928
112103
  }
111929
- async function putFile(pair, cid, data, meta = {}) {
111930
- return putAttachment(pair, cid, "file", data, meta);
112104
+ async function putFile(chatId, cid, data, meta = {}) {
112105
+ return putAttachment(chatId, cid, "file", data, meta);
111931
112106
  }
111932
- function readMsgAttachment(readChatMedia, pair, msg) {
111933
- return readChatFile(readChatMedia, pair, msg);
112107
+ function readMsgAttachment(readChatMedia, msg) {
112108
+ return readChatFile(readChatMedia, msg);
111934
112109
  }
111935
- function readMsgFile(readChatMedia, pair, msg) {
111936
- return readMsgAttachment(readChatMedia, pair, msg);
112110
+ function readMsgFile(readChatMedia, msg) {
112111
+ return readMsgAttachment(readChatMedia, msg);
111937
112112
  }
111938
112113
  var init_media2 = __esm(() => {
111939
112114
  init_files2();
@@ -112182,14 +112357,6 @@ function ownerEditedPreview(senderPubkey, message) {
112182
112357
  failed: false
112183
112358
  };
112184
112359
  }
112185
- function messageFileChatId(message) {
112186
- try {
112187
- const ref = getMediaFileRef(message?.p);
112188
- return ref?.type === "chat" ? ref.chatId : "";
112189
- } catch {
112190
- return "";
112191
- }
112192
- }
112193
112360
  async function recipientForSend(cloud, receiverChatPK, options, needed) {
112194
112361
  if (!needed) {
112195
112362
  return null;
@@ -112210,50 +112377,51 @@ async function sendMsg(cloud, senderPubkey, senderPrivkey, receiverChatPK, messa
112210
112377
  if (!chatId) {
112211
112378
  throw new Error("chat id required");
112212
112379
  }
112213
- const pair = await getCachedPair(senderPubkey, senderPrivkey, receiverChatPK, { chatId });
112214
- if (options?.linkId && cleanText(options.linkId) !== pair.linkId) {
112215
- throw new Error("link mismatch");
112216
- }
112217
- const retention = cleanChatRetention(options?.retention ?? options?.ttlMode);
112218
- const tsMs = Date.now();
112219
- const messagePayload = withMessageRetention(message, retention);
112220
- const actionOp = actionOpForPayload(messagePayload);
112221
- const { head, body } = await sealMsg(pair, messagePayload, { op: actionOp, ts: tsMs });
112222
- const ttlMs = actionOp === CHAT_ACTION_OPS.CREATE ? newStorageTtlMs() : null;
112223
- const messageId = head.cid;
112224
- const msgData = {
112225
- head,
112226
- body,
112227
- ttlMs
112228
- };
112229
- const recipientProfile = await recipientForSend(cloud, receiverChatPK, options, updatePreview || options?.ping === true);
112230
- const sentMessage = ownerPreview(senderPubkey, messagePayload, messageId, head, tsMs, ttlMs);
112231
- const preview2 = updatePreview ? sentMessage : null;
112232
- const ownerEntry = updatePreview ? await ownEntryWrite(cloud, cleanText(options?.senderUid), senderPrivkey, pair, {
112233
- peerUid: recipientProfile?.uid || cleanText(options?.receiverUid),
112234
- peerSigningPublicKey: recipientProfile?.signingPublicKey,
112235
- entry: options?.ownEntry,
112236
- settings: options?.chatSettings,
112237
- preview: preview2,
112238
- ts: tsMs
112239
- }) : null;
112240
- const pingKind = updatePreview ? "message" : cleanText(options?.pingKind) || "ping";
112241
- const ping = recipientProfile?.uid && (updatePreview || options?.ping === true) ? await sealPing(senderPubkey, senderPrivkey, receiverChatPK, {
112242
- kind: pingKind,
112243
- chatId,
112244
- senderUid: cleanText(options?.senderUid),
112245
- messageId,
112246
- ts: tsMs
112247
- }) : null;
112248
- const write = await cloud.chat.messages.send({
112249
- chatId,
112250
- messageId,
112251
- message: msgData,
112252
- ownerEntry,
112253
- inbox: ping ? { recipientUid: recipientProfile?.uid, ping } : null,
112254
- onCommitted: typeof options?.onCommitted === "function" ? (committed) => options.onCommitted({ ...committed, message: sentMessage, preview: preview2 }) : undefined
112380
+ return withCachedPair(senderPubkey, senderPrivkey, receiverChatPK, { chatId }, async (pair) => {
112381
+ if (options?.linkId && cleanText(options.linkId) !== pair.linkId) {
112382
+ throw new Error("link mismatch");
112383
+ }
112384
+ const retention = cleanChatRetention(options?.retention ?? options?.ttlMode);
112385
+ const tsMs = Date.now();
112386
+ const messagePayload = withMessageRetention(message, retention);
112387
+ const actionOp = actionOpForPayload(messagePayload);
112388
+ const { head, body } = await sealMsg(pair, messagePayload, { op: actionOp, ts: tsMs });
112389
+ const ttlMs = actionOp === CHAT_ACTION_OPS.CREATE ? newStorageTtlMs() : null;
112390
+ const messageId = head.cid;
112391
+ const msgData = {
112392
+ head,
112393
+ body,
112394
+ ttlMs
112395
+ };
112396
+ const recipientProfile = await recipientForSend(cloud, receiverChatPK, options, updatePreview || options?.ping === true);
112397
+ const sentMessage = ownerPreview(senderPubkey, messagePayload, messageId, head, tsMs, ttlMs);
112398
+ const preview2 = updatePreview ? sentMessage : null;
112399
+ const ownerEntry = updatePreview ? await ownEntryWrite(cloud, cleanText(options?.senderUid), senderPrivkey, pair, {
112400
+ peerUid: recipientProfile?.uid || cleanText(options?.receiverUid),
112401
+ peerSigningPublicKey: recipientProfile?.signingPublicKey,
112402
+ entry: options?.ownEntry,
112403
+ settings: options?.chatSettings,
112404
+ preview: preview2,
112405
+ ts: tsMs
112406
+ }) : null;
112407
+ const pingKind = updatePreview ? "message" : cleanText(options?.pingKind) || "ping";
112408
+ const ping = recipientProfile?.uid && (updatePreview || options?.ping === true) ? await sealPing(senderPubkey, senderPrivkey, receiverChatPK, {
112409
+ kind: pingKind,
112410
+ chatId,
112411
+ senderUid: cleanText(options?.senderUid),
112412
+ messageId,
112413
+ ts: tsMs
112414
+ }) : null;
112415
+ const write = await cloud.chat.messages.send({
112416
+ chatId,
112417
+ messageId,
112418
+ message: msgData,
112419
+ ownerEntry,
112420
+ inbox: ping ? { recipientUid: recipientProfile?.uid, ping } : null,
112421
+ onCommitted: typeof options?.onCommitted === "function" ? (committed) => options.onCommitted({ ...committed, message: sentMessage, preview: preview2 }) : undefined
112422
+ });
112423
+ return { chatId, msgId: messageId, cid: head.cid, message: sentMessage, preview: preview2, ...write?.skipped ? { skipped: true } : {} };
112255
112424
  });
112256
- return { chatId, msgId: messageId, cid: head.cid, message: sentMessage, preview: preview2, ...write?.skipped ? { skipped: true } : {} };
112257
112425
  }
112258
112426
  async function sendReadReceipt(cloud, senderPubkey, senderPrivkey, receiverChatPK, target, options = {}) {
112259
112427
  const receipt = {
@@ -112363,27 +112531,28 @@ async function setChatRetention(cloud, chatId, senderPubkey, senderPrivkey, peer
112363
112531
  throw new Error("vault locked");
112364
112532
  }
112365
112533
  const nextRetention = cleanChatRetention(retention);
112366
- const pair = await getCachedPair(senderPubkey, senderPrivkey, peerChatPK, { chatId });
112367
- if (chatId && pair.chatId !== chatId) {
112368
- throw new Error("chat mismatch");
112369
- }
112370
- const systemMessage = {
112371
- ...makeRetentionSystemMsg(nextRetention),
112372
- cid: cleanText(options?.cid) || makeCid(),
112373
- s: senderPubkey
112374
- };
112375
- await sendMsg(cloud, senderPubkey, senderPrivkey, peerChatPK, systemMessage, {
112376
- chatId,
112377
- linkId: pair.linkId,
112378
- updatePreview: true,
112379
- retention: nextRetention,
112380
- chatExists: true,
112381
- senderUid: options?.senderUid,
112382
- ownEntry: options?.ownEntry,
112383
- chatSettings: { retention: nextRetention },
112384
- onCommitted: options?.onCommitted
112534
+ return withCachedPair(senderPubkey, senderPrivkey, peerChatPK, { chatId }, async (pair) => {
112535
+ if (chatId && pair.chatId !== chatId) {
112536
+ throw new Error("chat mismatch");
112537
+ }
112538
+ const systemMessage = {
112539
+ ...makeRetentionSystemMsg(nextRetention),
112540
+ cid: cleanText(options?.cid) || makeCid(),
112541
+ s: senderPubkey
112542
+ };
112543
+ await sendMsg(cloud, senderPubkey, senderPrivkey, peerChatPK, systemMessage, {
112544
+ chatId,
112545
+ linkId: pair.linkId,
112546
+ updatePreview: true,
112547
+ retention: nextRetention,
112548
+ chatExists: true,
112549
+ senderUid: options?.senderUid,
112550
+ ownEntry: options?.ownEntry,
112551
+ chatSettings: { retention: nextRetention },
112552
+ onCommitted: options?.onCommitted
112553
+ });
112554
+ return nextRetention;
112385
112555
  });
112386
- return nextRetention;
112387
112556
  }
112388
112557
  async function uploadAttachmentMsg(_cloud, senderPubkey, senderPrivkey, receiverChatPK, attachment = {}) {
112389
112558
  if (!senderPrivkey || !senderPubkey) {
@@ -112397,95 +112566,96 @@ async function uploadAttachmentMsg(_cloud, senderPubkey, senderPrivkey, receiver
112397
112566
  if (!chatId) {
112398
112567
  throw new Error("chat id required");
112399
112568
  }
112400
- const pair = await getCachedPair(senderPubkey, senderPrivkey, receiverChatPK, { chatId });
112401
112569
  const type = cleanText(attachment?.type) || "file";
112402
112570
  const data = attachment?.data;
112403
112571
  const meta = attachment?.meta || {};
112404
112572
  switch (type) {
112405
112573
  case "img":
112406
- return putImg(pair, nextCid, data, meta);
112574
+ return putImg(chatId, nextCid, data, meta);
112407
112575
  case "gif":
112408
- return putGif(pair, nextCid, data, meta);
112576
+ return putGif(chatId, nextCid, data, meta);
112409
112577
  case "m4a":
112410
- return putM4a(pair, nextCid, data, meta);
112578
+ return putM4a(chatId, nextCid, data, meta);
112411
112579
  case "mp4":
112412
- return putMp4(pair, nextCid, data, meta);
112580
+ return putMp4(chatId, nextCid, data, meta);
112413
112581
  case "file":
112414
- return putFile(pair, nextCid, data, meta);
112582
+ return putFile(chatId, nextCid, data, meta);
112415
112583
  default:
112416
- return putAttachment(pair, nextCid, type, data, meta);
112584
+ return putAttachment(chatId, nextCid, type, data, meta);
112417
112585
  }
112418
112586
  }
112419
112587
  async function updateMsg(cloud, chatId, msgId, senderPubkey, senderPrivkey, receiverChatPK, newMessage, options = {}) {
112420
112588
  if (!senderPubkey || !senderPrivkey)
112421
112589
  throw new Error("vault locked");
112422
- const pair = await getCachedPair(senderPubkey, senderPrivkey, receiverChatPK, { chatId });
112423
- if (pair.chatId !== chatId) {
112424
- throw new Error("chat mismatch");
112425
- }
112426
- const target = cleanText(newMessage?.cid) || cleanText(msgId);
112427
- if (!target) {
112428
- throw new Error("message target required");
112429
- }
112430
- const op = newMessage?.t === "req" && cleanText(newMessage?.tx) ? CHAT_ACTION_OPS.PAY_CONFIRM : CHAT_ACTION_OPS.EDIT;
112431
- const tsMs = Date.now();
112432
- const action = {
112433
- ...newMessage || {},
112434
- cid: makeCid(),
112435
- s: senderPubkey
112436
- };
112437
- const { head, body } = await sealMsg(pair, action, { op, target, ts: tsMs });
112438
- const messageId = head.cid;
112439
- const updatePreview = options?.updatePreview === true;
112440
- const preview2 = updatePreview ? ownerEditedPreview(senderPubkey, newMessage) : null;
112441
- const ownerEntry = updatePreview ? await ownEntryWrite(cloud, cleanText(options?.senderUid), senderPrivkey, pair, {
112442
- peerUid: cleanText(options?.receiverUid),
112443
- peerSigningPublicKey: cleanText(options?.peerSigningPublicKey),
112444
- entry: options?.ownEntry,
112445
- preview: preview2,
112446
- ts: preview2?.ts ?? tsMs
112447
- }) : null;
112448
- await cloud.chat.messages.send({
112449
- chatId,
112450
- messageId,
112451
- message: {
112452
- head,
112453
- body,
112454
- ttlMs: null
112455
- },
112456
- ownerEntry,
112457
- inbox: null
112590
+ return withCachedPair(senderPubkey, senderPrivkey, receiverChatPK, { chatId }, async (pair) => {
112591
+ if (pair.chatId !== chatId) {
112592
+ throw new Error("chat mismatch");
112593
+ }
112594
+ const target = cleanText(newMessage?.cid) || cleanText(msgId);
112595
+ if (!target) {
112596
+ throw new Error("message target required");
112597
+ }
112598
+ const op = newMessage?.t === "req" && cleanText(newMessage?.tx) ? CHAT_ACTION_OPS.PAY_CONFIRM : CHAT_ACTION_OPS.EDIT;
112599
+ const tsMs = Date.now();
112600
+ const action = {
112601
+ ...newMessage || {},
112602
+ cid: makeCid(),
112603
+ s: senderPubkey
112604
+ };
112605
+ const { head, body } = await sealMsg(pair, action, { op, target, ts: tsMs });
112606
+ const messageId = head.cid;
112607
+ const updatePreview = options?.updatePreview === true;
112608
+ const preview2 = updatePreview ? ownerEditedPreview(senderPubkey, newMessage) : null;
112609
+ const ownerEntry = updatePreview ? await ownEntryWrite(cloud, cleanText(options?.senderUid), senderPrivkey, pair, {
112610
+ peerUid: cleanText(options?.receiverUid),
112611
+ peerSigningPublicKey: cleanText(options?.peerSigningPublicKey),
112612
+ entry: options?.ownEntry,
112613
+ preview: preview2,
112614
+ ts: preview2?.ts ?? tsMs
112615
+ }) : null;
112616
+ await cloud.chat.messages.send({
112617
+ chatId,
112618
+ messageId,
112619
+ message: {
112620
+ head,
112621
+ body,
112622
+ ttlMs: null
112623
+ },
112624
+ ownerEntry,
112625
+ inbox: null
112626
+ });
112627
+ return {
112628
+ chatId,
112629
+ msgId: messageId,
112630
+ cid: head.cid,
112631
+ preview: preview2
112632
+ };
112458
112633
  });
112459
- return {
112460
- chatId,
112461
- msgId: messageId,
112462
- cid: head.cid,
112463
- preview: preview2
112464
- };
112465
112634
  }
112466
112635
  async function deleteMsg(cloud, chatId, messageOrId, senderPubkey, senderPrivkey, peerChatPK, options = {}) {
112467
112636
  if (!cloud || !chatId || !messageOrId || !senderPubkey || !senderPrivkey || !peerChatPK) {
112468
112637
  return false;
112469
112638
  }
112470
- const pair = await getCachedPair(senderPubkey, senderPrivkey, peerChatPK, { chatId });
112471
- if (pair.chatId !== chatId) {
112472
- return false;
112473
- }
112474
- const item = messageDeleteItems([messageOrId])[0];
112475
- const target = cleanText(options?.docId) || cleanText(item?.id);
112476
- if (!target || target.startsWith("local:")) {
112477
- return false;
112478
- }
112479
- const deleteTarget = cleanText(options?.target) || cleanText(getMessageKey(messageOrId)) || target;
112480
- await sendDeleteAction(cloud, senderPubkey, senderPrivkey, peerChatPK, deleteTarget, {
112481
- chatId,
112482
- linkId: options?.linkId,
112483
- senderUid: options?.senderUid
112484
- });
112485
- await cloud.chat.messages.delete(chatId, target, {
112486
- mediaPaths: item?.mediaPath ? [item.mediaPath] : []
112639
+ return withCachedPair(senderPubkey, senderPrivkey, peerChatPK, { chatId }, async (pair) => {
112640
+ if (pair.chatId !== chatId) {
112641
+ return false;
112642
+ }
112643
+ const item = messageDeleteItems([messageOrId])[0];
112644
+ const target = cleanText(options?.docId) || cleanText(item?.id);
112645
+ if (!target || target.startsWith("local:")) {
112646
+ return false;
112647
+ }
112648
+ const deleteTarget = cleanText(options?.target) || cleanText(getMessageKey(messageOrId)) || target;
112649
+ await sendDeleteAction(cloud, senderPubkey, senderPrivkey, peerChatPK, deleteTarget, {
112650
+ chatId,
112651
+ linkId: options?.linkId,
112652
+ senderUid: options?.senderUid
112653
+ });
112654
+ await cloud.chat.messages.delete(chatId, target, {
112655
+ mediaPaths: item?.mediaPath ? [item.mediaPath] : []
112656
+ });
112657
+ return true;
112487
112658
  });
112488
- return true;
112489
112659
  }
112490
112660
  async function deleteMsgs(cloud, chatId, messages, senderPubkey, senderPrivkey, peerChatPK) {
112491
112661
  if (!cloud || !chatId || !senderPubkey || !senderPrivkey || !peerChatPK) {
@@ -112495,31 +112665,31 @@ async function deleteMsgs(cloud, chatId, messages, senderPubkey, senderPrivkey,
112495
112665
  if (!items.length) {
112496
112666
  return 0;
112497
112667
  }
112498
- const pair = await getCachedPair(senderPubkey, senderPrivkey, peerChatPK, { chatId });
112499
- if (pair.chatId !== chatId) {
112500
- return 0;
112501
- }
112502
- let deleted = 0;
112503
- for (let index = 0;index < items.length; index += DELETE_WRITE_BATCH_SIZE) {
112504
- const chunk = items.slice(index, index + DELETE_WRITE_BATCH_SIZE);
112505
- const targets = chunk.map((item) => cleanText(item.id)).filter((target) => target && !target.startsWith("local:"));
112506
- const mediaPaths = chunk.map((item) => cleanText(item.mediaPath)).filter(Boolean);
112507
- deleted += await cloud.chat.messages.deleteMany(chatId, targets, { mediaPaths });
112508
- }
112509
- return deleted;
112668
+ return withCachedPair(senderPubkey, senderPrivkey, peerChatPK, { chatId }, async (pair) => {
112669
+ if (pair.chatId !== chatId) {
112670
+ return 0;
112671
+ }
112672
+ let deleted = 0;
112673
+ for (let index = 0;index < items.length; index += DELETE_WRITE_BATCH_SIZE) {
112674
+ const chunk = items.slice(index, index + DELETE_WRITE_BATCH_SIZE);
112675
+ const targets = chunk.map((item) => cleanText(item.id)).filter((target) => target && !target.startsWith("local:"));
112676
+ const mediaPaths = chunk.map((item) => cleanText(item.mediaPath)).filter(Boolean);
112677
+ deleted += await cloud.chat.messages.deleteMany(chatId, targets, { mediaPaths });
112678
+ }
112679
+ return deleted;
112680
+ });
112510
112681
  }
112511
- async function readMsgMedia(readChatMedia, userChatPK, userPrivKey, peerChatPK, msg) {
112512
- return readMsgAttachment2(readChatMedia, userChatPK, userPrivKey, peerChatPK, msg);
112682
+ async function readMsgMedia(readChatMedia, msg) {
112683
+ return readMsgAttachment2(readChatMedia, msg);
112513
112684
  }
112514
- async function readMsgAttachment2(readChatMedia, userChatPK, userPrivKey, peerChatPK, msg) {
112685
+ async function readMsgAttachment2(readChatMedia, msg) {
112515
112686
  if (typeof readChatMedia !== "function") {
112516
112687
  throw new Error("chat media read required");
112517
112688
  }
112518
- if (!userChatPK || !userPrivKey || !peerChatPK || !msg) {
112689
+ if (!msg) {
112519
112690
  return null;
112520
112691
  }
112521
- const pair = await getCachedPair(userChatPK, userPrivKey, peerChatPK, { chatId: msg?.chatId || messageFileChatId(msg) });
112522
- return readMsgFile(readChatMedia, pair, msg);
112692
+ return readMsgFile(readChatMedia, msg);
112523
112693
  }
112524
112694
  var DELETE_WRITE_BATCH_SIZE;
112525
112695
  var init_write = __esm(() => {
@@ -112762,14 +112932,14 @@ function hasInvalidStoredMediaRef(message) {
112762
112932
  return true;
112763
112933
  }
112764
112934
  }
112765
- function readMessageMedia(cloud, media, chatPK, chatPrivateKey, peerChatPK, message) {
112935
+ function readMessageMedia(cloud, media, message) {
112766
112936
  const readChatMedia = cloud?.chat?.media?.read;
112767
112937
  if (typeof media?.readMessageFile === "function") {
112768
- return media.readMessageFile(readChatMedia, chatPK, chatPrivateKey, peerChatPK, message);
112938
+ return media.readMessageFile(readChatMedia, message);
112769
112939
  }
112770
- return readMsgMedia(readChatMedia, chatPK, chatPrivateKey, peerChatPK, message);
112940
+ return readMsgMedia(readChatMedia, message);
112771
112941
  }
112772
- function createChatSave({ cloud, media = {}, chatBanned, chatPK, chatPrivateKey, localCache }) {
112942
+ function createChatSave({ cloud, media = {}, chatBanned, localCache }) {
112773
112943
  const makeMessagePermanent = async (chatId, message) => {
112774
112944
  if (chatBanned) {
112775
112945
  throw makeChatUnavailableError();
@@ -112787,7 +112957,7 @@ function createChatSave({ cloud, media = {}, chatBanned, chatPK, chatPrivateKey,
112787
112957
  const updated = await makeMsgTemporary(cloud, chatId, list, ttl);
112788
112958
  return { updated, ttl };
112789
112959
  };
112790
- const readMessageFile = async (peerChatPK, message) => {
112960
+ const readMessageFile = async (_peerChatPK, message) => {
112791
112961
  if (chatBanned) {
112792
112962
  throw makeChatUnavailableError();
112793
112963
  }
@@ -112809,7 +112979,7 @@ function createChatSave({ cloud, media = {}, chatBanned, chatPK, chatPrivateKey,
112809
112979
  throw makeFileGoneError();
112810
112980
  }
112811
112981
  try {
112812
- const bytes = await readMessageMedia(cloud, media, chatPK, chatPrivateKey, peerChatPK, message);
112982
+ const bytes = await readMessageMedia(cloud, media, message);
112813
112983
  saveMedia(localCache, message, bytes, message);
112814
112984
  return bytes;
112815
112985
  } catch (error) {
@@ -113455,8 +113625,11 @@ function withSharedMediaUpload(cloud, media, meta = {}) {
113455
113625
  };
113456
113626
  }
113457
113627
  function uploadMessageAttachment(cloud, media, senderPubkey, senderPrivkey, receiverChatPK, attachment = {}) {
113628
+ if (!senderPubkey || !senderPrivkey) {
113629
+ throw new Error("vault locked");
113630
+ }
113458
113631
  if (typeof media?.uploadAttachment === "function") {
113459
- return media.uploadAttachment(senderPubkey, senderPrivkey, receiverChatPK, attachment);
113632
+ return media.uploadAttachment(attachment);
113460
113633
  }
113461
113634
  return uploadAttachmentMsg(cloud, senderPubkey, senderPrivkey, receiverChatPK, withMediaUpload(cloud, media, attachment));
113462
113635
  }
@@ -113754,17 +113927,21 @@ ${cid}` : "";
113754
113927
  const requiredPreview = previewRequired || sendOptions.chatExists !== true;
113755
113928
  let local = await showLocalMessage(peerChatPK, message, sendOptions);
113756
113929
  let committed = false;
113930
+ let sent = false;
113931
+ const markSent = () => {
113932
+ if (sent)
113933
+ return;
113934
+ sent = true;
113935
+ sentChatIdsRef.current.add(local.chatId);
113936
+ markLocalStatus(local.chatId, local.cid, LOCAL_SENT);
113937
+ };
113757
113938
  const markCommitted = () => {
113758
113939
  if (committed)
113759
113940
  return;
113760
113941
  committed = true;
113761
- sentChatIdsRef.current.add(local.chatId);
113942
+ markSent();
113762
113943
  markDone(diag, "chat.send.commit", startedAt, { type: local?.t || "txt", previewRequired: !!requiredPreview });
113763
113944
  };
113764
- const markSent = () => {
113765
- sentChatIdsRef.current.add(local.chatId);
113766
- markLocalStatus(local.chatId, local.cid, LOCAL_SENT);
113767
- };
113768
113945
  return await new Promise((resolve, reject) => {
113769
113946
  const job = {
113770
113947
  previewKey: local.chatId,
@@ -113982,7 +114159,8 @@ ${cid}` : "";
113982
114159
  linkId: local.linkId,
113983
114160
  version: local.version,
113984
114161
  sendOptions,
113985
- nextAttachment: prepared.nextAttachment
114162
+ nextAttachment: prepared.nextAttachment,
114163
+ committed: false
113986
114164
  };
113987
114165
  }));
113988
114166
  return await new Promise((resolve, reject) => {
@@ -113991,7 +114169,9 @@ ${cid}` : "";
113991
114169
  reject,
113992
114170
  onError: () => {
113993
114171
  for (const item of locals) {
113994
- markLocalStatus(item.chatId, item.cid, LOCAL_FAILED);
114172
+ if (!item.committed) {
114173
+ markLocalStatus(item.chatId, item.cid, LOCAL_FAILED);
114174
+ }
113995
114175
  }
113996
114176
  },
113997
114177
  run: async () => {
@@ -114006,6 +114186,7 @@ ${cid}` : "";
114006
114186
  results.push({ peerChatPK: item.peerChatPK, ok: false, error: uploadError });
114007
114187
  continue;
114008
114188
  }
114189
+ let sentMessage;
114009
114190
  try {
114010
114191
  let uploaded = uploads.get(uploadKey);
114011
114192
  if (!uploaded) {
@@ -114018,17 +114199,32 @@ ${cid}` : "";
114018
114199
  cid: item.cid,
114019
114200
  s: chatPK
114020
114201
  };
114021
- const sentMessage = withMessageRetention(sent, item.sendOptions.retention);
114202
+ sentMessage = withMessageRetention(sent, item.sendOptions.retention);
114022
114203
  rememberCachedLocalMedia(item.chatId, item.cid, sentMessage);
114023
- await sendMsg(cloud, chatPK, chatPrivateKey, item.peerChatPK, sentMessage, { ...item.sendOptions, chatId: item.chatId, linkId: item.linkId, senderUid: uid });
114024
- markLocalStatus(item.chatId, item.cid, LOCAL_SENT);
114204
+ await sendMsg(cloud, chatPK, chatPrivateKey, item.peerChatPK, sentMessage, {
114205
+ ...item.sendOptions,
114206
+ chatId: item.chatId,
114207
+ linkId: item.linkId,
114208
+ senderUid: uid,
114209
+ onCommitted: () => {
114210
+ if (item.committed)
114211
+ return;
114212
+ item.committed = true;
114213
+ sentChatIdsRef.current.add(item.chatId);
114214
+ markLocalStatus(item.chatId, item.cid, LOCAL_SENT);
114215
+ }
114216
+ });
114025
114217
  results.push({ peerChatPK: item.peerChatPK, ok: true, message: sentMessage });
114026
114218
  } catch (error) {
114027
114219
  if (!uploads.has(uploadKey)) {
114028
114220
  uploadErrors.set(uploadKey, error);
114029
114221
  }
114030
- markLocalStatus(item.chatId, item.cid, LOCAL_FAILED);
114031
- results.push({ peerChatPK: item.peerChatPK, ok: false, error });
114222
+ if (item.committed) {
114223
+ results.push({ peerChatPK: item.peerChatPK, ok: true, message: sentMessage });
114224
+ } else {
114225
+ markLocalStatus(item.chatId, item.cid, LOCAL_FAILED);
114226
+ results.push({ peerChatPK: item.peerChatPK, ok: false, error });
114227
+ }
114032
114228
  }
114033
114229
  }
114034
114230
  return results;
@@ -114835,7 +115031,6 @@ async function decryptRecordEntries(records, userChatPK, userPrivKey, peerChatPK
114835
115031
  return [];
114836
115032
  }
114837
115033
  const signingKeysVersion = signingKeysFingerprint(options?.signingKeysByChatKey);
114838
- const pair = await getCachedPair(userChatPK, userPrivKey, peerChatPK, { chatId: options?.chatId });
114839
115034
  const entryOptions = { ...options, signingKeysVersion };
114840
115035
  const shouldYield = positiveInt(options?.yieldEvery, 0) > 0;
114841
115036
  const entries = new Array(source.length).fill(null);
@@ -114852,38 +115047,40 @@ async function decryptRecordEntries(records, userChatPK, userPrivKey, peerChatPK
114852
115047
  }
114853
115048
  pending.push({ record, index });
114854
115049
  };
114855
- const openPending = async (pending) => {
114856
- if (!pending.length) {
114857
- return;
114858
- }
114859
- const opened = await decryptRecordBatchWithPair(pending.map((item) => item.record), pair, cache, entryOptions);
114860
- for (let index = 0;index < opened.length; index += 1) {
114861
- const entry = opened[index];
114862
- if (entry) {
114863
- entries[pending[index].index] = entry;
115050
+ return withCachedPair(userChatPK, userPrivKey, peerChatPK, { chatId: options?.chatId }, async (pair) => {
115051
+ const openPending = async (pending) => {
115052
+ if (!pending.length) {
115053
+ return;
115054
+ }
115055
+ const opened = await decryptRecordBatchWithPair(pending.map((item) => item.record), pair, cache, entryOptions);
115056
+ for (let index = 0;index < opened.length; index += 1) {
115057
+ const entry = opened[index];
115058
+ if (entry) {
115059
+ entries[pending[index].index] = entry;
115060
+ }
115061
+ }
115062
+ };
115063
+ if (!options?.signal && !shouldYield) {
115064
+ const pending = [];
115065
+ for (let index = 0;index < source.length; index += 1) {
115066
+ collectRecord(source[index], index, pending);
114864
115067
  }
115068
+ await openPending(pending);
115069
+ return entries.filter(Boolean);
114865
115070
  }
114866
- };
114867
- if (!options?.signal && !shouldYield) {
114868
- const pending = [];
114869
- for (let index = 0;index < source.length; index += 1) {
114870
- collectRecord(source[index], index, pending);
115071
+ const chunkSize = positiveInt(options?.yieldEvery, source.length || 1);
115072
+ for (let index = 0;index < source.length; index += chunkSize) {
115073
+ throwIfAborted(options?.signal);
115074
+ const chunk = source.slice(index, index + chunkSize);
115075
+ const pending = [];
115076
+ for (let offset = 0;offset < chunk.length; offset += 1) {
115077
+ collectRecord(chunk[offset], index + offset, pending);
115078
+ }
115079
+ await openPending(pending);
115080
+ await maybeYield(index + chunk.length, options);
114871
115081
  }
114872
- await openPending(pending);
114873
115082
  return entries.filter(Boolean);
114874
- }
114875
- const chunkSize = positiveInt(options?.yieldEvery, source.length || 1);
114876
- for (let index = 0;index < source.length; index += chunkSize) {
114877
- throwIfAborted(options?.signal);
114878
- const chunk = source.slice(index, index + chunkSize);
114879
- const pending = [];
114880
- for (let offset = 0;offset < chunk.length; offset += 1) {
114881
- collectRecord(chunk[offset], index + offset, pending);
114882
- }
114883
- await openPending(pending);
114884
- await maybeYield(index + chunk.length, options);
114885
- }
114886
- return entries.filter(Boolean);
115083
+ });
114887
115084
  }
114888
115085
  function messageQueryLimit(pageSize) {
114889
115086
  return positiveInt(pageSize, MSG_BATCH_SIZE);
@@ -115229,9 +115426,10 @@ async function decryptMsg(msgData, userChatPK, userPrivKey, peerChatPK, options
115229
115426
  return null;
115230
115427
  }
115231
115428
  try {
115232
- const pair = await getCachedPair(userChatPK, userPrivKey, peerChatPK, { chatId: options?.chatId });
115233
- const message = await openMsg(pair, msgData, { signingKeysByChatKey: options?.signingKeysByChatKey, crypto: options?.crypto });
115234
- return normalizeDecryptedMsg(msgData, message);
115429
+ return await withCachedPair(userChatPK, userPrivKey, peerChatPK, { chatId: options?.chatId }, async (pair) => {
115430
+ const message = await openMsg(pair, msgData, { signingKeysByChatKey: options?.signingKeysByChatKey, crypto: options?.crypto });
115431
+ return normalizeDecryptedMsg(msgData, message);
115432
+ });
115235
115433
  } catch {
115236
115434
  return null;
115237
115435
  }
@@ -116301,12 +116499,12 @@ function warmKeyStore(ref) {
116301
116499
  }
116302
116500
  return ref.current;
116303
116501
  }
116304
- function readMessageMedia2(cloud, media, chatPK, chatPrivateKey, peerChatPK, message) {
116502
+ function readMessageMedia2(cloud, media, message) {
116305
116503
  const readChatMedia = cloud?.chat?.media?.read;
116306
116504
  if (typeof media?.readMessageFile === "function") {
116307
- return media.readMessageFile(readChatMedia, chatPK, chatPrivateKey, peerChatPK, message);
116505
+ return media.readMessageFile(readChatMedia, message);
116308
116506
  }
116309
- return readMsgMedia(readChatMedia, chatPK, chatPrivateKey, peerChatPK, message);
116507
+ return readMsgMedia(readChatMedia, message);
116310
116508
  }
116311
116509
  function isDenied3(error) {
116312
116510
  return error?.code === "permission-denied";
@@ -116513,12 +116711,12 @@ function createChatMessageBatches({ cloud, media = {}, chatPK, chatPrivateKey, c
116513
116711
  return;
116514
116712
  }
116515
116713
  const tasks = buildMediaTasks();
116516
- const readFile = async (peerChatPK, message) => {
116714
+ const readFile = async (_peerChatPK, message) => {
116517
116715
  const cached = await readCachedMedia(localCache, message);
116518
116716
  if (cached?.byteLength) {
116519
116717
  return cached;
116520
116718
  }
116521
- const bytes = await readMessageMedia2(cloud, media, chatPK, chatPrivateKey, peerChatPK, message);
116719
+ const bytes = await readMessageMedia2(cloud, media, message);
116522
116720
  if (bytes?.byteLength) {
116523
116721
  saveMedia(localCache, message, bytes, message);
116524
116722
  }
@@ -117265,6 +117463,16 @@ function pingSigningKeys(ping, existing) {
117265
117463
  [ping.payload.senderChatPK]: ping.payload.signingPublicKey
117266
117464
  };
117267
117465
  }
117466
+ function chatSettingsFromPreview(settings, preview2) {
117467
+ const current = normalizeChatSettings(settings);
117468
+ if (!isSystemMsg(preview2)) {
117469
+ return current;
117470
+ }
117471
+ return {
117472
+ ...current,
117473
+ retention: cleanChatRetention(preview2.retention)
117474
+ };
117475
+ }
117268
117476
  function chatFromPing(ping, entryId, existing, preview2, userChatPK, ms, options = {}) {
117269
117477
  const ts = timestampMs(ms, null) ?? timestampMs(existing?.ts, null) ?? timestampMs(preview2?.ts, null) ?? 0;
117270
117478
  if (!ts) {
@@ -117281,7 +117489,7 @@ function chatFromPing(ping, entryId, existing, preview2, userChatPK, ms, options
117281
117489
  peerChatPK: ping.payload.senderChatPK,
117282
117490
  peerUid: existing?.peerUid || cleanText(ping.payload.senderUid) || null,
117283
117491
  signingKeysByChatKey: pingSigningKeys(ping, existing),
117284
- settings: existing?.settings,
117492
+ settings: chatSettingsFromPreview(existing?.settings, preview2),
117285
117493
  saved: existing?.saved || null,
117286
117494
  readMs,
117287
117495
  startMs,
@@ -117340,7 +117548,7 @@ async function savePing(cloud, uid, userChatPK, userPrivKey, ping, options = {})
117340
117548
  peerUid: peerUid2 || existing?.peerUid,
117341
117549
  peerSigningPublicKey: ping.payload.signingPublicKey || existing?.signingKeysByChatKey?.[ping.payload.senderChatPK],
117342
117550
  signingKeysByChatKey,
117343
- settings: existing?.settings,
117551
+ settings: chatSettingsFromPreview(existing?.settings, preview2),
117344
117552
  preview: preview2 || existing?.preview,
117345
117553
  readMs: existing?.readMs,
117346
117554
  startMs: existing ? existing.startMs : timestampMs(options.startPreview?.ts ?? preview2?.ts, null, { positive: true })
@@ -117529,6 +117737,7 @@ var init_inbox = __esm(() => {
117529
117737
  init_ping();
117530
117738
  init_query();
117531
117739
  init_chats();
117740
+ init_ttl();
117532
117741
  init_time();
117533
117742
  init_protocol();
117534
117743
  });
@@ -118024,6 +118233,7 @@ function createChatList({
118024
118233
  let chats = [];
118025
118234
  let peers = [];
118026
118235
  let isChatDataReady = false;
118236
+ let isChatListLive = false;
118027
118237
  let chatSourceReady = false;
118028
118238
  let lastChat = null;
118029
118239
  let serverChatIds = [];
@@ -118060,6 +118270,7 @@ function createChatList({
118060
118270
  chats,
118061
118271
  peers,
118062
118272
  isChatDataReady,
118273
+ isChatListLive,
118063
118274
  hasChats: chats.length > 0,
118064
118275
  previewNow,
118065
118276
  lastChat,
@@ -118093,6 +118304,13 @@ function createChatList({
118093
118304
  isChatDataReady = value;
118094
118305
  publish();
118095
118306
  };
118307
+ const setIsChatListLive = (next) => {
118308
+ const value = nextValue(isChatListLive, next);
118309
+ if (Object.is(isChatListLive, value))
118310
+ return;
118311
+ isChatListLive = value;
118312
+ publish();
118313
+ };
118096
118314
  const setLastChat = (next) => {
118097
118315
  const value = nextValue(lastChat, next);
118098
118316
  if (Object.is(lastChat, value))
@@ -118302,6 +118520,7 @@ function createChatList({
118302
118520
  const resetChatList = (ready = false) => {
118303
118521
  chatSourceReady = ready;
118304
118522
  syncChatDataReady();
118523
+ setIsChatListLive(false);
118305
118524
  setChats((prev) => prev.length ? [] : prev);
118306
118525
  setPeers((prev) => prev.length ? [] : prev);
118307
118526
  setLastChat((prev) => prev ? null : prev);
@@ -118391,6 +118610,7 @@ function createChatList({
118391
118610
  resetAllChatState();
118392
118611
  return;
118393
118612
  }
118613
+ setIsChatListLive(false);
118394
118614
  let cancelled = false;
118395
118615
  const hydrateKey = localCache?.id ? `${localCache.id}:${chatPK}` : "";
118396
118616
  if (hydrateKey && lastHydratedCacheKeyRef.current !== hydrateKey) {
@@ -118490,6 +118710,7 @@ function createChatList({
118490
118710
  writeCache: true,
118491
118711
  serverEndReached: cachedHistoryEndRef.current
118492
118712
  });
118713
+ setIsChatListLive(true);
118493
118714
  listenUpdateSeqRef.current += 1;
118494
118715
  markDiag(diag, "chat.provider.listen.update", {
118495
118716
  seq: listenUpdateSeqRef.current,
@@ -118532,6 +118753,7 @@ function createChatList({
118532
118753
  markChatSourceReady();
118533
118754
  fillVisibleChatPage();
118534
118755
  }, (error) => {
118756
+ setIsChatListLive(false);
118535
118757
  markError(diag, "chat.provider.listen", listenStartedAt, error);
118536
118758
  if (shouldResetChatListAfterListenError(error)) {
118537
118759
  resetAllChatState();
@@ -122221,11 +122443,6 @@ function createWalletPrivacy({ wallet, ghostWallet, diag }) {
122221
122443
  const startedAt = Date.now();
122222
122444
  const desired = desiredPrivacy;
122223
122445
  markDiag(diag, "wallet.privacy.start", { ghostWallet: desired });
122224
- const current = typeof wallet.getWalletSettings === "function" ? await wallet.getWalletSettings() : null;
122225
- if (closed || requestRevision !== revision || current?.privateEnabled === true === desired) {
122226
- markDone(diag, "wallet.privacy", startedAt, { changed: false, cancelled: closed || requestRevision !== revision });
122227
- return;
122228
- }
122229
122446
  await wallet.setPrivacyEnabled(desired);
122230
122447
  markDone(diag, "wallet.privacy", startedAt, { changed: true });
122231
122448
  }).catch((error) => {
@@ -123075,7 +123292,7 @@ function adaptiveClaimBatchSize(maxBatchSize, queueDepth) {
123075
123292
  }
123076
123293
  return 1;
123077
123294
  }
123078
- function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomingTransfers, pendingClaimBatchSize = null, eagerOutgoingStatusRefresh = false, transferStore, appState = null, deferBackgroundWork = null, backgroundSignal = null, diag }) {
123295
+ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomingTransfers, pendingClaimBatchSize = null, eagerOutgoingStatusRefresh = false, eagerCacheHistory = true, transferStore, appState = null, deferBackgroundWork = null, backgroundSignal = null, diag }) {
123079
123296
  if (!transferStore || typeof transferStore.subscribe !== "function" || typeof transferStore.getSnapshot !== "function" || typeof transferStore.getCacheState !== "function" || typeof transferStore.setHistory !== "function") {
123080
123297
  throw new Error("createWalletTransferSession requires transferStore");
123081
123298
  }
@@ -123089,6 +123306,7 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
123089
123306
  const initialTransfers = canUseInitialStore ? transferStore.getTransfers() : [];
123090
123307
  const initialHistoryTransfers = canUseInitialStore ? transferStore.getHistoryTransfers() : [];
123091
123308
  const initialTxReady = canUseInitialStore && (initialStoreSnapshot.ready || initialTransfers.length > 0 || initialHistoryTransfers.length > 0);
123309
+ const initialCacheReady = initialTxReady && (eagerCacheHistory === false || initialStoreSnapshot.serverEndReached === true && initialHistoryTransfers.length >= initialStoreSnapshot.historyCount);
123092
123310
  const listeners = new Set;
123093
123311
  let isTxLoading = false;
123094
123312
  let publishedSnapshot = null;
@@ -124805,6 +125023,9 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
124805
125023
  if (!wallet || !localCache || !walletPKRef.current) {
124806
125024
  return;
124807
125025
  }
125026
+ if (initialCacheReady) {
125027
+ return;
125028
+ }
124808
125029
  const hydrateKey = `${localCache.id || "cache"}:${walletPKRef.current}`;
124809
125030
  if (cacheHydrateKeyRef.current === hydrateKey) {
124810
125031
  return;
@@ -124829,7 +125050,7 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
124829
125050
  return;
124830
125051
  }
124831
125052
  const hydratedHead = applyCachedTransferState(cachedHead, { label: "wallet.provider.cache.hydrate", publish: true, startedAt: hydrateStartedAt });
124832
- if (cancelled)
125053
+ if (cancelled || !eagerCacheHistory)
124833
125054
  return;
124834
125055
  await loadFullTransferCache({ label: "wallet.provider.cache.fullHydrate", publish: !hydratedHead });
124835
125056
  }).catch((error) => {
@@ -125238,6 +125459,7 @@ function createWalletSession({
125238
125459
  pendingClaimBatchSize = null,
125239
125460
  activeTransferRefreshMs = null,
125240
125461
  eagerOutgoingStatusRefresh = false,
125462
+ eagerCacheHistory = true,
125241
125463
  sentTransferRefreshDelayMs = null,
125242
125464
  deferBackgroundWork = null,
125243
125465
  appState = null,
@@ -125266,6 +125488,7 @@ function createWalletSession({
125266
125488
  claimIncomingTransfers,
125267
125489
  pendingClaimBatchSize,
125268
125490
  eagerOutgoingStatusRefresh,
125491
+ eagerCacheHistory,
125269
125492
  transferStore,
125270
125493
  appState,
125271
125494
  deferBackgroundWork,
@@ -125376,7 +125599,7 @@ function createWalletSession({
125376
125599
  balanceReady: balanceState.balanceReady,
125377
125600
  isBalanceLoading: balanceState.isBalanceLoading,
125378
125601
  isWalletDataReady: true,
125379
- isWalletDataLoaded: balanceState.balanceReady && currentTransferState.txReady,
125602
+ isWalletDataLoaded: balanceState.balanceReady || currentTransferState.txReady,
125380
125603
  refresh: claims.refresh,
125381
125604
  requestTransferDiscoveryHotWindow: polling.requestTransferDiscoveryHotWindow,
125382
125605
  claimDeposits: claims.claimDeposits,
@@ -169318,7 +169541,12 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
169318
169541
  "Content-Type": upload?.metadata?.contentType || "application/octet-stream"
169319
169542
  };
169320
169543
  if (typeof uploadSignedStorageBytes === "function") {
169321
- await uploadSignedStorageBytes(url, upload?.body, { ...signed, method, headers, metadata: upload?.metadata || {}, path: upload?.path || "" });
169544
+ await uploadSignedStorageBytes(url, upload?.body, { ...signed, method, headers, metadata: upload?.metadata || {}, path: upload?.path || "" }).catch((error) => {
169545
+ if (error?.status === 412 && (headers["X-Goog-If-Generation-Match"] === "0" || headers["x-goog-if-generation-match"] === "0")) {
169546
+ return true;
169547
+ }
169548
+ throw error;
169549
+ });
169322
169550
  return true;
169323
169551
  }
169324
169552
  const payload = typeof Blob !== "undefined" && upload?.body instanceof Blob ? upload.body : toBytes(upload?.body, "upload bytes");
@@ -169327,6 +169555,9 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
169327
169555
  headers,
169328
169556
  body: payload
169329
169557
  });
169558
+ if (response.status === 412 && (headers["X-Goog-If-Generation-Match"] === "0" || headers["x-goog-if-generation-match"] === "0")) {
169559
+ return true;
169560
+ }
169330
169561
  if (!response.ok) {
169331
169562
  const error = new Error(`signed upload failed (${response.status || 0})`);
169332
169563
  error.status = response.status || 0;
@@ -169841,20 +170072,12 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
169841
170072
  const snapshot = await getDocs(roleQuery);
169842
170073
  return peerRecordsFromSnapshot(snapshot);
169843
170074
  }
169844
- async function uploadChatMedia(upload) {
170075
+ async function uploadMedia(upload) {
169845
170076
  const path = upload?.path;
169846
170077
  if (!path)
169847
170078
  throw new Error("media path required");
169848
- getChatMediaFileRef(path);
169849
- await uploadStorageFile(path, upload?.body, upload?.metadata || {});
169850
- return true;
169851
- }
169852
- async function uploadSharedMedia(upload) {
169853
- const path = upload?.path;
169854
- if (!path)
169855
- throw new Error("shared media path required");
169856
- const signed = await callFunction("reserveSharedMediaUpload", {
169857
- sharedId: upload?.sharedId,
170079
+ getMediaFileRef(path);
170080
+ const signed = await callFunction("reserveMediaUpload", {
169858
170081
  path,
169859
170082
  size: uploadByteSize(upload?.body),
169860
170083
  contentType: upload?.metadata?.contentType || "application/octet-stream"
@@ -169862,6 +170085,14 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
169862
170085
  await uploadSignedStorageFile(upload, signed?.upload);
169863
170086
  return true;
169864
170087
  }
170088
+ async function uploadChatMedia(upload) {
170089
+ getChatMediaFileRef(upload?.path);
170090
+ return uploadMedia(upload);
170091
+ }
170092
+ async function uploadSharedMedia(upload) {
170093
+ getSharedMediaFileRef(upload?.path);
170094
+ return uploadMedia(upload);
170095
+ }
169865
170096
  async function readChatMedia(path) {
169866
170097
  if (!path)
169867
170098
  throw new Error("media path required");
@@ -174890,7 +175121,7 @@ var package_default;
174890
175121
  var init_package = __esm(() => {
174891
175122
  package_default = {
174892
175123
  name: "veyl",
174893
- version: "0.32.1",
175124
+ version: "0.35.0",
174894
175125
  private: true,
174895
175126
  license: "Apache-2.0",
174896
175127
  workspaces: {
@@ -174906,16 +175137,16 @@ var init_package = __esm(() => {
174906
175137
  "@noble/curves": "^2.2.0",
174907
175138
  "@noble/hashes": "^2.2.0",
174908
175139
  "@scure/bip39": "^2.2.0",
174909
- "@tailwindcss/postcss": "^4.3.2",
175140
+ "@tailwindcss/postcss": "^4.3.3",
174910
175141
  bech32: "^2.0.0",
174911
175142
  firebase: "^12.16.0",
174912
175143
  "lucide-react": "^1.24.0",
174913
175144
  next: "16.3.0-preview.6",
174914
175145
  "next-themes": "^0.4.6",
174915
- postcss: "^8.5.19",
174916
- react: "19.2.7",
174917
- "react-dom": "19.2.7",
174918
- tailwindcss: "^4.3.2",
175146
+ postcss: "^8.5.22",
175147
+ react: "19.2.8",
175148
+ "react-dom": "19.2.8",
175149
+ tailwindcss: "^4.3.3",
174919
175150
  "tw-animate-css": "^1.4.0"
174920
175151
  }
174921
175152
  },
@@ -175687,6 +175918,7 @@ class VeylHeadlessClient {
175687
175918
  this.userOwner.setNetwork(network);
175688
175919
  }
175689
175920
  });
175921
+ await opened.walletReady;
175690
175922
  this.userSnapshot = this.userOwner.getSnapshot();
175691
175923
  } catch (error) {
175692
175924
  if (opened)