@ghostlytools/cli 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +6 -6
  2. package/dist/assets/{api-aup3Amud.js → api-B1fADtqd.js} +65 -20
  3. package/dist/assets/api-B1fADtqd.js.map +1 -0
  4. package/dist/assets/{api-SYPJgFRU.js → api-Bg7bh_6E.js} +2 -2
  5. package/dist/assets/{apiKit-DYkVEs6r.js → apiKit-BsUZPHLn.js} +21 -6
  6. package/dist/assets/apiKit-BsUZPHLn.js.map +1 -0
  7. package/dist/assets/{apps-Rbk2HHXb.js → apps-BaUNt-5c.js} +91 -24
  8. package/dist/assets/apps-BaUNt-5c.js.map +1 -0
  9. package/dist/assets/{apps-DcZKu29s.js → apps-DepomCiX.js} +1 -1
  10. package/dist/assets/{arkade-CuWlNnCz.js → arkade-CNnMaoEQ.js} +2 -2
  11. package/dist/assets/{arkade-CuWlNnCz.js.map → arkade-CNnMaoEQ.js.map} +1 -1
  12. package/dist/assets/{backup-CSMw2GHv.js → backup-B5Q1W-Ey.js} +1 -1
  13. package/dist/assets/{backup-CJWLDHuS.js → backup-Bbz3lyC5.js} +6 -6
  14. package/dist/assets/{backup-CJWLDHuS.js.map → backup-Bbz3lyC5.js.map} +1 -1
  15. package/dist/assets/bark_ffi_wasm_bg-EY2uc0Bx.js.map +1 -1
  16. package/dist/assets/buttons-CUd7j0ZA.js +2 -0
  17. package/dist/assets/{buttons-QEnAGWoC.js → buttons-DS1iURp8.js} +4 -4
  18. package/dist/assets/{buttons-QEnAGWoC.js.map → buttons-DS1iURp8.js.map} +1 -1
  19. package/dist/assets/{envelope-K27mzoyu.js → envelope-fNC941z4.js} +2 -2
  20. package/dist/assets/{envelope-K27mzoyu.js.map → envelope-fNC941z4.js.map} +1 -1
  21. package/dist/assets/{errors-DO5z6h_l.js → errors-B-zMxbcd.js} +6 -5
  22. package/dist/assets/{errors-DO5z6h_l.js.map → errors-B-zMxbcd.js.map} +1 -1
  23. package/dist/assets/fedimint_client_wasm_bg-BtHHlFpp.js.map +1 -1
  24. package/dist/assets/{ghostly_iroh_web-Dvzh3Y6E.js → ghostly_iroh_web-BsAEFIjl.js} +30 -36
  25. package/dist/assets/ghostly_iroh_web-BsAEFIjl.js.map +1 -0
  26. package/dist/assets/ghostly_iroh_web_bg.wasm +0 -0
  27. package/dist/assets/{groupFiles-DHTrTA4I.js → groupFiles-6qnLCnOm.js} +47 -21
  28. package/dist/assets/{groupFiles-DHTrTA4I.js.map → groupFiles-6qnLCnOm.js.map} +1 -1
  29. package/dist/assets/{host-C7hA-Za7.js → host-Dj3C1fn3.js} +211 -42
  30. package/dist/assets/host-Dj3C1fn3.js.map +1 -0
  31. package/dist/assets/{hyperdhtRelay-BVoWY2ua.js → hyperdhtRelay-CrgYQN-g.js} +2 -2
  32. package/dist/assets/{hyperdhtRelay-BVoWY2ua.js.map → hyperdhtRelay-CrgYQN-g.js.map} +1 -1
  33. package/dist/assets/{irohWeb-BJPk9bMn.js → irohWeb-DSyLp5Re.js} +3 -3
  34. package/dist/assets/{irohWeb-BJPk9bMn.js.map → irohWeb-DSyLp5Re.js.map} +1 -1
  35. package/dist/assets/{main-DC8cUkDx.js → main-BDd11Yd1.js} +159 -54
  36. package/dist/assets/main-BDd11Yd1.js.map +1 -0
  37. package/dist/assets/{mainline-CxbxpoU4.js → mainline-CLTo0nGa.js} +2 -2
  38. package/dist/assets/{mainline-CxbxpoU4.js.map → mainline-CLTo0nGa.js.map} +1 -1
  39. package/dist/assets/{networkReason-D42iNuUB.js → networkReason-DwcadZz5.js} +2 -2
  40. package/dist/assets/{networkReason-D42iNuUB.js.map → networkReason-DwcadZz5.js.map} +1 -1
  41. package/dist/assets/{nwc-Cm4sYgLv.js → nwc-BNFyyECH.js} +3 -3
  42. package/dist/assets/{nwc-Cm4sYgLv.js.map → nwc-BNFyyECH.js.map} +1 -1
  43. package/dist/assets/{nwcProvider-0D7vdPDd.js → nwcProvider-BbjJOfLD.js} +2 -2
  44. package/dist/assets/{nwcProvider-0D7vdPDd.js.map → nwcProvider-BbjJOfLD.js.map} +1 -1
  45. package/dist/assets/{profiles-BHc9pEYg.js → profiles-gPCjLOQq.js} +2 -2
  46. package/dist/assets/{profiles-BHc9pEYg.js.map → profiles-gPCjLOQq.js.map} +1 -1
  47. package/dist/assets/{registry-CVdSZW5l.js → registry-CoLHpxkb.js} +17 -32
  48. package/dist/assets/registry-CoLHpxkb.js.map +1 -0
  49. package/dist/assets/{registry-CLolR0kM.js → registry-DoTb6uEr.js} +1 -1
  50. package/dist/assets/{resolve-BnlEajYz.js → resolve-BIDJ-UUl.js} +18 -3
  51. package/dist/assets/resolve-BIDJ-UUl.js.map +1 -0
  52. package/dist/assets/{server-CJNEKxzE.js → server-Bx3RGkBC.js} +282 -97
  53. package/dist/assets/server-Bx3RGkBC.js.map +1 -0
  54. package/dist/assets/{src-yMUQvdyL.js → src-D7gcJElT.js} +316 -104
  55. package/dist/assets/src-D7gcJElT.js.map +1 -0
  56. package/dist/assets/{turnRefs-B2a8to9q.js → turnRefs-PN644M9o.js} +2 -2
  57. package/dist/assets/{turnRefs-B2a8to9q.js.map → turnRefs-PN644M9o.js.map} +1 -1
  58. package/dist/assets/{usdt-DesFeDiG.js → usdt-B2TLRlYa.js} +3 -3
  59. package/dist/assets/{usdt-DesFeDiG.js.map → usdt-B2TLRlYa.js.map} +1 -1
  60. package/dist/ghostly.mjs +1 -1
  61. package/package.json +1 -1
  62. package/dist/assets/api-aup3Amud.js.map +0 -1
  63. package/dist/assets/apiKit-DYkVEs6r.js.map +0 -1
  64. package/dist/assets/apps-Rbk2HHXb.js.map +0 -1
  65. package/dist/assets/buttons-CGRqZ7z0.js +0 -2
  66. package/dist/assets/ghostly_iroh_web-Dvzh3Y6E.js.map +0 -1
  67. package/dist/assets/host-C7hA-Za7.js.map +0 -1
  68. package/dist/assets/main-DC8cUkDx.js.map +0 -1
  69. package/dist/assets/registry-CVdSZW5l.js.map +0 -1
  70. package/dist/assets/resolve-BnlEajYz.js.map +0 -1
  71. package/dist/assets/server-CJNEKxzE.js.map +0 -1
  72. package/dist/assets/src-yMUQvdyL.js.map +0 -1
@@ -1,19 +1,19 @@
1
1
  import { n as traceJoin } from "./joinTrace-CjRDFy85.js";
2
- import { $c as normalizeRelayUrl, $i as lastSharedWithMe, $o as parseCallSignal, $r as WakeLimiter, $t as didDhtFromPublicKey, A as CommunitySession, Aa as readForwarded, Ac as newDeviceSetSecret, Ao as rosterHas, Ar as pickHubs, As as handoffPakeFrame, B as knockRecords, Ba as validReactionNumber, Bc as firstDeviceSetSecret, Bi as dialDescriptors, Bn as findEndpoint, Bo as formatLocalTarget, Bs as readHandoffHello, C as sha256Paced, Cc as ENROLL_CAPABILITY, Cl as utf8Decode, Cn as isBitcoinAddress, Co as readCanonicalJson, Cr as doorHubs, Cs as handoffContextProof, D as openGroupTyping, Da as dhtEditId, Dc as deviceKeyZ32, Do as commitHash, Dr as mergeBeacon, Ds as handoffHelloFrame, E as groupTypingEpoch, Ea as RateWindow, Ec as deviceEchoNonce, Eo as MEMBER_KEY, Er as lobbyRecords, Es as handoffEphemeral, Et as normalizeAtprotoHandle, Fa as nextReactionNumber, Fc as TURN_SETTLE_MS, Fo as DHT_POLL_INTERVALS, Fr as shouldBeHub, Fs as newHandoffId, G as groupWakeFrame, Ga as BUTTON_ID, Gc as turnInstance, Gi as IdentityExchange, Gn as INVITE_TAKEN, Go as encodeControl, H as readKnocks, Ha as pairedReplyAuthor, Hc as readTurnPacket, Hn as parseCashuRequestPayload, Ho as webLocalFetch, Hr as TypingSender, Hs as readHandoffTurnFrame, I as decodeGroupEntryLink, Ia as queueReaction, Ic as classifyTurnRead, In as decodeBolt11, Ir as ChatFiles, Is as readHandoffAck, J as GROUP_LIMITS, Jc as turnStatedVerifier, Ji as identityRevocationValue, Jn as pairedMessageFits, Jr as nextPinNumber, Js as setAckFrame, K as groupWakes, Kc as turnKeys, Kn as LIVENESS_PING_MS, Kr as PIN_LIMITS, L as encodeGroupEntryLink, La as reactionEmoji, Lc as turnPutSummary, Li as CapsExchange, Ln as ENDPOINT, Lo as RELAY_POLL_INTERVALS, Lr as FILE_LIMITS, Ls as readHandoffCancel, M as communityHasFrame, Ma as GROUP_REACTION_FRAME, Mc as webCryptoSigner, Mo as edgeParams, Mr as readBeacon, Ms as handoffRetryAfter, N as decodeCommunityLink, Na as REACTION_LIMITS, No as DirectPathWatch, Nr as readBeaconHead, Ns as handoffStreamKey, O as sealGroupTyping, Oa as parseLinkPreview, Oc as deviceLinkPairing, Oo as parseGroupCommit, Or as mergeLobby, Os as handoffManifestFrames, P as encodeCommunityLink, Pa as ReactionWindow, Pc as TURN_RAISE_READ_TIMEOUT_MS, Pr as readLobby, Ps as handoffVersions, Q as frameBytes, Qc as currentRelays, Qi as revokesIdentity, Qn as GROUP_FILE_LIMITS, Qr as WAKE_CALL_INTERVAL_MS, Qt as didDhtDocument, R as entryParams, Ra as reactionIsNewer, Rc as TURN_LAST_SEQUENCE, Ri as DHT_TEXT_CAPABILITY, Rn as cashuRequestPayload, Ro as presenceSeenAt, Rr as transferEnded, Rs as readHandoffDone, S as readAppBundleAsync, Sa as parseImageMeta, Sc as DEVICES_CAPABILITY, Si as readManifest, Sl as toZ32, Sn as sparkInvoiceDetails, Sr as beaconRecords, Ss as handoffContext, T as GroupTypingBoard, Ta as EDIT_RATE_WINDOW_MS, Tc as deviceEchoFrame, Tn as isPubkyKey, Tr as lobbyKeys, Ts as handoffDigest, Tt as isAtprotoDid, Ua as readReply, Uc as signTurnPacket, Ui as IDENTITY_REVOCATION_LABEL, Un as parseFedimintRequestPayload, Uo as LIMITS, Us as readHandoffVerified, V as mergeKnocks, Va as wireReaction, Vc as nextTurnPosition, Vn as isFederationId, Vo as parseLocalTarget, Vs as readHandoffPake, W as GroupWakeLimiter, Wa as replySnippet, Wc as signTurnRelease, Wn as GhostLink, Wo as decodeControl, Wr as typingActivity, Ws as sealHandoffPiece, X as GROUP_READ_NOTE, Xa as STATUS_CARD_LIMITS, Xc as DEFAULT_RELAYS, Xi as newIdentityBinding, Xn as engineError, Xr as pinNumberHolds, Xs as signTombstone, Yc as turnVerifierMark, Yi as identityStatement, Yn as pairedMessageFrame, Yr as pinIsNewer, Ys as setUpdateFrame, Z as GroupSession, Zc as RelayTransport, Zi as receivedIdentityStatus, Zn as engineText, Zr as readPin, Zs as verifySetUpdate, _c as decodeInviteCode, _i as HOLD_LIMITS, _l as sanitizeAvatar, _n as validatePaymentTarget, _o as readAppRevocation, _r as mayBeHub, _s as handoffAttemptAllowed, a as planAppUpdate, ac as enrollDoneFrame, ai as checkPushEndpoint, al as PacketTooLargeError, an as EVM_TEST_CHAINS, ao as claimedTime, ar as readGroupFileMeta, as as HANDOFF_ACK, at as groupName, b as pacer, bc as inviteOwnership, bi as isHoldUrl, bl as randomBytes, bn as SPARK_PROVIDER, br as COMMUNITY_TOPOLOGY, bs as handoffBusyFrame, ca as DHT_MESSAGE_TTL, cc as enrollProofFrame, ci as chatAppId, cn as SEPOLIA_TEST_USDT, co as shownTime, cs as HANDOFF_HAVE, d as APP_BUNDLE_LIMITS, da as dhtFileId, dc as verifyEnrollProof, di as BUTTONS_CAPABILITY, dl as createIdentity, dn as USDT_PUBLIC_RPC, do as appFingerprint, ds as HANDOFF_PAKE, ea as PeerProofs, ei as deviceWakeRequest, en as encodeDidDhtDeactivated, f as APP_BUNDLE_MAGIC, fa as formatFileSize, fc as createDeviceInvite, fi as EDIT_CAPABILITY, fl as identityFromSeed, fn as WALLET_NETWORKS, fo as appRef, fs as HANDOFF_PIECE_BYTES, g as appViewOf, gc as createChatInvite, gi as HOLD_DATED_BACK_MS, gn as formatPaymentAmount, go as isAppRef, gr as MESH_HUBS, gs as filePartName, h as appDigest, hc as OwnInviteError, hi as HOLD_CAPABILITY, hl as verify, hn as assertWholeSats, ho as isAppKey, hr as readGroupSignal, hs as HANDOFF_VERIFIED, i as checkAppBeforeRun, ia as automaticTransport, ic as enrollDigits, ii as wakeRequest, il as withRequestOptions, in as ETHEREUM_USDT, io as arrivalKey, is as serviceIdFromName, it as mentionsMember, j as GROUP_READ_NOTE_COMMUNITY, ja as COMMUNITY_REACTION_FRAME, jc as seedSigner, jo as verifyCommitSignature, jr as rankHubs, js as handoffReleaseFrame, k as COMMUNITY_LIMITS, ka as forwardedAgain, kc as devicePingFrame, ko as rosterAdmin, kr as newerHead, ks as handoffOfferFrame, l as readAppStoreSteps, la as peekDhtMailbox, lc as readEnrollGrant, li as isAppVersion, lr as CarriedTransport, ls as HANDOFF_HELLO, ma as sanitizeMime, mc as readDeviceInvite, mi as UPGRADE_CAPABILITY, ml as publicKeyFromZ32, mn as assertTokenUnits, mo as isAppHash, mr as groupSignalFrame, ms as HANDOFF_TIMINGS, n as appStoreDecision, na as validExternalKey, nc as enrollCancelFrame, ni as parseWakeFrame, nl as budgetRetryMs, no as sameButtonText, ns as HTTP_SERVICE_PROTO, nt as carryMentions, o as APP_STORE_LIMITS, oc as enrollGrantFrame, oi as vapidKeysMatch, on as ONCHAIN_PROVIDER, oo as heardTime, os as HANDOFF_CANCEL, p as APP_ICON_PATH, pa as sanitizeFileName, pc as deviceInviteJoinerParams, pi as STATUS_CARD_CAPABILITY, pl as identityFromSeedB64, pn as arkAddressPrefix, ps as HANDOFF_REQUEST, q as parseGroupWakeFrame, qc as turnName, qi as emptyIdentityLedger, qn as PAIRED_FRAME_TOO_LARGE, qo as RTC_CONFIG, qr as mayPin, qs as readSetUpdate, r as appUpdateDecision, ra as TRANSPORTS, rc as enrollCancelReason, ri as relayRequest, rl as isDiscoveryBudgetError, rn as signDidDhtPacket, ro as statusCardText, rr as groupFileFallback, rs as LEGACY_SERVICES, rt as parseCommunityEdit, sc as enrollHelloFrame, sl as createRelayPayload, sn as PaymentPreflightError, so as receivedTimestamp, ss as HANDOFF_DONE, t as appRevocationFor, ta as emptyProofLedger, ti as newWakeToken, tl as traceLink, tn as encodeDidDhtPacket, to as checkStatusCard, ts as emptyLinkRecords, tt as GROUP_EDIT_TEXT_BYTES, ua as DHT_FILE_CAPABILITY, uc as readEnrollHello, ui as APPS_CAPABILITY, un as TEST_USDT_FAUCET_AMOUNT, uo as APP_STATEMENT_LIMITS, us as HANDOFF_OFFER, va as fileMessageText, vc as encodeInviteCode, vi as HoldKeys, vl as bytesEqual, vn as walletNetworkOf, vr as meshHubs, vs as handoffAttemptFailed, wa as EDIT_BUFFER_MS, wc as HANDOFF_CAPABILITY, wl as utf8Encode, wo as GROUP_MEMBER_CAP, wr as freshHubs, ws as handoffDataFrame, xa as parseVideoMeta, xi as newMailbox, xl as toBase64Url, xn as sparkAddressKind, xo as canonicalJson, xr as beaconKeys, xs as handoffCancelFrame, y as isAppUrl, ya as parseVoiceMeta, yi as HoldRefusedError, yl as fromBase64Url, yn as SPARK_NETWORKS, yr as pickMeshHubs, ys as handoffAttemptSucceeded, z as knockIdentity, za as readReaction, zc as TURN_MAX, zi as capsDescriptors, zn as fedimintRequestPayload, zo as GhostlyHttpError, zr as transferStalled, zs as readHandoffHave } from "./src-yMUQvdyL.js";
2
+ import { $c as currentRelays, $i as revokesIdentity, $n as GROUP_FILE_LIMITS, $r as WAKE_CALL_INTERVAL_MS, $t as didDhtFromPublicKey, A as CommunitySession, Aa as forwardedAgain, Ac as devicePingFrame, Ao as rosterAdmin, Ar as newerHead, As as handoffOfferFrame, B as knockRecords, Ba as readReaction, Bc as TURN_MAX, Bi as capsDescriptors, Bn as findEndpoint, Bo as GhostlyHttpError, Br as transferStalled, Bs as readHandoffHave, C as sha256Paced, Ca as parseImageMeta, Cc as DEVICES_CAPABILITY, Ci as readManifest, Cl as randomBytes, Cn as isBitcoinAddress, Cr as beaconRecords, Cs as handoffContext, D as openGroupTyping, Da as RateWindow, Dc as deviceEchoNonce, Dl as utf8Encode, Do as MEMBER_KEY, Dr as lobbyRecords, Ds as handoffEphemeral, E as groupTypingEpoch, Ea as EDIT_RATE_WINDOW_MS, Ec as deviceEchoFrame, El as utf8Decode, Er as lobbyKeys, Es as handoffDigest, Et as normalizeAtprotoHandle, Fa as ReactionWindow, Fc as TURN_RAISE_READ_TIMEOUT_MS, Fr as readLobby, Fs as handoffVersions, G as groupWakeFrame, Ga as replySnippet, Gc as signTurnRelease, Gn as INVITE_TAKEN, Go as decodeControl, Gr as typingActivity, Gs as sealHandoffPiece, H as readKnocks, Ha as wireReaction, Hc as nextTurnPosition, Hn as parseCashuRequestPayload, Ho as parseLocalTarget, Hs as readHandoffPake, I as decodeGroupEntryLink, Ia as nextReactionNumber, Ic as TURN_SETTLE_MS, In as decodeBolt11, Io as DHT_POLL_INTERVALS, Ir as shouldBeHub, Is as newHandoffId, J as GROUP_LIMITS, Jc as turnName, Ji as emptyIdentityLedger, Jn as pairedMessageFits, Jo as RTC_CONFIG, Jr as mayPin, Js as readSetUpdate, K as groupWakes, Ka as BUTTON_ID, Kc as turnInstance, Ki as IdentityExchange, Kn as LIVENESS_PING_MS, Ko as encodeControl, L as encodeGroupEntryLink, La as queueReaction, Lc as classifyTurnRead, Ln as ENDPOINT, Lr as ChatFiles, Ls as readHandoffAck, M as communityHasFrame, Ma as COMMUNITY_REACTION_FRAME, Mc as seedSigner, Mo as verifyCommitSignature, Mr as rankHubs, Ms as handoffReleaseFrame, N as decodeCommunityLink, Na as GROUP_REACTION_FRAME, Nc as webCryptoSigner, No as edgeParams, Nr as readBeacon, Ns as handoffRetryAfter, O as sealGroupTyping, Oa as dhtEditId, Oc as deviceKeyZ32, Oo as commitHash, Or as mergeBeacon, Os as handoffHelloFrame, P as encodeCommunityLink, Pa as REACTION_LIMITS, Po as DirectPathWatch, Pr as readBeaconHead, Ps as handoffStreamKey, Q as frameBytes, Qc as RelayTransport, Qi as receivedIdentityStatus, Qn as parseEngineError, Qr as readPin, Qs as verifySetUpdate, Qt as didDhtDocument, R as entryParams, Ra as reactionEmoji, Rc as turnPutSummary, Ri as CapsExchange, Rn as cashuRequestPayload, Ro as RELAY_POLL_INTERVALS, Rr as FILE_LIMITS, Rs as readHandoffCancel, S as readAppBundleAsync, Sa as parseVideoMeta, Si as newMailbox, Sn as sparkInvoiceDetails, So as canonicalJson, Sr as beaconKeys, Ss as handoffCancelFrame, T as GroupTypingBoard, Ta as EDIT_BUFFER_MS, Tc as HANDOFF_CAPABILITY, Tl as toZ32, Tn as isPubkyKey, To as GROUP_MEMBER_CAP, Tr as freshHubs, Ts as handoffDataFrame, Tt as isAtprotoDid, Ua as pairedReplyAuthor, Uc as readTurnPacket, Un as parseFedimintRequestPayload, Uo as webLocalFetch, Ur as TypingSender, Us as readHandoffTurnFrame, V as mergeKnocks, Va as validReactionNumber, Vc as firstDeviceSetSecret, Vi as dialDescriptors, Vn as isFederationId, Vo as formatLocalTarget, Vs as readHandoffHello, W as GroupWakeLimiter, Wa as readReply, Wc as signTurnPacket, Wi as IDENTITY_REVOCATION_LABEL, Wn as GhostLink, Wo as LIMITS, Ws as readHandoffVerified, X as GROUP_READ_NOTE, Xc as turnVerifierMark, Xi as identityStatement, Xn as engineError, Xr as pinIsNewer, Xs as setUpdateFrame, Yc as turnStatedVerifier, Yi as identityRevocationValue, Yn as pairedMessageFrame, Yr as nextPinNumber, Ys as setAckFrame, Z as GroupSession, Za as STATUS_CARD_LIMITS, Zc as DEFAULT_RELAYS, Zi as newIdentityBinding, Zn as engineText, Zr as pinNumberHolds, Zs as signTombstone, _c as createChatInvite, _i as HOLD_DATED_BACK_MS, _n as validatePaymentTarget, _o as isAppRef, _r as MESH_HUBS, _s as filePartName, a as planAppUpdate, aa as automaticTransport, ac as enrollDigits, ai as wakeRequest, al as withRequestOptions, an as EVM_TEST_CHAINS, ao as arrivalKey, as as serviceIdFromName, at as groupName, b as pacer, ba as parseVoiceMeta, bi as HoldRefusedError, bl as bytesEqual, bn as SPARK_PROVIDER, br as pickMeshHubs, bs as handoffAttemptSucceeded, cc as enrollHelloFrame, cl as createRelayPayload, cn as SEPOLIA_TEST_USDT, co as receivedTimestamp, cs as HANDOFF_DONE, d as APP_BUNDLE_LIMITS, da as DHT_FILE_CAPABILITY, dc as readEnrollHello, di as APPS_CAPABILITY, dn as USDT_PUBLIC_RPC, do as APP_STATEMENT_LIMITS, ds as HANDOFF_OFFER, ea as lastSharedWithMe, ei as WakeLimiter, el as normalizeRelayUrl, en as encodeDidDhtDeactivated, es as parseCallSignal, f as APP_BUNDLE_MAGIC, fa as dhtFileId, fc as verifyEnrollProof, fi as BUTTONS_CAPABILITY, fl as createIdentity, fn as WALLET_NETWORKS, fo as appFingerprint, fs as HANDOFF_PAKE, g as appViewOf, gc as OwnInviteError, gi as HOLD_CAPABILITY, gl as verify, gn as formatPaymentAmount, go as isAppKey, gr as readGroupSignal, gs as HANDOFF_VERIFIED, h as appDigest, ha as sanitizeMime, hc as readDeviceInvite, hi as UPGRADE_CAPABILITY, hl as publicKeyFromZ32, hn as assertWholeSats, ho as isAppHash, hr as groupSignalFrame, hs as HANDOFF_TIMINGS, i as checkAppBeforeRun, ia as TRANSPORTS, ic as enrollCancelReason, ii as relayRequest, il as isDiscoveryBudgetError, in as ETHEREUM_USDT, io as statusCardText, ir as groupFileFallback, is as LEGACY_SERVICES, it as mentionsMember, j as GROUP_READ_NOTE_COMMUNITY, ja as readForwarded, jc as newDeviceSetSecret, jo as rosterHas, jr as pickHubs, js as handoffPakeFrame, k as COMMUNITY_LIMITS, ka as parseLinkPreview, kc as deviceLinkPairing, ko as parseGroupCommit, kr as mergeLobby, ks as handoffManifestFrames, l as readAppStoreSteps, la as DHT_MESSAGE_TTL, lc as enrollProofFrame, li as chatAppId, lo as shownTime, ls as HANDOFF_HAVE, ma as sanitizeFileName, mc as deviceInviteJoinerParams, mi as STATUS_CARD_CAPABILITY, ml as identityFromSeedB64, mn as assertTokenUnits, ms as HANDOFF_REQUEST, n as appStoreDecision, na as emptyProofLedger, ni as newWakeToken, nl as traceLink, no as checkStatusCard, ns as emptyLinkRecords, nt as carryMentions, o as APP_STORE_LIMITS, oc as enrollDoneFrame, oi as checkPushEndpoint, ol as PacketTooLargeError, on as ONCHAIN_PROVIDER, oo as claimedTime, or as readGroupFileMeta, os as HANDOFF_ACK, p as APP_ICON_PATH, pa as formatFileSize, pc as createDeviceInvite, pi as EDIT_CAPABILITY, pl as identityFromSeed, pn as arkAddressPrefix, po as appRef, ps as HANDOFF_PIECE_BYTES, q as parseGroupWakeFrame, qc as turnKeys, qn as PAIRED_FRAME_TOO_LARGE, qr as PIN_LIMITS, r as appUpdateDecision, ra as validExternalKey, rc as enrollCancelFrame, ri as parseWakeFrame, rl as budgetRetryMs, rn as signDidDhtPacket, ro as sameButtonText, rs as HTTP_SERVICE_PROTO, rt as parseCommunityEdit, sc as enrollGrantFrame, si as vapidKeysMatch, sn as PaymentPreflightError, so as heardTime, ss as HANDOFF_CANCEL, t as appRevocationFor, ta as PeerProofs, ti as deviceWakeRequest, tn as encodeDidDhtPacket, tt as GROUP_EDIT_TEXT_BYTES, ua as peekDhtMailbox, uc as readEnrollGrant, ui as isAppVersion, un as TEST_USDT_FAUCET_AMOUNT, ur as CarriedTransport, us as HANDOFF_HELLO, vc as decodeInviteCode, vi as HOLD_LIMITS, vl as sanitizeAvatar, vn as walletNetworkOf, vo as readAppRevocation, vr as mayBeHub, vs as handoffAttemptAllowed, wc as ENROLL_CAPABILITY, wl as toBase64Url, wo as readCanonicalJson, wr as doorHubs, ws as handoffContextProof, xc as inviteOwnership, xi as isHoldUrl, xl as fromBase64Url, xn as sparkAddressKind, xr as COMMUNITY_TOPOLOGY, xs as handoffBusyFrame, y as isAppUrl, ya as fileMessageText, yc as encodeInviteCode, yi as HoldKeys, yl as sanitizeDisplayText, yn as SPARK_NETWORKS, yr as meshHubs, ys as handoffAttemptFailed, z as knockIdentity, za as reactionIsNewer, zc as TURN_LAST_SEQUENCE, zi as DHT_TEXT_CAPABILITY, zn as fedimintRequestPayload, zo as presenceSeenAt, zr as transferEnded, zs as readHandoffDone } from "./src-D7gcJElT.js";
3
3
  import { i as SourceUnreachableError, n as PROVIDER_PLATFORMS, o as isNothingSpentError, r as SourceConfigError, t as NothingSpentError, u as redact } from "./types-C4_KafL_.js";
4
4
  import { t as bitcoindRpc } from "./bitcoindProvider-DGw3xBSa.js";
5
- import { t as nwc } from "./nwcProvider-0D7vdPDd.js";
5
+ import { t as nwc } from "./nwcProvider-BbjJOfLD.js";
6
6
  import { a as clearProfileStores, c as fileStore, d as profileOpenFailure, f as store, i as STORES$1, l as newestWalletTx, m as wrap, n as APP_STORAGE_SCOPE_INDEX, o as databaseName, p as transact, r as CARD_INDEX, s as deleteDatabase, t as APP_STORAGE_CHAT_INDEX, u as openDb } from "./idb-B5L8wuOm.js";
7
- import { a as sealSeed, i as newDeviceKey, o as unsealSeed, r as intentRepository } from "./networkReason-D42iNuUB.js";
8
- import { $ as setSingleWriterGate, A as LightningCards, B as breezDatabaseInUse, C as FileAppender, D as streamStored, E as storedSize, F as ProviderSources, G as ModeChanged, H as dropBreezDatabase, I as sourceKey, J as networkLabel, K as ModeGate, L as CASHU_MINT_SOURCE, M as isRecoveryPhrase, N as normalizePhrase, P as lightningPaying, Q as setAwayWallets, R as cashuMint, S as WALLET_TYPES, T as removeStored, U as iceServerProblem, V as breezDatabasesFor, W as iceServerSetting, X as refuseAway, Y as awayFrom, Z as requireTurn, _ as crossNetwork, a as storedGroupFiles, b as walletInstances, c as receivedPairedReply, d as paymentAlias, f as paymentWireId, g as createTiming, h as createFailure, i as groupOfFile, j as cardsKey, k as CASHU_CARD, l as replyRef, m as assertConfirmedReal, n as groupFileId, o as groupReply, p as WALLET_NAMES, q as WrongNetworkError, r as groupFileText, s as pairedWireReply, t as GroupFileDesk, u as replyTo, v as paymentNetwork, w as readStored$1, y as paymentNetworksOf, z as breezDatabase } from "./groupFiles-DHTrTA4I.js";
9
- import { i as fedimintDatabase, n as createIrohWebEndpoint, o as loadFedimintSdk, r as irohRelayProblem, t as DEFAULT_IROH_RELAYS } from "./irohWeb-BJPk9bMn.js";
7
+ import { a as sealSeed, i as newDeviceKey, o as unsealSeed, r as intentRepository } from "./networkReason-DwcadZz5.js";
8
+ import { $ as setSingleWriterGate, A as LightningCards, B as breezDatabaseInUse, C as FileAppender, D as streamStored, E as storedSize, F as ProviderSources, G as ModeChanged, H as dropBreezDatabase, I as sourceKey, J as networkLabel, K as ModeGate, L as CASHU_MINT_SOURCE, M as isRecoveryPhrase, N as normalizePhrase, P as lightningPaying, Q as setAwayWallets, R as cashuMint, S as WALLET_TYPES, T as removeStored, U as iceServerProblem, V as breezDatabasesFor, W as iceServerSetting, X as refuseAway, Y as awayFrom, Z as requireTurn, _ as crossNetwork, a as storedGroupFiles, b as walletInstances, c as receivedPairedReply, d as paymentAlias, f as paymentWireId, g as createTiming, h as createFailure, i as groupOfFile, j as cardsKey, k as CASHU_CARD, l as replyRef, m as assertConfirmedReal, n as groupFileId, o as groupReply, p as WALLET_NAMES, q as WrongNetworkError, r as groupFileText, s as pairedWireReply, t as GroupFileDesk, u as replyTo, v as paymentNetwork, w as readStored$1, y as paymentNetworksOf, z as breezDatabase } from "./groupFiles-6qnLCnOm.js";
9
+ import { i as fedimintDatabase, n as createIrohWebEndpoint, o as loadFedimintSdk, r as irohRelayProblem, t as DEFAULT_IROH_RELAYS } from "./irohWeb-DSyLp5Re.js";
10
10
  import { c as isTestMint, d as paysItsOwnInvoices, l as isWorthlessMint, n as DEFAULT_MINTS, o as fakesLightning, r as TEST_MINT, t as BITCOIN_INVOICE_ON_TESTNET, u as mintNetwork } from "./mints-BrlY773j.js";
11
- import { _ as reserveAdapterIds, f as setOwnDidSource, g as registeredOnchainProviders, h as registeredLightningProviders, l as chosenResolver, n as resolveAtprotoDid, p as onAdaptersChanged, t as assertPublicServer, v as boundedIdentityFetch, y as verifyIdentity } from "./resolve-BnlEajYz.js";
11
+ import { _ as reserveAdapterIds, b as testBuild, f as setOwnDidSource, g as registeredOnchainProviders, h as registeredLightningProviders, l as chosenResolver, n as resolveAtprotoDid, p as onAdaptersChanged, t as assertPublicServer, v as boundedIdentityFetch, y as verifyIdentity } from "./resolve-BIDJ-UUl.js";
12
12
  import { c as fileSpace, f as FILE_BYTES_STEP, o as fileBytes, r as blobDigest, s as fileBytesOf, u as removeFileBytes } from "./fileBytes-Di7VZtmU.js";
13
- import { n as MAX_BACKUP_BYTES, t as BACKUP_MEDIA_TYPE } from "./envelope-K27mzoyu.js";
13
+ import { n as MAX_BACKUP_BYTES, t as BACKUP_MEDIA_TYPE } from "./envelope-fNC941z4.js";
14
14
  import { t as hyperdhtRelayProblem } from "./hyperdhtRelay-BT_wRxab.js";
15
- import { hkdf } from "@noble/hashes/hkdf.js";
16
15
  import { sha256 } from "@noble/hashes/sha2.js";
16
+ import { hkdf } from "@noble/hashes/hkdf.js";
17
17
  import { base64, base64url, base64urlnopad, bech32, hex } from "@scure/base";
18
18
  import { bytesToHex, concatBytes, hexToBytes, utf8ToBytes } from "@noble/hashes/utils.js";
19
19
  import { chacha20poly1305 } from "@noble/ciphers/chacha.js";
@@ -199,7 +199,7 @@ function viewNews(before, after) {
199
199
  //#endregion
200
200
  //#region ../browser/src/engine/paymentAdapters/usdtWallet.ts
201
201
  /** The Ethereum libraries, a large part of the app: loaded once a USDT wallet is made, opened or restored, not with the engine. */
202
- var loadUsdt = () => import("./usdt-DesFeDiG.js");
202
+ var loadUsdt = () => import("./usdt-B2TLRlYa.js");
203
203
  /** Every new profile starts with this wallet: an address to receive on, nothing to set up. */
204
204
  var DEFAULT_USDT = {
205
205
  network: "ethereum",
@@ -648,7 +648,7 @@ async function restoreArkDatabase(walletId, snapshot) {
648
648
  //#endregion
649
649
  //#region ../browser/src/engine/paymentAdapters/arkWallet.ts
650
650
  /** The Ark SDK, a large part of the app: loaded once an Ark wallet is made, opened or restored, not with the engine. */
651
- var loadArkade = () => import("./arkade-CuWlNnCz.js");
651
+ var loadArkade = () => import("./arkade-CNnMaoEQ.js");
652
652
  /** How long a Testnet wallet about to make way for another waits for its server to say what it holds. */
653
653
  var RETIRE_READ_MS = 1e4;
654
654
  /** Every new profile starts with this wallet: nothing to set up before receiving. */
@@ -6362,9 +6362,6 @@ var webln = {
6362
6362
  return WeblnLightning.connect(await findWebln(), host.mode, host.signal);
6363
6363
  }
6364
6364
  };
6365
- //#endregion
6366
- //#region ../browser/src/engine/paymentAdapters/providers/testing.ts
6367
- var TEST_PROVIDERS_FLAG = "ghostly-test-providers";
6368
6365
  var hex$4 = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
6369
6366
  var random$1 = (n) => crypto.getRandomValues(new Uint8Array(n));
6370
6367
  var uint = (value, length = 1) => {
@@ -6611,11 +6608,7 @@ var fakeLightning = {
6611
6608
  kind: "lightning",
6612
6609
  description: "In-memory regtest node for tests. Holds nothing.",
6613
6610
  networks: ["regtest"],
6614
- platforms: [
6615
- "web",
6616
- "extension",
6617
- "desktop"
6618
- ],
6611
+ platforms: ["web", "desktop"],
6619
6612
  fields: FIELDS,
6620
6613
  experimental: true,
6621
6614
  validate,
@@ -6629,11 +6622,7 @@ var fakeOnchain = {
6629
6622
  kind: "onchain",
6630
6623
  description: "In-memory regtest wallet for tests. Holds nothing.",
6631
6624
  networks: ["regtest"],
6632
- platforms: [
6633
- "web",
6634
- "extension",
6635
- "desktop"
6636
- ],
6625
+ platforms: ["web", "desktop"],
6637
6626
  fields: FIELDS,
6638
6627
  experimental: true,
6639
6628
  validate,
@@ -6644,7 +6633,7 @@ var fakeOnchain = {
6644
6633
  /** Whether this browser asked for the fake providers (an e2e run sets it before the app starts). */
6645
6634
  function testProvidersEnabled() {
6646
6635
  try {
6647
- return globalThis.localStorage?.getItem(TEST_PROVIDERS_FLAG) === "1";
6636
+ return testBuild() && globalThis.localStorage?.getItem("ghostly-test-providers") === "1";
6648
6637
  } catch {
6649
6638
  return false;
6650
6639
  }
@@ -6714,7 +6703,7 @@ var registry;
6714
6703
  var loading$1;
6715
6704
  var listeners = /* @__PURE__ */ new Set();
6716
6705
  function loadIdentityProviders() {
6717
- return loading$1 ??= import("./registry-CLolR0kM.js").then((m) => {
6706
+ return loading$1 ??= import("./registry-DoTb6uEr.js").then((m) => {
6718
6707
  registry = m;
6719
6708
  for (const listener of listeners) listener();
6720
6709
  return m;
@@ -9973,7 +9962,12 @@ var APP_FETCH_LIMITS = {
9973
9962
  sigBytes: 1024,
9974
9963
  /** `ghostly-revoke.json`: at most 4096 signed revocations. */
9975
9964
  revocationsBytes: 16777216,
9976
- timeoutMs: 3e4
9965
+ /** How long the host has to answer (the response's headers). */
9966
+ timeoutMs: 3e4,
9967
+ /** How long the body may go without a byte: a read still arriving is never cut by the clock alone. */
9968
+ idleMs: 3e4,
9969
+ /** A whole read at most: the 16 MiB cap at about 224 kbit/s. */
9970
+ totalMs: 6e5
9977
9971
  };
9978
9972
  /** A URL this client may read an app, a store or a revocation list from. */
9979
9973
  function isAppFetchUrl(value) {
@@ -9996,10 +9990,22 @@ function boundedAppFetch(options = {}) {
9996
9990
  if (!isAppFetchUrl(url)) throw new AppFetchError("host", "Apps are read only from raw.githubusercontent.com, or from cdn.jsdelivr.net at a commit, over HTTPS");
9997
9991
  if (options.online && !options.online()) throw new AppFetchError("offline", "Offline");
9998
9992
  const fetcher = options.fetcher ?? globalThis.fetch.bind(globalThis);
9993
+ const controller = new AbortController();
9994
+ const cut = () => {
9995
+ controller.abort(new DOMException("The read took too long", "TimeoutError"));
9996
+ };
9997
+ const total = setTimeout(cut, options.totalMs ?? APP_FETCH_LIMITS.totalMs);
9998
+ let idle = setTimeout(cut, options.timeoutMs ?? APP_FETCH_LIMITS.timeoutMs);
9999
+ const heard = () => {
10000
+ clearTimeout(idle);
10001
+ idle = setTimeout(cut, options.idleMs ?? APP_FETCH_LIMITS.idleMs);
10002
+ };
10003
+ const failure = (error) => controller.signal.aborted ? new AppFetchError("slow", "That took too long on this connection") : new AppFetchError("network", error instanceof Error ? error.message : String(error));
10004
+ const { signal } = controller;
9999
10005
  let response;
10000
10006
  try {
10001
10007
  response = await fetcher(url, {
10002
- signal: AbortSignal.timeout(options.timeoutMs ?? APP_FETCH_LIMITS.timeoutMs),
10008
+ signal,
10003
10009
  credentials: "omit",
10004
10010
  referrerPolicy: "no-referrer",
10005
10011
  cache: "no-store",
@@ -10007,38 +10013,55 @@ function boundedAppFetch(options = {}) {
10007
10013
  ...peek && { headers: { range: `bytes=0-${maxBytes - 1}` } }
10008
10014
  });
10009
10015
  } catch (error) {
10010
- throw new AppFetchError("network", error instanceof Error ? error.message : String(error));
10011
- }
10012
- if (response.url && response.url !== url && !isAppFetchUrl(response.url)) throw new AppFetchError("host", "The host sent the request elsewhere");
10013
- if (!response.ok) {
10014
- await response.body?.cancel().catch(() => {});
10015
- throw new AppFetchError("status", `The host answered ${response.status}`, response.status);
10016
- }
10017
- if (!peek && Number(response.headers.get("content-length")) > maxBytes) {
10018
- await response.body?.cancel().catch(() => {});
10019
- throw new AppFetchError("too-large", "Too large");
10016
+ clearTimeout(total);
10017
+ clearTimeout(idle);
10018
+ throw failure(error);
10020
10019
  }
10020
+ heard();
10021
10021
  const parts = [];
10022
10022
  let length = 0;
10023
- const reader = response.body?.getReader();
10024
- if (reader) try {
10025
- for (;;) {
10026
- const { done, value } = await reader.read();
10027
- if (done) break;
10028
- if (length + value.length > maxBytes) {
10029
- if (!peek) throw new AppFetchError("too-large", "Too large");
10030
- parts.push(value.subarray(0, maxBytes - length));
10031
- length = maxBytes;
10032
- break;
10023
+ try {
10024
+ if (response.url && response.url !== url && !isAppFetchUrl(response.url)) throw new AppFetchError("host", "The host sent the request elsewhere");
10025
+ if (!response.ok) {
10026
+ await response.body?.cancel().catch(() => {});
10027
+ throw new AppFetchError("status", `The host answered ${response.status}`, response.status);
10028
+ }
10029
+ if (!peek && Number(response.headers.get("content-length")) > maxBytes) {
10030
+ await response.body?.cancel().catch(() => {});
10031
+ throw new AppFetchError("too-large", "Too large");
10032
+ }
10033
+ const reader = response.body?.getReader();
10034
+ if (reader) {
10035
+ const stop = () => {
10036
+ reader.cancel().catch(() => {});
10037
+ };
10038
+ signal.addEventListener("abort", stop);
10039
+ try {
10040
+ for (;;) {
10041
+ const { done, value } = await reader.read();
10042
+ if (signal.aborted) throw failure(signal.reason);
10043
+ if (done) break;
10044
+ heard();
10045
+ if (length + value.length > maxBytes) {
10046
+ if (!peek) throw new AppFetchError("too-large", "Too large");
10047
+ parts.push(value.subarray(0, maxBytes - length));
10048
+ length = maxBytes;
10049
+ break;
10050
+ }
10051
+ parts.push(value);
10052
+ length += value.length;
10053
+ }
10054
+ } catch (error) {
10055
+ if (error instanceof AppFetchError) throw error;
10056
+ throw failure(error);
10057
+ } finally {
10058
+ signal.removeEventListener("abort", stop);
10059
+ await reader.cancel().catch(() => {});
10033
10060
  }
10034
- parts.push(value);
10035
- length += value.length;
10036
10061
  }
10037
- } catch (error) {
10038
- if (error instanceof AppFetchError) throw error;
10039
- throw new AppFetchError("network", error instanceof Error ? error.message : String(error));
10040
10062
  } finally {
10041
- await reader.cancel().catch(() => {});
10063
+ clearTimeout(total);
10064
+ clearTimeout(idle);
10042
10065
  }
10043
10066
  const bytes = new Uint8Array(length);
10044
10067
  let at = 0;
@@ -18687,6 +18710,7 @@ var FileDesk = class {
18687
18710
  announced: /* @__PURE__ */ new Map(),
18688
18711
  saving: Promise.resolve(),
18689
18712
  lastSaved: /* @__PURE__ */ new Map(),
18713
+ unsaved: /* @__PURE__ */ new Map(),
18690
18714
  files: void 0
18691
18715
  };
18692
18716
  made.files = new ChatFiles({
@@ -18726,6 +18750,7 @@ var FileDesk = class {
18726
18750
  const chat = this.chat(linkId);
18727
18751
  if (open) chat.files.attach();
18728
18752
  else chat.files.detach();
18753
+ if (!open) for (const [id, { record }] of [...chat.unsaved]) this.save(chat, id, record);
18729
18754
  for (const record of chat.files.records()) this.show(linkId, chat, record, chat.files.transferred(record.direction, record.id));
18730
18755
  this.deps.changed();
18731
18756
  }
@@ -18911,6 +18936,8 @@ var FileDesk = class {
18911
18936
  const wireId = out ? fileId.slice(`${linkId}-out-`.length) : [...chat.local].find(([, local]) => local === fileId)?.[0];
18912
18937
  if (!wireId) return;
18913
18938
  chat.files.forget(out ? "out" : "in", wireId);
18939
+ clearTimeout(chat.unsaved.get(fileId)?.timer);
18940
+ chat.unsaved.delete(fileId);
18914
18941
  if (!out) {
18915
18942
  chat.local.delete(wireId);
18916
18943
  chat.announced.delete(wireId);
@@ -18928,7 +18955,9 @@ var FileDesk = class {
18928
18955
  }
18929
18956
  /** The chat went away: its transfers stop here. */
18930
18957
  drop(linkId) {
18931
- this.chats.get(linkId)?.files.detach();
18958
+ const chat = this.chats.get(linkId);
18959
+ chat?.files.detach();
18960
+ for (const { timer } of chat?.unsaved.values() ?? []) clearTimeout(timer);
18932
18961
  this.chats.delete(linkId);
18933
18962
  for (const id of [
18934
18963
  ...this.moved.keys(),
@@ -19114,23 +19143,45 @@ var FileDesk = class {
19114
19143
  this.show(linkId, chat, record, transferred, progress);
19115
19144
  if (offered) this.deps.flush?.();
19116
19145
  const id = record.direction === "out" ? outgoingFileId(linkId, record.id) : chat.local.get(record.id);
19117
- const now = Date.now();
19118
- if (!progress || now - (chat.lastSaved.get(id) ?? 0) >= SAVE_EVERY_MS) {
19119
- chat.lastSaved.set(id, now);
19120
- const transfer = this.deps.transfers.get(id);
19121
- chat.saving = chat.saving.then(() => fileStore.patch(id, {
19122
- wire3: record,
19123
- ...transfer && { transfer: {
19124
- state: transfer.state,
19125
- transferred: transfer.transferred,
19126
- size: transfer.size,
19127
- error: transfer.error
19128
- } }
19129
- })).catch(() => {});
19146
+ const now = Date.now(), last = chat.lastSaved.get(id) ?? 0;
19147
+ if (!progress || now - last >= SAVE_EVERY_MS) this.save(chat, id, record);
19148
+ else {
19149
+ const held = chat.unsaved.get(id);
19150
+ if (held) held.record = record;
19151
+ else {
19152
+ const timer = setTimeout(() => {
19153
+ const newest = chat.unsaved.get(id);
19154
+ if (newest) this.save(chat, id, newest.record);
19155
+ }, last + SAVE_EVERY_MS - now);
19156
+ timer.unref?.();
19157
+ chat.unsaved.set(id, {
19158
+ record,
19159
+ timer
19160
+ });
19161
+ }
19130
19162
  }
19131
19163
  if (record.state === "done" && record.direction === "in") this.deps.wireIds(linkId)?.add(record.id);
19132
19164
  this.deps.changed(progress ? 250 : 50);
19133
19165
  }
19166
+ /** Writes a transfer's record and the view of it, after the chat's writes before it; what was held back goes with it. */
19167
+ save(chat, id, record) {
19168
+ const held = chat.unsaved.get(id);
19169
+ if (held) {
19170
+ clearTimeout(held.timer);
19171
+ chat.unsaved.delete(id);
19172
+ }
19173
+ chat.lastSaved.set(id, Date.now());
19174
+ const transfer = this.deps.transfers.get(id);
19175
+ chat.saving = chat.saving.then(() => fileStore.patch(id, {
19176
+ wire3: record,
19177
+ ...transfer && { transfer: {
19178
+ state: transfer.state,
19179
+ transferred: transfer.transferred,
19180
+ size: transfer.size,
19181
+ error: transfer.error
19182
+ } }
19183
+ })).catch(() => {});
19184
+ }
19134
19185
  show(linkId, chat, record, transferred, progress = false) {
19135
19186
  const id = record.direction === "out" ? outgoingFileId(linkId, record.id) : chat.local.get(record.id);
19136
19187
  if (!id) return;
@@ -23726,8 +23777,12 @@ var MeshHubs = class {
23726
23777
  else if (!live.myHubs.length) for (const key of [...this.host.edges(groupId).keys(), ...hubs]) out.add(key);
23727
23778
  else {
23728
23779
  for (const key of live.myHubs) out.add(key);
23729
- const admin = session.admin;
23730
- if (admin && legacy.includes(admin)) out.add(admin);
23780
+ const admin = session.admin, mine = this.host.edges(groupId);
23781
+ const hubUp = live.myHubs.some((key) => {
23782
+ const id = mine.get(key);
23783
+ return !!id && this.host.linkReady(id);
23784
+ });
23785
+ if (admin && (legacy.includes(admin) || !hubUp)) out.add(admin);
23731
23786
  }
23732
23787
  if (session.isAdmin) for (const key of legacy) out.add(key);
23733
23788
  }
@@ -26807,6 +26862,7 @@ var Groups = class {
26807
26862
  attach(state, frames = []) {
26808
26863
  const session = new GroupSession(state, {
26809
26864
  clock: () => this.now(),
26865
+ nick: () => this.host.myNick?.(),
26810
26866
  seqFloor: () => this.host.seqFloor?.() ?? 0,
26811
26867
  adminWork: () => this.host.adminWork?.(state.id) ?? true,
26812
26868
  adminTurn: () => this.host.adminTurn?.(state.id) ?? Promise.resolve(true),
@@ -27179,10 +27235,7 @@ function mergeGroupPay(current, frame, author, members) {
27179
27235
  ...next,
27180
27236
  by: frame.by
27181
27237
  };
27182
- if (next.state === "paid" && !next.by && next.from === "*" && next.claims?.length === 1) next = {
27183
- ...next,
27184
- by: next.claims[0]
27185
- };
27238
+ if (next.from === "*" && next.rail === "lightning" && next.by) next = (({ by: _, ...rest }) => rest)(next);
27186
27239
  if (next.state === "paid" && next.from !== "*" && !next.by) next = {
27187
27240
  ...next,
27188
27241
  by: next.from
@@ -27213,7 +27266,7 @@ function authoredNote(p, at, all, current, memberOf = () => void 0) {
27213
27266
  const from = p.group ? "*" : at.peer;
27214
27267
  if (!from) return null;
27215
27268
  const payment = all.find((x) => x.kind === "payment" && x.direction === "in" && x.requestId === p.id && x.state === "settled");
27216
- const by = !p.group ? from : payment ? memberOf(payment.linkId) : current?.claims?.length === 1 ? current.claims[0] : void 0;
27269
+ const by = !p.group ? from : payment ? memberOf(payment.linkId) : void 0;
27217
27270
  const st = p.state === "settled" ? "paid" : p.state === "failed" || p.state === "reclaimed" ? "closed" : "open";
27218
27271
  return {
27219
27272
  ...base,
@@ -27269,7 +27322,6 @@ var GroupPayments = class {
27269
27322
  if (!frame || !membership) return;
27270
27323
  const next = mergeGroupPay((await this.load(groupId)).get(frame.id), frame, author, membership.members);
27271
27324
  if (next) await this.write(groupId, membership.me, next);
27272
- await this.author();
27273
27325
  });
27274
27326
  }
27275
27327
  /** Says what changed about this device's own payments in groups. The engine calls it whenever payments change. */
@@ -28208,6 +28260,8 @@ var TICK_MS = 3e4;
28208
28260
  /** The contact's pointer is read this often while something is outstanding either way, else rarely. */
28209
28261
  var POLL_BUSY_MS = 3e4;
28210
28262
  var POLL_IDLE_MS = 3e5;
28263
+ /** No two reads of the contact's pointer come closer than this, whatever asked for them. */
28264
+ var POLL_GAP_MS = 5e3;
28211
28265
  /** The manifest and its addresses are signed again before they get this old (they live seven days). */
28212
28266
  var RENEW_AFTER_MS = 3456e5;
28213
28267
  /** My pointer is published again this often while something is held, in case a relay forgot it. */
@@ -28242,6 +28296,9 @@ var HoldEngine = class {
28242
28296
  /** Chats whose pointer the relays' request budget held back: it goes out as soon as the budget frees a request. */
28243
28297
  pointerDue = /* @__PURE__ */ new Set();
28244
28298
  retryTimer = null;
28299
+ /** A read the contact's word asked for, one waiting per chat, and when the last such read went out. */
28300
+ peerPolls = /* @__PURE__ */ new Map();
28301
+ lastPeerPoll = /* @__PURE__ */ new Map();
28245
28302
  constructor(host) {
28246
28303
  this.host = host;
28247
28304
  }
@@ -28262,6 +28319,8 @@ var HoldEngine = class {
28262
28319
  if (this.timer) clearTimeout(this.timer);
28263
28320
  if (this.retryTimer) clearTimeout(this.retryTimer);
28264
28321
  this.timer = this.retryTimer = null;
28322
+ for (const timer of this.peerPolls.values()) clearTimeout(timer);
28323
+ this.peerPolls.clear();
28265
28324
  await Promise.allSettled([...this.chains.values()]);
28266
28325
  }
28267
28326
  serialize(linkId, run) {
@@ -28343,7 +28402,24 @@ var HoldEngine = class {
28343
28402
  if (said.peerTop !== void 0 && said.peerTop > hold.inSeq) this.expecting.set(linkId, said.peerTop);
28344
28403
  });
28345
28404
  this.host.changed();
28346
- if (this.expecting.has(linkId)) this.wake(linkId);
28405
+ if (this.expecting.has(linkId)) this.peerPoll(linkId);
28406
+ }
28407
+ /**
28408
+ * The contact says it holds items for this side: its pointer is read soon, but at the pace the chat's own reads keep.
28409
+ * Only this person's action (`wake`) skips the gap; the contact's word, however often it comes, makes one read wait
28410
+ * per chat, never within `POLL_GAP_MS` of the last read nor within `POLL_BUSY_MS` of the last read it asked for.
28411
+ */
28412
+ peerPoll(linkId) {
28413
+ if (!this.running || this.peerPolls.has(linkId)) return;
28414
+ const at = Math.max((this.lastPoll.get(linkId) ?? 0) + POLL_GAP_MS, (this.lastPeerPoll.get(linkId) ?? -Infinity) + POLL_BUSY_MS);
28415
+ this.peerPolls.set(linkId, setTimeout(() => {
28416
+ this.peerPolls.delete(linkId);
28417
+ const hold = this.state(linkId);
28418
+ if (!this.running || !this.expecting.has(linkId) || !hold.enabled || !hold.peerAllows || !this.keysFor(linkId)) return;
28419
+ if ((this.lastPoll.get(linkId) ?? 0) + POLL_GAP_MS > this.now()) return this.peerPoll(linkId);
28420
+ this.lastPeerPoll.set(linkId, this.now());
28421
+ this.serialize(linkId, () => this.poll(linkId)).catch(() => {});
28422
+ }, Math.max(0, at - this.now())));
28347
28423
  }
28348
28424
  /** Ways of paying the contact allowed at the last session, whatever the hold settings; undefined before any. */
28349
28425
  lastPeerMethods(linkId) {
@@ -28560,6 +28636,9 @@ var HoldEngine = class {
28560
28636
  this.errors.delete(linkId);
28561
28637
  this.chains.delete(linkId);
28562
28638
  this.pointerDue.delete(linkId);
28639
+ clearTimeout(this.peerPolls.get(linkId));
28640
+ this.peerPolls.delete(linkId);
28641
+ this.lastPeerPoll.delete(linkId);
28563
28642
  }
28564
28643
  /**
28565
28644
  * Publishes my pointer. One the relays' request budget held back is no failure: the item is stored, the pointer is
@@ -28661,7 +28740,7 @@ var HoldEngine = class {
28661
28740
  await this.serialize(linkId, () => this.maintain(linkId)).catch(() => {});
28662
28741
  const busy = hold.outbox.some((e) => e.state !== "failed") || this.expecting.has(linkId);
28663
28742
  const due = (this.lastPoll.get(linkId) ?? 0) + (busy ? POLL_BUSY_MS : POLL_IDLE_MS) <= this.now();
28664
- if (hold.peerAllows && (all || due) && (this.lastPoll.get(linkId) ?? 0) + 5e3 <= this.now()) await this.serialize(linkId, () => this.poll(linkId)).catch(() => {});
28743
+ if (hold.peerAllows && (all || due) && (this.lastPoll.get(linkId) ?? 0) + POLL_GAP_MS <= this.now()) await this.serialize(linkId, () => this.poll(linkId)).catch(() => {});
28665
28744
  }
28666
28745
  if (this.running) this.timer = setTimeout(() => void this.tick(), TICK_MS);
28667
28746
  }
@@ -30841,6 +30920,9 @@ var CashuWallet = class CashuWallet {
30841
30920
  });
30842
30921
  const written = (await this.swapsAt(mint)).filter((s) => s.kind === "receive");
30843
30922
  let swap = written.find((s) => s.token === token);
30923
+ const own = payment?.(0, mint);
30924
+ const sameRecord = (a, b) => a?.id === b?.id && a?.linkId === b?.linkId && a?.direction === b?.direction;
30925
+ if (swap && !sameRecord(swap.payment, own)) throw engineError("ecashBeingReceived");
30844
30926
  if (swap?.finished) return {
30845
30927
  amount: swap.tx?.amount ?? 0,
30846
30928
  mint,
@@ -31621,6 +31703,18 @@ var METHOD_NAME = {
31621
31703
  var allows = (link, method) => method === "arkade" ? link.supportsArkPayments : method === "bark" ? link.supportsBarkPayments : method === "bitcoin" ? link.supportsBitcoinPayments : method === "fedimint" ? link.supportsFedimintPayments : method === "spark" ? link.supportsSparkPayments : link.supportsUsdtPayments;
31622
31704
  /** Federation ecash is counted in msats; a chat counts whole sats. */
31623
31705
  var fedimintSats = (msats) => Math.floor(msats / 1e3);
31706
+ /**
31707
+ * Which notes these are, whatever the spelling: the Fedimint client drops whitespace anywhere and reads URL-safe or
31708
+ * standard base64, so the digest is of the bytes. Text that is no base64 is digested without its whitespace.
31709
+ */
31710
+ function fedimintNotesDigest(notes) {
31711
+ const text = notes.replace(/\s+/g, "");
31712
+ try {
31713
+ return digestHex(toBase64Url(fromBase64Url(text.replace(/=+$/, ""))));
31714
+ } catch {
31715
+ return digestHex(text);
31716
+ }
31717
+ }
31624
31718
  /** The distinct wallets of a per-network set (one object may stand for both). */
31625
31719
  var distinct = (each) => [...new Set(WALLET_NETWORKS.map((n) => each[n]))];
31626
31720
  /**
@@ -33634,8 +33728,9 @@ var PaymentDesk = class {
33634
33728
  await refuse(`${networkLabel(walletNetworkOf(network))} Fedimint is off in this chat`);
33635
33729
  return;
33636
33730
  }
33637
- const digest = digestHex(notes.trim());
33638
- const before = [...this.payments.values()].find((p) => p.notesDigest === digest && p.direction === "in" && p.kind === "payment");
33731
+ const digest = fedimintNotesDigest(notes);
33732
+ const spelled = digestHex(notes.trim());
33733
+ const before = [...this.payments.values()].find((p) => (p.notesDigest === digest || p.notesDigest === spelled) && p.direction === "in" && p.kind === "payment");
33639
33734
  if (before) {
33640
33735
  link.sendPaymentResult({
33641
33736
  id: payment.id,
@@ -34511,6 +34606,11 @@ var DEFAULT_SETTINGS = {
34511
34606
  var CARD_RESTORE_MOST = 50;
34512
34607
  var CARD_RESTORE_MS = 6048e5;
34513
34608
  /**
34609
+ * A look for those cards reads the chat's whole history, so a chat starts one at a time, at most once this often: a
34610
+ * contact that says `edit/1` again and again on a session gets one look now and one more after the gap, whatever the count.
34611
+ */
34612
+ var CARD_RESTORE_EVERY_MS = 3e4;
34613
+ /**
34514
34614
  * A round trip that moved is shown at once, and written to disk by itself (relaxed, no disk sync of its own) at most
34515
34615
  * this often: a 15 s sample on an internet path moves by 10 ms most of the time. A power cut loses at most this much
34516
34616
  * of the round trip's drift, never a row; any other change to the story writes the latest round trip with it.
@@ -34567,6 +34667,8 @@ var SPARE_INVITE_WARM_AFTER_TAKE_MS = 3e4;
34567
34667
  * first 11 s, and read nothing more for the rest of the minute, the others' answers included (bug hunt r7a, 2026-09-29).
34568
34668
  */
34569
34669
  var STARTUP_QUIET_MS = 15e3;
34670
+ /** At start, how long links start in one task before the page gets the thread back (`startNow`). */
34671
+ var START_SLICE_MS = 10;
34570
34672
  var SPARE_INVITE_MAX_AGE_MS = 9e5;
34571
34673
  /** How far before this clock a warm packet is dated (`warmKey`): further than a contact's clock is ever behind. */
34572
34674
  var WARM_DATED_BACK_MS = 864e5;
@@ -34709,8 +34811,12 @@ var GhostlyNode = class GhostlyNode {
34709
34811
  /** Other profiles of this device, looked at for new messages (WISP 04 § Checking other profiles). */
34710
34812
  profilePeek;
34711
34813
  /** Whether this device's WebRTC attempts say direct connections are blocked on its network (`directPath.ts`). */
34712
- directPath = new DirectPathWatch(() => {
34713
- if (!this.shuttingDown) this.emitState();
34814
+ directPath = new DirectPathWatch((blocked) => {
34815
+ if (this.shuttingDown) return;
34816
+ this.emitState();
34817
+ if (blocked) {
34818
+ for (const [linkId, live] of this.links) if (live.stored.group && !live.link?.isDataLinkOpen) this.edgeWithoutRtc(linkId);
34819
+ }
34714
34820
  });
34715
34821
  /** Whether this device's clock seems to be off, from what the relays' answers and several contacts' packets say (`clockWatch.ts`). */
34716
34822
  clock = new ClockWatch(() => {
@@ -34720,9 +34826,11 @@ var GhostlyNode = class GhostlyNode {
34720
34826
  /**
34721
34827
  * Edges of groups whose WebRTC did not connect (`edgeWithoutRtc`): they go on as an app with no WebRTC does, over a
34722
34828
  * native transport, until the member is seen online again after a packet said it was gone (`away`, `edgeRtcPresence`).
34723
- * Not kept: the next start tries WebRTC first again.
34829
+ * Not kept: the next start tries WebRTC first again. `held`: it stays native on a return too, until the network changes.
34724
34830
  */
34725
34831
  edgeRtcOff = /* @__PURE__ */ new Map();
34832
+ /** Edges put back on WebRTC by a return (`edgeRtcPresence`) since the network last changed: failing again holds them native. */
34833
+ edgeRtcRetried = /* @__PURE__ */ new Set();
34726
34834
  pollIntervals;
34727
34835
  localFetch;
34728
34836
  links = /* @__PURE__ */ new Map();
@@ -34814,6 +34922,8 @@ var GhostlyNode = class GhostlyNode {
34814
34922
  outboxes = /* @__PURE__ */ new Map();
34815
34923
  /** My edits on their way, per 1:1 chat (WISP 400 § Edits). */
34816
34924
  editQueues = /* @__PURE__ */ new Map();
34925
+ /** Per 1:1 chat, its look for cards to send again (`restoreCardsSoon`): when the last began, and what waits. */
34926
+ cardRestores = /* @__PURE__ */ new Map();
34817
34927
  /** The contacts' edits of messages not here yet. */
34818
34928
  editBuffer = new EditBuffer();
34819
34929
  /** Received status card updates, applied at most once a second per message (WISP 405 · Status Cards). */
@@ -35380,7 +35490,7 @@ var GhostlyNode = class GhostlyNode {
35380
35490
  subject
35381
35491
  }),
35382
35492
  publish: (seed, records) => this.transport.publish(identityFromSeed(seed), records),
35383
- resolve: async (key) => (await this.transport.resolve(key))?.records ?? null
35493
+ resolve: async (key) => (await this.transport.resolve(key, { background: true }))?.records ?? null
35384
35494
  });
35385
35495
  /** The profile's did:dht: a key of its own, public, never tied to a chat (WISP 310-did-dht). */
35386
35496
  did = new ProfileDid({
@@ -35935,7 +36045,7 @@ var GhostlyNode = class GhostlyNode {
35935
36045
  const fresh = !this.walletFeedbackIds.has(tx.id);
35936
36046
  this.walletFeedbackIds.add(tx.id);
35937
36047
  if (this.walletFeedbackReady && fresh && tx.amount > 0 && tx.timestamp >= this.feedbackStartedAt) {
35938
- if (tx.kind === "lightning-in" || tx.kind === "ecash-in") this.feedback("coin", tx.id, void 0, false, tx.note === "Test coins from the test mint" ? "testcoins" : void 0);
36048
+ if (tx.kind === "lightning-in" || tx.kind === "ecash-in") this.feedback("coin", tx.id, void 0, false, tx.kind === "lightning-in" && tx.note === "Test coins from the test mint" ? "testcoins" : void 0);
35939
36049
  if (tx.kind === "lightning-out") this.feedback("confirmed", tx.id);
35940
36050
  }
35941
36051
  }
@@ -36074,12 +36184,16 @@ var GhostlyNode = class GhostlyNode {
36074
36184
  if (note) this.reactionNotes.set(`group:${group.id}`, note);
36075
36185
  }
36076
36186
  this.publicProfiles.start();
36187
+ const pause = pacer(START_SLICE_MS);
36077
36188
  if (this.networkOn) for (const linkId of this.nativeStartOrder([...history.keys()])) {
36189
+ if (!this.networkOn || this.shuttingDown) break;
36078
36190
  const messages = history.get(linkId);
36079
36191
  if (!this.links.has(linkId)) continue;
36080
36192
  this.startLink(linkId, messages);
36081
36193
  if (!this.links.get(linkId)?.stored.group) this.refreshPublicProfiles({ linkId }).catch(() => {});
36194
+ await pause();
36082
36195
  }
36196
+ if (this.shuttingDown) return;
36083
36197
  this.emitState();
36084
36198
  if (this.networkOn) {
36085
36199
  this.hold.start();
@@ -36512,6 +36626,8 @@ var GhostlyNode = class GhostlyNode {
36512
36626
  this.publicProfiles.stop();
36513
36627
  this.publicActivity.clear();
36514
36628
  for (const queue of this.editQueues.values()) queue.stop();
36629
+ for (const restore of this.cardRestores.values()) clearTimeout(restore.timer);
36630
+ this.cardRestores.clear();
36515
36631
  this.groupEdits.stop();
36516
36632
  this.reactions.stop();
36517
36633
  this.cardEdits.stop();
@@ -37468,11 +37584,13 @@ var GhostlyNode = class GhostlyNode {
37468
37584
  this.outboxes.delete(linkId);
37469
37585
  this.editQueues.get(linkId)?.stop();
37470
37586
  this.editQueues.delete(linkId);
37587
+ clearTimeout(this.cardRestores.get(linkId)?.timer);
37588
+ this.cardRestores.delete(linkId);
37471
37589
  this.editBuffer.forget(linkId);
37472
37590
  live.carried?.stop();
37591
+ this.links.delete(linkId);
37473
37592
  live.link?.stop(true);
37474
37593
  live.caps?.stop();
37475
- this.links.delete(linkId);
37476
37594
  this.identities.forget(linkId);
37477
37595
  this.nostrSocial.forgetLink(linkId);
37478
37596
  this.hold.forgetLink(linkId);
@@ -37487,9 +37605,10 @@ var GhostlyNode = class GhostlyNode {
37487
37605
  renameLink({ linkId, label }) {
37488
37606
  const live = this.links.get(linkId);
37489
37607
  if (!live) return;
37608
+ const clean = sanitizeDisplayText(label.trim().replace(/\s+/gu, " "), 48)?.replace(/ {2,}/g, " ");
37490
37609
  live.stored = {
37491
37610
  ...live.stored,
37492
- label: label.trim().slice(0, 48) || void 0
37611
+ label: clean
37493
37612
  };
37494
37613
  db.patchLink(linkId, { label: live.stored.label });
37495
37614
  this.emitState();
@@ -37519,6 +37638,10 @@ var GhostlyNode = class GhostlyNode {
37519
37638
  this.transport.networkChanged?.();
37520
37639
  this.directPath.reset();
37521
37640
  }
37641
+ if (params.network) {
37642
+ for (const off of this.edgeRtcOff.values()) off.held = false;
37643
+ this.edgeRtcRetried.clear();
37644
+ }
37522
37645
  if (params.network) for (const failed of this.nativeFailed.values()) {
37523
37646
  failed.at = 0;
37524
37647
  failed.wait = 0;
@@ -38009,6 +38132,42 @@ var GhostlyNode = class GhostlyNode {
38009
38132
  }
38010
38133
  if (changed.length) await this.messagesChanged(linkId, changed);
38011
38134
  }
38135
+ /**
38136
+ * `restoreCards`, then what waits goes. One look at a time per chat, at most once per `CARD_RESTORE_EVERY_MS`: asked
38137
+ * again while one runs or within the gap, one more look goes once it is over, and what waits still goes at once (the
38138
+ * edit queue reads the history once a session).
38139
+ */
38140
+ restoreCardsSoon(linkId) {
38141
+ const flush = () => this.editsFor(linkId).flush().catch(() => {});
38142
+ let state = this.cardRestores.get(linkId);
38143
+ if (!state) this.cardRestores.set(linkId, state = {
38144
+ at: -Infinity,
38145
+ busy: false,
38146
+ again: false
38147
+ });
38148
+ if (state.busy) {
38149
+ state.again = true;
38150
+ return;
38151
+ }
38152
+ const wait = state.at + CARD_RESTORE_EVERY_MS - Date.now();
38153
+ if (wait > 0) {
38154
+ state.timer ??= setTimeout(() => {
38155
+ state.timer = void 0;
38156
+ if (this.cardRestores.get(linkId) === state) this.restoreCardsSoon(linkId);
38157
+ }, wait);
38158
+ flush();
38159
+ return;
38160
+ }
38161
+ state.busy = true;
38162
+ state.at = Date.now();
38163
+ this.restoreCards(linkId).catch(() => {}).then(flush).finally(() => {
38164
+ state.busy = false;
38165
+ if (state.again && this.cardRestores.get(linkId) === state) {
38166
+ state.again = false;
38167
+ this.restoreCardsSoon(linkId);
38168
+ }
38169
+ });
38170
+ }
38012
38171
  /** My edits in one 1:1 chat on their way to the contact. */
38013
38172
  editsFor(linkId) {
38014
38173
  let queue = this.editQueues.get(linkId);
@@ -39962,8 +40121,15 @@ var GhostlyNode = class GhostlyNode {
39962
40121
  if (typing === true && this.settings.sendTyping !== false) this.groups.setTyping(groupId, true, typingActivity(kind, status));
39963
40122
  else this.groups.setTyping(groupId, false);
39964
40123
  }
40124
+ /**
40125
+ * A call's signaling: read the contact's record fast, for its `_call`. A compatibility chat's only: a paired chat's
40126
+ * signals go only over its live session (WISP 601), so a read finds nothing for the call, and its data link keeps
40127
+ * its own fast reads (an offer's look would be cut short by a call's `fast: false`).
40128
+ */
39965
40129
  setFastPoll({ linkId, fast }) {
39966
- this.links.get(linkId)?.link?.session.setFastPoll(fast);
40130
+ const live = this.links.get(linkId);
40131
+ if (live?.stored.profile) return;
40132
+ live?.link?.session.setFastPoll(fast);
39967
40133
  }
39968
40134
  /** Used by the viewer: a request to a service some peer shares with us. */
39969
40135
  async request(peerPubKeyZ32, serviceId, request) {
@@ -40224,7 +40390,8 @@ var GhostlyNode = class GhostlyNode {
40224
40390
  for (const url of defaults) try {
40225
40391
  answered.push((await this.wallet.checkMint(url)).url);
40226
40392
  } catch (error) {
40227
- last = error;
40393
+ const unreachable = error instanceof Error ? parseEngineError(error.message) : null;
40394
+ last = unreachable?.code === "mintUnreachable" ? Object.assign(engineError("hostUnreachable", unreachable.values), { cause: error }) : error;
40228
40395
  }
40229
40396
  if (!answered.length) throw last instanceof Error ? last : /* @__PURE__ */ new Error("No mint answered");
40230
40397
  await this.updateSettings({ settings: {
@@ -41902,6 +42069,7 @@ var GhostlyNode = class GhostlyNode {
41902
42069
  if (!live?.stored.group) return;
41903
42070
  this.links.delete(linkId);
41904
42071
  this.edgeRtcOff.delete(linkId);
42072
+ this.edgeRtcRetried.delete(linkId);
41905
42073
  this.groupWakeReceived.delete(linkId);
41906
42074
  this.quietEdges.delete(linkId);
41907
42075
  this.groupNativeWaiting.delete(linkId);
@@ -42182,15 +42350,19 @@ var GhostlyNode = class GhostlyNode {
42182
42350
  },
42183
42351
  onDirectEvidence: (evidence) => {
42184
42352
  this.directPath.note(peer, evidence);
42185
- if (!entry && evidence !== "open" && evidence !== "closed") this.edgeWithoutRtc(linkId);
42353
+ if (evidence !== "open" && evidence !== "closed") this.edgeWithoutRtc(linkId);
42186
42354
  },
42187
42355
  onAnswersRefused: () => {
42188
- if (!entry) this.edgeWithoutRtc(linkId);
42356
+ this.edgeWithoutRtc(linkId);
42189
42357
  },
42190
42358
  onDataLinkState: (state) => {
42191
42359
  traceJoin(group, `link.${state}`, { role });
42192
42360
  if (state === "open" || live.dataLink === "open") live.lastSyncAt = Date.now();
42193
42361
  if (state === "open" && !entry) this.groups.edgeOpen(group, peer);
42362
+ if (state === "open") {
42363
+ const off = this.edgeRtcOff.get(linkId);
42364
+ if (off) off.held = true;
42365
+ }
42194
42366
  if (state === "open" && !entry && member()) this.desk.replay(linkId).catch(() => {});
42195
42367
  live.dataLink = state;
42196
42368
  if (entry && state !== "open" && live.entryDone) this.closeGroupLink(linkId).catch(() => {});
@@ -42232,13 +42404,19 @@ var GhostlyNode = class GhostlyNode {
42232
42404
  * app with no WebRTC (WISP 902 § Transports): it runs its native endpoints and says so in its packet (`_tr`, with no
42233
42405
  * `webrtc/1`), the member's app starts its own for it as it does for a Linux Desktop, and the two meet over Iroh
42234
42406
  * through its relay. Until the member comes back (`edgeRtcPresence`), or for this run of the app.
42407
+ * An entry session of a group's link too: there, the admin's packet was seen and the session never went live, and the
42408
+ * join stayed at "Let in: getting the group's keys" for as long as the joiner waited (2026-10-16).
42235
42409
  */
42236
42410
  async edgeWithoutRtc(linkId) {
42237
42411
  const live = this.links.get(linkId);
42238
- if (!live?.link || !live.stored.group || live.stored.groupEntry || this.shuttingDown || this.options.webrtcGroupLinks) return;
42412
+ if (!live?.link || !live.stored.group || this.shuttingDown || this.options.webrtcGroupLinks) return;
42239
42413
  if (this.edgeRtcOff.has(linkId) || this.keepsGroupNative(live.stored) || !Object.keys(this.nativeFactories).length) return;
42240
- this.edgeRtcOff.set(linkId, { away: false });
42414
+ this.edgeRtcOff.set(linkId, {
42415
+ away: false,
42416
+ held: this.edgeRtcRetried.has(linkId)
42417
+ });
42241
42418
  traceLink(live.myPubKeyZ32, "edge-rtc-off", {});
42419
+ if (live.stored.groupEntry) traceJoin(live.stored.group, "link.native", { role: live.stored.groupEntry });
42242
42420
  await this.restartEdge(linkId);
42243
42421
  }
42244
42422
  /**
@@ -42247,7 +42425,10 @@ var GhostlyNode = class GhostlyNode {
42247
42425
  * not a network that blocks direct connections. Held native for this whole run, the edge said so in its packet, the
42248
42426
  * member's app followed on its next starts, and past the group links' native slots members stayed unreachable for as
42249
42427
  * long as this app ran (2026-10-09). The edge tries WebRTC again, its `_tr` with `webrtc/1` again (WISP 902 §
42250
- * Transports); a network that really blocks it costs one more failed attempt per return.
42428
+ * Transports). Not an edge held native (`held`: it failed again after a return, or went live natively), nor while
42429
+ * this device's attempts say its network blocks direct connections: a member's packet that carries a WebRTC signal
42430
+ * and its `_tr` can leave its `_svc` out for size and read as gone, and on a VPN each such return put the edge back on
42431
+ * a WebRTC that died again (2026-10-16). A network that really blocks it costs one more failed attempt per network.
42251
42432
  */
42252
42433
  edgeRtcPresence(linkId, presence) {
42253
42434
  const off = this.edgeRtcOff.get(linkId);
@@ -42256,9 +42437,10 @@ var GhostlyNode = class GhostlyNode {
42256
42437
  off.away = true;
42257
42438
  return;
42258
42439
  }
42259
- if (!off.away) return;
42440
+ if (!off.away || off.held || this.directPath.blocked) return;
42260
42441
  const live = this.links.get(linkId);
42261
42442
  this.edgeRtcOff.delete(linkId);
42443
+ this.edgeRtcRetried.add(linkId);
42262
42444
  if (live) traceLink(live.myPubKeyZ32, "edge-rtc-back", {});
42263
42445
  this.groupNativeWaiting.delete(linkId);
42264
42446
  this.nativeFailed.delete(linkId);
@@ -42273,6 +42455,7 @@ var GhostlyNode = class GhostlyNode {
42273
42455
  await link.stop(false).catch(() => {});
42274
42456
  if (this.links.get(linkId) !== live || live.link || this.shuttingDown || !this.networkOn) return;
42275
42457
  this.startEdge(linkId);
42458
+ if (live.stored.groupEntry) this.links.get(linkId)?.link?.expectPeer();
42276
42459
  }
42277
42460
  startLink(linkId, messages) {
42278
42461
  const live = this.links.get(linkId);
@@ -42510,7 +42693,7 @@ var GhostlyNode = class GhostlyNode {
42510
42693
  onMessageEdit: stored.profile && !stored.group ? (edit) => this.receiveEdit(linkId, edit) : void 0,
42511
42694
  onEditReceipt: stored.profile && !stored.group ? (id, e) => this.editsFor(linkId).received(id, e) : void 0,
42512
42695
  onEditSupport: (supported) => {
42513
- if (supported && stored.profile && !stored.group) this.restoreCards(linkId).catch(() => {}).then(() => this.editsFor(linkId).flush()).catch(() => {});
42696
+ if (supported && stored.profile && !stored.group) this.restoreCardsSoon(linkId);
42514
42697
  },
42515
42698
  onWakeSupport: (supported) => {
42516
42699
  if (supported && stored.profile && !stored.group) this.shareWake(linkId);
@@ -42536,6 +42719,7 @@ var GhostlyNode = class GhostlyNode {
42536
42719
  if (peer && !live.stored.group) this.clock.peer(peer, packetAt, readBefore, readAt);
42537
42720
  },
42538
42721
  onDataLinkState: (state) => {
42722
+ if (this.links.get(linkId) !== live) return;
42539
42723
  const was = live.dataLink;
42540
42724
  live.dataLink = state;
42541
42725
  if (state === "open") {
@@ -42649,6 +42833,7 @@ var GhostlyNode = class GhostlyNode {
42649
42833
  onFileComplete: (id, direction) => this.fileSettled(this.localFileId(linkId, id, direction, { failed: false }), void 0, linkId),
42650
42834
  onFilesFrame: (frame) => this.fileDesk.handle(linkId, frame),
42651
42835
  onFilesSession: (open) => {
42836
+ if (this.links.get(linkId) !== live) return;
42652
42837
  this.fileDesk.session(linkId, open);
42653
42838
  if (open) this.sayWithdrawnFiles(linkId);
42654
42839
  },
@@ -42782,7 +42967,7 @@ var GhostlyNode = class GhostlyNode {
42782
42967
  get nativeFactories() {
42783
42968
  const relay = this.relaysHyperdht ? this.hyperdhtRelay : "";
42784
42969
  return {
42785
- ...relay ? { "hyperdht/1": async (seedB64) => (await import("./hyperdhtRelay-BVoWY2ua.js")).createRelayedHyperEndpoint(seedB64, relay) } : {},
42970
+ ...relay ? { "hyperdht/1": async (seedB64) => (await import("./hyperdhtRelay-CrgYQN-g.js")).createRelayedHyperEndpoint(seedB64, relay) } : {},
42786
42971
  ...this.options.nativeTransports,
42787
42972
  ...this.ownIrohRelays ? { "iroh/1": (seedB64) => this.options.nativeTransports["iroh/1"](seedB64, this.settings.irohRelays?.length ? { relays: [...this.settings.irohRelays] } : {}) } : {},
42788
42973
  ...this.options.irohWeb ? { "iroh/1": (seedB64) => createIrohWebEndpoint(seedB64, { relays: this.irohRelays }) } : {}
@@ -43631,4 +43816,4 @@ function signalKind(signal) {
43631
43816
  //#endregion
43632
43817
  export { EngineServer };
43633
43818
 
43634
- //# sourceMappingURL=server-CJNEKxzE.js.map
43819
+ //# sourceMappingURL=server-Bx3RGkBC.js.map