@glyphteck/veyl 0.35.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6227,7 +6227,7 @@ var init_config = __esm(() => {
6227
6227
  LOCAL_PROFILE_CACHE_MAX_AGE_MS = 30 * DAY_MS;
6228
6228
  LOCAL_AVATAR_CACHE_MAX_BYTES = 128 * MIB_BYTES;
6229
6229
  LOCAL_AVATAR_CACHE_MAX_AGE_MS = 30 * DAY_MS;
6230
- AVATAR_IMAGE_MAX_BYTES = 8 * MIB_BYTES;
6230
+ AVATAR_IMAGE_MAX_BYTES = 256 * KIB_BYTES;
6231
6231
  CHAT_READY_DOWNLOAD_MAX_BYTES = 8 * MIB_BYTES;
6232
6232
  CHAT_MESSAGE_FILE_CACHE_MAX_BYTES = 64 * MIB_BYTES;
6233
6233
  CHAT_UNSAVED_TTL_MS = CHAT_UNSAVED_TTL_DAYS * DAY_MS;
@@ -106368,18 +106368,13 @@ var init_values = __esm(() => {
106368
106368
 
106369
106369
  // ../../shared/avatar.js
106370
106370
  function readAvatarVersion(value) {
106371
- if (value == null || value === "" || typeof value !== "number" && typeof value !== "string") {
106372
- return null;
106373
- }
106374
- const version = Number(value);
106375
- return Number.isSafeInteger(version) && version >= 0 ? version : null;
106376
- }
106377
- function avatarUrlWithVersion(url, version) {
106378
- if (!url)
106379
- return null;
106380
- return version == null ? url : `${url}${url.includes("?") ? "&" : "?"}v=${encodeURIComponent(String(version))}`;
106371
+ const version = typeof value === "string" ? value.trim().toLowerCase() : "";
106372
+ return AVATAR_VERSION_RE.test(version) ? version : null;
106381
106373
  }
106382
- var init_avatar = () => {};
106374
+ var AVATAR_VERSION_RE;
106375
+ var init_avatar = __esm(() => {
106376
+ AVATAR_VERSION_RE = /^[0-9a-f]{64}$/;
106377
+ });
106383
106378
 
106384
106379
  // ../../shared/moderation.js
106385
106380
  function banUntilMs(ban) {
@@ -106657,11 +106652,16 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
106657
106652
  markDiag(diag, "user.avatar.fetch.start", { force: !!force, persist: !!persist, hasVersion: avatarVersion != null });
106658
106653
  try {
106659
106654
  const promise = (async () => {
106660
- const nextAvatar = await cloud.peer.avatar.url(uid, { version: avatarVersion });
106661
- if (persist && avatarVersion != null) {
106662
- cloud.peer.avatar.read(uid).then((bytes) => writeCachedAvatar(uid, { version: avatarVersion, bytes })).catch(() => {});
106655
+ if (persist && avatarVersion != null && avatarCache && typeof cloud.peer.avatar.read === "function") {
106656
+ try {
106657
+ const bytes = await cloud.peer.avatar.read(uid, { version: avatarVersion });
106658
+ const cachedAvatar = await writeCachedAvatar(uid, { version: avatarVersion, bytes });
106659
+ if (cachedAvatar) {
106660
+ return cachedAvatar;
106661
+ }
106662
+ } catch {}
106663
106663
  }
106664
- return nextAvatar;
106664
+ return cloud.peer.avatar.url(uid, { version: avatarVersion });
106665
106665
  })().then((nextAvatar) => {
106666
106666
  markDone(diag, "user.avatar.fetch", startedAt, { found: !!nextAvatar, hasVersion: avatarVersion != null });
106667
106667
  if (!nextAvatar || avatarFetch.uid !== uid || avatarFetch.key !== key) {
@@ -107001,10 +107001,12 @@ function createUser({ cloud, network, avatarCache = null, diag = null, onSession
107001
107001
  }
107002
107002
  function refetchAvatar(options = {}) {
107003
107003
  const requestedVersion = readAvatarVersion(options?.version);
107004
- const optimistic = options?.optimistic === true;
107005
- const nextVersion = requestedVersion ?? (optimistic && state.avatarVersion != null ? state.avatarVersion + 1 : state.avatarVersion);
107006
- const persist = options?.persist ?? !(optimistic && requestedVersion == null);
107007
- return fetchAvatar(state.uid, { force: true, persist, version: nextVersion });
107004
+ const nextVersion = requestedVersion ?? state.avatarVersion;
107005
+ return fetchAvatar(state.uid, {
107006
+ force: options?.force === true || state.avatarVersion !== nextVersion || !state.avatar,
107007
+ persist: options?.persist !== false,
107008
+ version: nextVersion
107009
+ });
107008
107010
  }
107009
107011
  function setNetwork(nextNetwork) {
107010
107012
  if (activeNetwork === nextNetwork)
@@ -109422,7 +109424,7 @@ function sameSigningKeys(left, right) {
109422
109424
  function sameChatShape(a, b) {
109423
109425
  if (!a || !b)
109424
109426
  return a === b;
109425
- return a.id === b.id && a.protocol === b.protocol && a.linkId === b.linkId && a.entryId === b.entryId && a.peerChatPK === b.peerChatPK && a.peerUid === b.peerUid && sameSigningKeys(a.signingKeysByChatKey, b.signingKeysByChatKey) && a.ts === b.ts && a.startMs === b.startMs && a.unseen === b.unseen && a.settings?.retention === b.settings?.retention && sameChatPreview(a.preview, b.preview);
109427
+ return a.id === b.id && a.protocol === b.protocol && a.linkId === b.linkId && a.entryId === b.entryId && a.peerChatPK === b.peerChatPK && a.peerUid === b.peerUid && sameSigningKeys(a.signingKeysByChatKey, b.signingKeysByChatKey) && a.signerShared === b.signerShared && a.ts === b.ts && a.startMs === b.startMs && a.unseen === b.unseen && a.settings?.retention === b.settings?.retention && sameChatPreview(a.preview, b.preview);
109426
109428
  }
109427
109429
  function sameChats(prev, next) {
109428
109430
  if (prev.length !== next.length)
@@ -109781,8 +109783,10 @@ function emptyPayload() {
109781
109783
  chatsById: {},
109782
109784
  profilesByUid: {},
109783
109785
  mediaByKey: {},
109786
+ walletBalance: null,
109784
109787
  resumeRoute: null,
109785
- lastCameraFacing: null
109788
+ lastCameraFacing: null,
109789
+ cloaked: false
109786
109790
  };
109787
109791
  }
109788
109792
  function isObject3(value) {
@@ -109843,8 +109847,26 @@ function normalizePayload(value) {
109843
109847
  chatsById: isObject3(input.chatsById) ? input.chatsById : {},
109844
109848
  profilesByUid: isObject3(input.profilesByUid) ? input.profilesByUid : {},
109845
109849
  mediaByKey: isObject3(input.mediaByKey) ? input.mediaByKey : {},
109850
+ walletBalance: cleanWalletBalance(input.walletBalance),
109846
109851
  resumeRoute: cleanResumeRoute(input.resumeRoute),
109847
- lastCameraFacing: cleanCameraFacing(input.lastCameraFacing)
109852
+ lastCameraFacing: cleanCameraFacing(input.lastCameraFacing),
109853
+ cloaked: input.cloaked === true
109854
+ };
109855
+ }
109856
+ function cleanWalletBalance(value) {
109857
+ if (!isObject3(value))
109858
+ return null;
109859
+ const walletPK = cleanText(value.walletPK).toLowerCase();
109860
+ const network = cleanText(value.network).toLowerCase();
109861
+ const balance = Number(value.balance);
109862
+ if (!walletPK || !network || !Number.isSafeInteger(balance) || balance < 0) {
109863
+ return null;
109864
+ }
109865
+ return {
109866
+ walletPK,
109867
+ network,
109868
+ balance,
109869
+ savedAt: Number.isFinite(value.savedAt) ? value.savedAt : 0
109848
109870
  };
109849
109871
  }
109850
109872
  function cleanResumeRoute(route) {
@@ -110290,8 +110312,8 @@ function stateFromPayload(payload, { chatId, selfChatPublicKey, peerChatPublicKe
110290
110312
  if (!cleanOlder.length && !cleanLive.length) {
110291
110313
  return { rejectReason: "empty-message-cache" };
110292
110314
  }
110293
- const historyStartReached = payload.historyStartReached === true && payload.truncated !== true;
110294
- const payloadHasOlder = historyStartReached ? false : payload.hasOlder === true || payload.truncated === true;
110315
+ const historyStartReached = payload.historyStartReached === true;
110316
+ const payloadHasOlder = payload.hasOlder === true || payload.truncated === true;
110295
110317
  const partialOlderThan = partial ? markerFromMessage(cleanOlder[0]) : null;
110296
110318
  return {
110297
110319
  historyMessages: cleanOlder,
@@ -110436,7 +110458,7 @@ function writeCachedMessageState(cache, { stateVersion = null, chatId, selfChatP
110436
110458
  signingKeysVersion: cleanText(signingKeysVersion),
110437
110459
  savedAt: Date.now(),
110438
110460
  hasOlder: nextHasOlder === true || compacted.truncated,
110439
- historyStartReached: nextHistoryStartReached === true && compacted.truncated !== true,
110461
+ historyStartReached: nextHistoryStartReached === true,
110440
110462
  olderThan: serializeMarker(storedOlderThan),
110441
110463
  olderLoaded: nextOlderLoaded === true || compacted.historyMessages.length > 0,
110442
110464
  latestServerPage: batchFromLive(compacted.latestMessages, expiredKeys, deletedKeys, nextServerBatch),
@@ -110695,6 +110717,36 @@ var init_chats2 = __esm(() => {
110695
110717
  "use client";
110696
110718
  });
110697
110719
 
110720
+ // ../../shared/cache/localdata/balance.js
110721
+ function normalizedBalance(value) {
110722
+ const balance = Number(value);
110723
+ return Number.isSafeInteger(balance) && balance >= 0 ? balance : null;
110724
+ }
110725
+ function writeCachedWalletBalance(cache, { walletPK = null, network = null, balance = null } = {}) {
110726
+ const nextWalletPK = lowerText(walletPK);
110727
+ const nextNetwork = lowerText(network);
110728
+ const nextBalance = normalizedBalance(balance);
110729
+ if (!cache || !nextWalletPK || !nextNetwork || nextBalance == null) {
110730
+ return Promise.resolve(false);
110731
+ }
110732
+ const current = cache.read?.()?.walletBalance;
110733
+ if (sameText(current?.walletPK, nextWalletPK) && sameText(current?.network, nextNetwork) && normalizedBalance(current?.balance) === nextBalance) {
110734
+ return Promise.resolve(false);
110735
+ }
110736
+ return cache.patch((draft) => {
110737
+ draft.walletBalance = {
110738
+ walletPK: nextWalletPK,
110739
+ network: nextNetwork,
110740
+ balance: nextBalance,
110741
+ savedAt: Date.now()
110742
+ };
110743
+ return draft;
110744
+ }).then(() => true);
110745
+ }
110746
+ var init_balance = __esm(() => {
110747
+ "use client";
110748
+ });
110749
+
110698
110750
  // ../../shared/cache/localdata/profiles.js
110699
110751
  function isUsableProfile(profile, now = Date.now()) {
110700
110752
  if (!profile?.uid || !profile.walletPK && !profile.chatPK) {
@@ -110935,24 +110987,26 @@ async function readCachedTransferHeadState(cache, { walletPK = null } = {}) {
110935
110987
  const stored = cache ? await cache.readPart(TX_HEAD_PART).catch(() => null) : null;
110936
110988
  return stateFromPayload2(stored, { walletPK, source: "head" });
110937
110989
  }
110938
- function writeCachedTransferState(cache, { transfers, walletPK = null, serverEndReached = false, nextOffset = null, oldestTxMs = null } = {}) {
110939
- if (!cache || !Array.isArray(transfers)) {
110940
- return Promise.resolve(false);
110990
+ function cachedTransferPayloads({ transfers, walletPK = null, serverEndReached = false, historyCount = null, nextOffset = null, oldestTxMs = null } = {}) {
110991
+ if (!Array.isArray(transfers)) {
110992
+ return null;
110941
110993
  }
110942
110994
  const nextWalletPK = lowerText(walletPK);
110943
110995
  if (!nextWalletPK) {
110944
- return Promise.resolve(false);
110996
+ return null;
110945
110997
  }
110946
110998
  const visible = [...transfers].filter((tx) => tx?.id && isVisibleTransfer(tx) && transferBelongsToWallet(tx, nextWalletPK)).sort((a, b) => txCreatedMs(b) - txCreatedMs(a));
110947
110999
  const sorted2 = visible.slice(0, Math.max(1, WALLET_TRANSFER_CACHE_LIMIT)).map(jsonClean);
110948
111000
  const capped = sorted2.length < visible.length;
111001
+ const knownHistoryCount = Math.max(sorted2.length, Math.floor(Number(historyCount) || 0));
111002
+ const complete = serverEndReached === true && !capped && sorted2.length >= knownHistoryCount;
110949
111003
  const fullPayload = {
110950
111004
  version: CACHE_VERSION,
110951
111005
  walletPK: nextWalletPK,
110952
- serverEndReached: serverEndReached === true && !capped,
110953
- historyCount: sorted2.length,
111006
+ serverEndReached: complete,
111007
+ historyCount: knownHistoryCount,
110954
111008
  nextOffset: Number.isFinite(nextOffset) ? nextOffset : sorted2.length,
110955
- oldestTxMs: serverEndReached === true && !capped && Number.isFinite(oldestTxMs) ? oldestTxMs : null,
111009
+ oldestTxMs: complete && Number.isFinite(oldestTxMs) ? oldestTxMs : null,
110956
111010
  savedAt: Date.now(),
110957
111011
  transfers: sorted2
110958
111012
  };
@@ -110961,7 +111015,24 @@ function writeCachedTransferState(cache, { transfers, walletPK = null, serverEnd
110961
111015
  ...fullPayload,
110962
111016
  transfers: headTransfers
110963
111017
  };
110964
- return Promise.all([cache.writePart(TX_HEAD_PART, headPayload), cache.writePart(TX_PART, fullPayload)]).then((results) => results.some(Boolean));
111018
+ return { fullPayload, headPayload };
111019
+ }
111020
+ function writeCachedTransferHeadState(cache, state = {}) {
111021
+ if (!cache) {
111022
+ return Promise.resolve(false);
111023
+ }
111024
+ const payloads = cachedTransferPayloads(state);
111025
+ return payloads ? cache.writePart(TX_HEAD_PART, payloads.headPayload) : Promise.resolve(false);
111026
+ }
111027
+ function writeCachedTransferState(cache, state = {}) {
111028
+ if (!cache) {
111029
+ return Promise.resolve(false);
111030
+ }
111031
+ const payloads = cachedTransferPayloads(state);
111032
+ if (!payloads) {
111033
+ return Promise.resolve(false);
111034
+ }
111035
+ return Promise.all([cache.writePart(TX_HEAD_PART, payloads.headPayload), cache.writePart(TX_PART, payloads.fullPayload)]).then((results) => results.some(Boolean));
110965
111036
  }
110966
111037
  var TX_PART = "wallet-transfers", TX_HEAD_PART = "wallet-transfers-head", CACHE_VERSION = 2, CACHE_HEAD_LIMIT;
110967
111038
  var init_transfers = __esm(() => {
@@ -111437,6 +111508,7 @@ var init_vault2 = __esm(() => {
111437
111508
  // ../../shared/cache/localdata.js
111438
111509
  var init_localdata = __esm(() => {
111439
111510
  init_chats2();
111511
+ init_balance();
111440
111512
  init_media();
111441
111513
  init_messages2();
111442
111514
  init_profiles();
@@ -111514,7 +111586,8 @@ function makeOwnChatEntry(pair, fields = {}) {
111514
111586
  preview: fields.preview || null,
111515
111587
  saved: fields.saved || null,
111516
111588
  readMs: Number.isFinite(fields.readMs) ? fields.readMs : null,
111517
- startMs: Number.isFinite(fields.startMs) ? fields.startMs : null
111589
+ startMs: Number.isFinite(fields.startMs) ? fields.startMs : null,
111590
+ signerShared: fields.signerShared === true
111518
111591
  };
111519
111592
  }
111520
111593
  var CHAT_ENTRY_VERSION = 1;
@@ -112279,7 +112352,25 @@ async function setChatRead(cloud, uid, chatPrivKey, chatId, readMs, options = {}
112279
112352
  body: await sealOwnChatEntry(chatPrivKey, entryId, {
112280
112353
  ...entry,
112281
112354
  readMs: Math.max(currentReadMs, nextReadMs),
112282
- ...hasPreviewPatch ? { preview: preview2 || null } : {}
112355
+ ...hasPreviewPatch ? { preview: preview2 || null } : {},
112356
+ ...options.signerShared === true ? { signerShared: true } : {}
112357
+ })
112358
+ });
112359
+ return true;
112360
+ }
112361
+ async function setChatSignerShared(cloud, uid, chatPrivKey, chatId) {
112362
+ if (!cloud || !uid || !chatPrivKey || !chatId) {
112363
+ return false;
112364
+ }
112365
+ const entryId = ownChatEntryId(chatPrivKey, chatId);
112366
+ const entry = await readOwnEntry(cloud, uid, chatPrivKey, entryId);
112367
+ if (!entry?.chatId || entry.signerShared === true) {
112368
+ return entry?.signerShared === true;
112369
+ }
112370
+ await cloud.user.chats.write(uid, entryId, {
112371
+ body: await sealOwnChatEntry(chatPrivKey, entryId, {
112372
+ ...entry,
112373
+ signerShared: true
112283
112374
  })
112284
112375
  });
112285
112376
  return true;
@@ -112322,7 +112413,8 @@ async function ownEntryWrite(cloud, uid, chatPrivKey, pair, fields = {}) {
112322
112413
  preview: fields.preview || existing?.preview,
112323
112414
  saved: existing?.saved || null,
112324
112415
  readMs: existing?.readMs,
112325
- startMs: existing?.startMs
112416
+ startMs: existing?.startMs,
112417
+ signerShared: fields.signerShared === true || existing?.signerShared === true
112326
112418
  });
112327
112419
  const tsMs = timestampMs(fields.ts, null);
112328
112420
  return {
@@ -112417,10 +112509,22 @@ async function sendMsg(cloud, senderPubkey, senderPrivkey, receiverChatPK, messa
112417
112509
  messageId,
112418
112510
  message: msgData,
112419
112511
  ownerEntry,
112420
- inbox: ping ? { recipientUid: recipientProfile?.uid, ping } : null,
112512
+ inbox: ping ? { recipientUid: recipientProfile?.uid, ping, notify: options?.notify !== false } : null,
112421
112513
  onCommitted: typeof options?.onCommitted === "function" ? (committed) => options.onCommitted({ ...committed, message: sentMessage, preview: preview2 }) : undefined
112422
112514
  });
112423
- return { chatId, msgId: messageId, cid: head.cid, message: sentMessage, preview: preview2, ...write?.skipped ? { skipped: true } : {} };
112515
+ const delivered = options?.ping === true ? !!ping && write?.delivered !== false : write?.delivered !== false;
112516
+ if (ping && delivered && ownerEntry && options?.ownEntry?.signerShared !== true) {
112517
+ await setChatSignerShared(cloud, cleanText(options?.senderUid), senderPrivkey, chatId).catch(() => false);
112518
+ }
112519
+ return {
112520
+ chatId,
112521
+ msgId: messageId,
112522
+ cid: head.cid,
112523
+ message: sentMessage,
112524
+ preview: preview2,
112525
+ delivered,
112526
+ ...write?.skipped ? { skipped: true } : {}
112527
+ };
112424
112528
  });
112425
112529
  }
112426
112530
  async function sendReadReceipt(cloud, senderPubkey, senderPrivkey, receiverChatPK, target, options = {}) {
@@ -112429,7 +112533,13 @@ async function sendReadReceipt(cloud, senderPubkey, senderPrivkey, receiverChatP
112429
112533
  cid: makeCid(),
112430
112534
  s: senderPubkey
112431
112535
  };
112432
- return sendMsg(cloud, senderPubkey, senderPrivkey, receiverChatPK, receipt, { ...options, updatePreview: false, ping: false });
112536
+ return sendMsg(cloud, senderPubkey, senderPrivkey, receiverChatPK, receipt, {
112537
+ ...options,
112538
+ updatePreview: false,
112539
+ ping: options?.ping === true,
112540
+ pingKind: "read",
112541
+ notify: false
112542
+ });
112433
112543
  }
112434
112544
  async function sendReaction(cloud, senderPubkey, senderPrivkey, receiverChatPK, target, emoji, options = {}) {
112435
112545
  const reaction = {
@@ -113046,7 +113156,7 @@ function messageExpiryGroups(messages, selfChatPublicKey, seenAt) {
113046
113156
  { messages: day, expiresAt: seenAt + AFTER_SEEN_MS }
113047
113157
  ].filter((group) => group.messages.length);
113048
113158
  }
113049
- function createChatSeen({ cloud, uid, chatBanned, chatPK, chatPrivateKey, localCache, pendingReadRef, readCacheRef, readWriteInterval, getChatRetention, setChats, diag }) {
113159
+ function createChatSeen({ cloud, uid, chatBanned, chatPK, chatPrivateKey, localCache, pendingReadRef, readCacheRef, readWriteInterval, getChatRetention, sendOptionsForPeer, setChats, diag }) {
113050
113160
  const scheduleRead = (chatId, message, previewMs, sendReceipt, expiryMessages = []) => {
113051
113161
  scheduleReadWrite({
113052
113162
  pendingRead: pendingReadRef.current,
@@ -113057,24 +113167,37 @@ function createChatSeen({ cloud, uid, chatBanned, chatPK, chatPrivateKey, localC
113057
113167
  expiryMessages: sendReceipt ? expiryMessages : [],
113058
113168
  interval: readWriteInterval,
113059
113169
  write: async (pending) => {
113170
+ let receiptError = null;
113171
+ let receiptResult = null;
113172
+ let introduceSigner = false;
113173
+ if (pending.receipt) {
113174
+ const sendOptions = sendOptionsForPeer(pending.receipt.peerChatPK);
113175
+ introduceSigner = sendOptions?.signerShared !== true;
113176
+ try {
113177
+ receiptResult = await sendReadReceipt(cloud, chatPK, chatPrivateKey, pending.receipt.peerChatPK, pending.receipt.target, {
113178
+ chatId,
113179
+ retention: getChatRetention(chatId),
113180
+ senderUid: uid,
113181
+ receiverUid: sendOptions?.receiverUid,
113182
+ ping: introduceSigner
113183
+ });
113184
+ } catch (error) {
113185
+ receiptError = error;
113186
+ }
113187
+ }
113060
113188
  const writes = [
113061
113189
  setChatRead(cloud, uid, chatPrivateKey, chatId, pending.previewMs, {
113062
- preview: (currentPreview) => withChatPreviewOpened(currentPreview, pending.message, pending.previewMs, chatPK) || currentPreview
113190
+ preview: (currentPreview) => withChatPreviewOpened(currentPreview, pending.message, pending.previewMs, chatPK) || currentPreview,
113191
+ signerShared: introduceSigner && receiptResult?.delivered === true
113063
113192
  })
113064
113193
  ];
113065
113194
  if (pending.receipt) {
113066
- writes.push(sendReadReceipt(cloud, chatPK, chatPrivateKey, pending.receipt.peerChatPK, pending.receipt.target, {
113067
- chatId,
113068
- retention: getChatRetention(chatId),
113069
- senderUid: uid
113070
- }));
113071
113195
  for (const group of messageExpiryGroups(pending.expiryMessages, chatPK, pending.expirySeenAt)) {
113072
113196
  writes.push(setMessageExpiry(cloud, chatId, group.messages, group.expiresAt));
113073
113197
  }
113074
113198
  }
113075
113199
  const results = await Promise.allSettled(writes);
113076
113200
  const ownerError = results[0]?.status === "rejected" ? results[0].reason : null;
113077
- const receiptError = pending.receipt && results[1]?.status === "rejected" ? results[1].reason : null;
113078
113201
  if (ownerError) {
113079
113202
  markError(diag, "chat.read.state.write", Date.now(), ownerError);
113080
113203
  }
@@ -113082,7 +113205,7 @@ function createChatSeen({ cloud, uid, chatBanned, chatPK, chatPrivateKey, localC
113082
113205
  readCacheRef.current.delete(chatId);
113083
113206
  markError(diag, "chat.read.receipt.write", Date.now(), receiptError);
113084
113207
  }
113085
- for (const result of results.slice(pending.receipt ? 2 : 1)) {
113208
+ for (const result of results.slice(1)) {
113086
113209
  if (result?.status === "rejected") {
113087
113210
  markError(diag, "chat.message.expiry.write", Date.now(), result.reason);
113088
113211
  }
@@ -115133,7 +115256,11 @@ function readableEntryWindow(entries, pageSize, chatPK, peerChatPK, options = {}
115133
115256
  const limitCount = positiveInt(pageSize, MSG_BATCH_SIZE);
115134
115257
  const projectedEntries = readableProjectedEntries(source, chatPK, peerChatPK, options);
115135
115258
  if (!projectedEntries.length) {
115136
- return { count: 0, entries: [], start: 0 };
115259
+ return {
115260
+ count: 0,
115261
+ entries: source.filter((entry) => isProjectionSupportMsg(entry?.message)),
115262
+ start: 0
115263
+ };
115137
115264
  }
115138
115265
  const indexes = projectedEntries.map((entry) => entry.index);
115139
115266
  const projectedByIndex = new Map(projectedEntries.map((entry) => [entry.index, entry.message]));
@@ -117493,6 +117620,7 @@ function chatFromPing(ping, entryId, existing, preview2, userChatPK, ms, options
117493
117620
  saved: existing?.saved || null,
117494
117621
  readMs,
117495
117622
  startMs,
117623
+ signerShared: existing?.signerShared === true,
117496
117624
  preview: visiblePreview,
117497
117625
  ts,
117498
117626
  unseen: visiblePreview ? isChatUnseenForUser({ preview: visiblePreview, readMs }, userChatPK) : false
@@ -117551,7 +117679,8 @@ async function savePing(cloud, uid, userChatPK, userPrivKey, ping, options = {})
117551
117679
  settings: chatSettingsFromPreview(existing?.settings, preview2),
117552
117680
  preview: preview2 || existing?.preview,
117553
117681
  readMs: existing?.readMs,
117554
- startMs: existing ? existing.startMs : timestampMs(options.startPreview?.ts ?? preview2?.ts, null, { positive: true })
117682
+ startMs: existing ? existing.startMs : timestampMs(options.startPreview?.ts ?? preview2?.ts, null, { positive: true }),
117683
+ signerShared: existing?.signerShared === true
117555
117684
  });
117556
117685
  const body = await sealOwnChatEntry(userPrivKey, entryId, entry);
117557
117686
  const record = { body };
@@ -117561,14 +117690,16 @@ async function savePing(cloud, uid, userChatPK, userPrivKey, ping, options = {})
117561
117690
  await cloud.user.chats.write(uid, entryId, record);
117562
117691
  return true;
117563
117692
  }
117564
- function chatWithReadPreview(existing, preview2, userChatPK) {
117565
- if (!existing?.id || !preview2) {
117693
+ function chatWithReadPreview(existing, preview2, userChatPK, fields = {}) {
117694
+ if (!existing?.id) {
117566
117695
  return null;
117567
117696
  }
117568
117697
  return {
117569
117698
  ...existing,
117570
- preview: preview2,
117571
- unseen: isChatUnseenForUser({ preview: preview2, readMs: existing.readMs }, userChatPK)
117699
+ signingKeysByChatKey: fields.signingKeysByChatKey || existing.signingKeysByChatKey,
117700
+ peerUid: fields.peerUid || existing.peerUid,
117701
+ preview: preview2 || existing.preview,
117702
+ unseen: preview2 ? isChatUnseenForUser({ preview: preview2, readMs: existing.readMs }, userChatPK) : existing.unseen
117572
117703
  };
117573
117704
  }
117574
117705
  function patchReadPingPreview(existing, receiptPreview, userChatPK) {
@@ -117649,6 +117780,8 @@ async function processInbox(cloud, uid, userChatPK, userPrivKey, options = {}) {
117649
117780
  let current = chatsById.get(group.chatId) || null;
117650
117781
  let wrote = false;
117651
117782
  let handled = false;
117783
+ const identityPing = group.message?.ping || group.read?.ping || group.delete?.ping;
117784
+ const peerUid2 = await resolvePingUid(cloud, identityPing?.payload);
117652
117785
  if (group.message) {
117653
117786
  const item = group.message;
117654
117787
  const entryId = ownChatEntryId(userPrivKey, item.chatId);
@@ -117669,7 +117802,8 @@ async function processInbox(cloud, uid, userChatPK, userPrivKey, options = {}) {
117669
117802
  signingKeysByChatKey,
117670
117803
  preview: preview2,
117671
117804
  startPreview,
117672
- tsMs: item.ms
117805
+ tsMs: item.ms,
117806
+ peerUid: peerUid2
117673
117807
  });
117674
117808
  }
117675
117809
  if (group.read) {
@@ -117679,22 +117813,24 @@ async function processInbox(cloud, uid, userChatPK, userPrivKey, options = {}) {
117679
117813
  const signingKeysByChatKey = pingSigningKeys(item.ping, existing);
117680
117814
  const receiptPreview = await readPingMsg(cloud, userChatPK, userPrivKey, item.ping, signingKeysByChatKey);
117681
117815
  const openedPreview = patchReadPingPreview(existing, receiptPreview, userChatPK);
117682
- const chat = chatWithReadPreview(current, openedPreview, userChatPK);
117816
+ const chat = chatWithReadPreview(current || existing, openedPreview, userChatPK, {
117817
+ signingKeysByChatKey,
117818
+ peerUid: peerUid2
117819
+ });
117683
117820
  handled = true;
117684
- if (openedPreview) {
117685
- if (chat) {
117686
- current = chat;
117687
- chatsById.set(chat.id, chat);
117688
- options.onPingChat?.(chat);
117689
- }
117690
- const writeExisting = chat || existing;
117691
- wrote = await savePing(cloud, uid, userChatPK, userPrivKey, item.ping, {
117692
- existing: writeExisting,
117693
- signingKeysByChatKey,
117694
- preview: openedPreview,
117695
- writeTs: false
117696
- }) || wrote;
117821
+ if (chat) {
117822
+ current = chat;
117823
+ chatsById.set(chat.id, chat);
117824
+ options.onPingChat?.(chat);
117697
117825
  }
117826
+ const writeExisting = chat || existing;
117827
+ wrote = await savePing(cloud, uid, userChatPK, userPrivKey, item.ping, {
117828
+ existing: writeExisting,
117829
+ signingKeysByChatKey,
117830
+ preview: openedPreview || existing?.preview || null,
117831
+ writeTs: false,
117832
+ peerUid: peerUid2
117833
+ }) || wrote;
117698
117834
  }
117699
117835
  if (group.delete) {
117700
117836
  const item = group.delete;
@@ -117715,7 +117851,8 @@ async function processInbox(cloud, uid, userChatPK, userPrivKey, options = {}) {
117715
117851
  wrote = await savePing(cloud, uid, userChatPK, userPrivKey, item.ping, {
117716
117852
  existing: writeExisting,
117717
117853
  signingKeysByChatKey,
117718
- preview: deletedPreview
117854
+ preview: deletedPreview,
117855
+ peerUid: peerUid2
117719
117856
  }) || wrote;
117720
117857
  }
117721
117858
  }
@@ -118121,6 +118258,7 @@ async function decryptChatEntry(entryRecord, userChatPK, userPrivKey) {
118121
118258
  saved: entry.saved || null,
118122
118259
  readMs,
118123
118260
  startMs,
118261
+ signerShared: entry.signerShared === true,
118124
118262
  preview: visiblePreview,
118125
118263
  ts,
118126
118264
  unseen: visiblePreview ? isChatUnseenForUser({ preview: visiblePreview, readMs }, userChatPK) : false
@@ -118907,6 +119045,7 @@ function createChatList({
118907
119045
  chatExists: !!peerChat?.id || hasServerChatForPeer(peerChatPK),
118908
119046
  receiverUid: peerChat?.peerUid || "",
118909
119047
  peerSigningPublicKey: peerChat?.signingKeysByChatKey?.[peerChatPK] || "",
119048
+ signerShared: peerChat?.signerShared === true,
118910
119049
  ownEntry: peerChat?.entryId ? peerChat : null
118911
119050
  };
118912
119051
  };
@@ -119604,6 +119743,7 @@ function createChatSession({
119604
119743
  readCacheRef,
119605
119744
  readWriteInterval,
119606
119745
  getChatRetention,
119746
+ sendOptionsForPeer,
119607
119747
  setChats,
119608
119748
  diag
119609
119749
  });
@@ -120748,7 +120888,7 @@ function createPeersApi({ cloud, network, avatarCache = null }) {
120748
120888
  }
120749
120889
  if (avatarVersion != null && typeof cloud.peer.avatar.read === "function") {
120750
120890
  try {
120751
- const bytes = await cloud.peer.avatar.read(uid);
120891
+ const bytes = await cloud.peer.avatar.read(uid, { version: avatarVersion });
120752
120892
  const cachedSource = await writeCachedAvatar(uid, avatarVersion, bytes);
120753
120893
  if (cachedSource) {
120754
120894
  return cachedSource;
@@ -121285,6 +121425,10 @@ function getSatsBalance(result) {
121285
121425
  }
121286
121426
  return satsBalance;
121287
121427
  }
121428
+ function displayBalanceValue(value) {
121429
+ const balance = Number(value);
121430
+ return Number.isSafeInteger(balance) && balance >= 0 ? balance : null;
121431
+ }
121288
121432
  function samePlainObject(a, b) {
121289
121433
  if (a === b) {
121290
121434
  return true;
@@ -121313,9 +121457,10 @@ function sameTokenBalances(a, b) {
121313
121457
  }
121314
121458
  return true;
121315
121459
  }
121316
- function createWalletBalance({ wallet, diag, onChange = null }) {
121460
+ function createWalletBalance({ wallet, diag, initialDisplayBalance = null, onBalance = null, onChange = null }) {
121317
121461
  let snapshot = {
121318
121462
  balance: null,
121463
+ displayBalance: displayBalanceValue(initialDisplayBalance),
121319
121464
  satsBalance: null,
121320
121465
  tokenBalances: new Map,
121321
121466
  balanceReady: false,
@@ -121325,10 +121470,28 @@ function createWalletBalance({ wallet, diag, onChange = null }) {
121325
121470
  snapshot = { ...snapshot, ...changes };
121326
121471
  onChange?.();
121327
121472
  };
121473
+ const publishDisplayBalance = (changes, value, trusted = false) => {
121474
+ const displayBalance = displayBalanceValue(value);
121475
+ if (displayBalance == null || !trusted && !snapshot.balanceReady) {
121476
+ return null;
121477
+ }
121478
+ if (!Object.is(snapshot.displayBalance, displayBalance)) {
121479
+ changes.displayBalance = displayBalance;
121480
+ }
121481
+ return displayBalance;
121482
+ };
121328
121483
  const setBalance = (nextBalance) => {
121329
121484
  const value = typeof nextBalance === "function" ? nextBalance(snapshot.balance) : nextBalance;
121485
+ const changes = {};
121330
121486
  if (!Object.is(snapshot.balance, value)) {
121331
- publish({ balance: value });
121487
+ changes.balance = value;
121488
+ }
121489
+ const displayBalance = publishDisplayBalance(changes, value);
121490
+ if (Object.keys(changes).length) {
121491
+ publish(changes);
121492
+ }
121493
+ if (displayBalance != null) {
121494
+ onBalance?.(displayBalance);
121332
121495
  }
121333
121496
  };
121334
121497
  const setTokenBalances = (nextTokenBalances) => {
@@ -121337,12 +121500,13 @@ function createWalletBalance({ wallet, diag, onChange = null }) {
121337
121500
  publish({ tokenBalances: new Map(source) });
121338
121501
  }
121339
121502
  };
121340
- const setBalanceResult = (result) => {
121503
+ const setBalanceResult = (result, { ready = false } = {}) => {
121341
121504
  const changes = {};
121342
121505
  const nextBalance = getBalanceValue(result);
121343
121506
  if (nextBalance != null && !Object.is(snapshot.balance, nextBalance)) {
121344
121507
  changes.balance = nextBalance;
121345
121508
  }
121509
+ const nextDisplayBalance = publishDisplayBalance(changes, nextBalance, true);
121346
121510
  const nextSatsBalance = getSatsBalance(result);
121347
121511
  if (nextSatsBalance && !samePlainObject(snapshot.satsBalance, nextSatsBalance)) {
121348
121512
  changes.satsBalance = nextSatsBalance;
@@ -121351,9 +121515,15 @@ function createWalletBalance({ wallet, diag, onChange = null }) {
121351
121515
  if (nextTokenBalances && !sameTokenBalances(snapshot.tokenBalances, nextTokenBalances)) {
121352
121516
  changes.tokenBalances = new Map(nextTokenBalances);
121353
121517
  }
121518
+ if (ready && !snapshot.balanceReady) {
121519
+ changes.balanceReady = true;
121520
+ }
121354
121521
  if (Object.keys(changes).length) {
121355
121522
  publish(changes);
121356
121523
  }
121524
+ if (nextDisplayBalance != null) {
121525
+ onBalance?.(nextDisplayBalance);
121526
+ }
121357
121527
  };
121358
121528
  const setSatsBalanceResult = (nextSatsBalance) => {
121359
121529
  if (!nextSatsBalance || typeof nextSatsBalance !== "object") {
@@ -121366,9 +121536,13 @@ function createWalletBalance({ wallet, diag, onChange = null }) {
121366
121536
  if (nextSatsBalance.available != null && !Object.is(snapshot.balance, nextSatsBalance.available)) {
121367
121537
  changes.balance = nextSatsBalance.available;
121368
121538
  }
121539
+ const nextDisplayBalance = publishDisplayBalance(changes, nextSatsBalance.available);
121369
121540
  if (Object.keys(changes).length) {
121370
121541
  publish(changes);
121371
121542
  }
121543
+ if (nextDisplayBalance != null) {
121544
+ onBalance?.(nextDisplayBalance);
121545
+ }
121372
121546
  };
121373
121547
  const getBalance = async () => {
121374
121548
  if (!wallet) {
@@ -121382,7 +121556,7 @@ function createWalletBalance({ wallet, diag, onChange = null }) {
121382
121556
  }
121383
121557
  try {
121384
121558
  const result = await wallet.getBalance();
121385
- setBalanceResult(result);
121559
+ setBalanceResult(result, { ready: true });
121386
121560
  markDone(diag, "wallet.balance", startedAt, { hasBalance: getBalanceValue(result) != null, hasSatsBalance: !!getSatsBalance(result), hasTokenBalances: !!getTokenBalances(result) });
121387
121561
  } catch (error) {
121388
121562
  markError(diag, "wallet.balance", startedAt, error);
@@ -121402,6 +121576,7 @@ function createWalletBalance({ wallet, diag, onChange = null }) {
121402
121576
  const reset = () => {
121403
121577
  snapshot = {
121404
121578
  balance: null,
121579
+ displayBalance: null,
121405
121580
  satsBalance: null,
121406
121581
  tokenBalances: new Map,
121407
121582
  balanceReady: false,
@@ -121418,7 +121593,7 @@ function createWalletBalance({ wallet, diag, onChange = null }) {
121418
121593
  setSatsBalanceResult
121419
121594
  };
121420
121595
  }
121421
- var init_balance = () => {};
121596
+ var init_balance2 = () => {};
121422
121597
 
121423
121598
  // ../../shared/wallet/deposits.js
121424
121599
  async function getClaimableDepositUtxos(wallet, options = {}) {
@@ -123196,8 +123371,8 @@ function pendingTransfers(transfers = []) {
123196
123371
  function pendingTransferIds(transfers = []) {
123197
123372
  return pendingTransfers(transfers).map((tx) => String(tx.id));
123198
123373
  }
123199
- function pendingIncomingWalletTransfers(transfers = []) {
123200
- return pendingTransfers(transfers).filter((tx) => isIncomingTransfer(tx) && isWalletTransfer(tx));
123374
+ function pendingIncomingTransfers(transfers = []) {
123375
+ return pendingTransfers(transfers).filter(isIncomingTransfer);
123201
123376
  }
123202
123377
  function uniqueIds(ids = []) {
123203
123378
  return [...new Set((Array.isArray(ids) ? ids : [ids]).map((id) => String(id || "")).filter(Boolean))];
@@ -123292,7 +123467,7 @@ function adaptiveClaimBatchSize(maxBatchSize, queueDepth) {
123292
123467
  }
123293
123468
  return 1;
123294
123469
  }
123295
- function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomingTransfers, pendingClaimBatchSize = null, eagerOutgoingStatusRefresh = false, eagerCacheHistory = true, transferStore, appState = null, deferBackgroundWork = null, backgroundSignal = null, diag }) {
123470
+ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomingTransfers, pendingClaimBatchSize = null, eagerOutgoingStatusRefresh = false, eagerCacheHistory = true, transferStore, appState = null, deferBackgroundWork = null, backgroundSignal = null, refreshBalance = null, diag }) {
123296
123471
  if (!transferStore || typeof transferStore.subscribe !== "function" || typeof transferStore.getSnapshot !== "function" || typeof transferStore.getCacheState !== "function" || typeof transferStore.setHistory !== "function") {
123297
123472
  throw new Error("createWalletTransferSession requires transferStore");
123298
123473
  }
@@ -123357,7 +123532,7 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
123357
123532
  const cacheHydrateKeyRef = { current: "" };
123358
123533
  const cacheHydratePromiseRef = { current: null };
123359
123534
  const fullCacheHydratePromiseRef = { current: null };
123360
- const cacheWriteRef = { current: { timer: null, cache: null, state: null } };
123535
+ const cacheWriteRef = { current: { timer: null, cache: null, state: null, fullHistory: false } };
123361
123536
  const cacheDirtyRef = { current: false };
123362
123537
  const lastFetchAtRef = { current: 0 };
123363
123538
  const claimBatchSize = Math.max(1, Number(pendingClaimBatchSize) || PENDING_TRANSFER_CLAIM_BATCH_SIZE);
@@ -123685,9 +123860,9 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
123685
123860
  return false;
123686
123861
  }
123687
123862
  const cachedTransfers = filterTransfersForWallet(cached.transfers, walletPKRef.current);
123688
- const cachedPendingIncoming = pendingIncomingWalletTransfers(cachedTransfers);
123689
- const cachedPending = pendingTransfers(cachedTransfers);
123690
- const cachedPublishTransfers = cachedTransfers;
123863
+ const cachedPublishTransfers = !publish && historyTransfersRef.current.length ? mergeTransferPageForWallet(historyTransfersRef.current, cachedTransfers, walletPKRef.current, "append") : cachedTransfers;
123864
+ const cachedPendingIncoming = pendingIncomingTransfers(cachedPublishTransfers);
123865
+ const cachedPending = pendingTransfers(cachedPublishTransfers);
123691
123866
  rememberPendingTransferIds(cachedPending.map((tx) => tx.id));
123692
123867
  markDiag(diag, label, {
123693
123868
  elapsedMs: Date.now() - startedAt,
@@ -123826,9 +124001,11 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
123826
124001
  pending.timer = null;
123827
124002
  const cache = pending.cache;
123828
124003
  const state = pending.state;
124004
+ const fullHistory = pending.fullHistory;
123829
124005
  pending.state = null;
123830
124006
  if (cache && state) {
123831
- writeCachedTransferState(cache, state);
124007
+ const write = fullHistory ? writeCachedTransferState : writeCachedTransferHeadState;
124008
+ write(cache, state);
123832
124009
  }
123833
124010
  };
123834
124011
  const queueWalletFetch = (read) => {
@@ -123933,7 +124110,7 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
123933
124110
  return pendingQueryPromiseRef.current;
123934
124111
  };
123935
124112
  const pendingIncomingClaimCandidates = (sourceTransfers = []) => {
123936
- return pendingIncomingWalletTransfers(sourceTransfers).filter((tx) => {
124113
+ return pendingIncomingTransfers(sourceTransfers).filter((tx) => {
123937
124114
  const id = String(tx?.id || "");
123938
124115
  return id && !claimedTransferIdsRef.current.has(id);
123939
124116
  });
@@ -124149,6 +124326,9 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
124149
124326
  batchClaimedIds.push(...staged);
124150
124327
  flushClaimed();
124151
124328
  const uniqueBatchClaimedIds = uniqueIds(batchClaimedIds);
124329
+ if (uniqueBatchClaimedIds.length && typeof refreshBalance === "function") {
124330
+ await refreshBalance();
124331
+ }
124152
124332
  const unclaimedIds = candidateIds.filter((id) => !uniqueBatchClaimedIds.includes(id) && !claimedTransferIdsRef.current.has(id));
124153
124333
  if (unclaimedIds.length) {
124154
124334
  const elapsedMs = Date.now() - startedAt;
@@ -124671,7 +124851,7 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
124671
124851
  try {
124672
124852
  await yieldToUi();
124673
124853
  const pendingTransfers2 = await getPendingTransfersSnapshot();
124674
- const incomingTransfers = pendingIncomingWalletTransfers(pendingTransfers2);
124854
+ const incomingTransfers = pendingIncomingTransfers(pendingTransfers2);
124675
124855
  const claimCandidates = pendingIncomingClaimCandidates(incomingTransfers);
124676
124856
  if (!incomingTransfers.length) {
124677
124857
  markDone(diag, "wallet.pendingTxs.active", startedAt, { pending: 0, claimed: 0, reason });
@@ -125016,7 +125196,7 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
125016
125196
  if (cacheWriteRef.current.timer) {
125017
125197
  clearTimeout(cacheWriteRef.current.timer);
125018
125198
  }
125019
- cacheWriteRef.current = { timer: null, cache: null, state: null };
125199
+ cacheWriteRef.current = { timer: null, cache: null, state: null, fullHistory: false };
125020
125200
  cacheDirtyRef.current = false;
125021
125201
  };
125022
125202
  const startCacheHydration = () => {
@@ -125074,12 +125254,10 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
125074
125254
  if (!cacheDirtyRef.current && !cacheWriteRef.current.timer) {
125075
125255
  return;
125076
125256
  }
125077
- if (historyTransfersRef.current.length < knownHistoryCountRef.current) {
125078
- return;
125079
- }
125080
125257
  cacheDirtyRef.current = false;
125081
125258
  cacheWriteRef.current.cache = localCache;
125082
125259
  cacheWriteRef.current.state = transferStore.getCacheState();
125260
+ cacheWriteRef.current.fullHistory = historyTransfersRef.current.length >= knownHistoryCountRef.current;
125083
125261
  if (cacheWriteRef.current.timer) {
125084
125262
  clearTimeout(cacheWriteRef.current.timer);
125085
125263
  }
@@ -125108,7 +125286,7 @@ function createWalletTransferSession({ wallet, walletPK, localCache, claimIncomi
125108
125286
  serverHistoryComplete: serverHistoryCompleteRef.current,
125109
125287
  hasMoreTxs: !historyComplete,
125110
125288
  hasPendingTxs,
125111
- hasPendingIncomingTxs: pendingIncomingWalletTransfers(transfersRef.current).length > 0,
125289
+ hasPendingIncomingTxs: pendingIncomingTransfers(transfersRef.current).length > 0,
125112
125290
  beginSdkActivity,
125113
125291
  getRecentTxs,
125114
125292
  claimPendingIncomingTransfers: claimPendingIncomingTransfers2,
@@ -125464,6 +125642,7 @@ function createWalletSession({
125464
125642
  deferBackgroundWork = null,
125465
125643
  appState = null,
125466
125644
  transferStore,
125645
+ cachedBalance = null,
125467
125646
  ghostWallet = true,
125468
125647
  diag = null
125469
125648
  }) {
@@ -125481,6 +125660,7 @@ function createWalletSession({
125481
125660
  const txSubscriptions = createFieldSubscriptions(() => publishedSnapshot?.txValue);
125482
125661
  let polling = null;
125483
125662
  let readyDiag = null;
125663
+ let refreshWalletBalance = null;
125484
125664
  const transferSession = createWalletTransferSession({
125485
125665
  wallet,
125486
125666
  walletPK,
@@ -125493,6 +125673,7 @@ function createWalletSession({
125493
125673
  appState,
125494
125674
  deferBackgroundWork,
125495
125675
  backgroundSignal: backgroundAbort.signal,
125676
+ refreshBalance: () => refreshWalletBalance?.(),
125496
125677
  diag
125497
125678
  });
125498
125679
  const getTransferState = () => transferSession.getSnapshot();
@@ -125512,7 +125693,16 @@ function createWalletSession({
125512
125693
  listener();
125513
125694
  }
125514
125695
  };
125515
- const balance = createWalletBalance({ wallet, diag, onChange: publish });
125696
+ const balance = createWalletBalance({
125697
+ wallet,
125698
+ diag,
125699
+ initialDisplayBalance: cachedBalance,
125700
+ onBalance: (value) => {
125701
+ writeCachedWalletBalance(localCache, { walletPK, network, balance: value }).catch(() => {});
125702
+ },
125703
+ onChange: publish
125704
+ });
125705
+ refreshWalletBalance = balance.getBalance;
125516
125706
  const funding = createFundingAddress({ wallet, network, diag, onChange: publish });
125517
125707
  const updateWalletData = createWalletData({
125518
125708
  wallet,
@@ -125593,6 +125783,7 @@ function createWalletSession({
125593
125783
  wallet,
125594
125784
  network,
125595
125785
  balance: balanceState.balance,
125786
+ displayBalance: balanceState.displayBalance,
125596
125787
  satsBalance: balanceState.satsBalance,
125597
125788
  tokenBalances: balanceState.tokenBalances,
125598
125789
  fundingAddress: funding.getSnapshot(),
@@ -125691,7 +125882,7 @@ function createWalletSession({
125691
125882
  };
125692
125883
  }
125693
125884
  var init_session4 = __esm(() => {
125694
- init_balance();
125885
+ init_balance2();
125695
125886
  init_claims();
125696
125887
  init_funding();
125697
125888
  init_lightning();
@@ -125700,6 +125891,7 @@ var init_session4 = __esm(() => {
125700
125891
  init_send2();
125701
125892
  init_transfers2();
125702
125893
  init_withdraw();
125894
+ init_localdata();
125703
125895
  });
125704
125896
 
125705
125897
  // ../../shared/wallet/transferstore.js
@@ -125908,6 +126100,7 @@ function createTransferStore() {
125908
126100
  transfers: state.historyTransfers,
125909
126101
  walletPK: state.walletPK,
125910
126102
  serverEndReached: state.serverEndReached,
126103
+ historyCount: state.historyCount,
125911
126104
  nextOffset: state.nextOffset,
125912
126105
  oldestTxMs: state.serverEndReached ? state.oldestVerifiedTxMs : null
125913
126106
  };
@@ -169391,9 +169584,13 @@ function peerRecordFromDoc(snap) {
169391
169584
  function peerRecordsFromSnapshot(snapshot) {
169392
169585
  return snapshot.docs.map(peerRecordFromDoc).filter(Boolean);
169393
169586
  }
169394
- function avatarPath(uid) {
169587
+ function avatarPath(uid, version8) {
169395
169588
  requireUid(uid);
169396
- return `${uid}/avatar.webp`;
169589
+ const avatarVersion = readAvatarVersion(version8);
169590
+ if (!avatarVersion) {
169591
+ throw new Error("avatar version required");
169592
+ }
169593
+ return `${uid}/avatars/${avatarVersion}.webp`;
169397
169594
  }
169398
169595
  function reportEvidencePath(reporter, targetUid, evidenceId) {
169399
169596
  if (!reporter || !targetUid || !evidenceId) {
@@ -169459,6 +169656,7 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
169459
169656
  }
169460
169657
  let activeVaultSigner = null;
169461
169658
  const localSessionTerminations = new Set;
169659
+ const pendingAvatars = new Map;
169462
169660
  function pageTs(value) {
169463
169661
  if (value instanceof Timestamp2) {
169464
169662
  return value;
@@ -169727,6 +169925,7 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
169727
169925
  }, onError);
169728
169926
  }
169729
169927
  async function logout() {
169928
+ pendingAvatars.clear();
169730
169929
  await signOut(requireAuth());
169731
169930
  return true;
169732
169931
  }
@@ -169860,25 +170059,45 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
169860
170059
  await callFunction("acceptAgreement");
169861
170060
  return true;
169862
170061
  }
169863
- async function writeProfileAvatar(uid, avatar) {
170062
+ async function setProfileAvatar(uid, data) {
169864
170063
  requireUid(uid);
169865
- await updateDoc(doc(db, "profiles", uid), { avatar });
169866
- return true;
169867
- }
169868
- async function uploadProfileAvatar(uid, data, { contentType = "image/webp" } = {}) {
169869
- const targetStorage = requireStorage2();
169870
- const path = avatarPath(uid);
169871
- const result = typeof uploadStorageBytes === "function" ? await uploadStorageBytes(targetStorage, path, data, { contentType }) : await uploadBytes(ref(targetStorage, path), typeof Blob !== "undefined" && data instanceof Blob ? data : toBytes(data, "upload bytes"), { contentType });
169872
- const url = await getDownloadURL(ref(targetStorage, path));
169873
- return {
169874
- url,
169875
- generation: result?.metadata?.generation ?? result?.generation ?? null
169876
- };
169877
- }
169878
- async function deleteProfileAvatar(uid) {
169879
- const targetStorage = requireStorage2();
169880
- await deleteObject(ref(targetStorage, avatarPath(uid)));
169881
- return true;
170064
+ if (requireAuth().currentUser?.uid !== uid) {
170065
+ throw new Error("avatar account mismatch");
170066
+ }
170067
+ if (data == null) {
170068
+ pendingAvatars.delete(uid);
170069
+ const result = await callFunction("setAvatar", { body: null });
170070
+ if (result?.avatar != null) {
170071
+ throw new Error("avatar clear returned invalid state");
170072
+ }
170073
+ return { version: null };
170074
+ }
170075
+ const bytes = new Uint8Array(encryptedBytes(data, "avatar bytes"));
170076
+ const pending = { bytes, version: null, promise: null };
170077
+ const entries = pendingAvatars.get(uid) || [];
170078
+ pendingAvatars.set(uid, [...entries, pending]);
170079
+ pending.promise = callFunction("setAvatar", {
170080
+ body: cloudBytesBase64(bytes, "avatar bytes")
170081
+ }).then((result) => {
170082
+ const version8 = readAvatarVersion(result?.avatar);
170083
+ if (!version8) {
170084
+ throw new Error("avatar publish returned invalid version");
170085
+ }
170086
+ pending.version = version8;
170087
+ return version8;
170088
+ });
170089
+ try {
170090
+ return { version: await pending.promise };
170091
+ } catch (error) {
170092
+ const active = pendingAvatars.get(uid) || [];
170093
+ const remaining = active.filter((entry) => entry !== pending);
170094
+ if (remaining.length) {
170095
+ pendingAvatars.set(uid, remaining);
170096
+ } else {
170097
+ pendingAvatars.delete(uid);
170098
+ }
170099
+ throw error;
170100
+ }
169882
170101
  }
169883
170102
  async function writeProfileIdentity(identity) {
169884
170103
  if (!identity || typeof identity !== "object" || Array.isArray(identity)) {
@@ -170015,13 +170234,28 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
170015
170234
  onUpdate?.(snap.exists() ? snap.data()?.active === true : false, { exists: snap.exists() });
170016
170235
  }, onError);
170017
170236
  }
170018
- async function readPeerAvatar(uid) {
170019
- return readStorageFile(avatarPath(uid));
170237
+ async function readPeerAvatar(uid, { version: version8 = null } = {}) {
170238
+ const avatarVersion = readAvatarVersion(version8);
170239
+ const entries = pendingAvatars.get(uid) || [];
170240
+ if (entries.length) {
170241
+ await Promise.allSettled(entries.map((entry) => entry.promise));
170242
+ const pending = entries.find((entry) => entry.version === avatarVersion);
170243
+ if (pending) {
170244
+ const active = pendingAvatars.get(uid) || [];
170245
+ const remaining = active.filter((entry) => entry !== pending);
170246
+ if (remaining.length) {
170247
+ pendingAvatars.set(uid, remaining);
170248
+ } else {
170249
+ pendingAvatars.delete(uid);
170250
+ }
170251
+ return new Uint8Array(pending.bytes);
170252
+ }
170253
+ }
170254
+ return readStorageFile(avatarPath(uid, avatarVersion));
170020
170255
  }
170021
170256
  async function peerAvatarUrl(uid, { version: version8 = null } = {}) {
170022
170257
  const targetStorage = requireStorage2();
170023
- const url = await getDownloadURL(ref(targetStorage, avatarPath(uid)));
170024
- return avatarUrlWithVersion(url, version8);
170258
+ return getDownloadURL(ref(targetStorage, avatarPath(uid, version8)));
170025
170259
  }
170026
170260
  function profileQueryField(field2, { network } = {}) {
170027
170261
  if (field2 === "walletPK") {
@@ -170480,12 +170714,13 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
170480
170714
  }
170481
170715
  };
170482
170716
  }
170483
- async function pushInbox(recipientUid, ping) {
170717
+ async function pushInbox(recipientUid, ping, options2 = {}) {
170484
170718
  requireUid(recipientUid);
170485
170719
  if (!ping)
170486
170720
  throw new Error("inbox ping required");
170487
170721
  await callFunction("push", {
170488
170722
  recipientUid,
170723
+ notify: options2?.notify !== false,
170489
170724
  ping: {
170490
170725
  v: ping.v,
170491
170726
  epk: ping.epk,
@@ -170524,7 +170759,9 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
170524
170759
  payload.onCommitted?.({ chatId, messageId });
170525
170760
  let delivered = true;
170526
170761
  if (payload.inbox?.recipientUid && payload.inbox?.ping) {
170527
- delivered = await pushInbox(payload.inbox.recipientUid, payload.inbox.ping).then(() => true, () => false);
170762
+ delivered = await pushInbox(payload.inbox.recipientUid, payload.inbox.ping, {
170763
+ notify: payload.inbox.notify !== false
170764
+ }).then(() => true, () => false);
170528
170765
  }
170529
170766
  return { chatId, messageId, delivered };
170530
170767
  }
@@ -170777,18 +171014,19 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
170777
171014
  }
170778
171015
  async function banAdminUser(uid, feature = "chat") {
170779
171016
  requireUid(uid);
170780
- await setDoc(doc(db, "moderation", uid), { banned: { [feature]: { until: null } } }, { merge: true });
170781
171017
  if (feature === "avatar") {
170782
- await deleteProfileAvatar(uid).catch((error) => {
170783
- if (error?.code !== "storage/object-not-found") {
170784
- throw error;
170785
- }
170786
- });
171018
+ await callFunction("setAvatarModeration", { uid, banned: true });
171019
+ return true;
170787
171020
  }
171021
+ await setDoc(doc(db, "moderation", uid), { banned: { [feature]: { until: null } } }, { merge: true });
170788
171022
  return true;
170789
171023
  }
170790
171024
  async function unbanAdminUser(uid, feature = "chat") {
170791
171025
  requireUid(uid);
171026
+ if (feature === "avatar") {
171027
+ await callFunction("setAvatarModeration", { uid, banned: false });
171028
+ return true;
171029
+ }
170792
171030
  await setDoc(doc(db, "moderation", uid), { banned: { [feature]: deleteField() } }, { merge: true });
170793
171031
  return true;
170794
171032
  }
@@ -170826,9 +171064,7 @@ function createFirebaseCloud({ db, auth: auth2, getAuth: getAuth2, functions: fu
170826
171064
  write: writeProfileIdentity
170827
171065
  },
170828
171066
  avatar: {
170829
- write: writeProfileAvatar,
170830
- upload: uploadProfileAvatar,
170831
- delete: deleteProfileAvatar
171067
+ set: setProfileAvatar
170832
171068
  }
170833
171069
  },
170834
171070
  private: {
@@ -173418,13 +173654,6 @@ function cleanMessage(value, limit2, label = "message") {
173418
173654
  throw new Error(`${label} must be ${limit2} characters or fewer`);
173419
173655
  return message;
173420
173656
  }
173421
- function readAvatarGeneration(value) {
173422
- const version8 = Number(value);
173423
- if (!Number.isSafeInteger(version8) || version8 <= 0) {
173424
- throw new Error("avatar upload did not return a valid generation");
173425
- }
173426
- return version8;
173427
- }
173428
173657
  function passkeyRows(linksList, passkeys, currentPasskeyId) {
173429
173658
  return [...linksList, ...passkeys].sort((left, right) => (right.createdAt || 0) - (left.createdAt || 0)).map((row) => ({
173430
173659
  ...row,
@@ -173512,23 +173741,17 @@ function createRuntimeProductActions({
173512
173741
  const user = await getUser();
173513
173742
  if (!data)
173514
173743
  throw new Error("avatar data required");
173515
- const result = await cloud.user.profile.avatar.upload(user.uid, data, {
173516
- contentType: options2.contentType || "image/webp"
173517
- });
173518
- const avatarVersion = readAvatarGeneration(result?.generation);
173519
- await cloud.user.profile.avatar.write(user.uid, avatarVersion);
173520
- await user.refetchAvatar?.({ version: avatarVersion, optimistic: true });
173521
- return { avatar: result?.url || null, avatarVersion };
173744
+ if (options2.contentType && options2.contentType !== "image/webp") {
173745
+ throw new Error("avatar content type must be image/webp");
173746
+ }
173747
+ const result = await cloud.user.profile.avatar.set(user.uid, data);
173748
+ await user.refetchAvatar?.({ version: result.version });
173749
+ const updated = await getUser();
173750
+ return { avatar: updated.avatar || null, avatarVersion: result.version };
173522
173751
  },
173523
173752
  async deleteAvatar() {
173524
173753
  const user = await getUser();
173525
- try {
173526
- await cloud.user.profile.avatar.delete(user.uid);
173527
- } catch (error) {
173528
- if (error?.code !== "storage/object-not-found")
173529
- throw error;
173530
- }
173531
- await cloud.user.profile.avatar.write(user.uid, null);
173754
+ await cloud.user.profile.avatar.set(user.uid, null);
173532
173755
  user.clearAvatar?.();
173533
173756
  return { deleted: true };
173534
173757
  }
@@ -174704,6 +174927,34 @@ class ProfileStore {
174704
174927
  var FILE_MODE2 = 384, DIR_MODE2 = 448;
174705
174928
  var init_storage = () => {};
174706
174929
 
174930
+ // ../../shared/actioncopy.js
174931
+ var ACCOUNT_DELETE_PREPARE = "you should export or empty your wallet first.", ACCOUNT_DELETE_CONDITION = "if you delete your account,", ACCOUNT_DELETE_CONSEQUENCE = "you will permanently lose access to your funds and chats.", PASSKEY_DELETE_PENDING = "deleting a pending link disables it.", PASSKEY_DELETE_VERIFIED = "deleting a verified passkey will log out any session currently using it.", PASSKEY_DELETE_LIMITS = "you can’t delete the passkey used by this session or the account’s last passkey.", ACCOUNT_DELETE_COPY, BLOCK_USER_BODY = "they will no longer be able to message you, and this chat will be permanently deleted.", UNBLOCK_USER_BODY = "they will be able to message you again.", CHAT_DELETE_BODY = "this chat will be permanently deleted.", PASSKEY_DELETE_COPY, WALLET_EXPORT_COPY, WITHDRAWAL_COPY, LOGOUT_ALL_DEVICES_BODY = "This will sign your account out on every connected device. If you suspect someone else has access, logging out may not be enough: they may already have copied your wallet recovery phrase or other sensitive information. Move your funds to a safe wallet only you control as soon as possible, then remove sensitive information from this account. Information already copied to another device cannot be erased remotely.";
174932
+ var init_actioncopy = __esm(() => {
174933
+ ACCOUNT_DELETE_COPY = Object.freeze({
174934
+ prepare: ACCOUNT_DELETE_PREPARE,
174935
+ condition: ACCOUNT_DELETE_CONDITION,
174936
+ consequence: ACCOUNT_DELETE_CONSEQUENCE,
174937
+ body: `${ACCOUNT_DELETE_PREPARE} ${ACCOUNT_DELETE_CONDITION} ${ACCOUNT_DELETE_CONSEQUENCE}`
174938
+ });
174939
+ PASSKEY_DELETE_COPY = Object.freeze({
174940
+ pendingBody: PASSKEY_DELETE_PENDING,
174941
+ body: PASSKEY_DELETE_VERIFIED,
174942
+ description: `${PASSKEY_DELETE_PENDING} ${PASSKEY_DELETE_VERIFIED}`,
174943
+ infoBody: `${PASSKEY_DELETE_PENDING} ${PASSKEY_DELETE_LIMITS} ${PASSKEY_DELETE_VERIFIED}`
174944
+ });
174945
+ WALLET_EXPORT_COPY = Object.freeze({
174946
+ networkLead: "this is not a bitcoin wallet.",
174947
+ networkBody: "you cannot use it like a normal bitcoin wallet. you can only use it with the spark network: with a new account on this platform, on a different platform that uses spark wallets, or directly through the spark sdk.",
174948
+ withdrawBody: "if you do not want to use this account anymore, withdraw your funds back to a bitcoin wallet instead.",
174949
+ secretBody: "the mnemonic gives full control of this Spark wallet. reveal it only somewhere private."
174950
+ });
174951
+ WITHDRAWAL_COPY = Object.freeze({
174952
+ feeBody: "you can withdraw your funds back to any bitcoin address. bitcoin transactions are not free. validators need to get paid.",
174953
+ reviewBody: "veyl gets a live Spark withdrawal quote after you press withdraw. review the destination address, the amount leaving your wallet, the quoted fee, and the amount that reaches the address before confirming.",
174954
+ irreversibleBody: "once confirmed, bitcoin withdrawals are irreversible."
174955
+ });
174956
+ });
174957
+
174707
174958
  // src/commands.js
174708
174959
  import { Buffer as Buffer4 } from "node:buffer";
174709
174960
  import { chmod as chmod3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
@@ -174871,6 +175122,7 @@ function cliHelpLines() {
174871
175122
  }
174872
175123
  var stringProp, numberProp, booleanProp, objectProp, stringArrayProp, MIME_BY_EXTENSION, TYPE_BY_MIME, optionsOnly = (_args, context) => ({ ...context.commandOptions }), firstArg = (key) => (args, context) => ({ ...context.commandOptions, [key]: args[0] }), peerInput, peerMessageInput = (args, context) => ({ ...context.commandOptions, peer: args[0], message: args.slice(1).join(" ") }), peerMessageIdInput = (args, context) => ({ ...context.commandOptions, peer: args[0], messageId: args[1] }), peerCidInput = (args, context) => ({ ...context.commandOptions, peer: args[0], cid: args[1] }), peerMessageActionInput = (args, context) => ({ ...context.commandOptions, peer: args[0], messageId: args[1], message: args.slice(2).join(" ") }), peerEmojiInput = (args, context) => ({ ...context.commandOptions, peer: args[0], messageId: args[1], emoji: args.slice(2).join(" ") }), peerSatsInput = (args, context) => ({ ...context.commandOptions, peer: args[0], sats: args[1] }), invoiceInput, requestInput, idInput, withdrawalInput = (args, context) => ({ ...context.commandOptions, address: args[0], sats: args[1] }), inviteLinkInput = (args, context) => ({ ...context.commandOptions, kind: args[0], sats: args[1] }), HEADLESS_COMMANDS, commandByName, cliPaths, MCP_TOOLS, groups, COMMANDS;
174873
175124
  var init_commands = __esm(() => {
175125
+ init_actioncopy();
174874
175126
  stringProp = Object.freeze({ type: "string" });
174875
175127
  numberProp = Object.freeze({ type: "number" });
174876
175128
  booleanProp = Object.freeze({ type: "boolean" });
@@ -174947,12 +175199,12 @@ var init_commands = __esm(() => {
174947
175199
  invokeCli: async (client2, input, context) => client2.account.acceptTerms({ ...input, termsVersion: await context.acceptTerms(input.termsVersion) })
174948
175200
  }),
174949
175201
  command({ name: "account_logout", group: "account", apiName: "logout", description: "Lock and sign out this runtime.", usage: "account logout", cliInput: optionsOnly, access: "write", invoke: (client2) => client2.account.logout() }),
174950
- command({ name: "account_logout_all", group: "account", apiName: "logoutAll", description: "Revoke all product auth sessions.", usage: "account logout-all", cliInput: optionsOnly, access: "destructive", invoke: (client2) => client2.account.logoutAll() }),
174951
- command({ name: "account_delete", group: "account", apiName: "delete", description: "Delete the account, chats, and local profile.", properties: { confirm: booleanProp }, required: ["confirm"], usage: "account delete", cliInput: optionsOnly, access: "destructive", invoke: (client2, input) => client2.account.delete(input) }),
175202
+ command({ name: "account_logout_all", group: "account", apiName: "logoutAll", description: LOGOUT_ALL_DEVICES_BODY, usage: "account logout-all", cliInput: optionsOnly, access: "destructive", invoke: (client2) => client2.account.logoutAll() }),
175203
+ command({ name: "account_delete", group: "account", apiName: "delete", description: ACCOUNT_DELETE_COPY.body, properties: { confirm: booleanProp }, required: ["confirm"], usage: "account delete", cliInput: optionsOnly, access: "destructive", invoke: (client2, input) => client2.account.delete(input) }),
174952
175204
  command({ name: "vault_create", group: "vault", apiName: "create", description: "Create and upload a locally encrypted vault.", properties: { secret: stringProp, saveSecret: booleanProp }, usage: "vault create", cliInput: vaultInput, access: "write", secret: true, invoke: (client2, input) => client2.vault.create(input) }),
174953
175205
  command({ name: "vault_unlock", group: "vault", apiName: "unlock", description: "Unlock the local vault.", properties: { secret: stringProp }, usage: "vault unlock", cliInput: vaultInput, access: "write", secret: true, invoke: (client2, input) => client2.vault.unlock(input) }),
174954
175206
  command({ name: "vault_lock", group: "vault", apiName: "lock", description: "Lock wallet and chat state.", usage: "vault lock", cliInput: optionsOnly, access: "write", invoke: (client2) => client2.vault.lock() }),
174955
- command({ name: "vault_export", group: "vault", apiName: "export", description: "Export the Spark wallet mnemonic.", properties: { secret: stringProp }, usage: "vault export", cliInput: vaultInput, mcp: false, secret: true, invoke: (client2, input) => client2.vault.export(input) }),
175207
+ command({ name: "vault_export", group: "vault", apiName: "export", description: WALLET_EXPORT_COPY.secretBody, properties: { secret: stringProp }, usage: "vault export", cliInput: vaultInput, mcp: false, secret: true, invoke: (client2, input) => client2.vault.export(input) }),
174956
175208
  command({ name: "profile_show", group: "profile", apiName: "show", description: "Show the public account profile.", usage: "profile show", cliInput: optionsOnly, invoke: (client2) => client2.profile.show() }),
174957
175209
  command({
174958
175210
  name: "profile_avatar_set",
@@ -174978,8 +175230,8 @@ var init_commands = __esm(() => {
174978
175230
  command({ name: "peers_search", group: "peers", apiName: "search", description: "Search public Veyl profiles.", properties: { query: stringProp, count: numberProp }, required: ["query"], usage: "peers search <query>", cliInput: (args, context) => ({ ...context.commandOptions, query: args.join(" ") }), invoke: (client2, input) => client2.peers.search(input.query, input) }),
174979
175231
  command({ name: "peers_list", group: "peers", apiName: "list", description: "List recent peers.", properties: { count: numberProp, recent: booleanProp }, usage: "peers list", cliInput: optionsOnly, invoke: (client2, input) => client2.peers.list(input) }),
174980
175232
  command({ name: "peers_blocked", group: "peers", apiName: "blocked", description: "List blocked peers.", usage: "peers blocked", cliInput: optionsOnly, invoke: (client2) => client2.peers.blocked() }),
174981
- command({ name: "peers_block", group: "peers", apiName: "block", description: "Block a peer and hide their local chat.", properties: { peer: stringProp }, required: ["peer"], usage: "peers block @name", cliInput: peerInput, access: "destructive", invoke: (client2, input) => client2.peers.block(input.peer) }),
174982
- command({ name: "peers_unblock", group: "peers", apiName: "unblock", description: "Unblock a peer.", properties: { peer: stringProp }, required: ["peer"], usage: "peers unblock @name", cliInput: peerInput, access: "write", invoke: (client2, input) => client2.peers.unblock(input.peer) }),
175233
+ command({ name: "peers_block", group: "peers", apiName: "block", description: BLOCK_USER_BODY, properties: { peer: stringProp }, required: ["peer"], usage: "peers block @name", cliInput: peerInput, access: "destructive", invoke: (client2, input) => client2.peers.block(input.peer) }),
175234
+ command({ name: "peers_unblock", group: "peers", apiName: "unblock", description: UNBLOCK_USER_BODY, properties: { peer: stringProp }, required: ["peer"], usage: "peers unblock @name", cliInput: peerInput, access: "write", invoke: (client2, input) => client2.peers.unblock(input.peer) }),
174983
175235
  command({ name: "chat_list", group: "chat", apiName: "list", description: "List chats.", properties: { count: numberProp }, usage: "chat list", cliInput: optionsOnly, invoke: (client2, input) => client2.chat.list(input) }),
174984
175236
  command({ name: "chat_messages", group: "chat", apiName: "messages", clientMethods: ["read"], description: "Read messages and mark the latest peer message as read.", properties: { peer: stringProp, count: numberProp }, required: ["peer"], usage: "chat messages @name", cliInput: peerInput, access: "write", invoke: (client2, input) => client2.chat.read(input.peer, input) }),
174985
175237
  command({ name: "chat_mark_read", group: "chat", apiName: "markRead", description: "Mark the latest peer message as read.", properties: { peer: stringProp }, required: ["peer"], usage: "chat mark-read @name", cliInput: peerInput, access: "write", invoke: (client2, input) => client2.chat.markRead(input.peer, input) }),
@@ -174992,7 +175244,7 @@ var init_commands = __esm(() => {
174992
175244
  command({ name: "chat_unsave", group: "chat", apiName: "unsave", description: "Return one message to temporary retention.", properties: { peer: stringProp, messageId: stringProp }, required: ["peer", "messageId"], usage: "chat unsave @name message-id", cliInput: peerMessageIdInput, access: "write", invoke: (client2, { peer, messageId, ...options2 }) => client2.chat.unsave(peer, messageId, options2) }),
174993
175245
  command({ name: "chat_edit", group: "chat", apiName: "edit", clientMethods: ["update"], description: "Edit a text message you sent.", properties: { peer: stringProp, messageId: stringProp, message: stringProp }, required: ["peer", "messageId", "message"], usage: "chat edit @name message-id message", cliInput: peerMessageActionInput, access: "write", invoke: (client2, { peer, messageId, message, ...options2 }) => client2.chat.update(peer, messageId, message, options2) }),
174994
175246
  command({ name: "chat_delete_message", group: "chat", apiName: "deleteMessage", clientMethods: ["delete"], description: "Delete one message immediately.", properties: { peer: stringProp, messageId: stringProp }, required: ["peer", "messageId"], usage: "chat delete-message @name message-id", cliInput: peerMessageIdInput, access: "destructive", invoke: (client2, { peer, messageId, ...options2 }) => client2.chat.delete(peer, messageId, options2) }),
174995
- command({ name: "chat_delete", group: "chat", apiName: "deleteChat", description: "Delete one chat.", properties: { peer: stringProp, cleanup: booleanProp }, required: ["peer"], usage: "chat delete @name", cliInput: peerInput, access: "destructive", invoke: (client2, input) => client2.chat.deleteChat(input.peer, input) }),
175247
+ command({ name: "chat_delete", group: "chat", apiName: "deleteChat", description: CHAT_DELETE_BODY, properties: { peer: stringProp, cleanup: booleanProp }, required: ["peer"], usage: "chat delete @name", cliInput: peerInput, access: "destructive", invoke: (client2, input) => client2.chat.deleteChat(input.peer, input) }),
174996
175248
  command({ name: "chat_retention", group: "chat", apiName: "retention", description: "Set chat retention to seen or 24h.", properties: { peer: stringProp, retention: stringProp }, required: ["peer", "retention"], usage: "chat retention @name seen|24h", cliInput: (args, context) => ({ ...context.commandOptions, peer: args[0], retention: args[1] }), access: "write", invoke: (client2, input) => client2.chat.retention(input.peer, input.retention, input) }),
174997
175249
  command({
174998
175250
  name: "chat_send_file",
@@ -175077,12 +175329,12 @@ var init_commands = __esm(() => {
175077
175329
  command({ name: "lightning_send", group: "lightning", apiName: "send", description: "Read a Lightning send request.", properties: { id: stringProp }, required: ["id"], usage: "lightning send id", cliInput: idInput, money: true, invoke: (client2, input) => client2.lightning.send(input.id, input) }),
175078
175330
  command({ name: "withdrawal_quote", group: "withdrawal", apiName: "quote", description: "Quote an on-chain withdrawal.", properties: { address: stringProp, sats: numberProp }, required: ["address", "sats"], usage: "withdrawal quote address sats", cliInput: withdrawalInput, access: "write", money: true, invoke: (client2, input) => client2.withdrawal.quote(input.address, input.sats, input) }),
175079
175331
  command({ name: "withdrawal_prepare", group: "withdrawal", apiName: "prepare", description: "Prepare an on-chain withdrawal review.", properties: { address: stringProp, sats: numberProp, speed: stringProp, deductFeeFromWithdrawalAmount: booleanProp }, required: ["address", "sats"], usage: "withdrawal prepare address sats", cliInput: withdrawalInput, access: "write", money: true, invoke: (client2, input) => client2.withdrawal.prepare(input.address, input.sats, input) }),
175080
- command({ name: "withdrawal_confirm", group: "withdrawal", apiName: "confirm", description: "Confirm an on-chain withdrawal.", properties: { address: stringProp, sats: numberProp, speed: stringProp, feeQuoteId: stringProp, feeAmountSats: numberProp, deductFeeFromWithdrawalAmount: booleanProp }, required: ["address", "sats"], usage: "withdrawal confirm address sats", cliInput: withdrawalInput, access: "destructive", money: true, invoke: (client2, input) => client2.withdrawal.confirm(input.address, input.sats, input) }),
175332
+ command({ name: "withdrawal_confirm", group: "withdrawal", apiName: "confirm", description: WITHDRAWAL_COPY.irreversibleBody, properties: { address: stringProp, sats: numberProp, speed: stringProp, feeQuoteId: stringProp, feeAmountSats: numberProp, deductFeeFromWithdrawalAmount: booleanProp }, required: ["address", "sats"], usage: "withdrawal confirm address sats", cliInput: withdrawalInput, access: "destructive", money: true, invoke: (client2, input) => client2.withdrawal.confirm(input.address, input.sats, input) }),
175081
175333
  command({ name: "invite_link", group: "invite", apiName: "link", description: "Create a join, chat, send, or request invite link.", properties: { kind: stringProp, sats: numberProp }, usage: "invite link [join|chat|send|request] [sats]", cliInput: inviteLinkInput, invoke: (client2, input) => client2.invite.link(input) }),
175082
175334
  command({ name: "invite_read", group: "invite", apiName: "read", description: "Parse a Veyl invite URL.", properties: { value: stringProp }, required: ["value"], usage: "invite read url", cliInput: firstArg("value"), invoke: (client2, input) => client2.invite.read(input.value) }),
175083
175335
  command({ name: "passkeys_list", group: "passkeys", apiName: "list", description: "List passkeys and pending links.", usage: "passkeys list", cliInput: optionsOnly, invoke: (client2, input) => client2.passkeys.list(input) }),
175084
175336
  command({ name: "passkeys_link", group: "passkeys", apiName: "link", clientMethods: ["createLink"], description: "Create a one-time passkey link.", properties: { webUrl: stringProp }, usage: "passkeys link", cliInput: optionsOnly, mcp: false, access: "write", secret: true, invoke: (client2, input) => client2.passkeys.createLink(input) }),
175085
- command({ name: "passkeys_delete", group: "passkeys", apiName: "delete", description: "Delete a pending link or verified passkey.", properties: { id: stringProp, webUrl: stringProp }, required: ["id"], usage: "passkeys delete id", cliInput: (args, context) => ({ ...context.commandOptions, id: args[0], webUrl: context.options.webUrl, onUrl: context.onPasskeyUrl }), access: "destructive", invoke: (client2, input) => client2.passkeys.delete(input.id, input) }),
175337
+ command({ name: "passkeys_delete", group: "passkeys", apiName: "delete", description: PASSKEY_DELETE_COPY.description, properties: { id: stringProp, webUrl: stringProp }, required: ["id"], usage: "passkeys delete id", cliInput: (args, context) => ({ ...context.commandOptions, id: args[0], webUrl: context.options.webUrl, onUrl: context.onPasskeyUrl }), access: "destructive", invoke: (client2, input) => client2.passkeys.delete(input.id, input) }),
175086
175338
  command({ name: "support_feedback", group: "support", apiName: "feedback", description: "Send product feedback.", properties: { message: stringProp, type: stringProp, context: stringProp }, required: ["message"], usage: "support feedback message", cliInput: (args, context) => ({ ...context.commandOptions, message: args.join(" ") }), access: "write", invoke: (client2, input) => client2.support.feedback(input.message, input) }),
175087
175339
  command({ name: "support_bug", group: "support", apiName: "bug", description: "Submit a bug report.", properties: { message: stringProp }, required: ["message"], usage: "support bug message", cliInput: (args, context) => ({ ...context.commandOptions, message: args.join(" ") }), access: "write", invoke: (client2, input) => client2.support.bug(input.message, input) }),
175088
175340
  command({ name: "support_report", group: "support", apiName: "report", description: "Report a peer or one harmful message.", properties: { peer: stringProp, messageId: stringProp, note: stringProp, includeAttachment: booleanProp }, required: ["peer"], usage: "support report @name [message-id]", cliInput: (args, context) => ({ ...context.commandOptions, peer: args[0], messageId: args[1] }), access: "write", invoke: (client2, input) => client2.support.report(input.peer, input) }),
@@ -175121,7 +175373,7 @@ var package_default;
175121
175373
  var init_package = __esm(() => {
175122
175374
  package_default = {
175123
175375
  name: "veyl",
175124
- version: "0.35.0",
175376
+ version: "0.41.0",
175125
175377
  private: true,
175126
175378
  license: "Apache-2.0",
175127
175379
  workspaces: {