@glyphteck/veyl 0.70.0 → 0.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1008,6 +1008,16 @@ function numberToBytesBE(n, len) {
1008
1008
  function numberToBytesLE(n, len) {
1009
1009
  return numberToBytesBE(n, len).reverse();
1010
1010
  }
1011
+ function equalBytes(a, b) {
1012
+ a = abytes2(a);
1013
+ b = abytes2(b);
1014
+ if (a.length !== b.length)
1015
+ return false;
1016
+ let diff = 0;
1017
+ for (let i = 0;i < a.length; i++)
1018
+ diff |= a[i] ^ b[i];
1019
+ return diff === 0;
1020
+ }
1011
1021
  function copyBytes2(bytes) {
1012
1022
  return Uint8Array.from(abytes2(bytes));
1013
1023
  }
@@ -4725,7 +4735,7 @@ function checkOpts2(defaults, opts) {
4725
4735
  const merged = Object.assign(defaults, opts);
4726
4736
  return merged;
4727
4737
  }
4728
- function equalBytes(a, b) {
4738
+ function equalBytes2(a, b) {
4729
4739
  a = abytes3(a);
4730
4740
  b = abytes3(b);
4731
4741
  if (a.length !== b.length)
@@ -5169,7 +5179,7 @@ var init_aes = __esm(() => {
5169
5179
  const passedTag = ciphertext.subarray(-tagLength);
5170
5180
  const tag = _computeTag(authKey, tagMask, data);
5171
5181
  toClean.push(tag);
5172
- if (!equalBytes(tag, passedTag)) {
5182
+ if (!equalBytes2(tag, passedTag)) {
5173
5183
  clean2(...toClean);
5174
5184
  throw new Error("aes-gcm: invalid tag");
5175
5185
  }
@@ -5856,7 +5866,7 @@ function sameText(left, right) {
5856
5866
  }
5857
5867
 
5858
5868
  // ../../core/config.js
5859
- 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 = 15, PASSWORD_MAX_CHARS = 64, PASSWORD_MAX_BYTES, ACCOUNT_CONNECTION_SLOW_MS, ACCOUNT_CONNECTION_UNAVAILABLE_MS, ACCOUNT_CONNECTION_RETRY_MIN_MS, ACCOUNT_CONNECTION_RETRY_MAX_MS, AUTOLOCK_MIN_MINUTES = 1, AUTOLOCK_MAX_MINUTES = 60, BTC_PRICE_FALLBACK = 80000, CHAT_MESSAGE_EDIT_WINDOW_MS, CHAT_SEND_CONNECTION_WAIT_MS, 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_CHAT_MESSAGE_CACHE_WRITE_DELAY_MS = 2500, LOCAL_AVATAR_CACHE_MAX_BYTES, LOCAL_AVATAR_CACHE_MAX_AGE_MS, AVATAR_IMAGE_MAX_BYTES, AVATAR_IMAGE_QUALITY_ATTEMPTS, CHAT_AVATAR_IMAGE_MAX_BYTES, CHAT_AVATAR_REF_MAX_CHARS = 256, 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_MESSAGE_MUTATION_MAX_ITEMS = 256, CHAT_TTL_CLIENT_DELETE_GRACE_MS, 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_INBOX_DISCOVERY_PARALLEL_CHATS, CHAT_INBOX_SETTLEMENT_PARALLEL_CHATS = 4, CHAT_INBOX_CURSOR_OVERLAP_MS, CHAT_LIST_CACHE_WRITE_DELAY_MS = 1500, CHAT_LIST_SNAPSHOT_COALESCE_MS = 80, CHAT_LIST_COMPLETE_SYNC_DELAY_MS = 250, 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_READ_WRITE_MAX_WAIT_MS, CHAT_LIVE_PING_INTERVAL_MS, CHAT_LIVE_SWEEP_INTERVAL_MS, CHAT_LIVE_STALE_AFTER_MS, CHAT_LIVE_RECONNECT_MIN_MS = 250, CHAT_LIVE_RECONNECT_MAX_MS, CHAT_LIVE_TYPING_IDLE_MS, CHAT_LIVE_TYPING_RENEW_MS, CHAT_LIVE_COMPOSITION_HANDOFF_MS, CHAT_LIVE_READ_SEND_INTERVAL_MS = 120, CHAT_LIVE_READ_SEND_MAX_WAIT_MS = 500, CHAT_LIVE_READ_WRITE_INTERVAL_MS, CHAT_LIVE_READ_WRITE_MAX_WAIT_MS, CHAT_SEND_QUEUE_RATE_LIMIT_COUNT = 12, CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS, CHAT_SETTINGS_BODY_MAX_BYTES, CHAT_TITLE_STATE_MAX_CHARS = 128, CHAT_MANIFEST_MAX_BYTES, CHAT_RECEIPT_MAX_MEMBERS = 32, CHAT_ORDINARY_DELIVERY_MAX_MEMBERS = 128, CHAT_MAX_MEMBERS = 256, CHAT_LARGE_GROUP_MENTION_MAX_TARGETS = 16, CHAT_MAX_TEXT_CHARS = 2048, CHAT_TITLE_INPUT_MAX_CHARS = 16, CHAT_MAX_REACTIONS, 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_THROTTLE_MS = 120, BAN_REFRESH_GRACE_MS = 50, REQUEST_MONEY_MAX_SATS, WALLET_TRANSFER_POLL_MS, WALLET_ACTIVE_TRANSFER_REFRESH_MS, 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_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;
5869
+ 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 = 15, PASSWORD_MAX_CHARS = 64, PASSWORD_MAX_BYTES, ACCOUNT_CONNECTION_SLOW_MS, ACCOUNT_CONNECTION_UNAVAILABLE_MS, ACCOUNT_CONNECTION_RETRY_MIN_MS, ACCOUNT_CONNECTION_RETRY_MAX_MS, AUTOLOCK_MIN_MINUTES = 1, AUTOLOCK_MAX_MINUTES = 60, BTC_PRICE_FALLBACK = 80000, CHAT_MESSAGE_EDIT_WINDOW_MS, CHAT_SEND_CONNECTION_WAIT_MS, 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_CHAT_MESSAGE_CACHE_WRITE_DELAY_MS = 2500, LOCAL_AVATAR_CACHE_MAX_BYTES, LOCAL_AVATAR_CACHE_MAX_AGE_MS, AVATAR_IMAGE_MAX_BYTES, AVATAR_IMAGE_QUALITY_ATTEMPTS, CHAT_AVATAR_IMAGE_MAX_BYTES, CHAT_AVATAR_REF_MAX_CHARS = 256, 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_MESSAGE_MUTATION_MAX_ITEMS = 256, CHAT_TTL_CLIENT_DELETE_GRACE_MS, CHAT_BATCH_CLEANUP_IDLE_TIMEOUT_MS = 1500, CHAT_BATCH_CLEANUP_IDLE_DELAY_MS = 250, CHAT_LIST_PAGE_SIZE = 20, CHAT_ADMISSION_CACHE_PEERS = 1024, CHAT_LIST_LIVE_COUNT, CHAT_INBOX_PING_PAGE_SIZE = 25, CHAT_INBOX_DISCOVERY_PARALLEL_CHATS, CHAT_INBOX_SETTLEMENT_PARALLEL_CHATS = 4, CHAT_INBOX_CURSOR_OVERLAP_MS, CHAT_LIST_CACHE_WRITE_DELAY_MS = 1500, CHAT_LIST_SNAPSHOT_COALESCE_MS = 80, CHAT_LIST_COMPLETE_SYNC_DELAY_MS = 250, 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_READ_WRITE_MAX_WAIT_MS, CHAT_LIVE_PING_INTERVAL_MS, CHAT_LIVE_SWEEP_INTERVAL_MS, CHAT_LIVE_STALE_AFTER_MS, CHAT_LIVE_RECONNECT_MIN_MS = 250, CHAT_LIVE_RECONNECT_MAX_MS, CHAT_LIVE_TYPING_IDLE_MS, CHAT_LIVE_TYPING_RENEW_MS, CHAT_LIVE_COMPOSITION_HANDOFF_MS, CHAT_LIVE_READ_SEND_INTERVAL_MS = 120, CHAT_LIVE_READ_SEND_MAX_WAIT_MS = 500, CHAT_LIVE_READ_WRITE_INTERVAL_MS, CHAT_LIVE_READ_WRITE_MAX_WAIT_MS, CHAT_SEND_QUEUE_RATE_LIMIT_COUNT = 12, CHAT_SEND_QUEUE_RATE_LIMIT_WINDOW_MS, CHAT_SETTINGS_BODY_MAX_BYTES, CHAT_TITLE_STATE_MAX_CHARS = 128, CHAT_MANIFEST_MAX_BYTES, CHAT_RECEIPT_MAX_MEMBERS = 32, CHAT_ORDINARY_DELIVERY_MAX_MEMBERS = 128, CHAT_MAX_MEMBERS = 256, CHAT_LARGE_GROUP_MENTION_MAX_TARGETS = 16, CHAT_MAX_TEXT_CHARS = 2048, CHAT_TITLE_INPUT_MAX_CHARS = 16, CHAT_MAX_REACTIONS, 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_THROTTLE_MS = 120, BAN_REFRESH_GRACE_MS = 50, REQUEST_MONEY_MAX_SATS, WALLET_TRANSFER_POLL_MS, WALLET_ACTIVE_TRANSFER_REFRESH_MS, 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_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;
5860
5870
  var init_config = __esm(() => {
5861
5871
  MINUTE_MS = 60 * MS_PER_SECOND;
5862
5872
  HOUR_MS = 60 * MINUTE_MS;
@@ -5997,9 +6007,12 @@ function normalizeChatNotificationMode(value, manifest) {
5997
6007
  const mode = cleanText(value);
5998
6008
  if (!NOTIFICATION_MODES.has(mode))
5999
6009
  throw new Error("chat notification mode required");
6000
- if (manifest?.lineage !== "group")
6010
+ if (manifest?.lineage === "self")
6001
6011
  return CHAT_NOTIFICATION_MODES.ALL;
6002
- if (mode === CHAT_NOTIFICATION_MODES.ALL && manifest.members?.length > CHAT_ORDINARY_DELIVERY_MAX_MEMBERS) {
6012
+ if (manifest?.lineage === "direct" && mode === CHAT_NOTIFICATION_MODES.MENTIONS) {
6013
+ throw new Error("mentions notifications require a group chat");
6014
+ }
6015
+ if (mode === CHAT_NOTIFICATION_MODES.ALL && manifest?.lineage === "group" && manifest.members?.length > CHAT_ORDINARY_DELIVERY_MAX_MEMBERS) {
6003
6016
  return CHAT_NOTIFICATION_MODES.MENTIONS;
6004
6017
  }
6005
6018
  return mode;
@@ -7109,7 +7122,7 @@ var xchacha20, ZEROS162, updatePadded = (h, msg) => {
7109
7122
  const data = ciphertext.subarray(0, -tagLength);
7110
7123
  const passedTag = ciphertext.subarray(-tagLength);
7111
7124
  const tag = computeTag2(xorStream, key, nonce, data, AAD);
7112
- if (!equalBytes(passedTag, tag)) {
7125
+ if (!equalBytes2(passedTag, tag)) {
7113
7126
  clean2(tag);
7114
7127
  throw new Error("invalid tag");
7115
7128
  }
@@ -8849,35 +8862,68 @@ function chatAdmissionCapabilityCommitment(value) {
8849
8862
  cleanBytes(capability);
8850
8863
  }
8851
8864
  }
8852
- function directAdmissionCapabilityForPeer(chatPrivateKey, chatPK, profile) {
8853
- return deriveDirectAdmissionCapability(chatPrivateKey, chatPK, profile?.chatPK, profile?.chatPK);
8854
- }
8855
- function hasDirectAdmissionGrant(chatPrivateKey, chatPK, profile) {
8856
- const policy = normalizeChatAdmission(profile?.chatAdmission);
8857
- if (!policy.directGrants.length)
8858
- return false;
8859
- const capability = directAdmissionCapabilityForPeer(chatPrivateKey, chatPK, profile);
8860
- try {
8861
- return policy.directGrants.includes(chatAdmissionCapabilityCommitment(capability));
8862
- } finally {
8863
- cleanBytes(capability);
8864
- }
8865
+ function deriveDirectAdmissionCommitment(chatPrivateKey, chatPK, peerChatPK, recipientChatPK) {
8866
+ return chatAdmissionCapabilityCommitment(deriveDirectAdmissionCapability(chatPrivateKey, chatPK, peerChatPK, recipientChatPK));
8865
8867
  }
8866
- function directAdmissionForPeer(chatPrivateKey, chatPK, profile, ownAdmission = null) {
8867
- if (ownAdmission != null && normalizeChatAdmission(ownAdmission).direct === CHAT_DIRECT_ADMISSION_MODES.CLOSED && incomingChatAdmissionDecision(chatPrivateKey, chatPK, profile, ownAdmission, "direct") !== CHAT_ADMISSION_DECISIONS.ACCEPT)
8868
+ function directAdmissionDecision(chatPK, profile, ownAdmission, commitmentFor) {
8869
+ const ownPolicy = normalizeChatAdmission(ownAdmission);
8870
+ if (ownAdmission != null && ownPolicy.direct === CHAT_DIRECT_ADMISSION_MODES.CLOSED && !ownPolicy.directGrants.includes(commitmentFor(profile?.chatPK, chatPK)))
8868
8871
  return CHAT_ADMISSION_DECISIONS.REJECT;
8869
- if (hasDirectAdmissionGrant(chatPrivateKey, chatPK, profile)) {
8872
+ const policy = normalizeChatAdmission(profile?.chatAdmission);
8873
+ if (policy.directGrants.length && policy.directGrants.includes(commitmentFor(profile?.chatPK, profile?.chatPK))) {
8870
8874
  return CHAT_ADMISSION_DECISIONS.ACCEPT;
8871
8875
  }
8872
- const mode = normalizeChatAdmission(profile?.chatAdmission).direct;
8876
+ const mode = policy.direct;
8873
8877
  if (mode === CHAT_DIRECT_ADMISSION_MODES.OPEN)
8874
8878
  return CHAT_ADMISSION_DECISIONS.ACCEPT;
8875
8879
  if (mode === CHAT_DIRECT_ADMISSION_MODES.REQUESTS)
8876
8880
  return CHAT_ADMISSION_DECISIONS.REQUEST;
8877
8881
  return CHAT_ADMISSION_DECISIONS.REJECT;
8878
8882
  }
8879
- function canStartDirectChat(chatPrivateKey, chatPK, profile, ownAdmission = null) {
8880
- return directAdmissionForPeer(chatPrivateKey, chatPK, profile, ownAdmission) !== CHAT_ADMISSION_DECISIONS.REJECT;
8883
+ function directAdmissionForPeer(chatPrivateKey, chatPK, profile, ownAdmission = null) {
8884
+ return directAdmissionDecision(chatPK, profile, ownAdmission, (peerChatPK, recipientChatPK) => deriveDirectAdmissionCommitment(chatPrivateKey, chatPK, peerChatPK, recipientChatPK));
8885
+ }
8886
+ function createChatAdmission({ getIdentity, deriveCommitment = deriveDirectAdmissionCommitment }) {
8887
+ let chatPK = null;
8888
+ let privateKey = null;
8889
+ let privateKeyBytes = null;
8890
+ const peers = new Map;
8891
+ const reset = () => {
8892
+ peers.clear();
8893
+ cleanBytes(privateKeyBytes);
8894
+ chatPK = null;
8895
+ privateKey = null;
8896
+ privateKeyBytes = null;
8897
+ };
8898
+ const directAdmissionForPeer2 = (profile) => {
8899
+ const identity = getIdentity();
8900
+ if (chatPK !== identity.chatPK || privateKey !== identity.chatPrivateKey || privateKey instanceof Uint8Array && !equalBytes(privateKey, privateKeyBytes)) {
8901
+ reset();
8902
+ chatPK = identity.chatPK;
8903
+ privateKey = identity.chatPrivateKey;
8904
+ privateKeyBytes = privateKey instanceof Uint8Array ? new Uint8Array(privateKey) : null;
8905
+ }
8906
+ return directAdmissionDecision(chatPK, profile, identity.chatAdmission, (peerChatPK, recipientChatPK) => {
8907
+ let commitments = peers.get(peerChatPK);
8908
+ if (!commitments?.has(recipientChatPK)) {
8909
+ const commitment = deriveCommitment(privateKey, chatPK, peerChatPK, recipientChatPK);
8910
+ if (!commitments) {
8911
+ commitments = new Map;
8912
+ if (peers.size >= CHAT_ADMISSION_CACHE_PEERS)
8913
+ peers.delete(peers.keys().next().value);
8914
+ }
8915
+ commitments.set(recipientChatPK, commitment);
8916
+ }
8917
+ peers.delete(peerChatPK);
8918
+ peers.set(peerChatPK, commitments);
8919
+ return commitments.get(recipientChatPK);
8920
+ });
8921
+ };
8922
+ return {
8923
+ directAdmissionForPeer: directAdmissionForPeer2,
8924
+ canStartDirectChat: (profile) => directAdmissionForPeer2(profile) !== CHAT_ADMISSION_DECISIONS.REJECT,
8925
+ reset
8926
+ };
8881
8927
  }
8882
8928
  function canInviteToGroup(profile, ownAdmission = null) {
8883
8929
  return (ownAdmission == null || normalizeChatAdmission(ownAdmission).groups === CHAT_GROUP_ADMISSION_MODES.OPEN) && normalizeChatAdmission(profile?.chatAdmission).groups === CHAT_GROUP_ADMISSION_MODES.OPEN;
@@ -8938,6 +8984,8 @@ var CHAT_ADMISSION_VERSION = 1, CHAT_ADMISSION_GRANT_COUNT = 8, CHAT_ADMISSION_D
8938
8984
  var init_admission = __esm(() => {
8939
8985
  init_ed25519();
8940
8986
  init_sha2();
8987
+ init_utils2();
8988
+ init_config();
8941
8989
  init_core();
8942
8990
  init_kdf();
8943
8991
  init_manifest();
@@ -11142,6 +11190,29 @@ var init_transition = __esm(() => {
11142
11190
  TRANSITION_MEMBER_SCOPE = encoder.encode("veyl-chat-transition-member-v3");
11143
11191
  });
11144
11192
 
11193
+ // ../../core/chat/capabilities.js
11194
+ function memberCount(value) {
11195
+ const count = Number(value);
11196
+ if (!Number.isSafeInteger(count) || count < 1 || count > CHAT_MAX_MEMBERS) {
11197
+ throw new Error("invalid chat member count");
11198
+ }
11199
+ return count;
11200
+ }
11201
+ function chatCapabilities(value) {
11202
+ const members = memberCount(value);
11203
+ return Object.freeze({
11204
+ members,
11205
+ sharedReceipts: members <= CHAT_RECEIPT_MAX_MEMBERS,
11206
+ ordinaryDelivery: members <= CHAT_ORDINARY_DELIVERY_MAX_MEMBERS
11207
+ });
11208
+ }
11209
+ function chatEpochCapabilities(epochState) {
11210
+ return chatCapabilities(epochState?.manifest?.members?.length);
11211
+ }
11212
+ var init_capabilities = __esm(() => {
11213
+ init_config();
11214
+ });
11215
+
11145
11216
  // ../../core/chat/ttl.js
11146
11217
  function hasChatRetention(value) {
11147
11218
  const retention = cleanText(value);
@@ -11190,7 +11261,7 @@ function messageExpiryMs(retention, seenAt) {
11190
11261
  }
11191
11262
  return cleanChatRetention(retention) === CHAT_RETENTION_24H ? seenAt + AFTER_SEEN_MS : seenAt;
11192
11263
  }
11193
- var CHAT_RETENTION_SEEN = "seen", CHAT_RETENTION_24H = "24h", CHAT_RETENTION_FOREVER = "forever", DEFAULT_CHAT_RETENTION, CHAT_RETENTION_VALUES, CHAT_PROTOCOL_RETENTION_VALUES, CHAT_RETENTION_LABELS, MESSAGE_STORAGE_TTL_MS, AFTER_SEEN_MS;
11264
+ var CHAT_RETENTION_SEEN = "seen", CHAT_RETENTION_24H = "24h", CHAT_RETENTION_FOREVER = "forever", DEFAULT_CHAT_RETENTION, CHAT_RETENTION_VALUES, CHAT_PROTOCOL_RETENTION_VALUES, CHAT_RETENTION_LABELS, CHAT_RETENTION_DESCRIPTIONS, MESSAGE_STORAGE_TTL_MS, AFTER_SEEN_MS;
11194
11265
  var init_ttl = __esm(() => {
11195
11266
  init_config();
11196
11267
  init_time();
@@ -11202,6 +11273,10 @@ var init_ttl = __esm(() => {
11202
11273
  [CHAT_RETENTION_SEEN]: "delete after seen",
11203
11274
  [CHAT_RETENTION_24H]: "keep for 24h"
11204
11275
  });
11276
+ CHAT_RETENTION_DESCRIPTIONS = Object.freeze({
11277
+ [CHAT_RETENTION_SEEN]: "messages now delete after being seen",
11278
+ [CHAT_RETENTION_24H]: "messages now delete 24h after being seen"
11279
+ });
11205
11280
  MESSAGE_STORAGE_TTL_MS = CHAT_UNSAVED_TTL_MS;
11206
11281
  AFTER_SEEN_MS = CHAT_AFTER_SEEN_MS;
11207
11282
  });
@@ -11843,6 +11918,8 @@ async function prepareOwnChatMutation(identity, entryId, current, update) {
11843
11918
  open: (record) => openOwnChatMutationEntry(identity, entryId, record),
11844
11919
  prepare: async (record) => {
11845
11920
  if (!record) {
11921
+ if (current != null)
11922
+ throw makeChatUnavailableError();
11846
11923
  return prepareMutation(identity, entryId, null, update);
11847
11924
  }
11848
11925
  if (!record?.body || !Number.isSafeInteger(record.revision) || record.revision < 1) {
@@ -12680,29 +12757,6 @@ var init_chatstate = __esm(() => {
12680
12757
  STATE_SIGN_SCOPE = encoder.encode("veyl-chat-member-state-v3-sign");
12681
12758
  });
12682
12759
 
12683
- // ../../core/chat/capabilities.js
12684
- function memberCount(value) {
12685
- const count = Number(value);
12686
- if (!Number.isSafeInteger(count) || count < 1 || count > CHAT_MAX_MEMBERS) {
12687
- throw new Error("invalid chat member count");
12688
- }
12689
- return count;
12690
- }
12691
- function chatCapabilities(value) {
12692
- const members = memberCount(value);
12693
- return Object.freeze({
12694
- members,
12695
- sharedReceipts: members <= CHAT_RECEIPT_MAX_MEMBERS,
12696
- ordinaryDelivery: members <= CHAT_ORDINARY_DELIVERY_MAX_MEMBERS
12697
- });
12698
- }
12699
- function chatEpochCapabilities(epochState) {
12700
- return chatCapabilities(epochState?.manifest?.members?.length);
12701
- }
12702
- var init_capabilities = __esm(() => {
12703
- init_config();
12704
- });
12705
-
12706
12760
  // ../../core/chat/memberstate.js
12707
12761
  function openStateEpoch(epochState, actor = null) {
12708
12762
  if (!epochState?.manifest || !epochState?.epochSecret)
@@ -13893,7 +13947,8 @@ function getMsgReactions(msg) {
13893
13947
  continue;
13894
13948
  }
13895
13949
  seen.add(user);
13896
- reactions.push({ emoji, user });
13950
+ const ts = timestampMs(item.ts, null);
13951
+ reactions.push({ emoji, user, ...ts == null ? {} : { ts } });
13897
13952
  if (reactions.length >= MAX_REACTIONS) {
13898
13953
  break;
13899
13954
  }
@@ -13906,7 +13961,7 @@ function sameReactions(a, b) {
13906
13961
  if (left.length !== right.length) {
13907
13962
  return false;
13908
13963
  }
13909
- return left.every((reaction, index) => reaction.user === right[index]?.user && reaction.emoji === right[index]?.emoji);
13964
+ return left.every((reaction, index) => reaction.user === right[index]?.user && reaction.emoji === right[index]?.emoji && reaction.ts === right[index]?.ts);
13910
13965
  }
13911
13966
  function hasReactionControl(messages) {
13912
13967
  for (const msg of messages || []) {
@@ -13946,7 +14001,8 @@ function deriveMessageReactions(messages, chatPK, peerChatPK, memberChatPKs = nu
13946
14001
  byTarget.set(target, reactions);
13947
14002
  }
13948
14003
  if (hasText(msg.emoji)) {
13949
- reactions.set(user, { emoji: cleanReactionEmoji(msg.emoji), user });
14004
+ const ts = timestampMs(getMessageOrderMs(msg), null);
14005
+ reactions.set(user, { emoji: cleanReactionEmoji(msg.emoji), user, ...ts == null ? {} : { ts } });
13950
14006
  } else {
13951
14007
  reactions.delete(user);
13952
14008
  }
@@ -15075,7 +15131,7 @@ function senderIdentity(senderChatPK, senderPrivateKey, options) {
15075
15131
  notificationPrivateKey: options.notificationPrivateKey
15076
15132
  };
15077
15133
  }
15078
- function ownerPreview(senderChatPK, message, messageId, head, tsMs, ttlMs) {
15134
+ function ownerPreview(epoch, senderChatPK, message, messageId, head, tsMs, ttlMs) {
15079
15135
  return {
15080
15136
  ...message || {},
15081
15137
  s: senderChatPK,
@@ -15084,6 +15140,8 @@ function ownerPreview(senderChatPK, message, messageId, head, tsMs, ttlMs) {
15084
15140
  id: messageId,
15085
15141
  ts: tsMs,
15086
15142
  ttl: Number.isFinite(ttlMs) ? ttlMs : null,
15143
+ epochId: epoch.epochId,
15144
+ epochMemberChatPKs: epoch.manifest.members.map((member) => member.chatPK),
15087
15145
  pending: false,
15088
15146
  failed: false
15089
15147
  };
@@ -15300,7 +15358,7 @@ async function prepareMsgRecord(identity, epochState, message, options = {}) {
15300
15358
  msgId: messageId,
15301
15359
  cid: head.cid,
15302
15360
  record: { lane: epoch.messageLane, head, body, ttlMs },
15303
- message: ownerPreview(identity.chatPK, messagePayload, messageId, head, tsMs, ttlMs),
15361
+ message: ownerPreview(epoch, identity.chatPK, messagePayload, messageId, head, tsMs, ttlMs),
15304
15362
  mentionRecipientChatPKs: mentionTargets.recipientChatPKs,
15305
15363
  tsMs
15306
15364
  };
@@ -20020,12 +20078,6 @@ function dropCachedChats(cache, chatIds, currentPayload = null) {
20020
20078
  ...ids.map((chatId) => dropCachedMessageState(cache, chatId))
20021
20079
  ]));
20022
20080
  }
20023
- function dropCachedChat(cache, chatId) {
20024
- if (!chatId) {
20025
- return;
20026
- }
20027
- dropCachedChats(cache, [chatId]);
20028
- }
20029
20081
  var init_chats2 = __esm(() => {
20030
20082
  init_config();
20031
20083
  init_time();
@@ -24926,6 +24978,17 @@ function createRouteMemory(limit = CHAT_MESSAGE_VIEW_CACHE_SIZE) {
24926
24978
  }
24927
24979
  return {
24928
24980
  clear,
24981
+ dropChat(chatId) {
24982
+ const id = String(chatId || "").trim();
24983
+ if (!id)
24984
+ return;
24985
+ const keys = chatRouteMemoryKeys(id, [...states.keys(), ...retained.keys()]);
24986
+ for (const key of keys) {
24987
+ states.delete(key);
24988
+ retained.delete(key);
24989
+ }
24990
+ activeChats.delete(id);
24991
+ },
24929
24992
  resetAccount(nextAccountKey) {
24930
24993
  const next = nextAccountKey || "";
24931
24994
  if (accountKey === next) {
@@ -26530,6 +26593,9 @@ async function verifyOpenedPingEpoch(context) {
26530
26593
  const decision = await decideIncomingChat(context, epochState);
26531
26594
  if (decision === CHAT_ADMISSION_DECISIONS.REJECT)
26532
26595
  return "rejected";
26596
+ const unavailable = await checkOpenedWelcomeAvailability(context);
26597
+ if (unavailable)
26598
+ return unavailable;
26533
26599
  if (decision === CHAT_ADMISSION_DECISIONS.REQUEST)
26534
26600
  return "request";
26535
26601
  } else if (context.entry && (epochState.manifest.lineage === "direct" || epochState.manifest.lineage === "group")) {
@@ -26539,6 +26605,27 @@ async function verifyOpenedPingEpoch(context) {
26539
26605
  }
26540
26606
  return null;
26541
26607
  }
26608
+ async function checkOpenedWelcomeAvailability(context) {
26609
+ const { cloud, identity, options, uid, epochState } = context;
26610
+ assertInboxLease(options);
26611
+ try {
26612
+ await cloud.chat.state.transition(epochState.manifest.chatId, epochState.manifest.epochId, epochState.stateCapability);
26613
+ assertInboxLease(options);
26614
+ return null;
26615
+ } catch (error) {
26616
+ const code = cleanText(error?.code).toLowerCase();
26617
+ if (code !== "permission-denied" && !code.endsWith("/permission-denied"))
26618
+ throw error;
26619
+ try {
26620
+ await retireConfirmedDeletedChat(cloud, uid, identity, context.committedEntry || context.entry || makeOwnChatEntry(epochState), options);
26621
+ return "deleted";
26622
+ } catch (confirmationError) {
26623
+ if (isRetryableInboxError(confirmationError))
26624
+ throw confirmationError;
26625
+ throw error;
26626
+ }
26627
+ }
26628
+ }
26542
26629
  function startOpenedWelcomeHydration(context) {
26543
26630
  const membershipWelcome = (!context.entry || context.restoringMembership) && context.payload.kind === "welcome";
26544
26631
  if (!membershipWelcome)
@@ -26775,17 +26862,30 @@ async function settleOpenedPing(prepared, onAuthoritative) {
26775
26862
  const createsMembership = !context.entry && context.payload.kind === "welcome";
26776
26863
  const ownerPersistence = createsMembership ? captureAsync(persistOpenedPingOwner(context)) : null;
26777
26864
  const messageStartedAt = Date.now();
26778
- const result = await applyOpenedPingMessage(context);
26865
+ const messageOutcome = await captureAsync(applyOpenedPingMessage(context));
26779
26866
  markDiag(context.options.diag, "chat.inbox.settle.message.done", {
26780
26867
  elapsedMs: Date.now() - messageStartedAt
26781
26868
  });
26782
- if (result)
26783
- return result;
26784
- prepareOpenedPingActivity(context);
26785
26869
  if (ownerPersistence)
26786
26870
  await unwrapAsync(ownerPersistence);
26787
- else
26788
- await persistOpenedPingOwner(context);
26871
+ else if (messageOutcome.error)
26872
+ throw messageOutcome.error;
26873
+ if (messageOutcome.value)
26874
+ return messageOutcome.value;
26875
+ if (!messageOutcome.error) {
26876
+ prepareOpenedPingActivity(context);
26877
+ if (!ownerPersistence)
26878
+ await persistOpenedPingOwner(context);
26879
+ }
26880
+ if (context.membershipWelcome) {
26881
+ const unavailable = await checkOpenedWelcomeAvailability(context);
26882
+ if (unavailable) {
26883
+ await cleanupOpenedPing(context);
26884
+ return unavailable;
26885
+ }
26886
+ }
26887
+ if (messageOutcome.error)
26888
+ throw messageOutcome.error;
26789
26889
  publishOpenedPing(context);
26790
26890
  onAuthoritative?.();
26791
26891
  const phaseStartedAt = Date.now();
@@ -26912,6 +27012,7 @@ async function preparePreparedPing(cloud, uid, identity, prepared, options) {
26912
27012
  ping,
26913
27013
  uid
26914
27014
  });
27015
+ assertInboxLease(options);
26915
27016
  return { opened, releaseMutation };
26916
27017
  } catch (error) {
26917
27018
  releaseMutation();
@@ -27366,10 +27467,11 @@ async function prepareInboxWork(context, prepared, processingOptions = {}) {
27366
27467
  const { blockedUids, cloud, identity, options, state, uid } = context;
27367
27468
  const { document: document2, opened } = prepared;
27368
27469
  let announcement = null;
27470
+ let ping = null;
27369
27471
  try {
27370
27472
  const authenticated = await authenticateInboxPing(cloud, identity, document2, opened, state.currentChats, context.profileCache);
27371
27473
  assertInboxLease(options);
27372
- const ping = await preparePreparedPing(cloud, uid, identity, authenticated, {
27474
+ ping = await preparePreparedPing(cloud, uid, identity, authenticated, {
27373
27475
  ...options,
27374
27476
  blockedUids,
27375
27477
  currentChats: state.currentChats,
@@ -27386,9 +27488,11 @@ async function prepareInboxWork(context, prepared, processingOptions = {}) {
27386
27488
  if (ping.opened?.result === "request") {
27387
27489
  await hydrateOpenedChatRequest(ping.opened.context);
27388
27490
  }
27491
+ assertInboxLease(options);
27389
27492
  announcement = announcePreparedWelcome(context, authenticated, ping, document2, prepared.startedAt);
27390
27493
  return { announcement, document: document2, ping, prepared, processingOptions, status: null };
27391
27494
  } catch (error) {
27495
+ ping?.releaseMutation();
27392
27496
  state.revokeAnnouncement(announcement);
27393
27497
  if (processingOptions.deferUnknownWelcome === true && error?.message === "chat mls welcome required") {
27394
27498
  markDiag(options.diag, "chat.inbox.document.deferred", { reason: "welcome_required" });
@@ -27516,6 +27620,8 @@ function createInboxSettlementController(options = {}) {
27516
27620
  for (const job of lane.jobs) {
27517
27621
  if (job.settled)
27518
27622
  continue;
27623
+ if (job.state === "ready")
27624
+ job.discard?.(job.prepared);
27519
27625
  job.settled = true;
27520
27626
  job.state = "settled";
27521
27627
  job.deferred.reject(error);
@@ -27560,9 +27666,12 @@ function createInboxSettlementController(options = {}) {
27560
27666
  }
27561
27667
  job.state = "preparing";
27562
27668
  Promise.resolve().then(job.prepare).then((prepared) => {
27563
- if (job.settled)
27669
+ if (job.settled) {
27670
+ job.discard?.(prepared);
27564
27671
  return;
27672
+ }
27565
27673
  if (!hasLease()) {
27674
+ job.discard?.(prepared);
27566
27675
  finishJob(job, inboxLeaseError());
27567
27676
  return;
27568
27677
  }
@@ -27583,6 +27692,7 @@ function createInboxSettlementController(options = {}) {
27583
27692
  if (!job || job.settled)
27584
27693
  continue;
27585
27694
  if (!hasLease()) {
27695
+ job.discard?.(job.prepared);
27586
27696
  finishJob(job, inboxLeaseError());
27587
27697
  continue;
27588
27698
  }
@@ -27618,6 +27728,7 @@ function createInboxSettlementController(options = {}) {
27618
27728
  const job = {
27619
27729
  chatId,
27620
27730
  deferred: inboxDeferred(),
27731
+ discard: task.discard,
27621
27732
  lane,
27622
27733
  prepare: task.prepare,
27623
27734
  prepared: null,
@@ -27675,6 +27786,10 @@ async function processInboxDocuments(context, documents, processingOptions = {})
27675
27786
  const deferred = [];
27676
27787
  await Promise.all(groups.map((group) => context.settlementController.schedule(cleanText(group[0]?.opened?.payload?.chatId), {
27677
27788
  prepare: () => context.discoveryPool.run(() => prepareInboxWork(context, group[0], processingOptions)),
27789
+ discard: (work) => {
27790
+ work?.ping?.releaseMutation();
27791
+ context.state.revokeAnnouncement(work?.announcement);
27792
+ },
27678
27793
  settle: async (first, markAuthoritative) => {
27679
27794
  if (await settleInboxWork(context, first, markAuthoritative) === "deferred")
27680
27795
  deferred.push(first.document);
@@ -27986,6 +28101,7 @@ function listenToChats(cloud, uid, userChatPK, userPrivKey, onUpdate, onError, o
27986
28101
  nextAfterChat: snapshot.nextAfterChat,
27987
28102
  hasMore: snapshot.hasMore,
27988
28103
  ownerListToken: snapshot.ownerListToken,
28104
+ sourceRevision: snapshot.sourceRevision,
27989
28105
  source: "server"
27990
28106
  });
27991
28107
  deleteConfirmedUserChats(cloud, uid, page.deletedEntryIds);
@@ -28210,7 +28326,8 @@ function listenToChats(cloud, uid, userChatPK, userPrivKey, onUpdate, onError, o
28210
28326
  latestOwnerSnapshot = {
28211
28327
  docs: entries,
28212
28328
  nextAfterChat: meta.nextAfterChat ?? null,
28213
- hasMore: !!meta.hasMore
28329
+ hasMore: !!meta.hasMore,
28330
+ sourceRevision: options.getSourceRevision?.()
28214
28331
  };
28215
28332
  queueOwnerSnapshot();
28216
28333
  }, onError, { limitCount: pageSize });
@@ -28721,15 +28838,15 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
28721
28838
  const ownerFor = (entry) => composeActivity(structuralOwnerFor(entry), entry);
28722
28839
  const cacheFor = (entry) => composeActivity(entry?.cache, entry);
28723
28840
  const sourceCanPublish = (chat, sourceRevision) => {
28841
+ const boundary = authorityBoundaries.get(chat?.id);
28842
+ if (boundary?.terminal)
28843
+ return false;
28724
28844
  if (!chat?.id || !Number.isSafeInteger(sourceRevision))
28725
28845
  return true;
28726
28846
  if (sourceRevision < sourceRevisionFloor)
28727
28847
  return false;
28728
- const boundary = authorityBoundaries.get(chat.id);
28729
28848
  if (!boundary)
28730
28849
  return true;
28731
- if (boundary.terminal)
28732
- return false;
28733
28850
  return sourceRevision >= boundary.revision || compareOwners(chat, boundary.owner) > 0;
28734
28851
  };
28735
28852
  const sourceCanRemove = (chatId, sourceRevision) => {
@@ -28869,21 +28986,33 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
28869
28986
  updatePeers(shown);
28870
28987
  return shown;
28871
28988
  };
28989
+ const clearRemovedOwners = (chatIds) => {
28990
+ if (!chatIds.length)
28991
+ return;
28992
+ const sources = getSources();
28993
+ for (const chatId of chatIds)
28994
+ sources.pendingDeleteIdsRef.current.delete(chatId);
28995
+ sources.onOwnerRemoved?.(chatIds);
28996
+ cache.removeMany(chatIds);
28997
+ const removedIds = new Set(chatIds);
28998
+ sources.setSelectedChat((current) => removedIds.has(current) ? null : current);
28999
+ };
28872
29000
  const convergeCompleteOwnerSet = (incomingIds = null, sourceRevision = null) => {
28873
- const evictedCacheIds = [];
29001
+ const removedIds = [];
28874
29002
  for (const [chatId, entry] of entries) {
28875
- if (!entry.observedOwner && !entry.committedOwner && !entry.announcement) {
28876
- if (!sourceCanRemove(chatId, sourceRevision))
28877
- continue;
28878
- recordTerminal(chatId, cacheFor(entry));
28879
- if (entry.cache)
28880
- evictedCacheIds.push(chatId);
28881
- entries.delete(chatId);
28882
- } else if (!incomingIds || !incomingIds.has(chatId)) {
29003
+ if (incomingIds ? incomingIds.has(chatId) : entry.observedOwner)
29004
+ continue;
29005
+ if (!sourceCanRemove(chatId, sourceRevision))
29006
+ continue;
29007
+ if (entry.announcement && !authorityBoundaries.get(chatId)?.owner) {
28883
29008
  entry.cache = null;
29009
+ continue;
28884
29010
  }
29011
+ recordTerminal(chatId, ownerFor(entry) || cacheFor(entry));
29012
+ entries.delete(chatId);
29013
+ removedIds.push(chatId);
28885
29014
  }
28886
- cache.removeMany(evictedCacheIds);
29015
+ clearRemovedOwners(removedIds);
28887
29016
  };
28888
29017
  const observeOwnerChats = (rows, options = {}) => {
28889
29018
  const incoming = (rows || []).filter((chat) => chat?.id && sourceCanPublish(chat, options.sourceRevision));
@@ -28959,6 +29088,8 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
28959
29088
  const announceWelcome = (announcement) => {
28960
29089
  if (!announcement?.chatId || !announcement?.token)
28961
29090
  return false;
29091
+ if (authorityBoundaries.get(announcement.chatId)?.terminal)
29092
+ return false;
28962
29093
  const sources = getSources();
28963
29094
  if (sources.pendingDeleteIdsRef.current.has(announcement.chatId))
28964
29095
  return false;
@@ -28995,7 +29126,6 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
28995
29126
  return false;
28996
29127
  if (!sourceCanRemove(chatId, options.sourceRevision))
28997
29128
  return false;
28998
- recordTerminal(chatId, cacheFor(entry));
28999
29129
  entry.cache = null;
29000
29130
  entry.presentation = null;
29001
29131
  cache.remove(chatId);
@@ -29005,15 +29135,12 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
29005
29135
  return true;
29006
29136
  };
29007
29137
  const removeOwner = (chatId, options = {}) => {
29008
- const sources = getSources();
29009
29138
  if (!chatId)
29010
29139
  return false;
29011
29140
  recordTerminal(chatId, ownerFor(entries.get(chatId)) || cacheFor(entries.get(chatId)));
29012
- sources.pendingDeleteIdsRef.current.delete(chatId);
29013
29141
  const removed = entries.delete(chatId);
29014
- cache.remove(chatId);
29142
+ clearRemovedOwners([chatId]);
29015
29143
  publishOwners({ ...options, warm: false });
29016
- sources.setSelectedChat((current) => current === chatId ? null : current);
29017
29144
  return removed;
29018
29145
  };
29019
29146
  const removeInboxOwner = (chatId, options = {}) => {
@@ -29370,7 +29497,7 @@ function createChatListListener({
29370
29497
  sync();
29371
29498
  }, CHAT_LIST_LISTENER_RETRY_MS);
29372
29499
  };
29373
- const subscription = subscribe(sources.cloud, sources.uid, sources.chatPK, sources.chatPrivateKey, (nextChats, _nextPeers, meta = {}) => handleUpdate(nextChats, meta, listenStartedAt, index.getAuthorityRevision()), (error) => {
29500
+ const subscription = subscribe(sources.cloud, sources.uid, sources.chatPK, sources.chatPrivateKey, (nextChats, _nextPeers, meta = {}) => handleUpdate(nextChats, meta, listenStartedAt, meta.sourceRevision), (error) => {
29374
29501
  const current = getSources();
29375
29502
  state.setIsChatListLive(false);
29376
29503
  markError(current.diag, "chat.provider.listen", listenStartedAt, error);
@@ -29381,6 +29508,7 @@ function createChatListListener({
29381
29508
  retry(error);
29382
29509
  }, {
29383
29510
  limitCount: CHAT_LIST_LIVE_COUNT,
29511
+ getSourceRevision: index.getAuthorityRevision,
29384
29512
  onInboxAnnouncement: index.announceWelcome,
29385
29513
  onInboxRevoke: index.revokeAnnouncement,
29386
29514
  onInboxCommit: index.commitOwner,
@@ -31039,7 +31167,7 @@ function patchMessageFromCachedMessage(message, cached) {
31039
31167
  const patch = {};
31040
31168
  let changed = false;
31041
31169
  const cachedReactions = getMsgReactions(cached);
31042
- if (cachedReactions.length && !sameReactionProjection(message, cached)) {
31170
+ if (cachedReactions.length && !sameReactions(message.reactions, cached.reactions)) {
31043
31171
  patch.reactions = cachedReactions;
31044
31172
  changed = true;
31045
31173
  }
@@ -31065,19 +31193,6 @@ function patchMessageFromCachedMessage(message, cached) {
31065
31193
  }
31066
31194
  return changed ? { ...message, ...patch } : message;
31067
31195
  }
31068
- function sameReactionProjection(a, b) {
31069
- const left = getMsgReactions(a);
31070
- const right = getMsgReactions(b);
31071
- if (left.length !== right.length) {
31072
- return false;
31073
- }
31074
- for (let index = 0;index < left.length; index += 1) {
31075
- if (left[index].user !== right[index]?.user || left[index].emoji !== right[index]?.emoji) {
31076
- return false;
31077
- }
31078
- }
31079
- return true;
31080
- }
31081
31196
  function patchMessagesFromCachedWindow(messages, cachedMessages) {
31082
31197
  if (!messages?.length || !cachedMessages?.length) {
31083
31198
  return messages || [];
@@ -31734,6 +31849,7 @@ async function createGroupChat(cloud, identityValue, memberValues, settings = {}
31734
31849
  if (!identity.chatSigningSecret)
31735
31850
  throw new Error("unlocked chat signing identity required");
31736
31851
  const mls = await lifecycle.mls?.open?.();
31852
+ lifecycle.assertCurrent?.();
31737
31853
  if (!mls)
31738
31854
  throw new Error("mls engine required");
31739
31855
  const selfLeafId = lifecycle.selfLeafId || randomBytes3(32);
@@ -31754,7 +31870,7 @@ async function createGroupChat(cloud, identityValue, memberValues, settings = {}
31754
31870
  if (additions.length)
31755
31871
  mlsGroup = await mls.stageChange(mlsGroup.snapshot, { adds: additions });
31756
31872
  const material = deriveChatMlsEpochMaterial(mlsGroup.epochKey, { chatId, epochVersion: 1 });
31757
- const lineage = members.length === 1 ? CHAT_LINEAGES.SELF : members.length === 2 ? CHAT_LINEAGES.DIRECT : CHAT_LINEAGES.GROUP;
31873
+ const lineage = lifecycle.lineage || (members.length === 1 ? CHAT_LINEAGES.SELF : members.length === 2 ? CHAT_LINEAGES.DIRECT : CHAT_LINEAGES.GROUP);
31758
31874
  const manifest = makeInitialEpochManifest({
31759
31875
  chatId,
31760
31876
  epochId: material.epochId,
@@ -31817,6 +31933,7 @@ async function createGroupChat(cloud, identityValue, memberValues, settings = {}
31817
31933
  });
31818
31934
  initial.backend.membershipOutbox = queuedWelcomes.map(({ outboxId: outboxId2, sealed }) => ({ outboxId: outboxId2, sealed }));
31819
31935
  stage = "owner";
31936
+ lifecycle.assertCurrent?.();
31820
31937
  const registration = registerInitialDelivery(cloud, identity, initialState);
31821
31938
  const stateId = chatMlsStateId(identity.chatPrivateKey, chatId, initial.transitionCommitment);
31822
31939
  const ownerMlsState = await sealChatMlsState(identity.chatPrivateKey, stateId, { v: 1, chatId, transitionCommitment: initial.transitionCommitment }, mlsGroup.snapshot);
@@ -31828,6 +31945,8 @@ async function createGroupChat(cloud, identityValue, memberValues, settings = {}
31828
31945
  let stateError = null;
31829
31946
  try {
31830
31947
  stage = "state";
31948
+ lifecycle.assertCurrent?.();
31949
+ lifecycle.onCreateDispatched?.(initial.manifest.chatId);
31831
31950
  await cloud.chat.state.create({
31832
31951
  ...initial.backend,
31833
31952
  ownerEntry: owner.write,
@@ -32182,21 +32301,23 @@ function createChatSessionLaunches({
32182
32301
  if (!opened?.chatId || !opened?.entry)
32183
32302
  return "";
32184
32303
  const chat = projectOwnChatEntry(opened.entry, opened.entryId || "", operation.identity.chatPK, opened.tsMs || Date.now());
32185
- operation.listOwner.commitOwner(chat);
32304
+ if (!operation.listOwner.commitOwner(chat))
32305
+ throw makeChatUnavailableError();
32186
32306
  return opened.chatId;
32187
32307
  };
32188
32308
  const presentOpenedChat = (opened, operation) => {
32189
32309
  assertCurrent(operation);
32190
32310
  if (!opened?.chatId || !opened?.entry)
32191
32311
  return "";
32192
- operation.pendingOwner.adoptOpenedChatId(opened.chatId);
32193
32312
  const chatId = commitOpenedChat(opened, operation);
32313
+ operation.pendingOwner.adoptOpenedChatId(chatId);
32194
32314
  pendingPeerSelect = null;
32195
32315
  setSelectedChat(chatId);
32196
32316
  return chatId;
32197
32317
  };
32198
32318
  const createChat = async (profiles, settings, lifecycle, adoptOpenedChat, operation, { profilesCurrent = false } = {}) => {
32199
32319
  assertCurrent(operation);
32320
+ lifecycle.assertCurrent?.();
32200
32321
  const { blocked, chatPK, chatPrivateKey } = operation.identity;
32201
32322
  let profileList = Array.isArray(profiles) ? profiles : [profiles];
32202
32323
  if (!profilesCurrent && profileList.length) {
@@ -32204,9 +32325,10 @@ function createChatSessionLaunches({
32204
32325
  }
32205
32326
  const blockedSet = new Set(blocked);
32206
32327
  const requestedMembers = profileList.map(profileMember);
32207
- if (profileList.length === 1) {
32328
+ const expectedLineage = lifecycle.lineage || (requestedMembers.length === 0 ? "self" : requestedMembers.length === 1 ? "direct" : "group");
32329
+ if (expectedLineage === "direct") {
32208
32330
  assertDirectAdmission(profileList[0], operation);
32209
- } else if (profileList.length > 1) {
32331
+ } else if (expectedLineage === "group") {
32210
32332
  if (normalizeChatAdmission(operation.identity.chatAdmission).groups === CHAT_GROUP_ADMISSION_MODES.CLOSED) {
32211
32333
  throw new Error("group chats are closed");
32212
32334
  }
@@ -32220,8 +32342,7 @@ function createChatSessionLaunches({
32220
32342
  if (blockedSet.has(member.uid))
32221
32343
  throw new Error("blocked peer cannot be added");
32222
32344
  }
32223
- const canonicalChatId = lifecycle.chatId || (requestedMembers.length === 0 ? deriveSelfChatId(chatPrivateKey, chatPK) : "");
32224
- const expectedLineage = requestedMembers.length === 0 ? "self" : requestedMembers.length === 1 ? "direct" : "group";
32345
+ const canonicalChatId = lifecycle.chatId || (expectedLineage === "self" ? deriveSelfChatId(chatPrivateKey, chatPK) : "");
32225
32346
  const expectedMemberKeys = [chatPK, ...requestedMembers.map((member) => member.chatPK)].sort();
32226
32347
  const matchingExistingChat = () => {
32227
32348
  assertCurrent(operation);
@@ -32260,6 +32381,7 @@ function createChatSessionLaunches({
32260
32381
  markDiag(diag, "chat.group.create.prekeys.start", { members: requestedMembers.length });
32261
32382
  const additions = await prepareMlsAdditions(profileList);
32262
32383
  assertCurrent(operation);
32384
+ lifecycle.assertCurrent?.();
32263
32385
  markDiag(diag, "chat.group.create.prekeys.done", { members: requestedMembers.length, elapsedMs: Date.now() - prekeyStartedAt });
32264
32386
  if (!additions.length && requestedMembers.length)
32265
32387
  throw new Error("peer mls key package required");
@@ -32269,15 +32391,23 @@ function createChatSessionLaunches({
32269
32391
  const created = await createGroupChat(cloud, operation.transitionIdentity, [...byKey.values()], settings, {
32270
32392
  mls,
32271
32393
  chatId: canonicalChatId || undefined,
32394
+ lineage: expectedLineage,
32272
32395
  wakeMessageId: lifecycle.wakeMessageId,
32396
+ assertCurrent: () => {
32397
+ assertCurrent(operation);
32398
+ lifecycle.assertCurrent?.();
32399
+ },
32400
+ onCreateDispatched: lifecycle.onCreateDispatched,
32273
32401
  onStateReady: (opened) => {
32274
32402
  assertCurrent(operation);
32403
+ lifecycle.assertCurrent?.();
32275
32404
  stateReady = true;
32276
32405
  const chatId = adoptOpenedChat(opened);
32277
32406
  lifecycle.onStateReady?.(chatId, opened, { created: true });
32278
32407
  },
32279
32408
  onOwnerReady: async (opened) => {
32280
32409
  assertCurrent(operation);
32410
+ lifecycle.assertCurrent?.();
32281
32411
  const chatId = adoptOpenedChat(opened);
32282
32412
  const result = await lifecycle.onOwnerReady?.(chatId, opened, { created: true });
32283
32413
  assertCurrent(operation);
@@ -32288,7 +32418,7 @@ function createChatSessionLaunches({
32288
32418
  return adoptOpenedChat(created);
32289
32419
  } catch (error) {
32290
32420
  assertCurrent(operation);
32291
- if (stateReady || !canonicalChatId || requestedMembers.length > 0)
32421
+ if (stateReady || !canonicalChatId)
32292
32422
  throw error;
32293
32423
  await ensureChat(canonicalChatId);
32294
32424
  assertCurrent(operation);
@@ -32302,6 +32432,10 @@ function createChatSessionLaunches({
32302
32432
  const operation = operationContext();
32303
32433
  return createChat(profiles, settings, lifecycle, (opened) => presentOpenedChat(opened, operation), operation);
32304
32434
  };
32435
+ const materializeGroupChat = (profiles, settings = {}, lifecycle = {}) => {
32436
+ const operation = operationContext();
32437
+ return createChat(profiles, settings, lifecycle, (opened) => commitOpenedChat(opened, operation), operation);
32438
+ };
32305
32439
  const runExistingDirectLifecycle = async (chatId, lifecycle, operation) => {
32306
32440
  assertCurrent(operation);
32307
32441
  const existing = getOwnerChat(chatId);
@@ -32373,6 +32507,7 @@ function createChatSessionLaunches({
32373
32507
  getOpenOperation: (chatId) => directOpens.get(chatId)?.promise || null,
32374
32508
  getPeerChatId,
32375
32509
  materializeDirectChat,
32510
+ materializeGroupChat,
32376
32511
  openDirectChat,
32377
32512
  openNotesChat,
32378
32513
  releaseOpenOperation: (chatId) => {
@@ -32500,16 +32635,21 @@ function createChatSessionPending({
32500
32635
  getPeerProfile,
32501
32636
  resolvePeerProfile,
32502
32637
  setLocalByChat,
32503
- getLocalByChat = () => new Map
32638
+ getLocalByChat = () => new Map,
32639
+ retireMaterializedChat = () => {}
32504
32640
  }) {
32641
+ const admission = createChatAdmission({ getIdentity });
32505
32642
  let pendingLaunch = null;
32506
32643
  const pendingOperations = new Map;
32507
32644
  let generation = 0;
32645
+ const operationForChat = (chatId) => pendingOperations.get(chatId) || [...pendingOperations.values()].find((state) => state.canonicalChatId === chatId) || null;
32646
+ const launchForChat = (chatId) => pendingLaunch && [pendingLaunch.id, pendingLaunch.canonicalChatId].includes(chatId) ? pendingLaunch : operationForChat(chatId)?.launch || storedLaunch(chatId);
32508
32647
  const storedLaunch = (chatId) => getLocalByChat().get(chatId)?.find((message) => message.chatDraft)?.chatDraft || null;
32509
32648
  const getPendingLaunch = () => {
32510
32649
  if (!pendingLaunch)
32511
32650
  return null;
32512
- const messages = getLocalByChat().get(pendingLaunch.id) || [];
32651
+ const localByChat = getLocalByChat();
32652
+ const messages = localByChat.get(pendingLaunch.id) || localByChat.get(pendingLaunch.canonicalChatId) || [];
32513
32653
  const message = messages[messages.length - 1] || null;
32514
32654
  return { ...pendingLaunch, messages, message, chat: {
32515
32655
  ...pendingLaunch.chat,
@@ -32517,23 +32657,33 @@ function createChatSessionPending({
32517
32657
  ts: message ? timestampMs(message.ts, null) : null
32518
32658
  } };
32519
32659
  };
32660
+ const resolvePendingChatId = (chatId) => {
32661
+ const canonicalId = operationForChat(chatId)?.canonicalChatId || launchForChat(chatId)?.canonicalChatId;
32662
+ return canonicalId && getOwnerChat(canonicalId) ? canonicalId : chatId;
32663
+ };
32520
32664
  const finish = (launchId) => {
32521
32665
  if (pendingLaunch?.id !== launchId)
32522
32666
  return;
32667
+ if (pendingLaunch.canonicalChatId !== launchId) {
32668
+ publish();
32669
+ return;
32670
+ }
32523
32671
  pendingLaunch = null;
32524
32672
  pendingOperations.delete(launchId);
32525
32673
  publish();
32526
32674
  };
32527
32675
  const track = (launchId, operation) => {
32528
32676
  const tracked = Promise.resolve(operation);
32529
- pendingOperations.set(launchId, tracked);
32677
+ const state = pendingOperations.get(launchId) || { active: true, promise: null };
32678
+ state.promise = tracked;
32679
+ pendingOperations.set(launchId, state);
32530
32680
  tracked.then(() => {
32531
- if (pendingOperations.get(launchId) !== tracked)
32681
+ if (pendingOperations.get(launchId) !== state || state.promise !== tracked)
32532
32682
  return;
32533
32683
  pendingOperations.delete(launchId);
32534
32684
  finish(launchId);
32535
32685
  }, () => {
32536
- if (pendingOperations.get(launchId) !== tracked)
32686
+ if (pendingOperations.get(launchId) !== state || state.promise !== tracked)
32537
32687
  return;
32538
32688
  pendingOperations.delete(launchId);
32539
32689
  if (pendingLaunch?.id !== launchId)
@@ -32545,6 +32695,12 @@ function createChatSessionPending({
32545
32695
  });
32546
32696
  return tracked;
32547
32697
  };
32698
+ const cancelOperation = (launchId) => {
32699
+ const operation = pendingOperations.get(launchId);
32700
+ if (operation)
32701
+ operation.active = false;
32702
+ pendingOperations.delete(launchId);
32703
+ };
32548
32704
  const show = (launch) => {
32549
32705
  if (pendingLaunch?.id && pendingLaunch.id !== launch.id && !pendingOperations.has(pendingLaunch.id)) {
32550
32706
  getRouteOwner().releaseOpenOperation?.(pendingLaunch.id);
@@ -32640,7 +32796,7 @@ function createChatSessionPending({
32640
32796
  return cached;
32641
32797
  return (fallbackProfiles || []).find((profile) => profileMatchesPeer(profile, peer)) || null;
32642
32798
  };
32643
- const profileAcceptsDirect = (identity, profile) => !!identity?.chatPK && !!identity?.chatPrivateKey && directAdmissionForPeer(identity.chatPrivateKey, identity.chatPK, profile, identity.chatAdmission) !== CHAT_ADMISSION_DECISIONS.REJECT;
32799
+ const profileAcceptsDirect = (identity, profile) => !!identity?.chatPK && !!identity?.chatPrivateKey && admission.directAdmissionForPeer(profile) !== CHAT_ADMISSION_DECISIONS.REJECT;
32644
32800
  const isChatAdmitted = (chat, fallbackProfiles = []) => {
32645
32801
  const identity = getIdentity();
32646
32802
  if (!chat)
@@ -32773,18 +32929,29 @@ function createChatSessionPending({
32773
32929
  throw error;
32774
32930
  }
32775
32931
  };
32776
- const sendDurableAttachment = async (chatId, attachment, options = {}) => {
32777
- const target = await admittedSendTarget(chatId, options);
32778
- return getSendActions().sendAttachment(target.routeAnchor, attachment, target.options);
32779
- };
32780
- const sendDurableSharedAttachment = async (chatId, attachment, options = {}) => {
32932
+ const withReadyAttachmentTarget = async (chatId, options, send) => {
32933
+ const attemptGeneration = generation;
32934
+ await waitForSend(chatId, { expiresAt: options.expiresAt, signal: options.signal });
32935
+ if (attemptGeneration !== generation)
32936
+ throw makeChatUnavailableError();
32781
32937
  const target = await admittedSendTarget(chatId, options);
32782
- return getSendActions().sendSharedAttachment(target.routeAnchor, attachment, target.options);
32938
+ if (attemptGeneration !== generation)
32939
+ throw makeChatUnavailableError();
32940
+ return send(target);
32783
32941
  };
32942
+ const sendDurableAttachment = (chatId, attachment, options = {}) => withReadyAttachmentTarget(chatId, options, (target) => getSendActions().sendAttachment(target.routeAnchor, attachment, target.options));
32943
+ const sendDurableSharedAttachment = (chatId, attachment, options = {}) => withReadyAttachmentTarget(chatId, options, (target) => getSendActions().sendSharedAttachment(target.routeAnchor, attachment, target.options));
32784
32944
  const stagePendingMessage = (launchId, local) => {
32785
- if (!pendingLaunch || pendingLaunch.id !== launchId)
32945
+ const launch = launchForChat(launchId);
32946
+ if (!launch || launch.id !== launchId)
32786
32947
  throw makeChatUnavailableError();
32787
- const { id, profiles: profiles2, settings, chat } = pendingLaunch;
32948
+ const { id, profiles: profiles2, settings, chat } = launch;
32949
+ const canonicalId = resolvePendingChatId(launchId);
32950
+ if (getOwnerChat(canonicalId)) {
32951
+ const { chatDraft: _draft, ...content } = local;
32952
+ setLocalByChat((current) => addLocalMessage(current, canonicalId, content));
32953
+ return;
32954
+ }
32788
32955
  const chatDraft = { id, canonicalChatId: id, profiles: profiles2, settings, chat };
32789
32956
  setLocalByChat((current) => addLocalMessage(current, launchId, { ...local, chatDraft }));
32790
32957
  };
@@ -32796,6 +32963,8 @@ function createChatSessionPending({
32796
32963
  const canonical = targetId !== launchId ? current.get(targetId) || [] : [];
32797
32964
  const writerIds = new Set(canonical.map((message) => message.cid));
32798
32965
  const staged = (current.get(launchId) || []).filter((message) => !writerIds.has(message.cid));
32966
+ if (!canonical.length && !staged.length)
32967
+ return current;
32799
32968
  next.set(targetId, [...canonical, ...staged].map((message) => {
32800
32969
  const { chatDraft, ...content } = message;
32801
32970
  return {
@@ -32811,14 +32980,22 @@ function createChatSessionPending({
32811
32980
  if (getOwnerChat(canonicalId) && pendingLaunch?.id === launchId)
32812
32981
  setSelectedChat(canonicalId);
32813
32982
  };
32814
- const retirePeer = (peerChatPK) => {
32815
- const launch = pendingLaunch;
32816
- if (!launch?.chat?.members?.some((member) => member?.chatPK === peerChatPK))
32983
+ const dropChat = (chatId) => {
32984
+ const launch = launchForChat(chatId);
32985
+ if (!launch || launch.id !== chatId && launch.canonicalChatId !== chatId)
32817
32986
  return false;
32818
32987
  beginSelection();
32819
32988
  getRouteOwner().releaseOpenOperation?.(launch.id);
32820
- pendingLaunch = null;
32821
- pendingOperations.delete(launch.id);
32989
+ if (pendingLaunch?.id === launch.id)
32990
+ pendingLaunch = null;
32991
+ cancelOperation(launch.id);
32992
+ setLocalByChat((current) => {
32993
+ if (!current.has(launch.id))
32994
+ return current;
32995
+ const next = new Map(current);
32996
+ next.delete(launch.id);
32997
+ return next;
32998
+ });
32822
32999
  if (getSelectedChatId() === launch.id || getSelectedChatId() === launch.canonicalChatId) {
32823
33000
  setSelectedChat(null);
32824
33001
  } else {
@@ -32826,25 +33003,74 @@ function createChatSessionPending({
32826
33003
  }
32827
33004
  return true;
32828
33005
  };
33006
+ const retirePeer = (peerChatPK) => pendingLaunch?.chat?.members?.some((member) => member?.chatPK === peerChatPK) ? dropChat(pendingLaunch.id) : false;
33007
+ const discardChat = async (chatId) => {
33008
+ const state = operationForChat(chatId);
33009
+ if (!state?.dispatched)
33010
+ return { discarded: dropChat(chatId), chat: null };
33011
+ state.discarding = true;
33012
+ if (pendingLaunch?.id === state.launch.id)
33013
+ pendingLaunch = null;
33014
+ if ([state.launch.id, state.canonicalChatId].includes(getSelectedChatId()))
33015
+ setSelectedChat(null);
33016
+ else
33017
+ publish();
33018
+ state.retirementTarget ||= getOwnerChat(state.canonicalChatId) || null;
33019
+ if (state.retirementTarget)
33020
+ retireMaterializedChat(state.canonicalChatId);
33021
+ const attemptGeneration = generation;
33022
+ await state.promise.catch(() => null);
33023
+ if (attemptGeneration !== generation)
33024
+ throw makeChatUnavailableError();
33025
+ if (!state.retirementTarget) {
33026
+ if (!getOwnerChat(state.canonicalChatId))
33027
+ await ensureChat(state.canonicalChatId);
33028
+ state.retirementTarget = getOwnerChat(state.canonicalChatId) || null;
33029
+ }
33030
+ if (attemptGeneration !== generation || !state.retirementTarget)
33031
+ throw makeChatUnavailableError();
33032
+ retireMaterializedChat(state.canonicalChatId);
33033
+ return { discarded: true, chat: state.retirementTarget };
33034
+ };
32829
33035
  const materialize = (launchId, wakeMessageId, sendInitial) => {
32830
33036
  const launch = pendingLaunch;
32831
33037
  if (!launch || launch.id !== launchId || pendingOperations.has(launchId))
32832
33038
  throw makeChatUnavailableError();
32833
33039
  const attemptGeneration = generation;
33040
+ const state = { active: true, promise: null, launch, canonicalChatId: launchId };
33041
+ pendingOperations.set(launchId, state);
33042
+ const assertCurrent = () => {
33043
+ if (attemptGeneration !== generation || !state.active)
33044
+ throw makeChatUnavailableError();
33045
+ };
32834
33046
  const expiresAt = Date.now() + CHAT_SEND_CONNECTION_WAIT_MS;
32835
33047
  let canonicalId = launchId;
32836
33048
  const operation = (async () => {
32837
33049
  await waitForSend(launchId, { expiresAt });
32838
- if (attemptGeneration !== generation)
32839
- throw makeChatUnavailableError();
33050
+ assertCurrent();
32840
33051
  let initialResult = null;
32841
33052
  const lifecycle = {
32842
33053
  wakeMessageId,
33054
+ assertCurrent,
33055
+ onCreateDispatched: (chatId) => {
33056
+ assertCurrent();
33057
+ state.dispatched = true;
33058
+ state.canonicalChatId = chatId;
33059
+ state.launch = { ...state.launch, canonicalChatId: chatId };
33060
+ },
32843
33061
  onStateReady: (chatId) => {
33062
+ assertCurrent();
32844
33063
  canonicalId = chatId;
33064
+ state.canonicalChatId = chatId;
33065
+ state.launch = { ...state.launch, canonicalChatId: chatId };
32845
33066
  adoptOpenedChatId(chatId, launchId);
33067
+ if (state.discarding) {
33068
+ state.retirementTarget = getOwnerChat(chatId);
33069
+ retireMaterializedChat(chatId);
33070
+ }
32846
33071
  },
32847
33072
  onOwnerReady: async (chatId, _opened, context) => {
33073
+ assertCurrent();
32848
33074
  canonicalId = chatId;
32849
33075
  initialResult = await sendInitial(chatId, { ...context, expiresAt });
32850
33076
  return initialResult;
@@ -32859,18 +33085,20 @@ function createChatSessionPending({
32859
33085
  canonicalId = resolvedChatId;
32860
33086
  initialResult = await sendInitial(resolvedChatId, { created: false, expiresAt });
32861
33087
  } else {
32862
- canonicalId = await getRouteOwner().createGroupChat(launch.profiles, launch.settings, {
33088
+ canonicalId = await getRouteOwner().materializeGroupChat(launch.profiles, launch.settings, {
32863
33089
  ...lifecycle,
32864
- chatId: launchId
33090
+ chatId: launch.canonicalChatId,
33091
+ lineage: launch.chat.lineage
32865
33092
  });
32866
33093
  }
32867
33094
  }
32868
33095
  if (attemptGeneration !== generation)
32869
33096
  throw makeChatUnavailableError();
32870
- settleMaterialization(launchId, canonicalId);
33097
+ if (state.active)
33098
+ settleMaterialization(launchId, canonicalId);
32871
33099
  return { ...initialResult || {}, chatId: canonicalId };
32872
33100
  })().catch((error) => {
32873
- if (attemptGeneration === generation)
33101
+ if (attemptGeneration === generation && state.active)
32874
33102
  settleMaterialization(launchId, canonicalId, true);
32875
33103
  throw error;
32876
33104
  });
@@ -32881,10 +33109,74 @@ function createChatSessionPending({
32881
33109
  ...context?.expiresAt ? { expiresAt: context.expiresAt } : {},
32882
33110
  ...context?.created === true ? { deliverRecipients: false, ownerImmediate: true } : {}
32883
33111
  });
33112
+ const runChatMutation = (chatId, mutate, { initialProfiles } = {}) => {
33113
+ const launch = launchForChat(chatId);
33114
+ const canonicalId = resolvePendingChatId(chatId);
33115
+ const active = launch && pendingOperations.get(launch.id);
33116
+ if (active?.discarding)
33117
+ throw makeChatUnavailableError();
33118
+ if (!active && getOwnerChat(canonicalId))
33119
+ return mutate(canonicalId, { created: false });
33120
+ if (!launch || getIdentity().chatBanned)
33121
+ throw makeChatUnavailableError();
33122
+ const attemptGeneration = generation;
33123
+ const perform = async (id, context) => {
33124
+ if (attemptGeneration !== generation)
33125
+ throw makeChatUnavailableError();
33126
+ const value = await mutate(id, context);
33127
+ if (attemptGeneration !== generation)
33128
+ throw makeChatUnavailableError();
33129
+ const ownerEntry = getOwnerChat(id)?.ownEntry;
33130
+ if (!ownerEntry)
33131
+ throw makeChatUnavailableError();
33132
+ return { value, ownerEntry };
33133
+ };
33134
+ if (active) {
33135
+ return track(launch.id, active.promise.then(async (result) => {
33136
+ if (!active.active)
33137
+ throw makeChatUnavailableError();
33138
+ const id = result?.chatId || launch.canonicalChatId;
33139
+ return { ...await perform(id, { created: false }), chatId: id };
33140
+ })).then((result) => result.value);
33141
+ }
33142
+ pendingLaunch = launch;
33143
+ if (initialProfiles) {
33144
+ if (launch.chat.lineage === "self")
33145
+ throw new Error("self chat membership is fixed");
33146
+ const profiles2 = initialProfiles(launch.profiles);
33147
+ if (profiles2 === launch.profiles)
33148
+ return Promise.resolve({ unchanged: true });
33149
+ const members = profiles2.map(profileMember);
33150
+ chatCapabilities(members.length + 1);
33151
+ const self2 = memberFromChatIdentity(transitionIdentity());
33152
+ if (members.some((member) => member.chatPK === self2.chatPK))
33153
+ throw new Error("current user is already a chat member");
33154
+ const lineage = launch.chat.lineage === "group" || members.length !== 1 ? "group" : launch.chat.lineage;
33155
+ pendingLaunch = {
33156
+ ...launch,
33157
+ canonicalChatId: lineage === "group" && launch.chat.lineage !== "group" ? randomChatId() : launch.canonicalChatId,
33158
+ profiles: profiles2.map((profile, index) => pendingProfile(profile, members[index])),
33159
+ chat: {
33160
+ ...launch.chat,
33161
+ lineage,
33162
+ peerChatPK: lineage === "direct" ? members[0]?.chatPK || null : null,
33163
+ members: [self2, ...members],
33164
+ memberCount: members.length + 1,
33165
+ memberChatPKs: [self2.chatPK, ...members.map((member) => member.chatPK)],
33166
+ memberUids: [self2.uid, ...members.map((member) => member.uid)]
33167
+ }
33168
+ };
33169
+ publish();
33170
+ }
33171
+ return materialize(launch.id, "", (id, context) => perform(id, {
33172
+ ...context,
33173
+ ...initialSendOptions({}, context)
33174
+ })).then((result) => result.value);
33175
+ };
32884
33176
  const sendPendingMessage = (launchId, message, options = {}) => {
32885
33177
  const { chatPK } = getIdentity();
32886
- const launch = pendingLaunch;
32887
- const previousOperation = pendingOperations.get(launchId);
33178
+ const launch = launchForChat(launchId);
33179
+ const previousOperation = pendingOperations.get(launchId)?.promise;
32888
33180
  if (!launch || launch.id !== launchId)
32889
33181
  throw makeChatUnavailableError();
32890
33182
  const routeAnchor = launch.chat?.members?.find((member) => member.chatPK !== chatPK)?.chatPK || chatPK;
@@ -32903,8 +33195,8 @@ function createChatSessionPending({
32903
33195
  };
32904
33196
  const sendPendingAttachment = (launchId, attachment, options = {}) => {
32905
33197
  const { chatPK } = getIdentity();
32906
- const launch = pendingLaunch;
32907
- const previousOperation = pendingOperations.get(launchId);
33198
+ const launch = launchForChat(launchId);
33199
+ const previousOperation = pendingOperations.get(launchId)?.promise;
32908
33200
  if (!launch || launch.id !== launchId)
32909
33201
  throw makeChatUnavailableError();
32910
33202
  const routeAnchor = launch.chat?.members?.find((member) => member.chatPK !== chatPK)?.chatPK || chatPK;
@@ -32919,8 +33211,8 @@ function createChatSessionPending({
32919
33211
  };
32920
33212
  const sendPendingSharedAttachment = (launchId, attachment, options = {}) => {
32921
33213
  const { chatPK } = getIdentity();
32922
- const launch = pendingLaunch;
32923
- const previousOperation = pendingOperations.get(launchId);
33214
+ const launch = launchForChat(launchId);
33215
+ const previousOperation = pendingOperations.get(launchId)?.promise;
32924
33216
  if (!launch || launch.id !== launchId)
32925
33217
  throw makeChatUnavailableError();
32926
33218
  const routeAnchor = launch.chat?.members?.find((member) => member.chatPK !== chatPK)?.chatPK || chatPK;
@@ -32933,6 +33225,12 @@ function createChatSessionPending({
32933
33225
  return track(launchId, previousOperation.then((result) => sendDurableSharedAttachment(result?.chatId || launchId, queuedAttachment, options)));
32934
33226
  };
32935
33227
  const sendChatMessage = (chatId, message, options = {}) => {
33228
+ const operation = operationForChat(chatId);
33229
+ if (operation?.discarding)
33230
+ throw makeChatUnavailableError();
33231
+ if (operation)
33232
+ return sendPendingMessage(operation.launch.id, message, options);
33233
+ chatId = resolvePendingChatId(chatId);
32936
33234
  if (!getOwnerChat(chatId)) {
32937
33235
  if (!pendingLaunch)
32938
33236
  pendingLaunch = storedLaunch(chatId);
@@ -32943,9 +33241,10 @@ function createChatSessionPending({
32943
33241
  return sendDurableMessage(chatId, message, options);
32944
33242
  };
32945
33243
  const retryMessage = (chatId, cid) => {
33244
+ chatId = resolvePendingChatId(chatId);
32946
33245
  const attemptGeneration = generation;
32947
33246
  const launch = pendingLaunch && (pendingLaunch.id === chatId || pendingLaunch.canonicalChatId === chatId) ? pendingLaunch : storedLaunch(chatId);
32948
- const failed = getLocalByChat().get(launch?.id || chatId)?.find((message) => message.cid === cid && message.failed);
33247
+ const failed = getLocalByChat().get(chatId)?.find((message) => message.cid === cid && message.failed);
32949
33248
  if (!failed)
32950
33249
  return false;
32951
33250
  if (launch && !getOwnerChat(chatId)) {
@@ -32982,8 +33281,24 @@ function createChatSessionPending({
32982
33281
  getSendActions().discardLocalMessage(chatId, cid);
32983
33282
  return true;
32984
33283
  };
32985
- const sendChatAttachment = (chatId, attachment, options = {}) => pendingLaunch?.id === chatId && !getOwnerChat(chatId) ? sendPendingAttachment(chatId, attachment, options) : sendDurableAttachment(chatId, attachment, options);
32986
- const sendChatSharedAttachment = (chatId, attachment, options = {}) => pendingLaunch?.id === chatId && !getOwnerChat(chatId) ? sendPendingSharedAttachment(chatId, attachment, options) : sendDurableSharedAttachment(chatId, attachment, options);
33284
+ const sendChatAttachment = (chatId, attachment, options = {}) => {
33285
+ const operation = operationForChat(chatId);
33286
+ if (operation?.discarding)
33287
+ throw makeChatUnavailableError();
33288
+ if (operation)
33289
+ return sendPendingAttachment(operation.launch.id, attachment, options);
33290
+ chatId = resolvePendingChatId(chatId);
33291
+ return pendingLaunch?.id === chatId && !getOwnerChat(chatId) ? sendPendingAttachment(chatId, attachment, options) : sendDurableAttachment(chatId, attachment, options);
33292
+ };
33293
+ const sendChatSharedAttachment = (chatId, attachment, options = {}) => {
33294
+ const operation = operationForChat(chatId);
33295
+ if (operation?.discarding)
33296
+ throw makeChatUnavailableError();
33297
+ if (operation)
33298
+ return sendPendingSharedAttachment(operation.launch.id, attachment, options);
33299
+ chatId = resolvePendingChatId(chatId);
33300
+ return pendingLaunch?.id === chatId && !getOwnerChat(chatId) ? sendPendingSharedAttachment(chatId, attachment, options) : sendDurableSharedAttachment(chatId, attachment, options);
33301
+ };
32987
33302
  const sendDirectAttachment = async (profile, attachment, options = {}) => sendChatAttachment(await getRouteOwner().openDirectChat(profile), attachment, options);
32988
33303
  const sendMaterializedDirect = async (profile, payload, options, send) => {
32989
33304
  const queuedPayload = { ...payload, cid: makeSendCid(payload) };
@@ -33030,6 +33345,10 @@ function createChatSessionPending({
33030
33345
  return settleNormalizedTargets(normalizedTargets, (target) => sendPickerTarget(target, (chatId) => sendChatSharedAttachment(chatId, attachment), (profile) => sendPickerDirectSharedAttachment(profile, attachment)));
33031
33346
  };
33032
33347
  return {
33348
+ directAdmissionForPeer: admission.directAdmissionForPeer,
33349
+ dropChat,
33350
+ discardChat,
33351
+ canStartDirectChat: admission.canStartDirectChat,
33033
33352
  adoptOpenedChatId,
33034
33353
  adoptResolved,
33035
33354
  beginPendingChat,
@@ -33037,8 +33356,11 @@ function createChatSessionPending({
33037
33356
  canSendToChat,
33038
33357
  isChatAdmitted,
33039
33358
  isPendingLaunchAdmitted,
33040
- isPendingChat: (chatId) => pendingLaunch?.id === chatId || !!storedLaunch(chatId),
33359
+ isPendingChat: (chatId) => !!launchForChat(chatId),
33041
33360
  getPendingLaunch,
33361
+ getActionChat: (chatId) => getOwnerChat(resolvePendingChatId(chatId)) || launchForChat(chatId)?.chat || null,
33362
+ hasPendingChatOperation: (chatId) => !!operationForChat(chatId),
33363
+ runChatMutation,
33042
33364
  openNewChat,
33043
33365
  retirePeer,
33044
33366
  reconcileAdmission: () => {
@@ -33048,7 +33370,7 @@ function createChatSessionPending({
33048
33370
  beginSelection();
33049
33371
  getRouteOwner().clearOpenOperations?.();
33050
33372
  pendingLaunch = null;
33051
- pendingOperations.delete(launch.id);
33373
+ cancelOperation(launch.id);
33052
33374
  if (getSelectedChatId() === launch.id || getSelectedChatId() === launch.canonicalChatId) {
33053
33375
  setSelectedChat(null);
33054
33376
  } else {
@@ -33058,10 +33380,13 @@ function createChatSessionPending({
33058
33380
  },
33059
33381
  reset: () => {
33060
33382
  generation += 1;
33383
+ admission.reset();
33061
33384
  beginSelection();
33062
33385
  if (pendingLaunch?.id)
33063
33386
  getRouteOwner().releaseOpenOperation?.(pendingLaunch.id);
33064
33387
  pendingLaunch = null;
33388
+ for (const operation of pendingOperations.values())
33389
+ operation.active = false;
33065
33390
  pendingOperations.clear();
33066
33391
  },
33067
33392
  selectChat: (chatId, { flushPrevious }) => {
@@ -33074,9 +33399,14 @@ function createChatSessionPending({
33074
33399
  const selectResolved = () => {
33075
33400
  if (!isSelectionCurrent(selection) || getIdentity().chatBanned)
33076
33401
  return false;
33077
- if (pendingLaunch && chatId !== pendingLaunch.id && !pendingOperations.has(pendingLaunch.id)) {
33402
+ if (pendingLaunch && chatId !== pendingLaunch.id) {
33403
+ if (chatId === pendingLaunch.canonicalChatId) {
33404
+ settleMaterialization(pendingLaunch.id, chatId);
33405
+ }
33078
33406
  getRouteOwner().releaseOpenOperation?.(pendingLaunch.id);
33079
33407
  pendingLaunch = null;
33408
+ if (getSelectedChatId() === chatId)
33409
+ publish();
33080
33410
  }
33081
33411
  const previousChatId = getSelectedChatId();
33082
33412
  if (previousChatId && previousChatId !== chatId)
@@ -33131,6 +33461,7 @@ var init_sessionpending = __esm(() => {
33131
33461
  init_messages();
33132
33462
  init_state();
33133
33463
  init_targets();
33464
+ init_capabilities();
33134
33465
  });
33135
33466
 
33136
33467
  // ../../core/chat/sessionmembership.js
@@ -33625,7 +33956,6 @@ function createChatDelete({
33625
33956
  for (const chatId of chatIds || []) {
33626
33957
  if (!chatId)
33627
33958
  continue;
33628
- dropCachedChat(localCache, chatId);
33629
33959
  closeMessageBatchRef.current?.(chatId);
33630
33960
  if (localForRender.has(chatId)) {
33631
33961
  if (!localChanged)
@@ -33657,6 +33987,7 @@ function createChatDelete({
33657
33987
  keepSelectedDeletedChatIdsRef.current.delete(chatId);
33658
33988
  }
33659
33989
  }
33990
+ dropCachedChats(localCache, ids);
33660
33991
  clearDeletedChatState(ids);
33661
33992
  renderVisibleChats();
33662
33993
  setSelectedChat((current) => current && ids.includes(current) && !options?.keepSelected ? null : current);
@@ -33714,9 +34045,7 @@ function createChatDelete({
33714
34045
  const dropChat = (chatId) => {
33715
34046
  if (!chatId)
33716
34047
  return;
33717
- clearDeletedChatState([chatId]);
33718
34048
  listActionsRef.current?.removeOwner?.(chatId, { warm: false });
33719
- setSelectedChat((current) => current === chatId ? null : current);
33720
34049
  };
33721
34050
  const dropLeftChat = (chatId) => {
33722
34051
  if (!chatId)
@@ -35137,8 +35466,6 @@ async function setOwnChatNotificationMode(cloud, identity, entryId, entry, value
35137
35466
  if (!current?.current || current.retirement)
35138
35467
  throw makeChatUnavailableError();
35139
35468
  const manifest = current.current.manifest;
35140
- if (manifest.lineage !== "group")
35141
- throw new Error("group chat required");
35142
35469
  const requestedMode = cleanText(value);
35143
35470
  const notificationMode = normalizeChatNotificationMode(requestedMode, manifest);
35144
35471
  if (notificationMode !== requestedMode) {
@@ -35198,20 +35525,20 @@ function createChatSessionNotifications({
35198
35525
  throw makeChatUnavailableError();
35199
35526
  }
35200
35527
  };
35201
- const requireOwnerChat = (chatId, operation, { group = false } = {}) => {
35528
+ const requireOwnerChat = (chatId, operation) => {
35202
35529
  assertCurrent(operation);
35203
35530
  const chat = getOwnerChat(chatId);
35204
- if (operation.identity.chatBanned || canWriteChat(chatId) !== true || group && chat?.lineage !== "group" || !chat.ownEntry || chat.ownEntry.retirement || !chat.entryId) {
35531
+ if (operation.identity.chatBanned || canWriteChat(chatId) !== true || !chat?.ownEntry || chat.ownEntry.retirement || !chat.entryId) {
35205
35532
  throw makeChatUnavailableError();
35206
35533
  }
35207
35534
  return chat;
35208
35535
  };
35209
35536
  const setChatNotificationMode = async (chatId, mode) => {
35210
35537
  const operation = operationContext();
35211
- requireOwnerChat(chatId, operation, { group: true });
35538
+ requireOwnerChat(chatId, operation);
35212
35539
  const releaseMutation = await operation.mutationGate.acquireExclusive(chatId);
35213
35540
  try {
35214
- const chat = requireOwnerChat(chatId, operation, { group: true });
35541
+ const chat = requireOwnerChat(chatId, operation);
35215
35542
  const committed = await writeMode(cloud, operation.identity, chat.entryId, chat.ownEntry, mode);
35216
35543
  assertCurrent(operation);
35217
35544
  const projected = projectOwnChatEntry(committed, chat.entryId, operation.identity.chatPK, chat.ts);
@@ -35604,7 +35931,7 @@ function createChatSession({
35604
35931
  const owner = getOwnerChat(chatId);
35605
35932
  if (owner)
35606
35933
  return !isChatMembershipRemoved(owner);
35607
- return pendingOwner?.getPendingLaunch()?.id === chatId;
35934
+ return pendingOwner?.isPendingChat(chatId) === true;
35608
35935
  };
35609
35936
  const requireOnline = () => {
35610
35937
  if (!online)
@@ -35619,10 +35946,6 @@ function createChatSession({
35619
35946
  if (chatId && !isMutableChat(chatId))
35620
35947
  throw makeChatUnavailableError();
35621
35948
  };
35622
- const gateChatMutation = (fn) => (chatId, ...rest) => {
35623
- requireMutableChat(chatId);
35624
- return fn(chatId, ...rest);
35625
- };
35626
35949
  const gateAdmittedChatMutation = (fn) => (chatId, ...rest) => {
35627
35950
  requireMutableChat(chatId);
35628
35951
  return Promise.resolve(pendingOwner.assertChatAdmission(chatId)).then(() => fn(chatId, ...rest));
@@ -35824,7 +36147,8 @@ function createChatSession({
35824
36147
  getPeerProfile,
35825
36148
  resolvePeerProfile,
35826
36149
  setLocalByChat,
35827
- getLocalByChat: () => localByChatRef.current
36150
+ getLocalByChat: () => localByChatRef.current,
36151
+ retireMaterializedChat: (chatId) => actionOwner.deleteActions.beginRetirement(chatId)
35828
36152
  });
35829
36153
  launchOwner = createChatSessionLaunches({
35830
36154
  cloud,
@@ -35864,29 +36188,79 @@ function createChatSession({
35864
36188
  const openDirectChat = (...args) => launchOwner.openDirectChat(...args);
35865
36189
  const openNotesChat = (...args) => launchOwner.openNotesChat(...args);
35866
36190
  const openNewChat = (...args) => pendingOwner.openNewChat(...args);
35867
- const directAdmissionForPeer2 = (profile) => directAdmissionForPeer(chatPrivateKey, chatPK, profile, chatAdmission);
35868
- const canStartDirectChat2 = (profile) => canStartDirectChat(chatPrivateKey, chatPK, profile, chatAdmission);
36191
+ const { directAdmissionForPeer: directAdmissionForPeer2, canStartDirectChat } = pendingOwner;
35869
36192
  const canInviteToGroup2 = (profile) => canInviteToGroup(profile, chatAdmission);
35870
36193
  const createGroupChat2 = (...args) => launchOwner.createGroupChat(...args);
35871
36194
  const selectLocalChat = (...args) => launchOwner.selectLocalChat(...args);
35872
- const addChatMembers = async (chatId, profiles2) => {
36195
+ const mutateMaterializedChat = (chatId, mutate, options) => {
35873
36196
  requireMutableChat(chatId);
35874
- await pendingOwner.assertChatAdmission(chatId);
35875
- return membershipOwner.addChatMembers(chatId, profiles2);
36197
+ return pendingOwner.runChatMutation(chatId, async (id, context) => {
36198
+ await pendingOwner.assertChatAdmission(id);
36199
+ return mutate(id, context);
36200
+ }, options);
35876
36201
  };
35877
- const kickChatMember = async (chatId, memberChatPK) => {
35878
- requireMutableChat(chatId);
35879
- await pendingOwner.assertChatAdmission(chatId);
35880
- return membershipOwner.kickChatMember(chatId, memberChatPK);
36202
+ const addChatMembers = (chatId, profiles2) => {
36203
+ const additions = Array.isArray(profiles2) ? profiles2 : [profiles2];
36204
+ additions.forEach(membershipOwner.profileMember);
36205
+ return mutateMaterializedChat(chatId, (id, context) => context.created ? { entry: getOwnerChat(id).ownEntry } : membershipOwner.addChatMembers(id, additions), {
36206
+ initialProfiles: (current) => {
36207
+ const next = new Map(current.map((profile) => [profile.chatPK, profile]));
36208
+ for (const profile of additions)
36209
+ next.set(profile.chatPK, profile);
36210
+ return next.size === current.length ? current : [...next.values()];
36211
+ }
36212
+ });
36213
+ };
36214
+ const kickChatMember = (chatId, memberChatPK) => {
36215
+ const target = cleanText(memberChatPK);
36216
+ if (!target)
36217
+ throw new Error("chat member required");
36218
+ if (target === chatPK && pendingOwner.isPendingChat(chatId)) {
36219
+ return deleteChat(chatId);
36220
+ }
36221
+ return mutateMaterializedChat(chatId, (id, context) => context.created ? { entry: getOwnerChat(id).ownEntry } : membershipOwner.kickChatMember(id, target), {
36222
+ initialProfiles: (current) => current.some((profile) => profile.chatPK === target) ? current.filter((profile) => profile.chatPK !== target) : current
36223
+ });
35881
36224
  };
35882
36225
  const leaveChat = (chatId) => {
36226
+ if (pendingOwner.isPendingChat(chatId)) {
36227
+ return deleteChat(chatId);
36228
+ }
35883
36229
  requireMutableChat(chatId);
35884
36230
  return membershipOwner.leaveChat(chatId);
35885
36231
  };
35886
36232
  const leaveOwnedChat = (...args) => membershipOwner.leaveOwnedChat(...args);
35887
- const updateChatSettings2 = gateAdmittedChatMutation((...args) => settingsOwner.updateChatSettings(...args));
35888
- const updateChatAvatar = gateAdmittedChatMutation((...args) => settingsOwner.updateChatAvatar(...args));
35889
- const setChatNotificationMode = gateAdmittedChatMutation((...args) => notificationsOwner.setChatNotificationMode(...args));
36233
+ const sharedSettingsChat = (chatId) => {
36234
+ const chat = pendingOwner.getActionChat(chatId);
36235
+ if (chat?.lineage === "self")
36236
+ throw new Error("self chat identity follows your profile");
36237
+ return chat;
36238
+ };
36239
+ const updateChatSettings2 = async (chatId, settings) => {
36240
+ const chat = sharedSettingsChat(chatId);
36241
+ const requested = normalizeChatSettingsInput({ ...chat?.settings, ...settings });
36242
+ const changes = ["title", "avatarRef", "retention"].filter((key) => Object.prototype.hasOwnProperty.call(settings, key));
36243
+ const patch = Object.fromEntries(changes.map((key) => [key, requested[key]]));
36244
+ if (chat && !pendingOwner.hasPendingChatOperation(chatId) && changes.every((key) => chat.settings?.[key] === requested[key])) {
36245
+ return Promise.resolve({ unchanged: true });
36246
+ }
36247
+ return mutateMaterializedChat(chatId, (id) => settingsOwner.updateChatSettings(id, patch));
36248
+ };
36249
+ const updateChatAvatar = async (chatId, data) => {
36250
+ assertChatAvatarBytes(data);
36251
+ const chat = sharedSettingsChat(chatId);
36252
+ if (chat?.lineage !== "group")
36253
+ throw new Error("group chat required");
36254
+ return mutateMaterializedChat(chatId, (id) => settingsOwner.updateChatAvatar(id, data));
36255
+ };
36256
+ const setChatNotificationMode = async (chatId, mode) => {
36257
+ const chat = pendingOwner.getActionChat(chatId);
36258
+ if (chat && normalizeChatNotificationMode(mode, chat) !== mode)
36259
+ throw new Error("all notifications are unavailable in large groups");
36260
+ if (chat && !pendingOwner.hasPendingChatOperation(chatId) && (chat.notificationMode || defaultChatNotificationMode(chat)) === mode)
36261
+ return mode;
36262
+ return mutateMaterializedChat(chatId, (id) => notificationsOwner.setChatNotificationMode(id, mode));
36263
+ };
35890
36264
  const updateMessage = async (chatId, original, changes) => {
35891
36265
  if (chatBanned)
35892
36266
  throw makeChatUnavailableError();
@@ -36040,7 +36414,27 @@ function createChatSession({
36040
36414
  };
36041
36415
  const dropChat = (...args) => actionOwner.deleteActions.dropChat(...args);
36042
36416
  const dropUnavailableChat = (...args) => actionOwner.deleteActions.dropUnavailableChat(...args);
36043
- const deleteChat = gateOnlineMutation((...args) => actionOwner.deleteActions.deleteChat(...args));
36417
+ const deleteChat = async (value, options) => {
36418
+ const inputs = Array.isArray(value) ? value : [value];
36419
+ const durable = [];
36420
+ let discarded = 0;
36421
+ for (const input of inputs) {
36422
+ const id = cleanText(typeof input === "string" ? input : input?.chatId || input?.id);
36423
+ if (pendingOwner.hasPendingChatOperation(id) || !getOwnerChat(id) && pendingOwner.isPendingChat(id)) {
36424
+ const result = await pendingOwner.discardChat(id);
36425
+ if (result.chat)
36426
+ durable.push(result.chat);
36427
+ else if (result.discarded)
36428
+ discarded += 1;
36429
+ } else
36430
+ durable.push(input);
36431
+ }
36432
+ if (!durable.length)
36433
+ return Array.isArray(value) ? discarded : discarded > 0;
36434
+ requireOnline();
36435
+ const deleted = await actionOwner.deleteActions.deleteChat(Array.isArray(value) ? durable : durable[0], options);
36436
+ return Array.isArray(value) ? discarded + Number(deleted) : deleted;
36437
+ };
36044
36438
  const purgePeerChats = async (affected, operation) => {
36045
36439
  const identity = operation.identity;
36046
36440
  for (const chat of affected) {
@@ -36145,14 +36539,14 @@ function createChatSession({
36145
36539
  const sendPendingChatMessage = (...args) => pendingOwner.sendPendingChatMessage(...args);
36146
36540
  const sendChatMessage = sendMessage;
36147
36541
  const sendDirectAttachment = gateOnlineMutation((...args) => pendingOwner.sendDirectAttachment(...args));
36148
- const sendChatAttachment = gateChatMutation((...args) => pendingOwner.sendChatAttachment(...args));
36542
+ const sendChatAttachment = gateOnlineMutation((...args) => pendingOwner.sendChatAttachment(...args));
36149
36543
  const retryMessage = async (...args) => {
36150
36544
  const result = await pendingOwner.retryMessage(...args);
36151
36545
  if (online)
36152
36546
  drainMembershipOutbox();
36153
36547
  return result;
36154
36548
  };
36155
- const sendAttachment = gateChatMutation((...args) => pendingOwner.sendChatAttachment(...args));
36549
+ const sendAttachment = gateOnlineMutation((...args) => pendingOwner.sendChatAttachment(...args));
36156
36550
  const sendChatImage = (chatId, image, options = {}) => sendChatAttachment(chatId, { ...image, type: image?.type || "img" }, options);
36157
36551
  const sendAttachmentMany = gateOnlineMutation((...args) => pendingOwner.sendAttachmentMany(...args));
36158
36552
  const sendImageMany = (targets, image, options = {}) => sendAttachmentMany(targets, { ...image, type: image?.type || "img" }, options);
@@ -36210,7 +36604,11 @@ function createChatSession({
36210
36604
  });
36211
36605
  return state;
36212
36606
  };
36213
- const setChatTtl = gateAdmittedChatMutation((...args) => settingsOwner.setChatTtl(...args));
36607
+ const setChatTtl = async (chatId, retention) => {
36608
+ const next = cleanChatRetention(retention);
36609
+ await updateChatSettings2(chatId, { retention: next });
36610
+ return next;
36611
+ };
36214
36612
  const makeMessagePermanent = gateAdmittedChatMutation((...args) => actionOwner.saveActions.makeMessagePermanent(...args));
36215
36613
  const makeMessageTemporary = gateAdmittedChatMutation((...args) => actionOwner.saveActions.makeMessageTemporary(...args));
36216
36614
  const readMessageFile = (...args) => actionOwner.saveActions.readMessageFile(...args);
@@ -36313,6 +36711,17 @@ function createChatSession({
36313
36711
  chatsRef,
36314
36712
  reconcileMessageBatches: (...args) => messageBatchOwner.reconcileChats(...args),
36315
36713
  warmChats: (...args) => messageBatchOwner.warm(...args),
36714
+ onOwnerRemoved: (chatIds) => {
36715
+ for (const chatId of chatIds)
36716
+ pendingOwner.dropChat(chatId);
36717
+ actionOwner.deleteActions.clearDeletedChatState(chatIds);
36718
+ for (const chatId of chatIds) {
36719
+ actionOwner.liveActions.closeChatActivity(chatId);
36720
+ historyOwner.clear(chatId);
36721
+ messageBatchOwner.routeMemory.dropChat(chatId);
36722
+ routeMemoryScopes.delete(chatId);
36723
+ }
36724
+ },
36316
36725
  onTransition: stageCommittedMembershipMessage,
36317
36726
  onRemoved: (removedChatId, _epochVersion, retiredEntry) => {
36318
36727
  const applied = chatListOwner.applyMembershipRemoval(removedChatId, retiredEntry);
@@ -36369,7 +36778,7 @@ function createChatSession({
36369
36778
  openNotesChat,
36370
36779
  openNewChat,
36371
36780
  directAdmissionForPeer: directAdmissionForPeer2,
36372
- canStartDirectChat: canStartDirectChat2,
36781
+ canStartDirectChat,
36373
36782
  canInviteToGroup: canInviteToGroup2,
36374
36783
  createGroupChat: createGroupChat2,
36375
36784
  addChatMembers,
@@ -36663,6 +37072,10 @@ var init_session3 = __esm(() => {
36663
37072
  init_sessionsources();
36664
37073
  init_entry();
36665
37074
  init_admission();
37075
+ init_avatar2();
37076
+ init_settings2();
37077
+ init_ttl();
37078
+ init_notifications();
36666
37079
  init_write();
36667
37080
  DEFAULT_READ_WRITE_INTERVAL_MS = CHAT_READ_WRITE_INTERVAL_MS;
36668
37081
  EMPTY_LOCAL_MESSAGES = Object.freeze([]);
@@ -36845,7 +37258,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
36845
37258
  let expiryTimer = null;
36846
37259
  let command = null;
36847
37260
  let authenticating = null;
36848
- let snapshot = Object.freeze({ visibility: null, ready: false, pending: false, error: null, ...UNKNOWN_PRESENCE });
37261
+ let snapshot = Object.freeze({ visibility: null, ready: false, pending: false, pendingVisibility: null, error: null, ...UNKNOWN_PRESENCE });
36849
37262
  function topic() {
36850
37263
  return sources.uid ? derivePresenceTopic(realm2, sources.uid) : null;
36851
37264
  }
@@ -36853,7 +37266,13 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
36853
37266
  return Boolean(channel && challenge && policy && sources.enabled && !closed);
36854
37267
  }
36855
37268
  function publish(patch = {}) {
36856
- const next = { ...snapshot, ...patch, ready: isReady() };
37269
+ const next = {
37270
+ ...snapshot,
37271
+ ...patch,
37272
+ ready: isReady(),
37273
+ pending: Boolean(command),
37274
+ pendingVisibility: command?.policy.visibility ?? null
37275
+ };
36857
37276
  if (Object.keys(next).every((key) => Object.is(next[key], snapshot[key])))
36858
37277
  return;
36859
37278
  snapshot = Object.freeze(next);
@@ -36909,7 +37328,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
36909
37328
  const pending = command;
36910
37329
  command = null;
36911
37330
  clearTimeout(pending.timer);
36912
- publish({ pending: false, error });
37331
+ publish({ error });
36913
37332
  if (error)
36914
37333
  pending.reject(error);
36915
37334
  else
@@ -37149,12 +37568,14 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
37149
37568
  listener();
37150
37569
  }
37151
37570
  function update(next) {
37152
- const identityChanged = sources.uid !== next.uid || sources.identity?.publicKey !== next.identity?.publicKey;
37571
+ const accountChanged = sources.uid !== next.uid;
37572
+ const identityChanged = accountChanged || sources.identity?.publicKey !== next.identity?.publicKey;
37153
37573
  if (identityChanged) {
37154
37574
  stop();
37155
37575
  policy = null;
37156
37576
  records.clear();
37157
- peers.clear();
37577
+ if (accountChanged)
37578
+ peers.clear();
37158
37579
  publish({ visibility: null, error: null, ...UNKNOWN_PRESENCE });
37159
37580
  }
37160
37581
  sources = next;
@@ -37184,7 +37605,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
37184
37605
  reject,
37185
37606
  timer: setTimeout(() => finishCommand(new Error("could not confirm activity setting; reconnect to check")), COMMAND_TIMEOUT_MS)
37186
37607
  };
37187
- publish({ pending: true, error: null });
37608
+ publish({ error: null });
37188
37609
  if (!channel.send(presenceShard(topic()), { type: "policy", expectedRevision: policy.revision, policy: nextPolicy })) {
37189
37610
  finishCommand(new Error("could not update activity while offline"));
37190
37611
  }
@@ -40970,6 +41391,7 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
40970
41391
  let profilesLoading = false;
40971
41392
  let hydratedCacheKey = "";
40972
41393
  let avatarHydrateKey = "";
41394
+ let avatarPublishSession = null;
40973
41395
  let profilesReady = false;
40974
41396
  let blockedPeersReady = false;
40975
41397
  let discoveredPeerUids = new Set;
@@ -41380,12 +41802,21 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
41380
41802
  const session = lifecycle;
41381
41803
  const startedAt = Date.now();
41382
41804
  const requested = walletPKs.length + chatPeerPKs.length;
41383
- hydrateCachedAvatars(walletPKs, chatPeerPKs).then((changed) => {
41805
+ hydrateCachedAvatars(walletPKs, chatPeerPKs, () => {
41806
+ if (!isCurrent(session) || avatarPublishSession === session)
41807
+ return;
41808
+ avatarPublishSession = session;
41809
+ queueMicrotask(() => {
41810
+ if (avatarPublishSession !== session)
41811
+ return;
41812
+ avatarPublishSession = null;
41813
+ if (isCurrent(session))
41814
+ rebuild({ base: true, persist: false });
41815
+ });
41816
+ }).then((changed) => {
41384
41817
  if (!isCurrent(session))
41385
41818
  return;
41386
41819
  markDiag(diag, "peers.avatars.hydrate", { requested, changed, elapsedMs: Date.now() - startedAt });
41387
- if (changed)
41388
- rebuild({ base: true });
41389
41820
  }).catch((error) => {
41390
41821
  if (!isCurrent(session))
41391
41822
  return;
@@ -41989,7 +42420,7 @@ function createPeersApi({ cloud, network, avatarCache = null }) {
41989
42420
  function uniqueLookupKeys(keys) {
41990
42421
  return uniqueValues(keys);
41991
42422
  }
41992
- async function hydrateCachedAvatars(walletPKs, chatPKs) {
42423
+ async function hydrateCachedAvatars(walletPKs, chatPKs, onHydrated) {
41993
42424
  if (typeof avatarCache?.read !== "function")
41994
42425
  return false;
41995
42426
  const profiles2 = new Map;
@@ -42008,21 +42439,18 @@ function createPeersApi({ cloud, network, avatarCache = null }) {
42008
42439
  queue(key, "chat");
42009
42440
  if (!profiles2.size)
42010
42441
  return false;
42011
- const cached = await Promise.all(Array.from(profiles2.values()).map(async ({ uid, version }) => ({
42012
- avatar: await readCachedAvatar(uid, version),
42013
- uid,
42014
- version
42015
- })));
42016
42442
  let changed = false;
42017
- for (const entry of cached) {
42018
- if (!entry.avatar)
42019
- continue;
42020
- const current = profileCache.get(entry.uid);
42021
- if (!current || current.avatarVersion !== entry.version || current.avatar === entry.avatar)
42022
- continue;
42023
- storeProfile({ ...current, avatar: entry.avatar });
42443
+ await Promise.all(Array.from(profiles2.values()).map(async ({ uid, version }) => {
42444
+ const avatar = await readCachedAvatar(uid, version);
42445
+ if (!avatar)
42446
+ return;
42447
+ const current = profileCache.get(uid);
42448
+ if (!current || current.avatarVersion !== version || current.avatar === avatar)
42449
+ return;
42450
+ storeProfile({ ...current, avatar });
42024
42451
  changed = true;
42025
- }
42452
+ onHydrated?.();
42453
+ }));
42026
42454
  return changed;
42027
42455
  }
42028
42456
  function needsAvatarResolve(profile) {
@@ -44692,6 +45120,7 @@ function createPayment({
44692
45120
  throw new Error("bitcoin payment must be reviewed before sending");
44693
45121
  }
44694
45122
  const payment = paymentInput?.state === PAYMENT_STATE_PREPARED ? normalizePreparedPayment(paymentInput, wallet, network) : await preparePayment(paymentInput);
45123
+ assertReady();
44695
45124
  const startedAt = Date.now();
44696
45125
  const diagKind = payment.kind;
44697
45126
  markDiag(diag, "wallet.payment.start", { kind: diagKind });
@@ -53576,20 +54005,7 @@ function sameMessageList(a, b) {
53576
54005
  for (let i = 0;i < a.length; i += 1) {
53577
54006
  const left = a[i];
53578
54007
  const right = b[i];
53579
- if (getMessageKey(left) !== getMessageKey(right) || getMessageOrderMs(left) !== getMessageOrderMs(right) || hasMessageTtl(left) !== hasMessageTtl(right) || timestampKey(left?.ttl ?? null) !== timestampKey(right?.ttl ?? null) || !sameMessageReactions(left, right)) {
53580
- return false;
53581
- }
53582
- }
53583
- return true;
53584
- }
53585
- function sameMessageReactions(a, b) {
53586
- const left = getMsgReactions(a);
53587
- const right = getMsgReactions(b);
53588
- if (left.length !== right.length) {
53589
- return false;
53590
- }
53591
- for (let index2 = 0;index2 < left.length; index2 += 1) {
53592
- if (left[index2].user !== right[index2]?.user || left[index2].emoji !== right[index2]?.emoji) {
54008
+ if (getMessageKey(left) !== getMessageKey(right) || getMessageOrderMs(left) !== getMessageOrderMs(right) || hasMessageTtl(left) !== hasMessageTtl(right) || timestampKey(left?.ttl ?? null) !== timestampKey(right?.ttl ?? null) || !sameReactions(left?.reactions, right?.reactions)) {
53593
54009
  return false;
53594
54010
  }
53595
54011
  }
@@ -54008,7 +54424,7 @@ function cacheMessageSignature(message) {
54008
54424
  if (!message)
54009
54425
  return "";
54010
54426
  const hasTtl = hasMessageTtl(message);
54011
- const reactions = getMsgReactions(message).map((reaction) => `${reaction.user}:${reaction.emoji}`).join(",");
54427
+ const reactions = getMsgReactions(message).map((reaction) => `${reaction.user}:${reaction.emoji}:${reaction.ts ?? ""}`).join(",");
54012
54428
  return [
54013
54429
  cacheKeySignature(messageKeys(message)),
54014
54430
  getMessageOrderMs(message) || 0,
@@ -60624,7 +61040,7 @@ var package_default;
60624
61040
  var init_package2 = __esm(() => {
60625
61041
  package_default = {
60626
61042
  name: "veyl",
60627
- version: "0.70.0",
61043
+ version: "0.71.0",
60628
61044
  packageManager: "bun@1.4.0",
60629
61045
  private: true,
60630
61046
  license: "UNLICENSED",
@@ -79009,7 +79425,7 @@ function ensureBytes(title, hex3, expectedLength) {
79009
79425
  throw new Error(title + " of length " + expectedLength + " expected, got " + len);
79010
79426
  return res;
79011
79427
  }
79012
- function equalBytes2(a, b) {
79428
+ function equalBytes3(a, b) {
79013
79429
  if (a.length !== b.length)
79014
79430
  return false;
79015
79431
  let diff = 0;
@@ -109820,7 +110236,7 @@ var init_legacy = __esm(() => {
109820
110236
  });
109821
110237
 
109822
110238
  // ../../node_modules/.bun/micro-packed@0.7.3/node_modules/micro-packed/lib/esm/index.js
109823
- function equalBytes3(a, b) {
110239
+ function equalBytes4(a, b) {
109824
110240
  if (a.length !== b.length)
109825
110241
  return false;
109826
110242
  for (let i = 0;i < a.length; i++)
@@ -109995,7 +110411,7 @@ class _Reader {
109995
110411
  const leftBytes = this.data.length - idx;
109996
110412
  if (leftBytes < needle.length)
109997
110413
  return;
109998
- if (equalBytes3(needle, this.data.subarray(idx, idx + needle.length)))
110414
+ if (equalBytes4(needle, this.data.subarray(idx, idx + needle.length)))
109999
110415
  return idx;
110000
110416
  }
110001
110417
  return;
@@ -110347,7 +110763,7 @@ function magic(inner, constant, check = true) {
110347
110763
  encodeStream: (w, _value) => inner.encodeStream(w, constant),
110348
110764
  decodeStream: (r) => {
110349
110765
  const value = inner.decodeStream(r);
110350
- if (check && typeof value !== "object" && value !== constant || isBytes6(constant) && !equalBytes3(constant, value)) {
110766
+ if (check && typeof value !== "object" && value !== constant || isBytes6(constant) && !equalBytes4(constant, value)) {
110351
110767
  throw r.err(`magic: invalid value: ${value} !== ${constant}`);
110352
110768
  }
110353
110769
  return;
@@ -110454,7 +110870,7 @@ function array(len, inner) {
110454
110870
  if (len.length > _w.pos - startPos)
110455
110871
  return;
110456
110872
  const data = _w.finish(false).subarray(startPos, _w.pos);
110457
- if (equalBytes3(data.subarray(0, len.length), len))
110873
+ if (equalBytes4(data.subarray(0, len.length), len))
110458
110874
  throw _w.err(`array: inner element encoding same as separator. elm=${elm} data=${data}`);
110459
110875
  }
110460
110876
  });
@@ -110474,7 +110890,7 @@ function array(len, inner) {
110474
110890
  }
110475
110891
  } else if (isBytes6(len)) {
110476
110892
  for (let i = 0;; i++) {
110477
- if (equalBytes3(r.bytes(len.length, true), len)) {
110893
+ if (equalBytes4(r.bytes(len.length, true), len)) {
110478
110894
  r.bytes(len.length);
110479
110895
  break;
110480
110896
  }
@@ -110687,7 +111103,7 @@ var EMPTY2, NULL, createView4 = (arr) => new DataView(arr.buffer, arr.byteOffset
110687
111103
  decodeStream: (r) => {
110688
111104
  let hasFlag = r.leftBytes >= flagValue.length;
110689
111105
  if (hasFlag) {
110690
- hasFlag = equalBytes3(r.bytes(flagValue.length, true), flagValue);
111106
+ hasFlag = equalBytes4(r.bytes(flagValue.length, true), flagValue);
110691
111107
  if (hasFlag)
110692
111108
  r.bytes(flagValue.length);
110693
111109
  }
@@ -110705,7 +111121,7 @@ var init_esm2 = __esm(() => {
110705
111121
  EMPTY2 = /* @__PURE__ */ new Uint8Array;
110706
111122
  NULL = /* @__PURE__ */ new Uint8Array([0]);
110707
111123
  utils2 = {
110708
- equalBytes: equalBytes3,
111124
+ equalBytes: equalBytes4,
110709
111125
  isBytes: isBytes6,
110710
111126
  isCoder,
110711
111127
  checkBounds,
@@ -110947,7 +111363,7 @@ function compareBytes(a, b) {
110947
111363
  return Math.sign(a[i] - b[i]);
110948
111364
  return Math.sign(a.length - b.length);
110949
111365
  }
110950
- var Point, CURVE_ORDER, isBytes7, concatBytes7, equalBytes4, hash160 = (msg) => ripemd160(sha2562(msg)), sha256x2 = (...msgs) => sha2562(sha2562(concatBytes7(...msgs))), randomPrivateKeyBytes, pubSchnorr, pubECDSA, hasLowR = (sig) => sig.r < CURVE_ORDER / 2n, signSchnorr, tagSchnorr, PubT, TAPROOT_UNSPENDABLE_KEY, NETWORK, TEST_NETWORK;
111366
+ var Point, CURVE_ORDER, isBytes7, concatBytes7, equalBytes5, hash160 = (msg) => ripemd160(sha2562(msg)), sha256x2 = (...msgs) => sha2562(sha2562(concatBytes7(...msgs))), randomPrivateKeyBytes, pubSchnorr, pubECDSA, hasLowR = (sig) => sig.r < CURVE_ORDER / 2n, signSchnorr, tagSchnorr, PubT, TAPROOT_UNSPENDABLE_KEY, NETWORK, TEST_NETWORK;
110951
111367
  var init_utils6 = __esm(() => {
110952
111368
  init_secp256k1();
110953
111369
  init_legacy();
@@ -110957,7 +111373,7 @@ var init_utils6 = __esm(() => {
110957
111373
  CURVE_ORDER = secp256k1.CURVE.n;
110958
111374
  isBytes7 = utils2.isBytes;
110959
111375
  concatBytes7 = utils2.concatBytes;
110960
- equalBytes4 = utils2.equalBytes;
111376
+ equalBytes5 = utils2.equalBytes;
110961
111377
  randomPrivateKeyBytes = schnorr.utils.randomPrivateKey;
110962
111378
  pubSchnorr = schnorr.getPublicKey;
110963
111379
  pubECDSA = secp256k1.getPublicKey;
@@ -111476,7 +111892,7 @@ function mergeKeyMap(psbtEnum, val, cur, allowedFields, allowUnknown) {
111476
111892
  } else if (typeof res[k] === "string") {
111477
111893
  res[k] = vC.decode(hex3.decode(res[k]));
111478
111894
  } else if (cannotChange && k in val && cur && cur[k] !== undefined) {
111479
- if (!equalBytes4(vC.encode(val[k]), vC.encode(cur[k])))
111895
+ if (!equalBytes5(vC.encode(val[k]), vC.encode(cur[k])))
111480
111896
  throw new Error(`Cannot change signed field=${k}`);
111481
111897
  }
111482
111898
  }
@@ -111675,7 +112091,7 @@ function isValidPubkey(pub, type) {
111675
112091
  }
111676
112092
  }
111677
112093
  function checkWSH(s, witnessScript) {
111678
- if (!equalBytes4(s.hash, sha2562(witnessScript)))
112094
+ if (!equalBytes5(s.hash, sha2562(witnessScript)))
111679
112095
  throw new Error("checkScript: wsh wrong witnessScript hash");
111680
112096
  const w = OutScript.decode(witnessScript);
111681
112097
  if (w.type === "tr" || w.type === "tr_ns" || w.type === "tr_ms")
@@ -111689,7 +112105,7 @@ function checkScript(script, redeemScript, witnessScript) {
111689
112105
  if (s.type === "tr_ns" || s.type === "tr_ms" || s.type === "ms" || s.type == "pk")
111690
112106
  throw new Error(`checkScript: non-wrapped ${s.type}`);
111691
112107
  if (s.type === "sh" && redeemScript) {
111692
- if (!equalBytes4(s.hash, hash160(redeemScript)))
112108
+ if (!equalBytes5(s.hash, hash160(redeemScript)))
111693
112109
  throw new Error("checkScript: sh wrong redeemScript hash");
111694
112110
  const r = OutScript.decode(redeemScript);
111695
112111
  if (r.type === "tr" || r.type === "tr_ns" || r.type === "tr_ms")
@@ -111726,9 +112142,9 @@ function checkTaprootScript(script, internalPubKey, allowUnknownOutputs = false,
111726
112142
  const outms = out;
111727
112143
  if (!allowUnknownOutputs && outms.pubkeys) {
111728
112144
  for (const p of outms.pubkeys) {
111729
- if (equalBytes4(p, TAPROOT_UNSPENDABLE_KEY))
112145
+ if (equalBytes5(p, TAPROOT_UNSPENDABLE_KEY))
111730
112146
  throw new Error("Unspendable taproot key in leaf script");
111731
- if (equalBytes4(p, internalPubKey)) {
112147
+ if (equalBytes5(p, internalPubKey)) {
111732
112148
  throw new Error("Using P2TR with leaf script with same key as internal key is not supported");
111733
112149
  }
111734
112150
  }
@@ -111779,7 +112195,7 @@ function taprootHashTree(tree, internalPubKey, allowUnknownOutputs = false, cust
111779
112195
  tree = tree[0];
111780
112196
  if (!Array.isArray(tree)) {
111781
112197
  const { leafVersion: version, script: leafScript } = tree;
111782
- if (tree.tapLeafScript || tree.tapMerkleRoot && !equalBytes4(tree.tapMerkleRoot, EMPTY2))
112198
+ if (tree.tapLeafScript || tree.tapMerkleRoot && !equalBytes5(tree.tapMerkleRoot, EMPTY2))
111783
112199
  throw new Error("P2TR: tapRoot leafScript cannot have tree");
111784
112200
  const script = typeof leafScript === "string" ? hex3.decode(leafScript) : leafScript;
111785
112201
  if (!isBytes7(script))
@@ -112158,7 +112574,7 @@ function cloneDeep(obj) {
112158
112574
  throw new Error(`cloneDeep: unknown type=${obj} (${typeof obj})`);
112159
112575
  }
112160
112576
  function getTaprootKeys(privKey, pubKey, internalKey, merkleRoot = EMPTY2) {
112161
- if (equalBytes4(internalKey, pubKey)) {
112577
+ if (equalBytes5(internalKey, pubKey)) {
112162
112578
  privKey = taprootTweakPrivKey(privKey, merkleRoot);
112163
112579
  pubKey = pubSchnorr(privKey);
112164
112580
  }
@@ -112254,7 +112670,7 @@ function validateInput(i) {
112254
112670
  if (i.index > last)
112255
112671
  throw new Error(`validateInput: index(${i.index}) not in nonWitnessUtxo`);
112256
112672
  const prevOut = i.nonWitnessUtxo.outputs[i.index];
112257
- if (i.witnessUtxo && (!equalBytes4(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount))
112673
+ if (i.witnessUtxo && (!equalBytes5(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount))
112258
112674
  throw new Error("validateInput: witnessUtxo different from nonWitnessUtxo");
112259
112675
  if (i.txid) {
112260
112676
  const outputs = i.nonWitnessUtxo.outputs;
@@ -112810,7 +113226,7 @@ class Transaction {
112810
113226
  let s = privateKey;
112811
113227
  for (const i of path)
112812
113228
  s = s.deriveChild(i);
112813
- if (!equalBytes4(s.publicKey, pubKey))
113229
+ if (!equalBytes5(s.publicKey, pubKey))
112814
113230
  throw new Error("bip32Derivation: wrong pubKey");
112815
113231
  if (!s.privateKey)
112816
113232
  throw new Error("bip32Derivation: no privateKey");
@@ -112847,7 +113263,7 @@ class Transaction {
112847
113263
  if (input.tapInternalKey) {
112848
113264
  const { pubKey, privKey } = getTaprootKeys(privateKey, schnorrPub, input.tapInternalKey, merkleRoot);
112849
113265
  const [taprootPubKey, _] = taprootTweakPubkey(input.tapInternalKey, merkleRoot);
112850
- if (equalBytes4(taprootPubKey, pubKey)) {
113266
+ if (equalBytes5(taprootPubKey, pubKey)) {
112851
113267
  const hash = this.preimageWitnessV1(idx, prevOutScript, sighash, amount);
112852
113268
  const sig = concatBytes7(signSchnorr(hash, privKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : EMPTY2);
112853
113269
  this.updateInput(idx, { tapKeySig: sig }, true);
@@ -112861,7 +113277,7 @@ class Transaction {
112861
113277
  const scriptDecoded = Script.decode(script);
112862
113278
  const ver = _script[_script.length - 1];
112863
113279
  const hash = tapLeafHash(script, ver);
112864
- const pos = scriptDecoded.findIndex((i) => isBytes7(i) && equalBytes4(i, schnorrPub));
113280
+ const pos = scriptDecoded.findIndex((i) => isBytes7(i) && equalBytes5(i, schnorrPub));
112865
113281
  if (pos === -1)
112866
113282
  continue;
112867
113283
  const msg = this.preimageWitnessV1(idx, prevOutScript, sighash, amount, undefined, script, ver);
@@ -112878,7 +113294,7 @@ class Transaction {
112878
113294
  let hasPubkey = false;
112879
113295
  const pubKeyHash = hash160(pubKey);
112880
113296
  for (const i of Script.decode(inputType.lastScript)) {
112881
- if (isBytes7(i) && (equalBytes4(i, pubKey) || equalBytes4(i, pubKeyHash)))
113297
+ if (isBytes7(i) && (equalBytes5(i, pubKey) || equalBytes5(i, pubKeyHash)))
112882
113298
  hasPubkey = true;
112883
113299
  }
112884
113300
  if (!hasPubkey)
@@ -112928,14 +113344,14 @@ class Transaction {
112928
113344
  const ver = _script[_script.length - 1];
112929
113345
  const outScript = OutScript.decode(script);
112930
113346
  const hash = tapLeafHash(script, ver);
112931
- const scriptSig = input.tapScriptSig.filter((i) => equalBytes4(i[0].leafHash, hash));
113347
+ const scriptSig = input.tapScriptSig.filter((i) => equalBytes5(i[0].leafHash, hash));
112932
113348
  let signatures = [];
112933
113349
  if (outScript.type === "tr_ms") {
112934
113350
  const m = outScript.m;
112935
113351
  const pubkeys = outScript.pubkeys;
112936
113352
  let added = 0;
112937
113353
  for (const pub of pubkeys) {
112938
- const sigIdx = scriptSig.findIndex((i) => equalBytes4(i[0].pubKey, pub));
113354
+ const sigIdx = scriptSig.findIndex((i) => equalBytes5(i[0].pubKey, pub));
112939
113355
  if (added === m || sigIdx === -1) {
112940
113356
  signatures.push(EMPTY2);
112941
113357
  continue;
@@ -112947,7 +113363,7 @@ class Transaction {
112947
113363
  continue;
112948
113364
  } else if (outScript.type === "tr_ns") {
112949
113365
  for (const pub of outScript.pubkeys) {
112950
- const sigIdx = scriptSig.findIndex((i) => equalBytes4(i[0].pubKey, pub));
113366
+ const sigIdx = scriptSig.findIndex((i) => equalBytes5(i[0].pubKey, pub));
112951
113367
  if (sigIdx === -1)
112952
113368
  continue;
112953
113369
  signatures.push(scriptSig[sigIdx][1]);
@@ -112957,7 +113373,7 @@ class Transaction {
112957
113373
  } else if (outScript.type === "unknown" && this.opts.allowUnknownInputs) {
112958
113374
  const scriptDecoded = Script.decode(script);
112959
113375
  signatures = scriptSig.map(([{ pubKey }, signature]) => {
112960
- const pos = scriptDecoded.findIndex((i) => isBytes7(i) && equalBytes4(i, pubKey));
113376
+ const pos = scriptDecoded.findIndex((i) => isBytes7(i) && equalBytes5(i, pubKey));
112961
113377
  if (pos === -1)
112962
113378
  throw new Error("finalize/taproot: cannot find position of pubkey in script");
112963
113379
  return { signature, pos };
@@ -113005,7 +113421,7 @@ class Transaction {
113005
113421
  const pubkeys = inputType.last.pubkeys;
113006
113422
  let signatures = [];
113007
113423
  for (const pub of pubkeys) {
113008
- const sign3 = input.partialSig.find((s) => equalBytes4(pub, s[0]));
113424
+ const sign3 = input.partialSig.find((s) => equalBytes5(pub, s[0]));
113009
113425
  if (!sign3)
113010
113426
  continue;
113011
113427
  signatures.push(sign3[1]);
@@ -113079,7 +113495,7 @@ class Transaction {
113079
113495
  }
113080
113496
  const thisUnsigned = this.global.unsignedTx ? RawOldTx.encode(this.global.unsignedTx) : EMPTY2;
113081
113497
  const otherUnsigned = other.global.unsignedTx ? RawOldTx.encode(other.global.unsignedTx) : EMPTY2;
113082
- if (!equalBytes4(thisUnsigned, otherUnsigned))
113498
+ if (!equalBytes5(thisUnsigned, otherUnsigned))
113083
113499
  throw new Error(`Transaction/combine: different unsigned tx`);
113084
113500
  this.global = mergeKeyMap(PSBTGlobal, this.global, other.global, undefined, this.opts.allowUnknown);
113085
113501
  for (let i = 0;i < this.inputs.length; i++)
@@ -117912,7 +118328,7 @@ function validateCoopExitPayoutTransaction({ rawCoopExitTransaction, expectedCoo
117912
118328
  const output = tx.getOutput(i);
117913
118329
  if (!output?.script || output.amount === undefined)
117914
118330
  continue;
117915
- if (!equalBytes4(output.script, expectedScript))
118331
+ if (!equalBytes5(output.script, expectedScript))
117916
118332
  continue;
117917
118333
  if (output.amount >= BigInt(expectedPayoutAmountSats))
117918
118334
  return;
@@ -118330,7 +118746,7 @@ function createSenderSpendTx({ htlcTx, network, hash, hashLockDestinationPubkey,
118330
118746
  const entry = p2.tapLeafScript.find(([, scriptWithVer]) => Buffer.compare(scriptWithVer.slice(0, -1), sequenceLockScript) === 0);
118331
118747
  const controlBlockBytes = TaprootControlBlock.encode(entry[0]);
118332
118748
  const seqLeafEntry = p2.tapLeafScript.find(([_, leaf]) => {
118333
- return equalBytes2(leaf.subarray(0, leaf.length - 1), sequenceLockScript);
118749
+ return equalBytes3(leaf.subarray(0, leaf.length - 1), sequenceLockScript);
118334
118750
  });
118335
118751
  const sighash = senderSpendTx.preimageWitnessV1(0, [sequenceLockScript], SigHash.DEFAULT, [htlcAmount]);
118336
118752
  senderSpendTx.updateInput(0, {
@@ -118380,7 +118796,7 @@ function createReceiverSpendTx({ htlcTx, network, hash, hashLockDestinationPubke
118380
118796
  script: sequenceLockScript
118381
118797
  }]), network, true);
118382
118798
  const hashLeafEntry = p2.tapLeafScript.find(([_, leaf]) => {
118383
- return equalBytes2(leaf.subarray(0, leaf.length - 1), hashLockScript);
118799
+ return equalBytes3(leaf.subarray(0, leaf.length - 1), hashLockScript);
118384
118800
  });
118385
118801
  spendTx.updateInput(0, {
118386
118802
  witnessUtxo: {
@@ -121130,7 +121546,7 @@ async function withRetry(operation, options = {}) {
121130
121546
  function scopeTransferLeavesToReceiver(transfer, receiverIdentityPublicKey) {
121131
121547
  if (transfer.receivers.length <= 1)
121132
121548
  return transfer;
121133
- const ownReceiver = transfer.receivers.find((receiver) => equalBytes2(receiver.identityPublicKey, receiverIdentityPublicKey));
121549
+ const ownReceiver = transfer.receivers.find((receiver) => equalBytes3(receiver.identityPublicKey, receiverIdentityPublicKey));
121134
121550
  if (!ownReceiver?.id)
121135
121551
  throw new SparkValidationError("Cannot scope claim: wallet is not a receiver of this multi-receiver transfer");
121136
121552
  const ownLeaves = transfer.leaves.filter((leaf) => leaf.transferReceiverId === ownReceiver.id);
@@ -121145,7 +121561,7 @@ function isReceiverLegComplete(transfer, receiverIdentityPublicKey) {
121145
121561
  const wholeComplete = transfer.status === TransferStatus.TRANSFER_STATUS_COMPLETED;
121146
121562
  if (transfer.receivers.length <= 1)
121147
121563
  return wholeComplete;
121148
- const ownReceiver = transfer.receivers.find((receiver) => equalBytes2(receiver.identityPublicKey, receiverIdentityPublicKey));
121564
+ const ownReceiver = transfer.receivers.find((receiver) => equalBytes3(receiver.identityPublicKey, receiverIdentityPublicKey));
121149
121565
  if (!ownReceiver)
121150
121566
  return false;
121151
121567
  return ownReceiver.status === TransferReceiverStatus.TRANSFER_RECEIVER_STATUS_COMPLETED || wholeComplete;
@@ -121891,7 +122307,7 @@ function isReceiverTransferStreamEvent(event2) {
121891
122307
  function findOutputPayingScript(tx, script) {
121892
122308
  for (let i = 0;i < tx.outputsLength; i++) {
121893
122309
  const outputScript = tx.getOutput(i).script;
121894
- if (outputScript && equalBytes2(outputScript, script))
122310
+ if (outputScript && equalBytes3(outputScript, script))
121895
122311
  return i;
121896
122312
  }
121897
122313
  }
@@ -121922,7 +122338,7 @@ function resolveRecoverableOutput(leaf, nodes, network, recoveryTxid, outputInde
121922
122338
  const prevOut = tx.getOutput(outputIndex);
121923
122339
  if (prevOut.amount === undefined)
121924
122340
  continue;
121925
- if (!prevOut.script || !equalBytes2(prevOut.script, leafScript))
122341
+ if (!prevOut.script || !equalBytes3(prevOut.script, leafScript))
121926
122342
  continue;
121927
122343
  return {
121928
122344
  txid,
@@ -123211,9 +123627,9 @@ var import_nice_grpc_common, import_light_bolt11_decoder, SparkFrostBase = class
123211
123627
  }
123212
123628
  signTransactionIndex(tx, index2, publicKey) {
123213
123629
  let privateKey;
123214
- if (equalBytes2(publicKey, this.identityKey?.publicKey ?? new Uint8Array))
123630
+ if (equalBytes3(publicKey, this.identityKey?.publicKey ?? new Uint8Array))
123215
123631
  privateKey = this.identityKey?.privateKey;
123216
- else if (equalBytes2(publicKey, this.depositKey?.publicKey ?? new Uint8Array))
123632
+ else if (equalBytes3(publicKey, this.depositKey?.publicKey ?? new Uint8Array))
123217
123633
  privateKey = this.depositKey?.privateKey;
123218
123634
  if (!privateKey)
123219
123635
  throw new SparkValidationError("Private key not found for public key", {
@@ -124054,7 +124470,7 @@ var import_nice_grpc_common, import_light_bolt11_decoder, SparkFrostBase = class
124054
124470
  return response.transfer;
124055
124471
  }
124056
124472
  compareTransfers(transfer1, transfer2) {
124057
- return transfer1.id === transfer2.id && equalBytes2(transfer1.senderIdentityPublicKey, transfer2.senderIdentityPublicKey) && transfer1.status === transfer2.status && transfer1.totalValue === transfer2.totalValue && transfer1.expiryTime?.getTime() === transfer2.expiryTime?.getTime() && transfer1.leaves.length === transfer2.leaves.length;
124473
+ return transfer1.id === transfer2.id && equalBytes3(transfer1.senderIdentityPublicKey, transfer2.senderIdentityPublicKey) && transfer1.status === transfer2.status && transfer1.totalValue === transfer2.totalValue && transfer1.expiryTime?.getTime() === transfer2.expiryTime?.getTime() && transfer1.leaves.length === transfer2.leaves.length;
124058
124474
  }
124059
124475
  }, TransferService, CoopExitService, DENOMINATIONS, Swap = class {
124060
124476
  inLeaves;
@@ -124578,7 +124994,7 @@ var import_nice_grpc_common, import_light_bolt11_decoder, SparkFrostBase = class
124578
124994
  }
124579
124995
  }
124580
124996
  async handleTransferEvent(transfer) {
124581
- if (!this.identityPublicKey || !equalBytes2(transfer.senderIdentityPublicKey, this.identityPublicKey))
124997
+ if (!this.identityPublicKey || !equalBytes3(transfer.senderIdentityPublicKey, this.identityPublicKey))
124582
124998
  return;
124583
124999
  const leafIds = transfer.leaves.flatMap((leaf) => leaf.leaf ? [leaf.leaf.id] : []);
124584
125000
  this.log("handleTransferEvent", `transfer=${transfer.id} type=${transfer.type} status=${transfer.status} leaves=[${leafIds.join(",")}]`);
@@ -125208,12 +125624,12 @@ var import_nice_grpc_common, import_light_bolt11_decoder, SparkFrostBase = class
125208
125624
  return pendingTransfer ? await this.transferService.claimTransfer(pendingTransfer) : [];
125209
125625
  }
125210
125626
  verifyKey(pubkey1, pubkey2, verifyingKey) {
125211
- return equalBytes2(addPublicKeys(pubkey1, pubkey2), verifyingKey);
125627
+ return equalBytes3(addPublicKeys(pubkey1, pubkey2), verifyingKey);
125212
125628
  }
125213
125629
  isLeafConsistent(leaf, opLeaf) {
125214
125630
  if (!opLeaf)
125215
125631
  return false;
125216
- return leaf.status === opLeaf.status && !!leaf.signingKeyshare && !!opLeaf.signingKeyshare && equalBytes2(leaf.signingKeyshare.publicKey, opLeaf.signingKeyshare.publicKey) && equalBytes2(leaf.nodeTx, opLeaf.nodeTx);
125632
+ return leaf.status === opLeaf.status && !!leaf.signingKeyshare && !!opLeaf.signingKeyshare && equalBytes3(leaf.signingKeyshare.publicKey, opLeaf.signingKeyshare.publicKey) && equalBytes3(leaf.nodeTx, opLeaf.nodeTx);
125217
125633
  }
125218
125634
  restoreLocalLockedToAvailable(leafIds) {
125219
125635
  let changed = false;
@@ -134356,7 +134772,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
134356
134772
  if (traceId)
134357
134773
  this.logEvent(`handleStreamEvent: Receiver transfer ${transfer.id} traceId=${traceId}`);
134358
134774
  const { senderIdentityPublicKey, receiverIdentityPublicKey } = transfer;
134359
- const isSelf = equalBytes2(senderIdentityPublicKey, receiverIdentityPublicKey);
134775
+ const isSelf = equalBytes3(senderIdentityPublicKey, receiverIdentityPublicKey);
134360
134776
  this.logEvent(`handleStreamEvent: Receiver transfer: id=${transfer.id} type=${transfer.type} status=${transfer.status} totalValue=${transfer.totalValue} leaves=${transfer.leaves.length} selfTransfer=${isSelf}`);
134361
134777
  if (!isSelf) {
134362
134778
  const incomingLeaves = transfer.leaves.map((l) => l.leaf).filter((l) => !!l);
@@ -135339,7 +135755,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
135339
135755
  const network = this.config.getNetwork();
135340
135756
  const identityPublicKey = await this.config.signer.getIdentityPublicKey();
135341
135757
  const nodes = await this.leafManager.queryWatchtowerExitedNodes();
135342
- return nodes.filter((node) => node.refundTx.length > 0 && equalBytes2(node.ownerIdentityPublicKey, identityPublicKey)).map((leaf) => {
135758
+ return nodes.filter((node) => node.refundTx.length > 0 && equalBytes3(node.ownerIdentityPublicKey, identityPublicKey)).map((leaf) => {
135343
135759
  const recoverable = resolveRecoverableOutput(leaf, nodes, network);
135344
135760
  return {
135345
135761
  leafId: leaf.id,
@@ -135753,7 +136169,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
135753
136169
  }
135754
136170
  const transfer = await this.transferService.sendTransferV3(allLeafKeyTweaks);
135755
136171
  const signerIdentityPublicKey = await this.config.signer.getIdentityPublicKey();
135756
- if (decodedReceivers.some((r) => equalBytes2(r.identityPublicKey, signerIdentityPublicKey))) {
136172
+ if (decodedReceivers.some((r) => equalBytes3(r.identityPublicKey, signerIdentityPublicKey))) {
135757
136173
  const pending = await this.transferService.queryTransfer(transfer.id);
135758
136174
  if (pending)
135759
136175
  await this.claimTransfer({ transfer: pending });
@@ -135797,7 +136213,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
135797
136213
  return await Promise.all(jobs.map(async (job) => {
135798
136214
  try {
135799
136215
  const transfer = await this.transferService.sendTransferWithKeyTweaks(job.leafKeyTweaks, job.sparkInvoice, job.transferId);
135800
- if (equalBytes2(signerIdentityPublicKey, job.receiverIdentityPubkey)) {
136216
+ if (equalBytes3(signerIdentityPublicKey, job.receiverIdentityPubkey)) {
135801
136217
  const pending = await this.transferService.queryTransfer(transfer.id);
135802
136218
  if (pending)
135803
136219
  await this.claimTransfer({ transfer: pending });
@@ -136472,7 +136888,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
136472
136888
  if (!transfer)
136473
136889
  throw new Error("Failed to provide preimage");
136474
136890
  const receiverIdentityPublicKey = transfer.receiverIdentityPublicKey;
136475
- if (equalBytes2(receiverIdentityPublicKey, await this.config.signer.getIdentityPublicKey()))
136891
+ if (equalBytes3(receiverIdentityPublicKey, await this.config.signer.getIdentityPublicKey()))
136476
136892
  await this.claimTransfer({
136477
136893
  transfer,
136478
136894
  emit: true
@@ -136512,7 +136928,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
136512
136928
  fee
136513
136929
  });
136514
136930
  this.config.signer.signTransactionIndex(spendTx, 0, await this.config.signer.getIdentityPublicKey());
136515
- const sig = spendTx.getInput(0).tapScriptSig.find(([ref2]) => equalBytes2(ref2.leafHash, leafHash))?.[1];
136931
+ const sig = spendTx.getInput(0).tapScriptSig.find(([ref2]) => equalBytes3(ref2.leafHash, leafHash))?.[1];
136516
136932
  spendTx.updateInput(0, { finalScriptWitness: [
136517
136933
  sig,
136518
136934
  hexToBytes4(preimage),