@glyphteck/veyl 0.70.0 → 0.71.1

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/cli.js CHANGED
@@ -1009,6 +1009,16 @@ function numberToBytesBE(n, len) {
1009
1009
  function numberToBytesLE(n, len) {
1010
1010
  return numberToBytesBE(n, len).reverse();
1011
1011
  }
1012
+ function equalBytes(a, b) {
1013
+ a = abytes2(a);
1014
+ b = abytes2(b);
1015
+ if (a.length !== b.length)
1016
+ return false;
1017
+ let diff = 0;
1018
+ for (let i = 0;i < a.length; i++)
1019
+ diff |= a[i] ^ b[i];
1020
+ return diff === 0;
1021
+ }
1012
1022
  function copyBytes2(bytes) {
1013
1023
  return Uint8Array.from(abytes2(bytes));
1014
1024
  }
@@ -4726,7 +4736,7 @@ function checkOpts2(defaults, opts) {
4726
4736
  const merged = Object.assign(defaults, opts);
4727
4737
  return merged;
4728
4738
  }
4729
- function equalBytes(a, b) {
4739
+ function equalBytes2(a, b) {
4730
4740
  a = abytes3(a);
4731
4741
  b = abytes3(b);
4732
4742
  if (a.length !== b.length)
@@ -5170,7 +5180,7 @@ var init_aes = __esm(() => {
5170
5180
  const passedTag = ciphertext.subarray(-tagLength);
5171
5181
  const tag = _computeTag(authKey, tagMask, data);
5172
5182
  toClean.push(tag);
5173
- if (!equalBytes(tag, passedTag)) {
5183
+ if (!equalBytes2(tag, passedTag)) {
5174
5184
  clean2(...toClean);
5175
5185
  throw new Error("aes-gcm: invalid tag");
5176
5186
  }
@@ -5857,7 +5867,7 @@ function sameText(left, right) {
5857
5867
  }
5858
5868
 
5859
5869
  // ../../core/config.js
5860
- 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;
5870
+ 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;
5861
5871
  var init_config = __esm(() => {
5862
5872
  MINUTE_MS = 60 * MS_PER_SECOND;
5863
5873
  HOUR_MS = 60 * MINUTE_MS;
@@ -5998,9 +6008,12 @@ function normalizeChatNotificationMode(value, manifest) {
5998
6008
  const mode = cleanText(value);
5999
6009
  if (!NOTIFICATION_MODES.has(mode))
6000
6010
  throw new Error("chat notification mode required");
6001
- if (manifest?.lineage !== "group")
6011
+ if (manifest?.lineage === "self")
6002
6012
  return CHAT_NOTIFICATION_MODES.ALL;
6003
- if (mode === CHAT_NOTIFICATION_MODES.ALL && manifest.members?.length > CHAT_ORDINARY_DELIVERY_MAX_MEMBERS) {
6013
+ if (manifest?.lineage === "direct" && mode === CHAT_NOTIFICATION_MODES.MENTIONS) {
6014
+ throw new Error("mentions notifications require a group chat");
6015
+ }
6016
+ if (mode === CHAT_NOTIFICATION_MODES.ALL && manifest?.lineage === "group" && manifest.members?.length > CHAT_ORDINARY_DELIVERY_MAX_MEMBERS) {
6004
6017
  return CHAT_NOTIFICATION_MODES.MENTIONS;
6005
6018
  }
6006
6019
  return mode;
@@ -7110,7 +7123,7 @@ var xchacha20, ZEROS162, updatePadded = (h, msg) => {
7110
7123
  const data = ciphertext.subarray(0, -tagLength);
7111
7124
  const passedTag = ciphertext.subarray(-tagLength);
7112
7125
  const tag = computeTag2(xorStream, key, nonce, data, AAD);
7113
- if (!equalBytes(passedTag, tag)) {
7126
+ if (!equalBytes2(passedTag, tag)) {
7114
7127
  clean2(tag);
7115
7128
  throw new Error("invalid tag");
7116
7129
  }
@@ -8850,35 +8863,68 @@ function chatAdmissionCapabilityCommitment(value) {
8850
8863
  cleanBytes(capability);
8851
8864
  }
8852
8865
  }
8853
- function directAdmissionCapabilityForPeer(chatPrivateKey, chatPK, profile) {
8854
- return deriveDirectAdmissionCapability(chatPrivateKey, chatPK, profile?.chatPK, profile?.chatPK);
8855
- }
8856
- function hasDirectAdmissionGrant(chatPrivateKey, chatPK, profile) {
8857
- const policy = normalizeChatAdmission(profile?.chatAdmission);
8858
- if (!policy.directGrants.length)
8859
- return false;
8860
- const capability = directAdmissionCapabilityForPeer(chatPrivateKey, chatPK, profile);
8861
- try {
8862
- return policy.directGrants.includes(chatAdmissionCapabilityCommitment(capability));
8863
- } finally {
8864
- cleanBytes(capability);
8865
- }
8866
+ function deriveDirectAdmissionCommitment(chatPrivateKey, chatPK, peerChatPK, recipientChatPK) {
8867
+ return chatAdmissionCapabilityCommitment(deriveDirectAdmissionCapability(chatPrivateKey, chatPK, peerChatPK, recipientChatPK));
8866
8868
  }
8867
- function directAdmissionForPeer(chatPrivateKey, chatPK, profile, ownAdmission = null) {
8868
- if (ownAdmission != null && normalizeChatAdmission(ownAdmission).direct === CHAT_DIRECT_ADMISSION_MODES.CLOSED && incomingChatAdmissionDecision(chatPrivateKey, chatPK, profile, ownAdmission, "direct") !== CHAT_ADMISSION_DECISIONS.ACCEPT)
8869
+ function directAdmissionDecision(chatPK, profile, ownAdmission, commitmentFor) {
8870
+ const ownPolicy = normalizeChatAdmission(ownAdmission);
8871
+ if (ownAdmission != null && ownPolicy.direct === CHAT_DIRECT_ADMISSION_MODES.CLOSED && !ownPolicy.directGrants.includes(commitmentFor(profile?.chatPK, chatPK)))
8869
8872
  return CHAT_ADMISSION_DECISIONS.REJECT;
8870
- if (hasDirectAdmissionGrant(chatPrivateKey, chatPK, profile)) {
8873
+ const policy = normalizeChatAdmission(profile?.chatAdmission);
8874
+ if (policy.directGrants.length && policy.directGrants.includes(commitmentFor(profile?.chatPK, profile?.chatPK))) {
8871
8875
  return CHAT_ADMISSION_DECISIONS.ACCEPT;
8872
8876
  }
8873
- const mode = normalizeChatAdmission(profile?.chatAdmission).direct;
8877
+ const mode = policy.direct;
8874
8878
  if (mode === CHAT_DIRECT_ADMISSION_MODES.OPEN)
8875
8879
  return CHAT_ADMISSION_DECISIONS.ACCEPT;
8876
8880
  if (mode === CHAT_DIRECT_ADMISSION_MODES.REQUESTS)
8877
8881
  return CHAT_ADMISSION_DECISIONS.REQUEST;
8878
8882
  return CHAT_ADMISSION_DECISIONS.REJECT;
8879
8883
  }
8880
- function canStartDirectChat(chatPrivateKey, chatPK, profile, ownAdmission = null) {
8881
- return directAdmissionForPeer(chatPrivateKey, chatPK, profile, ownAdmission) !== CHAT_ADMISSION_DECISIONS.REJECT;
8884
+ function directAdmissionForPeer(chatPrivateKey, chatPK, profile, ownAdmission = null) {
8885
+ return directAdmissionDecision(chatPK, profile, ownAdmission, (peerChatPK, recipientChatPK) => deriveDirectAdmissionCommitment(chatPrivateKey, chatPK, peerChatPK, recipientChatPK));
8886
+ }
8887
+ function createChatAdmission({ getIdentity, deriveCommitment = deriveDirectAdmissionCommitment }) {
8888
+ let chatPK = null;
8889
+ let privateKey = null;
8890
+ let privateKeyBytes = null;
8891
+ const peers = new Map;
8892
+ const reset = () => {
8893
+ peers.clear();
8894
+ cleanBytes(privateKeyBytes);
8895
+ chatPK = null;
8896
+ privateKey = null;
8897
+ privateKeyBytes = null;
8898
+ };
8899
+ const directAdmissionForPeer2 = (profile) => {
8900
+ const identity = getIdentity();
8901
+ if (chatPK !== identity.chatPK || privateKey !== identity.chatPrivateKey || privateKey instanceof Uint8Array && !equalBytes(privateKey, privateKeyBytes)) {
8902
+ reset();
8903
+ chatPK = identity.chatPK;
8904
+ privateKey = identity.chatPrivateKey;
8905
+ privateKeyBytes = privateKey instanceof Uint8Array ? new Uint8Array(privateKey) : null;
8906
+ }
8907
+ return directAdmissionDecision(chatPK, profile, identity.chatAdmission, (peerChatPK, recipientChatPK) => {
8908
+ let commitments = peers.get(peerChatPK);
8909
+ if (!commitments?.has(recipientChatPK)) {
8910
+ const commitment = deriveCommitment(privateKey, chatPK, peerChatPK, recipientChatPK);
8911
+ if (!commitments) {
8912
+ commitments = new Map;
8913
+ if (peers.size >= CHAT_ADMISSION_CACHE_PEERS)
8914
+ peers.delete(peers.keys().next().value);
8915
+ }
8916
+ commitments.set(recipientChatPK, commitment);
8917
+ }
8918
+ peers.delete(peerChatPK);
8919
+ peers.set(peerChatPK, commitments);
8920
+ return commitments.get(recipientChatPK);
8921
+ });
8922
+ };
8923
+ return {
8924
+ directAdmissionForPeer: directAdmissionForPeer2,
8925
+ canStartDirectChat: (profile) => directAdmissionForPeer2(profile) !== CHAT_ADMISSION_DECISIONS.REJECT,
8926
+ reset
8927
+ };
8882
8928
  }
8883
8929
  function canInviteToGroup(profile, ownAdmission = null) {
8884
8930
  return (ownAdmission == null || normalizeChatAdmission(ownAdmission).groups === CHAT_GROUP_ADMISSION_MODES.OPEN) && normalizeChatAdmission(profile?.chatAdmission).groups === CHAT_GROUP_ADMISSION_MODES.OPEN;
@@ -8939,6 +8985,8 @@ var CHAT_ADMISSION_VERSION = 1, CHAT_ADMISSION_GRANT_COUNT = 8, CHAT_ADMISSION_D
8939
8985
  var init_admission = __esm(() => {
8940
8986
  init_ed25519();
8941
8987
  init_sha2();
8988
+ init_utils2();
8989
+ init_config();
8942
8990
  init_core();
8943
8991
  init_kdf();
8944
8992
  init_manifest();
@@ -11143,6 +11191,29 @@ var init_transition = __esm(() => {
11143
11191
  TRANSITION_MEMBER_SCOPE = encoder.encode("veyl-chat-transition-member-v3");
11144
11192
  });
11145
11193
 
11194
+ // ../../core/chat/capabilities.js
11195
+ function memberCount(value) {
11196
+ const count = Number(value);
11197
+ if (!Number.isSafeInteger(count) || count < 1 || count > CHAT_MAX_MEMBERS) {
11198
+ throw new Error("invalid chat member count");
11199
+ }
11200
+ return count;
11201
+ }
11202
+ function chatCapabilities(value) {
11203
+ const members = memberCount(value);
11204
+ return Object.freeze({
11205
+ members,
11206
+ sharedReceipts: members <= CHAT_RECEIPT_MAX_MEMBERS,
11207
+ ordinaryDelivery: members <= CHAT_ORDINARY_DELIVERY_MAX_MEMBERS
11208
+ });
11209
+ }
11210
+ function chatEpochCapabilities(epochState) {
11211
+ return chatCapabilities(epochState?.manifest?.members?.length);
11212
+ }
11213
+ var init_capabilities = __esm(() => {
11214
+ init_config();
11215
+ });
11216
+
11146
11217
  // ../../core/chat/ttl.js
11147
11218
  function hasChatRetention(value) {
11148
11219
  const retention = cleanText(value);
@@ -11191,7 +11262,7 @@ function messageExpiryMs(retention, seenAt) {
11191
11262
  }
11192
11263
  return cleanChatRetention(retention) === CHAT_RETENTION_24H ? seenAt + AFTER_SEEN_MS : seenAt;
11193
11264
  }
11194
- 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;
11265
+ 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;
11195
11266
  var init_ttl = __esm(() => {
11196
11267
  init_config();
11197
11268
  init_time();
@@ -11203,6 +11274,10 @@ var init_ttl = __esm(() => {
11203
11274
  [CHAT_RETENTION_SEEN]: "delete after seen",
11204
11275
  [CHAT_RETENTION_24H]: "keep for 24h"
11205
11276
  });
11277
+ CHAT_RETENTION_DESCRIPTIONS = Object.freeze({
11278
+ [CHAT_RETENTION_SEEN]: "messages now delete after being seen",
11279
+ [CHAT_RETENTION_24H]: "messages now delete 24h after being seen"
11280
+ });
11206
11281
  MESSAGE_STORAGE_TTL_MS = CHAT_UNSAVED_TTL_MS;
11207
11282
  AFTER_SEEN_MS = CHAT_AFTER_SEEN_MS;
11208
11283
  });
@@ -11844,6 +11919,8 @@ async function prepareOwnChatMutation(identity, entryId, current, update) {
11844
11919
  open: (record) => openOwnChatMutationEntry(identity, entryId, record),
11845
11920
  prepare: async (record) => {
11846
11921
  if (!record) {
11922
+ if (current != null)
11923
+ throw makeChatUnavailableError();
11847
11924
  return prepareMutation(identity, entryId, null, update);
11848
11925
  }
11849
11926
  if (!record?.body || !Number.isSafeInteger(record.revision) || record.revision < 1) {
@@ -12681,29 +12758,6 @@ var init_chatstate = __esm(() => {
12681
12758
  STATE_SIGN_SCOPE = encoder.encode("veyl-chat-member-state-v3-sign");
12682
12759
  });
12683
12760
 
12684
- // ../../core/chat/capabilities.js
12685
- function memberCount(value) {
12686
- const count = Number(value);
12687
- if (!Number.isSafeInteger(count) || count < 1 || count > CHAT_MAX_MEMBERS) {
12688
- throw new Error("invalid chat member count");
12689
- }
12690
- return count;
12691
- }
12692
- function chatCapabilities(value) {
12693
- const members = memberCount(value);
12694
- return Object.freeze({
12695
- members,
12696
- sharedReceipts: members <= CHAT_RECEIPT_MAX_MEMBERS,
12697
- ordinaryDelivery: members <= CHAT_ORDINARY_DELIVERY_MAX_MEMBERS
12698
- });
12699
- }
12700
- function chatEpochCapabilities(epochState) {
12701
- return chatCapabilities(epochState?.manifest?.members?.length);
12702
- }
12703
- var init_capabilities = __esm(() => {
12704
- init_config();
12705
- });
12706
-
12707
12761
  // ../../core/chat/memberstate.js
12708
12762
  function openStateEpoch(epochState, actor = null) {
12709
12763
  if (!epochState?.manifest || !epochState?.epochSecret)
@@ -13894,7 +13948,8 @@ function getMsgReactions(msg) {
13894
13948
  continue;
13895
13949
  }
13896
13950
  seen.add(user);
13897
- reactions.push({ emoji, user });
13951
+ const ts = timestampMs(item.ts, null);
13952
+ reactions.push({ emoji, user, ...ts == null ? {} : { ts } });
13898
13953
  if (reactions.length >= MAX_REACTIONS) {
13899
13954
  break;
13900
13955
  }
@@ -13907,7 +13962,7 @@ function sameReactions(a, b) {
13907
13962
  if (left.length !== right.length) {
13908
13963
  return false;
13909
13964
  }
13910
- return left.every((reaction, index) => reaction.user === right[index]?.user && reaction.emoji === right[index]?.emoji);
13965
+ return left.every((reaction, index) => reaction.user === right[index]?.user && reaction.emoji === right[index]?.emoji && reaction.ts === right[index]?.ts);
13911
13966
  }
13912
13967
  function hasReactionControl(messages) {
13913
13968
  for (const msg of messages || []) {
@@ -13947,7 +14002,8 @@ function deriveMessageReactions(messages, chatPK, peerChatPK, memberChatPKs = nu
13947
14002
  byTarget.set(target, reactions);
13948
14003
  }
13949
14004
  if (hasText(msg.emoji)) {
13950
- reactions.set(user, { emoji: cleanReactionEmoji(msg.emoji), user });
14005
+ const ts = timestampMs(getMessageOrderMs(msg), null);
14006
+ reactions.set(user, { emoji: cleanReactionEmoji(msg.emoji), user, ...ts == null ? {} : { ts } });
13951
14007
  } else {
13952
14008
  reactions.delete(user);
13953
14009
  }
@@ -15076,7 +15132,7 @@ function senderIdentity(senderChatPK, senderPrivateKey, options) {
15076
15132
  notificationPrivateKey: options.notificationPrivateKey
15077
15133
  };
15078
15134
  }
15079
- function ownerPreview(senderChatPK, message, messageId, head, tsMs, ttlMs) {
15135
+ function ownerPreview(epoch, senderChatPK, message, messageId, head, tsMs, ttlMs) {
15080
15136
  return {
15081
15137
  ...message || {},
15082
15138
  s: senderChatPK,
@@ -15085,6 +15141,8 @@ function ownerPreview(senderChatPK, message, messageId, head, tsMs, ttlMs) {
15085
15141
  id: messageId,
15086
15142
  ts: tsMs,
15087
15143
  ttl: Number.isFinite(ttlMs) ? ttlMs : null,
15144
+ epochId: epoch.epochId,
15145
+ epochMemberChatPKs: epoch.manifest.members.map((member) => member.chatPK),
15088
15146
  pending: false,
15089
15147
  failed: false
15090
15148
  };
@@ -15301,7 +15359,7 @@ async function prepareMsgRecord(identity, epochState, message, options = {}) {
15301
15359
  msgId: messageId,
15302
15360
  cid: head.cid,
15303
15361
  record: { lane: epoch.messageLane, head, body, ttlMs },
15304
- message: ownerPreview(identity.chatPK, messagePayload, messageId, head, tsMs, ttlMs),
15362
+ message: ownerPreview(epoch, identity.chatPK, messagePayload, messageId, head, tsMs, ttlMs),
15305
15363
  mentionRecipientChatPKs: mentionTargets.recipientChatPKs,
15306
15364
  tsMs
15307
15365
  };
@@ -20021,12 +20079,6 @@ function dropCachedChats(cache, chatIds, currentPayload = null) {
20021
20079
  ...ids.map((chatId) => dropCachedMessageState(cache, chatId))
20022
20080
  ]));
20023
20081
  }
20024
- function dropCachedChat(cache, chatId) {
20025
- if (!chatId) {
20026
- return;
20027
- }
20028
- dropCachedChats(cache, [chatId]);
20029
- }
20030
20082
  var init_chats2 = __esm(() => {
20031
20083
  init_config();
20032
20084
  init_time();
@@ -24927,6 +24979,17 @@ function createRouteMemory(limit = CHAT_MESSAGE_VIEW_CACHE_SIZE) {
24927
24979
  }
24928
24980
  return {
24929
24981
  clear,
24982
+ dropChat(chatId) {
24983
+ const id = String(chatId || "").trim();
24984
+ if (!id)
24985
+ return;
24986
+ const keys = chatRouteMemoryKeys(id, [...states.keys(), ...retained.keys()]);
24987
+ for (const key of keys) {
24988
+ states.delete(key);
24989
+ retained.delete(key);
24990
+ }
24991
+ activeChats.delete(id);
24992
+ },
24930
24993
  resetAccount(nextAccountKey) {
24931
24994
  const next = nextAccountKey || "";
24932
24995
  if (accountKey === next) {
@@ -26531,6 +26594,9 @@ async function verifyOpenedPingEpoch(context) {
26531
26594
  const decision = await decideIncomingChat(context, epochState);
26532
26595
  if (decision === CHAT_ADMISSION_DECISIONS.REJECT)
26533
26596
  return "rejected";
26597
+ const unavailable = await checkOpenedWelcomeAvailability(context);
26598
+ if (unavailable)
26599
+ return unavailable;
26534
26600
  if (decision === CHAT_ADMISSION_DECISIONS.REQUEST)
26535
26601
  return "request";
26536
26602
  } else if (context.entry && (epochState.manifest.lineage === "direct" || epochState.manifest.lineage === "group")) {
@@ -26540,6 +26606,27 @@ async function verifyOpenedPingEpoch(context) {
26540
26606
  }
26541
26607
  return null;
26542
26608
  }
26609
+ async function checkOpenedWelcomeAvailability(context) {
26610
+ const { cloud, identity, options, uid, epochState } = context;
26611
+ assertInboxLease(options);
26612
+ try {
26613
+ await cloud.chat.state.transition(epochState.manifest.chatId, epochState.manifest.epochId, epochState.stateCapability);
26614
+ assertInboxLease(options);
26615
+ return null;
26616
+ } catch (error) {
26617
+ const code = cleanText(error?.code).toLowerCase();
26618
+ if (code !== "permission-denied" && !code.endsWith("/permission-denied"))
26619
+ throw error;
26620
+ try {
26621
+ await retireConfirmedDeletedChat(cloud, uid, identity, context.committedEntry || context.entry || makeOwnChatEntry(epochState), options);
26622
+ return "deleted";
26623
+ } catch (confirmationError) {
26624
+ if (isRetryableInboxError(confirmationError))
26625
+ throw confirmationError;
26626
+ throw error;
26627
+ }
26628
+ }
26629
+ }
26543
26630
  function startOpenedWelcomeHydration(context) {
26544
26631
  const membershipWelcome = (!context.entry || context.restoringMembership) && context.payload.kind === "welcome";
26545
26632
  if (!membershipWelcome)
@@ -26776,17 +26863,30 @@ async function settleOpenedPing(prepared, onAuthoritative) {
26776
26863
  const createsMembership = !context.entry && context.payload.kind === "welcome";
26777
26864
  const ownerPersistence = createsMembership ? captureAsync(persistOpenedPingOwner(context)) : null;
26778
26865
  const messageStartedAt = Date.now();
26779
- const result = await applyOpenedPingMessage(context);
26866
+ const messageOutcome = await captureAsync(applyOpenedPingMessage(context));
26780
26867
  markDiag(context.options.diag, "chat.inbox.settle.message.done", {
26781
26868
  elapsedMs: Date.now() - messageStartedAt
26782
26869
  });
26783
- if (result)
26784
- return result;
26785
- prepareOpenedPingActivity(context);
26786
26870
  if (ownerPersistence)
26787
26871
  await unwrapAsync(ownerPersistence);
26788
- else
26789
- await persistOpenedPingOwner(context);
26872
+ else if (messageOutcome.error)
26873
+ throw messageOutcome.error;
26874
+ if (messageOutcome.value)
26875
+ return messageOutcome.value;
26876
+ if (!messageOutcome.error) {
26877
+ prepareOpenedPingActivity(context);
26878
+ if (!ownerPersistence)
26879
+ await persistOpenedPingOwner(context);
26880
+ }
26881
+ if (context.membershipWelcome) {
26882
+ const unavailable = await checkOpenedWelcomeAvailability(context);
26883
+ if (unavailable) {
26884
+ await cleanupOpenedPing(context);
26885
+ return unavailable;
26886
+ }
26887
+ }
26888
+ if (messageOutcome.error)
26889
+ throw messageOutcome.error;
26790
26890
  publishOpenedPing(context);
26791
26891
  onAuthoritative?.();
26792
26892
  const phaseStartedAt = Date.now();
@@ -26913,6 +27013,7 @@ async function preparePreparedPing(cloud, uid, identity, prepared, options) {
26913
27013
  ping,
26914
27014
  uid
26915
27015
  });
27016
+ assertInboxLease(options);
26916
27017
  return { opened, releaseMutation };
26917
27018
  } catch (error) {
26918
27019
  releaseMutation();
@@ -27367,10 +27468,11 @@ async function prepareInboxWork(context, prepared, processingOptions = {}) {
27367
27468
  const { blockedUids, cloud, identity, options, state, uid } = context;
27368
27469
  const { document: document2, opened } = prepared;
27369
27470
  let announcement = null;
27471
+ let ping = null;
27370
27472
  try {
27371
27473
  const authenticated = await authenticateInboxPing(cloud, identity, document2, opened, state.currentChats, context.profileCache);
27372
27474
  assertInboxLease(options);
27373
- const ping = await preparePreparedPing(cloud, uid, identity, authenticated, {
27475
+ ping = await preparePreparedPing(cloud, uid, identity, authenticated, {
27374
27476
  ...options,
27375
27477
  blockedUids,
27376
27478
  currentChats: state.currentChats,
@@ -27387,9 +27489,11 @@ async function prepareInboxWork(context, prepared, processingOptions = {}) {
27387
27489
  if (ping.opened?.result === "request") {
27388
27490
  await hydrateOpenedChatRequest(ping.opened.context);
27389
27491
  }
27492
+ assertInboxLease(options);
27390
27493
  announcement = announcePreparedWelcome(context, authenticated, ping, document2, prepared.startedAt);
27391
27494
  return { announcement, document: document2, ping, prepared, processingOptions, status: null };
27392
27495
  } catch (error) {
27496
+ ping?.releaseMutation();
27393
27497
  state.revokeAnnouncement(announcement);
27394
27498
  if (processingOptions.deferUnknownWelcome === true && error?.message === "chat mls welcome required") {
27395
27499
  markDiag(options.diag, "chat.inbox.document.deferred", { reason: "welcome_required" });
@@ -27517,6 +27621,8 @@ function createInboxSettlementController(options = {}) {
27517
27621
  for (const job of lane.jobs) {
27518
27622
  if (job.settled)
27519
27623
  continue;
27624
+ if (job.state === "ready")
27625
+ job.discard?.(job.prepared);
27520
27626
  job.settled = true;
27521
27627
  job.state = "settled";
27522
27628
  job.deferred.reject(error);
@@ -27561,9 +27667,12 @@ function createInboxSettlementController(options = {}) {
27561
27667
  }
27562
27668
  job.state = "preparing";
27563
27669
  Promise.resolve().then(job.prepare).then((prepared) => {
27564
- if (job.settled)
27670
+ if (job.settled) {
27671
+ job.discard?.(prepared);
27565
27672
  return;
27673
+ }
27566
27674
  if (!hasLease()) {
27675
+ job.discard?.(prepared);
27567
27676
  finishJob(job, inboxLeaseError());
27568
27677
  return;
27569
27678
  }
@@ -27584,6 +27693,7 @@ function createInboxSettlementController(options = {}) {
27584
27693
  if (!job || job.settled)
27585
27694
  continue;
27586
27695
  if (!hasLease()) {
27696
+ job.discard?.(job.prepared);
27587
27697
  finishJob(job, inboxLeaseError());
27588
27698
  continue;
27589
27699
  }
@@ -27619,6 +27729,7 @@ function createInboxSettlementController(options = {}) {
27619
27729
  const job = {
27620
27730
  chatId,
27621
27731
  deferred: inboxDeferred(),
27732
+ discard: task.discard,
27622
27733
  lane,
27623
27734
  prepare: task.prepare,
27624
27735
  prepared: null,
@@ -27676,6 +27787,10 @@ async function processInboxDocuments(context, documents, processingOptions = {})
27676
27787
  const deferred = [];
27677
27788
  await Promise.all(groups.map((group) => context.settlementController.schedule(cleanText(group[0]?.opened?.payload?.chatId), {
27678
27789
  prepare: () => context.discoveryPool.run(() => prepareInboxWork(context, group[0], processingOptions)),
27790
+ discard: (work) => {
27791
+ work?.ping?.releaseMutation();
27792
+ context.state.revokeAnnouncement(work?.announcement);
27793
+ },
27679
27794
  settle: async (first, markAuthoritative) => {
27680
27795
  if (await settleInboxWork(context, first, markAuthoritative) === "deferred")
27681
27796
  deferred.push(first.document);
@@ -27987,6 +28102,7 @@ function listenToChats(cloud, uid, userChatPK, userPrivKey, onUpdate, onError, o
27987
28102
  nextAfterChat: snapshot.nextAfterChat,
27988
28103
  hasMore: snapshot.hasMore,
27989
28104
  ownerListToken: snapshot.ownerListToken,
28105
+ sourceRevision: snapshot.sourceRevision,
27990
28106
  source: "server"
27991
28107
  });
27992
28108
  deleteConfirmedUserChats(cloud, uid, page.deletedEntryIds);
@@ -28211,7 +28327,8 @@ function listenToChats(cloud, uid, userChatPK, userPrivKey, onUpdate, onError, o
28211
28327
  latestOwnerSnapshot = {
28212
28328
  docs: entries,
28213
28329
  nextAfterChat: meta.nextAfterChat ?? null,
28214
- hasMore: !!meta.hasMore
28330
+ hasMore: !!meta.hasMore,
28331
+ sourceRevision: options.getSourceRevision?.()
28215
28332
  };
28216
28333
  queueOwnerSnapshot();
28217
28334
  }, onError, { limitCount: pageSize });
@@ -28722,15 +28839,15 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
28722
28839
  const ownerFor = (entry) => composeActivity(structuralOwnerFor(entry), entry);
28723
28840
  const cacheFor = (entry) => composeActivity(entry?.cache, entry);
28724
28841
  const sourceCanPublish = (chat, sourceRevision) => {
28842
+ const boundary = authorityBoundaries.get(chat?.id);
28843
+ if (boundary?.terminal)
28844
+ return false;
28725
28845
  if (!chat?.id || !Number.isSafeInteger(sourceRevision))
28726
28846
  return true;
28727
28847
  if (sourceRevision < sourceRevisionFloor)
28728
28848
  return false;
28729
- const boundary = authorityBoundaries.get(chat.id);
28730
28849
  if (!boundary)
28731
28850
  return true;
28732
- if (boundary.terminal)
28733
- return false;
28734
28851
  return sourceRevision >= boundary.revision || compareOwners(chat, boundary.owner) > 0;
28735
28852
  };
28736
28853
  const sourceCanRemove = (chatId, sourceRevision) => {
@@ -28870,21 +28987,33 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
28870
28987
  updatePeers(shown);
28871
28988
  return shown;
28872
28989
  };
28990
+ const clearRemovedOwners = (chatIds) => {
28991
+ if (!chatIds.length)
28992
+ return;
28993
+ const sources = getSources();
28994
+ for (const chatId of chatIds)
28995
+ sources.pendingDeleteIdsRef.current.delete(chatId);
28996
+ sources.onOwnerRemoved?.(chatIds);
28997
+ cache.removeMany(chatIds);
28998
+ const removedIds = new Set(chatIds);
28999
+ sources.setSelectedChat((current) => removedIds.has(current) ? null : current);
29000
+ };
28873
29001
  const convergeCompleteOwnerSet = (incomingIds = null, sourceRevision = null) => {
28874
- const evictedCacheIds = [];
29002
+ const removedIds = [];
28875
29003
  for (const [chatId, entry] of entries) {
28876
- if (!entry.observedOwner && !entry.committedOwner && !entry.announcement) {
28877
- if (!sourceCanRemove(chatId, sourceRevision))
28878
- continue;
28879
- recordTerminal(chatId, cacheFor(entry));
28880
- if (entry.cache)
28881
- evictedCacheIds.push(chatId);
28882
- entries.delete(chatId);
28883
- } else if (!incomingIds || !incomingIds.has(chatId)) {
29004
+ if (incomingIds ? incomingIds.has(chatId) : entry.observedOwner)
29005
+ continue;
29006
+ if (!sourceCanRemove(chatId, sourceRevision))
29007
+ continue;
29008
+ if (entry.announcement && !authorityBoundaries.get(chatId)?.owner) {
28884
29009
  entry.cache = null;
29010
+ continue;
28885
29011
  }
29012
+ recordTerminal(chatId, ownerFor(entry) || cacheFor(entry));
29013
+ entries.delete(chatId);
29014
+ removedIds.push(chatId);
28886
29015
  }
28887
- cache.removeMany(evictedCacheIds);
29016
+ clearRemovedOwners(removedIds);
28888
29017
  };
28889
29018
  const observeOwnerChats = (rows, options = {}) => {
28890
29019
  const incoming = (rows || []).filter((chat) => chat?.id && sourceCanPublish(chat, options.sourceRevision));
@@ -28960,6 +29089,8 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
28960
29089
  const announceWelcome = (announcement) => {
28961
29090
  if (!announcement?.chatId || !announcement?.token)
28962
29091
  return false;
29092
+ if (authorityBoundaries.get(announcement.chatId)?.terminal)
29093
+ return false;
28963
29094
  const sources = getSources();
28964
29095
  if (sources.pendingDeleteIdsRef.current.has(announcement.chatId))
28965
29096
  return false;
@@ -28996,7 +29127,6 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
28996
29127
  return false;
28997
29128
  if (!sourceCanRemove(chatId, options.sourceRevision))
28998
29129
  return false;
28999
- recordTerminal(chatId, cacheFor(entry));
29000
29130
  entry.cache = null;
29001
29131
  entry.presentation = null;
29002
29132
  cache.remove(chatId);
@@ -29006,15 +29136,12 @@ function createChatListIndex({ cache, filterHiddenChats, getSources, isHiddenCha
29006
29136
  return true;
29007
29137
  };
29008
29138
  const removeOwner = (chatId, options = {}) => {
29009
- const sources = getSources();
29010
29139
  if (!chatId)
29011
29140
  return false;
29012
29141
  recordTerminal(chatId, ownerFor(entries.get(chatId)) || cacheFor(entries.get(chatId)));
29013
- sources.pendingDeleteIdsRef.current.delete(chatId);
29014
29142
  const removed = entries.delete(chatId);
29015
- cache.remove(chatId);
29143
+ clearRemovedOwners([chatId]);
29016
29144
  publishOwners({ ...options, warm: false });
29017
- sources.setSelectedChat((current) => current === chatId ? null : current);
29018
29145
  return removed;
29019
29146
  };
29020
29147
  const removeInboxOwner = (chatId, options = {}) => {
@@ -29371,7 +29498,7 @@ function createChatListListener({
29371
29498
  sync();
29372
29499
  }, CHAT_LIST_LISTENER_RETRY_MS);
29373
29500
  };
29374
- const subscription = subscribe(sources.cloud, sources.uid, sources.chatPK, sources.chatPrivateKey, (nextChats, _nextPeers, meta = {}) => handleUpdate(nextChats, meta, listenStartedAt, index.getAuthorityRevision()), (error) => {
29501
+ const subscription = subscribe(sources.cloud, sources.uid, sources.chatPK, sources.chatPrivateKey, (nextChats, _nextPeers, meta = {}) => handleUpdate(nextChats, meta, listenStartedAt, meta.sourceRevision), (error) => {
29375
29502
  const current = getSources();
29376
29503
  state.setIsChatListLive(false);
29377
29504
  markError(current.diag, "chat.provider.listen", listenStartedAt, error);
@@ -29382,6 +29509,7 @@ function createChatListListener({
29382
29509
  retry(error);
29383
29510
  }, {
29384
29511
  limitCount: CHAT_LIST_LIVE_COUNT,
29512
+ getSourceRevision: index.getAuthorityRevision,
29385
29513
  onInboxAnnouncement: index.announceWelcome,
29386
29514
  onInboxRevoke: index.revokeAnnouncement,
29387
29515
  onInboxCommit: index.commitOwner,
@@ -31040,7 +31168,7 @@ function patchMessageFromCachedMessage(message, cached) {
31040
31168
  const patch = {};
31041
31169
  let changed = false;
31042
31170
  const cachedReactions = getMsgReactions(cached);
31043
- if (cachedReactions.length && !sameReactionProjection(message, cached)) {
31171
+ if (cachedReactions.length && !sameReactions(message.reactions, cached.reactions)) {
31044
31172
  patch.reactions = cachedReactions;
31045
31173
  changed = true;
31046
31174
  }
@@ -31066,19 +31194,6 @@ function patchMessageFromCachedMessage(message, cached) {
31066
31194
  }
31067
31195
  return changed ? { ...message, ...patch } : message;
31068
31196
  }
31069
- function sameReactionProjection(a, b) {
31070
- const left = getMsgReactions(a);
31071
- const right = getMsgReactions(b);
31072
- if (left.length !== right.length) {
31073
- return false;
31074
- }
31075
- for (let index = 0;index < left.length; index += 1) {
31076
- if (left[index].user !== right[index]?.user || left[index].emoji !== right[index]?.emoji) {
31077
- return false;
31078
- }
31079
- }
31080
- return true;
31081
- }
31082
31197
  function patchMessagesFromCachedWindow(messages, cachedMessages) {
31083
31198
  if (!messages?.length || !cachedMessages?.length) {
31084
31199
  return messages || [];
@@ -31735,6 +31850,7 @@ async function createGroupChat(cloud, identityValue, memberValues, settings = {}
31735
31850
  if (!identity.chatSigningSecret)
31736
31851
  throw new Error("unlocked chat signing identity required");
31737
31852
  const mls = await lifecycle.mls?.open?.();
31853
+ lifecycle.assertCurrent?.();
31738
31854
  if (!mls)
31739
31855
  throw new Error("mls engine required");
31740
31856
  const selfLeafId = lifecycle.selfLeafId || randomBytes3(32);
@@ -31755,7 +31871,7 @@ async function createGroupChat(cloud, identityValue, memberValues, settings = {}
31755
31871
  if (additions.length)
31756
31872
  mlsGroup = await mls.stageChange(mlsGroup.snapshot, { adds: additions });
31757
31873
  const material = deriveChatMlsEpochMaterial(mlsGroup.epochKey, { chatId, epochVersion: 1 });
31758
- const lineage = members.length === 1 ? CHAT_LINEAGES.SELF : members.length === 2 ? CHAT_LINEAGES.DIRECT : CHAT_LINEAGES.GROUP;
31874
+ const lineage = lifecycle.lineage || (members.length === 1 ? CHAT_LINEAGES.SELF : members.length === 2 ? CHAT_LINEAGES.DIRECT : CHAT_LINEAGES.GROUP);
31759
31875
  const manifest = makeInitialEpochManifest({
31760
31876
  chatId,
31761
31877
  epochId: material.epochId,
@@ -31818,6 +31934,7 @@ async function createGroupChat(cloud, identityValue, memberValues, settings = {}
31818
31934
  });
31819
31935
  initial.backend.membershipOutbox = queuedWelcomes.map(({ outboxId: outboxId2, sealed }) => ({ outboxId: outboxId2, sealed }));
31820
31936
  stage = "owner";
31937
+ lifecycle.assertCurrent?.();
31821
31938
  const registration = registerInitialDelivery(cloud, identity, initialState);
31822
31939
  const stateId = chatMlsStateId(identity.chatPrivateKey, chatId, initial.transitionCommitment);
31823
31940
  const ownerMlsState = await sealChatMlsState(identity.chatPrivateKey, stateId, { v: 1, chatId, transitionCommitment: initial.transitionCommitment }, mlsGroup.snapshot);
@@ -31829,6 +31946,8 @@ async function createGroupChat(cloud, identityValue, memberValues, settings = {}
31829
31946
  let stateError = null;
31830
31947
  try {
31831
31948
  stage = "state";
31949
+ lifecycle.assertCurrent?.();
31950
+ lifecycle.onCreateDispatched?.(initial.manifest.chatId);
31832
31951
  await cloud.chat.state.create({
31833
31952
  ...initial.backend,
31834
31953
  ownerEntry: owner.write,
@@ -32183,21 +32302,23 @@ function createChatSessionLaunches({
32183
32302
  if (!opened?.chatId || !opened?.entry)
32184
32303
  return "";
32185
32304
  const chat = projectOwnChatEntry(opened.entry, opened.entryId || "", operation.identity.chatPK, opened.tsMs || Date.now());
32186
- operation.listOwner.commitOwner(chat);
32305
+ if (!operation.listOwner.commitOwner(chat))
32306
+ throw makeChatUnavailableError();
32187
32307
  return opened.chatId;
32188
32308
  };
32189
32309
  const presentOpenedChat = (opened, operation) => {
32190
32310
  assertCurrent(operation);
32191
32311
  if (!opened?.chatId || !opened?.entry)
32192
32312
  return "";
32193
- operation.pendingOwner.adoptOpenedChatId(opened.chatId);
32194
32313
  const chatId = commitOpenedChat(opened, operation);
32314
+ operation.pendingOwner.adoptOpenedChatId(chatId);
32195
32315
  pendingPeerSelect = null;
32196
32316
  setSelectedChat(chatId);
32197
32317
  return chatId;
32198
32318
  };
32199
32319
  const createChat = async (profiles, settings, lifecycle, adoptOpenedChat, operation, { profilesCurrent = false } = {}) => {
32200
32320
  assertCurrent(operation);
32321
+ lifecycle.assertCurrent?.();
32201
32322
  const { blocked, chatPK, chatPrivateKey } = operation.identity;
32202
32323
  let profileList = Array.isArray(profiles) ? profiles : [profiles];
32203
32324
  if (!profilesCurrent && profileList.length) {
@@ -32205,9 +32326,10 @@ function createChatSessionLaunches({
32205
32326
  }
32206
32327
  const blockedSet = new Set(blocked);
32207
32328
  const requestedMembers = profileList.map(profileMember);
32208
- if (profileList.length === 1) {
32329
+ const expectedLineage = lifecycle.lineage || (requestedMembers.length === 0 ? "self" : requestedMembers.length === 1 ? "direct" : "group");
32330
+ if (expectedLineage === "direct") {
32209
32331
  assertDirectAdmission(profileList[0], operation);
32210
- } else if (profileList.length > 1) {
32332
+ } else if (expectedLineage === "group") {
32211
32333
  if (normalizeChatAdmission(operation.identity.chatAdmission).groups === CHAT_GROUP_ADMISSION_MODES.CLOSED) {
32212
32334
  throw new Error("group chats are closed");
32213
32335
  }
@@ -32221,8 +32343,7 @@ function createChatSessionLaunches({
32221
32343
  if (blockedSet.has(member.uid))
32222
32344
  throw new Error("blocked peer cannot be added");
32223
32345
  }
32224
- const canonicalChatId = lifecycle.chatId || (requestedMembers.length === 0 ? deriveSelfChatId(chatPrivateKey, chatPK) : "");
32225
- const expectedLineage = requestedMembers.length === 0 ? "self" : requestedMembers.length === 1 ? "direct" : "group";
32346
+ const canonicalChatId = lifecycle.chatId || (expectedLineage === "self" ? deriveSelfChatId(chatPrivateKey, chatPK) : "");
32226
32347
  const expectedMemberKeys = [chatPK, ...requestedMembers.map((member) => member.chatPK)].sort();
32227
32348
  const matchingExistingChat = () => {
32228
32349
  assertCurrent(operation);
@@ -32261,6 +32382,7 @@ function createChatSessionLaunches({
32261
32382
  markDiag(diag, "chat.group.create.prekeys.start", { members: requestedMembers.length });
32262
32383
  const additions = await prepareMlsAdditions(profileList);
32263
32384
  assertCurrent(operation);
32385
+ lifecycle.assertCurrent?.();
32264
32386
  markDiag(diag, "chat.group.create.prekeys.done", { members: requestedMembers.length, elapsedMs: Date.now() - prekeyStartedAt });
32265
32387
  if (!additions.length && requestedMembers.length)
32266
32388
  throw new Error("peer mls key package required");
@@ -32270,15 +32392,23 @@ function createChatSessionLaunches({
32270
32392
  const created = await createGroupChat(cloud, operation.transitionIdentity, [...byKey.values()], settings, {
32271
32393
  mls,
32272
32394
  chatId: canonicalChatId || undefined,
32395
+ lineage: expectedLineage,
32273
32396
  wakeMessageId: lifecycle.wakeMessageId,
32397
+ assertCurrent: () => {
32398
+ assertCurrent(operation);
32399
+ lifecycle.assertCurrent?.();
32400
+ },
32401
+ onCreateDispatched: lifecycle.onCreateDispatched,
32274
32402
  onStateReady: (opened) => {
32275
32403
  assertCurrent(operation);
32404
+ lifecycle.assertCurrent?.();
32276
32405
  stateReady = true;
32277
32406
  const chatId = adoptOpenedChat(opened);
32278
32407
  lifecycle.onStateReady?.(chatId, opened, { created: true });
32279
32408
  },
32280
32409
  onOwnerReady: async (opened) => {
32281
32410
  assertCurrent(operation);
32411
+ lifecycle.assertCurrent?.();
32282
32412
  const chatId = adoptOpenedChat(opened);
32283
32413
  const result = await lifecycle.onOwnerReady?.(chatId, opened, { created: true });
32284
32414
  assertCurrent(operation);
@@ -32289,7 +32419,7 @@ function createChatSessionLaunches({
32289
32419
  return adoptOpenedChat(created);
32290
32420
  } catch (error) {
32291
32421
  assertCurrent(operation);
32292
- if (stateReady || !canonicalChatId || requestedMembers.length > 0)
32422
+ if (stateReady || !canonicalChatId)
32293
32423
  throw error;
32294
32424
  await ensureChat(canonicalChatId);
32295
32425
  assertCurrent(operation);
@@ -32303,6 +32433,10 @@ function createChatSessionLaunches({
32303
32433
  const operation = operationContext();
32304
32434
  return createChat(profiles, settings, lifecycle, (opened) => presentOpenedChat(opened, operation), operation);
32305
32435
  };
32436
+ const materializeGroupChat = (profiles, settings = {}, lifecycle = {}) => {
32437
+ const operation = operationContext();
32438
+ return createChat(profiles, settings, lifecycle, (opened) => commitOpenedChat(opened, operation), operation);
32439
+ };
32306
32440
  const runExistingDirectLifecycle = async (chatId, lifecycle, operation) => {
32307
32441
  assertCurrent(operation);
32308
32442
  const existing = getOwnerChat(chatId);
@@ -32374,6 +32508,7 @@ function createChatSessionLaunches({
32374
32508
  getOpenOperation: (chatId) => directOpens.get(chatId)?.promise || null,
32375
32509
  getPeerChatId,
32376
32510
  materializeDirectChat,
32511
+ materializeGroupChat,
32377
32512
  openDirectChat,
32378
32513
  openNotesChat,
32379
32514
  releaseOpenOperation: (chatId) => {
@@ -32501,16 +32636,21 @@ function createChatSessionPending({
32501
32636
  getPeerProfile,
32502
32637
  resolvePeerProfile,
32503
32638
  setLocalByChat,
32504
- getLocalByChat = () => new Map
32639
+ getLocalByChat = () => new Map,
32640
+ retireMaterializedChat = () => {}
32505
32641
  }) {
32642
+ const admission = createChatAdmission({ getIdentity });
32506
32643
  let pendingLaunch = null;
32507
32644
  const pendingOperations = new Map;
32508
32645
  let generation = 0;
32646
+ const operationForChat = (chatId) => pendingOperations.get(chatId) || [...pendingOperations.values()].find((state) => state.canonicalChatId === chatId) || null;
32647
+ const launchForChat = (chatId) => pendingLaunch && [pendingLaunch.id, pendingLaunch.canonicalChatId].includes(chatId) ? pendingLaunch : operationForChat(chatId)?.launch || storedLaunch(chatId);
32509
32648
  const storedLaunch = (chatId) => getLocalByChat().get(chatId)?.find((message) => message.chatDraft)?.chatDraft || null;
32510
32649
  const getPendingLaunch = () => {
32511
32650
  if (!pendingLaunch)
32512
32651
  return null;
32513
- const messages = getLocalByChat().get(pendingLaunch.id) || [];
32652
+ const localByChat = getLocalByChat();
32653
+ const messages = localByChat.get(pendingLaunch.id) || localByChat.get(pendingLaunch.canonicalChatId) || [];
32514
32654
  const message = messages[messages.length - 1] || null;
32515
32655
  return { ...pendingLaunch, messages, message, chat: {
32516
32656
  ...pendingLaunch.chat,
@@ -32518,23 +32658,33 @@ function createChatSessionPending({
32518
32658
  ts: message ? timestampMs(message.ts, null) : null
32519
32659
  } };
32520
32660
  };
32661
+ const resolvePendingChatId = (chatId) => {
32662
+ const canonicalId = operationForChat(chatId)?.canonicalChatId || launchForChat(chatId)?.canonicalChatId;
32663
+ return canonicalId && getOwnerChat(canonicalId) ? canonicalId : chatId;
32664
+ };
32521
32665
  const finish = (launchId) => {
32522
32666
  if (pendingLaunch?.id !== launchId)
32523
32667
  return;
32668
+ if (pendingLaunch.canonicalChatId !== launchId) {
32669
+ publish();
32670
+ return;
32671
+ }
32524
32672
  pendingLaunch = null;
32525
32673
  pendingOperations.delete(launchId);
32526
32674
  publish();
32527
32675
  };
32528
32676
  const track = (launchId, operation) => {
32529
32677
  const tracked = Promise.resolve(operation);
32530
- pendingOperations.set(launchId, tracked);
32678
+ const state = pendingOperations.get(launchId) || { active: true, promise: null };
32679
+ state.promise = tracked;
32680
+ pendingOperations.set(launchId, state);
32531
32681
  tracked.then(() => {
32532
- if (pendingOperations.get(launchId) !== tracked)
32682
+ if (pendingOperations.get(launchId) !== state || state.promise !== tracked)
32533
32683
  return;
32534
32684
  pendingOperations.delete(launchId);
32535
32685
  finish(launchId);
32536
32686
  }, () => {
32537
- if (pendingOperations.get(launchId) !== tracked)
32687
+ if (pendingOperations.get(launchId) !== state || state.promise !== tracked)
32538
32688
  return;
32539
32689
  pendingOperations.delete(launchId);
32540
32690
  if (pendingLaunch?.id !== launchId)
@@ -32546,6 +32696,12 @@ function createChatSessionPending({
32546
32696
  });
32547
32697
  return tracked;
32548
32698
  };
32699
+ const cancelOperation = (launchId) => {
32700
+ const operation = pendingOperations.get(launchId);
32701
+ if (operation)
32702
+ operation.active = false;
32703
+ pendingOperations.delete(launchId);
32704
+ };
32549
32705
  const show = (launch) => {
32550
32706
  if (pendingLaunch?.id && pendingLaunch.id !== launch.id && !pendingOperations.has(pendingLaunch.id)) {
32551
32707
  getRouteOwner().releaseOpenOperation?.(pendingLaunch.id);
@@ -32641,7 +32797,7 @@ function createChatSessionPending({
32641
32797
  return cached;
32642
32798
  return (fallbackProfiles || []).find((profile) => profileMatchesPeer(profile, peer)) || null;
32643
32799
  };
32644
- const profileAcceptsDirect = (identity, profile) => !!identity?.chatPK && !!identity?.chatPrivateKey && directAdmissionForPeer(identity.chatPrivateKey, identity.chatPK, profile, identity.chatAdmission) !== CHAT_ADMISSION_DECISIONS.REJECT;
32800
+ const profileAcceptsDirect = (identity, profile) => !!identity?.chatPK && !!identity?.chatPrivateKey && admission.directAdmissionForPeer(profile) !== CHAT_ADMISSION_DECISIONS.REJECT;
32645
32801
  const isChatAdmitted = (chat, fallbackProfiles = []) => {
32646
32802
  const identity = getIdentity();
32647
32803
  if (!chat)
@@ -32774,18 +32930,29 @@ function createChatSessionPending({
32774
32930
  throw error;
32775
32931
  }
32776
32932
  };
32777
- const sendDurableAttachment = async (chatId, attachment, options = {}) => {
32778
- const target = await admittedSendTarget(chatId, options);
32779
- return getSendActions().sendAttachment(target.routeAnchor, attachment, target.options);
32780
- };
32781
- const sendDurableSharedAttachment = async (chatId, attachment, options = {}) => {
32933
+ const withReadyAttachmentTarget = async (chatId, options, send) => {
32934
+ const attemptGeneration = generation;
32935
+ await waitForSend(chatId, { expiresAt: options.expiresAt, signal: options.signal });
32936
+ if (attemptGeneration !== generation)
32937
+ throw makeChatUnavailableError();
32782
32938
  const target = await admittedSendTarget(chatId, options);
32783
- return getSendActions().sendSharedAttachment(target.routeAnchor, attachment, target.options);
32939
+ if (attemptGeneration !== generation)
32940
+ throw makeChatUnavailableError();
32941
+ return send(target);
32784
32942
  };
32943
+ const sendDurableAttachment = (chatId, attachment, options = {}) => withReadyAttachmentTarget(chatId, options, (target) => getSendActions().sendAttachment(target.routeAnchor, attachment, target.options));
32944
+ const sendDurableSharedAttachment = (chatId, attachment, options = {}) => withReadyAttachmentTarget(chatId, options, (target) => getSendActions().sendSharedAttachment(target.routeAnchor, attachment, target.options));
32785
32945
  const stagePendingMessage = (launchId, local) => {
32786
- if (!pendingLaunch || pendingLaunch.id !== launchId)
32946
+ const launch = launchForChat(launchId);
32947
+ if (!launch || launch.id !== launchId)
32787
32948
  throw makeChatUnavailableError();
32788
- const { id, profiles: profiles2, settings, chat } = pendingLaunch;
32949
+ const { id, profiles: profiles2, settings, chat } = launch;
32950
+ const canonicalId = resolvePendingChatId(launchId);
32951
+ if (getOwnerChat(canonicalId)) {
32952
+ const { chatDraft: _draft, ...content } = local;
32953
+ setLocalByChat((current) => addLocalMessage(current, canonicalId, content));
32954
+ return;
32955
+ }
32789
32956
  const chatDraft = { id, canonicalChatId: id, profiles: profiles2, settings, chat };
32790
32957
  setLocalByChat((current) => addLocalMessage(current, launchId, { ...local, chatDraft }));
32791
32958
  };
@@ -32797,6 +32964,8 @@ function createChatSessionPending({
32797
32964
  const canonical = targetId !== launchId ? current.get(targetId) || [] : [];
32798
32965
  const writerIds = new Set(canonical.map((message) => message.cid));
32799
32966
  const staged = (current.get(launchId) || []).filter((message) => !writerIds.has(message.cid));
32967
+ if (!canonical.length && !staged.length)
32968
+ return current;
32800
32969
  next.set(targetId, [...canonical, ...staged].map((message) => {
32801
32970
  const { chatDraft, ...content } = message;
32802
32971
  return {
@@ -32812,14 +32981,22 @@ function createChatSessionPending({
32812
32981
  if (getOwnerChat(canonicalId) && pendingLaunch?.id === launchId)
32813
32982
  setSelectedChat(canonicalId);
32814
32983
  };
32815
- const retirePeer = (peerChatPK) => {
32816
- const launch = pendingLaunch;
32817
- if (!launch?.chat?.members?.some((member) => member?.chatPK === peerChatPK))
32984
+ const dropChat = (chatId) => {
32985
+ const launch = launchForChat(chatId);
32986
+ if (!launch || launch.id !== chatId && launch.canonicalChatId !== chatId)
32818
32987
  return false;
32819
32988
  beginSelection();
32820
32989
  getRouteOwner().releaseOpenOperation?.(launch.id);
32821
- pendingLaunch = null;
32822
- pendingOperations.delete(launch.id);
32990
+ if (pendingLaunch?.id === launch.id)
32991
+ pendingLaunch = null;
32992
+ cancelOperation(launch.id);
32993
+ setLocalByChat((current) => {
32994
+ if (!current.has(launch.id))
32995
+ return current;
32996
+ const next = new Map(current);
32997
+ next.delete(launch.id);
32998
+ return next;
32999
+ });
32823
33000
  if (getSelectedChatId() === launch.id || getSelectedChatId() === launch.canonicalChatId) {
32824
33001
  setSelectedChat(null);
32825
33002
  } else {
@@ -32827,25 +33004,74 @@ function createChatSessionPending({
32827
33004
  }
32828
33005
  return true;
32829
33006
  };
33007
+ const retirePeer = (peerChatPK) => pendingLaunch?.chat?.members?.some((member) => member?.chatPK === peerChatPK) ? dropChat(pendingLaunch.id) : false;
33008
+ const discardChat = async (chatId) => {
33009
+ const state = operationForChat(chatId);
33010
+ if (!state?.dispatched)
33011
+ return { discarded: dropChat(chatId), chat: null };
33012
+ state.discarding = true;
33013
+ if (pendingLaunch?.id === state.launch.id)
33014
+ pendingLaunch = null;
33015
+ if ([state.launch.id, state.canonicalChatId].includes(getSelectedChatId()))
33016
+ setSelectedChat(null);
33017
+ else
33018
+ publish();
33019
+ state.retirementTarget ||= getOwnerChat(state.canonicalChatId) || null;
33020
+ if (state.retirementTarget)
33021
+ retireMaterializedChat(state.canonicalChatId);
33022
+ const attemptGeneration = generation;
33023
+ await state.promise.catch(() => null);
33024
+ if (attemptGeneration !== generation)
33025
+ throw makeChatUnavailableError();
33026
+ if (!state.retirementTarget) {
33027
+ if (!getOwnerChat(state.canonicalChatId))
33028
+ await ensureChat(state.canonicalChatId);
33029
+ state.retirementTarget = getOwnerChat(state.canonicalChatId) || null;
33030
+ }
33031
+ if (attemptGeneration !== generation || !state.retirementTarget)
33032
+ throw makeChatUnavailableError();
33033
+ retireMaterializedChat(state.canonicalChatId);
33034
+ return { discarded: true, chat: state.retirementTarget };
33035
+ };
32830
33036
  const materialize = (launchId, wakeMessageId, sendInitial) => {
32831
33037
  const launch = pendingLaunch;
32832
33038
  if (!launch || launch.id !== launchId || pendingOperations.has(launchId))
32833
33039
  throw makeChatUnavailableError();
32834
33040
  const attemptGeneration = generation;
33041
+ const state = { active: true, promise: null, launch, canonicalChatId: launchId };
33042
+ pendingOperations.set(launchId, state);
33043
+ const assertCurrent = () => {
33044
+ if (attemptGeneration !== generation || !state.active)
33045
+ throw makeChatUnavailableError();
33046
+ };
32835
33047
  const expiresAt = Date.now() + CHAT_SEND_CONNECTION_WAIT_MS;
32836
33048
  let canonicalId = launchId;
32837
33049
  const operation = (async () => {
32838
33050
  await waitForSend(launchId, { expiresAt });
32839
- if (attemptGeneration !== generation)
32840
- throw makeChatUnavailableError();
33051
+ assertCurrent();
32841
33052
  let initialResult = null;
32842
33053
  const lifecycle = {
32843
33054
  wakeMessageId,
33055
+ assertCurrent,
33056
+ onCreateDispatched: (chatId) => {
33057
+ assertCurrent();
33058
+ state.dispatched = true;
33059
+ state.canonicalChatId = chatId;
33060
+ state.launch = { ...state.launch, canonicalChatId: chatId };
33061
+ },
32844
33062
  onStateReady: (chatId) => {
33063
+ assertCurrent();
32845
33064
  canonicalId = chatId;
33065
+ state.canonicalChatId = chatId;
33066
+ state.launch = { ...state.launch, canonicalChatId: chatId };
32846
33067
  adoptOpenedChatId(chatId, launchId);
33068
+ if (state.discarding) {
33069
+ state.retirementTarget = getOwnerChat(chatId);
33070
+ retireMaterializedChat(chatId);
33071
+ }
32847
33072
  },
32848
33073
  onOwnerReady: async (chatId, _opened, context) => {
33074
+ assertCurrent();
32849
33075
  canonicalId = chatId;
32850
33076
  initialResult = await sendInitial(chatId, { ...context, expiresAt });
32851
33077
  return initialResult;
@@ -32860,18 +33086,20 @@ function createChatSessionPending({
32860
33086
  canonicalId = resolvedChatId;
32861
33087
  initialResult = await sendInitial(resolvedChatId, { created: false, expiresAt });
32862
33088
  } else {
32863
- canonicalId = await getRouteOwner().createGroupChat(launch.profiles, launch.settings, {
33089
+ canonicalId = await getRouteOwner().materializeGroupChat(launch.profiles, launch.settings, {
32864
33090
  ...lifecycle,
32865
- chatId: launchId
33091
+ chatId: launch.canonicalChatId,
33092
+ lineage: launch.chat.lineage
32866
33093
  });
32867
33094
  }
32868
33095
  }
32869
33096
  if (attemptGeneration !== generation)
32870
33097
  throw makeChatUnavailableError();
32871
- settleMaterialization(launchId, canonicalId);
33098
+ if (state.active)
33099
+ settleMaterialization(launchId, canonicalId);
32872
33100
  return { ...initialResult || {}, chatId: canonicalId };
32873
33101
  })().catch((error) => {
32874
- if (attemptGeneration === generation)
33102
+ if (attemptGeneration === generation && state.active)
32875
33103
  settleMaterialization(launchId, canonicalId, true);
32876
33104
  throw error;
32877
33105
  });
@@ -32882,10 +33110,74 @@ function createChatSessionPending({
32882
33110
  ...context?.expiresAt ? { expiresAt: context.expiresAt } : {},
32883
33111
  ...context?.created === true ? { deliverRecipients: false, ownerImmediate: true } : {}
32884
33112
  });
33113
+ const runChatMutation = (chatId, mutate, { initialProfiles } = {}) => {
33114
+ const launch = launchForChat(chatId);
33115
+ const canonicalId = resolvePendingChatId(chatId);
33116
+ const active = launch && pendingOperations.get(launch.id);
33117
+ if (active?.discarding)
33118
+ throw makeChatUnavailableError();
33119
+ if (!active && getOwnerChat(canonicalId))
33120
+ return mutate(canonicalId, { created: false });
33121
+ if (!launch || getIdentity().chatBanned)
33122
+ throw makeChatUnavailableError();
33123
+ const attemptGeneration = generation;
33124
+ const perform = async (id, context) => {
33125
+ if (attemptGeneration !== generation)
33126
+ throw makeChatUnavailableError();
33127
+ const value = await mutate(id, context);
33128
+ if (attemptGeneration !== generation)
33129
+ throw makeChatUnavailableError();
33130
+ const ownerEntry = getOwnerChat(id)?.ownEntry;
33131
+ if (!ownerEntry)
33132
+ throw makeChatUnavailableError();
33133
+ return { value, ownerEntry };
33134
+ };
33135
+ if (active) {
33136
+ return track(launch.id, active.promise.then(async (result) => {
33137
+ if (!active.active)
33138
+ throw makeChatUnavailableError();
33139
+ const id = result?.chatId || launch.canonicalChatId;
33140
+ return { ...await perform(id, { created: false }), chatId: id };
33141
+ })).then((result) => result.value);
33142
+ }
33143
+ pendingLaunch = launch;
33144
+ if (initialProfiles) {
33145
+ if (launch.chat.lineage === "self")
33146
+ throw new Error("self chat membership is fixed");
33147
+ const profiles2 = initialProfiles(launch.profiles);
33148
+ if (profiles2 === launch.profiles)
33149
+ return Promise.resolve({ unchanged: true });
33150
+ const members = profiles2.map(profileMember);
33151
+ chatCapabilities(members.length + 1);
33152
+ const self2 = memberFromChatIdentity(transitionIdentity());
33153
+ if (members.some((member) => member.chatPK === self2.chatPK))
33154
+ throw new Error("current user is already a chat member");
33155
+ const lineage = launch.chat.lineage === "group" || members.length !== 1 ? "group" : launch.chat.lineage;
33156
+ pendingLaunch = {
33157
+ ...launch,
33158
+ canonicalChatId: lineage === "group" && launch.chat.lineage !== "group" ? randomChatId() : launch.canonicalChatId,
33159
+ profiles: profiles2.map((profile, index) => pendingProfile(profile, members[index])),
33160
+ chat: {
33161
+ ...launch.chat,
33162
+ lineage,
33163
+ peerChatPK: lineage === "direct" ? members[0]?.chatPK || null : null,
33164
+ members: [self2, ...members],
33165
+ memberCount: members.length + 1,
33166
+ memberChatPKs: [self2.chatPK, ...members.map((member) => member.chatPK)],
33167
+ memberUids: [self2.uid, ...members.map((member) => member.uid)]
33168
+ }
33169
+ };
33170
+ publish();
33171
+ }
33172
+ return materialize(launch.id, "", (id, context) => perform(id, {
33173
+ ...context,
33174
+ ...initialSendOptions({}, context)
33175
+ })).then((result) => result.value);
33176
+ };
32885
33177
  const sendPendingMessage = (launchId, message, options = {}) => {
32886
33178
  const { chatPK } = getIdentity();
32887
- const launch = pendingLaunch;
32888
- const previousOperation = pendingOperations.get(launchId);
33179
+ const launch = launchForChat(launchId);
33180
+ const previousOperation = pendingOperations.get(launchId)?.promise;
32889
33181
  if (!launch || launch.id !== launchId)
32890
33182
  throw makeChatUnavailableError();
32891
33183
  const routeAnchor = launch.chat?.members?.find((member) => member.chatPK !== chatPK)?.chatPK || chatPK;
@@ -32904,8 +33196,8 @@ function createChatSessionPending({
32904
33196
  };
32905
33197
  const sendPendingAttachment = (launchId, attachment, options = {}) => {
32906
33198
  const { chatPK } = getIdentity();
32907
- const launch = pendingLaunch;
32908
- const previousOperation = pendingOperations.get(launchId);
33199
+ const launch = launchForChat(launchId);
33200
+ const previousOperation = pendingOperations.get(launchId)?.promise;
32909
33201
  if (!launch || launch.id !== launchId)
32910
33202
  throw makeChatUnavailableError();
32911
33203
  const routeAnchor = launch.chat?.members?.find((member) => member.chatPK !== chatPK)?.chatPK || chatPK;
@@ -32920,8 +33212,8 @@ function createChatSessionPending({
32920
33212
  };
32921
33213
  const sendPendingSharedAttachment = (launchId, attachment, options = {}) => {
32922
33214
  const { chatPK } = getIdentity();
32923
- const launch = pendingLaunch;
32924
- const previousOperation = pendingOperations.get(launchId);
33215
+ const launch = launchForChat(launchId);
33216
+ const previousOperation = pendingOperations.get(launchId)?.promise;
32925
33217
  if (!launch || launch.id !== launchId)
32926
33218
  throw makeChatUnavailableError();
32927
33219
  const routeAnchor = launch.chat?.members?.find((member) => member.chatPK !== chatPK)?.chatPK || chatPK;
@@ -32934,6 +33226,12 @@ function createChatSessionPending({
32934
33226
  return track(launchId, previousOperation.then((result) => sendDurableSharedAttachment(result?.chatId || launchId, queuedAttachment, options)));
32935
33227
  };
32936
33228
  const sendChatMessage = (chatId, message, options = {}) => {
33229
+ const operation = operationForChat(chatId);
33230
+ if (operation?.discarding)
33231
+ throw makeChatUnavailableError();
33232
+ if (operation)
33233
+ return sendPendingMessage(operation.launch.id, message, options);
33234
+ chatId = resolvePendingChatId(chatId);
32937
33235
  if (!getOwnerChat(chatId)) {
32938
33236
  if (!pendingLaunch)
32939
33237
  pendingLaunch = storedLaunch(chatId);
@@ -32944,9 +33242,10 @@ function createChatSessionPending({
32944
33242
  return sendDurableMessage(chatId, message, options);
32945
33243
  };
32946
33244
  const retryMessage = (chatId, cid) => {
33245
+ chatId = resolvePendingChatId(chatId);
32947
33246
  const attemptGeneration = generation;
32948
33247
  const launch = pendingLaunch && (pendingLaunch.id === chatId || pendingLaunch.canonicalChatId === chatId) ? pendingLaunch : storedLaunch(chatId);
32949
- const failed = getLocalByChat().get(launch?.id || chatId)?.find((message) => message.cid === cid && message.failed);
33248
+ const failed = getLocalByChat().get(chatId)?.find((message) => message.cid === cid && message.failed);
32950
33249
  if (!failed)
32951
33250
  return false;
32952
33251
  if (launch && !getOwnerChat(chatId)) {
@@ -32983,8 +33282,24 @@ function createChatSessionPending({
32983
33282
  getSendActions().discardLocalMessage(chatId, cid);
32984
33283
  return true;
32985
33284
  };
32986
- const sendChatAttachment = (chatId, attachment, options = {}) => pendingLaunch?.id === chatId && !getOwnerChat(chatId) ? sendPendingAttachment(chatId, attachment, options) : sendDurableAttachment(chatId, attachment, options);
32987
- const sendChatSharedAttachment = (chatId, attachment, options = {}) => pendingLaunch?.id === chatId && !getOwnerChat(chatId) ? sendPendingSharedAttachment(chatId, attachment, options) : sendDurableSharedAttachment(chatId, attachment, options);
33285
+ const sendChatAttachment = (chatId, attachment, options = {}) => {
33286
+ const operation = operationForChat(chatId);
33287
+ if (operation?.discarding)
33288
+ throw makeChatUnavailableError();
33289
+ if (operation)
33290
+ return sendPendingAttachment(operation.launch.id, attachment, options);
33291
+ chatId = resolvePendingChatId(chatId);
33292
+ return pendingLaunch?.id === chatId && !getOwnerChat(chatId) ? sendPendingAttachment(chatId, attachment, options) : sendDurableAttachment(chatId, attachment, options);
33293
+ };
33294
+ const sendChatSharedAttachment = (chatId, attachment, options = {}) => {
33295
+ const operation = operationForChat(chatId);
33296
+ if (operation?.discarding)
33297
+ throw makeChatUnavailableError();
33298
+ if (operation)
33299
+ return sendPendingSharedAttachment(operation.launch.id, attachment, options);
33300
+ chatId = resolvePendingChatId(chatId);
33301
+ return pendingLaunch?.id === chatId && !getOwnerChat(chatId) ? sendPendingSharedAttachment(chatId, attachment, options) : sendDurableSharedAttachment(chatId, attachment, options);
33302
+ };
32988
33303
  const sendDirectAttachment = async (profile, attachment, options = {}) => sendChatAttachment(await getRouteOwner().openDirectChat(profile), attachment, options);
32989
33304
  const sendMaterializedDirect = async (profile, payload, options, send) => {
32990
33305
  const queuedPayload = { ...payload, cid: makeSendCid(payload) };
@@ -33031,6 +33346,10 @@ function createChatSessionPending({
33031
33346
  return settleNormalizedTargets(normalizedTargets, (target) => sendPickerTarget(target, (chatId) => sendChatSharedAttachment(chatId, attachment), (profile) => sendPickerDirectSharedAttachment(profile, attachment)));
33032
33347
  };
33033
33348
  return {
33349
+ directAdmissionForPeer: admission.directAdmissionForPeer,
33350
+ dropChat,
33351
+ discardChat,
33352
+ canStartDirectChat: admission.canStartDirectChat,
33034
33353
  adoptOpenedChatId,
33035
33354
  adoptResolved,
33036
33355
  beginPendingChat,
@@ -33038,8 +33357,11 @@ function createChatSessionPending({
33038
33357
  canSendToChat,
33039
33358
  isChatAdmitted,
33040
33359
  isPendingLaunchAdmitted,
33041
- isPendingChat: (chatId) => pendingLaunch?.id === chatId || !!storedLaunch(chatId),
33360
+ isPendingChat: (chatId) => !!launchForChat(chatId),
33042
33361
  getPendingLaunch,
33362
+ getActionChat: (chatId) => getOwnerChat(resolvePendingChatId(chatId)) || launchForChat(chatId)?.chat || null,
33363
+ hasPendingChatOperation: (chatId) => !!operationForChat(chatId),
33364
+ runChatMutation,
33043
33365
  openNewChat,
33044
33366
  retirePeer,
33045
33367
  reconcileAdmission: () => {
@@ -33049,7 +33371,7 @@ function createChatSessionPending({
33049
33371
  beginSelection();
33050
33372
  getRouteOwner().clearOpenOperations?.();
33051
33373
  pendingLaunch = null;
33052
- pendingOperations.delete(launch.id);
33374
+ cancelOperation(launch.id);
33053
33375
  if (getSelectedChatId() === launch.id || getSelectedChatId() === launch.canonicalChatId) {
33054
33376
  setSelectedChat(null);
33055
33377
  } else {
@@ -33059,10 +33381,13 @@ function createChatSessionPending({
33059
33381
  },
33060
33382
  reset: () => {
33061
33383
  generation += 1;
33384
+ admission.reset();
33062
33385
  beginSelection();
33063
33386
  if (pendingLaunch?.id)
33064
33387
  getRouteOwner().releaseOpenOperation?.(pendingLaunch.id);
33065
33388
  pendingLaunch = null;
33389
+ for (const operation of pendingOperations.values())
33390
+ operation.active = false;
33066
33391
  pendingOperations.clear();
33067
33392
  },
33068
33393
  selectChat: (chatId, { flushPrevious }) => {
@@ -33075,9 +33400,14 @@ function createChatSessionPending({
33075
33400
  const selectResolved = () => {
33076
33401
  if (!isSelectionCurrent(selection) || getIdentity().chatBanned)
33077
33402
  return false;
33078
- if (pendingLaunch && chatId !== pendingLaunch.id && !pendingOperations.has(pendingLaunch.id)) {
33403
+ if (pendingLaunch && chatId !== pendingLaunch.id) {
33404
+ if (chatId === pendingLaunch.canonicalChatId) {
33405
+ settleMaterialization(pendingLaunch.id, chatId);
33406
+ }
33079
33407
  getRouteOwner().releaseOpenOperation?.(pendingLaunch.id);
33080
33408
  pendingLaunch = null;
33409
+ if (getSelectedChatId() === chatId)
33410
+ publish();
33081
33411
  }
33082
33412
  const previousChatId = getSelectedChatId();
33083
33413
  if (previousChatId && previousChatId !== chatId)
@@ -33132,6 +33462,7 @@ var init_sessionpending = __esm(() => {
33132
33462
  init_messages();
33133
33463
  init_state();
33134
33464
  init_targets();
33465
+ init_capabilities();
33135
33466
  });
33136
33467
 
33137
33468
  // ../../core/chat/sessionmembership.js
@@ -33626,7 +33957,6 @@ function createChatDelete({
33626
33957
  for (const chatId of chatIds || []) {
33627
33958
  if (!chatId)
33628
33959
  continue;
33629
- dropCachedChat(localCache, chatId);
33630
33960
  closeMessageBatchRef.current?.(chatId);
33631
33961
  if (localForRender.has(chatId)) {
33632
33962
  if (!localChanged)
@@ -33658,6 +33988,7 @@ function createChatDelete({
33658
33988
  keepSelectedDeletedChatIdsRef.current.delete(chatId);
33659
33989
  }
33660
33990
  }
33991
+ dropCachedChats(localCache, ids);
33661
33992
  clearDeletedChatState(ids);
33662
33993
  renderVisibleChats();
33663
33994
  setSelectedChat((current) => current && ids.includes(current) && !options?.keepSelected ? null : current);
@@ -33715,9 +34046,7 @@ function createChatDelete({
33715
34046
  const dropChat = (chatId) => {
33716
34047
  if (!chatId)
33717
34048
  return;
33718
- clearDeletedChatState([chatId]);
33719
34049
  listActionsRef.current?.removeOwner?.(chatId, { warm: false });
33720
- setSelectedChat((current) => current === chatId ? null : current);
33721
34050
  };
33722
34051
  const dropLeftChat = (chatId) => {
33723
34052
  if (!chatId)
@@ -35138,8 +35467,6 @@ async function setOwnChatNotificationMode(cloud, identity, entryId, entry, value
35138
35467
  if (!current?.current || current.retirement)
35139
35468
  throw makeChatUnavailableError();
35140
35469
  const manifest = current.current.manifest;
35141
- if (manifest.lineage !== "group")
35142
- throw new Error("group chat required");
35143
35470
  const requestedMode = cleanText(value);
35144
35471
  const notificationMode = normalizeChatNotificationMode(requestedMode, manifest);
35145
35472
  if (notificationMode !== requestedMode) {
@@ -35199,20 +35526,20 @@ function createChatSessionNotifications({
35199
35526
  throw makeChatUnavailableError();
35200
35527
  }
35201
35528
  };
35202
- const requireOwnerChat = (chatId, operation, { group = false } = {}) => {
35529
+ const requireOwnerChat = (chatId, operation) => {
35203
35530
  assertCurrent(operation);
35204
35531
  const chat = getOwnerChat(chatId);
35205
- if (operation.identity.chatBanned || canWriteChat(chatId) !== true || group && chat?.lineage !== "group" || !chat.ownEntry || chat.ownEntry.retirement || !chat.entryId) {
35532
+ if (operation.identity.chatBanned || canWriteChat(chatId) !== true || !chat?.ownEntry || chat.ownEntry.retirement || !chat.entryId) {
35206
35533
  throw makeChatUnavailableError();
35207
35534
  }
35208
35535
  return chat;
35209
35536
  };
35210
35537
  const setChatNotificationMode = async (chatId, mode) => {
35211
35538
  const operation = operationContext();
35212
- requireOwnerChat(chatId, operation, { group: true });
35539
+ requireOwnerChat(chatId, operation);
35213
35540
  const releaseMutation = await operation.mutationGate.acquireExclusive(chatId);
35214
35541
  try {
35215
- const chat = requireOwnerChat(chatId, operation, { group: true });
35542
+ const chat = requireOwnerChat(chatId, operation);
35216
35543
  const committed = await writeMode(cloud, operation.identity, chat.entryId, chat.ownEntry, mode);
35217
35544
  assertCurrent(operation);
35218
35545
  const projected = projectOwnChatEntry(committed, chat.entryId, operation.identity.chatPK, chat.ts);
@@ -35605,7 +35932,7 @@ function createChatSession({
35605
35932
  const owner = getOwnerChat(chatId);
35606
35933
  if (owner)
35607
35934
  return !isChatMembershipRemoved(owner);
35608
- return pendingOwner?.getPendingLaunch()?.id === chatId;
35935
+ return pendingOwner?.isPendingChat(chatId) === true;
35609
35936
  };
35610
35937
  const requireOnline = () => {
35611
35938
  if (!online)
@@ -35620,10 +35947,6 @@ function createChatSession({
35620
35947
  if (chatId && !isMutableChat(chatId))
35621
35948
  throw makeChatUnavailableError();
35622
35949
  };
35623
- const gateChatMutation = (fn) => (chatId, ...rest) => {
35624
- requireMutableChat(chatId);
35625
- return fn(chatId, ...rest);
35626
- };
35627
35950
  const gateAdmittedChatMutation = (fn) => (chatId, ...rest) => {
35628
35951
  requireMutableChat(chatId);
35629
35952
  return Promise.resolve(pendingOwner.assertChatAdmission(chatId)).then(() => fn(chatId, ...rest));
@@ -35825,7 +36148,8 @@ function createChatSession({
35825
36148
  getPeerProfile,
35826
36149
  resolvePeerProfile,
35827
36150
  setLocalByChat,
35828
- getLocalByChat: () => localByChatRef.current
36151
+ getLocalByChat: () => localByChatRef.current,
36152
+ retireMaterializedChat: (chatId) => actionOwner.deleteActions.beginRetirement(chatId)
35829
36153
  });
35830
36154
  launchOwner = createChatSessionLaunches({
35831
36155
  cloud,
@@ -35865,29 +36189,79 @@ function createChatSession({
35865
36189
  const openDirectChat = (...args) => launchOwner.openDirectChat(...args);
35866
36190
  const openNotesChat = (...args) => launchOwner.openNotesChat(...args);
35867
36191
  const openNewChat = (...args) => pendingOwner.openNewChat(...args);
35868
- const directAdmissionForPeer2 = (profile) => directAdmissionForPeer(chatPrivateKey, chatPK, profile, chatAdmission);
35869
- const canStartDirectChat2 = (profile) => canStartDirectChat(chatPrivateKey, chatPK, profile, chatAdmission);
36192
+ const { directAdmissionForPeer: directAdmissionForPeer2, canStartDirectChat } = pendingOwner;
35870
36193
  const canInviteToGroup2 = (profile) => canInviteToGroup(profile, chatAdmission);
35871
36194
  const createGroupChat2 = (...args) => launchOwner.createGroupChat(...args);
35872
36195
  const selectLocalChat = (...args) => launchOwner.selectLocalChat(...args);
35873
- const addChatMembers = async (chatId, profiles2) => {
36196
+ const mutateMaterializedChat = (chatId, mutate, options) => {
35874
36197
  requireMutableChat(chatId);
35875
- await pendingOwner.assertChatAdmission(chatId);
35876
- return membershipOwner.addChatMembers(chatId, profiles2);
36198
+ return pendingOwner.runChatMutation(chatId, async (id, context) => {
36199
+ await pendingOwner.assertChatAdmission(id);
36200
+ return mutate(id, context);
36201
+ }, options);
35877
36202
  };
35878
- const kickChatMember = async (chatId, memberChatPK) => {
35879
- requireMutableChat(chatId);
35880
- await pendingOwner.assertChatAdmission(chatId);
35881
- return membershipOwner.kickChatMember(chatId, memberChatPK);
36203
+ const addChatMembers = (chatId, profiles2) => {
36204
+ const additions = Array.isArray(profiles2) ? profiles2 : [profiles2];
36205
+ additions.forEach(membershipOwner.profileMember);
36206
+ return mutateMaterializedChat(chatId, (id, context) => context.created ? { entry: getOwnerChat(id).ownEntry } : membershipOwner.addChatMembers(id, additions), {
36207
+ initialProfiles: (current) => {
36208
+ const next = new Map(current.map((profile) => [profile.chatPK, profile]));
36209
+ for (const profile of additions)
36210
+ next.set(profile.chatPK, profile);
36211
+ return next.size === current.length ? current : [...next.values()];
36212
+ }
36213
+ });
36214
+ };
36215
+ const kickChatMember = (chatId, memberChatPK) => {
36216
+ const target = cleanText(memberChatPK);
36217
+ if (!target)
36218
+ throw new Error("chat member required");
36219
+ if (target === chatPK && pendingOwner.isPendingChat(chatId)) {
36220
+ return deleteChat(chatId);
36221
+ }
36222
+ return mutateMaterializedChat(chatId, (id, context) => context.created ? { entry: getOwnerChat(id).ownEntry } : membershipOwner.kickChatMember(id, target), {
36223
+ initialProfiles: (current) => current.some((profile) => profile.chatPK === target) ? current.filter((profile) => profile.chatPK !== target) : current
36224
+ });
35882
36225
  };
35883
36226
  const leaveChat = (chatId) => {
36227
+ if (pendingOwner.isPendingChat(chatId)) {
36228
+ return deleteChat(chatId);
36229
+ }
35884
36230
  requireMutableChat(chatId);
35885
36231
  return membershipOwner.leaveChat(chatId);
35886
36232
  };
35887
36233
  const leaveOwnedChat = (...args) => membershipOwner.leaveOwnedChat(...args);
35888
- const updateChatSettings2 = gateAdmittedChatMutation((...args) => settingsOwner.updateChatSettings(...args));
35889
- const updateChatAvatar = gateAdmittedChatMutation((...args) => settingsOwner.updateChatAvatar(...args));
35890
- const setChatNotificationMode = gateAdmittedChatMutation((...args) => notificationsOwner.setChatNotificationMode(...args));
36234
+ const sharedSettingsChat = (chatId) => {
36235
+ const chat = pendingOwner.getActionChat(chatId);
36236
+ if (chat?.lineage === "self")
36237
+ throw new Error("self chat identity follows your profile");
36238
+ return chat;
36239
+ };
36240
+ const updateChatSettings2 = async (chatId, settings) => {
36241
+ const chat = sharedSettingsChat(chatId);
36242
+ const requested = normalizeChatSettingsInput({ ...chat?.settings, ...settings });
36243
+ const changes = ["title", "avatarRef", "retention"].filter((key) => Object.prototype.hasOwnProperty.call(settings, key));
36244
+ const patch = Object.fromEntries(changes.map((key) => [key, requested[key]]));
36245
+ if (chat && !pendingOwner.hasPendingChatOperation(chatId) && changes.every((key) => chat.settings?.[key] === requested[key])) {
36246
+ return Promise.resolve({ unchanged: true });
36247
+ }
36248
+ return mutateMaterializedChat(chatId, (id) => settingsOwner.updateChatSettings(id, patch));
36249
+ };
36250
+ const updateChatAvatar = async (chatId, data) => {
36251
+ assertChatAvatarBytes(data);
36252
+ const chat = sharedSettingsChat(chatId);
36253
+ if (chat?.lineage !== "group")
36254
+ throw new Error("group chat required");
36255
+ return mutateMaterializedChat(chatId, (id) => settingsOwner.updateChatAvatar(id, data));
36256
+ };
36257
+ const setChatNotificationMode = async (chatId, mode) => {
36258
+ const chat = pendingOwner.getActionChat(chatId);
36259
+ if (chat && normalizeChatNotificationMode(mode, chat) !== mode)
36260
+ throw new Error("all notifications are unavailable in large groups");
36261
+ if (chat && !pendingOwner.hasPendingChatOperation(chatId) && (chat.notificationMode || defaultChatNotificationMode(chat)) === mode)
36262
+ return mode;
36263
+ return mutateMaterializedChat(chatId, (id) => notificationsOwner.setChatNotificationMode(id, mode));
36264
+ };
35891
36265
  const updateMessage = async (chatId, original, changes) => {
35892
36266
  if (chatBanned)
35893
36267
  throw makeChatUnavailableError();
@@ -36041,7 +36415,27 @@ function createChatSession({
36041
36415
  };
36042
36416
  const dropChat = (...args) => actionOwner.deleteActions.dropChat(...args);
36043
36417
  const dropUnavailableChat = (...args) => actionOwner.deleteActions.dropUnavailableChat(...args);
36044
- const deleteChat = gateOnlineMutation((...args) => actionOwner.deleteActions.deleteChat(...args));
36418
+ const deleteChat = async (value, options) => {
36419
+ const inputs = Array.isArray(value) ? value : [value];
36420
+ const durable = [];
36421
+ let discarded = 0;
36422
+ for (const input of inputs) {
36423
+ const id = cleanText(typeof input === "string" ? input : input?.chatId || input?.id);
36424
+ if (pendingOwner.hasPendingChatOperation(id) || !getOwnerChat(id) && pendingOwner.isPendingChat(id)) {
36425
+ const result = await pendingOwner.discardChat(id);
36426
+ if (result.chat)
36427
+ durable.push(result.chat);
36428
+ else if (result.discarded)
36429
+ discarded += 1;
36430
+ } else
36431
+ durable.push(input);
36432
+ }
36433
+ if (!durable.length)
36434
+ return Array.isArray(value) ? discarded : discarded > 0;
36435
+ requireOnline();
36436
+ const deleted = await actionOwner.deleteActions.deleteChat(Array.isArray(value) ? durable : durable[0], options);
36437
+ return Array.isArray(value) ? discarded + Number(deleted) : deleted;
36438
+ };
36045
36439
  const purgePeerChats = async (affected, operation) => {
36046
36440
  const identity = operation.identity;
36047
36441
  for (const chat of affected) {
@@ -36056,6 +36450,8 @@ function createChatSession({
36056
36450
  await leaveOwnedChat(chat, { optimistic: false });
36057
36451
  assertBulkOperationCurrent(operation);
36058
36452
  await cloud.user.chats.unlink(identity.uid, chat.entryId);
36453
+ assertBulkOperationCurrent(operation);
36454
+ operation.deleteActions.commitRetirement(chat.id);
36059
36455
  } else {
36060
36456
  await operation.deleteActions.deleteChat(chat, { cleanup: false });
36061
36457
  }
@@ -36070,14 +36466,30 @@ function createChatSession({
36070
36466
  const { uid: operationUid, chatPK: operationChatPK, chatPrivateKey: operationChatPrivateKey } = operation.identity;
36071
36467
  if (!operationUid || !operationChatPK || !operationChatPrivateKey)
36072
36468
  return retiredPending;
36073
- const allChats = await loadOwnerChats(cloud, operationUid, operationChatPK, operationChatPrivateKey);
36074
- assertBulkOperationCurrent(operation);
36075
- const affected = allChats.filter((chat) => chat?.members?.some((member) => member.chatPK === peerChatPK));
36076
- await purgePeerChats(affected, operation);
36077
- assertBulkOperationCurrent(operation);
36078
- await options.onDeliveryRevoked?.();
36079
- assertBulkOperationCurrent(operation);
36080
- return retiredPending || affected.length > 0;
36469
+ const visible = chatsRef.current.filter((chat) => chat?.members?.some((member) => member.chatPK === peerChatPK) && !operation.deleteActions.isChatPendingDelete(chat.id));
36470
+ for (const chat of visible)
36471
+ operation.deleteActions.beginRetirement(chat.id);
36472
+ try {
36473
+ const allChats = await loadOwnerChats(cloud, operationUid, operationChatPK, operationChatPrivateKey);
36474
+ assertBulkOperationCurrent(operation);
36475
+ const affected = allChats.filter((chat) => chat?.members?.some((member) => member.chatPK === peerChatPK));
36476
+ await purgePeerChats(affected, operation);
36477
+ assertBulkOperationCurrent(operation);
36478
+ for (const chat of visible)
36479
+ operation.deleteActions.commitRetirement(chat.id);
36480
+ await options.onDeliveryRevoked?.();
36481
+ assertBulkOperationCurrent(operation);
36482
+ return retiredPending || affected.length > 0;
36483
+ } catch (error) {
36484
+ if (isBulkOperationCurrent(operation)) {
36485
+ for (const chat of visible) {
36486
+ if (operation.deleteActions.isChatPendingDelete(chat.id)) {
36487
+ operation.deleteActions.rollbackRetirement(chat.id);
36488
+ }
36489
+ }
36490
+ }
36491
+ throw error;
36492
+ }
36081
36493
  };
36082
36494
  const retireBlockedChats = async (blockedUids) => {
36083
36495
  const operation = bulkOperation(blockedUids);
@@ -36146,14 +36558,14 @@ function createChatSession({
36146
36558
  const sendPendingChatMessage = (...args) => pendingOwner.sendPendingChatMessage(...args);
36147
36559
  const sendChatMessage = sendMessage;
36148
36560
  const sendDirectAttachment = gateOnlineMutation((...args) => pendingOwner.sendDirectAttachment(...args));
36149
- const sendChatAttachment = gateChatMutation((...args) => pendingOwner.sendChatAttachment(...args));
36561
+ const sendChatAttachment = gateOnlineMutation((...args) => pendingOwner.sendChatAttachment(...args));
36150
36562
  const retryMessage = async (...args) => {
36151
36563
  const result = await pendingOwner.retryMessage(...args);
36152
36564
  if (online)
36153
36565
  drainMembershipOutbox();
36154
36566
  return result;
36155
36567
  };
36156
- const sendAttachment = gateChatMutation((...args) => pendingOwner.sendChatAttachment(...args));
36568
+ const sendAttachment = gateOnlineMutation((...args) => pendingOwner.sendChatAttachment(...args));
36157
36569
  const sendChatImage = (chatId, image, options = {}) => sendChatAttachment(chatId, { ...image, type: image?.type || "img" }, options);
36158
36570
  const sendAttachmentMany = gateOnlineMutation((...args) => pendingOwner.sendAttachmentMany(...args));
36159
36571
  const sendImageMany = (targets, image, options = {}) => sendAttachmentMany(targets, { ...image, type: image?.type || "img" }, options);
@@ -36211,7 +36623,11 @@ function createChatSession({
36211
36623
  });
36212
36624
  return state;
36213
36625
  };
36214
- const setChatTtl = gateAdmittedChatMutation((...args) => settingsOwner.setChatTtl(...args));
36626
+ const setChatTtl = async (chatId, retention) => {
36627
+ const next = cleanChatRetention(retention);
36628
+ await updateChatSettings2(chatId, { retention: next });
36629
+ return next;
36630
+ };
36215
36631
  const makeMessagePermanent = gateAdmittedChatMutation((...args) => actionOwner.saveActions.makeMessagePermanent(...args));
36216
36632
  const makeMessageTemporary = gateAdmittedChatMutation((...args) => actionOwner.saveActions.makeMessageTemporary(...args));
36217
36633
  const readMessageFile = (...args) => actionOwner.saveActions.readMessageFile(...args);
@@ -36314,6 +36730,17 @@ function createChatSession({
36314
36730
  chatsRef,
36315
36731
  reconcileMessageBatches: (...args) => messageBatchOwner.reconcileChats(...args),
36316
36732
  warmChats: (...args) => messageBatchOwner.warm(...args),
36733
+ onOwnerRemoved: (chatIds) => {
36734
+ for (const chatId of chatIds)
36735
+ pendingOwner.dropChat(chatId);
36736
+ actionOwner.deleteActions.clearDeletedChatState(chatIds);
36737
+ for (const chatId of chatIds) {
36738
+ actionOwner.liveActions.closeChatActivity(chatId);
36739
+ historyOwner.clear(chatId);
36740
+ messageBatchOwner.routeMemory.dropChat(chatId);
36741
+ routeMemoryScopes.delete(chatId);
36742
+ }
36743
+ },
36317
36744
  onTransition: stageCommittedMembershipMessage,
36318
36745
  onRemoved: (removedChatId, _epochVersion, retiredEntry) => {
36319
36746
  const applied = chatListOwner.applyMembershipRemoval(removedChatId, retiredEntry);
@@ -36370,7 +36797,7 @@ function createChatSession({
36370
36797
  openNotesChat,
36371
36798
  openNewChat,
36372
36799
  directAdmissionForPeer: directAdmissionForPeer2,
36373
- canStartDirectChat: canStartDirectChat2,
36800
+ canStartDirectChat,
36374
36801
  canInviteToGroup: canInviteToGroup2,
36375
36802
  createGroupChat: createGroupChat2,
36376
36803
  addChatMembers,
@@ -36664,6 +37091,10 @@ var init_session3 = __esm(() => {
36664
37091
  init_sessionsources();
36665
37092
  init_entry();
36666
37093
  init_admission();
37094
+ init_avatar2();
37095
+ init_settings2();
37096
+ init_ttl();
37097
+ init_notifications();
36667
37098
  init_write();
36668
37099
  DEFAULT_READ_WRITE_INTERVAL_MS = CHAT_READ_WRITE_INTERVAL_MS;
36669
37100
  EMPTY_LOCAL_MESSAGES = Object.freeze([]);
@@ -36846,7 +37277,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
36846
37277
  let expiryTimer = null;
36847
37278
  let command = null;
36848
37279
  let authenticating = null;
36849
- let snapshot = Object.freeze({ visibility: null, ready: false, pending: false, error: null, ...UNKNOWN_PRESENCE });
37280
+ let snapshot = Object.freeze({ visibility: null, ready: false, pending: false, pendingVisibility: null, error: null, ...UNKNOWN_PRESENCE });
36850
37281
  function topic() {
36851
37282
  return sources.uid ? derivePresenceTopic(realm2, sources.uid) : null;
36852
37283
  }
@@ -36854,7 +37285,13 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
36854
37285
  return Boolean(channel && challenge && policy && sources.enabled && !closed);
36855
37286
  }
36856
37287
  function publish(patch = {}) {
36857
- const next = { ...snapshot, ...patch, ready: isReady() };
37288
+ const next = {
37289
+ ...snapshot,
37290
+ ...patch,
37291
+ ready: isReady(),
37292
+ pending: Boolean(command),
37293
+ pendingVisibility: command?.policy.visibility ?? null
37294
+ };
36858
37295
  if (Object.keys(next).every((key) => Object.is(next[key], snapshot[key])))
36859
37296
  return;
36860
37297
  snapshot = Object.freeze(next);
@@ -36910,7 +37347,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
36910
37347
  const pending = command;
36911
37348
  command = null;
36912
37349
  clearTimeout(pending.timer);
36913
- publish({ pending: false, error });
37350
+ publish({ error });
36914
37351
  if (error)
36915
37352
  pending.reject(error);
36916
37353
  else
@@ -37150,12 +37587,14 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
37150
37587
  listener();
37151
37588
  }
37152
37589
  function update(next) {
37153
- const identityChanged = sources.uid !== next.uid || sources.identity?.publicKey !== next.identity?.publicKey;
37590
+ const accountChanged = sources.uid !== next.uid;
37591
+ const identityChanged = accountChanged || sources.identity?.publicKey !== next.identity?.publicKey;
37154
37592
  if (identityChanged) {
37155
37593
  stop();
37156
37594
  policy = null;
37157
37595
  records.clear();
37158
- peers.clear();
37596
+ if (accountChanged)
37597
+ peers.clear();
37159
37598
  publish({ visibility: null, error: null, ...UNKNOWN_PRESENCE });
37160
37599
  }
37161
37600
  sources = next;
@@ -37185,7 +37624,7 @@ function createPresence({ cloud, diag = null, now = Date.now } = {}) {
37185
37624
  reject,
37186
37625
  timer: setTimeout(() => finishCommand(new Error("could not confirm activity setting; reconnect to check")), COMMAND_TIMEOUT_MS)
37187
37626
  };
37188
- publish({ pending: true, error: null });
37627
+ publish({ error: null });
37189
37628
  if (!channel.send(presenceShard(topic()), { type: "policy", expectedRevision: policy.revision, policy: nextPolicy })) {
37190
37629
  finishCommand(new Error("could not update activity while offline"));
37191
37630
  }
@@ -40971,6 +41410,7 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
40971
41410
  let profilesLoading = false;
40972
41411
  let hydratedCacheKey = "";
40973
41412
  let avatarHydrateKey = "";
41413
+ let avatarPublishSession = null;
40974
41414
  let profilesReady = false;
40975
41415
  let blockedPeersReady = false;
40976
41416
  let discoveredPeerUids = new Set;
@@ -41381,12 +41821,21 @@ function createPeerList({ peerApi, presence, diag, onMissingPeers }) {
41381
41821
  const session = lifecycle;
41382
41822
  const startedAt = Date.now();
41383
41823
  const requested = walletPKs.length + chatPeerPKs.length;
41384
- hydrateCachedAvatars(walletPKs, chatPeerPKs).then((changed) => {
41824
+ hydrateCachedAvatars(walletPKs, chatPeerPKs, () => {
41825
+ if (!isCurrent(session) || avatarPublishSession === session)
41826
+ return;
41827
+ avatarPublishSession = session;
41828
+ queueMicrotask(() => {
41829
+ if (avatarPublishSession !== session)
41830
+ return;
41831
+ avatarPublishSession = null;
41832
+ if (isCurrent(session))
41833
+ rebuild({ base: true, persist: false });
41834
+ });
41835
+ }).then((changed) => {
41385
41836
  if (!isCurrent(session))
41386
41837
  return;
41387
41838
  markDiag(diag, "peers.avatars.hydrate", { requested, changed, elapsedMs: Date.now() - startedAt });
41388
- if (changed)
41389
- rebuild({ base: true });
41390
41839
  }).catch((error) => {
41391
41840
  if (!isCurrent(session))
41392
41841
  return;
@@ -41990,7 +42439,7 @@ function createPeersApi({ cloud, network, avatarCache = null }) {
41990
42439
  function uniqueLookupKeys(keys) {
41991
42440
  return uniqueValues(keys);
41992
42441
  }
41993
- async function hydrateCachedAvatars(walletPKs, chatPKs) {
42442
+ async function hydrateCachedAvatars(walletPKs, chatPKs, onHydrated) {
41994
42443
  if (typeof avatarCache?.read !== "function")
41995
42444
  return false;
41996
42445
  const profiles2 = new Map;
@@ -42009,21 +42458,18 @@ function createPeersApi({ cloud, network, avatarCache = null }) {
42009
42458
  queue(key, "chat");
42010
42459
  if (!profiles2.size)
42011
42460
  return false;
42012
- const cached = await Promise.all(Array.from(profiles2.values()).map(async ({ uid, version }) => ({
42013
- avatar: await readCachedAvatar(uid, version),
42014
- uid,
42015
- version
42016
- })));
42017
42461
  let changed = false;
42018
- for (const entry of cached) {
42019
- if (!entry.avatar)
42020
- continue;
42021
- const current = profileCache.get(entry.uid);
42022
- if (!current || current.avatarVersion !== entry.version || current.avatar === entry.avatar)
42023
- continue;
42024
- storeProfile({ ...current, avatar: entry.avatar });
42462
+ await Promise.all(Array.from(profiles2.values()).map(async ({ uid, version }) => {
42463
+ const avatar = await readCachedAvatar(uid, version);
42464
+ if (!avatar)
42465
+ return;
42466
+ const current = profileCache.get(uid);
42467
+ if (!current || current.avatarVersion !== version || current.avatar === avatar)
42468
+ return;
42469
+ storeProfile({ ...current, avatar });
42025
42470
  changed = true;
42026
- }
42471
+ onHydrated?.();
42472
+ }));
42027
42473
  return changed;
42028
42474
  }
42029
42475
  function needsAvatarResolve(profile) {
@@ -44693,6 +45139,7 @@ function createPayment({
44693
45139
  throw new Error("bitcoin payment must be reviewed before sending");
44694
45140
  }
44695
45141
  const payment = paymentInput?.state === PAYMENT_STATE_PREPARED ? normalizePreparedPayment(paymentInput, wallet, network) : await preparePayment(paymentInput);
45142
+ assertReady();
44696
45143
  const startedAt = Date.now();
44697
45144
  const diagKind = payment.kind;
44698
45145
  markDiag(diag, "wallet.payment.start", { kind: diagKind });
@@ -53577,20 +54024,7 @@ function sameMessageList(a, b) {
53577
54024
  for (let i = 0;i < a.length; i += 1) {
53578
54025
  const left = a[i];
53579
54026
  const right = b[i];
53580
- if (getMessageKey(left) !== getMessageKey(right) || getMessageOrderMs(left) !== getMessageOrderMs(right) || hasMessageTtl(left) !== hasMessageTtl(right) || timestampKey(left?.ttl ?? null) !== timestampKey(right?.ttl ?? null) || !sameMessageReactions(left, right)) {
53581
- return false;
53582
- }
53583
- }
53584
- return true;
53585
- }
53586
- function sameMessageReactions(a, b) {
53587
- const left = getMsgReactions(a);
53588
- const right = getMsgReactions(b);
53589
- if (left.length !== right.length) {
53590
- return false;
53591
- }
53592
- for (let index2 = 0;index2 < left.length; index2 += 1) {
53593
- if (left[index2].user !== right[index2]?.user || left[index2].emoji !== right[index2]?.emoji) {
54027
+ 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)) {
53594
54028
  return false;
53595
54029
  }
53596
54030
  }
@@ -54009,7 +54443,7 @@ function cacheMessageSignature(message) {
54009
54443
  if (!message)
54010
54444
  return "";
54011
54445
  const hasTtl = hasMessageTtl(message);
54012
- const reactions = getMsgReactions(message).map((reaction) => `${reaction.user}:${reaction.emoji}`).join(",");
54446
+ const reactions = getMsgReactions(message).map((reaction) => `${reaction.user}:${reaction.emoji}:${reaction.ts ?? ""}`).join(",");
54013
54447
  return [
54014
54448
  cacheKeySignature(messageKeys(message)),
54015
54449
  getMessageOrderMs(message) || 0,
@@ -60625,7 +61059,7 @@ var package_default;
60625
61059
  var init_package2 = __esm(() => {
60626
61060
  package_default = {
60627
61061
  name: "veyl",
60628
- version: "0.70.0",
61062
+ version: "0.71.1",
60629
61063
  packageManager: "bun@1.4.0",
60630
61064
  private: true,
60631
61065
  license: "UNLICENSED",
@@ -79010,7 +79444,7 @@ function ensureBytes(title, hex3, expectedLength) {
79010
79444
  throw new Error(title + " of length " + expectedLength + " expected, got " + len);
79011
79445
  return res;
79012
79446
  }
79013
- function equalBytes2(a, b) {
79447
+ function equalBytes3(a, b) {
79014
79448
  if (a.length !== b.length)
79015
79449
  return false;
79016
79450
  let diff = 0;
@@ -109821,7 +110255,7 @@ var init_legacy = __esm(() => {
109821
110255
  });
109822
110256
 
109823
110257
  // ../../node_modules/.bun/micro-packed@0.7.3/node_modules/micro-packed/lib/esm/index.js
109824
- function equalBytes3(a, b) {
110258
+ function equalBytes4(a, b) {
109825
110259
  if (a.length !== b.length)
109826
110260
  return false;
109827
110261
  for (let i = 0;i < a.length; i++)
@@ -109996,7 +110430,7 @@ class _Reader {
109996
110430
  const leftBytes = this.data.length - idx;
109997
110431
  if (leftBytes < needle.length)
109998
110432
  return;
109999
- if (equalBytes3(needle, this.data.subarray(idx, idx + needle.length)))
110433
+ if (equalBytes4(needle, this.data.subarray(idx, idx + needle.length)))
110000
110434
  return idx;
110001
110435
  }
110002
110436
  return;
@@ -110348,7 +110782,7 @@ function magic(inner, constant, check = true) {
110348
110782
  encodeStream: (w, _value) => inner.encodeStream(w, constant),
110349
110783
  decodeStream: (r) => {
110350
110784
  const value = inner.decodeStream(r);
110351
- if (check && typeof value !== "object" && value !== constant || isBytes6(constant) && !equalBytes3(constant, value)) {
110785
+ if (check && typeof value !== "object" && value !== constant || isBytes6(constant) && !equalBytes4(constant, value)) {
110352
110786
  throw r.err(`magic: invalid value: ${value} !== ${constant}`);
110353
110787
  }
110354
110788
  return;
@@ -110455,7 +110889,7 @@ function array(len, inner) {
110455
110889
  if (len.length > _w.pos - startPos)
110456
110890
  return;
110457
110891
  const data = _w.finish(false).subarray(startPos, _w.pos);
110458
- if (equalBytes3(data.subarray(0, len.length), len))
110892
+ if (equalBytes4(data.subarray(0, len.length), len))
110459
110893
  throw _w.err(`array: inner element encoding same as separator. elm=${elm} data=${data}`);
110460
110894
  }
110461
110895
  });
@@ -110475,7 +110909,7 @@ function array(len, inner) {
110475
110909
  }
110476
110910
  } else if (isBytes6(len)) {
110477
110911
  for (let i = 0;; i++) {
110478
- if (equalBytes3(r.bytes(len.length, true), len)) {
110912
+ if (equalBytes4(r.bytes(len.length, true), len)) {
110479
110913
  r.bytes(len.length);
110480
110914
  break;
110481
110915
  }
@@ -110688,7 +111122,7 @@ var EMPTY2, NULL, createView4 = (arr) => new DataView(arr.buffer, arr.byteOffset
110688
111122
  decodeStream: (r) => {
110689
111123
  let hasFlag = r.leftBytes >= flagValue.length;
110690
111124
  if (hasFlag) {
110691
- hasFlag = equalBytes3(r.bytes(flagValue.length, true), flagValue);
111125
+ hasFlag = equalBytes4(r.bytes(flagValue.length, true), flagValue);
110692
111126
  if (hasFlag)
110693
111127
  r.bytes(flagValue.length);
110694
111128
  }
@@ -110706,7 +111140,7 @@ var init_esm2 = __esm(() => {
110706
111140
  EMPTY2 = /* @__PURE__ */ new Uint8Array;
110707
111141
  NULL = /* @__PURE__ */ new Uint8Array([0]);
110708
111142
  utils2 = {
110709
- equalBytes: equalBytes3,
111143
+ equalBytes: equalBytes4,
110710
111144
  isBytes: isBytes6,
110711
111145
  isCoder,
110712
111146
  checkBounds,
@@ -110948,7 +111382,7 @@ function compareBytes(a, b) {
110948
111382
  return Math.sign(a[i] - b[i]);
110949
111383
  return Math.sign(a.length - b.length);
110950
111384
  }
110951
- 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;
111385
+ 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;
110952
111386
  var init_utils6 = __esm(() => {
110953
111387
  init_secp256k1();
110954
111388
  init_legacy();
@@ -110958,7 +111392,7 @@ var init_utils6 = __esm(() => {
110958
111392
  CURVE_ORDER = secp256k1.CURVE.n;
110959
111393
  isBytes7 = utils2.isBytes;
110960
111394
  concatBytes7 = utils2.concatBytes;
110961
- equalBytes4 = utils2.equalBytes;
111395
+ equalBytes5 = utils2.equalBytes;
110962
111396
  randomPrivateKeyBytes = schnorr.utils.randomPrivateKey;
110963
111397
  pubSchnorr = schnorr.getPublicKey;
110964
111398
  pubECDSA = secp256k1.getPublicKey;
@@ -111477,7 +111911,7 @@ function mergeKeyMap(psbtEnum, val, cur, allowedFields, allowUnknown) {
111477
111911
  } else if (typeof res[k] === "string") {
111478
111912
  res[k] = vC.decode(hex3.decode(res[k]));
111479
111913
  } else if (cannotChange && k in val && cur && cur[k] !== undefined) {
111480
- if (!equalBytes4(vC.encode(val[k]), vC.encode(cur[k])))
111914
+ if (!equalBytes5(vC.encode(val[k]), vC.encode(cur[k])))
111481
111915
  throw new Error(`Cannot change signed field=${k}`);
111482
111916
  }
111483
111917
  }
@@ -111676,7 +112110,7 @@ function isValidPubkey(pub, type) {
111676
112110
  }
111677
112111
  }
111678
112112
  function checkWSH(s, witnessScript) {
111679
- if (!equalBytes4(s.hash, sha2562(witnessScript)))
112113
+ if (!equalBytes5(s.hash, sha2562(witnessScript)))
111680
112114
  throw new Error("checkScript: wsh wrong witnessScript hash");
111681
112115
  const w = OutScript.decode(witnessScript);
111682
112116
  if (w.type === "tr" || w.type === "tr_ns" || w.type === "tr_ms")
@@ -111690,7 +112124,7 @@ function checkScript(script, redeemScript, witnessScript) {
111690
112124
  if (s.type === "tr_ns" || s.type === "tr_ms" || s.type === "ms" || s.type == "pk")
111691
112125
  throw new Error(`checkScript: non-wrapped ${s.type}`);
111692
112126
  if (s.type === "sh" && redeemScript) {
111693
- if (!equalBytes4(s.hash, hash160(redeemScript)))
112127
+ if (!equalBytes5(s.hash, hash160(redeemScript)))
111694
112128
  throw new Error("checkScript: sh wrong redeemScript hash");
111695
112129
  const r = OutScript.decode(redeemScript);
111696
112130
  if (r.type === "tr" || r.type === "tr_ns" || r.type === "tr_ms")
@@ -111727,9 +112161,9 @@ function checkTaprootScript(script, internalPubKey, allowUnknownOutputs = false,
111727
112161
  const outms = out;
111728
112162
  if (!allowUnknownOutputs && outms.pubkeys) {
111729
112163
  for (const p of outms.pubkeys) {
111730
- if (equalBytes4(p, TAPROOT_UNSPENDABLE_KEY))
112164
+ if (equalBytes5(p, TAPROOT_UNSPENDABLE_KEY))
111731
112165
  throw new Error("Unspendable taproot key in leaf script");
111732
- if (equalBytes4(p, internalPubKey)) {
112166
+ if (equalBytes5(p, internalPubKey)) {
111733
112167
  throw new Error("Using P2TR with leaf script with same key as internal key is not supported");
111734
112168
  }
111735
112169
  }
@@ -111780,7 +112214,7 @@ function taprootHashTree(tree, internalPubKey, allowUnknownOutputs = false, cust
111780
112214
  tree = tree[0];
111781
112215
  if (!Array.isArray(tree)) {
111782
112216
  const { leafVersion: version, script: leafScript } = tree;
111783
- if (tree.tapLeafScript || tree.tapMerkleRoot && !equalBytes4(tree.tapMerkleRoot, EMPTY2))
112217
+ if (tree.tapLeafScript || tree.tapMerkleRoot && !equalBytes5(tree.tapMerkleRoot, EMPTY2))
111784
112218
  throw new Error("P2TR: tapRoot leafScript cannot have tree");
111785
112219
  const script = typeof leafScript === "string" ? hex3.decode(leafScript) : leafScript;
111786
112220
  if (!isBytes7(script))
@@ -112159,7 +112593,7 @@ function cloneDeep(obj) {
112159
112593
  throw new Error(`cloneDeep: unknown type=${obj} (${typeof obj})`);
112160
112594
  }
112161
112595
  function getTaprootKeys(privKey, pubKey, internalKey, merkleRoot = EMPTY2) {
112162
- if (equalBytes4(internalKey, pubKey)) {
112596
+ if (equalBytes5(internalKey, pubKey)) {
112163
112597
  privKey = taprootTweakPrivKey(privKey, merkleRoot);
112164
112598
  pubKey = pubSchnorr(privKey);
112165
112599
  }
@@ -112255,7 +112689,7 @@ function validateInput(i) {
112255
112689
  if (i.index > last)
112256
112690
  throw new Error(`validateInput: index(${i.index}) not in nonWitnessUtxo`);
112257
112691
  const prevOut = i.nonWitnessUtxo.outputs[i.index];
112258
- if (i.witnessUtxo && (!equalBytes4(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount))
112692
+ if (i.witnessUtxo && (!equalBytes5(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount))
112259
112693
  throw new Error("validateInput: witnessUtxo different from nonWitnessUtxo");
112260
112694
  if (i.txid) {
112261
112695
  const outputs = i.nonWitnessUtxo.outputs;
@@ -112811,7 +113245,7 @@ class Transaction {
112811
113245
  let s = privateKey;
112812
113246
  for (const i of path)
112813
113247
  s = s.deriveChild(i);
112814
- if (!equalBytes4(s.publicKey, pubKey))
113248
+ if (!equalBytes5(s.publicKey, pubKey))
112815
113249
  throw new Error("bip32Derivation: wrong pubKey");
112816
113250
  if (!s.privateKey)
112817
113251
  throw new Error("bip32Derivation: no privateKey");
@@ -112848,7 +113282,7 @@ class Transaction {
112848
113282
  if (input.tapInternalKey) {
112849
113283
  const { pubKey, privKey } = getTaprootKeys(privateKey, schnorrPub, input.tapInternalKey, merkleRoot);
112850
113284
  const [taprootPubKey, _] = taprootTweakPubkey(input.tapInternalKey, merkleRoot);
112851
- if (equalBytes4(taprootPubKey, pubKey)) {
113285
+ if (equalBytes5(taprootPubKey, pubKey)) {
112852
113286
  const hash = this.preimageWitnessV1(idx, prevOutScript, sighash, amount);
112853
113287
  const sig = concatBytes7(signSchnorr(hash, privKey, _auxRand), sighash !== SignatureHash.DEFAULT ? new Uint8Array([sighash]) : EMPTY2);
112854
113288
  this.updateInput(idx, { tapKeySig: sig }, true);
@@ -112862,7 +113296,7 @@ class Transaction {
112862
113296
  const scriptDecoded = Script.decode(script);
112863
113297
  const ver = _script[_script.length - 1];
112864
113298
  const hash = tapLeafHash(script, ver);
112865
- const pos = scriptDecoded.findIndex((i) => isBytes7(i) && equalBytes4(i, schnorrPub));
113299
+ const pos = scriptDecoded.findIndex((i) => isBytes7(i) && equalBytes5(i, schnorrPub));
112866
113300
  if (pos === -1)
112867
113301
  continue;
112868
113302
  const msg = this.preimageWitnessV1(idx, prevOutScript, sighash, amount, undefined, script, ver);
@@ -112879,7 +113313,7 @@ class Transaction {
112879
113313
  let hasPubkey = false;
112880
113314
  const pubKeyHash = hash160(pubKey);
112881
113315
  for (const i of Script.decode(inputType.lastScript)) {
112882
- if (isBytes7(i) && (equalBytes4(i, pubKey) || equalBytes4(i, pubKeyHash)))
113316
+ if (isBytes7(i) && (equalBytes5(i, pubKey) || equalBytes5(i, pubKeyHash)))
112883
113317
  hasPubkey = true;
112884
113318
  }
112885
113319
  if (!hasPubkey)
@@ -112929,14 +113363,14 @@ class Transaction {
112929
113363
  const ver = _script[_script.length - 1];
112930
113364
  const outScript = OutScript.decode(script);
112931
113365
  const hash = tapLeafHash(script, ver);
112932
- const scriptSig = input.tapScriptSig.filter((i) => equalBytes4(i[0].leafHash, hash));
113366
+ const scriptSig = input.tapScriptSig.filter((i) => equalBytes5(i[0].leafHash, hash));
112933
113367
  let signatures = [];
112934
113368
  if (outScript.type === "tr_ms") {
112935
113369
  const m = outScript.m;
112936
113370
  const pubkeys = outScript.pubkeys;
112937
113371
  let added = 0;
112938
113372
  for (const pub of pubkeys) {
112939
- const sigIdx = scriptSig.findIndex((i) => equalBytes4(i[0].pubKey, pub));
113373
+ const sigIdx = scriptSig.findIndex((i) => equalBytes5(i[0].pubKey, pub));
112940
113374
  if (added === m || sigIdx === -1) {
112941
113375
  signatures.push(EMPTY2);
112942
113376
  continue;
@@ -112948,7 +113382,7 @@ class Transaction {
112948
113382
  continue;
112949
113383
  } else if (outScript.type === "tr_ns") {
112950
113384
  for (const pub of outScript.pubkeys) {
112951
- const sigIdx = scriptSig.findIndex((i) => equalBytes4(i[0].pubKey, pub));
113385
+ const sigIdx = scriptSig.findIndex((i) => equalBytes5(i[0].pubKey, pub));
112952
113386
  if (sigIdx === -1)
112953
113387
  continue;
112954
113388
  signatures.push(scriptSig[sigIdx][1]);
@@ -112958,7 +113392,7 @@ class Transaction {
112958
113392
  } else if (outScript.type === "unknown" && this.opts.allowUnknownInputs) {
112959
113393
  const scriptDecoded = Script.decode(script);
112960
113394
  signatures = scriptSig.map(([{ pubKey }, signature]) => {
112961
- const pos = scriptDecoded.findIndex((i) => isBytes7(i) && equalBytes4(i, pubKey));
113395
+ const pos = scriptDecoded.findIndex((i) => isBytes7(i) && equalBytes5(i, pubKey));
112962
113396
  if (pos === -1)
112963
113397
  throw new Error("finalize/taproot: cannot find position of pubkey in script");
112964
113398
  return { signature, pos };
@@ -113006,7 +113440,7 @@ class Transaction {
113006
113440
  const pubkeys = inputType.last.pubkeys;
113007
113441
  let signatures = [];
113008
113442
  for (const pub of pubkeys) {
113009
- const sign3 = input.partialSig.find((s) => equalBytes4(pub, s[0]));
113443
+ const sign3 = input.partialSig.find((s) => equalBytes5(pub, s[0]));
113010
113444
  if (!sign3)
113011
113445
  continue;
113012
113446
  signatures.push(sign3[1]);
@@ -113080,7 +113514,7 @@ class Transaction {
113080
113514
  }
113081
113515
  const thisUnsigned = this.global.unsignedTx ? RawOldTx.encode(this.global.unsignedTx) : EMPTY2;
113082
113516
  const otherUnsigned = other.global.unsignedTx ? RawOldTx.encode(other.global.unsignedTx) : EMPTY2;
113083
- if (!equalBytes4(thisUnsigned, otherUnsigned))
113517
+ if (!equalBytes5(thisUnsigned, otherUnsigned))
113084
113518
  throw new Error(`Transaction/combine: different unsigned tx`);
113085
113519
  this.global = mergeKeyMap(PSBTGlobal, this.global, other.global, undefined, this.opts.allowUnknown);
113086
113520
  for (let i = 0;i < this.inputs.length; i++)
@@ -117913,7 +118347,7 @@ function validateCoopExitPayoutTransaction({ rawCoopExitTransaction, expectedCoo
117913
118347
  const output = tx.getOutput(i);
117914
118348
  if (!output?.script || output.amount === undefined)
117915
118349
  continue;
117916
- if (!equalBytes4(output.script, expectedScript))
118350
+ if (!equalBytes5(output.script, expectedScript))
117917
118351
  continue;
117918
118352
  if (output.amount >= BigInt(expectedPayoutAmountSats))
117919
118353
  return;
@@ -118331,7 +118765,7 @@ function createSenderSpendTx({ htlcTx, network, hash, hashLockDestinationPubkey,
118331
118765
  const entry = p2.tapLeafScript.find(([, scriptWithVer]) => Buffer.compare(scriptWithVer.slice(0, -1), sequenceLockScript) === 0);
118332
118766
  const controlBlockBytes = TaprootControlBlock.encode(entry[0]);
118333
118767
  const seqLeafEntry = p2.tapLeafScript.find(([_, leaf]) => {
118334
- return equalBytes2(leaf.subarray(0, leaf.length - 1), sequenceLockScript);
118768
+ return equalBytes3(leaf.subarray(0, leaf.length - 1), sequenceLockScript);
118335
118769
  });
118336
118770
  const sighash = senderSpendTx.preimageWitnessV1(0, [sequenceLockScript], SigHash.DEFAULT, [htlcAmount]);
118337
118771
  senderSpendTx.updateInput(0, {
@@ -118381,7 +118815,7 @@ function createReceiverSpendTx({ htlcTx, network, hash, hashLockDestinationPubke
118381
118815
  script: sequenceLockScript
118382
118816
  }]), network, true);
118383
118817
  const hashLeafEntry = p2.tapLeafScript.find(([_, leaf]) => {
118384
- return equalBytes2(leaf.subarray(0, leaf.length - 1), hashLockScript);
118818
+ return equalBytes3(leaf.subarray(0, leaf.length - 1), hashLockScript);
118385
118819
  });
118386
118820
  spendTx.updateInput(0, {
118387
118821
  witnessUtxo: {
@@ -121131,7 +121565,7 @@ async function withRetry(operation, options = {}) {
121131
121565
  function scopeTransferLeavesToReceiver(transfer, receiverIdentityPublicKey) {
121132
121566
  if (transfer.receivers.length <= 1)
121133
121567
  return transfer;
121134
- const ownReceiver = transfer.receivers.find((receiver) => equalBytes2(receiver.identityPublicKey, receiverIdentityPublicKey));
121568
+ const ownReceiver = transfer.receivers.find((receiver) => equalBytes3(receiver.identityPublicKey, receiverIdentityPublicKey));
121135
121569
  if (!ownReceiver?.id)
121136
121570
  throw new SparkValidationError("Cannot scope claim: wallet is not a receiver of this multi-receiver transfer");
121137
121571
  const ownLeaves = transfer.leaves.filter((leaf) => leaf.transferReceiverId === ownReceiver.id);
@@ -121146,7 +121580,7 @@ function isReceiverLegComplete(transfer, receiverIdentityPublicKey) {
121146
121580
  const wholeComplete = transfer.status === TransferStatus.TRANSFER_STATUS_COMPLETED;
121147
121581
  if (transfer.receivers.length <= 1)
121148
121582
  return wholeComplete;
121149
- const ownReceiver = transfer.receivers.find((receiver) => equalBytes2(receiver.identityPublicKey, receiverIdentityPublicKey));
121583
+ const ownReceiver = transfer.receivers.find((receiver) => equalBytes3(receiver.identityPublicKey, receiverIdentityPublicKey));
121150
121584
  if (!ownReceiver)
121151
121585
  return false;
121152
121586
  return ownReceiver.status === TransferReceiverStatus.TRANSFER_RECEIVER_STATUS_COMPLETED || wholeComplete;
@@ -121892,7 +122326,7 @@ function isReceiverTransferStreamEvent(event2) {
121892
122326
  function findOutputPayingScript(tx, script) {
121893
122327
  for (let i = 0;i < tx.outputsLength; i++) {
121894
122328
  const outputScript = tx.getOutput(i).script;
121895
- if (outputScript && equalBytes2(outputScript, script))
122329
+ if (outputScript && equalBytes3(outputScript, script))
121896
122330
  return i;
121897
122331
  }
121898
122332
  }
@@ -121923,7 +122357,7 @@ function resolveRecoverableOutput(leaf, nodes, network, recoveryTxid, outputInde
121923
122357
  const prevOut = tx.getOutput(outputIndex);
121924
122358
  if (prevOut.amount === undefined)
121925
122359
  continue;
121926
- if (!prevOut.script || !equalBytes2(prevOut.script, leafScript))
122360
+ if (!prevOut.script || !equalBytes3(prevOut.script, leafScript))
121927
122361
  continue;
121928
122362
  return {
121929
122363
  txid,
@@ -123212,9 +123646,9 @@ var import_nice_grpc_common, import_light_bolt11_decoder, SparkFrostBase = class
123212
123646
  }
123213
123647
  signTransactionIndex(tx, index2, publicKey) {
123214
123648
  let privateKey;
123215
- if (equalBytes2(publicKey, this.identityKey?.publicKey ?? new Uint8Array))
123649
+ if (equalBytes3(publicKey, this.identityKey?.publicKey ?? new Uint8Array))
123216
123650
  privateKey = this.identityKey?.privateKey;
123217
- else if (equalBytes2(publicKey, this.depositKey?.publicKey ?? new Uint8Array))
123651
+ else if (equalBytes3(publicKey, this.depositKey?.publicKey ?? new Uint8Array))
123218
123652
  privateKey = this.depositKey?.privateKey;
123219
123653
  if (!privateKey)
123220
123654
  throw new SparkValidationError("Private key not found for public key", {
@@ -124055,7 +124489,7 @@ var import_nice_grpc_common, import_light_bolt11_decoder, SparkFrostBase = class
124055
124489
  return response.transfer;
124056
124490
  }
124057
124491
  compareTransfers(transfer1, transfer2) {
124058
- 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;
124492
+ 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;
124059
124493
  }
124060
124494
  }, TransferService, CoopExitService, DENOMINATIONS, Swap = class {
124061
124495
  inLeaves;
@@ -124579,7 +125013,7 @@ var import_nice_grpc_common, import_light_bolt11_decoder, SparkFrostBase = class
124579
125013
  }
124580
125014
  }
124581
125015
  async handleTransferEvent(transfer) {
124582
- if (!this.identityPublicKey || !equalBytes2(transfer.senderIdentityPublicKey, this.identityPublicKey))
125016
+ if (!this.identityPublicKey || !equalBytes3(transfer.senderIdentityPublicKey, this.identityPublicKey))
124583
125017
  return;
124584
125018
  const leafIds = transfer.leaves.flatMap((leaf) => leaf.leaf ? [leaf.leaf.id] : []);
124585
125019
  this.log("handleTransferEvent", `transfer=${transfer.id} type=${transfer.type} status=${transfer.status} leaves=[${leafIds.join(",")}]`);
@@ -125209,12 +125643,12 @@ var import_nice_grpc_common, import_light_bolt11_decoder, SparkFrostBase = class
125209
125643
  return pendingTransfer ? await this.transferService.claimTransfer(pendingTransfer) : [];
125210
125644
  }
125211
125645
  verifyKey(pubkey1, pubkey2, verifyingKey) {
125212
- return equalBytes2(addPublicKeys(pubkey1, pubkey2), verifyingKey);
125646
+ return equalBytes3(addPublicKeys(pubkey1, pubkey2), verifyingKey);
125213
125647
  }
125214
125648
  isLeafConsistent(leaf, opLeaf) {
125215
125649
  if (!opLeaf)
125216
125650
  return false;
125217
- return leaf.status === opLeaf.status && !!leaf.signingKeyshare && !!opLeaf.signingKeyshare && equalBytes2(leaf.signingKeyshare.publicKey, opLeaf.signingKeyshare.publicKey) && equalBytes2(leaf.nodeTx, opLeaf.nodeTx);
125651
+ return leaf.status === opLeaf.status && !!leaf.signingKeyshare && !!opLeaf.signingKeyshare && equalBytes3(leaf.signingKeyshare.publicKey, opLeaf.signingKeyshare.publicKey) && equalBytes3(leaf.nodeTx, opLeaf.nodeTx);
125218
125652
  }
125219
125653
  restoreLocalLockedToAvailable(leafIds) {
125220
125654
  let changed = false;
@@ -134357,7 +134791,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
134357
134791
  if (traceId)
134358
134792
  this.logEvent(`handleStreamEvent: Receiver transfer ${transfer.id} traceId=${traceId}`);
134359
134793
  const { senderIdentityPublicKey, receiverIdentityPublicKey } = transfer;
134360
- const isSelf = equalBytes2(senderIdentityPublicKey, receiverIdentityPublicKey);
134794
+ const isSelf = equalBytes3(senderIdentityPublicKey, receiverIdentityPublicKey);
134361
134795
  this.logEvent(`handleStreamEvent: Receiver transfer: id=${transfer.id} type=${transfer.type} status=${transfer.status} totalValue=${transfer.totalValue} leaves=${transfer.leaves.length} selfTransfer=${isSelf}`);
134362
134796
  if (!isSelf) {
134363
134797
  const incomingLeaves = transfer.leaves.map((l) => l.leaf).filter((l) => !!l);
@@ -135340,7 +135774,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
135340
135774
  const network = this.config.getNetwork();
135341
135775
  const identityPublicKey = await this.config.signer.getIdentityPublicKey();
135342
135776
  const nodes = await this.leafManager.queryWatchtowerExitedNodes();
135343
- return nodes.filter((node) => node.refundTx.length > 0 && equalBytes2(node.ownerIdentityPublicKey, identityPublicKey)).map((leaf) => {
135777
+ return nodes.filter((node) => node.refundTx.length > 0 && equalBytes3(node.ownerIdentityPublicKey, identityPublicKey)).map((leaf) => {
135344
135778
  const recoverable = resolveRecoverableOutput(leaf, nodes, network);
135345
135779
  return {
135346
135780
  leafId: leaf.id,
@@ -135754,7 +136188,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
135754
136188
  }
135755
136189
  const transfer = await this.transferService.sendTransferV3(allLeafKeyTweaks);
135756
136190
  const signerIdentityPublicKey = await this.config.signer.getIdentityPublicKey();
135757
- if (decodedReceivers.some((r) => equalBytes2(r.identityPublicKey, signerIdentityPublicKey))) {
136191
+ if (decodedReceivers.some((r) => equalBytes3(r.identityPublicKey, signerIdentityPublicKey))) {
135758
136192
  const pending = await this.transferService.queryTransfer(transfer.id);
135759
136193
  if (pending)
135760
136194
  await this.claimTransfer({ transfer: pending });
@@ -135798,7 +136232,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
135798
136232
  return await Promise.all(jobs.map(async (job) => {
135799
136233
  try {
135800
136234
  const transfer = await this.transferService.sendTransferWithKeyTweaks(job.leafKeyTweaks, job.sparkInvoice, job.transferId);
135801
- if (equalBytes2(signerIdentityPublicKey, job.receiverIdentityPubkey)) {
136235
+ if (equalBytes3(signerIdentityPublicKey, job.receiverIdentityPubkey)) {
135802
136236
  const pending = await this.transferService.queryTransfer(transfer.id);
135803
136237
  if (pending)
135804
136238
  await this.claimTransfer({ transfer: pending });
@@ -136473,7 +136907,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
136473
136907
  if (!transfer)
136474
136908
  throw new Error("Failed to provide preimage");
136475
136909
  const receiverIdentityPublicKey = transfer.receiverIdentityPublicKey;
136476
- if (equalBytes2(receiverIdentityPublicKey, await this.config.signer.getIdentityPublicKey()))
136910
+ if (equalBytes3(receiverIdentityPublicKey, await this.config.signer.getIdentityPublicKey()))
136477
136911
  await this.claimTransfer({
136478
136912
  transfer,
136479
136913
  emit: true
@@ -136513,7 +136947,7 @@ var init_spark_wallet_CIYOesd9 = __esm(() => {
136513
136947
  fee
136514
136948
  });
136515
136949
  this.config.signer.signTransactionIndex(spendTx, 0, await this.config.signer.getIdentityPublicKey());
136516
- const sig = spendTx.getInput(0).tapScriptSig.find(([ref2]) => equalBytes2(ref2.leafHash, leafHash))?.[1];
136950
+ const sig = spendTx.getInput(0).tapScriptSig.find(([ref2]) => equalBytes3(ref2.leafHash, leafHash))?.[1];
136517
136951
  spendTx.updateInput(0, { finalScriptWitness: [
136518
136952
  sig,
136519
136953
  hexToBytes4(preimage),