@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
|
@@ -4349,7 +4349,7 @@ function buildSdpFromSignal(signal) {
|
|
|
4349
4349
|
function sdpHasCandidates(sdp) {
|
|
4350
4350
|
return /^a=candidate:/m.test(sdp ?? "");
|
|
4351
4351
|
}
|
|
4352
|
-
/** How long to keep collecting after the candidate we were waiting for showed up. */
|
|
4352
|
+
/** How long to keep collecting after the candidate we were waiting for showed up (a call's; a chat's data link sets its own). */
|
|
4353
4353
|
var ICE_SETTLE_MS = 400;
|
|
4354
4354
|
/** IPv4 ranges that are not reachable from the internet: this network, private, shared (CGNAT), loopback, link-local, the documentation ones, benchmarking, multicast and up. */
|
|
4355
4355
|
var NOT_GLOBAL_IPV4 = [
|
|
@@ -4393,7 +4393,7 @@ function isGlobalIpv4Host(candidate) {
|
|
|
4393
4393
|
* (`isGlobalIpv4Host`): a device with no NAT gets none, and its offers waited
|
|
4394
4394
|
* the whole timeout whenever gathering did not complete.
|
|
4395
4395
|
*/
|
|
4396
|
-
function waitForIceGathering(pc, timeoutMs = 1e4, { stallMs, fresh = false } = {}) {
|
|
4396
|
+
function waitForIceGathering(pc, timeoutMs = 1e4, { stallMs, fresh = false, settleMs = ICE_SETTLE_MS, hostBoundMs } = {}) {
|
|
4397
4397
|
return new Promise((resolve) => {
|
|
4398
4398
|
if (pc.iceGatheringState === "complete" && (!fresh || sdpHasCandidates(pc.localDescription?.sdp))) {
|
|
4399
4399
|
resolve();
|
|
@@ -4406,6 +4406,7 @@ function waitForIceGathering(pc, timeoutMs = 1e4, { stallMs, fresh = false } = {
|
|
|
4406
4406
|
const finish = () => {
|
|
4407
4407
|
clearTimeout(timeout);
|
|
4408
4408
|
if (stalled) clearTimeout(stalled);
|
|
4409
|
+
if (bounded) clearTimeout(bounded);
|
|
4409
4410
|
if (settle) clearTimeout(settle);
|
|
4410
4411
|
pc.removeEventListener("icegatheringstatechange", onState);
|
|
4411
4412
|
pc.removeEventListener("icecandidate", onCandidate);
|
|
@@ -4415,13 +4416,16 @@ function waitForIceGathering(pc, timeoutMs = 1e4, { stallMs, fresh = false } = {
|
|
|
4415
4416
|
const stalled = stallMs === void 0 ? null : setTimeout(() => {
|
|
4416
4417
|
if (!found && !sdpHasCandidates(pc.localDescription?.sdp)) finish();
|
|
4417
4418
|
}, stallMs);
|
|
4419
|
+
const bounded = hostBoundMs === void 0 ? null : setTimeout(() => {
|
|
4420
|
+
if (found || sdpHasCandidates(pc.localDescription?.sdp)) finish();
|
|
4421
|
+
}, hostBoundMs);
|
|
4418
4422
|
const onState = () => {
|
|
4419
4423
|
if (pc.iceGatheringState === "complete" && (!fresh || sdpHasCandidates(pc.localDescription?.sdp))) finish();
|
|
4420
4424
|
};
|
|
4421
4425
|
const onCandidate = (event) => {
|
|
4422
4426
|
if (event.candidate) found = true;
|
|
4423
4427
|
const line = event.candidate?.candidate;
|
|
4424
|
-
if (!settle && line && (line.includes(wanted) || !usesTurn && isGlobalIpv4Host(line))) settle = setTimeout(finish,
|
|
4428
|
+
if (!settle && line && (line.includes(wanted) || !usesTurn && isGlobalIpv4Host(line))) settle = setTimeout(finish, settleMs);
|
|
4425
4429
|
};
|
|
4426
4430
|
pc.addEventListener("icegatheringstatechange", onState);
|
|
4427
4431
|
pc.addEventListener("icecandidate", onCandidate);
|
|
@@ -6087,11 +6091,27 @@ var DirectPathWatch = class {
|
|
|
6087
6091
|
this.onChange(blocked);
|
|
6088
6092
|
}
|
|
6089
6093
|
};
|
|
6094
|
+
//#endregion
|
|
6095
|
+
//#region ../core/src/datalink.ts
|
|
6096
|
+
/**
|
|
6097
|
+
* An attempt ended from outside (another transport went live first) that had both descriptions for this long and no
|
|
6098
|
+
* connection says what one that failed does: a path that exists connects within a second or two.
|
|
6099
|
+
*/
|
|
6100
|
+
var STALLED_EVIDENCE_MS = 5e3;
|
|
6090
6101
|
/** A candidate another network can reach: server reflexive (STUN answered), relayed (TURN), or learnt from the peer. */
|
|
6091
6102
|
var PUBLIC_CANDIDATE = /^a=candidate:.* typ (srflx|relay|prflx)\b/m;
|
|
6092
6103
|
var CONNECT_TIMEOUT_MS = 9e4;
|
|
6093
6104
|
var ICE_GATHERING_TIMEOUT_MS = 5e3;
|
|
6094
6105
|
/**
|
|
6106
|
+
* An offer with its host candidates waits this long at most for a server reflexive one (STUN answered), not
|
|
6107
|
+
* `ICE_GATHERING_TIMEOUT_MS`. Where the STUN servers answer, they do in some 50 ms (0.44 s an offer took here, with the
|
|
6108
|
+
* settle); where they do not, every offer of a first pairing waited the full 5 s, twice when the inviter offered too
|
|
6109
|
+
* (One's e2e runs: 13 offers of 89 at 5 s, 9 more at 1.3-3 s, 2026-10-05). An offer without one still connects to most
|
|
6110
|
+
* contacts (their checks learn this side's address from its own); behind a restricted NAT on the contact's side it may
|
|
6111
|
+
* not, and the next offer of the chat then waits as long as it takes (`fullGather`).
|
|
6112
|
+
*/
|
|
6113
|
+
var OFFER_HOST_GATHER_MS = 2e3;
|
|
6114
|
+
/**
|
|
6095
6115
|
* A connection with no candidate at all by then has stalled: Chromium, rarely and under load, gathers none, and an
|
|
6096
6116
|
* offer or answer without one can never connect. It is made again in its place, up to `GATHER_ATTEMPTS` in all.
|
|
6097
6117
|
*/
|
|
@@ -6105,6 +6125,13 @@ var DataLink = class {
|
|
|
6105
6125
|
state = "idle";
|
|
6106
6126
|
pc = null;
|
|
6107
6127
|
myOfferTs = 0;
|
|
6128
|
+
/**
|
|
6129
|
+
* An offer of this side's that went without a public candidate came to nothing (it was answered, and no path
|
|
6130
|
+
* connected): the next one waits for its STUN servers as long as it takes (`ICE_GATHERING_TIMEOUT_MS`).
|
|
6131
|
+
*/
|
|
6132
|
+
fullGather = false;
|
|
6133
|
+
/** The offer out has no public candidate (as it went: the connection's description gains any that come later). */
|
|
6134
|
+
offerLacksPublic = false;
|
|
6108
6135
|
lastSignalTs = 0;
|
|
6109
6136
|
/** The peer's description as this connection was given it: Chrome shows it as `remoteDescription` only once applied. */
|
|
6110
6137
|
remoteSdp = null;
|
|
@@ -6118,17 +6145,35 @@ var DataLink = class {
|
|
|
6118
6145
|
this.options = options;
|
|
6119
6146
|
}
|
|
6120
6147
|
/** Offers a connection to the peer. Resolves once the offer is published. */
|
|
6121
|
-
|
|
6148
|
+
/**
|
|
6149
|
+
* Makes an offer and publishes it. `gate`: gathered ahead, while the caller still finds out whether the peer is there
|
|
6150
|
+
* (a joiner reading the inviter's record): the link stays idle meanwhile, and the offer goes once the gate says yes.
|
|
6151
|
+
* On no, the connection is dropped, nothing having been said, and `"held"` is the answer.
|
|
6152
|
+
*/
|
|
6153
|
+
async connect(gate) {
|
|
6122
6154
|
if (this.state !== "idle") return;
|
|
6123
6155
|
this.answered = null;
|
|
6124
|
-
|
|
6125
|
-
|
|
6156
|
+
if (!gate) {
|
|
6157
|
+
this.setState("offering");
|
|
6158
|
+
this.options.setFastPoll(true, true);
|
|
6159
|
+
}
|
|
6126
6160
|
try {
|
|
6127
6161
|
const pc = await this.gathered(true, async (pc) => {
|
|
6128
6162
|
await pc.setLocalDescription(await pc.createOffer());
|
|
6129
6163
|
});
|
|
6130
6164
|
if (!pc) return;
|
|
6165
|
+
if (gate) {
|
|
6166
|
+
const go = await gate;
|
|
6167
|
+
if (this.pc !== pc || this.state !== "idle") return;
|
|
6168
|
+
if (!go) {
|
|
6169
|
+
this.teardown();
|
|
6170
|
+
return "held";
|
|
6171
|
+
}
|
|
6172
|
+
this.setState("offering");
|
|
6173
|
+
this.options.setFastPoll(true, true);
|
|
6174
|
+
}
|
|
6131
6175
|
this.myOfferTs = Date.now();
|
|
6176
|
+
this.offerLacksPublic = !PUBLIC_CANDIDATE.test(pc.localDescription?.sdp ?? "");
|
|
6132
6177
|
const signal = {
|
|
6133
6178
|
t: "o",
|
|
6134
6179
|
ts: this.myOfferTs,
|
|
@@ -6221,11 +6266,17 @@ var DataLink = class {
|
|
|
6221
6266
|
}
|
|
6222
6267
|
}
|
|
6223
6268
|
close() {
|
|
6224
|
-
const
|
|
6269
|
+
const stalled = Date.now() - this.connectingSince >= STALLED_EVIDENCE_MS;
|
|
6270
|
+
const evidence = stalled ? this.directEvidence() : null;
|
|
6271
|
+
this.noteNoPublic(stalled);
|
|
6225
6272
|
this.answered = null;
|
|
6226
6273
|
this.reset();
|
|
6227
6274
|
if (evidence) this.options.onDirect?.(evidence);
|
|
6228
6275
|
}
|
|
6276
|
+
/** This side's offer went with no public candidate, was answered, and came to nothing: the next offer gathers for as long as it takes. */
|
|
6277
|
+
noteNoPublic(ended) {
|
|
6278
|
+
if (ended && this.myOfferTs && this.offerLacksPublic && this.state === "connecting") this.fullGather = true;
|
|
6279
|
+
}
|
|
6229
6280
|
/**
|
|
6230
6281
|
* What this attempt says about direct connections if it ends now without opening: nothing unless both descriptions
|
|
6231
6282
|
* were exchanged (this side's offer answered, or its answer out). Then, what this device gathered: no public
|
|
@@ -6245,6 +6296,7 @@ var DataLink = class {
|
|
|
6245
6296
|
const answered = this.state === "connecting" ? this.answered : null;
|
|
6246
6297
|
const left = answered ? CONNECT_TIMEOUT_MS - (Date.now() - answered.at) : 0;
|
|
6247
6298
|
const evidence = this.directEvidence();
|
|
6299
|
+
this.noteNoPublic(true);
|
|
6248
6300
|
if (answered && answered.again < 2 && left > REANSWER_MARGIN_MS && this.options.offerStanding?.(answered.offer.ts)) {
|
|
6249
6301
|
answered.again++;
|
|
6250
6302
|
traceLink(this.options.myPubKeyZ32, "reanswer", {
|
|
@@ -6296,7 +6348,11 @@ var DataLink = class {
|
|
|
6296
6348
|
await describe(pc);
|
|
6297
6349
|
if (this.pc !== pc) return null;
|
|
6298
6350
|
const gathering = Date.now();
|
|
6299
|
-
await waitForIceGathering(pc, ICE_GATHERING_TIMEOUT_MS, {
|
|
6351
|
+
await waitForIceGathering(pc, ICE_GATHERING_TIMEOUT_MS, {
|
|
6352
|
+
stallMs: GATHER_STALL_MS,
|
|
6353
|
+
settleMs: 150,
|
|
6354
|
+
...offer && !this.fullGather ? { hostBoundMs: OFFER_HOST_GATHER_MS } : {}
|
|
6355
|
+
});
|
|
6300
6356
|
if (this.pc !== pc) return null;
|
|
6301
6357
|
const ms = Date.now() - gathering;
|
|
6302
6358
|
if (sdpHasCandidates(pc.localDescription?.sdp)) {
|
|
@@ -6332,6 +6388,7 @@ var DataLink = class {
|
|
|
6332
6388
|
if (this.pc !== pc) return;
|
|
6333
6389
|
this.clearTimers();
|
|
6334
6390
|
this.answered = null;
|
|
6391
|
+
this.fullGather = false;
|
|
6335
6392
|
this.setState("open");
|
|
6336
6393
|
this.options.setFastPoll(false);
|
|
6337
6394
|
this.options.publishSignal(null);
|
|
@@ -6413,7 +6470,7 @@ var DataLink = class {
|
|
|
6413
6470
|
//#endregion
|
|
6414
6471
|
//#region ../core/src/groupCrypto.ts
|
|
6415
6472
|
/**
|
|
6416
|
-
* The cryptography of the `group-mesh/1` profile (WISP 900 /
|
|
6473
|
+
* The cryptography of the `group-mesh/1` profile (WISP 900 / 902 group mesh).
|
|
6417
6474
|
*
|
|
6418
6475
|
* One random 32-byte **epoch secret** per membership state. It never travels in
|
|
6419
6476
|
* the clear: the committer seals it to each member of the new roster with an
|
|
@@ -6555,7 +6612,7 @@ function edgeParams(groupId, mySeed, myZ32, peerZ32) {
|
|
|
6555
6612
|
}
|
|
6556
6613
|
/**
|
|
6557
6614
|
* The cap the chain rules and the engine apply: `MAX_GROUP_MEMBERS`, always, in the apps. Only the scale harness raises
|
|
6558
|
-
* it, to measure what hubs would cost past 32 (WISP
|
|
6615
|
+
* it, to measure what hubs would cost past 32 (WISP 902 · Group Mesh § Cost per member); an app never does.
|
|
6559
6616
|
*/
|
|
6560
6617
|
var GROUP_MEMBER_CAP = { max: 32 };
|
|
6561
6618
|
var MAX_GROUP_CHAIN = 1024;
|
|
@@ -6689,7 +6746,7 @@ function verifyChain(raw, groupId) {
|
|
|
6689
6746
|
*/
|
|
6690
6747
|
var OWN_FRAME_LIMIT = 2 ** 32 - 2 ** 23;
|
|
6691
6748
|
/**
|
|
6692
|
-
* Frames said again (WISP
|
|
6749
|
+
* Frames said again (WISP 902, both profiles): a member's own frames that no edge took when it said them are
|
|
6693
6750
|
* remembered (`kept`, the latest) and, once the chain it follows is newer than such a frame's and has not moved for
|
|
6694
6751
|
* `settledMs`, looked at once for saying again where the members let in while it was behind can read them. A catch-up
|
|
6695
6752
|
* brings commits one by one, and a frame said again at the first of them would still miss those let in by the next.
|
|
@@ -6770,7 +6827,7 @@ var STATUS_CARD_LIMITS = {
|
|
|
6770
6827
|
/** Labels on a task, and a label's length in characters. */
|
|
6771
6828
|
tags: 3,
|
|
6772
6829
|
tag: 24,
|
|
6773
|
-
/** Buttons on a message (WISP
|
|
6830
|
+
/** Buttons on a message (WISP 406 · Message Buttons). */
|
|
6774
6831
|
buttons: 6,
|
|
6775
6832
|
/** A button's id: what a press names. */
|
|
6776
6833
|
buttonId: 32,
|
|
@@ -6831,7 +6888,7 @@ var ID$4 = /^[A-Za-z0-9_.:][A-Za-z0-9_.:-]{0,63}$/;
|
|
|
6831
6888
|
var BUTTON_ID = /^[A-Za-z0-9_.:][A-Za-z0-9_.:-]{0,31}$/;
|
|
6832
6889
|
var CRON = /^[0-9A-Za-z*,/?#\- ]{1,64}$/;
|
|
6833
6890
|
/**
|
|
6834
|
-
* Whether a typed answer is a button's label or id (WISP
|
|
6891
|
+
* Whether a typed answer is a button's label or id (WISP 406 · Message Buttons): ignoring case and spaces at the ends.
|
|
6835
6892
|
* Not the device's locale's case: the presser's and the author's apps must agree ("I" is not "ı" anywhere).
|
|
6836
6893
|
*/
|
|
6837
6894
|
var sameButtonText = (a, b) => a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
@@ -7315,13 +7372,13 @@ var wireReply = (reply) => ({
|
|
|
7315
7372
|
* {"t":"paired-reacted","n":1790000000000}
|
|
7316
7373
|
*
|
|
7317
7374
|
* Off the live session they ride on the DHT envelopes (WISP 403 § Reactions). Groups carry them in their own frames
|
|
7318
|
-
* (WISP
|
|
7375
|
+
* (WISP 902 § Reactions). Older apps drop all of these: a `t` they do not know, or a trailing element they skip.
|
|
7319
7376
|
*/
|
|
7320
7377
|
var REACTION_FRAME = "paired-reaction";
|
|
7321
7378
|
var REACTED_FRAME = "paired-reacted";
|
|
7322
|
-
/** A reaction in a private group, over the edge from the member who reacts (WISP
|
|
7379
|
+
/** A reaction in a private group, over the edge from the member who reacts (WISP 902 group mesh § Reactions). */
|
|
7323
7380
|
var GROUP_REACTION_FRAME = "group-react";
|
|
7324
|
-
/** A reaction in a community, as an application frame sealed to the group (WISP
|
|
7381
|
+
/** A reaction in a community, as an application frame sealed to the group (WISP 903 group community § Reactions). */
|
|
7325
7382
|
var COMMUNITY_REACTION_FRAME = "reaction";
|
|
7326
7383
|
var REACTION_LIMITS = {
|
|
7327
7384
|
/** UTF-8 bytes of one emoji: a family or a subdivision flag fits, a run of emoji does not. */
|
|
@@ -7679,7 +7736,7 @@ function parseLinkPreview(raw, text) {
|
|
|
7679
7736
|
* side: the frame comes on the session authenticated with the pinned contact, and the receiver looks the id up among
|
|
7680
7737
|
* that contact's messages only. The highest `e` wins, whatever order frames arrive in; an edit that is not newer is
|
|
7681
7738
|
* confirmed and changes nothing. A preview (`pv`, WISP 401 § Link previews) may come with the new text, as with a
|
|
7682
|
-
* message. A status card (`sc`, WISP
|
|
7739
|
+
* message. A status card (`sc`, WISP 405 · Status Cards) may come with it too; a card message takes up to
|
|
7683
7740
|
* `STATUS_CARD_LIMITS.edits` edits, a number past `MAX_EDITS_PER_MESSAGE` holding only with a card. Older apps drop
|
|
7684
7741
|
* both frames: they have an id, but a `t` they do not know.
|
|
7685
7742
|
*/
|
|
@@ -7970,10 +8027,20 @@ var INVISIBLE = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
|
7970
8027
|
* "CON", "nul.txt", "COM1 .log" open a device there, not a file.
|
|
7971
8028
|
*/
|
|
7972
8029
|
var WINDOWS_DEVICE = /^(CON|PRN|AUX|NUL|CONIN\$|CONOUT\$|COM[0-9¹²³]|LPT[0-9¹²³])\s*(\.|$)/i;
|
|
8030
|
+
/**
|
|
8031
|
+
* At most `MAX_NAME_LENGTH` characters. A longer name loses the end of its stem, not its extension (up to 15
|
|
8032
|
+
* characters after the last dot): "…report.pdf" stays a PDF to the system it is saved on.
|
|
8033
|
+
*/
|
|
8034
|
+
function shorten(name) {
|
|
8035
|
+
const chars = [...name];
|
|
8036
|
+
if (chars.length <= MAX_NAME_LENGTH) return name;
|
|
8037
|
+
const extension = [.../\.[^.\s]{1,15}$/u.exec(name)?.[0] ?? ""];
|
|
8038
|
+
return chars.slice(0, MAX_NAME_LENGTH - extension.length).join("").trimEnd() + extension.join("");
|
|
8039
|
+
}
|
|
7973
8040
|
/** A file name is display text and a download suggestion, never a path. */
|
|
7974
8041
|
function sanitizeFileName(name) {
|
|
7975
|
-
const clean =
|
|
7976
|
-
if (WINDOWS_DEVICE.test(clean)) return
|
|
8042
|
+
const clean = shorten(name.replace(INVISIBLE, "").replace(/[/\\:]/g, "").replace(/^[\s.]+/, "").trim());
|
|
8043
|
+
if (WINDOWS_DEVICE.test(clean)) return shorten("_" + clean);
|
|
7977
8044
|
return clean || "file";
|
|
7978
8045
|
}
|
|
7979
8046
|
function sanitizeMime(mime) {
|
|
@@ -8451,6 +8518,8 @@ var DhtDelivery = class DhtDelivery {
|
|
|
8451
8518
|
/** Until when the relays' request budget holds publications back: what is due then goes at once. */
|
|
8452
8519
|
budgetUntil = 0;
|
|
8453
8520
|
errors = {};
|
|
8521
|
+
/** When the device's network last came back (`networkBack`): a read or publication started before failed for want of it. */
|
|
8522
|
+
networkBackAt = -Infinity;
|
|
8454
8523
|
foreignKeySeenAt;
|
|
8455
8524
|
/** When the pinned mailbox was last looked in while the contact had not said it uses it. */
|
|
8456
8525
|
pinnedProbeAt = 0;
|
|
@@ -8640,6 +8709,18 @@ var DhtDelivery = class DhtDelivery {
|
|
|
8640
8709
|
this.tick();
|
|
8641
8710
|
}
|
|
8642
8711
|
/**
|
|
8712
|
+
* The device's network came back: a read or publication error from while it was away says nothing now (No Pkarr
|
|
8713
|
+
* relay reachable). It goes at once, and the mailbox is read now: one that still fails says so again.
|
|
8714
|
+
*/
|
|
8715
|
+
networkBack() {
|
|
8716
|
+
this.networkBackAt = Date.now();
|
|
8717
|
+
if (!this.errors.read && !this.errors.publish) return;
|
|
8718
|
+
delete this.errors.read;
|
|
8719
|
+
delete this.errors.publish;
|
|
8720
|
+
this.changed();
|
|
8721
|
+
this.tick();
|
|
8722
|
+
}
|
|
8723
|
+
/**
|
|
8643
8724
|
* Reads the contact's mailbox at the signaling pace for a while (a fresh packet of a contact not pinned yet).
|
|
8644
8725
|
* `signal`: the contact is leaving DHT only (a fresh packet on its link key, or its offer), and the live link waits
|
|
8645
8726
|
* on a read to say so: the next read goes now, and it and the next ones while the contact still says DHT only
|
|
@@ -9286,18 +9367,20 @@ var DhtDelivery = class DhtDelivery {
|
|
|
9286
9367
|
const signal = this.signalReads > 0;
|
|
9287
9368
|
this.urgent = false;
|
|
9288
9369
|
if (signal) this.signalReads--;
|
|
9370
|
+
const readAt = Date.now();
|
|
9289
9371
|
try {
|
|
9290
9372
|
await this.read(background, signal);
|
|
9291
9373
|
if (!this.running) return;
|
|
9292
9374
|
delete this.errors.read;
|
|
9293
9375
|
} catch (error) {
|
|
9294
|
-
if (!isDiscoveryBudgetError(error)) this.errors.read = `Could not read DHT delivery: ${error instanceof Error ? error.message : String(error)}`;
|
|
9376
|
+
if (!isDiscoveryBudgetError(error) && readAt >= this.networkBackAt) this.errors.read = `Could not read DHT delivery: ${error instanceof Error ? error.message : String(error)}`;
|
|
9295
9377
|
}
|
|
9296
9378
|
if (this.state.peerMode !== "dht") this.signalReads = 0;
|
|
9379
|
+
const publishAt = Date.now();
|
|
9297
9380
|
try {
|
|
9298
9381
|
await this.publish();
|
|
9299
9382
|
} catch (error) {
|
|
9300
|
-
if (!isDiscoveryBudgetError(error)) this.errors.publish = `Could not publish DHT delivery: ${error instanceof Error ? error.message : String(error)}`;
|
|
9383
|
+
if (!isDiscoveryBudgetError(error) && publishAt >= this.networkBackAt) this.errors.publish = `Could not publish DHT delivery: ${error instanceof Error ? error.message : String(error)}`;
|
|
9301
9384
|
}
|
|
9302
9385
|
this.changed();
|
|
9303
9386
|
});
|
|
@@ -9795,7 +9878,10 @@ var TransportSwitch = class {
|
|
|
9795
9878
|
begin(context, actual, migrated = false) {
|
|
9796
9879
|
const landed = migrated ? this.plan : null;
|
|
9797
9880
|
this.settled = landed ? this.signature(landed.local, landed.remote) : "";
|
|
9798
|
-
if (!migrated)
|
|
9881
|
+
if (!migrated) {
|
|
9882
|
+
this.failed = "";
|
|
9883
|
+
this.remote = null;
|
|
9884
|
+
}
|
|
9799
9885
|
if (this.apart) {
|
|
9800
9886
|
this.intent = Math.max(this.intent, this.lastRemote?.intent ?? 0) + 1;
|
|
9801
9887
|
this.apart = false;
|
|
@@ -9846,7 +9932,7 @@ var TransportSwitch = class {
|
|
|
9846
9932
|
keep(reason) {
|
|
9847
9933
|
const target = this.plan?.choices[0];
|
|
9848
9934
|
if (this.plan) this.settled = this.signature(this.plan.local, this.plan.remote);
|
|
9849
|
-
if (target && target !== this.actual) this.options.kept?.(target, reason);
|
|
9935
|
+
if (target && target !== this.actual && !this.plan?.upgrade) this.options.kept?.(target, reason);
|
|
9850
9936
|
this.send({
|
|
9851
9937
|
t: "paired-switch-keep",
|
|
9852
9938
|
id: this.plan?.id
|
|
@@ -9886,7 +9972,7 @@ var TransportSwitch = class {
|
|
|
9886
9972
|
this.preparing = false;
|
|
9887
9973
|
}
|
|
9888
9974
|
fail(error) {
|
|
9889
|
-
const target = this.plan?.choices[0];
|
|
9975
|
+
const target = this.plan?.choices[0], upgrade = !!this.plan?.upgrade;
|
|
9890
9976
|
if (this.plan) this.failed = this.signature(this.plan.local, this.plan.remote);
|
|
9891
9977
|
this.send({
|
|
9892
9978
|
t: "paired-switch-failed",
|
|
@@ -9894,7 +9980,8 @@ var TransportSwitch = class {
|
|
|
9894
9980
|
});
|
|
9895
9981
|
this.clearPlan();
|
|
9896
9982
|
this.options.cancel();
|
|
9897
|
-
this.
|
|
9983
|
+
if (upgrade) this.options.state();
|
|
9984
|
+
else this.unreached(target, error, error);
|
|
9898
9985
|
this.replanIfMoved();
|
|
9899
9986
|
}
|
|
9900
9987
|
/**
|
|
@@ -9916,13 +10003,29 @@ var TransportSwitch = class {
|
|
|
9916
10003
|
const ranked = rankTransports(allowedTransports(local), allowedTransports(remote), relayed);
|
|
9917
10004
|
if (!ranked.length) return [];
|
|
9918
10005
|
const winner = this.winner(local, remote);
|
|
9919
|
-
const target = winner.intent === 0 && this.actual && ranked.includes(this.actual) ? this.actual : ranked.includes(winner.preferred) ? winner.preferred : ranked[0];
|
|
10006
|
+
const target = this.upgrading(winner, ranked, relayed) ? ranked[0] : winner.intent === 0 && this.actual && ranked.includes(this.actual) ? this.actual : ranked.includes(winner.preferred) ? winner.preferred : ranked[0];
|
|
9920
10007
|
return [target, ...local.fallback && remote.fallback ? ranked.filter((t) => t !== target) : []];
|
|
9921
10008
|
}
|
|
10009
|
+
/** Nobody chose, the session is on a relayed transport, a direct one ranks first, and both sides move (`upgrade/1`). */
|
|
10010
|
+
upgrading(winner, ranked, relayed) {
|
|
10011
|
+
return winner.intent === 0 && !!this.actual && relayed.includes(this.actual) && ranked.includes(this.actual) && ranked[0] !== this.actual && !relayed.includes(ranked[0]) && !!this.options.upgrade?.();
|
|
10012
|
+
}
|
|
10013
|
+
isUpgrade(local, remote) {
|
|
10014
|
+
const relayed = relayedTransports(local.descriptors, remote.descriptors);
|
|
10015
|
+
return this.upgrading(this.winner(local, remote), rankTransports(allowedTransports(local), allowedTransports(remote), relayed), relayed);
|
|
10016
|
+
}
|
|
10017
|
+
/**
|
|
10018
|
+
* Something `upgrade` depends on changed (what the contact's app offers on this session, said once it is ready): plan
|
|
10019
|
+
* again. The session's first agreement, made before that, settled on the relay it is on; a move off it is not that.
|
|
10020
|
+
*/
|
|
10021
|
+
reconsider() {
|
|
10022
|
+
if (this.context && this.remote && !this.plan && this.isUpgrade(this.local(), this.remote) && this.settled === this.signature(this.local(), this.remote)) this.settled = "";
|
|
10023
|
+
this.reconcile();
|
|
10024
|
+
}
|
|
9922
10025
|
install(plan) {
|
|
9923
10026
|
this.clearPlan();
|
|
9924
10027
|
this.plan = plan;
|
|
9925
|
-
this.options.state(void 0, plan.choices[0]);
|
|
10028
|
+
this.options.state(void 0, plan.upgrade ? void 0 : plan.choices[0]);
|
|
9926
10029
|
this.timer = setTimeout(() => this.fail("Transport change timed out. Your previous connection is kept when available; retry or choose another transport."), this.options.timeoutMs ?? 3e4);
|
|
9927
10030
|
}
|
|
9928
10031
|
reconcile() {
|
|
@@ -9944,11 +10047,16 @@ var TransportSwitch = class {
|
|
|
9944
10047
|
return;
|
|
9945
10048
|
}
|
|
9946
10049
|
if (signature === this.failed) return;
|
|
9947
|
-
this.
|
|
10050
|
+
const upgrade = this.isUpgrade(local, remote);
|
|
10051
|
+
this.options.state(void 0, upgrade ? void 0 : choices[0]);
|
|
9948
10052
|
if (this.options.key > this.options.peerKey) {
|
|
9949
10053
|
if (!this.timer) this.timer = setTimeout(() => {
|
|
9950
10054
|
this.failed = signature;
|
|
9951
10055
|
this.clearPlan();
|
|
10056
|
+
if (upgrade) {
|
|
10057
|
+
this.options.state();
|
|
10058
|
+
return;
|
|
10059
|
+
}
|
|
9952
10060
|
this.unreached(choices[0], "Your contact did not acknowledge the transport change. Retry when both peers are connected.", "Your contact did not answer");
|
|
9953
10061
|
}, this.options.timeoutMs ?? 3e4);
|
|
9954
10062
|
return;
|
|
@@ -9957,7 +10065,8 @@ var TransportSwitch = class {
|
|
|
9957
10065
|
id: `${signature}:${choices[0]}`,
|
|
9958
10066
|
local,
|
|
9959
10067
|
remote,
|
|
9960
|
-
choices
|
|
10068
|
+
choices,
|
|
10069
|
+
...upgrade && { upgrade }
|
|
9961
10070
|
};
|
|
9962
10071
|
this.install(plan);
|
|
9963
10072
|
this.send({
|
|
@@ -9998,7 +10107,8 @@ var TransportSwitch = class {
|
|
|
9998
10107
|
id: String(frame.id),
|
|
9999
10108
|
local,
|
|
10000
10109
|
remote,
|
|
10001
|
-
choices
|
|
10110
|
+
choices,
|
|
10111
|
+
...this.isUpgrade(local, remote) && { upgrade: true }
|
|
10002
10112
|
});
|
|
10003
10113
|
this.options.prepare(this.plan, false);
|
|
10004
10114
|
this.send({
|
|
@@ -10009,17 +10119,18 @@ var TransportSwitch = class {
|
|
|
10009
10119
|
}
|
|
10010
10120
|
if (!this.plan || frame.id !== this.plan.id) return true;
|
|
10011
10121
|
if (frame.t === "paired-switch-failed") {
|
|
10012
|
-
const target = this.plan.choices[0];
|
|
10122
|
+
const target = this.plan.choices[0], upgrade = !!this.plan.upgrade;
|
|
10013
10123
|
this.failed = this.signature(this.plan.local, this.plan.remote);
|
|
10014
10124
|
this.clearPlan();
|
|
10015
10125
|
this.options.cancel();
|
|
10016
|
-
|
|
10126
|
+
if (upgrade) this.options.state();
|
|
10127
|
+
else this.unreached(target, "The transport change failed. Retry or choose another transport.");
|
|
10017
10128
|
this.replanIfMoved();
|
|
10018
10129
|
return true;
|
|
10019
10130
|
}
|
|
10020
10131
|
if (frame.t === "paired-switch-keep") {
|
|
10021
10132
|
if (this.actual && this.plan.choices.includes(this.actual)) {
|
|
10022
|
-
if (this.plan.choices[0] !== this.actual) this.options.kept?.(this.plan.choices[0]);
|
|
10133
|
+
if (this.plan.choices[0] !== this.actual && !this.plan.upgrade) this.options.kept?.(this.plan.choices[0]);
|
|
10023
10134
|
this.settled = this.signature(this.plan.local, this.plan.remote);
|
|
10024
10135
|
this.clearPlan();
|
|
10025
10136
|
this.options.state();
|
|
@@ -10438,7 +10549,7 @@ var lastOf = (timeline, side, proofs) => {
|
|
|
10438
10549
|
/**
|
|
10439
10550
|
* The timeline after one step. A share already on it is updated in place, never repeated: a request sent again at
|
|
10440
10551
|
* a reconnect, a second share of a proof the contact already verified. A new attempt after a failure or a stop is
|
|
10441
|
-
* a new entry. A stop is written once.
|
|
10552
|
+
* a new entry. A stop is written once; a withdrawal later found revoked becomes a revocation in place.
|
|
10442
10553
|
*/
|
|
10443
10554
|
function applyIdentityEvent(timeline, event, at) {
|
|
10444
10555
|
const out = [...timeline];
|
|
@@ -10498,7 +10609,13 @@ function applyIdentityEvent(timeline, event, at) {
|
|
|
10498
10609
|
break;
|
|
10499
10610
|
}
|
|
10500
10611
|
case "stopped":
|
|
10501
|
-
if (last?.kind === "stopped")
|
|
10612
|
+
if (last?.kind === "stopped") {
|
|
10613
|
+
if (last.reason === "withdrawn" && event.reason === "revoked") out[i] = {
|
|
10614
|
+
...last,
|
|
10615
|
+
reason: "revoked"
|
|
10616
|
+
};
|
|
10617
|
+
break;
|
|
10618
|
+
}
|
|
10502
10619
|
add({
|
|
10503
10620
|
proof: event.proof,
|
|
10504
10621
|
side: event.side,
|
|
@@ -11717,7 +11834,7 @@ async function fitSignedPairedSignalWith(signal, signer, from, to, fits) {
|
|
|
11717
11834
|
//#endregion
|
|
11718
11835
|
//#region ../core/src/storeForward.ts
|
|
11719
11836
|
/**
|
|
11720
|
-
* Store-and-forward for an away contact (WISP
|
|
11837
|
+
* Store-and-forward for an away contact (WISP 404 draft, `hold/1`).
|
|
11721
11838
|
*
|
|
11722
11839
|
* What a person sends while the contact is away is sealed into a *bundle* (signed by the sender's
|
|
11723
11840
|
* participation key, then encrypted to a key only the two of them can derive), put in the sender's
|
|
@@ -12440,15 +12557,23 @@ var WAKE_SESSION_CAPABILITY = "wake/1";
|
|
|
12440
12557
|
var PIN_CAPABILITY = "pin/1";
|
|
12441
12558
|
/**
|
|
12442
12559
|
* Status cards: this app shows a message's card (`sc`) and takes a card message's edits past the text's 100, which an
|
|
12443
|
-
* older app would drop (WISP
|
|
12560
|
+
* older app would drop (WISP 405 · Status Cards, `statusCards.ts`). A card itself needs nothing: older apps show its text.
|
|
12444
12561
|
*/
|
|
12445
12562
|
var STATUS_CARD_CAPABILITY = "status-card/1";
|
|
12446
12563
|
/**
|
|
12447
12564
|
* Message buttons: this app shows a message's buttons (a card of kind `buttons`) and presses them with a reply naming
|
|
12448
|
-
* the button (WISP
|
|
12565
|
+
* the button (WISP 406 · Message Buttons). Nothing is gated on it: an older app shows the text and sends replies, and
|
|
12449
12566
|
* a press reads as an ordinary reply there. It tells a bot whether its contact can press at all.
|
|
12450
12567
|
*/
|
|
12451
12568
|
var BUTTONS_CAPABILITY = "buttons/1";
|
|
12569
|
+
/**
|
|
12570
|
+
* A session live on a relayed transport while a direct one ranks first, with nobody's choice behind it, moves to the
|
|
12571
|
+
* direct one once, in place, its signaling on the session (WISP 100 § Back to a quiet contact): what a chat that came
|
|
12572
|
+
* back over a relay at once (rather than wait for an offer's answer) needs to end where a direct path would have put
|
|
12573
|
+
* it. Both sides must say it: the switch is planned by one and checked by the other. Also in the capability record
|
|
12574
|
+
* (WISP 03), so an app back after an absence knows before any session whether a relay can carry it there at once.
|
|
12575
|
+
*/
|
|
12576
|
+
var UPGRADE_CAPABILITY = "upgrade/1";
|
|
12452
12577
|
/** Every capability this app knows on a session: what `receive` reports changes of. */
|
|
12453
12578
|
var KNOWN_SESSION_CAPABILITIES = [
|
|
12454
12579
|
CALLS_CAPABILITY,
|
|
@@ -12460,7 +12585,8 @@ var KNOWN_SESSION_CAPABILITIES = [
|
|
|
12460
12585
|
WAKE_SESSION_CAPABILITY,
|
|
12461
12586
|
PIN_CAPABILITY,
|
|
12462
12587
|
STATUS_CARD_CAPABILITY,
|
|
12463
|
-
BUTTONS_CAPABILITY
|
|
12588
|
+
BUTTONS_CAPABILITY,
|
|
12589
|
+
UPGRADE_CAPABILITY
|
|
12464
12590
|
];
|
|
12465
12591
|
var SESSION_CAPABILITIES_FRAME = "paired-capabilities";
|
|
12466
12592
|
var MAX_ENTRIES$1 = 32;
|
|
@@ -14275,7 +14401,7 @@ var PairedCalls = class {
|
|
|
14275
14401
|
//#endregion
|
|
14276
14402
|
//#region ../core/src/communityRendezvous.ts
|
|
14277
14403
|
/**
|
|
14278
|
-
* Where the members of a `group-community/1` group find each other (WISP
|
|
14404
|
+
* Where the members of a `group-community/1` group find each other (WISP 903 · Group Community §
|
|
14279
14405
|
* Topology): a **beacon** listing the current hubs, and a **lobby** per hub where a member asks
|
|
14280
14406
|
* that hub for an edge. Both are Pkarr records under identities every member derives from the
|
|
14281
14407
|
* group's rendezvous secret, sealed with a key from the same secret: relays see a key and an
|
|
@@ -14376,7 +14502,7 @@ function unpack(bytes, withLoad, max) {
|
|
|
14376
14502
|
return out;
|
|
14377
14503
|
}
|
|
14378
14504
|
/**
|
|
14379
|
-
* The hubs that take turns at the door (WISP
|
|
14505
|
+
* The hubs that take turns at the door (WISP 903 § Admission): those that said so lately and have
|
|
14380
14506
|
* been hubs for a minute, so that every hub, having read the beacon at least once since, agrees on
|
|
14381
14507
|
* the same set. Only when there is none (a group's first minute), the one that has been a hub longest. And when none said so lately, a hub asking (`me`)
|
|
14382
14508
|
* that sees no other fresh hub in the beacon is the door alone, listed or not: its own entry may not have reached the
|
|
@@ -14504,7 +14630,7 @@ function shouldBeHub(me, hubs, now = Date.now()) {
|
|
|
14504
14630
|
//#endregion
|
|
14505
14631
|
//#region ../core/src/groupHubs.ts
|
|
14506
14632
|
/**
|
|
14507
|
-
* Hubs in a private (mesh) group (WISP
|
|
14633
|
+
* Hubs in a private (mesh) group (WISP 902 · Group Mesh § Hubs). Up to `threshold` members a group is a full mesh, as
|
|
14508
14634
|
* before. Past it, members that stay online (the Desktop app and the CLI, or members the admin pins) become **hubs**:
|
|
14509
14635
|
* they keep edges with every other hub and with the members that ask them, and pass on every frame they take. The
|
|
14510
14636
|
* other members keep edges with `hubsPerMember` hubs only. Frames are author-signed and sealed under the epoch key, and
|
|
@@ -15041,6 +15167,12 @@ var DHT_PIN_GRACE_MS = 1e4;
|
|
|
15041
15167
|
var RESUME_WAIT_MS = 1e4;
|
|
15042
15168
|
/** Two dials crossing: how long the lower key's side waits for its own to connect before it takes the contact's. */
|
|
15043
15169
|
var CROSSED_WAIT_MS = 3e3;
|
|
15170
|
+
/** A WebRTC attempt under way, not yet open: what a member's packet saying it has no WebRTC ends (`peerPacketTransports`). */
|
|
15171
|
+
var WEBRTC_ATTEMPT = /* @__PURE__ */ new Set([
|
|
15172
|
+
"offering",
|
|
15173
|
+
"answering",
|
|
15174
|
+
"connecting"
|
|
15175
|
+
]);
|
|
15044
15176
|
/** What a joiner is told when someone else used the invite first (`DhtDeliveryView.inviteTaken`). */
|
|
15045
15177
|
var INVITE_TAKEN = "Someone else joined with this invite first. Ask your contact for a new one.";
|
|
15046
15178
|
/**
|
|
@@ -15151,7 +15283,7 @@ var PONG_WAIT_MS = 4e3;
|
|
|
15151
15283
|
* A `paired-message` frame. The preview (`pv`) is left out when the frame would pass `MAX_PAIRED_MESSAGE_FRAME`
|
|
15152
15284
|
* with it: the text matters, the card does not. A reply (`r`, WISP 401 § Replies) is a few hundred bytes at most
|
|
15153
15285
|
* and always goes, and so does a forwarded message's hop count (`fw`, WISP 401 § Forwards) and a status card (`sc`,
|
|
15154
|
-
* WISP
|
|
15286
|
+
* WISP 405 · Status Cards), whose 8 KiB with the text's 16 stay well inside the frame.
|
|
15155
15287
|
*/
|
|
15156
15288
|
function pairedMessageFrame(id, ts, m, preview, reply, forwarded, card) {
|
|
15157
15289
|
const r = {
|
|
@@ -15186,6 +15318,11 @@ function pairedMessageFrame(id, ts, m, preview, reply, forwarded, card) {
|
|
|
15186
15318
|
* names the transport. One just read that names it with no descriptor says its endpoint is not up (WISP 03: still
|
|
15187
15319
|
* starting, or no free listener there): the one known is not dialled meanwhile (none, `undefined`).
|
|
15188
15320
|
*/
|
|
15321
|
+
/** The endpoint a descriptor names (Iroh's endpoint id, HyperDHT's public key), whatever relay it is homed on. */
|
|
15322
|
+
function endpointId(transport, descriptor) {
|
|
15323
|
+
const d = descriptor;
|
|
15324
|
+
return transport === "iroh/1" ? d?.id : d?.publicKey;
|
|
15325
|
+
}
|
|
15189
15326
|
function recordDescriptor(transport, known, record, newer, listed) {
|
|
15190
15327
|
if (!record) return newer && listed ? void 0 : known;
|
|
15191
15328
|
if (!known) return record;
|
|
@@ -15240,6 +15377,9 @@ var GhostLink = class {
|
|
|
15240
15377
|
endpoints = /* @__PURE__ */ new Map();
|
|
15241
15378
|
activeBinding;
|
|
15242
15379
|
dialing = false;
|
|
15380
|
+
/** A joiner's dial made as the link starts (`dialEarly`): its offer goes once the first look finds the inviter. */
|
|
15381
|
+
dialGate;
|
|
15382
|
+
firstLookDone;
|
|
15243
15383
|
dhtPinGrace = null;
|
|
15244
15384
|
/** Native attempts that failed in a row, and transports demoted until when (WISP 100, demotion). */
|
|
15245
15385
|
nativeFailures = /* @__PURE__ */ new Map();
|
|
@@ -15323,6 +15463,8 @@ var GhostLink = class {
|
|
|
15323
15463
|
unansweredPings = 0;
|
|
15324
15464
|
/** The last ping's `PONG_WAIT_MS`: running until something comes back from the contact. */
|
|
15325
15465
|
pongWait = null;
|
|
15466
|
+
/** The last ping's wait ran out with nothing back, and nothing came since (`restartedRedial`). */
|
|
15467
|
+
pongLate = false;
|
|
15326
15468
|
openWaiters = [];
|
|
15327
15469
|
/** Set while a WebRTC attempt this side dialled is pending: the ranked transports to try if it fails. */
|
|
15328
15470
|
afterRtc;
|
|
@@ -15330,8 +15472,14 @@ var GhostLink = class {
|
|
|
15330
15472
|
raceTimer = null;
|
|
15331
15473
|
/** Set while `race` dials: the offer ending meanwhile is followed up by the race. */
|
|
15332
15474
|
racing = false;
|
|
15333
|
-
/**
|
|
15475
|
+
/**
|
|
15476
|
+
* The WebRTC offer this side's dial made last, with fallback allowed: what a transport that comes up meanwhile joins.
|
|
15477
|
+
* `resume`: the dial that made it is this side back after an absence, over WebRTC (`resuming`).
|
|
15478
|
+
*/
|
|
15334
15479
|
offered;
|
|
15480
|
+
/** The attempt (its epoch) whose direct transports were knocked on as its offer went out (`knockEarly`), and whether that is under way. */
|
|
15481
|
+
knockedEarly = -1;
|
|
15482
|
+
knockingEarly = false;
|
|
15335
15483
|
/** When that offer's answer was applied (`RACE_ANSWERED_MS`); 0 while the data link is not connecting on it. */
|
|
15336
15484
|
answeredAt = 0;
|
|
15337
15485
|
/** This side's offer is out unanswered, or answered and not connected yet: what is ranked after WebRTC may race it. */
|
|
@@ -15344,6 +15492,13 @@ var GhostLink = class {
|
|
|
15344
15492
|
autoConnectFailures = 0;
|
|
15345
15493
|
/** Native transports a record just read listed with no way to dial them (`record-undescribed`), until one describes them again. */
|
|
15346
15494
|
undescribed = /* @__PURE__ */ new Set();
|
|
15495
|
+
/**
|
|
15496
|
+
* The native dial under way (`dialNative`): its attempt, its transport, the endpoint it went to (`redescribedDial`),
|
|
15497
|
+
* and whether it started with this device offline (`networkBack`).
|
|
15498
|
+
*/
|
|
15499
|
+
nativeDial = null;
|
|
15500
|
+
/** When the device's network last came back (`wake({ network: true })`): a dial under way across it is no failure. */
|
|
15501
|
+
networkBackAt = -Infinity;
|
|
15347
15502
|
/** The peer's packet I last looked fast for its offer after (its timestamp): once per packet. */
|
|
15348
15503
|
offerAwaitedFor = 0;
|
|
15349
15504
|
/** Whether the stream was blocked (DHT-only on either side) when last looked at. */
|
|
@@ -15501,7 +15656,10 @@ var GhostLink = class {
|
|
|
15501
15656
|
firstPublish: options.firstPublish ?? (this.tracker && options.pairingProgress?.role === "joiner" || this.resuming ? "after-first-poll" : "at-start"),
|
|
15502
15657
|
events: {
|
|
15503
15658
|
onFirstPoll: () => {
|
|
15504
|
-
|
|
15659
|
+
const online = this.session.peerPresence.online, early = this.firstLookDone;
|
|
15660
|
+
this.firstLookDone = void 0;
|
|
15661
|
+
early?.(online);
|
|
15662
|
+
if (!this.dialing || early && !online) this.session.ensureAdvertised();
|
|
15505
15663
|
},
|
|
15506
15664
|
onMessages: (messages, batch) => {
|
|
15507
15665
|
if (options.params.profile) return;
|
|
@@ -15622,7 +15780,13 @@ var GhostLink = class {
|
|
|
15622
15780
|
if (this.activeBinding) return;
|
|
15623
15781
|
events.onDataLinkState?.(state);
|
|
15624
15782
|
if (state === "open") this.afterRtc = void 0;
|
|
15625
|
-
if (state === "answering")
|
|
15783
|
+
if (state === "answering") {
|
|
15784
|
+
this.answeringEpoch = this.connectionEpoch;
|
|
15785
|
+
if (this.resumingOverRtc) {
|
|
15786
|
+
const epoch = this.connectionEpoch;
|
|
15787
|
+
setTimeout(() => this.maybeKnockAnswering(epoch), 0);
|
|
15788
|
+
}
|
|
15789
|
+
}
|
|
15626
15790
|
this.answeredAt = 0;
|
|
15627
15791
|
if (state === "connecting" && was === "offering" && this.offered?.epoch === this.connectionEpoch) {
|
|
15628
15792
|
this.answeredAt = Date.now();
|
|
@@ -15700,7 +15864,8 @@ var GhostLink = class {
|
|
|
15700
15864
|
},
|
|
15701
15865
|
unreached: (target, reason) => {
|
|
15702
15866
|
this.unreached(target, reason);
|
|
15703
|
-
}
|
|
15867
|
+
},
|
|
15868
|
+
upgrade: () => !!this.options.upgradeSupport && this.sessionCapabilities.agreed("upgrade/1")
|
|
15704
15869
|
});
|
|
15705
15870
|
if (options.native?.automatic === false) this.switcher.choseApart();
|
|
15706
15871
|
}
|
|
@@ -15967,9 +16132,36 @@ var GhostLink = class {
|
|
|
15967
16132
|
this.session.expectPeer();
|
|
15968
16133
|
this.resumeWaitUntil = Date.now() + RESUME_WAIT_MS;
|
|
15969
16134
|
}
|
|
15970
|
-
if (this.tracker && !this.tracker.done && this.options.pairingProgress?.role === "joiner")
|
|
16135
|
+
if (this.tracker && !this.tracker.done && this.options.pairingProgress?.role === "joiner") {
|
|
16136
|
+
this.dht?.expect();
|
|
16137
|
+
this.dialEarly();
|
|
16138
|
+
}
|
|
15971
16139
|
this.dht?.start();
|
|
15972
16140
|
}
|
|
16141
|
+
/**
|
|
16142
|
+
* A joiner's offer is gathered as the link starts, while its first look at the inviter's record is under way, and goes
|
|
16143
|
+
* once that look finds the inviter (`dialGate`): the two used to come one after the other, the read (0.15 to 0.8 s)
|
|
16144
|
+
* then the gathering (0.2 s to 2 s).
|
|
16145
|
+
*/
|
|
16146
|
+
dialEarly() {
|
|
16147
|
+
if (!this.options.params.profile || !this.options.autoConnect || this.deliveryMode === "dht" || this.streamBlocked || this.dialing || this.channel) return;
|
|
16148
|
+
const startedAt = this.options.pairingProgress?.startedAt ?? 0;
|
|
16149
|
+
if (Date.now() - startedAt > 6e4) return;
|
|
16150
|
+
this.dialGate = new Promise((resolve) => {
|
|
16151
|
+
this.firstLookDone = (online) => {
|
|
16152
|
+
if (online) {
|
|
16153
|
+
this.lastAutoConnectAt = Date.now();
|
|
16154
|
+
this.autoConnectFailures++;
|
|
16155
|
+
}
|
|
16156
|
+
resolve(online);
|
|
16157
|
+
};
|
|
16158
|
+
});
|
|
16159
|
+
traceLink(this.myPubKeyZ32, "dial", { early: true });
|
|
16160
|
+
this.dial().catch((error) => {
|
|
16161
|
+
this.tracker?.failed("transport", true);
|
|
16162
|
+
this.dialFailed(error instanceof Error ? error.message : String(error));
|
|
16163
|
+
});
|
|
16164
|
+
}
|
|
15973
16165
|
async stop(announce = true) {
|
|
15974
16166
|
this.stopped = true;
|
|
15975
16167
|
this.clearRace();
|
|
@@ -16021,18 +16213,32 @@ var GhostLink = class {
|
|
|
16021
16213
|
});
|
|
16022
16214
|
}
|
|
16023
16215
|
/** Closes what a switch replaced once the contact has had time to move too. */
|
|
16024
|
-
retire(old, also) {
|
|
16216
|
+
retire(old, also, rtc = false) {
|
|
16025
16217
|
if (this.retiring.has(old)) return;
|
|
16218
|
+
let closed = () => {};
|
|
16219
|
+
const done = new Promise((resolve) => {
|
|
16220
|
+
closed = resolve;
|
|
16221
|
+
});
|
|
16026
16222
|
const close = () => {
|
|
16027
16223
|
this.retiring.delete(old);
|
|
16028
16224
|
old.close();
|
|
16029
16225
|
also();
|
|
16226
|
+
closed();
|
|
16030
16227
|
};
|
|
16031
16228
|
this.retiring.set(old, {
|
|
16032
16229
|
close,
|
|
16033
|
-
timer: setTimeout(close, SWITCH_RETIRE_MS)
|
|
16230
|
+
timer: setTimeout(close, SWITCH_RETIRE_MS),
|
|
16231
|
+
rtc,
|
|
16232
|
+
closed: done
|
|
16034
16233
|
});
|
|
16035
16234
|
}
|
|
16235
|
+
/**
|
|
16236
|
+
* The WebRTC session a switch moved off is still retiring (`SWITCH_RETIRE_MS`): its data link is open, and a new offer
|
|
16237
|
+
* cannot go until it closes. Resolves once it has (at once when none is).
|
|
16238
|
+
*/
|
|
16239
|
+
rtcRetired() {
|
|
16240
|
+
return Promise.all([...this.retiring.values()].filter((entry) => entry.rtc).map((entry) => entry.closed)).then(() => {});
|
|
16241
|
+
}
|
|
16036
16242
|
closeRetired() {
|
|
16037
16243
|
for (const { timer, close } of [...this.retiring.values()]) {
|
|
16038
16244
|
clearTimeout(timer);
|
|
@@ -16104,7 +16310,7 @@ var GhostLink = class {
|
|
|
16104
16310
|
const listed = !!wanted && !!this.peerRecordTransports?.includes(wanted), dialable = !!wanted && !!this.peerDescriptors[wanted];
|
|
16105
16311
|
this.peerRecordTransports = [...transports];
|
|
16106
16312
|
let changed = false, redescribed = false, again = false, news = false;
|
|
16107
|
-
const renamed = /* @__PURE__ */ new Set();
|
|
16313
|
+
const renamed = /* @__PURE__ */ new Set(), reidentified = /* @__PURE__ */ new Set();
|
|
16108
16314
|
for (const t of ["iroh/1", "hyperdht/1"]) {
|
|
16109
16315
|
const known = this.peerDescriptors[t], next = recordDescriptor(t, known, descriptors[t], fresh, transports.includes(t));
|
|
16110
16316
|
if (next === known) continue;
|
|
@@ -16124,6 +16330,7 @@ var GhostLink = class {
|
|
|
16124
16330
|
changed = true;
|
|
16125
16331
|
redescribed ||= t === wanted;
|
|
16126
16332
|
renamed.add(t);
|
|
16333
|
+
if (known && endpointId(t, known) !== endpointId(t, next)) reidentified.add(t);
|
|
16127
16334
|
this.nativeFailures.delete(t);
|
|
16128
16335
|
this.demotedUntil.delete(t);
|
|
16129
16336
|
}
|
|
@@ -16149,6 +16356,8 @@ var GhostLink = class {
|
|
|
16149
16356
|
this.redial();
|
|
16150
16357
|
return;
|
|
16151
16358
|
}
|
|
16359
|
+
if (held && heldOn && reidentified.has(heldOn) && this.restartedRedial(held, heldOn)) return;
|
|
16360
|
+
if (this.redescribedDial()) return;
|
|
16152
16361
|
if (this.channel || this.dialing) return;
|
|
16153
16362
|
if (news || !again) {
|
|
16154
16363
|
this.autoConnectFailures = 0;
|
|
@@ -16427,7 +16636,7 @@ var GhostLink = class {
|
|
|
16427
16636
|
if (!said) return;
|
|
16428
16637
|
const transports = said.transports, descriptors = dialDescriptors(said.descriptors);
|
|
16429
16638
|
traceLink(this.myPubKeyZ32, "packet-transports", { transports });
|
|
16430
|
-
if (!transports.includes("webrtc/1") && !this.channel && this.dataLink.state
|
|
16639
|
+
if (!transports.includes("webrtc/1") && !this.channel && WEBRTC_ATTEMPT.has(this.dataLink.state)) this.disconnect();
|
|
16431
16640
|
this.options.events?.onPacketTransports?.(transports, descriptors);
|
|
16432
16641
|
this.learnPeerTransports(transports, descriptors, true);
|
|
16433
16642
|
}
|
|
@@ -16649,6 +16858,7 @@ var GhostLink = class {
|
|
|
16649
16858
|
const remembered = this.peerTransports && rankTransports(local, this.peerFallback ? this.peerTransports : this.peerTransports.slice(0, 1), relayed);
|
|
16650
16859
|
const ranked = remembered?.length ? remembered : this.peerTransports ? rankTransports(local, this.peerTransports, relayed) : local.filter((t) => t === "webrtc/1");
|
|
16651
16860
|
const chosen = this.switcher.chosenTarget ?? this.choiceApart?.transport ?? this.resuming;
|
|
16861
|
+
const resume = this.resuming === "webrtc/1";
|
|
16652
16862
|
if (chosen && chosen === this.resuming && ranked.includes(chosen)) this.resuming = void 0;
|
|
16653
16863
|
const choices = chosen && ranked.includes(chosen) ? [chosen, ...ranked.filter((t) => t !== chosen)] : ranked;
|
|
16654
16864
|
if (!choices.length) {
|
|
@@ -16668,12 +16878,21 @@ var GhostLink = class {
|
|
|
16668
16878
|
epoch,
|
|
16669
16879
|
rest
|
|
16670
16880
|
} : void 0;
|
|
16671
|
-
const
|
|
16672
|
-
|
|
16881
|
+
const standing = this.offerOut && this.offered?.epoch === epoch ? this.offered : void 0;
|
|
16882
|
+
const offeredAt = standing?.at ?? Date.now();
|
|
16883
|
+
this.offered = fallback ? standing ?? {
|
|
16673
16884
|
epoch,
|
|
16674
|
-
at: offeredAt
|
|
16885
|
+
at: offeredAt,
|
|
16886
|
+
...resume && { resume }
|
|
16675
16887
|
} : void 0;
|
|
16676
|
-
|
|
16888
|
+
const gate = this.dialGate;
|
|
16889
|
+
this.dialGate = void 0;
|
|
16890
|
+
if (await this.dataLink.connect(gate) === "held") {
|
|
16891
|
+
traceLink(this.myPubKeyZ32, "dial-held", {});
|
|
16892
|
+
this.afterRtc = void 0;
|
|
16893
|
+
this.offered = void 0;
|
|
16894
|
+
return;
|
|
16895
|
+
}
|
|
16677
16896
|
if (this.dataLink.state !== "idle" || epoch !== this.connectionEpoch) {
|
|
16678
16897
|
this.scheduleRace(epoch, offeredAt);
|
|
16679
16898
|
return;
|
|
@@ -16695,6 +16914,7 @@ var GhostLink = class {
|
|
|
16695
16914
|
if (epoch === this.connectionEpoch) this.attemptEnded(error instanceof Error ? error.message : String(error));
|
|
16696
16915
|
throw error;
|
|
16697
16916
|
} finally {
|
|
16917
|
+
this.dialGate = void 0;
|
|
16698
16918
|
if (epoch === this.connectionEpoch) {
|
|
16699
16919
|
this.dialing = false;
|
|
16700
16920
|
if (!this.channel && !this.afterRtc && this.dataLink.state === "idle") this.attemptEnded();
|
|
@@ -16711,6 +16931,12 @@ var GhostLink = class {
|
|
|
16711
16931
|
const descriptor = this.peerDescriptors[transport];
|
|
16712
16932
|
if (!endpoint || !descriptor) return /* @__PURE__ */ new Error("Peer native address unavailable; reconnect WebRTC once to exchange endpoints");
|
|
16713
16933
|
const started = Date.now();
|
|
16934
|
+
const dialed = this.nativeDial = {
|
|
16935
|
+
epoch,
|
|
16936
|
+
transport,
|
|
16937
|
+
endpoint: endpointId(transport, descriptor),
|
|
16938
|
+
offline: this.isOffline()
|
|
16939
|
+
};
|
|
16714
16940
|
try {
|
|
16715
16941
|
const { channel, binding } = await endpoint.connect(descriptor);
|
|
16716
16942
|
this.nativeFailures.delete(transport);
|
|
@@ -16729,6 +16955,10 @@ var GhostLink = class {
|
|
|
16729
16955
|
});
|
|
16730
16956
|
if (epoch !== this.connectionEpoch) return true;
|
|
16731
16957
|
this.attemptFailed(transport, error instanceof Error ? error.message : String(error));
|
|
16958
|
+
if (dialed.offline || this.isOffline() || this.networkBackAt >= started) {
|
|
16959
|
+
traceLink(this.myPubKeyZ32, "dial-failed-offline", { transport });
|
|
16960
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
16961
|
+
}
|
|
16732
16962
|
const failures = (this.nativeFailures.get(transport) ?? 0) + 1;
|
|
16733
16963
|
if (failures >= 3) {
|
|
16734
16964
|
this.nativeFailures.delete(transport);
|
|
@@ -16736,8 +16966,59 @@ var GhostLink = class {
|
|
|
16736
16966
|
traceLink(this.myPubKeyZ32, "demote", { transport });
|
|
16737
16967
|
} else this.nativeFailures.set(transport, failures);
|
|
16738
16968
|
return error instanceof Error ? error : new Error(String(error));
|
|
16969
|
+
} finally {
|
|
16970
|
+
if (this.nativeDial === dialed) this.nativeDial = null;
|
|
16739
16971
|
}
|
|
16740
16972
|
}
|
|
16973
|
+
/**
|
|
16974
|
+
* The native dial under way went to an endpoint the contact's newer packet no longer names for that transport: its
|
|
16975
|
+
* app started again (a page reloading, as both devices of a handoff do), and the endpoint dialled went with the old
|
|
16976
|
+
* one. That dial would end only at its connect timeout (Iroh: 20 s, then the next attempt's wait): it is let go, and
|
|
16977
|
+
* the endpoint the packet names dialled now. Not while a WebRTC offer of this attempt stands: the race that follows
|
|
16978
|
+
* it keeps its own order.
|
|
16979
|
+
*/
|
|
16980
|
+
redescribedDial() {
|
|
16981
|
+
const dial = this.nativeDial;
|
|
16982
|
+
if (!dial || dial.epoch !== this.connectionEpoch || !this.dialing || this.channel || this.dataLink.state !== "idle") return false;
|
|
16983
|
+
const now = this.peerDescriptors[dial.transport];
|
|
16984
|
+
if (!now || endpointId(dial.transport, now) === dial.endpoint) return false;
|
|
16985
|
+
traceLink(this.myPubKeyZ32, "redescribed-dial", { transport: dial.transport });
|
|
16986
|
+
this.nativeDial = null;
|
|
16987
|
+
this.connectionEpoch++;
|
|
16988
|
+
this.dialing = false;
|
|
16989
|
+
this.clearRace();
|
|
16990
|
+
this.afterRtc = void 0;
|
|
16991
|
+
this.autoConnectFailures = Math.max(0, this.autoConnectFailures - 1);
|
|
16992
|
+
this.redial();
|
|
16993
|
+
return true;
|
|
16994
|
+
}
|
|
16995
|
+
/** Whether this device has no network now (`GhostLinkOptions.offline`). */
|
|
16996
|
+
isOffline() {
|
|
16997
|
+
return this.options.offline ? this.options.offline() : globalThis.navigator?.onLine === false;
|
|
16998
|
+
}
|
|
16999
|
+
/**
|
|
17000
|
+
* The device's network came back. A native dial that started while it was away would end only at its connect timeout
|
|
17001
|
+
* (Iroh: 20 s), well past the return: it is let go, and the chat dialled again now. Its WebRTC offer, if one stands,
|
|
17002
|
+
* was made with no network too, and goes with it.
|
|
17003
|
+
*/
|
|
17004
|
+
networkBack() {
|
|
17005
|
+
this.networkBackAt = Date.now();
|
|
17006
|
+
const dial = this.nativeDial;
|
|
17007
|
+
if (!dial?.offline || dial.epoch !== this.connectionEpoch || !this.dialing || this.channel) return;
|
|
17008
|
+
if (this.stopped || this.leaving || this.streamBlocked || this.keyStopped || !this.options.autoConnect) return;
|
|
17009
|
+
traceLink(this.myPubKeyZ32, "offline-dial", { transport: dial.transport });
|
|
17010
|
+
this.nativeDial = null;
|
|
17011
|
+
if (this.dataLink.state !== "idle") this.disconnect();
|
|
17012
|
+
else {
|
|
17013
|
+
this.connectionEpoch++;
|
|
17014
|
+
this.dialing = false;
|
|
17015
|
+
this.clearRace();
|
|
17016
|
+
this.afterRtc = void 0;
|
|
17017
|
+
}
|
|
17018
|
+
this.autoConnectFailures = 0;
|
|
17019
|
+
this.lastAutoConnectAt = 0;
|
|
17020
|
+
this.redial();
|
|
17021
|
+
}
|
|
16741
17022
|
/** A WebRTC attempt this side dialled ended without opening: the next ranked transports, in order. */
|
|
16742
17023
|
async dialAfterRtc(next) {
|
|
16743
17024
|
this.dialing = true;
|
|
@@ -16772,12 +17053,65 @@ var GhostLink = class {
|
|
|
16772
17053
|
const answered = this.answeredAt ? this.answeredAt + RACE_ANSWERED_MS : Infinity;
|
|
16773
17054
|
const at = next.rest.filter((t) => t !== "webrtc/1").map((t) => Math.max(Math.min(offeredAt + (relayed.includes(t) ? RACE_RELAYED_MS : RACE_DIRECT_MS), answered), this.canDial(t) ? 0 : now + RACE_RETRY_MS));
|
|
16774
17055
|
if (!at.length) return;
|
|
17056
|
+
if (this.offered?.resume && this.knockedEarly !== epoch) setTimeout(() => this.maybeKnockEarly(epoch), 0);
|
|
16775
17057
|
this.raceTimer = setTimeout(() => {
|
|
16776
17058
|
this.raceTimer = null;
|
|
16777
17059
|
this.race(epoch, offeredAt);
|
|
16778
17060
|
}, Math.max(0, Math.min(...at) - now));
|
|
16779
17061
|
}
|
|
16780
17062
|
/**
|
|
17063
|
+
* Back after an absence, this side answers an offer the contact made while it was away (it dials whatever its key):
|
|
17064
|
+
* the contact looks for that answer at the pace of an offer out a while (every 8 s on the relays), and its attempt may
|
|
17065
|
+
* end before it does. The direct transports both run are knocked on meanwhile, and the relayed ones where both apps
|
|
17066
|
+
* move off a relay once live (`upgrade/1`): the first live wins, and a knock that does not connect counts as nothing.
|
|
17067
|
+
*/
|
|
17068
|
+
maybeKnockAnswering(epoch) {
|
|
17069
|
+
if (!this.resumingOverRtc || epoch !== this.connectionEpoch || this.knockedEarly === epoch || this.dataLink.state === "idle") return;
|
|
17070
|
+
if (this.channel || this.dialing || this.stopped || this.streamBlocked || this.keyStopped || !this.contactQuiet(1e4)) return;
|
|
17071
|
+
if (!this.fallback || !this.peerFallback) return;
|
|
17072
|
+
this.knockedEarly = epoch;
|
|
17073
|
+
if (this.resuming === "webrtc/1") this.resuming = void 0;
|
|
17074
|
+
const relayed = this.upgradesFromRelay() ? [] : this.relayedTransports;
|
|
17075
|
+
const offer = this.transportOffer();
|
|
17076
|
+
const direct = TRANSPORTS.filter((t) => t !== "webrtc/1" && offer.includes(t) && !!this.peerTransports?.includes(t) && !relayed.includes(t) && this.canDial(t));
|
|
17077
|
+
if (!direct.length) return;
|
|
17078
|
+
this.knockEarly(epoch, direct, () => this.dataLink.state === "answering" || this.dataLink.state === "connecting");
|
|
17079
|
+
}
|
|
17080
|
+
/** Back after an absence over WebRTC: not dialled yet, or this attempt's offer is the resume's (`offered.resume`). */
|
|
17081
|
+
get resumingOverRtc() {
|
|
17082
|
+
return this.resuming === "webrtc/1" || !!this.offered?.resume && this.offered.epoch === this.connectionEpoch;
|
|
17083
|
+
}
|
|
17084
|
+
/** Both apps move a chat off a relay once live (`upgrade/1`): this one, and the contact's by its capability record. */
|
|
17085
|
+
upgradesFromRelay() {
|
|
17086
|
+
return !!this.options.upgradeSupport && !!this.options.peerUpgrades?.();
|
|
17087
|
+
}
|
|
17088
|
+
/** The contact's newest packet is older than `EXPECT_PEER_MS`: it is not signalling, and reads at a slow pace. */
|
|
17089
|
+
contactQuiet(ms = EXPECT_PEER_MS) {
|
|
17090
|
+
return Date.now() - presenceSeenAt(this.session.peerPresence) >= ms;
|
|
17091
|
+
}
|
|
17092
|
+
/** The direct transports of an attempt that resumes to a quiet contact, knocked on while its offer stands (`scheduleRace`). */
|
|
17093
|
+
maybeKnockEarly(epoch) {
|
|
17094
|
+
const next = this.afterRtc;
|
|
17095
|
+
if (!this.offered?.resume || this.offered.epoch !== epoch || this.knockedEarly === epoch || next?.epoch !== epoch || epoch !== this.connectionEpoch) return;
|
|
17096
|
+
if (this.dialing || this.channel || this.stopped || !this.offerOut || !this.contactQuiet()) return;
|
|
17097
|
+
const relayed = this.upgradesFromRelay() ? [] : this.relayedTransports;
|
|
17098
|
+
const direct = next.rest.filter((t) => !relayed.includes(t) && this.canDial(t));
|
|
17099
|
+
if (!direct.length) return;
|
|
17100
|
+
this.knockedEarly = epoch;
|
|
17101
|
+
this.knockEarly(epoch, direct);
|
|
17102
|
+
}
|
|
17103
|
+
async knockEarly(epoch, transports, standing = () => this.offerOut) {
|
|
17104
|
+
this.knockingEarly = true;
|
|
17105
|
+
try {
|
|
17106
|
+
for (const transport of transports) {
|
|
17107
|
+
if (epoch !== this.connectionEpoch || this.channel || this.stopped || !standing()) return;
|
|
17108
|
+
await this.knock(transport);
|
|
17109
|
+
}
|
|
17110
|
+
} finally {
|
|
17111
|
+
this.knockingEarly = false;
|
|
17112
|
+
}
|
|
17113
|
+
}
|
|
17114
|
+
/**
|
|
16781
17115
|
* A native endpoint that started while this side's offer is out unanswered: its transport was not up when the dial
|
|
16782
17116
|
* ranked what to try, and joins the race (and what the offer ending goes on to) if both sides allow it.
|
|
16783
17117
|
*/
|
|
@@ -16822,6 +17156,13 @@ var GhostLink = class {
|
|
|
16822
17156
|
*/
|
|
16823
17157
|
async race(epoch, offeredAt) {
|
|
16824
17158
|
const next = this.afterRtc;
|
|
17159
|
+
if (this.knockingEarly && next?.epoch === epoch && epoch === this.connectionEpoch && !this.channel) {
|
|
17160
|
+
this.raceTimer = setTimeout(() => {
|
|
17161
|
+
this.raceTimer = null;
|
|
17162
|
+
this.race(epoch, offeredAt);
|
|
17163
|
+
}, RACE_RETRY_MS);
|
|
17164
|
+
return;
|
|
17165
|
+
}
|
|
16825
17166
|
if (!next || next.epoch !== epoch || epoch !== this.connectionEpoch || this.channel || this.dialing || this.stopped || this.leaving || this.streamBlocked || this.keyStopped || !this.offerOut) return;
|
|
16826
17167
|
const relayed = this.relayedTransports;
|
|
16827
17168
|
const late = Date.now() - offeredAt >= 4e4 || this.answeredAt > 0 && Date.now() - this.answeredAt >= 6e3;
|
|
@@ -16913,7 +17254,7 @@ var GhostLink = class {
|
|
|
16913
17254
|
* the DHT has no room for one, and the text goes without it. A `reply` (WISP 401 § Replies) goes whole on the
|
|
16914
17255
|
* session; on the DHT only its id does, which the contact looks up in its own history. `forwarded`: the hop count
|
|
16915
17256
|
* of a forwarded message (WISP 401 § Forwards), on both paths (the DHT drops it when the packet has no room). `card`:
|
|
16916
|
-
* a status card (WISP
|
|
17257
|
+
* a status card (WISP 405 · Status Cards), on the live session only; on the DHT the text, its fallback, goes alone.
|
|
16917
17258
|
*/
|
|
16918
17259
|
async sendMessage(text, timestamp = Date.now(), stableId, preview, reply, forwarded, card) {
|
|
16919
17260
|
const trimmed = text.trim();
|
|
@@ -17387,6 +17728,7 @@ var GhostLink = class {
|
|
|
17387
17728
|
if (this.options.pinSupport) offered.push(PIN_CAPABILITY);
|
|
17388
17729
|
if (this.options.statusCardSupport) offered.push(STATUS_CARD_CAPABILITY);
|
|
17389
17730
|
if (this.options.buttonsSupport) offered.push(BUTTONS_CAPABILITY);
|
|
17731
|
+
if (this.options.upgradeSupport) offered.push(UPGRADE_CAPABILITY);
|
|
17390
17732
|
if (this.options.deviceCapabilities) offered.push(...this.options.deviceCapabilities);
|
|
17391
17733
|
return offered;
|
|
17392
17734
|
}
|
|
@@ -17617,6 +17959,7 @@ var GhostLink = class {
|
|
|
17617
17959
|
if (changed.includes("edit/1")) this.options.events?.onEditSupport?.(this.supportsEdits);
|
|
17618
17960
|
if (changed.includes("pin/1")) this.options.events?.onPinSupport?.(this.supportsPins);
|
|
17619
17961
|
if (changed.includes("wake/1")) this.options.events?.onWakeSupport?.(this.supportsWake);
|
|
17962
|
+
if (changed.includes("upgrade/1")) this.switcher.reconsider();
|
|
17620
17963
|
const devices = this.options.deviceCapabilities;
|
|
17621
17964
|
if (devices && changed.some((capability) => devices.includes(capability))) this.options.events?.onDeviceCapabilities?.(devices.filter((capability) => this.supportsDevice(capability)));
|
|
17622
17965
|
this.emitPairingState();
|
|
@@ -17715,7 +18058,9 @@ var GhostLink = class {
|
|
|
17715
18058
|
};
|
|
17716
18059
|
if (transport === "webrtc/1") {
|
|
17717
18060
|
this.rtcCandidateWaiter = done;
|
|
17718
|
-
this.
|
|
18061
|
+
this.rtcRetired().then(() => {
|
|
18062
|
+
if (active) return this.dataLink.connect();
|
|
18063
|
+
}).catch(done.reject);
|
|
17719
18064
|
} else {
|
|
17720
18065
|
const endpoint = this.endpoints.get(transport), descriptor = plan.remote.descriptors[transport];
|
|
17721
18066
|
if (!endpoint || !descriptor) {
|
|
@@ -17969,7 +18314,7 @@ var GhostLink = class {
|
|
|
17969
18314
|
oldPaired?.stop();
|
|
17970
18315
|
if (oldChannel) this.retire(oldChannel, () => {
|
|
17971
18316
|
if (!oldBinding && this.activeBinding) this.dataLink.close();
|
|
17972
|
-
});
|
|
18317
|
+
}, !oldBinding);
|
|
17973
18318
|
this.session.setDataLinkOpen(true);
|
|
17974
18319
|
if (migration.plan) switched = {
|
|
17975
18320
|
from,
|
|
@@ -18388,6 +18733,7 @@ var GhostLink = class {
|
|
|
18388
18733
|
this.pongWait = null;
|
|
18389
18734
|
if (this.channel !== channel || this.stopped) return;
|
|
18390
18735
|
traceLink(this.myPubKeyZ32, "pong-late", { unanswered: this.unansweredPings });
|
|
18736
|
+
this.pongLate = true;
|
|
18391
18737
|
this.session.pollNow();
|
|
18392
18738
|
}, PONG_WAIT_MS);
|
|
18393
18739
|
};
|
|
@@ -18424,6 +18770,24 @@ var GhostLink = class {
|
|
|
18424
18770
|
heardFromPeer() {
|
|
18425
18771
|
if (this.pongWait) clearTimeout(this.pongWait);
|
|
18426
18772
|
this.pongWait = null;
|
|
18773
|
+
this.pongLate = false;
|
|
18774
|
+
}
|
|
18775
|
+
/**
|
|
18776
|
+
* The session runs on a native transport, its last ping went unanswered, and the contact's newer packet names another
|
|
18777
|
+
* endpoint for that transport: the contact's app started again (a web page reloading, as a device that takes the
|
|
18778
|
+
* profile does twice), and the endpoint this session runs on went with it, with nothing said. A browser's relay-only
|
|
18779
|
+
* Iroh closes nothing as a page goes, and QUIC notices only at its idle timeout (30 s): until then this side held a
|
|
18780
|
+
* session nothing answered, and the contact, whose key does not dial, waited for it to dial (a Use here said hello
|
|
18781
|
+
* into that session and gave up "unreachable", Omarchy 2026-10-05). The session is let go, and the new endpoint
|
|
18782
|
+
* dialled now, as when the session closes.
|
|
18783
|
+
*/
|
|
18784
|
+
restartedRedial(channel, transport) {
|
|
18785
|
+
if (!this.pongLate || this.channel !== channel || this.paired?.state.status !== "ready") return false;
|
|
18786
|
+
traceLink(this.myPubKeyZ32, "restarted-redial", { transport });
|
|
18787
|
+
this.stopLiveness();
|
|
18788
|
+
this.disconnect();
|
|
18789
|
+
this.peerLost("restarted");
|
|
18790
|
+
return true;
|
|
18427
18791
|
}
|
|
18428
18792
|
dropDeadSession(channel) {
|
|
18429
18793
|
this.stopLiveness();
|
|
@@ -18503,9 +18867,13 @@ var GhostLink = class {
|
|
|
18503
18867
|
}
|
|
18504
18868
|
/**
|
|
18505
18869
|
* Someone is here again (the chat was opened, the window came back): look now, and try at once rather
|
|
18506
|
-
* than after the wait between failed attempts.
|
|
18870
|
+
* than after the wait between failed attempts. `network`: the device's network just came back.
|
|
18507
18871
|
*/
|
|
18508
|
-
wake() {
|
|
18872
|
+
wake(params = {}) {
|
|
18873
|
+
if (params.network) {
|
|
18874
|
+
this.dht?.networkBack();
|
|
18875
|
+
this.networkBack();
|
|
18876
|
+
}
|
|
18509
18877
|
this.autoConnectFailures = 0;
|
|
18510
18878
|
this.lastAutoConnectAt = 0;
|
|
18511
18879
|
this.session.pollNow();
|
|
@@ -19849,7 +20217,7 @@ function validatePaymentTarget(value, now = Date.now()) {
|
|
|
19849
20217
|
* did:dht (the DIF method, https://github.com/decentralized-identity/did-dht/blob/main/spec/spec.md): a W3C DID document written as DNS records into the
|
|
19850
20218
|
* Pkarr packet of an Ed25519 key, the BEP44 mutable item Ghostly already publishes chats under. This
|
|
19851
20219
|
* module maps a document to that packet and back (strictly: a malformed packet is an error, never a
|
|
19852
|
-
* guess) and resolves a did:dht through Pkarr relays. WISP
|
|
20220
|
+
* guess) and resolves a did:dht through Pkarr relays. WISP 310-did-dht describes Ghostly's use.
|
|
19853
20221
|
*/
|
|
19854
20222
|
var DID_DHT_PREFIX = "did:dht:";
|
|
19855
20223
|
/** The TTL the spec recommends: Mainline's default record lifetime. */
|
|
@@ -22598,7 +22966,7 @@ var wireMentions = (mentions) => mentions.map(({ k, o, l }) => ({
|
|
|
22598
22966
|
//#endregion
|
|
22599
22967
|
//#region ../core/src/groupEdits.ts
|
|
22600
22968
|
/**
|
|
22601
|
-
* Editing a sent text in a group (WISP
|
|
22969
|
+
* Editing a sent text in a group (WISP 902 § Edits, both profiles). As in a 1:1 chat (WISP 401 § Edits), the author says
|
|
22602
22970
|
* the whole new text of one of its own messages with an edit number of its own per message, and the highest number
|
|
22603
22971
|
* wins whatever order edits arrive in. A group has no receipts: the author says an edit to every member it can, and
|
|
22604
22972
|
* again later (a private group: when an edge opens; a community: its members catch up from each other).
|
|
@@ -22610,7 +22978,7 @@ var wireMentions = (mentions) => mentions.map(({ k, o, l }) => ({
|
|
|
22610
22978
|
*
|
|
22611
22979
|
* { "x": { "t": "edit", "id": <message id>, "v": 3, "ts": 1790000000000, "text": "Done: 12 of 12", "m"?: [mentions], "sc"?: {card} } }
|
|
22612
22980
|
*
|
|
22613
|
-
* `sc` is the status card of this version (WISP
|
|
22981
|
+
* `sc` is the status card of this version (WISP 405 · Status Cards); an edit with one may be numbered up to
|
|
22614
22982
|
* `STATUS_CARD_LIMITS.edits`, one without only up to `MAX_EDITS_PER_MESSAGE`.
|
|
22615
22983
|
*
|
|
22616
22984
|
* Older apps drop both: a mesh session ignores a `t` it does not know, a community app the `x` frames it does not know.
|
|
@@ -22740,7 +23108,7 @@ var GROUP_LIMITS = {
|
|
|
22740
23108
|
*/
|
|
22741
23109
|
relay: 256,
|
|
22742
23110
|
relayBytes: 262144,
|
|
22743
|
-
/** Other members' edits kept to hand on, the latest per message (WISP
|
|
23111
|
+
/** Other members' edits kept to hand on, the latest per message (WISP 902 § Edits): count and ciphertext bytes. */
|
|
22744
23112
|
relayEdits: 64,
|
|
22745
23113
|
relayEditBytes: 524288,
|
|
22746
23114
|
/** Edits of one member handed on for one sync that asks for them: fewer than a receiver takes in a window. */
|
|
@@ -22867,7 +23235,7 @@ var editAad = (f) => JSON.stringify([
|
|
|
22867
23235
|
f.v,
|
|
22868
23236
|
f.ts
|
|
22869
23237
|
]);
|
|
22870
|
-
/** What a member signs of its reaction, so that a hub can pass it on (WISP
|
|
23238
|
+
/** What a member signs of its reaction, so that a hub can pass it on (WISP 902 · Group Mesh § Hubs). */
|
|
22871
23239
|
var reactionSigned = (g, r) => utf8Encode(JSON.stringify([
|
|
22872
23240
|
"ghostly-group/1 react",
|
|
22873
23241
|
g,
|
|
@@ -22880,7 +23248,7 @@ var reactionSigned = (g, r) => utf8Encode(JSON.stringify([
|
|
|
22880
23248
|
* on vouches for. A frame of its own, which apps from before hubs drop: they would take it as the hub's reaction.
|
|
22881
23249
|
*/
|
|
22882
23250
|
var GROUP_REACTED_FRAME = "group-reacted";
|
|
22883
|
-
/** What a member signs of its pin: any member passes it on as it is (WISP
|
|
23251
|
+
/** What a member signs of its pin: any member passes it on as it is (WISP 902 · Group Mesh § Pinned message). */
|
|
22884
23252
|
var pinSigned = (g, p) => utf8Encode(JSON.stringify([
|
|
22885
23253
|
"ghostly-group/1 pin",
|
|
22886
23254
|
g,
|
|
@@ -23148,7 +23516,7 @@ var GroupSession = class {
|
|
|
23148
23516
|
get id() {
|
|
23149
23517
|
return this.state.id;
|
|
23150
23518
|
}
|
|
23151
|
-
/** The name the admin gave the group, else the one it had when I got in (WISP
|
|
23519
|
+
/** The name the admin gave the group, else the one it had when I got in (WISP 902 § Metadata). */
|
|
23152
23520
|
get name() {
|
|
23153
23521
|
return groupDisplayName(this.state.meta, this.state.name);
|
|
23154
23522
|
}
|
|
@@ -23184,7 +23552,7 @@ var GroupSession = class {
|
|
|
23184
23552
|
get picture() {
|
|
23185
23553
|
return groupMetaPicture(this.state.meta);
|
|
23186
23554
|
}
|
|
23187
|
-
/** Whom the admin pinned as hubs and excluded (WISP
|
|
23555
|
+
/** Whom the admin pinned as hubs and excluded (WISP 902 · Group Mesh § Hubs), from the group's metadata. */
|
|
23188
23556
|
get hubPolicy() {
|
|
23189
23557
|
const meta = this.state.meta;
|
|
23190
23558
|
if (!this.policyOf || this.policyOf.sig !== meta?.sig) this.policyOf = {
|
|
@@ -23357,7 +23725,7 @@ var GroupSession = class {
|
|
|
23357
23725
|
this.hooks.changed();
|
|
23358
23726
|
return { secret };
|
|
23359
23727
|
}
|
|
23360
|
-
/** Someone out of the roster is not handed on any more, whatever they sent while in it (WISP
|
|
23728
|
+
/** Someone out of the roster is not handed on any more, whatever they sent while in it (WISP 902 § Catch-up). */
|
|
23361
23729
|
pruneRelay() {
|
|
23362
23730
|
if (this.state.relay?.length) this.state.relay = this.state.relay.filter((f) => rosterHas(this.roster, f.s));
|
|
23363
23731
|
if (this.state.relayEdits?.length) this.state.relayEdits = this.state.relayEdits.filter((f) => rosterHas(this.roster, f.s));
|
|
@@ -23424,8 +23792,8 @@ var GroupSession = class {
|
|
|
23424
23792
|
* Encrypts and signs a text, keeps it for catch-up and sends it to every other member. `mentions`: places of the
|
|
23425
23793
|
* text that name members (everyone: the admin only); what does not hold is left out. `reply`: the message it
|
|
23426
23794
|
* answers, sealed apart from the text (`r`) like the mentions, so an older app still reads the text. `forwarded`: the
|
|
23427
|
-
* hop count of a forwarded text (`f`, WISP
|
|
23428
|
-
* box of its own (`sc`, WISP
|
|
23795
|
+
* hop count of a forwarded text (`f`, WISP 902 § Forwards). `card`: a status card, checked by the caller, sealed in a
|
|
23796
|
+
* box of its own (`sc`, WISP 405 · Status Cards); the text is its fallback.
|
|
23429
23797
|
*/
|
|
23430
23798
|
sendText(text, now = Date.now(), mentions = [], reply, forwarded, card) {
|
|
23431
23799
|
return this.serialize(async () => {
|
|
@@ -23609,7 +23977,7 @@ var GroupSession = class {
|
|
|
23609
23977
|
return first ? groupMessageId(f.s, first.e, first.n) : groupMessageId(f.s, f.e, f.n);
|
|
23610
23978
|
}
|
|
23611
23979
|
/**
|
|
23612
|
-
* Says an edit of one of my messages (WISP
|
|
23980
|
+
* Says an edit of one of my messages (WISP 902 § Edits): edit `v`, the whole new text and its mentions, sealed under
|
|
23613
23981
|
* the key of the message's epoch and signed, to every member of that epoch still in the group, or to `to` alone (an
|
|
23614
23982
|
* edge that just opened). Someone admitted after the message cannot open it and is not sent it. The message's
|
|
23615
23983
|
* epoch key must still be here: past `GROUP_LIMITS.secrets` epochs a message cannot be edited.
|
|
@@ -23710,7 +24078,7 @@ var GroupSession = class {
|
|
|
23710
24078
|
}
|
|
23711
24079
|
/**
|
|
23712
24080
|
* A frame from an authenticated member over the pairwise edge. Anything malformed or unauthorized is dropped.
|
|
23713
|
-
* Resolves to what it took that a hub passes on to its other edges (WISP
|
|
24081
|
+
* Resolves to what it took that a hub passes on to its other edges (WISP 902 · Group Mesh § Hubs): each message,
|
|
23714
24082
|
* edit, commit (without the secret sealed for me), metadata statement and signed leave, the first time only, so a
|
|
23715
24083
|
* flood among hubs stops at every member that already has it. Only what its author still in the roster signed.
|
|
23716
24084
|
*/
|
|
@@ -24260,7 +24628,7 @@ var GroupSession = class {
|
|
|
24260
24628
|
}
|
|
24261
24629
|
/**
|
|
24262
24630
|
* Pins a member as a hub, excludes one from being a hub, or leaves it to the member's app (`null`): only the admin,
|
|
24263
|
-
* in the group's metadata beside the picture (WISP
|
|
24631
|
+
* in the group's metadata beside the picture (WISP 902 · Group Mesh § Hubs).
|
|
24264
24632
|
*/
|
|
24265
24633
|
setHub(key, role, now = Date.now()) {
|
|
24266
24634
|
return this.serialize(async () => {
|
|
@@ -24372,7 +24740,7 @@ var GroupSession = class {
|
|
|
24372
24740
|
//#endregion
|
|
24373
24741
|
//#region ../core/src/groupWake.ts
|
|
24374
24742
|
/**
|
|
24375
|
-
* Wake-up push in a private group (WISP
|
|
24743
|
+
* Wake-up push in a private group (WISP 902 · Group Mesh § Wake-up push): a member whose web app is closed is woken
|
|
24376
24744
|
* when a message names it. `wake/1` as a 1:1 chat has it (WISP 401 § Wake-up push), carried on the group's edges: each
|
|
24377
24745
|
* member shares its subscription with each other member on their own edge, under a token of that edge alone, and
|
|
24378
24746
|
* only in a group it has not muted:
|
|
@@ -24537,7 +24905,7 @@ function entryParams(link, mySeed, myZ32, peerZ32) {
|
|
|
24537
24905
|
//#endregion
|
|
24538
24906
|
//#region ../core/src/groupCommunity.ts
|
|
24539
24907
|
/**
|
|
24540
|
-
* `group-community/1` (WISP
|
|
24908
|
+
* `group-community/1` (WISP 903 · Group Community): a group whose link is the way in, for hundreds
|
|
24541
24909
|
* of members, where the admin need not be online for anyone to join.
|
|
24542
24910
|
*
|
|
24543
24911
|
* What differs from `group-mesh/1`:
|
|
@@ -24833,7 +25201,7 @@ var seenKey = (e, h) => `${e}:${h}`;
|
|
|
24833
25201
|
/** One of a member's frames, among its own. */
|
|
24834
25202
|
var frameKey = (f) => `${f.e}:${f.h}:${f.n}`;
|
|
24835
25203
|
/**
|
|
24836
|
-
* Where a frame said again under a newer commit was first said (`o`, WISP
|
|
25204
|
+
* Where a frame said again under a newer commit was first said (`o`, WISP 903 § Messages and catch-up): its commit's
|
|
24837
25205
|
* epoch and short hash and its number there. Never the frame's own place, nor a later epoch than its own.
|
|
24838
25206
|
*/
|
|
24839
25207
|
function readOriginal(o, frame) {
|
|
@@ -24855,7 +25223,7 @@ function seenIn(seen, f) {
|
|
|
24855
25223
|
return f.n === entry.high || entry.window.includes(f.n);
|
|
24856
25224
|
}
|
|
24857
25225
|
/**
|
|
24858
|
-
* Whether a stored community state holds the frame a beacon's head names (WISP
|
|
25226
|
+
* Whether a stored community state holds the frame a beacon's head names (WISP 903 § Head), read without running
|
|
24859
25227
|
* the group: its own frame, one it took, or one of an epoch so far behind its own that it would not be taken now.
|
|
24860
25228
|
*/
|
|
24861
25229
|
function communityHasFrame(state, f) {
|
|
@@ -25033,7 +25401,7 @@ var CommunitySession = class {
|
|
|
25033
25401
|
get id() {
|
|
25034
25402
|
return this.state.id;
|
|
25035
25403
|
}
|
|
25036
|
-
/** The name the admin gave the group, else the one it had when I got in (WISP
|
|
25404
|
+
/** The name the admin gave the group, else the one it had when I got in (WISP 903 § Metadata). */
|
|
25037
25405
|
get name() {
|
|
25038
25406
|
return groupDisplayName(this.state.meta, this.state.name);
|
|
25039
25407
|
}
|
|
@@ -25104,7 +25472,7 @@ var CommunitySession = class {
|
|
|
25104
25472
|
return -1;
|
|
25105
25473
|
}
|
|
25106
25474
|
/**
|
|
25107
|
-
* What someone the chain took out is told when it comes back (WISP
|
|
25475
|
+
* What someone the chain took out is told when it comes back (WISP 903 § Leaving and removal): the commits from where
|
|
25108
25476
|
* it says it is (`sync`: its tip, or its locator) up to the one that took it out, and nothing after. Later commits
|
|
25109
25477
|
* are not its business (a `link` commit names the new entry key); no secret, no stored frame, no seed goes with them.
|
|
25110
25478
|
* Empty when it is a member, was never one, or already holds that commit.
|
|
@@ -25701,8 +26069,8 @@ var CommunitySession = class {
|
|
|
25701
26069
|
/**
|
|
25702
26070
|
* `mentions`: places of the text that name members, sealed with it (`m`). Never everyone in a community. They
|
|
25703
26071
|
* count against the text's 16 KiB, so the box stays within what older apps accept. `reply`: the message it
|
|
25704
|
-
* answers (`r`), counted the same way. `forwarded`: a forwarded text's hop count (`fw`, WISP
|
|
25705
|
-
* a status card, checked by the caller (`sc`, WISP
|
|
26072
|
+
* answers (`r`), counted the same way. `forwarded`: a forwarded text's hop count (`fw`, WISP 903 § Forwards). `card`:
|
|
26073
|
+
* a status card, checked by the caller (`sc`, WISP 405 · Status Cards), counted the same way; the text is its fallback.
|
|
25706
26074
|
*/
|
|
25707
26075
|
sendText(text, nick, now = Date.now(), mentions = [], reply, forwarded, card) {
|
|
25708
26076
|
return this.serialize(async () => {
|
|
@@ -25754,7 +26122,7 @@ var CommunitySession = class {
|
|
|
25754
26122
|
});
|
|
25755
26123
|
}
|
|
25756
26124
|
/**
|
|
25757
|
-
* An edit of one of my messages (WISP
|
|
26125
|
+
* An edit of one of my messages (WISP 903 · Group Community § Edits): an application frame like `sendApp`'s, sealed
|
|
25758
26126
|
* to the current epoch, but with a text's room rather than an application frame's, since it carries a whole text.
|
|
25759
26127
|
* The nick is left out when the text leaves no room for it.
|
|
25760
26128
|
*/
|
|
@@ -26467,7 +26835,7 @@ function signCommunity(commit, seed) {
|
|
|
26467
26835
|
//#endregion
|
|
26468
26836
|
//#region ../core/src/groupTyping.ts
|
|
26469
26837
|
/**
|
|
26470
|
-
* The typing indicator of a private group (WISP
|
|
26838
|
+
* The typing indicator of a private group (WISP 902 · Group Mesh § Typing): "Ana is typing", said on the group's
|
|
26471
26839
|
* edges only, the way a 1:1 chat says it on its session (WISP 401 § Typing), with the same kinds, limits and wording.
|
|
26472
26840
|
*
|
|
26473
26841
|
* {"t":"group-typing","g":"<group>","e":3,"n":"<nonce>","c":"<sealed>"}
|
|
@@ -26571,6 +26939,6 @@ var GroupTypingBoard = class {
|
|
|
26571
26939
|
}
|
|
26572
26940
|
};
|
|
26573
26941
|
//#endregion
|
|
26574
|
-
export { checkAtprotoProofRecord as $, filePartName as $a, BUTTON_ID as $i, freshHubs as $n, createChatInvite as $o, IdentityExchange as $r, randomBytes as $s, isPubkyKey as $t, GROUP_EDIT_TEXT_BYTES as A, RTC_CONFIG as Aa, voicePeaksOf as Ai, findEndpoint as An, readSetUpdate as Ao, newWakeToken as Ar, RelayTransport as As, encodeDidDhtPacket as At, sameSshKey as B, LEGACY_SERVICES as Ba, GROUP_REACTION_FRAME as Bi, SIGNAL_FRESH_MS as Bn, enrollCancelReason as Bo, HOLD_LIMITS as Br, createRelayPayload as Bs, USDT_PUBLIC_RPC as Bt, groupWakes as C, formatLocalTarget as Ca, sanitizeFileName as Ci, resolveLightningDestination as Cn, readHandoffHello as Co, nextPinNumber as Cr, nextTurnPosition as Cs, parseDidSignaturePaste as Ct, GroupSession as D, decodeControl as Da, baseMime as Di, ENDPOINT as Dn, sealHandoffPiece as Do, WAKE_CALL_INTERVAL_MS as Dr, turnKeys as Ds, didDhtDocument as Dt, GROUP_READ_NOTE as E, LIMITS as Ea, VOICE_LIMITS as Ei, decodeBolt11 as En, readHandoffVerified as Eo, readPin as Er, signTurnRelease as Es, strictBase64Url as Et, SSHSIG_MAX_ARMOR as F, extractParamsFromSdp as Fa, dhtEditId as Fi, INVITE_TAKEN as Fn, ENROLL_DONE as Fo, vapidKeysMatch as Fr, budgetRetryMs as Fs, ONCHAIN_PROVIDER as Ft, decodeAnyBitcoinAddress as G, HANDOFF_HAVE as Ga, reactionEmoji as Gi, MESH_HUBS as Gn, enrollProofFrame as Go, CapsExchange as Gr, identityFromSeed as Gs, validatePaymentTarget as Gt, verifySshSignature as H, HANDOFF_ACK as Ha, ReactionWindow as Hi, SIGNAL_REPLY_MS as Hn, enrollDoneFrame as Ho, HoldRefusedError as Hr, openRelayPayload as Hs, assertTokenUnits as Ht, SSHSIG_NAMESPACE as I, parseCallSignal as Ia, parseLinkPreview as Ii, LIVENESS_PING_MS as In, ENROLL_GRANT as Io, BUTTONS_CAPABILITY as Ir, isDiscoveryBudgetError as Is, PaymentPreflightError as It, ATPROTO_PROOF_COLLECTION as J, HANDOFF_PAKE as Ja, validReactionNumber as Ji, pickMeshHubs as Jn, verifyEnrollProof as Jo, dialDescriptors as Jr, verify as Js, SPARK_PROVIDER as Jt, ATPROTO_DECLARED_SCOPE as K, HANDOFF_HELLO as Ka, reactionIsNewer as Ki, mayBeHub as Kn, readEnrollGrant as Ko, DHT_TEXT_CAPABILITY as Kr, identityFromSeedB64 as Ks, walletNetworkOf as Kt, SshSigError as L, signalHasVideo as La, forwardedAgain as Li, pairedMessageFrame as Ln, ENROLL_HELLO as Lo, EDIT_CAPABILITY as Lr, withRequestOptions as Ls, SEPOLIA_TEST_USDT as Lt, parseCommunityEdit as M, buildSdpFromSignal as Ma, EDIT_BUFFER_MS as Mi, parseCashuRequestPayload as Mn, setUpdateFrame as Mo, relayRequest as Mr, normalizeRelayUrl as Ms, signDidDhtPacket as Mt, mentionsMember as N, callRtcConfig as Na, EDIT_RATE_WINDOW_MS as Ni, parseFedimintRequestPayload as Nn, signTombstone as No, wakeRequest as Nr, setLinkTraceSink as Ns, ETHEREUM_USDT as Nt, COMMUNITY_EDIT_FRAME as O, encodeControl as Oa, fileMessageText as Oi, cashuRequestPayload as On, SET_ACK as Oo, WakeLimiter as Or, turnName as Os, didDhtFromPublicKey as Ot, groupName as P, callSignalHeardAt as Pa, RateWindow as Pi, GhostLink as Pn, verifySetUpdate as Po, checkPushEndpoint as Pr, traceLink as Ps, EVM_TEST_CHAINS as Pt, atprotoProofRkey as Q, HANDOFF_VERIFIED as Qa, replySnippet as Qi, doorHubs as Qn, OwnInviteError as Qo, IdentityCheckUnavailable as Qr, fromBase64Url as Qs, PUBKY_PROOF_MAX_BYTES as Qt, parseSshPublicKey as R, emptyLinkRecords as Ra, readForwarded as Ri, CarriedTransport as Rn, ENROLL_PROOF as Ro, HOLD_CAPABILITY as Rr, PacketTooLargeError as Rs, SEPOLIA_TEST_USDT_FAUCET as Rt, groupWakeFrame as S, GhostlyHttpError as Sa, safeBlobType as Si, requestLnurlInvoice as Sn, readHandoffHave as So, mayPin as Sr, firstDeviceSetSecret as Ss, parseDidDocument as St, GROUP_REACTED_FRAME as T, webLocalFetch as Ta, PLAYABLE_AUDIO as Ti, engineText as Tn, readHandoffTurnFrame as To, pinNumberHolds as Tr, signTurnPacket as Ts, staticDidDocument as Tt, verifyBitcoinMessage as U, HANDOFF_CANCEL as Ua, nextReactionNumber as Ui, groupSignalFrame as Un, enrollGrantFrame as Uo, newMailbox as Ur, parseRelayPayload as Us, assertWholeSats as Ut, sshSignCommand as V, serviceIdFromName as Va, REACTION_LIMITS as Vi, SIGNAL_HOLD_MS as Vn, enrollDigits as Vo, HoldKeys as Vr, newerPacket as Vs, WALLET_NETWORKS as Vt, BitcoinAddressError as W, HANDOFF_DONE as Wa, queueReaction as Wi, readGroupSignal as Wn, enrollHelloFrame as Wo, readManifest as Wr, createIdentity as Ws, formatPaymentAmount as Wt, atprotoDidMethod as X, HANDOFF_REQUEST as Xa, pairedReplyAuthor as Xi, beaconKeys as Xn, deviceInviteJoinerParams as Xo, IDENTITY_MAX_VALIDITY as Xr, sanitizeAvatar as Xs, sparkInvoiceDetails as Xt, ATPROTO_PROOF_SCOPE as Y, HANDOFF_PIECE_BYTES as Ya, wireReaction as Yi, COMMUNITY_TOPOLOGY as Yn, createDeviceInvite as Yo, IDENTITY_MAX_EVIDENCE as Yr, MAX_AVATAR_LENGTH as Ys, sparkAddressKind as Yt, atprotoProofRecord as Z, HANDOFF_TIMINGS as Za, readReply as Zi, beaconRecords as Zn, readDeviceInvite as Zo, IDENTITY_REVOCATION_LABEL as Zr, bytesEqual as Zs, isBitcoinAddress as Zt, knockRecords as _, edgeParams as _a, peekDhtMailbox as _i, recordsFromTxt as _n, handoffVersions as _o, TypingSender as _r, TURN_SETTLE_MS as _s, didWebFileNames as _t, sealGroupTyping as a, sameButtonText as aa, receivedIdentityStatus as ai, pubkyProofPath as an, handoffContext as ao, pickHubs as ar, ENROLL_CAPABILITY as as, verifyAtprotoRecordProof as at, GROUP_WAKE_FRAME as b, RELAY_POLL_INTERVALS as ba, parseImageMeta as bi, txtValue as bn, readHandoffCancel as bo, GROUP_PIN_FRAME as br, TURN_LAST_SEQUENCE as bs, normalizeDid as bt, GROUP_READ_NOTE_COMMUNITY as c, claimedTime as ca, PeerProofs as ci, DOMAIN_FILE_MAX_BYTES as cn, handoffDigest as co, readBeaconHead as cr, deviceEchoNonce as cs, DID_JWS_MAX_LENGTH as ct, encodeCommunityLink as d, shownTime as da, TRANSPORTS as di, domainTxtRecord as dn, handoffManifestFrames as do, ChatFiles as dr, devicePingFrame as ds, checkDidSignature as dt, ITEM_STATES as ea, toBase64Url as ec, boundVerifiedIdentity as ei, isPubkyProofFolder as en, handoffAttemptAllowed as eo, lobbyKeys as er, decodeInviteCode as es, isAtprotoDid as et, KNOCK_TTL_MS as f, GROUP_MEMBER_CAP as fa, automaticTransport as fi, domainWellKnownFile as fn, handoffOfferFrame as fo, FILE_LIMITS as fr, newDeviceSetSecret as fs, didGhostlyServiceEntry as ft, knockIdentity as g, verifyCommitSignature as ga, DHT_MESSAGE_TTL as gi, normalizeDomain as gn, handoffStreamKey as go, TYPING_REFRESH_MS as gr, TURN_RAISE_READ_TIMEOUT_MS as gs, didWebFile as gt, entryParams as h, rosterHas as ha, DHT_FILE_TOO_LARGE as hi, nip05RootKey as hn, handoffRetryAfter as ho, TYPING_KINDS as hr, TURN_PUT_WINDOW_MS as hs, didWebDocumentUrl as ht, openGroupTyping as i, checkStatusCard as ia, newIdentityBinding as ii, pubkyProofFile as in, handoffCancelFrame as io, newerHead as ir, DEVICES_CAPABILITY as is, AtprotoProofError as it, carryMentions as j, answersOffer as ja, parseVideoMeta as ji, isFederationId as jn, setAckFrame as jo, parseWakeFrame as jr, currentRelays as js, resolveDidDht as jt, GROUP_EDIT_FRAME as k, CALL_SIGNAL_MAX_AGE_MS as ka, parseVoiceMeta as ki, fedimintRequestPayload as kn, SET_UPDATE as ko, deviceWakeRequest as kr, DEFAULT_RELAYS as ks, encodeDidDhtDeactivated as kt, communityHasFrame as l, heardTime as la, emptyProofLedger as li, decodeDnsResponse as ln, handoffEphemeral as lo, readLobby as lr, deviceKeyZ32 as ls, DidError as lt, encodeGroupEntryLink as m, MEMBER_KEY as ma, irohRelayUrl as mi, isPublicAddress as mn, handoffReleaseFrame as mo, transferStalled as mr, webCryptoSigner as ms, didSignedMessages as mt, GroupTypingBoard as n, STATUS_CARD_LIMITS as na, utf8Decode as nc, identityRevocationValue as ni, normalizePubkyKey as nn, handoffAttemptSucceeded as no, mergeBeacon as nr, inviteLink as ns, parseAtprotoDidDocument as nt, COMMUNITY_LIMITS as o, statusCardText as oa, revokesIdentity as oi, homeserverWebEndpoint as on, handoffContextProof as oo, rankHubs as or, HANDOFF_CAPABILITY as os, DID_DOCUMENT_MAX_BYTES as ot, decodeGroupEntryLink as p, MAX_GROUP_CHAIN as pa, irohDescriptor as pi, encodeDnsQuery as pn, handoffPakeFrame as po, transferEnded as pr, seedSigner as ps, didJwsAlg as pt, ATPROTO_FULL_SCOPE as q, HANDOFF_OFFER as qa, readReaction as qi, meshHubs as qn, readEnrollHello as qo, capsDescriptors as qr, publicKeyFromZ32 as qs, SPARK_NETWORKS as qt, groupTypingEpoch as r, buttonLabelClash as ra, utf8Encode as rc, identityStatement as ri, pubkyProofCapability as rn, handoffBusyFrame as ro, mergeLobby as rr, inviteOwnership as rs, shortAtprotoDid as rt, CommunitySession as s, arrivalKey as sa, lastSharedWithMe as si, pubkyHomeserverOf as sn, handoffDataFrame as so, readBeacon as sr, deviceEchoFrame as ss, DID_FILE_MAX_BYTES as st, GROUP_TYPING_FRAME as t, RUN_RESULTS as ta, toZ32 as tc, emptyIdentityLedger as ti, newPubkyProofFolder as tn, handoffAttemptFailed as to, lobbyRecords as tr, encodeInviteCode as ts, normalizeAtprotoHandle as tt, decodeCommunityLink as u, receivedTimestamp as ua, validExternalKey as ui, dnsProofName as un, handoffHelloFrame as uo, shouldBeHub as ur, deviceLinkPairing as us, checkDidJws as ut, mergeKnocks as v, DirectPathWatch as va, DHT_FILE_CAPABILITY as vi, recordsFromWellKnown as vn, newHandoffId as vo, sanitizeTypingStatus as vr, classifyTurnRead as vs, didWebFileUrl as vt, parseGroupWakeFrame as w, parseLocalTarget as wa, sanitizeMime as wi, engineError as wn, readHandoffPake as wo, pinIsNewer as wr, readTurnPacket as ws, signingKeys as wt, GroupWakeLimiter as x, presenceSeenAt as xa, readImageMeta as xi, wellKnownUrl as xn, readHandoffDone as xo, PIN_LIMITS as xr, TURN_MAX as xs, parseDid as xt, readKnocks as y, EXPECT_PEER_MS as ya, dhtFileId as yi, recordsName as yn, readHandoffAck as yo, typingActivity as yr, turnPutSummary as ys, didWebHost as yt, parseSshSignature as z, HTTP_SERVICE_PROTO as za, COMMUNITY_REACTION_FRAME as zi, GROUP_SIGNAL_FRAME as zn, enrollCancelFrame as zo, HOLD_DATED_BACK_MS as zr, RELAY_PAYLOAD_MAX_BYTES as zs, TEST_USDT_FAUCET_AMOUNT as zt };
|
|
26942
|
+
export { checkAtprotoProofRecord as $, HANDOFF_VERIFIED as $a, replySnippet as $i, freshHubs as $n, OwnInviteError as $o, IdentityCheckUnavailable as $r, fromBase64Url as $s, isPubkyKey as $t, GROUP_EDIT_TEXT_BYTES as A, CALL_SIGNAL_MAX_AGE_MS as Aa, parseVoiceMeta as Ai, findEndpoint as An, SET_UPDATE as Ao, newWakeToken as Ar, DEFAULT_RELAYS as As, encodeDidDhtPacket as At, sameSshKey as B, HTTP_SERVICE_PROTO as Ba, COMMUNITY_REACTION_FRAME as Bi, SIGNAL_FRESH_MS as Bn, enrollCancelFrame as Bo, HOLD_DATED_BACK_MS as Br, RELAY_PAYLOAD_MAX_BYTES as Bs, USDT_PUBLIC_RPC as Bt, groupWakes as C, GhostlyHttpError as Ca, safeBlobType as Ci, resolveLightningDestination as Cn, readHandoffHave as Co, nextPinNumber as Cr, firstDeviceSetSecret as Cs, parseDidSignaturePaste as Ct, GroupSession as D, LIMITS as Da, VOICE_LIMITS as Di, ENDPOINT as Dn, readHandoffVerified as Do, WAKE_CALL_INTERVAL_MS as Dr, signTurnRelease as Ds, didDhtDocument as Dt, GROUP_READ_NOTE as E, webLocalFetch as Ea, PLAYABLE_AUDIO as Ei, decodeBolt11 as En, readHandoffTurnFrame as Eo, readPin as Er, signTurnPacket as Es, strictBase64Url as Et, SSHSIG_MAX_ARMOR as F, callSignalHeardAt as Fa, RateWindow as Fi, INVITE_TAKEN as Fn, verifySetUpdate as Fo, vapidKeysMatch as Fr, traceLink as Fs, ONCHAIN_PROVIDER as Ft, decodeAnyBitcoinAddress as G, HANDOFF_DONE as Ga, queueReaction as Gi, MESH_HUBS as Gn, enrollHelloFrame as Go, readManifest as Gr, createIdentity as Gs, validatePaymentTarget as Gt, verifySshSignature as H, serviceIdFromName as Ha, REACTION_LIMITS as Hi, SIGNAL_REPLY_MS as Hn, enrollDigits as Ho, HoldKeys as Hr, newerPacket as Hs, assertTokenUnits as Ht, SSHSIG_NAMESPACE as I, extractParamsFromSdp as Ia, dhtEditId as Ii, LIVENESS_PING_MS as In, ENROLL_DONE as Io, BUTTONS_CAPABILITY as Ir, budgetRetryMs as Is, PaymentPreflightError as It, ATPROTO_PROOF_COLLECTION as J, HANDOFF_OFFER as Ja, readReaction as Ji, pickMeshHubs as Jn, readEnrollHello as Jo, capsDescriptors as Jr, publicKeyFromZ32 as Js, SPARK_PROVIDER as Jt, ATPROTO_DECLARED_SCOPE as K, HANDOFF_HAVE as Ka, reactionEmoji as Ki, mayBeHub as Kn, enrollProofFrame as Ko, CapsExchange as Kr, identityFromSeed as Ks, walletNetworkOf as Kt, SshSigError as L, parseCallSignal as La, parseLinkPreview as Li, pairedMessageFrame as Ln, ENROLL_GRANT as Lo, EDIT_CAPABILITY as Lr, isDiscoveryBudgetError as Ls, SEPOLIA_TEST_USDT as Lt, parseCommunityEdit as M, answersOffer as Ma, parseVideoMeta as Mi, parseCashuRequestPayload as Mn, setAckFrame as Mo, relayRequest as Mr, currentRelays as Ms, signDidDhtPacket as Mt, mentionsMember as N, buildSdpFromSignal as Na, EDIT_BUFFER_MS as Ni, parseFedimintRequestPayload as Nn, setUpdateFrame as No, wakeRequest as Nr, normalizeRelayUrl as Ns, ETHEREUM_USDT as Nt, COMMUNITY_EDIT_FRAME as O, decodeControl as Oa, baseMime as Oi, cashuRequestPayload as On, sealHandoffPiece as Oo, WakeLimiter as Or, turnKeys as Os, didDhtFromPublicKey as Ot, groupName as P, callRtcConfig as Pa, EDIT_RATE_WINDOW_MS as Pi, GhostLink as Pn, signTombstone as Po, checkPushEndpoint as Pr, setLinkTraceSink as Ps, EVM_TEST_CHAINS as Pt, atprotoProofRkey as Q, HANDOFF_TIMINGS as Qa, readReply as Qi, doorHubs as Qn, readDeviceInvite as Qo, IDENTITY_REVOCATION_LABEL as Qr, bytesEqual as Qs, PUBKY_PROOF_MAX_BYTES as Qt, parseSshPublicKey as R, signalHasVideo as Ra, forwardedAgain as Ri, CarriedTransport as Rn, ENROLL_HELLO as Ro, UPGRADE_CAPABILITY as Rr, withRequestOptions as Rs, SEPOLIA_TEST_USDT_FAUCET as Rt, groupWakeFrame as S, presenceSeenAt as Sa, readImageMeta as Si, requestLnurlInvoice as Sn, readHandoffDone as So, mayPin as Sr, TURN_MAX as Ss, parseDidDocument as St, GROUP_REACTED_FRAME as T, parseLocalTarget as Ta, sanitizeMime as Ti, engineText as Tn, readHandoffPake as To, pinNumberHolds as Tr, readTurnPacket as Ts, staticDidDocument as Tt, verifyBitcoinMessage as U, HANDOFF_ACK as Ua, ReactionWindow as Ui, groupSignalFrame as Un, enrollDoneFrame as Uo, HoldRefusedError as Ur, openRelayPayload as Us, assertWholeSats as Ut, sshSignCommand as V, LEGACY_SERVICES as Va, GROUP_REACTION_FRAME as Vi, SIGNAL_HOLD_MS as Vn, enrollCancelReason as Vo, HOLD_LIMITS as Vr, createRelayPayload as Vs, WALLET_NETWORKS as Vt, BitcoinAddressError as W, HANDOFF_CANCEL as Wa, nextReactionNumber as Wi, readGroupSignal as Wn, enrollGrantFrame as Wo, newMailbox as Wr, parseRelayPayload as Ws, formatPaymentAmount as Wt, atprotoDidMethod as X, HANDOFF_PIECE_BYTES as Xa, wireReaction as Xi, beaconKeys as Xn, createDeviceInvite as Xo, IDENTITY_MAX_EVIDENCE as Xr, MAX_AVATAR_LENGTH as Xs, sparkInvoiceDetails as Xt, ATPROTO_PROOF_SCOPE as Y, HANDOFF_PAKE as Ya, validReactionNumber as Yi, COMMUNITY_TOPOLOGY as Yn, verifyEnrollProof as Yo, dialDescriptors as Yr, verify as Ys, sparkAddressKind as Yt, atprotoProofRecord as Z, HANDOFF_REQUEST as Za, pairedReplyAuthor as Zi, beaconRecords as Zn, deviceInviteJoinerParams as Zo, IDENTITY_MAX_VALIDITY as Zr, sanitizeAvatar as Zs, isBitcoinAddress as Zt, knockRecords as _, verifyCommitSignature as _a, DHT_MESSAGE_TTL as _i, recordsFromTxt as _n, handoffStreamKey as _o, TypingSender as _r, TURN_RAISE_READ_TIMEOUT_MS as _s, didWebFileNames as _t, sealGroupTyping as a, checkStatusCard as aa, newIdentityBinding as ai, pubkyProofPath as an, handoffCancelFrame as ao, pickHubs as ar, DEVICES_CAPABILITY as as, verifyAtprotoRecordProof as at, GROUP_WAKE_FRAME as b, EXPECT_PEER_MS as ba, dhtFileId as bi, txtValue as bn, readHandoffAck as bo, GROUP_PIN_FRAME as br, turnPutSummary as bs, normalizeDid as bt, GROUP_READ_NOTE_COMMUNITY as c, arrivalKey as ca, lastSharedWithMe as ci, DOMAIN_FILE_MAX_BYTES as cn, handoffDataFrame as co, readBeaconHead as cr, deviceEchoFrame as cs, DID_JWS_MAX_LENGTH as ct, encodeCommunityLink as d, receivedTimestamp as da, validExternalKey as di, domainTxtRecord as dn, handoffHelloFrame as do, ChatFiles as dr, deviceLinkPairing as ds, checkDidSignature as dt, BUTTON_ID as ea, randomBytes as ec, IdentityExchange as ei, isPubkyProofFolder as en, filePartName as eo, lobbyKeys as er, createChatInvite as es, isAtprotoDid as et, KNOCK_TTL_MS as f, shownTime as fa, TRANSPORTS as fi, domainWellKnownFile as fn, handoffManifestFrames as fo, FILE_LIMITS as fr, devicePingFrame as fs, didGhostlyServiceEntry as ft, knockIdentity as g, rosterHas as ga, DHT_FILE_TOO_LARGE as gi, normalizeDomain as gn, handoffRetryAfter as go, TYPING_REFRESH_MS as gr, TURN_PUT_WINDOW_MS as gs, didWebFile as gt, entryParams as h, MEMBER_KEY as ha, irohRelayUrl as hi, nip05RootKey as hn, handoffReleaseFrame as ho, TYPING_KINDS as hr, webCryptoSigner as hs, didWebDocumentUrl as ht, openGroupTyping as i, buttonLabelClash as ia, utf8Encode as ic, identityStatement as ii, pubkyProofFile as in, handoffBusyFrame as io, newerHead as ir, inviteOwnership as is, AtprotoProofError as it, carryMentions as j, RTC_CONFIG as ja, voicePeaksOf as ji, isFederationId as jn, readSetUpdate as jo, parseWakeFrame as jr, RelayTransport as js, resolveDidDht as jt, GROUP_EDIT_FRAME as k, encodeControl as ka, fileMessageText as ki, fedimintRequestPayload as kn, SET_ACK as ko, deviceWakeRequest as kr, turnName as ks, encodeDidDhtDeactivated as kt, communityHasFrame as l, claimedTime as la, PeerProofs as li, decodeDnsResponse as ln, handoffDigest as lo, readLobby as lr, deviceEchoNonce as ls, DidError as lt, encodeGroupEntryLink as m, MAX_GROUP_CHAIN as ma, irohDescriptor as mi, isPublicAddress as mn, handoffPakeFrame as mo, transferStalled as mr, seedSigner as ms, didSignedMessages as mt, GroupTypingBoard as n, RUN_RESULTS as na, toZ32 as nc, emptyIdentityLedger as ni, normalizePubkyKey as nn, handoffAttemptFailed as no, mergeBeacon as nr, encodeInviteCode as ns, parseAtprotoDidDocument as nt, COMMUNITY_LIMITS as o, sameButtonText as oa, receivedIdentityStatus as oi, homeserverWebEndpoint as on, handoffContext as oo, rankHubs as or, ENROLL_CAPABILITY as os, DID_DOCUMENT_MAX_BYTES as ot, decodeGroupEntryLink as p, GROUP_MEMBER_CAP as pa, automaticTransport as pi, encodeDnsQuery as pn, handoffOfferFrame as po, transferEnded as pr, newDeviceSetSecret as ps, didJwsAlg as pt, ATPROTO_FULL_SCOPE as q, HANDOFF_HELLO as qa, reactionIsNewer as qi, meshHubs as qn, readEnrollGrant as qo, DHT_TEXT_CAPABILITY as qr, identityFromSeedB64 as qs, SPARK_NETWORKS as qt, groupTypingEpoch as r, STATUS_CARD_LIMITS as ra, utf8Decode as rc, identityRevocationValue as ri, pubkyProofCapability as rn, handoffAttemptSucceeded as ro, mergeLobby as rr, inviteLink as rs, shortAtprotoDid as rt, CommunitySession as s, statusCardText as sa, revokesIdentity as si, pubkyHomeserverOf as sn, handoffContextProof as so, readBeacon as sr, HANDOFF_CAPABILITY as ss, DID_FILE_MAX_BYTES as st, GROUP_TYPING_FRAME as t, ITEM_STATES as ta, toBase64Url as tc, boundVerifiedIdentity as ti, newPubkyProofFolder as tn, handoffAttemptAllowed as to, lobbyRecords as tr, decodeInviteCode as ts, normalizeAtprotoHandle as tt, decodeCommunityLink as u, heardTime as ua, emptyProofLedger as ui, dnsProofName as un, handoffEphemeral as uo, shouldBeHub as ur, deviceKeyZ32 as us, checkDidJws as ut, mergeKnocks as v, edgeParams as va, peekDhtMailbox as vi, recordsFromWellKnown as vn, handoffVersions as vo, sanitizeTypingStatus as vr, TURN_SETTLE_MS as vs, didWebFileUrl as vt, parseGroupWakeFrame as w, formatLocalTarget as wa, sanitizeFileName as wi, engineError as wn, readHandoffHello as wo, pinIsNewer as wr, nextTurnPosition as ws, signingKeys as wt, GroupWakeLimiter as x, RELAY_POLL_INTERVALS as xa, parseImageMeta as xi, wellKnownUrl as xn, readHandoffCancel as xo, PIN_LIMITS as xr, TURN_LAST_SEQUENCE as xs, parseDid as xt, readKnocks as y, DirectPathWatch as ya, DHT_FILE_CAPABILITY as yi, recordsName as yn, newHandoffId as yo, typingActivity as yr, classifyTurnRead as ys, didWebHost as yt, parseSshSignature as z, emptyLinkRecords as za, readForwarded as zi, GROUP_SIGNAL_FRAME as zn, ENROLL_PROOF as zo, HOLD_CAPABILITY as zr, PacketTooLargeError as zs, TEST_USDT_FAUCET_AMOUNT as zt };
|
|
26575
26943
|
|
|
26576
|
-
//# sourceMappingURL=src-
|
|
26944
|
+
//# sourceMappingURL=src-K3NijMq-.js.map
|