@ghostlytools/cli 1.1.2 → 1.1.4
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/README.md +6 -6
- package/SKILL.md +1 -1
- package/dist/assets/argon2id-088uQfnH.js.map +1 -1
- package/dist/assets/bark_ffi_wasm_bg-EY2uc0Bx.js.map +1 -1
- package/dist/assets/bitcoindevkit_bg-B-iLWrmX.js.map +1 -1
- package/dist/assets/breez_sdk_spark_wasm_bg.wasm +0 -0
- package/dist/assets/fedimint_client_wasm_bg-BtHHlFpp.js.map +1 -1
- package/dist/assets/{hyperdhtRelay-CVLCuqVr.js → hyperdhtRelay-BoxPHp7v.js} +3 -3
- package/dist/assets/{hyperdhtRelay-CVLCuqVr.js.map → hyperdhtRelay-BoxPHp7v.js.map} +1 -1
- package/dist/assets/legacy_ciphers-JJOu0JXL.js.map +1 -1
- package/dist/assets/{mainline-CKFpwXcB.js → mainline-dkA94eh2.js} +2 -2
- package/dist/assets/{mainline-CKFpwXcB.js.map → mainline-dkA94eh2.js.map} +1 -1
- package/dist/assets/nacl-fast-CDhNCYHs.js.map +1 -1
- package/dist/assets/noble_curves-DDWn5AH0.js.map +1 -1
- package/dist/assets/noble_hashes-B7AZyFyS.js.map +1 -1
- package/dist/assets/openpgp-BBFr-zHQ.js.map +1 -1
- package/dist/assets/{server-vGoe9MET.js → server-CXFTDnFI.js} +285 -99
- package/dist/assets/server-CXFTDnFI.js.map +1 -0
- package/dist/assets/sha512-BYId-G-T.js.map +1 -1
- package/dist/assets/{src-CJdPTl7Q.js → src-K3NijMq-.js} +455 -87
- package/dist/assets/src-K3NijMq-.js.map +1 -0
- package/dist/assets/storage-Bya5b2Xt.js.map +1 -1
- package/dist/assets/tree-store-N362nuzZ.js.map +1 -1
- package/dist/assets/unbzip2-stream-B1C-JX9L.js.map +1 -1
- package/dist/assets/{web-OUQl4Q7L.js → web-DwgdhfcY.js} +16 -16
- package/dist/assets/{web-OUQl4Q7L.js.map → web-DwgdhfcY.js.map} +1 -1
- package/dist/ghostly.mjs +55 -40
- package/dist/ghostly.mjs.map +1 -1
- package/examples/call-echo.mjs +1 -1
- package/package.json +7 -7
- package/dist/assets/server-vGoe9MET.js.map +0 -1
- package/dist/assets/src-CJdPTl7Q.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as fileBytesOf, c as removeFileBytes, d as __exportAll, i as fileBytes, n as blobDigest } from "./fileBytes-DxiJHmTk.js";
|
|
2
|
-
import { $a as
|
|
2
|
+
import { $a as HANDOFF_VERIFIED, $i as replySnippet, $n as freshHubs, $o as OwnInviteError, $s as fromBase64Url, $t as isPubkyKey, A as GROUP_EDIT_TEXT_BYTES, Ai as parseVoiceMeta, An as findEndpoint, Ar as newWakeToken, As as DEFAULT_RELAYS, At as encodeDidDhtPacket, Ba as HTTP_SERVICE_PROTO, Bi as COMMUNITY_REACTION_FRAME, Bo as enrollCancelFrame, Br as HOLD_DATED_BACK_MS, Bt as USDT_PUBLIC_RPC, C as groupWakes, Ca as GhostlyHttpError, Ci as safeBlobType, Co as readHandoffHave, Cr as nextPinNumber, Cs as firstDeviceSetSecret, D as GroupSession, Da as LIMITS, Dn as ENDPOINT, Do as readHandoffVerified, Dr as WAKE_CALL_INTERVAL_MS, Ds as signTurnRelease, Dt as didDhtDocument, E as GROUP_READ_NOTE, Ea as webLocalFetch, En as decodeBolt11, Eo as readHandoffTurnFrame, Er as readPin, Es as signTurnPacket, Fi as RateWindow, Fn as INVITE_TAKEN, Fo as verifySetUpdate, Fr as vapidKeysMatch, Fs as traceLink, Ft as ONCHAIN_PROVIDER, Ga as HANDOFF_DONE, Gi as queueReaction, Gn as MESH_HUBS, Go as enrollHelloFrame, Gr as readManifest, Gs as createIdentity, Gt as validatePaymentTarget, Ha as serviceIdFromName, Hi as REACTION_LIMITS, Ho as enrollDigits, Hr as HoldKeys, Ht as assertTokenUnits, Ii as dhtEditId, In as LIVENESS_PING_MS, Is as budgetRetryMs, It as PaymentPreflightError, Ja as HANDOFF_OFFER, Ji as readReaction, Jn as pickMeshHubs, Jo as readEnrollHello, Jr as capsDescriptors, Js as publicKeyFromZ32, Jt as SPARK_PROVIDER, Ka as HANDOFF_HAVE, Ki as reactionEmoji, Kn as mayBeHub, Ko as enrollProofFrame, Kr as CapsExchange, Ks as identityFromSeed, Kt as walletNetworkOf, La as parseCallSignal, Li as parseLinkPreview, Ln as pairedMessageFrame, Lr as EDIT_CAPABILITY, Ls as isDiscoveryBudgetError, Lt as SEPOLIA_TEST_USDT, M as parseCommunityEdit, Mi as parseVideoMeta, Mn as parseCashuRequestPayload, Mo as setAckFrame, Mr as relayRequest, Ms as currentRelays, Mt as signDidDhtPacket, N as mentionsMember, Ni as EDIT_BUFFER_MS, Nn as parseFedimintRequestPayload, No as setUpdateFrame, Nr as wakeRequest, Ns as normalizeRelayUrl, Nt as ETHEREUM_USDT, Oa as decodeControl, On as cashuRequestPayload, Oo as sealHandoffPiece, Or as WakeLimiter, Os as turnKeys, Ot as didDhtFromPublicKey, P as groupName, Pi as EDIT_RATE_WINDOW_MS, Pn as GhostLink, Po as signTombstone, Pr as checkPushEndpoint, Pt as EVM_TEST_CHAINS, Qa as HANDOFF_TIMINGS, Qi as readReply, Qn as doorHubs, Qo as readDeviceInvite, Qr as IDENTITY_REVOCATION_LABEL, Qs as bytesEqual, Ri as forwardedAgain, Rn as CarriedTransport, Rr as UPGRADE_CAPABILITY, Rs as withRequestOptions, Rt as SEPOLIA_TEST_USDT_FAUCET, S as groupWakeFrame, Sa as presenceSeenAt, So as readHandoffDone, Sr as mayPin, Ss as TURN_MAX, Ta as parseLocalTarget, Tn as engineText, To as readHandoffPake, Tr as pinNumberHolds, Ts as readTurnPacket, Ua as HANDOFF_ACK, Ui as ReactionWindow, Un as groupSignalFrame, Uo as enrollDoneFrame, Ur as HoldRefusedError, Ut as assertWholeSats, Va as LEGACY_SERVICES, Vi as GROUP_REACTION_FRAME, Vo as enrollCancelReason, Vr as HOLD_LIMITS, Vs as createRelayPayload, Vt as WALLET_NETWORKS, Wa as HANDOFF_CANCEL, Wi as nextReactionNumber, Wn as readGroupSignal, Wo as enrollGrantFrame, Wr as newMailbox, Wt as formatPaymentAmount, Xa as HANDOFF_PIECE_BYTES, Xi as wireReaction, Xn as beaconKeys, Xo as createDeviceInvite, Xt as sparkInvoiceDetails, Ya as HANDOFF_PAKE, Yi as validReactionNumber, Yn as COMMUNITY_TOPOLOGY, Yo as verifyEnrollProof, Yr as dialDescriptors, Ys as verify, Yt as sparkAddressKind, Za as HANDOFF_REQUEST, Zi as pairedReplyAuthor, Zn as beaconRecords, Zo as deviceInviteJoinerParams, Zs as sanitizeAvatar, Zt as isBitcoinAddress, _ as knockRecords, _a as verifyCommitSignature, _i as DHT_MESSAGE_TTL, _o as handoffStreamKey, _r as TypingSender, _s as TURN_RAISE_READ_TIMEOUT_MS, a as sealGroupTyping, aa as checkStatusCard, ai as newIdentityBinding, ao as handoffCancelFrame, ar as pickHubs, as as DEVICES_CAPABILITY, bi as dhtFileId, bo as readHandoffAck, bs as turnPutSummary, c as GROUP_READ_NOTE_COMMUNITY, ca as arrivalKey, ci as lastSharedWithMe, co as handoffDataFrame, cr as readBeaconHead, cs as deviceEchoFrame, d as encodeCommunityLink, da as receivedTimestamp, di as validExternalKey, do as handoffHelloFrame, dr as ChatFiles, ds as deviceLinkPairing, ea as BUTTON_ID, ec as randomBytes, ei as IdentityExchange, eo as filePartName, er as lobbyKeys, es as createChatInvite, et as isAtprotoDid, fa as shownTime, fi as TRANSPORTS, fo as handoffManifestFrames, fr as FILE_LIMITS, fs as devicePingFrame, g as knockIdentity, ga as rosterHas, go as handoffRetryAfter, h as entryParams, ha as MEMBER_KEY, hi as irohRelayUrl, ho as handoffReleaseFrame, hs as webCryptoSigner, i as openGroupTyping, ic as utf8Encode, ii as identityStatement, io as handoffBusyFrame, ir as newerHead, is as inviteOwnership, j as carryMentions, ja as RTC_CONFIG, jn as isFederationId, jo as readSetUpdate, jr as parseWakeFrame, js as RelayTransport, ka as encodeControl, ki as fileMessageText, kn as fedimintRequestPayload, kr as deviceWakeRequest, ks as turnName, kt as encodeDidDhtDeactivated, l as communityHasFrame, la as claimedTime, li as PeerProofs, lo as handoffDigest, lr as readLobby, ls as deviceEchoNonce, m as encodeGroupEntryLink, mi as irohDescriptor, mo as handoffPakeFrame, mr as transferStalled, ms as seedSigner, n as GroupTypingBoard, nc as toZ32, ni as emptyIdentityLedger, no as handoffAttemptFailed, nr as mergeBeacon, ns as encodeInviteCode, o as COMMUNITY_LIMITS, oa as sameButtonText, oi as receivedIdentityStatus, oo as handoffContext, or as rankHubs, os as ENROLL_CAPABILITY, p as decodeGroupEntryLink, pa as GROUP_MEMBER_CAP, pi as automaticTransport, po as handoffOfferFrame, pr as transferEnded, ps as newDeviceSetSecret, qa as HANDOFF_HELLO, qi as reactionIsNewer, qn as meshHubs, qo as readEnrollGrant, qr as DHT_TEXT_CAPABILITY, qs as identityFromSeedB64, qt as SPARK_NETWORKS, r as groupTypingEpoch, ra as STATUS_CARD_LIMITS, rc as utf8Decode, ri as identityRevocationValue, ro as handoffAttemptSucceeded, rr as mergeLobby, s as CommunitySession, sa as statusCardText, si as revokesIdentity, so as handoffContextProof, sr as readBeacon, ss as HANDOFF_CAPABILITY, tc as toBase64Url, to as handoffAttemptAllowed, tr as lobbyRecords, ts as decodeInviteCode, tt as normalizeAtprotoHandle, u as decodeCommunityLink, ua as heardTime, ui as emptyProofLedger, uo as handoffEphemeral, ur as shouldBeHub, us as deviceKeyZ32, v as mergeKnocks, va as edgeParams, vi as peekDhtMailbox, vo as handoffVersions, vs as TURN_SETTLE_MS, w as parseGroupWakeFrame, wa as formatLocalTarget, wn as engineError, wo as readHandoffHello, wr as pinIsNewer, ws as nextTurnPosition, x as GroupWakeLimiter, xa as RELAY_POLL_INTERVALS, xi as parseImageMeta, xo as readHandoffCancel, xr as PIN_LIMITS, xs as TURN_LAST_SEQUENCE, y as readKnocks, ya as DirectPathWatch, yi as DHT_FILE_CAPABILITY, yo as newHandoffId, yr as typingActivity, ys as classifyTurnRead, za as emptyLinkRecords, zi as readForwarded, zr as HOLD_CAPABILITY, zs as PacketTooLargeError, zt as TEST_USDT_FAUCET_AMOUNT } from "./src-K3NijMq-.js";
|
|
3
3
|
import { a as deleteDatabase, c as profileOpenFailure, d as wrap, i as databaseName, l as store, n as STORES$1, o as fileStore, r as clearProfileStores, s as openDb, t as CARD_INDEX, u as transact } from "./idb-Co2lObpU.js";
|
|
4
4
|
import { $ as onAdaptersChanged, A as intentRepository, C as ProviderSources, Ct as FileAppender, D as requireTurn, Dt as streamStored, E as refuseAway, Et as storedSize, F as ModeGate, G as fakesLightning, H as BITCOIN_INVOICE_ON_TESTNET, I as WrongNetworkError, J as mintNetwork, K as isTestMint, L as networkLabel, M as sealSeed, N as unsealSeed, O as setAwayWallets, P as ModeChanged, Q as identityProviders, R as CASHU_MINT_SOURCE, S as lightningPaying, St as paymentWireId, T as awayFrom, Tt as removeStored, U as DEFAULT_MINTS, V as WALLET_TYPES, W as TEST_MINT, X as BACKUP_MEDIA_TYPE, Y as paysItsOwnInvoices, Z as MAX_BACKUP_BYTES, _ as normalizePhrase, _t as pairedWireReply, a as createFailure, at as SourceConfigError, b as breezDatabasesFor, bt as replyTo, c as paymentNetwork, ct as networkMode, d as networkReason, dt as resolveAtprotoDid, et as registeredLightningProviders, f as rpcHost, ft as setOwnDidSource, g as isRecoveryPhrase, gt as groupReply, h as cardsKey, ht as chosenResolver, i as assertConfirmedReal, it as PROVIDER_PLATFORMS, j as newDeviceKey, k as setSingleWriterGate, l as paymentNetworksOf, lt as redact, m as LightningCards, mt as verifyIdentity, n as loadFedimintSdk, nt as reserveAdapterIds, o as createTiming, ot as SourceUnreachableError, p as CASHU_CARD, pt as boundedIdentityFetch, q as isWorthlessMint, r as WALLET_NAMES, rt as NothingSpentError, s as crossNetwork, st as isNothingSpentError, t as fedimintDatabase, tt as registeredOnchainProviders, u as walletInstances, ut as assertPublicServer, v as breezDatabase, vt as receivedPairedReply, w as sourceKey, wt as readStored$1, x as dropBreezDatabase, xt as paymentAlias, y as breezDatabaseInUse, yt as replyRef, z as cashuMint } from "../ghostly.mjs";
|
|
5
5
|
import { hkdf } from "@noble/hashes/hkdf.js";
|
|
@@ -190,6 +190,27 @@ var erc20 = new Interface([
|
|
|
190
190
|
]);
|
|
191
191
|
var same$1 = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
192
192
|
var CONFIRMATIONS = 2;
|
|
193
|
+
/** The JSON-RPC methods that only read: asked again once when they fail (see `UsdtAdapter.rpc`). */
|
|
194
|
+
var RETRIED_READS = /* @__PURE__ */ new Set([
|
|
195
|
+
"eth_chainId",
|
|
196
|
+
"eth_getCode",
|
|
197
|
+
"eth_call",
|
|
198
|
+
"eth_getBalance",
|
|
199
|
+
"eth_blockNumber",
|
|
200
|
+
"eth_getLogs",
|
|
201
|
+
"eth_getTransactionCount",
|
|
202
|
+
"eth_estimateGas",
|
|
203
|
+
"eth_getBlockByNumber",
|
|
204
|
+
"eth_maxPriorityFeePerGas",
|
|
205
|
+
"eth_gasPrice",
|
|
206
|
+
"eth_feeHistory",
|
|
207
|
+
"eth_getTransactionByHash",
|
|
208
|
+
"eth_getTransactionReceipt"
|
|
209
|
+
]);
|
|
210
|
+
var RETRY_AFTER_MS = 1e3;
|
|
211
|
+
/** Failures that would fail the same way again at once: not retried. */
|
|
212
|
+
var FINAL = /* @__PURE__ */ new WeakSet();
|
|
213
|
+
var timedOut = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
193
214
|
/**
|
|
194
215
|
* The `provider` a request carries to the contact. Which RPC pays is the payer's own choice, so it names none of
|
|
195
216
|
* this wallet's: a typed RPC URL may hold an API key (`/v3/<key>`), and every contact would read it. Older apps
|
|
@@ -259,7 +280,22 @@ var UsdtAdapter = class UsdtAdapter {
|
|
|
259
280
|
adapter.account = await adapter.manager.getAccount(0);
|
|
260
281
|
return adapter;
|
|
261
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* A read that fails is asked again, once, a moment later: a public RPC drops a request now and then, and a chain that
|
|
285
|
+
* just made a block can refuse a read of "latest" it no longer holds. Without it a network switch, a restore or New
|
|
286
|
+
* gave up on the first such answer. Never a broadcast or anything else that writes, nor a request that did not answer
|
|
287
|
+
* in time (the person would wait twice as long), nor one the server refused as such (4xx).
|
|
288
|
+
*/
|
|
262
289
|
async rpc(method, params = []) {
|
|
290
|
+
try {
|
|
291
|
+
return await this.ask(method, params);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (!RETRIED_READS.has(method) || FINAL.has(error)) throw error;
|
|
294
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_AFTER_MS));
|
|
295
|
+
return this.ask(method, params);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async ask(method, params) {
|
|
263
299
|
let response;
|
|
264
300
|
try {
|
|
265
301
|
response = await fetch(this.config.provider, {
|
|
@@ -275,9 +311,15 @@ var UsdtAdapter = class UsdtAdapter {
|
|
|
275
311
|
});
|
|
276
312
|
} catch (error) {
|
|
277
313
|
const reason = networkReason(error, rpcHost(this.config.provider));
|
|
278
|
-
|
|
314
|
+
const thrown = reason ? Object.assign(new Error(reason), { cause: error }) : error;
|
|
315
|
+
if (timedOut(error) && thrown && typeof thrown === "object") FINAL.add(thrown);
|
|
316
|
+
throw thrown;
|
|
317
|
+
}
|
|
318
|
+
if (!response.ok) {
|
|
319
|
+
const error = engineError("usdtRpcUnavailable");
|
|
320
|
+
if (response.status !== 429 && response.status < 500) FINAL.add(error);
|
|
321
|
+
throw error;
|
|
279
322
|
}
|
|
280
|
-
if (!response.ok) throw engineError("usdtRpcUnavailable");
|
|
281
323
|
const result = await response.json();
|
|
282
324
|
if (result.error || !Object.prototype.hasOwnProperty.call(result, "result")) throw new Error("USDT RPC rejected the operation");
|
|
283
325
|
return result.result;
|
|
@@ -1259,6 +1301,12 @@ var ArkadeAdapter = class ArkadeAdapter {
|
|
|
1259
1301
|
};
|
|
1260
1302
|
//#endregion
|
|
1261
1303
|
//#region ../browser/src/engine/paymentAdapters/backup.ts
|
|
1304
|
+
/**
|
|
1305
|
+
* Wallet database versions a backup may carry. 3 is what SDK 0.4.74-0.4.76 made (Ghostly 1.1.3 and earlier);
|
|
1306
|
+
* 4 is SDK 0.4.78's, which adds the vtxos `scriptUnspent` index. An older one is restored at its own version
|
|
1307
|
+
* and the SDK upgrades it the first time the wallet opens. arkBackupSchema.test.ts fails when the SDK moves on.
|
|
1308
|
+
*/
|
|
1309
|
+
var ARK_DB_VERSIONS = [3, 4];
|
|
1262
1310
|
var allowed = /* @__PURE__ */ new Set([
|
|
1263
1311
|
"vtxos",
|
|
1264
1312
|
"utxos",
|
|
@@ -1315,7 +1363,7 @@ async function snapshotArkDatabase(walletId) {
|
|
|
1315
1363
|
}
|
|
1316
1364
|
/** Only imports into a freshly allocated database; never replaces an existing wallet. */
|
|
1317
1365
|
async function restoreArkDatabase(walletId, snapshot) {
|
|
1318
|
-
if (snapshot.version
|
|
1366
|
+
if (!ARK_DB_VERSIONS.includes(snapshot.version) || !Array.isArray(snapshot.stores) || snapshot.stores.length > allowed.size || new Set(snapshot.stores.map((s) => s.name)).size !== snapshot.stores.length || snapshot.stores.some((s) => !allowed.has(s.name) || !Array.isArray(s.values) || !Array.isArray(s.keys) || s.values.length !== s.keys.length)) throw new Error("Unsupported Ark backup schema");
|
|
1319
1367
|
for (const name of [
|
|
1320
1368
|
"vtxos",
|
|
1321
1369
|
"utxos",
|
|
@@ -3330,7 +3378,7 @@ var loading$2;
|
|
|
3330
3378
|
/** Loads the WebAssembly once, on first use: profiles that never pick Breez never fetch its 12.8 MB. */
|
|
3331
3379
|
function loadBreezSdk() {
|
|
3332
3380
|
return loading$2 ??= (async () => {
|
|
3333
|
-
const breez = await import("./web-
|
|
3381
|
+
const breez = await import("./web-DwgdhfcY.js");
|
|
3334
3382
|
await breez.default();
|
|
3335
3383
|
return { async connect({ network, mnemonic, apiKey, storage }) {
|
|
3336
3384
|
const config = breez.defaultConfig(network);
|
|
@@ -8432,6 +8480,16 @@ var REVOCATIONS = "identityRevocations";
|
|
|
8432
8480
|
var REPUBLISH_MS$1 = 36e5;
|
|
8433
8481
|
/** How often the contacts' proofs are looked up for a revocation (and once shortly after start). */
|
|
8434
8482
|
var REVOCATION_CHECK_MS = 432e5;
|
|
8483
|
+
/**
|
|
8484
|
+
* After a contact withdraws a proof, how long to wait before each lookup for its revocation (WISP 300: removing a proof withdraws it from
|
|
8485
|
+
* every chat and revokes it). The notice goes out before the revocation is published, so not at once; each try backs
|
|
8486
|
+
* off, and the first that finds it stops the rest. The 12 h check covers it after that.
|
|
8487
|
+
*/
|
|
8488
|
+
var WITHDRAWN_REVOCATION_TRIES_MS = [
|
|
8489
|
+
2e4,
|
|
8490
|
+
6e4,
|
|
8491
|
+
18e4
|
|
8492
|
+
];
|
|
8435
8493
|
var DRAFT_TTL = 18e5;
|
|
8436
8494
|
var MAX_DRAFTS$1 = 4;
|
|
8437
8495
|
var MAX_PROOFS = 32;
|
|
@@ -8644,10 +8702,31 @@ var IdentityProofs = class {
|
|
|
8644
8702
|
}
|
|
8645
8703
|
for (const r of live) await this.publishRevocation(r).catch(() => {});
|
|
8646
8704
|
}
|
|
8647
|
-
/**
|
|
8705
|
+
/**
|
|
8706
|
+
* Looks up the contacts' proofs for a revocation (the Pkarr record only, not the provider): the ones standing, and the
|
|
8707
|
+
* ones withdrawn, which a removal also revokes. Not a proof already revoked, nor one past its expiry: its owner
|
|
8708
|
+
* publishes the revocation only until then.
|
|
8709
|
+
*/
|
|
8648
8710
|
async checkRevocations() {
|
|
8649
8711
|
if (!this.host.online()) return;
|
|
8650
|
-
for (const linkId of this.host.linkIds()) for (const r of this.host.ledger(linkId)?.received ?? []) if (
|
|
8712
|
+
for (const linkId of this.host.linkIds()) for (const r of this.host.ledger(linkId)?.received ?? []) if (this.revocable(r)) await this.exchange(linkId)?.recheck(r.id, { revocationOnly: true }).catch(() => {});
|
|
8713
|
+
}
|
|
8714
|
+
revocable(r) {
|
|
8715
|
+
return r.status !== "revoked" && Math.min(r.binding.expiresAt, r.verified.expiresAt ?? Infinity) > now();
|
|
8716
|
+
}
|
|
8717
|
+
/** A proof the contact just withdrew: looked up for its revocation a few times (`WITHDRAWN_REVOCATION_TRIES_MS`). */
|
|
8718
|
+
afterWithdrawal(linkId, id, tries = WITHDRAWN_REVOCATION_TRIES_MS) {
|
|
8719
|
+
const [wait, ...rest] = tries;
|
|
8720
|
+
if (wait === void 0) return;
|
|
8721
|
+
const timer = setTimeout(() => {
|
|
8722
|
+
this.timers = this.timers.filter((t) => t !== timer);
|
|
8723
|
+
const r = this.host.ledger(linkId)?.received.find((x) => x.id === id);
|
|
8724
|
+
if (!r || r.status !== "withdrawn" || !this.revocable(r)) return;
|
|
8725
|
+
(async () => {
|
|
8726
|
+
if ((this.host.online() ? await this.exchange(linkId)?.recheck(id, { revocationOnly: true }).catch(() => void 0) : void 0)?.status !== "revoked") this.afterWithdrawal(linkId, id, rest);
|
|
8727
|
+
})();
|
|
8728
|
+
}, wait);
|
|
8729
|
+
this.timers.push(timer);
|
|
8651
8730
|
}
|
|
8652
8731
|
views() {
|
|
8653
8732
|
return this.proofs.map((p) => this.viewOf(p));
|
|
@@ -8751,8 +8830,12 @@ var IdentityProofs = class {
|
|
|
8751
8830
|
}));
|
|
8752
8831
|
this.host.emit();
|
|
8753
8832
|
}
|
|
8754
|
-
frame(linkId, frame) {
|
|
8755
|
-
|
|
8833
|
+
async frame(linkId, frame) {
|
|
8834
|
+
const x = this.exchange(linkId);
|
|
8835
|
+
if (!x) return;
|
|
8836
|
+
const withdrawn = frame.t === "idp-withdraw" && typeof frame.id === "string" && this.host.ledger(linkId)?.received.some((r) => r.id === frame.id && r.status !== "withdrawn" && r.status !== "revoked");
|
|
8837
|
+
await x.receive(frame);
|
|
8838
|
+
if (withdrawn && this.host.ledger(linkId)?.received.some((r) => r.id === frame.id && r.status === "withdrawn")) this.afterWithdrawal(linkId, frame.id);
|
|
8756
8839
|
}
|
|
8757
8840
|
ready(linkId) {
|
|
8758
8841
|
if (this.greeted.has(linkId) || !this.host.channel(linkId)) return;
|
|
@@ -10799,7 +10882,7 @@ function nostrPost(note) {
|
|
|
10799
10882
|
}
|
|
10800
10883
|
/**
|
|
10801
10884
|
* Nostr: kind-1 notes and the kind-3 follow list of exactly the proven key, from the relays in the person's settings
|
|
10802
|
-
* (never relays named in events, hints or NIP-65 lists: docs/wisps/
|
|
10885
|
+
* (never relays named in events, hints or NIP-65 lists: docs/wisps/309-nostr-social.md). Replies are left out; a page
|
|
10803
10886
|
* reads twice as many notes to find ten that are not. Followers cannot be listed without trusting a counting service.
|
|
10804
10887
|
*/
|
|
10805
10888
|
var nostrActivity = {
|
|
@@ -13948,6 +14031,10 @@ var EnrollInviter = class extends Enrollment {
|
|
|
13948
14031
|
onPeerSeen: () => {
|
|
13949
14032
|
if (this.seen || this.over) return;
|
|
13950
14033
|
this.seen = true;
|
|
14034
|
+
if (this.view.role === "inviter" && this.view.step === "waiting") this.show({
|
|
14035
|
+
...this.view,
|
|
14036
|
+
seen: true
|
|
14037
|
+
});
|
|
13951
14038
|
this.timer(this.options.timing?.proofMs ?? 12e4, () => {
|
|
13952
14039
|
if (this.view.step === "waiting") this.fail("unreached");
|
|
13953
14040
|
});
|
|
@@ -16374,6 +16461,9 @@ var DeviceLinks = class {
|
|
|
16374
16461
|
const keeper = await this.turnKeeper();
|
|
16375
16462
|
if (!keeper) return null;
|
|
16376
16463
|
const outcome = await keeper.check(atStart);
|
|
16464
|
+
try {
|
|
16465
|
+
if (outcome) this.handoff?.turnRead?.(outcome);
|
|
16466
|
+
} catch {}
|
|
16377
16467
|
await this.refresh();
|
|
16378
16468
|
return outcome;
|
|
16379
16469
|
}
|
|
@@ -16570,6 +16660,11 @@ var SHOWN_AFTER_RELOAD = /* @__PURE__ */ new Set([
|
|
|
16570
16660
|
"cancelled"
|
|
16571
16661
|
]);
|
|
16572
16662
|
var reportOf = (busy) => busy === null ? null : typeof busy === "string" ? { why: busy } : busy;
|
|
16663
|
+
/**
|
|
16664
|
+
* What `handoff-busy` tells the taker of a local reason (WISP 06: the requesting device "Shows why"). A call is said as
|
|
16665
|
+
* itself, so the person waits for it to end; the rest stay "handoff": which wallet keeps the profile is said on this device.
|
|
16666
|
+
*/
|
|
16667
|
+
var wireWhy = (report) => report.why === "call" ? "call" : "handoff";
|
|
16573
16668
|
var name = (record, key) => record.deviceSet.find((slot) => slot?.key === key)?.name ?? "";
|
|
16574
16669
|
var slotOf = (record, key) => record.deviceSet.findIndex((slot) => slot?.key === key);
|
|
16575
16670
|
/** One session of a handoff with the other device: its own X25519 key, the other's hello, and the stream key. */
|
|
@@ -16929,6 +17024,25 @@ var HandoffGiver = class {
|
|
|
16929
17024
|
receive(from, frame) {
|
|
16930
17025
|
return this.exclusive(() => this.handle(from, frame)).catch(() => {});
|
|
16931
17026
|
}
|
|
17027
|
+
/**
|
|
17028
|
+
* A turn read on this standby found the record of `key` at `turn`: the device it released to took the turn, which is
|
|
17029
|
+
* what the `handoff-done` would have said. That frame goes once, as the taker reloads, and can be lost; without this
|
|
17030
|
+
* the screen said "Moving to …" for good, with no Use here, until that device offered the profile back.
|
|
17031
|
+
*/
|
|
17032
|
+
turnTaken(key, turn) {
|
|
17033
|
+
return this.exclusive(async () => {
|
|
17034
|
+
if (this.stopped || this.phase !== "released" || key !== this.peer || turn <= this.turn) return;
|
|
17035
|
+
await this.taken();
|
|
17036
|
+
}).catch(() => {});
|
|
17037
|
+
}
|
|
17038
|
+
/** The release was taken: the handoff note goes, and this device is a plain standby. */
|
|
17039
|
+
async taken() {
|
|
17040
|
+
await this.dropBreez();
|
|
17041
|
+
await this.ports.records.amend({ handoff: void 0 });
|
|
17042
|
+
this.phase = "idle";
|
|
17043
|
+
this.peer = null;
|
|
17044
|
+
this.changed();
|
|
17045
|
+
}
|
|
16932
17046
|
async handle(from, frame) {
|
|
16933
17047
|
if (this.stopped) return;
|
|
16934
17048
|
if (from === this.peer && frame.t.startsWith("handoff-")) this.heardAt = this.now();
|
|
@@ -16950,11 +17064,7 @@ var HandoffGiver = class {
|
|
|
16950
17064
|
case HANDOFF_DONE:
|
|
16951
17065
|
if (from !== this.peer || this.phase !== "released") return;
|
|
16952
17066
|
if (frame.t === "handoff-done" ? readHandoffDone(frame) === null : readHandoffTurnFrame(frame) === null) return;
|
|
16953
|
-
await this.
|
|
16954
|
-
await this.ports.records.amend({ handoff: void 0 });
|
|
16955
|
-
this.phase = "idle";
|
|
16956
|
-
this.peer = null;
|
|
16957
|
-
this.changed();
|
|
17067
|
+
await this.taken();
|
|
16958
17068
|
return;
|
|
16959
17069
|
case HANDOFF_CANCEL: {
|
|
16960
17070
|
const why = readHandoffCancel(frame);
|
|
@@ -17015,7 +17125,7 @@ var HandoffGiver = class {
|
|
|
17015
17125
|
this.failure = this.failWith(local);
|
|
17016
17126
|
this.changed();
|
|
17017
17127
|
}
|
|
17018
|
-
return busy(
|
|
17128
|
+
return busy(wireWhy(local));
|
|
17019
17129
|
}
|
|
17020
17130
|
const self = await this.ownSelf();
|
|
17021
17131
|
const versions = handoffVersions(self, session.peer);
|
|
@@ -17216,12 +17326,12 @@ var HandoffGiver = class {
|
|
|
17216
17326
|
while (this.now() < deadline && reportOf(await this.ports.busy(taker))?.why === "payment") await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
17217
17327
|
const still = reportOf(await this.ports.busy(taker));
|
|
17218
17328
|
if (still) {
|
|
17219
|
-
this.out(handoffBusyFrame(
|
|
17329
|
+
this.out(handoffBusyFrame(wireWhy(still), 30));
|
|
17220
17330
|
this.reset(still.why === "payment" ? "payment" : this.failWith(still));
|
|
17221
17331
|
return;
|
|
17222
17332
|
}
|
|
17223
17333
|
} else if (why) {
|
|
17224
|
-
this.out(handoffBusyFrame(
|
|
17334
|
+
this.out(handoffBusyFrame(wireWhy(why), 30));
|
|
17225
17335
|
this.reset(this.failWith(why));
|
|
17226
17336
|
return;
|
|
17227
17337
|
}
|
|
@@ -19095,7 +19205,7 @@ function canEdit(message) {
|
|
|
19095
19205
|
}
|
|
19096
19206
|
/** A group message's id: `<sender>:<epoch>:<seq>` in a private group, `<sender>:<epoch>:<commit>:<seq>` in a community. */
|
|
19097
19207
|
var GROUP_MESSAGE_ID = /^[a-z0-9]{52}:\d+:(?:[0-9a-f]{16}:)?\d+$/;
|
|
19098
|
-
/** A text of mine in a group (WISP
|
|
19208
|
+
/** A text of mine in a group (WISP 902 § Edits): one the group carried under its id, not a payment, a note or an event line. */
|
|
19099
19209
|
function canEditInGroup(message) {
|
|
19100
19210
|
return message.sender === "me" && message.linkId.startsWith("group:") && GROUP_MESSAGE_ID.test(message.id) && !message.file && !message.paymentId && !message.event && !message.groupPay;
|
|
19101
19211
|
}
|
|
@@ -19109,9 +19219,9 @@ function takesPeerEdit(message, text) {
|
|
|
19109
19219
|
/**
|
|
19110
19220
|
* The message after edit `seq`, made at `at`: the new text (and the preview and the status card that came with it, or
|
|
19111
19221
|
* none: each belongs to its version), the version it replaces kept in the history. A text that did not change adds no
|
|
19112
|
-
* version. A status card's update adds none either (WISP
|
|
19222
|
+
* version. A status card's update adds none either (WISP 405 · Status Cards): it replaces the card in place, and its
|
|
19113
19223
|
* text is only the card's fallback, so a bot's thousands of updates keep one card and no trail of versions. Buttons
|
|
19114
|
-
* (WISP
|
|
19224
|
+
* (WISP 406 · Message Buttons) are the exception: their text is the question people answer, so a new one keeps the
|
|
19115
19225
|
* version it replaces, and only a change of the buttons alone (the answer marked, closed) adds none.
|
|
19116
19226
|
*/
|
|
19117
19227
|
function withEdit(message, edit) {
|
|
@@ -19745,7 +19855,7 @@ var EditQueue = class {
|
|
|
19745
19855
|
/** A card's updates from someone else are applied (stored, shown) at most once per this long, per message. */
|
|
19746
19856
|
var CARD_APPLY_GAP_MS = 1e3;
|
|
19747
19857
|
/**
|
|
19748
|
-
* Received status card updates, paced per message (WISP
|
|
19858
|
+
* Received status card updates, paced per message (WISP 405 · Status Cards): the first applies at once, the rest within
|
|
19749
19859
|
* `CARD_APPLY_GAP_MS` wait, and only the highest edit number of them applies when the time is up. A sender that
|
|
19750
19860
|
* ignores its own pace costs this side one write and one render a second per card, whatever it sends.
|
|
19751
19861
|
*/
|
|
@@ -20771,8 +20881,8 @@ var HUB_CLOCK_SLACK_MS = COMMUNITY_TOPOLOGY.beaconEveryMs * 1.5;
|
|
|
20771
20881
|
/** A hub's clock is remembered this long after its entry was last in the beacon. */
|
|
20772
20882
|
var HUB_CLOCK_KEPT_MS = 6e5;
|
|
20773
20883
|
/**
|
|
20774
|
-
* Each hub's clock as its beacon entry showed it (WISP
|
|
20775
|
-
* hubs read their beacon the same way, WISP
|
|
20884
|
+
* Each hub's clock as its beacon entry showed it (WISP 903 · Group Community § Topology, Clocks that differ; the mesh's
|
|
20885
|
+
* hubs read their beacon the same way, WISP 902 · Group Mesh § Hubs). Held against mine as it is, a hub whose clock is
|
|
20776
20886
|
* a minute and a half from mine never looks fresh (its entry is "stale", or "not come yet"): a member saw no hub and a
|
|
20777
20887
|
* hub with such a clock was no hub to anyone. So a hub's clock is learned from its entry: when the entry moved between
|
|
20778
20888
|
* two readings of mine at most `readGapMs` apart, it was written between them, and what it says the time was then,
|
|
@@ -21082,9 +21192,11 @@ var Communities = class {
|
|
|
21082
21192
|
return !!id && this.host.linkReady(id, 2);
|
|
21083
21193
|
};
|
|
21084
21194
|
const hubs = freshHubs(this.hubs(live), this.now()).map((h) => h.key);
|
|
21195
|
+
const former = Object.entries(s.state.nicks).filter(([key]) => !rosterHas(s.roster, key));
|
|
21085
21196
|
return [{
|
|
21086
21197
|
...base,
|
|
21087
21198
|
name: s.name,
|
|
21199
|
+
...former.length ? { formerNames: Object.fromEntries(former) } : {},
|
|
21088
21200
|
status: s.status,
|
|
21089
21201
|
statusReason: s.state.statusReason,
|
|
21090
21202
|
epoch: s.epoch,
|
|
@@ -21223,14 +21335,14 @@ var Communities = class {
|
|
|
21223
21335
|
messageId: result.id
|
|
21224
21336
|
};
|
|
21225
21337
|
}
|
|
21226
|
-
/** An application frame to everyone in the group (WISP
|
|
21338
|
+
/** An application frame to everyone in the group (WISP 903 · Group Community § Payments). */
|
|
21227
21339
|
async sendApp(groupId, frame) {
|
|
21228
21340
|
const live = this.live.get(groupId);
|
|
21229
21341
|
if (!live) throw new Error("You are not in this group yet");
|
|
21230
21342
|
const result = await live.session.sendApp(frame, this.host.myNick?.(), this.now());
|
|
21231
21343
|
if ("error" in result) throw new Error(result.error);
|
|
21232
21344
|
}
|
|
21233
|
-
/** An edit of my message, through the group like any frame (WISP
|
|
21345
|
+
/** An edit of my message, through the group like any frame (WISP 903 · Group Community § Edits). An error when it cannot go now. */
|
|
21234
21346
|
async sendEdit(groupId, edit) {
|
|
21235
21347
|
const live = this.live.get(groupId);
|
|
21236
21348
|
if (!live) return "You are not in this group yet";
|
|
@@ -21635,7 +21747,7 @@ var Communities = class {
|
|
|
21635
21747
|
}
|
|
21636
21748
|
/**
|
|
21637
21749
|
* The members this hub may keep edges with: `hubCapacity`, or on an app with a budget of connections (a Mac, WISP
|
|
21638
|
-
*
|
|
21750
|
+
* 902 · Group Mesh § Hubs, Budget) what its other groups and 1:1 chats leave (`peerRoom`), less its edges to the
|
|
21639
21751
|
* other hubs and, for taking someone new (`entries`), its entry sessions.
|
|
21640
21752
|
*/
|
|
21641
21753
|
capacity(groupId, live, now, entries = true) {
|
|
@@ -21672,7 +21784,7 @@ var Communities = class {
|
|
|
21672
21784
|
return true;
|
|
21673
21785
|
}
|
|
21674
21786
|
/**
|
|
21675
|
-
* The newest message frame I hold, for the beacon's head (WISP
|
|
21787
|
+
* The newest message frame I hold, for the beacon's head (WISP 903 § Head): what a member's other profile on the
|
|
21676
21788
|
* same device compares with what it took, to show that something new was said while it was not running.
|
|
21677
21789
|
*/
|
|
21678
21790
|
newestFrame(live) {
|
|
@@ -22428,7 +22540,7 @@ var GONE_LINGER_MS = 15e3;
|
|
|
22428
22540
|
*/
|
|
22429
22541
|
var HUB_CLOCK_READ_GAP_MS = 75e3;
|
|
22430
22542
|
/**
|
|
22431
|
-
* The hubs of the private groups this device is in (WISP
|
|
22543
|
+
* The hubs of the private groups this device is in (WISP 902 · Group Mesh § Hubs): whether a group runs on hubs, who
|
|
22432
22544
|
* they are (the community's sealed beacon and lobbies, under the epoch's rendezvous secret), whether I am one, which
|
|
22433
22545
|
* edges that asks for, and passing frames on as a hub. `Groups` owns the sessions and the edges; this decides.
|
|
22434
22546
|
*/
|
|
@@ -22501,7 +22613,7 @@ var MeshHubs = class {
|
|
|
22501
22613
|
this.live.delete(groupId);
|
|
22502
22614
|
}
|
|
22503
22615
|
/**
|
|
22504
|
-
* The edges this app may keep in a group, within its budget of connections (WISP
|
|
22616
|
+
* The edges this app may keep in a group, within its budget of connections (WISP 902 · Group Mesh § Hubs, Budget):
|
|
22505
22617
|
* what the other groups' links do not hold, less what the groups I am a hub of still have to open. Undefined for an
|
|
22506
22618
|
* app without a budget. What this group holds itself does not count: a hub that stepped down would fit again at once.
|
|
22507
22619
|
*/
|
|
@@ -22912,7 +23024,7 @@ var MeshHubs = class {
|
|
|
22912
23024
|
}
|
|
22913
23025
|
/**
|
|
22914
23026
|
* As a hub, what the session took for the first time goes on to every other edge that is up: not back where it came
|
|
22915
|
-
* from, nor to its author. Everyone else takes it as a frame handed on (WISP
|
|
23027
|
+
* from, nor to its author. Everyone else takes it as a frame handed on (WISP 902 § Catch-up).
|
|
22916
23028
|
*/
|
|
22917
23029
|
passOn(groupId, session, from, frames) {
|
|
22918
23030
|
if (!this.live.get(groupId)?.hub || !frames.length) return 0;
|
|
@@ -22955,7 +23067,7 @@ function authorOf(frame) {
|
|
|
22955
23067
|
//#endregion
|
|
22956
23068
|
//#region ../browser/src/engine/groupTyping.ts
|
|
22957
23069
|
/**
|
|
22958
|
-
* Typing in private groups (WISP
|
|
23070
|
+
* Typing in private groups (WISP 902 · Group Mesh § Typing): this side's word goes sealed on every open edge to a
|
|
22959
23071
|
* member, and each member's word is shown until its `stop`, its message, or 6 s without a new `start`. Never stored.
|
|
22960
23072
|
* Community groups do not carry it yet (their frames are all stored messages).
|
|
22961
23073
|
*/
|
|
@@ -23022,7 +23134,7 @@ var GroupTypings = class {
|
|
|
23022
23134
|
//#endregion
|
|
23023
23135
|
//#region ../browser/src/engine/meshSignals.ts
|
|
23024
23136
|
/**
|
|
23025
|
-
* Edge signaling through members (WISP
|
|
23137
|
+
* Edge signaling through members (WISP 902 · Group Mesh § Signaling through members; `CarriedTransport` in core): which
|
|
23026
23138
|
* links an edge's packet is handed to, what a member does with one that is for someone else, and what happens to one
|
|
23027
23139
|
* that is for me. A packet is the edge's own Pkarr packet, signed under a key only its two ends derive: whoever
|
|
23028
23140
|
* carries it sees a signed blob, as a relay does, and the edge checks it as it checks a relay's answer.
|
|
@@ -23395,7 +23507,7 @@ var HERE_PER_MINUTE = 4;
|
|
|
23395
23507
|
* re-offer) slows in proportion, and an edge to someone away (read to notice them come back) by the square root:
|
|
23396
23508
|
* at 32 members, every four minutes and every minute, so 31 edges cost about 8 reads a minute when everyone is here
|
|
23397
23509
|
* and 32 when nobody is. Whoever reaches a member coming back first tells the others (`group-here`), who then look
|
|
23398
|
-
* fast for it (WISP
|
|
23510
|
+
* fast for it (WISP 902 § Cost per member).
|
|
23399
23511
|
*/
|
|
23400
23512
|
function meshEdgeScale(members, connected = true) {
|
|
23401
23513
|
const ratio = Math.max(1, (members - 1) / 8);
|
|
@@ -23489,15 +23601,15 @@ var Groups = class {
|
|
|
23489
23601
|
communities;
|
|
23490
23602
|
/** My messages and edits an edge of a private group took. */
|
|
23491
23603
|
frames = new FramesTaken();
|
|
23492
|
-
/** Hubs of the private groups past 16 members (WISP
|
|
23604
|
+
/** Hubs of the private groups past 16 members (WISP 902 · Group Mesh § Hubs). */
|
|
23493
23605
|
hubs;
|
|
23494
23606
|
/** Groups whose edges are being reconciled because their hubs changed: one at a time. */
|
|
23495
23607
|
hubReconcile = /* @__PURE__ */ new Set();
|
|
23496
23608
|
/** Groups a commit took me out of, and when: their edges close `REMOVED_LINGER_MS` later. */
|
|
23497
23609
|
removedAt = /* @__PURE__ */ new Map();
|
|
23498
|
-
/** Who is typing in each private group, and this side's word there (WISP
|
|
23610
|
+
/** Who is typing in each private group, and this side's word there (WISP 902 · Group Mesh § Typing). */
|
|
23499
23611
|
typings;
|
|
23500
|
-
/** Edge signaling through members (WISP
|
|
23612
|
+
/** Edge signaling through members (WISP 902 · Group Mesh § Signaling through members). */
|
|
23501
23613
|
signals;
|
|
23502
23614
|
constructor(host, store = db, timings = ENTRY_TIMINGS, communityTimings = COMMUNITY_TIMINGS, random, hubTimings = MESH_HUB_TIMINGS) {
|
|
23503
23615
|
this.host = host;
|
|
@@ -23583,6 +23695,7 @@ var Groups = class {
|
|
|
23583
23695
|
isAdmin: false,
|
|
23584
23696
|
members: [],
|
|
23585
23697
|
canSend: false,
|
|
23698
|
+
...group.formerNames ? { formerNames: group.formerNames } : {},
|
|
23586
23699
|
invitation: {
|
|
23587
23700
|
linkId: invitation.linkId,
|
|
23588
23701
|
contact: this.host.contactName(invitation.linkId) ?? "",
|
|
@@ -23677,7 +23790,7 @@ var Groups = class {
|
|
|
23677
23790
|
return this.isCommunity(groupId) ? this.communities.taken(groupId, key) : this.frames.count(groupId, key);
|
|
23678
23791
|
}
|
|
23679
23792
|
/**
|
|
23680
|
-
* Says an edit of my message to the group (WISP
|
|
23793
|
+
* Says an edit of my message to the group (WISP 902 § Edits): over the edges of a private group (to member `to`
|
|
23681
23794
|
* alone, when given), through a community like any frame. An error when it cannot go now.
|
|
23682
23795
|
*/
|
|
23683
23796
|
async sendEdit(groupId, edit, to) {
|
|
@@ -23762,7 +23875,7 @@ var Groups = class {
|
|
|
23762
23875
|
}
|
|
23763
23876
|
/**
|
|
23764
23877
|
* Sends a text; `messageId` is the id it is kept under here (what history, replies and reactions name). `forwarded`:
|
|
23765
|
-
* the hop count of a forwarded text (WISP
|
|
23878
|
+
* the hop count of a forwarded text (WISP 902 § Forwards). `card`: a checked status card, the text its fallback.
|
|
23766
23879
|
*/
|
|
23767
23880
|
async send(groupId, text, mentions = [], reply, forwarded, card) {
|
|
23768
23881
|
if (this.isCommunity(groupId)) return card ? this.communities.send(groupId, text, mentions, reply, forwarded, card) : this.communities.send(groupId, text, mentions, reply, forwarded);
|
|
@@ -23776,7 +23889,7 @@ var Groups = class {
|
|
|
23776
23889
|
};
|
|
23777
23890
|
}
|
|
23778
23891
|
/**
|
|
23779
|
-
* This side is typing in a private group (with what it is doing), or stopped (WISP
|
|
23892
|
+
* This side is typing in a private group (with what it is doing), or stopped (WISP 902 · Group Mesh § Typing).
|
|
23780
23893
|
* Nothing for a community: it does not carry typing yet.
|
|
23781
23894
|
*/
|
|
23782
23895
|
setTyping(groupId, typing, activity) {
|
|
@@ -23995,7 +24108,7 @@ var Groups = class {
|
|
|
23995
24108
|
}
|
|
23996
24109
|
/**
|
|
23997
24110
|
* The admin pins a member of a private group as a hub, excludes one from being a hub, or leaves it to the member's
|
|
23998
|
-
* app (`null`): said in the group's metadata, which every member keeps (WISP
|
|
24111
|
+
* app (`null`): said in the group's metadata, which every member keeps (WISP 902 · Group Mesh § Hubs).
|
|
23999
24112
|
*/
|
|
24000
24113
|
async setHub(groupId, key, role) {
|
|
24001
24114
|
if (this.isCommunity(groupId)) throw new Error("A community group chooses its hubs by itself");
|
|
@@ -24005,12 +24118,12 @@ var Groups = class {
|
|
|
24005
24118
|
rotate(groupId) {
|
|
24006
24119
|
return this.isCommunity(groupId) ? this.communities.rotate(groupId) : this.session(groupId).rotate();
|
|
24007
24120
|
}
|
|
24008
|
-
/** The admin sets or removes the group's picture; every member gets it over the edges (WISP
|
|
24121
|
+
/** The admin sets or removes the group's picture; every member gets it over the edges (WISP 902 § Metadata). */
|
|
24009
24122
|
async setPicture(groupId, picture) {
|
|
24010
24123
|
if (this.isCommunity(groupId)) return this.communities.setPicture(groupId, picture);
|
|
24011
24124
|
await this.session(groupId).setPicture(picture);
|
|
24012
24125
|
}
|
|
24013
|
-
/** The admin renames the group; every member gets the name over the edges, beside the picture (WISP
|
|
24126
|
+
/** The admin renames the group; every member gets the name over the edges, beside the picture (WISP 902 § Metadata). */
|
|
24014
24127
|
async rename(groupId, name) {
|
|
24015
24128
|
if (this.isCommunity(groupId)) return this.communities.rename(groupId, name);
|
|
24016
24129
|
await this.session(groupId).rename(name);
|
|
@@ -24173,7 +24286,7 @@ var Groups = class {
|
|
|
24173
24286
|
}
|
|
24174
24287
|
}
|
|
24175
24288
|
/**
|
|
24176
|
-
* Private groups past 16 members: their hubs (WISP
|
|
24289
|
+
* Private groups past 16 members: their hubs (WISP 902 · Group Mesh § Hubs), then their edges, when what the hubs
|
|
24177
24290
|
* ask for is not what exists (a hub came or went, I became one, a member asked me).
|
|
24178
24291
|
*/
|
|
24179
24292
|
async hubsTick(now) {
|
|
@@ -24224,7 +24337,7 @@ var Groups = class {
|
|
|
24224
24337
|
return Object.entries(this.stored.get(groupId)?.contacts ?? {}).find(([, id]) => id === linkId)?.[0];
|
|
24225
24338
|
}
|
|
24226
24339
|
/**
|
|
24227
|
-
* My packet for the edge to `to`, handed to links that reach that member or may pass it on (WISP
|
|
24340
|
+
* My packet for the edge to `to`, handed to links that reach that member or may pass it on (WISP 902 · Group Mesh
|
|
24228
24341
|
* § Signaling through members): how many took it, and whether one of them surely reaches that member. None in a
|
|
24229
24342
|
* community, whose members keep no edges with each other.
|
|
24230
24343
|
*/
|
|
@@ -24380,6 +24493,10 @@ var Groups = class {
|
|
|
24380
24493
|
if (existing?.state && existing.state.status === "active") return;
|
|
24381
24494
|
if (existing?.invitation?.seedB64) return;
|
|
24382
24495
|
if ([...this.stored.values()].filter((x) => x.invitation).length >= 32) return;
|
|
24496
|
+
const known = {
|
|
24497
|
+
...existing?.formerNames,
|
|
24498
|
+
...existing?.state?.nicks
|
|
24499
|
+
};
|
|
24383
24500
|
const invitation = {
|
|
24384
24501
|
id: g,
|
|
24385
24502
|
createdAt: Date.now(),
|
|
@@ -24390,7 +24507,8 @@ var Groups = class {
|
|
|
24390
24507
|
e: Number.isSafeInteger(frame.e) ? frame.e : 0,
|
|
24391
24508
|
n: Number.isSafeInteger(frame.n) ? frame.n : 1,
|
|
24392
24509
|
pieces: []
|
|
24393
|
-
}
|
|
24510
|
+
},
|
|
24511
|
+
...Object.keys(known).length ? { formerNames: known } : {}
|
|
24394
24512
|
};
|
|
24395
24513
|
if (existing) {
|
|
24396
24514
|
this.sessions.delete(g);
|
|
@@ -24483,6 +24601,8 @@ var Groups = class {
|
|
|
24483
24601
|
});
|
|
24484
24602
|
await this.store.putGroup(member);
|
|
24485
24603
|
if (!viaLink) await this.sessions.get(g).setNick(group.invitation.admin, this.host.contactName(linkId));
|
|
24604
|
+
const session = this.sessions.get(g);
|
|
24605
|
+
for (const [key, nick] of Object.entries(group.formerNames ?? {})) if (!session.state.nicks[key]) await session.setNick(key, nick);
|
|
24486
24606
|
await this.event(g, "joined", `You joined. ${GROUP_READ_NOTE}`, Date.now(), joined.state.chain.length - 1);
|
|
24487
24607
|
if (viaLink) traceJoin(g, "welcome.received");
|
|
24488
24608
|
if (viaLink) {
|
|
@@ -24581,7 +24701,7 @@ var Groups = class {
|
|
|
24581
24701
|
}
|
|
24582
24702
|
/**
|
|
24583
24703
|
* A reaction: from the member whose edge it came on (`group-react`), or signed by a member and passed on by a hub
|
|
24584
|
-
* (`group-reacted`). As a hub, a signed one goes on to the other edges (WISP
|
|
24704
|
+
* (`group-reacted`). As a hub, a signed one goes on to the other edges (WISP 902 · Group Mesh § Hubs).
|
|
24585
24705
|
*/
|
|
24586
24706
|
async reaction(groupId, session, from, raw) {
|
|
24587
24707
|
if (session.status !== "active" || raw.g !== groupId) return;
|
|
@@ -24992,7 +25112,7 @@ var Groups = class {
|
|
|
24992
25112
|
//#endregion
|
|
24993
25113
|
//#region ../browser/src/engine/groupEdges.ts
|
|
24994
25114
|
/**
|
|
24995
|
-
* An edge of a group as the group page shows it (WISP
|
|
25115
|
+
* An edge of a group as the group page shows it (WISP 902 § Mesh): open means group frames flow, which
|
|
24996
25116
|
* is what "reachable" means everywhere else in groups; what carries it; when the member was last heard.
|
|
24997
25117
|
*/
|
|
24998
25118
|
function edgeView(live, now = Date.now(), noSlot = false) {
|
|
@@ -25016,7 +25136,7 @@ function edgeView(live, now = Date.now(), noSlot = false) {
|
|
|
25016
25136
|
//#endregion
|
|
25017
25137
|
//#region ../browser/src/engine/groupPayments.ts
|
|
25018
25138
|
/**
|
|
25019
|
-
* Payments in a group (WISP
|
|
25139
|
+
* Payments in a group (WISP 902 § Payments). The money, the invoice and the receipts travel only on the edge
|
|
25020
25140
|
* between the two members, as they would on a 1:1 chat (the PaymentDesk does that part, with the edge's link as
|
|
25021
25141
|
* the chat). What the rest of the group learns is a `group-pay` note, sent by each side about its own part over
|
|
25022
25142
|
* every edge: who pays whom, how much, over what, and how it stands. A note is believed only from the member it
|
|
@@ -25335,7 +25455,7 @@ var GroupEdits = class {
|
|
|
25335
25455
|
host;
|
|
25336
25456
|
now;
|
|
25337
25457
|
buffer;
|
|
25338
|
-
/** Members' status card updates, applied at most once a second per message (WISP
|
|
25458
|
+
/** Members' status card updates, applied at most once a second per message (WISP 405 · Status Cards). */
|
|
25339
25459
|
cards;
|
|
25340
25460
|
sendPace = /* @__PURE__ */ new Map();
|
|
25341
25461
|
receivePace = /* @__PURE__ */ new Map();
|
|
@@ -25351,7 +25471,7 @@ var GroupEdits = class {
|
|
|
25351
25471
|
/**
|
|
25352
25472
|
* Edits a text of mine in a group: the new text here at once, and to the group as soon as the pace allows. Mentions
|
|
25353
25473
|
* the message had stay where their words still are; `added` are those picked while editing. `card`: the status card
|
|
25354
|
-
* of the new version, already checked (WISP
|
|
25474
|
+
* of the new version, already checked (WISP 405 · Status Cards); a card message then takes `STATUS_CARD_LIMITS.edits`.
|
|
25355
25475
|
*/
|
|
25356
25476
|
async edit(groupId, messageId, raw, added = [], card) {
|
|
25357
25477
|
const refuse = (error) => ({
|
|
@@ -25538,7 +25658,7 @@ var GroupEdits = class {
|
|
|
25538
25658
|
//#endregion
|
|
25539
25659
|
//#region ../browser/src/shared/payLinks.ts
|
|
25540
25660
|
/**
|
|
25541
|
-
* The desk's links to members of a community group (WISP
|
|
25661
|
+
* The desk's links to members of a community group (WISP 903 · Group Community § Payments): payments with a member
|
|
25542
25662
|
* go through the group, sealed to them, over `cpay:<group>:<member>`; a request to everyone over `cpay:<group>`.
|
|
25543
25663
|
*/
|
|
25544
25664
|
var pairLinkId = (groupId, member) => `cpay:${groupId}:${member}`;
|
|
@@ -25554,7 +25674,7 @@ function parsePayLink(linkId) {
|
|
|
25554
25674
|
//#endregion
|
|
25555
25675
|
//#region ../browser/src/engine/communityPay.ts
|
|
25556
25676
|
/**
|
|
25557
|
-
* Payments in community groups (WISP
|
|
25677
|
+
* Payments in community groups (WISP 903 · Group Community § Payments). Members have no edge to each other: what
|
|
25558
25678
|
* two members say about a payment (`pay-req`, `pay-ask`, `pay`, `pay-res`) is sealed to the other one inside a frame
|
|
25559
25679
|
* the whole group carries (`CommunitySession.sendPair`), so hubs relay it and keep it for a member who was away
|
|
25560
25680
|
* without being able to read it. A request to the whole group, and its "paid", go to everyone as an application
|
|
@@ -26003,7 +26123,7 @@ var HOLD_FETCH_BYTES_PER_S = 65536;
|
|
|
26003
26123
|
var holdFetchTimeoutMs = (max) => HOLD_FETCH_MS + Math.ceil(max / HOLD_FETCH_BYTES_PER_S) * 1e3;
|
|
26004
26124
|
var hex$2 = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
26005
26125
|
/**
|
|
26006
|
-
* Store-and-forward for away contacts (WISP
|
|
26126
|
+
* Store-and-forward for away contacts (WISP 404, `hold/1`), on both ends of every chat that turned it
|
|
26007
26127
|
* on: what this device sends while the contact is away is sealed and put in this device's own storage,
|
|
26008
26128
|
* named in a manifest, and pointed at from a small record on the DHT; what the contact held for this
|
|
26009
26129
|
* device is picked up from its pointer, checked and stored in order, and acknowledged on this device's
|
|
@@ -27513,7 +27633,7 @@ var ProfilePeek = class {
|
|
|
27513
27633
|
};
|
|
27514
27634
|
}
|
|
27515
27635
|
/**
|
|
27516
|
-
* A community group: its beacon, which hubs republish every 30 s with the newest frame they hold (WISP
|
|
27636
|
+
* A community group: its beacon, which hubs republish every 30 s with the newest frame they hold (WISP 903 § Head).
|
|
27517
27637
|
* New when this profile has not taken that frame. Nothing is opened but the head.
|
|
27518
27638
|
*/
|
|
27519
27639
|
async community(group, transport) {
|
|
@@ -27750,7 +27870,7 @@ var S3Store = class {
|
|
|
27750
27870
|
}
|
|
27751
27871
|
/**
|
|
27752
27872
|
* A URL that reads one object with no credential until `seconds` from now: what an away contact is
|
|
27753
|
-
* handed to pick up what was held for it (WISP
|
|
27873
|
+
* handed to pick up what was held for it (WISP 404). The secret key never leaves this device; the
|
|
27754
27874
|
* signature covers the exact object, method and lifetime, so the URL reads that object and nothing else.
|
|
27755
27875
|
*/
|
|
27756
27876
|
async presign(name, seconds) {
|
|
@@ -28613,7 +28733,8 @@ var CashuWallet = class CashuWallet {
|
|
|
28613
28733
|
let swap = (await this.swapsAt(mint)).find((s) => s.kind === "receive" && s.token === token);
|
|
28614
28734
|
if (swap?.finished) return {
|
|
28615
28735
|
amount: swap.tx?.amount ?? 0,
|
|
28616
|
-
mint
|
|
28736
|
+
mint,
|
|
28737
|
+
fee: swap.tx?.fee ?? 0
|
|
28617
28738
|
};
|
|
28618
28739
|
if (!swap) {
|
|
28619
28740
|
const preview = await (await this.wallet(mint)).prepareSwapToReceive(token);
|
|
@@ -28643,7 +28764,8 @@ var CashuWallet = class CashuWallet {
|
|
|
28643
28764
|
this.events.onChange();
|
|
28644
28765
|
return {
|
|
28645
28766
|
amount: credited,
|
|
28646
|
-
mint
|
|
28767
|
+
mint,
|
|
28768
|
+
fee: Math.max(0, faceValue - credited)
|
|
28647
28769
|
};
|
|
28648
28770
|
});
|
|
28649
28771
|
}
|
|
@@ -29279,6 +29401,17 @@ var CashuWallet = class CashuWallet {
|
|
|
29279
29401
|
* taken back.
|
|
29280
29402
|
*/
|
|
29281
29403
|
var UNIT = "sat";
|
|
29404
|
+
var satsWord = (n) => `${n} sat${n === 1 ? "" : "s"}`;
|
|
29405
|
+
/**
|
|
29406
|
+
* A refused send's review, once its ecash came back: why, and exactly what came back. Taking ecash back is a swap at
|
|
29407
|
+
* the mint, which can keep a fee: "The sats came back" said nothing of it. Without `back` (Fedimint, or not known),
|
|
29408
|
+
* the plain sentence.
|
|
29409
|
+
*/
|
|
29410
|
+
var refusedLine = (reason, back) => {
|
|
29411
|
+
const why = `Refused: ${reason.replace(/\.+$/, "")}.`;
|
|
29412
|
+
if (!back) return `${why} The sats came back.`;
|
|
29413
|
+
return back.fee > 0 ? `${why} ${satsWord(back.amount)} came back; the mint kept ${back.fee} as its fee.` : `${why} All ${satsWord(back.amount)} came back.`;
|
|
29414
|
+
};
|
|
29282
29415
|
var mintLightning = (wallet, network) => ({
|
|
29283
29416
|
createInvoice: (amount, paymentId) => wallet.receiveLightning(amount, paymentId, network),
|
|
29284
29417
|
quote: (invoice) => wallet.quoteInvoice(invoice, network),
|
|
@@ -29752,7 +29885,7 @@ var PaymentDesk = class {
|
|
|
29752
29885
|
return { paymentId: id };
|
|
29753
29886
|
}
|
|
29754
29887
|
/**
|
|
29755
|
-
* A request to a whole group (WISP
|
|
29888
|
+
* A request to a whole group (WISP 902 § Payments): one request, on one rail, sent to every member over their
|
|
29756
29889
|
* edge; the first payment that settles it wins. One rail only, because each enforces "once" by itself: a
|
|
29757
29890
|
* Lightning invoice can be paid once, and ecash is checked here before it is redeemed (a later token is refused
|
|
29758
29891
|
* unredeemed, so its payer takes it back). Two rails at once would let an invoice and a token both pay it.
|
|
@@ -29914,7 +30047,10 @@ var PaymentDesk = class {
|
|
|
29914
30047
|
endpoint: [ENDPOINT.bolt11, request.invoice]
|
|
29915
30048
|
}).catch(() => {});
|
|
29916
30049
|
}
|
|
29917
|
-
/**
|
|
30050
|
+
/**
|
|
30051
|
+
* Takes back ecash the peer never redeemed. If they did redeem it, the mint says so and the payment is settled.
|
|
30052
|
+
* Resolves to what came back of Cashu ecash (and the mint's fee for it); undefined when nothing did, or not Cashu.
|
|
30053
|
+
*/
|
|
29918
30054
|
reclaim(paymentId) {
|
|
29919
30055
|
let running = this.reclaims.get(paymentId);
|
|
29920
30056
|
if (!running) {
|
|
@@ -29945,7 +30081,7 @@ var PaymentDesk = class {
|
|
|
29945
30081
|
}
|
|
29946
30082
|
if (!payment?.token || payment.direction !== "out") throw new Error("Nothing to reclaim");
|
|
29947
30083
|
try {
|
|
29948
|
-
await this.wallet.receiveToken(payment.token, "reclaimed", payment.memo, { payment: () => ({
|
|
30084
|
+
const { amount, fee } = await this.wallet.receiveToken(payment.token, "reclaimed", payment.memo, { payment: () => ({
|
|
29949
30085
|
...this.current(payment),
|
|
29950
30086
|
state: "reclaimed",
|
|
29951
30087
|
token: void 0
|
|
@@ -29955,6 +30091,10 @@ var PaymentDesk = class {
|
|
|
29955
30091
|
state: "reclaimed",
|
|
29956
30092
|
token: void 0
|
|
29957
30093
|
});
|
|
30094
|
+
return {
|
|
30095
|
+
amount,
|
|
30096
|
+
fee
|
|
30097
|
+
};
|
|
29958
30098
|
} catch (error) {
|
|
29959
30099
|
const message = error instanceof Error ? error.message : String(error);
|
|
29960
30100
|
if (!/spent/i.test(message)) throw error;
|
|
@@ -29964,6 +30104,7 @@ var PaymentDesk = class {
|
|
|
29964
30104
|
state: "settled",
|
|
29965
30105
|
token: void 0
|
|
29966
30106
|
});
|
|
30107
|
+
return;
|
|
29967
30108
|
}
|
|
29968
30109
|
}
|
|
29969
30110
|
/** Asks this side made to pay, until the contact answers with a request (or two minutes pass). */
|
|
@@ -30052,7 +30193,7 @@ var PaymentDesk = class {
|
|
|
30052
30193
|
endpoints: [...payment.invoice ? [[ENDPOINT.bolt11, payment.invoice]] : [], ...payment.mints?.length ? [[ENDPOINT.cashu, cashuRequestPayload(payment.mints)]] : []]
|
|
30053
30194
|
};
|
|
30054
30195
|
}
|
|
30055
|
-
/** `held`: picked up from the contact's storage while it was away (WISP
|
|
30196
|
+
/** `held`: picked up from the contact's storage while it was away (WISP 404): only what this device allows counts, and only Cashu or Lightning. */
|
|
30056
30197
|
async onPaymentRequest(linkId, request, held = false) {
|
|
30057
30198
|
if (this.find(linkId, request.id)?.linkId === linkId) return;
|
|
30058
30199
|
if (held && (findEndpoint(request.endpoints, ENDPOINT.usdt) || findEndpoint(request.endpoints, ENDPOINT.arkade) || findEndpoint(request.endpoints, ENDPOINT.bark) || findEndpoint(request.endpoints, ENDPOINT.bitcoin) || findEndpoint(request.endpoints, ENDPOINT.fedimint) || findEndpoint(request.endpoints, ENDPOINT.spark))) return;
|
|
@@ -30379,8 +30520,8 @@ var PaymentDesk = class {
|
|
|
30379
30520
|
...payment,
|
|
30380
30521
|
error: result.error ?? "The payment was refused"
|
|
30381
30522
|
});
|
|
30382
|
-
await this.reclaim(payment.id).catch(() =>
|
|
30383
|
-
if (this.current(payment).state === "reclaimed") await this.host.onReviewedPaymentRefused?.(payment.id, result.error ?? "The payment was refused");
|
|
30523
|
+
const back = await this.reclaim(payment.id).catch(() => void 0);
|
|
30524
|
+
if (this.current(payment).state === "reclaimed") await this.host.onReviewedPaymentRefused?.(payment.id, result.error ?? "The payment was refused", back);
|
|
30384
30525
|
return;
|
|
30385
30526
|
}
|
|
30386
30527
|
Promise.resolve(this.host.onReviewedPaymentResult?.(payment.id)).catch(() => {});
|
|
@@ -32557,7 +32698,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
32557
32698
|
editQueues = /* @__PURE__ */ new Map();
|
|
32558
32699
|
/** The contacts' edits of messages not here yet. */
|
|
32559
32700
|
editBuffer = new EditBuffer();
|
|
32560
|
-
/** Received status card updates, applied at most once a second per message (WISP
|
|
32701
|
+
/** Received status card updates, applied at most once a second per message (WISP 405 · Status Cards). */
|
|
32561
32702
|
cardEdits = new CardEditPacer();
|
|
32562
32703
|
/** When each message's buttons were last pressed here (`pressButton`). */
|
|
32563
32704
|
pressedAt = /* @__PURE__ */ new Map();
|
|
@@ -32565,7 +32706,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
32565
32706
|
wakeLimiter = new WakeLimiter();
|
|
32566
32707
|
/** Call wake-ups have their own, shorter limit: a call is rarer than a message and cannot wait five minutes. */
|
|
32567
32708
|
callWakeLimiter = new WakeLimiter(WAKE_CALL_INTERVAL_MS);
|
|
32568
|
-
/** Mentions in private groups: one per member per 5 minutes, four per group per minute (WISP
|
|
32709
|
+
/** Mentions in private groups: one per member per 5 minutes, four per group per minute (WISP 902 · Group Mesh § Wake-up push). */
|
|
32569
32710
|
groupWakeLimiter = new GroupWakeLimiter();
|
|
32570
32711
|
/** `group-wake` frames read per edge, a handful a minute. */
|
|
32571
32712
|
groupWakeReceived = /* @__PURE__ */ new Map();
|
|
@@ -32603,7 +32744,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
32603
32744
|
paymentTimer = null;
|
|
32604
32745
|
/** Every minute, the reviews no Approve can send any more are cancelled (dropStaleReviews). */
|
|
32605
32746
|
staleReviewTimer;
|
|
32606
|
-
/** Group links: admins read knocks, joiners knock (WISP
|
|
32747
|
+
/** Group links: admins read knocks, joiners knock (WISP 902 § Entry link). */
|
|
32607
32748
|
groupEntryTimer = null;
|
|
32608
32749
|
walletView = {
|
|
32609
32750
|
mints: [],
|
|
@@ -32793,14 +32934,15 @@ var GhostlyNode = class GhostlyNode {
|
|
|
32793
32934
|
onReviewedPaymentResult: async (id) => {
|
|
32794
32935
|
await this.reconcilePayment({ id });
|
|
32795
32936
|
},
|
|
32796
|
-
onReviewedPaymentRefused: async (id, reason) => {
|
|
32937
|
+
onReviewedPaymentRefused: async (id, reason, back) => {
|
|
32797
32938
|
const intent = await intentRepository.get(id);
|
|
32798
32939
|
if (intent && ["submitted", "unknown"].includes(intent.review.state)) await intentRepository.put({
|
|
32799
32940
|
...intent,
|
|
32800
32941
|
review: {
|
|
32801
32942
|
...intent.review,
|
|
32802
32943
|
state: "failed",
|
|
32803
|
-
error:
|
|
32944
|
+
error: refusedLine(reason, back),
|
|
32945
|
+
...back ? { returned: back } : {}
|
|
32804
32946
|
}
|
|
32805
32947
|
});
|
|
32806
32948
|
this.emitState();
|
|
@@ -32872,7 +33014,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
32872
33014
|
await this.sparkWallets[network].adapter?.sync();
|
|
32873
33015
|
}
|
|
32874
33016
|
})));
|
|
32875
|
-
/** Store-and-forward for away contacts (WISP
|
|
33017
|
+
/** Store-and-forward for away contacts (WISP 404): items sealed into this device's own storage, picked up from the contact's. */
|
|
32876
33018
|
holdStore = null;
|
|
32877
33019
|
hold = new HoldEngine({
|
|
32878
33020
|
transport: void 0,
|
|
@@ -33094,7 +33236,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
33094
33236
|
publish: (seed, records) => this.transport.publish(identityFromSeed(seed), records),
|
|
33095
33237
|
resolve: async (key) => (await this.transport.resolve(key))?.records ?? null
|
|
33096
33238
|
});
|
|
33097
|
-
/** The profile's did:dht: a key of its own, public, never tied to a chat (WISP
|
|
33239
|
+
/** The profile's did:dht: a key of its own, public, never tied to a chat (WISP 310-did-dht). */
|
|
33098
33240
|
did = new ProfileDid({
|
|
33099
33241
|
online: () => this.networkOn,
|
|
33100
33242
|
emit: () => this.emitState(),
|
|
@@ -33322,7 +33464,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
33322
33464
|
communityPair: (groupId, sender, payload) => this.communityPay.receivePair(groupId, sender, payload)
|
|
33323
33465
|
});
|
|
33324
33466
|
/**
|
|
33325
|
-
* Payments in community groups (WISP
|
|
33467
|
+
* Payments in community groups (WISP 903 · Group Community § Payments): the desk pays and requests over a link
|
|
33326
33468
|
* per member that sends through the group, sealed to that member; the group request goes to everyone at once.
|
|
33327
33469
|
*/
|
|
33328
33470
|
communityPay = new CommunityPay({
|
|
@@ -33338,7 +33480,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
33338
33480
|
emit: () => this.emitState()
|
|
33339
33481
|
});
|
|
33340
33482
|
/**
|
|
33341
|
-
* Edits in groups (WISP
|
|
33483
|
+
* Edits in groups (WISP 902 § Edits): mine said to the group at the pace allowed, the members' on their messages.
|
|
33342
33484
|
* Never a new message: no sound, no unread.
|
|
33343
33485
|
*/
|
|
33344
33486
|
groupEdits = new GroupEdits({
|
|
@@ -33374,7 +33516,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
33374
33516
|
return this.outsideEdge(linkId) ? null : this.links.get(linkId)?.link ?? this.communityPay.link(linkId);
|
|
33375
33517
|
}
|
|
33376
33518
|
/**
|
|
33377
|
-
* Payments in groups (WISP
|
|
33519
|
+
* Payments in groups (WISP 902 § Payments): the money goes over the edge to one member through the desk, like a
|
|
33378
33520
|
* chat's; what the group sees of it goes to every member as a `group-pay` note.
|
|
33379
33521
|
*/
|
|
33380
33522
|
groupPayments = new GroupPayments({
|
|
@@ -35033,7 +35175,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
35033
35175
|
this.transport.networkChanged?.();
|
|
35034
35176
|
this.directPath.reset();
|
|
35035
35177
|
}
|
|
35036
|
-
for (const live of this.links.values()) live.link?.wake();
|
|
35178
|
+
for (const live of this.links.values()) live.link?.wake({ network: params.network });
|
|
35037
35179
|
this.hold.wake();
|
|
35038
35180
|
for (const network of WALLET_NETWORKS) {
|
|
35039
35181
|
this.lightnings[network].wake();
|
|
@@ -35053,7 +35195,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
35053
35195
|
}));
|
|
35054
35196
|
}
|
|
35055
35197
|
/**
|
|
35056
|
-
* `card`: a bot's status card (WISP
|
|
35198
|
+
* `card`: a bot's status card (WISP 405 · Status Cards), checked here by the sender's rule; the text is its fallback,
|
|
35057
35199
|
* written from the card when none is given. Only the headless runtime and SDKs send one: the app never offers it.
|
|
35058
35200
|
*/
|
|
35059
35201
|
async sendMessage(params) {
|
|
@@ -35068,7 +35210,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
35068
35210
|
error: "Nothing to send",
|
|
35069
35211
|
refused: true
|
|
35070
35212
|
};
|
|
35071
|
-
const live = this.
|
|
35213
|
+
const live = await this.linkWhenAdded(linkId);
|
|
35072
35214
|
const trimmed = text.trim();
|
|
35073
35215
|
const bytes = new TextEncoder().encode(trimmed).length;
|
|
35074
35216
|
if (live && !live.stored.profile && !(live.link?.isDataLinkOpen && trimmed.length <= LIMITS.maxChatMessageBytes / 4) && bytes > 500) return {
|
|
@@ -35293,7 +35435,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
35293
35435
|
static dhtByChoice(live) {
|
|
35294
35436
|
return live?.stored.deliveryMode === "dht" || live?.link?.dhtDelivery?.peerMode === "dht";
|
|
35295
35437
|
}
|
|
35296
|
-
/** A status card a caller asks to send, by the sender's rule (WISP
|
|
35438
|
+
/** A status card a caller asks to send, by the sender's rule (WISP 405 · Status Cards), or why it cannot go. */
|
|
35297
35439
|
static cardToSend(raw) {
|
|
35298
35440
|
const checked = checkStatusCard(raw);
|
|
35299
35441
|
return "error" in checked ? `Status card: ${checked.error}` : checked.card;
|
|
@@ -35418,7 +35560,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
35418
35560
|
return !!live?.caps?.peer?.capabilities.includes(EDIT_CAPABILITY);
|
|
35419
35561
|
}
|
|
35420
35562
|
/**
|
|
35421
|
-
* A question of mine with buttons went on the DHT floor or into a hold, which carry its text alone (WISP
|
|
35563
|
+
* A question of mine with buttons went on the DHT floor or into a hold, which carry its text alone (WISP 406 · Message
|
|
35422
35564
|
* Buttons): its buttons are due to go again live. Once: a row that had them restored already stays so.
|
|
35423
35565
|
*/
|
|
35424
35566
|
async buttonsWentBare(linkId, message) {
|
|
@@ -35426,7 +35568,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
35426
35568
|
await db.patchMessage(linkId, message.id, (current) => current.sender === "me" && current.card?.kind === "buttons" && !current.buttonsRestore ? { buttonsRestore: "due" } : null);
|
|
35427
35569
|
}
|
|
35428
35570
|
/**
|
|
35429
|
-
* Buttons the contact may not have (WISP
|
|
35571
|
+
* Buttons the contact may not have (WISP 406 · Message Buttons): a question whose text went on the DHT floor or into a
|
|
35430
35572
|
* hold reached the contact as text alone, and a copy under the same id that comes live later is taken as the one
|
|
35431
35573
|
* already there. Once the chat is live with an app that shows buttons (`buttons/1`) and takes edits, each such
|
|
35432
35574
|
* question goes again as an edit of its buttons alone: the same text, so no version and no edit mark. Once per
|
|
@@ -35526,7 +35668,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
35526
35668
|
}
|
|
35527
35669
|
await this.outboxFor(linkId).transmit(messageId, { manual: true });
|
|
35528
35670
|
}
|
|
35529
|
-
/** The contact is away, and both sides chose to hold what is sent meanwhile (WISP
|
|
35671
|
+
/** The contact is away, and both sides chose to hold what is sent meanwhile (WISP 404). */
|
|
35530
35672
|
holdingFor(live) {
|
|
35531
35673
|
return !!live.stored.profile && !!live.link && !live.link.isDataLinkOpen && this.hold.canHold(live.stored.id);
|
|
35532
35674
|
}
|
|
@@ -35875,7 +36017,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
35875
36017
|
...forwarded && { forwarded }
|
|
35876
36018
|
};
|
|
35877
36019
|
const wire = reply && pairedWireReply(reply);
|
|
35878
|
-
const live = this.links.get(linkId);
|
|
36020
|
+
const live = this.linksAdding.has(linkId) ? await this.linkWhenAdded(linkId) : this.links.get(linkId);
|
|
35879
36021
|
const fail = (error) => {
|
|
35880
36022
|
const transfer = {
|
|
35881
36023
|
state: "failed",
|
|
@@ -36634,7 +36776,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
36634
36776
|
return this.groups.messages(groupId);
|
|
36635
36777
|
}
|
|
36636
36778
|
/**
|
|
36637
|
-
* Presses a button of a message someone else sent to a chat or a group (`group:<id>`), WISP
|
|
36779
|
+
* Presses a button of a message someone else sent to a chat or a group (`group:<id>`), WISP 406 · Message Buttons:
|
|
36638
36780
|
* a reply to it whose text is the button's label and whose `r` names the button, so the author learns who pressed
|
|
36639
36781
|
* what, and an app without buttons reads an ordinary reply. Refused: my own message, a button it does not have,
|
|
36640
36782
|
* buttons the author closed, a question I already answered with a `once` button, a second press within a second.
|
|
@@ -36725,7 +36867,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
36725
36867
|
result.error ??= error;
|
|
36726
36868
|
};
|
|
36727
36869
|
const groupId = to.startsWith("group:") ? to.slice(6) : void 0;
|
|
36728
|
-
const live = groupId ? void 0 : this.
|
|
36870
|
+
const live = groupId ? void 0 : await this.linkWhenAdded(to);
|
|
36729
36871
|
if (groupId ? !this.membership(groupId) : !live || live.stored.group !== void 0) {
|
|
36730
36872
|
note(groupId ? "You are not in this group" : "Chat not found");
|
|
36731
36873
|
continue;
|
|
@@ -37099,7 +37241,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
37099
37241
|
if (typing === true && this.settings.sendTyping !== false) live.link?.setTyping(true, typingActivity(kind, status));
|
|
37100
37242
|
else live.link?.setTyping(false);
|
|
37101
37243
|
}
|
|
37102
|
-
/** WISP
|
|
37244
|
+
/** WISP 902 · Group Mesh § Typing: the same word in a private group, on its edges; a community says nothing yet. */
|
|
37103
37245
|
setGroupTyping({ groupId, typing, kind, status }) {
|
|
37104
37246
|
if (typeof groupId !== "string") return;
|
|
37105
37247
|
if (typing === true && this.settings.sendTyping !== false) this.groups.setTyping(groupId, true, typingActivity(kind, status));
|
|
@@ -37851,6 +37993,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
37851
37993
|
const paying = params.target.method === "cashu" ? mintNetwork(params.target.provider) : walletNetworkOf(params.target.network);
|
|
37852
37994
|
if (params.network && params.network !== paying) throw new Error(crossNetwork(params.network, paying));
|
|
37853
37995
|
if (params.linkId) {
|
|
37996
|
+
await this.linkWhenAdded(params.linkId);
|
|
37854
37997
|
const link = this.paymentLink(params.linkId);
|
|
37855
37998
|
if (!link) throw new Error("The peer is offline");
|
|
37856
37999
|
const payee = this.links.get(params.linkId)?.stored.peerPubKeyZ32 ?? parsePayLink(params.linkId)?.member ?? "";
|
|
@@ -37951,8 +38094,8 @@ var GhostlyNode = class GhostlyNode {
|
|
|
37951
38094
|
}).catch(() => []);
|
|
37952
38095
|
}
|
|
37953
38096
|
/** `network`: the Cashu card of that network sends (its mints). `confirmedReal`: required on Mainnet. */
|
|
37954
|
-
sendPayment(params) {
|
|
37955
|
-
const live = this.
|
|
38097
|
+
async sendPayment(params) {
|
|
38098
|
+
const live = await this.linkWhenAdded(params.linkId);
|
|
37956
38099
|
if (live && this.holdingFor(live)) throw new Error("Ecash is not held for an away contact. Send a request instead, or wait until they are back.");
|
|
37957
38100
|
return this.desk.send({
|
|
37958
38101
|
...params,
|
|
@@ -37961,7 +38104,8 @@ var GhostlyNode = class GhostlyNode {
|
|
|
37961
38104
|
}
|
|
37962
38105
|
/** `network`: the card's network; the request is paid only by a wallet of that network. */
|
|
37963
38106
|
/** `card`: the Lightning card the request's invoice comes from; absent, the network's default for receiving. */
|
|
37964
|
-
requestPayment(params) {
|
|
38107
|
+
async requestPayment(params) {
|
|
38108
|
+
await this.linkWhenAdded(params.linkId);
|
|
37965
38109
|
return this.desk.request({
|
|
37966
38110
|
linkId: params.linkId,
|
|
37967
38111
|
amount: params.amount,
|
|
@@ -37973,7 +38117,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
37973
38117
|
...params.rail === "cashu" || params.rail === "lightning" ? { rail: params.rail } : {}
|
|
37974
38118
|
});
|
|
37975
38119
|
}
|
|
37976
|
-
/** A request any member of a group may pay, once (WISP
|
|
38120
|
+
/** A request any member of a group may pay, once (WISP 902 § Payments). */
|
|
37977
38121
|
requestGroupPayment(params) {
|
|
37978
38122
|
const group = this.groups.views().find((g) => g.id === params.groupId);
|
|
37979
38123
|
if (group?.status !== "active") throw new Error("You are not in this group");
|
|
@@ -37984,8 +38128,9 @@ var GhostlyNode = class GhostlyNode {
|
|
|
37984
38128
|
});
|
|
37985
38129
|
}
|
|
37986
38130
|
/** Paying on a card without a request (Ark, Bark, Spark, USDT, on-chain): the contact's app answers with one, on this card's network. */
|
|
37987
|
-
askToPay(params) {
|
|
38131
|
+
async askToPay(params) {
|
|
37988
38132
|
if (params.method !== "arkade" && params.method !== "usdt" && params.method !== "bark" && params.method !== "bitcoin" && params.method !== "fedimint" && params.method !== "spark") throw new Error("Only Ark, Bark, Spark, USDT, on-chain Bitcoin and Fedimint are paid this way");
|
|
38133
|
+
await this.linkWhenAdded(params.linkId);
|
|
37989
38134
|
return this.desk.ask({
|
|
37990
38135
|
...params,
|
|
37991
38136
|
network: this.net(params.network)
|
|
@@ -38621,10 +38766,21 @@ var GhostlyNode = class GhostlyNode {
|
|
|
38621
38766
|
}
|
|
38622
38767
|
}
|
|
38623
38768
|
/**
|
|
38769
|
+
* Whether a device code may be used in this profile now, asked before the person presses Add (WISP 06 § User
|
|
38770
|
+
* experience): `ready`, or why not: `set` (this profile is already on several devices), `in-use` (it holds something,
|
|
38771
|
+
* `inUse`), `loading` (what it holds is not known yet). The page says up front that a new profile is made for the
|
|
38772
|
+
* code; `deviceEnrollJoin` still decides, and refuses as before.
|
|
38773
|
+
*/
|
|
38774
|
+
async deviceEnrollReady() {
|
|
38775
|
+
if (this.starting) await this.starting;
|
|
38776
|
+
if (await readDeviceRecord(databaseName()).catch(() => null)) return "set";
|
|
38777
|
+
return this.inUse() ?? "ready";
|
|
38778
|
+
}
|
|
38779
|
+
/**
|
|
38624
38780
|
* Whether this profile holds anything a person would lose if it became a standby here: a chat, a group, an identity,
|
|
38625
|
-
* money or a payment, or a wallet with keys of its own. The wallets a new profile gets by itself (Mainnet Cashu
|
|
38626
|
-
* USDT, `walletSetup.ts`) count only once they hold, wait for or have moved
|
|
38627
|
-
* join. `loading`: what they hold is not known yet (the engine is starting, or the USDT balance was not read).
|
|
38781
|
+
* money or a payment, or a wallet with keys of its own. The wallets a new profile gets by itself (Mainnet Cashu, with
|
|
38782
|
+
* the Cashu mints' Lightning card, and USDT, `walletSetup.ts`) count only once they hold, wait for or have moved
|
|
38783
|
+
* money: otherwise no new install could join. `loading`: what they hold is not known yet (the engine is starting, or the USDT balance was not read).
|
|
38628
38784
|
*/
|
|
38629
38785
|
inUse() {
|
|
38630
38786
|
if (this.links.size > 0 || this.groups.views().length > 0 || this.identities.views().length > 0) return "in-use";
|
|
@@ -38633,7 +38789,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
38633
38789
|
const networks = [this.walletView, ...Object.values(this.walletView.networks ?? {})];
|
|
38634
38790
|
const holds = (network) => !!network && (money(network.balance) || (network.history?.length ?? 0) > 0 || (network.awaiting?.length ?? 0) > 0 || money(network.setAside) || money(network.openSwaps) || money(network.swapsAmount) || money(network.unconfirmed) || !!network.usdt?.configured && (money(network.usdt.balance) || money(network.usdt.gasBalance)));
|
|
38635
38791
|
if (networks.some(holds)) return "in-use";
|
|
38636
|
-
const firstRun = (wallet) => wallet.network === "mainnet" && (wallet.type === "cashu" || wallet.type === "usdt");
|
|
38792
|
+
const firstRun = (wallet) => wallet.network === "mainnet" && (wallet.type === "cashu" || wallet.type === "usdt" || wallet.type === "lightning" && wallet.card === "cashu");
|
|
38637
38793
|
if ((this.walletView.wallets ?? []).some((wallet) => !firstRun(wallet))) return "in-use";
|
|
38638
38794
|
return networks.some((network) => !!network?.usdt?.configured && (network.usdt.locked || !network.usdt.read)) ? "loading" : null;
|
|
38639
38795
|
}
|
|
@@ -38647,6 +38803,11 @@ var GhostlyNode = class GhostlyNode {
|
|
|
38647
38803
|
}
|
|
38648
38804
|
/** The giver of this active device: answers a pull, makes a push, runs pass 1 while this engine goes on. */
|
|
38649
38805
|
handoffGiver = null;
|
|
38806
|
+
/** A call is on in one of the pages (`setCallOn`, told by the host for all of them): a handoff waits for it to end. */
|
|
38807
|
+
callOn = false;
|
|
38808
|
+
setCallOn({ on }) {
|
|
38809
|
+
this.callOn = on === true;
|
|
38810
|
+
}
|
|
38650
38811
|
async startHandoff() {
|
|
38651
38812
|
const host = handoffProfileHost(), links = this.deviceLinks;
|
|
38652
38813
|
if (!host || !links || this.handoffGiver || this.options.singleDevice) return;
|
|
@@ -38733,6 +38894,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
38733
38894
|
* or anything this build cannot judge. Null: the wallets move or stay home by their plan.
|
|
38734
38895
|
*/
|
|
38735
38896
|
async handoffBusy(taker) {
|
|
38897
|
+
if (this.callOn) return { why: "call" };
|
|
38736
38898
|
if (this.limitedMode || !this.walletsStarted) return { why: "loading" };
|
|
38737
38899
|
await this.refreshWallet().catch(() => {});
|
|
38738
38900
|
const { refusal } = await this.handoffPlan(taker);
|
|
@@ -38890,6 +39052,15 @@ var GhostlyNode = class GhostlyNode {
|
|
|
38890
39052
|
async existingLink(id) {
|
|
38891
39053
|
return await this.linksAdding.get(id) ?? id;
|
|
38892
39054
|
}
|
|
39055
|
+
/**
|
|
39056
|
+
* The chat by its id, once it has started if it is still being added: the app may send to it by that id (a message,
|
|
39057
|
+
* a file, a forward, a payment) right after a reload, and that send waits for the chat rather than say "You are
|
|
39058
|
+
* offline" for that moment. A chat not being added is returned at once, offline or not; one whose adding failed is gone.
|
|
39059
|
+
*/
|
|
39060
|
+
async linkWhenAdded(linkId) {
|
|
39061
|
+
await this.linksAdding.get(linkId)?.catch(() => {});
|
|
39062
|
+
return this.links.get(linkId);
|
|
39063
|
+
}
|
|
38893
39064
|
addLink(params, inviteCode) {
|
|
38894
39065
|
const id = identityFromSeedB64(params.seedB64).pubKeyZ32.slice(0, 16);
|
|
38895
39066
|
const adding = this.addLinkNow(params, inviteCode).finally(() => {
|
|
@@ -39256,7 +39427,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
39256
39427
|
if (native) this.ensureNativeEndpoints(linkId);
|
|
39257
39428
|
}
|
|
39258
39429
|
/**
|
|
39259
|
-
* Whether a group's link (an edge, an entry session) runs native endpoints (WISP
|
|
39430
|
+
* Whether a group's link (an edge, an entry session) runs native endpoints (WISP 902 § Transports): where this app has
|
|
39260
39431
|
* no WebRTC (the Linux Desktop), and where the member's has none, as its packet said. Between two apps that have
|
|
39261
39432
|
* WebRTC it runs none, as before: a group of eight would otherwise hold seven listeners per transport for nothing.
|
|
39262
39433
|
*/
|
|
@@ -39269,7 +39440,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
39269
39440
|
* (`DirectEvidence`): a network that lets no direct connection through (a VPN, a firewall, on either side). WebRTC is
|
|
39270
39441
|
* all an edge between two apps that have it runs, and there is no TURN server unless someone set one, so the edge
|
|
39271
39442
|
* never went live, and the member stayed unreachable for as long as that network lasted. The edge starts again as on an
|
|
39272
|
-
* app with no WebRTC (WISP
|
|
39443
|
+
* app with no WebRTC (WISP 902 § Transports): it runs its native endpoints and says so in its packet (`_tr`, with no
|
|
39273
39444
|
* `webrtc/1`), the member's app starts its own for it as it does for a Linux Desktop, and the two meet over Iroh
|
|
39274
39445
|
* through its relay. For this run of the app only.
|
|
39275
39446
|
*/
|
|
@@ -39318,6 +39489,8 @@ var GhostlyNode = class GhostlyNode {
|
|
|
39318
39489
|
statusCardSupport: true,
|
|
39319
39490
|
buttonsSupport: true,
|
|
39320
39491
|
wakeSupport: true,
|
|
39492
|
+
upgradeSupport: true,
|
|
39493
|
+
peerUpgrades: () => !!live.caps?.peer?.capabilities.includes(UPGRADE_CAPABILITY),
|
|
39321
39494
|
servicesSupport: this.options.servicesSupport ?? (this.options.platform ?? "web") !== "web",
|
|
39322
39495
|
dht: stored.profile ? {
|
|
39323
39496
|
state: stored.dhtDeliveryState,
|
|
@@ -39716,6 +39889,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
39716
39889
|
DHT_FILE_CAPABILITY,
|
|
39717
39890
|
...live?.stored.hold?.enabled ? [HOLD_CAPABILITY] : [],
|
|
39718
39891
|
"files/2",
|
|
39892
|
+
UPGRADE_CAPABILITY,
|
|
39719
39893
|
...GhostlyNode.testNoEdit() ? [] : [EDIT_CAPABILITY],
|
|
39720
39894
|
...cashu || lightning ? ["payments/1"] : [],
|
|
39721
39895
|
...cashu ? ["payments-cashu/1"] : [],
|
|
@@ -39772,7 +39946,7 @@ var GhostlyNode = class GhostlyNode {
|
|
|
39772
39946
|
get nativeFactories() {
|
|
39773
39947
|
const relay = this.relaysHyperdht ? this.hyperdhtRelay : "";
|
|
39774
39948
|
return {
|
|
39775
|
-
...relay ? { "hyperdht/1": async (seedB64) => (await import("./hyperdhtRelay-
|
|
39949
|
+
...relay ? { "hyperdht/1": async (seedB64) => (await import("./hyperdhtRelay-BoxPHp7v.js")).createRelayedHyperEndpoint(seedB64, relay) } : {},
|
|
39776
39950
|
...this.options.nativeTransports,
|
|
39777
39951
|
...this.ownIrohRelays ? { "iroh/1": (seedB64) => this.options.nativeTransports["iroh/1"](seedB64, this.settings.irohRelays?.length ? { relays: [...this.settings.irohRelays] } : {}) } : {},
|
|
39778
39952
|
...this.options.irohWeb ? { "iroh/1": (seedB64) => createIrohWebEndpoint(seedB64, { relays: this.irohRelays }) } : {}
|
|
@@ -40324,6 +40498,11 @@ var EngineServer = class {
|
|
|
40324
40498
|
* it too: the extension's peer runs with no tab open, and a tab opened during the ring used to show no call.
|
|
40325
40499
|
*/
|
|
40326
40500
|
offers = /* @__PURE__ */ new Map();
|
|
40501
|
+
/**
|
|
40502
|
+
* The clients a call is on in (`setCallOn`): the engine hears whether any is, so a handoff waits for every page's
|
|
40503
|
+
* call (WISP 06 § States and events). A client that goes away (a tab closed mid-call) is taken out with it.
|
|
40504
|
+
*/
|
|
40505
|
+
onCall = /* @__PURE__ */ new Set();
|
|
40327
40506
|
constructor(options = {}) {
|
|
40328
40507
|
this.node = new GhostlyNode({
|
|
40329
40508
|
onAttention: (event) => this.broadcast({
|
|
@@ -40393,6 +40572,7 @@ var EngineServer = class {
|
|
|
40393
40572
|
this.clients.delete(client);
|
|
40394
40573
|
this.histories.delete(client);
|
|
40395
40574
|
if (this.clients.size === 0) this.node.setActiveLink({ linkId: null });
|
|
40575
|
+
if (this.onCall.delete(client)) this.node.setCallOn({ on: this.onCall.size > 0 });
|
|
40396
40576
|
}
|
|
40397
40577
|
async handle(client, request) {
|
|
40398
40578
|
if (request?.kind !== "request") return;
|
|
@@ -40401,12 +40581,18 @@ var EngineServer = class {
|
|
|
40401
40581
|
id: request.id
|
|
40402
40582
|
};
|
|
40403
40583
|
if (request.method === "setCallSignal") this.offers.delete(request.params?.linkId ?? "");
|
|
40584
|
+
let params = request.params;
|
|
40585
|
+
if (request.method === "setCallOn") {
|
|
40586
|
+
if (request.params?.on === true) this.onCall.add(client);
|
|
40587
|
+
else this.onCall.delete(client);
|
|
40588
|
+
params = { on: this.onCall.size > 0 };
|
|
40589
|
+
}
|
|
40404
40590
|
try {
|
|
40405
40591
|
await this.ready;
|
|
40406
40592
|
const method = this.node[request.method];
|
|
40407
40593
|
if (typeof method !== "function") throw new Error(`Unknown method: ${request.method}`);
|
|
40408
40594
|
if (this.node.limited && !LIMITED_MODE_METHODS.has(String(request.method))) throw new Error(LIMITED_MODE_ERROR);
|
|
40409
|
-
response.result = await method.call(this.node,
|
|
40595
|
+
response.result = await method.call(this.node, params);
|
|
40410
40596
|
} catch (error) {
|
|
40411
40597
|
response.error = error instanceof Error ? error.message : String(error);
|
|
40412
40598
|
}
|
|
@@ -40493,4 +40679,4 @@ function signalKind(signal) {
|
|
|
40493
40679
|
//#endregion
|
|
40494
40680
|
export { EngineServer, hyperdhtRelayProblem as n, server_exports as t };
|
|
40495
40681
|
|
|
40496
|
-
//# sourceMappingURL=server-
|
|
40682
|
+
//# sourceMappingURL=server-CXFTDnFI.js.map
|