fedipod 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/groups.md +1 -0
- package/lib/client/c2s.mjs +19 -5
- package/lib/client/masto/index.mjs +9 -1
- package/lib/client/masto/render.mjs +5 -3
- package/lib/client/masto/statuses.mjs +7 -4
- package/lib/client/oidc-auth.mjs +10 -3
- package/lib/core/as2.mjs +1 -1
- package/lib/core/deliver.mjs +24 -1
- package/lib/core/intake/activities.mjs +54 -0
- package/lib/core/intake/index.mjs +2 -0
- package/lib/core/intake/notes.mjs +7 -0
- package/lib/core/publisher/collections.mjs +7 -4
- package/lib/core/publisher/index.mjs +34 -26
- package/lib/core/publisher/notes.mjs +76 -11
- package/lib/core/publisher/questions.mjs +10 -7
- package/lib/core/store.mjs +9 -0
- package/lib/core/wire.mjs +57 -10
- package/lib/device/admin/static.mjs +2 -2
- package/lib/device/admin/surface.mjs +11 -2
- package/lib/gateway/front-core.mjs +33 -5
- package/lib/gateway/httpsig.mjs +7 -3
- package/package.json +1 -1
- package/run-agent.mjs +2 -0
- package/scripts/stage-site.mjs +1 -0
- package/web/app/agent.mjs +2 -0
- package/web/app/dist/sw.js +290 -72
- package/web/app/dist/sw.js.map +3 -3
- package/web/app/site/_redirects +1 -0
- package/web/app/site/sw.js +290 -72
package/web/app/dist/sw.js
CHANGED
|
@@ -9589,6 +9589,7 @@ __export(wire_exports, {
|
|
|
9589
9589
|
AS_CTX: () => AS_CTX,
|
|
9590
9590
|
DEFAULT_ROOT: () => DEFAULT_ROOT,
|
|
9591
9591
|
FOLLOWERS_PAGE_SIZE: () => FOLLOWERS_PAGE_SIZE,
|
|
9592
|
+
HASHTAG_RE: () => HASHTAG_RE,
|
|
9592
9593
|
MENTION_RE: () => MENTION_RE,
|
|
9593
9594
|
OUTBOX_PAGE_SIZE: () => OUTBOX_PAGE_SIZE,
|
|
9594
9595
|
PUBLIC: () => PUBLIC,
|
|
@@ -9615,6 +9616,7 @@ __export(wire_exports, {
|
|
|
9615
9616
|
followersPageId: () => followersPageId,
|
|
9616
9617
|
followersPaging: () => followersPaging,
|
|
9617
9618
|
followsNeedApproval: () => followsNeedApproval,
|
|
9619
|
+
hashtagsIn: () => hashtagsIn,
|
|
9618
9620
|
hostMeta: () => hostMeta,
|
|
9619
9621
|
jrd: () => jrd,
|
|
9620
9622
|
likeActivity: () => likeActivity,
|
|
@@ -9627,11 +9629,13 @@ __export(wire_exports, {
|
|
|
9627
9629
|
orderedCollection: () => orderedCollection,
|
|
9628
9630
|
outboxHead: () => outboxHead,
|
|
9629
9631
|
outboxItemId: () => outboxItemId,
|
|
9632
|
+
outboxLocalItem: () => outboxLocalItem,
|
|
9630
9633
|
outboxPage: () => outboxPage,
|
|
9631
9634
|
outboxPageCount: () => outboxPageCount,
|
|
9632
9635
|
outboxPageId: () => outboxPageId,
|
|
9633
9636
|
outboxPages: () => outboxPages,
|
|
9634
9637
|
outboxPaging: () => outboxPaging,
|
|
9638
|
+
outboxWireItem: () => outboxWireItem,
|
|
9635
9639
|
pageItems: () => pageItems,
|
|
9636
9640
|
profilePageHtml: () => profilePageHtml,
|
|
9637
9641
|
publicHandle: () => publicHandle,
|
|
@@ -9639,6 +9643,7 @@ __export(wire_exports, {
|
|
|
9639
9643
|
rejectActivity: () => rejectActivity,
|
|
9640
9644
|
repliesId: () => repliesId,
|
|
9641
9645
|
sanitizeHtml: () => sanitizeHtml,
|
|
9646
|
+
tagHref: () => tagHref,
|
|
9642
9647
|
titledContent: () => titledContent,
|
|
9643
9648
|
tombstoneDoc: () => tombstoneDoc,
|
|
9644
9649
|
undoActivity: () => undoActivity,
|
|
@@ -9659,11 +9664,14 @@ function hostMeta(base) {
|
|
|
9659
9664
|
</XRD>
|
|
9660
9665
|
`;
|
|
9661
9666
|
}
|
|
9662
|
-
function jrd({ handle: handle7, host, actor, aliases = [] }) {
|
|
9667
|
+
function jrd({ handle: handle7, host, actor, aliases = [], page: page2 = null }) {
|
|
9663
9668
|
return {
|
|
9664
9669
|
subject: `acct:${handle7}@${host}`,
|
|
9665
9670
|
...aliases.length ? { aliases } : {},
|
|
9666
|
-
links: [
|
|
9671
|
+
links: [
|
|
9672
|
+
{ rel: "self", type: "application/activity+json", href: actor },
|
|
9673
|
+
...page2 ? [{ rel: "http://webfinger.net/rel/profile-page", type: "text/html", href: page2 }] : []
|
|
9674
|
+
]
|
|
9667
9675
|
};
|
|
9668
9676
|
}
|
|
9669
9677
|
function followsNeedApproval(config = {}) {
|
|
@@ -9926,7 +9934,7 @@ function outboxPage(outboxId, n, items) {
|
|
|
9926
9934
|
id: outboxPageId(outboxId, n),
|
|
9927
9935
|
type: "OrderedCollectionPage",
|
|
9928
9936
|
partOf: outboxId,
|
|
9929
|
-
orderedItems: items,
|
|
9937
|
+
orderedItems: items.map(outboxWireItem),
|
|
9930
9938
|
...n > 1 ? { next: outboxPageId(outboxId, n - 1) } : {}
|
|
9931
9939
|
};
|
|
9932
9940
|
}
|
|
@@ -10046,8 +10054,27 @@ function attachmentsOf(note) {
|
|
|
10046
10054
|
function mentionsIn(text) {
|
|
10047
10055
|
return [...new Set(String(text).match(MENTION_RE) || [])].map((m) => m.slice(1));
|
|
10048
10056
|
}
|
|
10049
|
-
function
|
|
10057
|
+
function hashtagsIn(text) {
|
|
10058
|
+
const seen = /* @__PURE__ */ new Map();
|
|
10059
|
+
for (const m of String(text).matchAll(HASHTAG_RE)) {
|
|
10060
|
+
const key = m[2].toLowerCase();
|
|
10061
|
+
if (!seen.has(key)) seen.set(key, m[2]);
|
|
10062
|
+
}
|
|
10063
|
+
return [...seen.values()];
|
|
10064
|
+
}
|
|
10065
|
+
function tagHref(urls, name) {
|
|
10066
|
+
return urls.profileHtml + "?tag=" + encodeURIComponent(String(name).toLowerCase());
|
|
10067
|
+
}
|
|
10068
|
+
function contentHtml(text, mentions = [], hashtags = []) {
|
|
10050
10069
|
let html = String(text).replace(/[&<>]/g, (c) => HTML_ESCAPES[c]);
|
|
10070
|
+
if (hashtags.length) {
|
|
10071
|
+
const byName = new Map(hashtags.filter((h) => h?.name && h?.href).map((h) => [h.name.toLowerCase(), h.href]));
|
|
10072
|
+
html = html.replace(HASHTAG_RE, (all, lead, name) => {
|
|
10073
|
+
const href = byName.get(name.toLowerCase());
|
|
10074
|
+
if (!href) return all;
|
|
10075
|
+
return `${lead}<a href="${String(href).replace(/[&<>"]/g, (c) => HTML_ESCAPES[c] || """)}" class="mention hashtag" rel="tag">#<span>${name}</span></a>`;
|
|
10076
|
+
});
|
|
10077
|
+
}
|
|
10051
10078
|
for (const m of mentions) {
|
|
10052
10079
|
if (!m?.handle || !m?.actor) continue;
|
|
10053
10080
|
const href = String(m.page || m.actor).replace(/[&<>"]/g, (c) => HTML_ESCAPES[c] || """);
|
|
@@ -10075,28 +10102,35 @@ function noteDoc({
|
|
|
10075
10102
|
mentions = [],
|
|
10076
10103
|
visibility = "public",
|
|
10077
10104
|
summary = null,
|
|
10105
|
+
sensitive = false,
|
|
10078
10106
|
updated = null,
|
|
10079
|
-
container = null
|
|
10107
|
+
container = null,
|
|
10108
|
+
also = []
|
|
10080
10109
|
}) {
|
|
10081
10110
|
const id = (container || urls.notes) + slug;
|
|
10082
10111
|
const who = mentions.map((m) => m.actor);
|
|
10083
|
-
const
|
|
10112
|
+
const extra = visibility === "direct" ? [] : also.filter((a) => a && !who.includes(a));
|
|
10113
|
+
const addressed = addressing(urls, visibility, [...who, ...extra]);
|
|
10114
|
+
const hashtags = hashtagsIn(content).map((name) => ({ name, href: tagHref(urls, name) }));
|
|
10084
10115
|
const note = {
|
|
10085
10116
|
"@context": AS_CTX,
|
|
10086
10117
|
id,
|
|
10087
10118
|
type: "Note",
|
|
10088
10119
|
attributedTo: urls.actor,
|
|
10089
|
-
content: contentHtml(content, mentions),
|
|
10120
|
+
content: contentHtml(content, mentions, hashtags),
|
|
10090
10121
|
published,
|
|
10091
10122
|
to: addressed.to,
|
|
10092
10123
|
cc: addressed.cc,
|
|
10093
10124
|
replies: repliesId(id)
|
|
10094
10125
|
};
|
|
10095
10126
|
if (summary) note.summary = summary;
|
|
10127
|
+
if (summary || sensitive) note.sensitive = true;
|
|
10096
10128
|
if (updated) note.updated = updated;
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10129
|
+
const tag = [
|
|
10130
|
+
...mentions.map((m) => ({ type: "Mention", href: m.actor, name: "@" + m.handle })),
|
|
10131
|
+
...hashtags.map((h) => ({ type: "Hashtag", href: h.href, name: "#" + h.name }))
|
|
10132
|
+
];
|
|
10133
|
+
if (tag.length) note.tag = tag;
|
|
10100
10134
|
if (inReplyTo) note.inReplyTo = inReplyTo;
|
|
10101
10135
|
if (attachments?.length) {
|
|
10102
10136
|
note.attachment = attachments.map((a) => ({
|
|
@@ -10274,7 +10308,7 @@ function addRemoveActivity({ urls, type, object, target, serial }) {
|
|
|
10274
10308
|
target
|
|
10275
10309
|
};
|
|
10276
10310
|
}
|
|
10277
|
-
var import_sanitize_html, AS_CTX, SEC_CTX, PUBLIC, DEFAULT_ROOT, assertionKeyId, OUTBOX_PAGE_SIZE, outboxPageId, outboxPageCount, outboxItemId, FOLLOWERS_PAGE_SIZE, followersPageId, followersPageCount, ALLOWED_TAGS, ALLOWED_ATTRS, MAX_ATTACHMENTS, MAX_ATTACHMENT_URL, attachmentUrl, HTML_ESCAPES, MENTION_RE;
|
|
10311
|
+
var import_sanitize_html, AS_CTX, SEC_CTX, PUBLIC, DEFAULT_ROOT, assertionKeyId, OUTBOX_PAGE_SIZE, outboxPageId, outboxPageCount, outboxItemId, outboxWireItem, outboxLocalItem, FOLLOWERS_PAGE_SIZE, followersPageId, followersPageCount, ALLOWED_TAGS, ALLOWED_ATTRS, MAX_ATTACHMENTS, MAX_ATTACHMENT_URL, attachmentUrl, HTML_ESCAPES, MENTION_RE, HASHTAG_RE;
|
|
10278
10312
|
var init_wire = __esm({
|
|
10279
10313
|
"lib/core/wire.mjs"() {
|
|
10280
10314
|
init_urls();
|
|
@@ -10289,6 +10323,8 @@ var init_wire = __esm({
|
|
|
10289
10323
|
outboxPageId = (outboxId, n) => `${outboxId}-${n}`;
|
|
10290
10324
|
outboxPageCount = (total) => Math.max(1, Math.ceil(total / OUTBOX_PAGE_SIZE));
|
|
10291
10325
|
outboxItemId = (i) => typeof i === "string" ? i : i?.id || null;
|
|
10326
|
+
outboxWireItem = (i) => typeof i === "string" ? createActivityId(i) : i;
|
|
10327
|
+
outboxLocalItem = (i) => typeof i === "string" && i.endsWith("-create") ? i.slice(0, -"-create".length) : i;
|
|
10292
10328
|
FOLLOWERS_PAGE_SIZE = 20;
|
|
10293
10329
|
followersPageId = (id, n) => `${id}-${n}`;
|
|
10294
10330
|
followersPageCount = (total) => Math.max(1, Math.ceil(total / FOLLOWERS_PAGE_SIZE));
|
|
@@ -10329,6 +10365,7 @@ var init_wire = __esm({
|
|
|
10329
10365
|
};
|
|
10330
10366
|
HTML_ESCAPES = { "&": "&", "<": "<", ">": ">" };
|
|
10331
10367
|
MENTION_RE = /@([A-Za-z0-9_.-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,}(?::\d+)?)/g;
|
|
10368
|
+
HASHTAG_RE = /(^|[^\/\w)])#([\p{L}\p{N}_]*[\p{L}_][\p{L}\p{N}_]*)/gu;
|
|
10332
10369
|
}
|
|
10333
10370
|
});
|
|
10334
10371
|
|
|
@@ -32650,11 +32687,12 @@ async function verifyHttpSignature(request, { documentLoader, keyCache, timeWind
|
|
|
32650
32687
|
} catch {
|
|
32651
32688
|
result = null;
|
|
32652
32689
|
}
|
|
32690
|
+
const method = request.headers.has("signature-input") ? "rfc9421" : "draft-cavage";
|
|
32653
32691
|
if (result?.verified) {
|
|
32654
32692
|
const key = result.key;
|
|
32655
32693
|
return {
|
|
32656
32694
|
verified: true,
|
|
32657
|
-
method
|
|
32695
|
+
method,
|
|
32658
32696
|
keyId: key.id?.href ?? null,
|
|
32659
32697
|
actor: key.ownerId?.href ?? null,
|
|
32660
32698
|
reason: null,
|
|
@@ -32675,7 +32713,7 @@ async function verifyHttpSignature(request, { documentLoader, keyCache, timeWind
|
|
|
32675
32713
|
if (kind === "keyFetchError") {
|
|
32676
32714
|
return {
|
|
32677
32715
|
verified: false,
|
|
32678
|
-
method
|
|
32716
|
+
method,
|
|
32679
32717
|
keyId: null,
|
|
32680
32718
|
actor: null,
|
|
32681
32719
|
reason: "key-unfetchable",
|
|
@@ -32684,7 +32722,7 @@ async function verifyHttpSignature(request, { documentLoader, keyCache, timeWind
|
|
|
32684
32722
|
}
|
|
32685
32723
|
return {
|
|
32686
32724
|
verified: false,
|
|
32687
|
-
method
|
|
32725
|
+
method,
|
|
32688
32726
|
keyId: null,
|
|
32689
32727
|
actor: null,
|
|
32690
32728
|
reason: "bad-signature",
|
|
@@ -34481,6 +34519,14 @@ var PodStore = class {
|
|
|
34481
34519
|
// — not from the pod, not from the RDF. Unverified entries are dropped first
|
|
34482
34520
|
// now, so a flood can only ever evict itself, and a stranger's genuine
|
|
34483
34521
|
// favourite still shows up, which is what recording them at all is for.
|
|
34522
|
+
// What an Undo unsays: the favourite or boost it names, matched by a
|
|
34523
|
+
// predicate on what was recorded. Returns how many went.
|
|
34524
|
+
removeNotifications(pred) {
|
|
34525
|
+
const all = this.getNotifications();
|
|
34526
|
+
const kept = all.filter((n) => !pred(n));
|
|
34527
|
+
if (kept.length !== all.length) this.write("notifications.json", kept);
|
|
34528
|
+
return all.length - kept.length;
|
|
34529
|
+
}
|
|
34484
34530
|
addNotification(n) {
|
|
34485
34531
|
const all = this.getNotifications();
|
|
34486
34532
|
const id = node_crypto_default.createHash("sha256").update(JSON.stringify(n)).digest("hex").slice(0, 16);
|
|
@@ -45433,8 +45479,9 @@ async function publishOutbox(publisher, outbox, { acls = false, force = false }
|
|
|
45433
45479
|
);
|
|
45434
45480
|
return wrote;
|
|
45435
45481
|
}
|
|
45436
|
-
function readPublishedOutbox(publisher) {
|
|
45437
|
-
|
|
45482
|
+
async function readPublishedOutbox(publisher) {
|
|
45483
|
+
const items = await readPublished(publisher.remote, publisher.urls);
|
|
45484
|
+
return Array.isArray(items) ? items.map(outboxLocalItem) : items;
|
|
45438
45485
|
}
|
|
45439
45486
|
async function publishFollowers(publisher, actors, { acls = false, force = false } = {}) {
|
|
45440
45487
|
const { urls } = publisher;
|
|
@@ -55941,6 +55988,35 @@ async function mentionsFor(publisher, content, inReplyTo) {
|
|
|
55941
55988
|
}
|
|
55942
55989
|
return mentions;
|
|
55943
55990
|
}
|
|
55991
|
+
async function replyTarget(publisher, inReplyTo, mentions = [], visibility = "public") {
|
|
55992
|
+
if (!inReplyTo || visibility === "direct" || !publisher.resolveActor) return null;
|
|
55993
|
+
const parent = publisher.store.getStatuses().find((s) => s.noteId === inReplyTo);
|
|
55994
|
+
let actor = parent?.actor || null;
|
|
55995
|
+
if (!parent) {
|
|
55996
|
+
const doc2 = await publisher.resolveActor(inReplyTo).catch(() => null);
|
|
55997
|
+
const by = doc2?.attributedTo;
|
|
55998
|
+
actor = typeof by === "string" ? by : (Array.isArray(by) ? by[0] : by)?.id || null;
|
|
55999
|
+
if (typeof actor === "object") actor = actor?.id || null;
|
|
56000
|
+
}
|
|
56001
|
+
if (!actor || actor === publisher.urls.actor) return null;
|
|
56002
|
+
if (mentions.some((m) => m.actor === actor)) return null;
|
|
56003
|
+
if (publisher.store.isBlocked?.(actor)) return null;
|
|
56004
|
+
const doc = await publisher.resolveActor(actor).catch(() => null);
|
|
56005
|
+
const inbox = doc?.endpoints?.sharedInbox || doc?.inbox || null;
|
|
56006
|
+
if (!inbox) publisher.log(`reply: no inbox found for ${actor} \u2014 they get it only through followers`);
|
|
56007
|
+
return { actor, inbox };
|
|
56008
|
+
}
|
|
56009
|
+
async function inboxesFor(publisher, actorIds = []) {
|
|
56010
|
+
const out = [];
|
|
56011
|
+
for (const id of [...new Set(actorIds.filter((a) => typeof a === "string" && a && a !== publisher.urls.actor))]) {
|
|
56012
|
+
if (!publisher.resolveActor) break;
|
|
56013
|
+
const doc = await publisher.resolveActor(id).catch(() => null);
|
|
56014
|
+
const inbox = doc?.endpoints?.sharedInbox || doc?.inbox;
|
|
56015
|
+
if (inbox) out.push(inbox);
|
|
56016
|
+
else publisher.log(`addressed actor ${id} has no inbox the agent can find \u2014 not delivered to`);
|
|
56017
|
+
}
|
|
56018
|
+
return out;
|
|
56019
|
+
}
|
|
55944
56020
|
function assertDirectAddressed(content, mentions) {
|
|
55945
56021
|
const wanted = [...new Set(mentionsIn(content))];
|
|
55946
56022
|
const missing = wanted.filter((h) => !mentions.some((m) => m.handle === h));
|
|
@@ -55966,7 +56042,7 @@ function rowContent(obj) {
|
|
|
55966
56042
|
const esc = (v) => String(v).replace(/[&<>"]/gu, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
55967
56043
|
return `<p><a href="${esc(obj?.id || "")}">${esc(obj?.type || "object")}</a></p>`;
|
|
55968
56044
|
}
|
|
55969
|
-
async function publishNote(publisher, content, { inReplyTo, attachments, visibility = "public", spoilerText = null, slug: wanted = null } = {}) {
|
|
56045
|
+
async function publishNote(publisher, content, { inReplyTo, attachments, visibility = "public", spoilerText = null, sensitive = false, slug: wanted = null, also = [], deliverTo = [] } = {}) {
|
|
55970
56046
|
const { urls } = publisher;
|
|
55971
56047
|
const priv = visibility === "private" || visibility === "direct";
|
|
55972
56048
|
if (priv) {
|
|
@@ -55977,6 +56053,7 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
55977
56053
|
const slug = await slugFor(publisher, priv ? urls.privateNotes : urls.notes, wanted, published);
|
|
55978
56054
|
const mentions = await publisher._mentionsFor(content, inReplyTo);
|
|
55979
56055
|
if (visibility === "direct") assertDirectAddressed(content, mentions);
|
|
56056
|
+
const reply = await replyTarget(publisher, inReplyTo, mentions, visibility);
|
|
55980
56057
|
const note = noteDoc({
|
|
55981
56058
|
urls,
|
|
55982
56059
|
slug,
|
|
@@ -55987,8 +56064,11 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
55987
56064
|
mentions,
|
|
55988
56065
|
visibility,
|
|
55989
56066
|
summary: spoilerText,
|
|
55990
|
-
|
|
56067
|
+
sensitive,
|
|
56068
|
+
container: priv ? urls.privateNotes : urls.notes,
|
|
56069
|
+
also: [...also, ...reply ? [reply.actor] : []]
|
|
55991
56070
|
});
|
|
56071
|
+
const addressedInboxes = await inboxesFor(publisher, [...also, ...deliverTo]);
|
|
55992
56072
|
await write4(publisher.remote, note.id, note);
|
|
55993
56073
|
await writeEmptyReplies(
|
|
55994
56074
|
publisher.remote,
|
|
@@ -56007,8 +56087,9 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
56007
56087
|
text: content,
|
|
56008
56088
|
visibility,
|
|
56009
56089
|
...spoilerText ? { spoiler: spoilerText } : {},
|
|
56090
|
+
...note.sensitive ? { sensitive: true } : {},
|
|
56010
56091
|
...attachments?.length ? { attachments } : {},
|
|
56011
|
-
...note
|
|
56092
|
+
...rowTags(note)
|
|
56012
56093
|
});
|
|
56013
56094
|
if (await publisher.store.commit?.() === false) publisher.log(`post published but its timeline row was refused: ${note.id}`);
|
|
56014
56095
|
const create = createActivity(note, urls);
|
|
@@ -56016,7 +56097,9 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
56016
56097
|
const contacts = publisher.store.getContacts();
|
|
56017
56098
|
const inboxes = [...new Set([
|
|
56018
56099
|
...visibility === "direct" ? [] : contacts.followers.map((f) => f.sharedInbox || f.inbox),
|
|
56019
|
-
...mentions.map((m) => m.inbox)
|
|
56100
|
+
...mentions.map((m) => m.inbox),
|
|
56101
|
+
reply?.inbox,
|
|
56102
|
+
...addressedInboxes
|
|
56020
56103
|
].filter(Boolean))];
|
|
56021
56104
|
await publisher.deliverer.deliverToAll(inboxes, create);
|
|
56022
56105
|
publisher.log(`note published: ${note.id} \u2192 ${inboxes.length} inbox(es)`);
|
|
@@ -56035,7 +56118,7 @@ async function publishNote(publisher, content, { inReplyTo, attachments, visibil
|
|
|
56035
56118
|
}
|
|
56036
56119
|
return note;
|
|
56037
56120
|
}
|
|
56038
|
-
async function publishObject(publisher, object, { visibility = "public", slug: wanted = null } = {}) {
|
|
56121
|
+
async function publishObject(publisher, object, { visibility = "public", slug: wanted = null, also = [], deliverTo = [] } = {}) {
|
|
56039
56122
|
const { urls } = publisher;
|
|
56040
56123
|
const priv = visibility === "private" || visibility === "direct";
|
|
56041
56124
|
if (priv) {
|
|
@@ -56047,11 +56130,13 @@ async function publishObject(publisher, object, { visibility = "public", slug: w
|
|
|
56047
56130
|
const pod = publisher.config?.remotePod || urls.base;
|
|
56048
56131
|
const ownId = typeof object.id === "string" && /^https?:\/\//u.test(object.id) && object.id.startsWith(pod) ? object.id : null;
|
|
56049
56132
|
const name = await slugFor(publisher, container, wanted, published);
|
|
56050
|
-
const addressed = addressing(urls, visibility);
|
|
56133
|
+
const addressed = addressing(urls, visibility, visibility === "direct" ? also : []);
|
|
56134
|
+
if (visibility !== "direct") addressed.cc = [.../* @__PURE__ */ new Set([...addressed.cc, ...also])];
|
|
56051
56135
|
let doc = null;
|
|
56052
56136
|
const id = ownId || container + name;
|
|
56053
56137
|
if (!ownId) {
|
|
56054
|
-
|
|
56138
|
+
const { bto, bcc, ...sent } = object;
|
|
56139
|
+
doc = { ...sent, id, attributedTo: urls.actor, published: object.published || published, to: addressed.to, cc: addressed.cc };
|
|
56055
56140
|
if (!doc["@context"]) doc["@context"] = AS_CTX;
|
|
56056
56141
|
if (typeof doc.content === "string") doc.content = sanitizeHtml(doc.content);
|
|
56057
56142
|
await write4(publisher.remote, id, doc);
|
|
@@ -56081,12 +56166,21 @@ async function publishObject(publisher, object, { visibility = "public", slug: w
|
|
|
56081
56166
|
};
|
|
56082
56167
|
await writeCreate(publisher.remote, create.id, create);
|
|
56083
56168
|
const contacts = publisher.store.getContacts();
|
|
56084
|
-
const inboxes =
|
|
56169
|
+
const inboxes = [...new Set([
|
|
56170
|
+
...visibility === "direct" ? [] : contacts.followers.map((f) => f.sharedInbox || f.inbox),
|
|
56171
|
+
...await inboxesFor(publisher, [...also, ...deliverTo])
|
|
56172
|
+
].filter(Boolean))];
|
|
56085
56173
|
await publisher.deliverer.deliverToAll(inboxes, create);
|
|
56086
56174
|
publisher.log(`${row.type || "object"} published: ${id} \u2192 ${inboxes.length} inbox(es)`);
|
|
56087
56175
|
return { id, createId, copied: !ownId };
|
|
56088
56176
|
}
|
|
56089
|
-
|
|
56177
|
+
function rowTags(note) {
|
|
56178
|
+
const tags = [].concat(note.tag || []);
|
|
56179
|
+
const mentions = tags.filter((t) => t.type === "Mention").map((t) => ({ href: t.href, name: t.name }));
|
|
56180
|
+
const hashtags = tags.filter((t) => t.type === "Hashtag").map((t) => ({ name: String(t.name).replace(/^#/, ""), url: t.href }));
|
|
56181
|
+
return { ...mentions.length ? { mentions } : {}, ...hashtags.length ? { tags: hashtags } : {} };
|
|
56182
|
+
}
|
|
56183
|
+
async function updateNote(publisher, s, { content, spoilerText = null, sensitive = null, attachments = null } = {}) {
|
|
56090
56184
|
const { urls } = publisher;
|
|
56091
56185
|
const updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
56092
56186
|
const inText = new Set(mentionsIn(content));
|
|
@@ -56103,6 +56197,7 @@ async function updateNote(publisher, s, { content, spoilerText = null, attachmen
|
|
|
56103
56197
|
const atts = attachments ?? s.attachments ?? [];
|
|
56104
56198
|
const container = String(s.noteId).startsWith(urls.privateNotes) ? urls.privateNotes : urls.notes;
|
|
56105
56199
|
const slug = s.slug || String(s.noteId).slice(container.length);
|
|
56200
|
+
const reply = await replyTarget(publisher, s.inReplyTo, mentions, s.visibility || "public");
|
|
56106
56201
|
const note = noteDoc({
|
|
56107
56202
|
urls,
|
|
56108
56203
|
slug,
|
|
@@ -56113,8 +56208,10 @@ async function updateNote(publisher, s, { content, spoilerText = null, attachmen
|
|
|
56113
56208
|
mentions,
|
|
56114
56209
|
visibility: s.visibility || "public",
|
|
56115
56210
|
summary: spoilerText,
|
|
56211
|
+
sensitive: sensitive ?? !!s.sensitive,
|
|
56116
56212
|
updated,
|
|
56117
|
-
container
|
|
56213
|
+
container,
|
|
56214
|
+
also: reply ? [reply.actor] : []
|
|
56118
56215
|
});
|
|
56119
56216
|
await write4(publisher.remote, note.id, note);
|
|
56120
56217
|
await writeCreate(publisher.remote, createActivityId(note.id), createActivity(note, urls));
|
|
@@ -56123,14 +56220,17 @@ async function updateNote(publisher, s, { content, spoilerText = null, attachmen
|
|
|
56123
56220
|
text: content,
|
|
56124
56221
|
editedAt: updated,
|
|
56125
56222
|
spoiler: spoilerText || void 0,
|
|
56223
|
+
sensitive: note.sensitive || void 0,
|
|
56126
56224
|
attachments: atts.length ? atts : void 0,
|
|
56127
|
-
mentions: note
|
|
56225
|
+
mentions: rowTags(note).mentions,
|
|
56226
|
+
tags: rowTags(note).tags
|
|
56128
56227
|
});
|
|
56129
56228
|
const update = updateActivity(note, urls);
|
|
56130
56229
|
const contacts = publisher.store.getContacts();
|
|
56131
56230
|
const inboxes = [...new Set([
|
|
56132
56231
|
...s.visibility === "direct" ? [] : contacts.followers.map((f) => f.sharedInbox || f.inbox),
|
|
56133
|
-
...mentions.map((m) => m.inbox)
|
|
56232
|
+
...mentions.map((m) => m.inbox),
|
|
56233
|
+
reply?.inbox
|
|
56134
56234
|
].filter(Boolean))];
|
|
56135
56235
|
await publisher.deliverer.deliverToAll(inboxes, update);
|
|
56136
56236
|
publisher.log(`note edited: ${note.id} \u2192 ${inboxes.length} inbox(es)`);
|
|
@@ -56356,7 +56456,8 @@ async function publishQuestion(publisher, content, {
|
|
|
56356
56456
|
expiresAt = null,
|
|
56357
56457
|
inReplyTo = void 0,
|
|
56358
56458
|
visibility = "public",
|
|
56359
|
-
spoilerText = null
|
|
56459
|
+
spoilerText = null,
|
|
56460
|
+
sensitive = false
|
|
56360
56461
|
} = {}) {
|
|
56361
56462
|
const { urls } = publisher;
|
|
56362
56463
|
const priv = visibility === "private" || visibility === "direct";
|
|
@@ -56383,6 +56484,7 @@ async function publishQuestion(publisher, content, {
|
|
|
56383
56484
|
const slug = published.slice(0, 10) + "-" + node_crypto_default.randomBytes(4).toString("hex");
|
|
56384
56485
|
const mentions = await publisher._mentionsFor(content, inReplyTo);
|
|
56385
56486
|
if (visibility === "direct") assertDirectAddressed(content, mentions);
|
|
56487
|
+
const reply = await replyTarget(publisher, inReplyTo, mentions, visibility);
|
|
56386
56488
|
const poll = {
|
|
56387
56489
|
multiple: !!multiple,
|
|
56388
56490
|
expiresAt: expiresAt || null,
|
|
@@ -56391,7 +56493,7 @@ async function publishQuestion(publisher, content, {
|
|
|
56391
56493
|
votersCount: 0,
|
|
56392
56494
|
// Resolved once, here: a tally rewrite must not cost a webfinger lookup
|
|
56393
56495
|
// per vote for people the poll named.
|
|
56394
|
-
mentionInboxes: [...new Set(mentions.map((m) => m.inbox).filter(Boolean))]
|
|
56496
|
+
mentionInboxes: [...new Set([...mentions.map((m) => m.inbox), reply?.inbox].filter(Boolean))]
|
|
56395
56497
|
};
|
|
56396
56498
|
const question = questionDoc({
|
|
56397
56499
|
urls,
|
|
@@ -56403,6 +56505,8 @@ async function publishQuestion(publisher, content, {
|
|
|
56403
56505
|
mentions,
|
|
56404
56506
|
visibility,
|
|
56405
56507
|
summary: spoilerText,
|
|
56508
|
+
sensitive,
|
|
56509
|
+
also: reply ? [reply.actor] : [],
|
|
56406
56510
|
container: priv ? urls.privateNotes : urls.notes,
|
|
56407
56511
|
options: poll.options,
|
|
56408
56512
|
multiple: poll.multiple,
|
|
@@ -56427,8 +56531,10 @@ async function publishQuestion(publisher, content, {
|
|
|
56427
56531
|
visibility,
|
|
56428
56532
|
poll,
|
|
56429
56533
|
inReplyTo,
|
|
56534
|
+
...reply ? { replyActor: reply.actor } : {},
|
|
56430
56535
|
...spoilerText ? { spoiler: spoilerText } : {},
|
|
56431
|
-
...question.
|
|
56536
|
+
...question.sensitive ? { sensitive: true } : {},
|
|
56537
|
+
...rowTags(question)
|
|
56432
56538
|
});
|
|
56433
56539
|
if (await publisher.store.commit?.() === false) publisher.log(`poll published but its timeline row was refused: ${question.id}`);
|
|
56434
56540
|
const create = publisher._pollActivity("Create", question, createActivityId(question.id));
|
|
@@ -56507,8 +56613,10 @@ async function republishPoll(publisher, questionId, { closing = null } = {}) {
|
|
|
56507
56613
|
inReplyTo: s.inReplyTo,
|
|
56508
56614
|
attachments: [],
|
|
56509
56615
|
mentions,
|
|
56616
|
+
also: s.replyActor ? [s.replyActor] : [],
|
|
56510
56617
|
visibility: s.visibility || "public",
|
|
56511
56618
|
summary: s.spoiler || null,
|
|
56619
|
+
sensitive: !!s.sensitive,
|
|
56512
56620
|
container,
|
|
56513
56621
|
options: poll.options,
|
|
56514
56622
|
multiple: !!poll.multiple,
|
|
@@ -56580,6 +56688,7 @@ var Publisher = class {
|
|
|
56580
56688
|
log: log2 = console.log,
|
|
56581
56689
|
probeFetch = null,
|
|
56582
56690
|
resolveMention = null,
|
|
56691
|
+
resolveActor = null,
|
|
56583
56692
|
clientOrigin = null
|
|
56584
56693
|
}) {
|
|
56585
56694
|
this.config = config;
|
|
@@ -56594,6 +56703,7 @@ var Publisher = class {
|
|
|
56594
56703
|
if (this.urls.toPod && this.remote?.setUrlMap) this.remote.setUrlMap(this.urls.toPod);
|
|
56595
56704
|
this.probeFetch = probeFetch || ((u, i) => this.remote.probe(u, i));
|
|
56596
56705
|
this.resolveMention = resolveMention;
|
|
56706
|
+
this.resolveActor = resolveActor;
|
|
56597
56707
|
this.pollTimers = /* @__PURE__ */ new Map();
|
|
56598
56708
|
this.log = log2;
|
|
56599
56709
|
}
|
|
@@ -56619,19 +56729,17 @@ var Publisher = class {
|
|
|
56619
56729
|
if (gw.frontActor) return String(gw.frontActor).replace(/ap\/actor\/?$/u, "ap/outbox");
|
|
56620
56730
|
return String(gw.url).replace(/ap\/inbox\/?$/u, "ap/outbox");
|
|
56621
56731
|
}
|
|
56622
|
-
|
|
56732
|
+
// Every actor document this identity publishes, from one place: the
|
|
56733
|
+
// profile publish and a Move both write it, and a Move that rebuilt it
|
|
56734
|
+
// by hand dropped the gateway inbox, the outbox door and the collections.
|
|
56735
|
+
actorDocFor({ priv = false, moderators = null, inbox = null, movedTo = this.config.movedTo || null } = {}) {
|
|
56623
56736
|
const { urls } = this;
|
|
56624
|
-
|
|
56625
|
-
const priv = await this.privateReady() === true;
|
|
56626
|
-
const moderators = (this.config.moderators || []).length ? urls.moderators : null;
|
|
56627
|
-
const gw = this.config.gateway;
|
|
56628
|
-
const gwActive = gw && gw.url && gw.mode && gw.mode !== "off";
|
|
56629
|
-
const actorDoc2 = actorDoc({
|
|
56737
|
+
return actorDoc({
|
|
56630
56738
|
urls,
|
|
56631
56739
|
handle: publicHandle(this.config),
|
|
56632
56740
|
name: this.config.name,
|
|
56633
56741
|
publicKeyPem: this.publicKeyPem,
|
|
56634
|
-
movedTo
|
|
56742
|
+
movedTo,
|
|
56635
56743
|
kind: this.config.kind,
|
|
56636
56744
|
approveJoins: followsNeedApproval(this.config),
|
|
56637
56745
|
assertionKey: this.assertionKey,
|
|
@@ -56645,7 +56753,7 @@ var Publisher = class {
|
|
|
56645
56753
|
pendingFollowers: priv ? urls.pendingFollowers : null,
|
|
56646
56754
|
pendingFollowing: priv ? urls.pendingFollowing : null,
|
|
56647
56755
|
blocked: priv ? urls.blocked : null,
|
|
56648
|
-
inbox
|
|
56756
|
+
inbox,
|
|
56649
56757
|
// The agent's own outbox endpoint, where it is reachable: a client
|
|
56650
56758
|
// following the actor must arrive somewhere that will take a write.
|
|
56651
56759
|
// Otherwise the Gateway's outbox door, when one is attached.
|
|
@@ -56655,6 +56763,15 @@ var Publisher = class {
|
|
|
56655
56763
|
oauthAuthorize: this.clientOrigin ? `${this.clientOrigin}oauth/authorize` : null,
|
|
56656
56764
|
oauthToken: this.clientOrigin ? `${this.clientOrigin}oauth/token` : null
|
|
56657
56765
|
});
|
|
56766
|
+
}
|
|
56767
|
+
async publishProfile({ force = false } = {}) {
|
|
56768
|
+
const { urls } = this;
|
|
56769
|
+
const host = new URL(urls.base).host;
|
|
56770
|
+
const priv = await this.privateReady() === true;
|
|
56771
|
+
const moderators = (this.config.moderators || []).length ? urls.moderators : null;
|
|
56772
|
+
const gw = this.config.gateway;
|
|
56773
|
+
const gwActive = gw && gw.url && gw.mode && gw.mode !== "off";
|
|
56774
|
+
const actorDoc2 = this.actorDocFor({ priv, moderators, inbox: gwActive ? gw.url : null });
|
|
56658
56775
|
const surface = node_crypto_default.createHash("sha256").update(JSON.stringify({
|
|
56659
56776
|
actor: actorDoc2,
|
|
56660
56777
|
handle: this.config.handle,
|
|
@@ -56670,7 +56787,7 @@ var Publisher = class {
|
|
|
56670
56787
|
await writeWebfinger(
|
|
56671
56788
|
this.remote,
|
|
56672
56789
|
urls,
|
|
56673
|
-
jrd({ handle: this.config.handle, host, actor: urls.actor })
|
|
56790
|
+
jrd({ handle: this.config.handle, host, actor: urls.actor, page: urls.profileHtml })
|
|
56674
56791
|
);
|
|
56675
56792
|
await writeHostMeta(this.remote, urls, hostMeta(urls.base));
|
|
56676
56793
|
const nodeinfoDocUrl = urls.home + "ap/nodeinfo-2.0";
|
|
@@ -56865,21 +56982,15 @@ var Publisher = class {
|
|
|
56865
56982
|
await this.deliverer.deliverToAll(inboxes, moveActivity(urls, target, Date.parse(at)));
|
|
56866
56983
|
this.config.movedTo = target;
|
|
56867
56984
|
this.store.setConfig({ ...this.store.getConfig(), movedTo: target, movedAt: at });
|
|
56868
|
-
|
|
56869
|
-
|
|
56870
|
-
|
|
56871
|
-
|
|
56872
|
-
|
|
56873
|
-
|
|
56874
|
-
|
|
56875
|
-
|
|
56876
|
-
|
|
56877
|
-
summary: this.config.summary || null,
|
|
56878
|
-
icon: this.config.icon || null,
|
|
56879
|
-
image: this.config.image || null,
|
|
56880
|
-
fields: this.config.fields || [],
|
|
56881
|
-
webId: this.remote.webId || null,
|
|
56882
|
-
aliases: this.config.aliases || []
|
|
56985
|
+
const gw = this.config.gateway;
|
|
56986
|
+
const gwActive = gw && gw.url && gw.mode && gw.mode !== "off";
|
|
56987
|
+
const priv = await this.privateReady() === true;
|
|
56988
|
+
const moderators = (this.config.moderators || []).length ? urls.moderators : null;
|
|
56989
|
+
await writeMoved(this.remote, urls, this.actorDocFor({
|
|
56990
|
+
priv,
|
|
56991
|
+
moderators,
|
|
56992
|
+
inbox: gwActive ? gw.url : null,
|
|
56993
|
+
movedTo: target
|
|
56883
56994
|
}));
|
|
56884
56995
|
await this.store.flush();
|
|
56885
56996
|
this.log(`moved to ${target}: Move sent to ${inboxes.length} inbox(es), actor now advertises movedTo`);
|
|
@@ -57583,6 +57694,8 @@ async function onFollow(intake, activity, actor, { trusted = false } = {}) {
|
|
|
57583
57694
|
intake.log(`Accept sent \u2192 ${doc.inbox}`);
|
|
57584
57695
|
}
|
|
57585
57696
|
async function onUndo(intake, activity, actor, { trusted = false } = {}) {
|
|
57697
|
+
const inner = activity.object && typeof activity.object === "object" ? activity.object : null;
|
|
57698
|
+
if (inner?.type === "Like" || inner?.type === "Announce") return onUndoReaction(intake, inner, actor, { trusted });
|
|
57586
57699
|
if (typeof activity.object === "object" && activity.object?.type && activity.object.type !== "Follow") return;
|
|
57587
57700
|
const named = typeof activity.object === "string" ? activity.object : activity.object?.id;
|
|
57588
57701
|
if (!named && !trusted) return;
|
|
@@ -57670,6 +57783,47 @@ async function onAnnounce(intake, activity, actor, objectId) {
|
|
|
57670
57783
|
}
|
|
57671
57784
|
return intake.ingestNote(objectId, actor, { via: actor });
|
|
57672
57785
|
}
|
|
57786
|
+
async function onUndoReaction(intake, inner, actor, { trusted = false } = {}) {
|
|
57787
|
+
const innerActor = typeof inner.actor === "string" ? inner.actor : inner.actor?.id;
|
|
57788
|
+
if (innerActor && innerActor !== actor) return;
|
|
57789
|
+
const wrapped = inner.object;
|
|
57790
|
+
const target = wrapped && typeof wrapped === "object" && (wrapped.type === "Create" || wrapped.type === "Update") ? wrapped.object : wrapped;
|
|
57791
|
+
const objectId = typeof target === "string" ? target : target?.id;
|
|
57792
|
+
if (!objectId) return;
|
|
57793
|
+
if (objectId.startsWith(intake.urls.notes)) {
|
|
57794
|
+
const type = inner.type === "Like" ? "favourite" : "reblog";
|
|
57795
|
+
const gone = intake.store.removeNotifications((n) => n.type === type && n.actor === actor && n.noteId === objectId);
|
|
57796
|
+
if (gone) intake.log(`${inner.type} withdrawn by ${actor} on ${objectId}`);
|
|
57797
|
+
return;
|
|
57798
|
+
}
|
|
57799
|
+
if (inner.type !== "Announce") return;
|
|
57800
|
+
const followed = trusted || intake.store.getContacts().following.some((f) => f.actor === actor && f.accepted);
|
|
57801
|
+
if (!followed) return;
|
|
57802
|
+
const s = intake.store.getStatuses().find((x) => x.noteId === objectId);
|
|
57803
|
+
if (!s || s.kind === "post" || s.via !== actor) return;
|
|
57804
|
+
await intake.forget(s);
|
|
57805
|
+
intake.log(`boost withdrawn by ${actor}: ${objectId} left the timeline`);
|
|
57806
|
+
}
|
|
57807
|
+
async function onBlock(intake, activity, actor, { trusted = false } = {}) {
|
|
57808
|
+
const objectId = typeof activity.object === "string" ? activity.object : activity.object?.id;
|
|
57809
|
+
if (objectId !== intake.urls.actor) return;
|
|
57810
|
+
if (!trusted) {
|
|
57811
|
+
intake.log(`Block from ${actor} arrived unverified \u2014 ignored`);
|
|
57812
|
+
return;
|
|
57813
|
+
}
|
|
57814
|
+
const contacts = intake.store.getContacts();
|
|
57815
|
+
const wasFollower = contacts.followers.some((f) => f.actor === actor);
|
|
57816
|
+
const wasFollowing = contacts.following.some((f) => f.actor === actor);
|
|
57817
|
+
if (wasFollower) dropFollower(contacts, actor, "blocked-us");
|
|
57818
|
+
contacts.following = contacts.following.filter((f) => f.actor !== actor);
|
|
57819
|
+
intake.store.setContacts(contacts);
|
|
57820
|
+
const reqs = intake.store.getRequests();
|
|
57821
|
+
const left = reqs.filter((r) => r.actor !== actor);
|
|
57822
|
+
if (left.length !== reqs.length) intake.store.setRequests(left);
|
|
57823
|
+
await intake.republish({ followers: wasFollower, following: wasFollowing, pending: true });
|
|
57824
|
+
const what = [wasFollower && "their follow of us", wasFollowing && "our follow of them"].filter(Boolean).join(" and ");
|
|
57825
|
+
intake.log(`blocked by ${actor}: ${what || "no relationship"} dropped`);
|
|
57826
|
+
}
|
|
57673
57827
|
async function onDelete(intake, activity, actor) {
|
|
57674
57828
|
const objectId = typeof activity.object === "string" ? activity.object : activity.object?.id;
|
|
57675
57829
|
if (!objectId) return "Delete without object id";
|
|
@@ -57875,6 +58029,7 @@ async function ingestNote(intake, objectId, actor, { via, inline = null } = {})
|
|
|
57875
58029
|
const followed = via || known2 || !known2 && await intake.isCoMember(author);
|
|
57876
58030
|
const kind = followed ? "timeline" : "mention";
|
|
57877
58031
|
const mentions = [].concat(note.tag || []).filter((t) => t?.type === "Mention" && t.href && t.name).slice(0, MAX_MENTIONS).map((t) => ({ href: httpOnly(String(t.href).slice(0, MAX_URL_CHARS)), name: String(t.name).slice(0, 256) })).filter((m) => m.href);
|
|
58032
|
+
const hashtags = [].concat(note.tag || []).filter((t) => t?.type === "Hashtag" && t.name).slice(0, MAX_MENTIONS).map((t) => ({ name: String(t.name).replace(/^#/, "").slice(0, 128), url: httpOnly(String(t.href || "").slice(0, MAX_URL_CHARS)) })).filter((t) => t.name);
|
|
57878
58033
|
const emojis = emojisOf(note);
|
|
57879
58034
|
const poll = pollOf(note);
|
|
57880
58035
|
const audience = [].concat(note.to || [], note.cc || []).map(String);
|
|
@@ -57891,6 +58046,8 @@ async function ingestNote(intake, objectId, actor, { via, inline = null } = {})
|
|
|
57891
58046
|
...nonPublic ? { nonPublic: true } : {},
|
|
57892
58047
|
// The author's content warning, shown as one: plain text only.
|
|
57893
58048
|
...note.summary ? { spoiler: String(note.summary).replace(/<[^>]*>/g, "") } : {},
|
|
58049
|
+
...note.sensitive === true ? { sensitive: true } : {},
|
|
58050
|
+
...hashtags.length ? { tags: hashtags } : {},
|
|
57894
58051
|
...poll ? { poll } : {},
|
|
57895
58052
|
...emojis.length ? { emojis } : {},
|
|
57896
58053
|
...mentions.length ? { mentions } : {},
|
|
@@ -63413,6 +63570,8 @@ var Intake = class {
|
|
|
63413
63570
|
case "Add":
|
|
63414
63571
|
case "Remove":
|
|
63415
63572
|
return this.onAddRemove(activity, actor);
|
|
63573
|
+
case "Block":
|
|
63574
|
+
return this.onBlock(activity, actor, { trusted });
|
|
63416
63575
|
default:
|
|
63417
63576
|
this.log(`ignored ${activity.type} from ${actor}`);
|
|
63418
63577
|
}
|
|
@@ -63506,6 +63665,9 @@ var Intake = class {
|
|
|
63506
63665
|
onUndo(...a) {
|
|
63507
63666
|
return onUndo(this, ...a);
|
|
63508
63667
|
}
|
|
63668
|
+
onBlock(...a) {
|
|
63669
|
+
return onBlock(this, ...a);
|
|
63670
|
+
}
|
|
63509
63671
|
onCreate(...a) {
|
|
63510
63672
|
return onCreate(this, ...a);
|
|
63511
63673
|
}
|
|
@@ -64135,7 +64297,7 @@ var C2S = class {
|
|
|
64135
64297
|
return this.send(res, 405, { error: "deliveries go to this actor's inbox on the pod, which the actor document names; this address is the owner reading their own" });
|
|
64136
64298
|
}
|
|
64137
64299
|
const reader = await this.auth(req, pathname);
|
|
64138
|
-
if (!reader.ok) return this.send(res, reader.status, { error: reader.error });
|
|
64300
|
+
if (!reader.ok) return this.send(res, reader.status, { error: reader.error }, reader.headers || {});
|
|
64139
64301
|
return this.sendInbox(res, url);
|
|
64140
64302
|
}
|
|
64141
64303
|
if (req.method === "GET" || req.method === "HEAD") {
|
|
@@ -64148,7 +64310,7 @@ var C2S = class {
|
|
|
64148
64310
|
return this.send(res, 405, { error: "POST the outbox; GET redirects to the pod" });
|
|
64149
64311
|
}
|
|
64150
64312
|
const who = await this.auth(req, pathname);
|
|
64151
|
-
if (!who.ok) return this.send(res, who.status, { error: who.error });
|
|
64313
|
+
if (!who.ok) return this.send(res, who.status, { error: who.error }, who.headers || {});
|
|
64152
64314
|
if (this.agent.viewer) {
|
|
64153
64315
|
const took = await this.agent.requestTakeover?.();
|
|
64154
64316
|
if (!took) return this.send(res, 503, { error: "another agent is active for this pod \u2014 takeover failed, try again" });
|
|
@@ -64202,6 +64364,13 @@ var C2S = class {
|
|
|
64202
64364
|
return reply(422, { error: e.message || String(e) });
|
|
64203
64365
|
}
|
|
64204
64366
|
}
|
|
64367
|
+
// The actors a client addressed by id, beyond the audience words: the ones
|
|
64368
|
+
// in to/cc are listed and delivered to; the ones in bto/bcc are delivered
|
|
64369
|
+
// to and never listed (§6).
|
|
64370
|
+
addressedActors(activity, object) {
|
|
64371
|
+
const pick = (...fields) => [...new Set(fields.flatMap((f) => arr(activity[f] ?? object?.[f]).map(idOf)))].filter((a) => typeof a === "string" && /^https?:\/\//u.test(a) && a !== PUBLIC && a !== this.urls.followers && a !== this.urls.actor);
|
|
64372
|
+
return { also: pick("to", "cc"), deliverTo: pick("bto", "bcc") };
|
|
64373
|
+
}
|
|
64205
64374
|
async _dispatch(activity, { slug, raw, reply }) {
|
|
64206
64375
|
const agent2 = this.agent;
|
|
64207
64376
|
const object = typeof activity.object === "object" && activity.object !== null ? activity.object : null;
|
|
@@ -64211,9 +64380,10 @@ var C2S = class {
|
|
|
64211
64380
|
const makes = object?.type || "Note";
|
|
64212
64381
|
if (!object) return reply(422, { error: "a Create carries the object it creates" });
|
|
64213
64382
|
const visibility = this.visibilityOf(activity, object);
|
|
64383
|
+
const { also, deliverTo } = this.addressedActors(activity, object);
|
|
64214
64384
|
if (makes !== "Note" && makes !== "Question") {
|
|
64215
64385
|
const asSent = raw && typeof raw === "object" && !Array.isArray(raw) ? ACTIVITY_TYPES.has(raw.type) ? raw.object && typeof raw.object === "object" ? raw.object : null : raw : null;
|
|
64216
|
-
const made = await agent2.publisher.publishObject(asSent || object, { visibility, slug });
|
|
64386
|
+
const made = await agent2.publisher.publishObject(asSent || object, { visibility, slug, also, deliverTo });
|
|
64217
64387
|
return reply(201, { id: made.createId, object: made.id }, { location: made.createId });
|
|
64218
64388
|
}
|
|
64219
64389
|
const text = String(object.source?.content ?? object.content ?? "");
|
|
@@ -64229,7 +64399,8 @@ var C2S = class {
|
|
|
64229
64399
|
expiresAt: object.endTime || null,
|
|
64230
64400
|
inReplyTo: idOf(object.inReplyTo) || void 0,
|
|
64231
64401
|
visibility,
|
|
64232
|
-
spoilerText: object.summary || null
|
|
64402
|
+
spoilerText: object.summary || null,
|
|
64403
|
+
sensitive: object.sensitive === true
|
|
64233
64404
|
});
|
|
64234
64405
|
return reply(
|
|
64235
64406
|
201,
|
|
@@ -64250,7 +64421,10 @@ var C2S = class {
|
|
|
64250
64421
|
attachments,
|
|
64251
64422
|
visibility,
|
|
64252
64423
|
spoilerText: object.summary || null,
|
|
64253
|
-
|
|
64424
|
+
sensitive: object.sensitive === true,
|
|
64425
|
+
slug,
|
|
64426
|
+
also,
|
|
64427
|
+
deliverTo
|
|
64254
64428
|
});
|
|
64255
64429
|
return reply(
|
|
64256
64430
|
201,
|
|
@@ -64277,6 +64451,7 @@ var C2S = class {
|
|
|
64277
64451
|
await agent2.publisher.updateNote(s, {
|
|
64278
64452
|
content: text,
|
|
64279
64453
|
spoilerText: object?.summary || null,
|
|
64454
|
+
sensitive: object?.sensitive === void 0 ? null : object.sensitive === true,
|
|
64280
64455
|
attachments
|
|
64281
64456
|
});
|
|
64282
64457
|
return reply(200, { ok: true, object: s.noteId });
|
|
@@ -65482,7 +65657,7 @@ function page(api, items, url, { limit = 20, max = 40, idOf: idOf2 = (s) => api.
|
|
|
65482
65657
|
}
|
|
65483
65658
|
const pageItems2 = items.slice(0, n);
|
|
65484
65659
|
if (!pageItems2.length) return { items: pageItems2, headers: {} };
|
|
65485
|
-
const base =
|
|
65660
|
+
const base = url.origin + url.pathname;
|
|
65486
65661
|
const q = (extra) => {
|
|
65487
65662
|
const u = new URL(base);
|
|
65488
65663
|
for (const [k, v] of url.searchParams) if (!["max_id", "since_id", "min_id"].includes(k)) u.searchParams.set(k, v);
|
|
@@ -65608,7 +65783,7 @@ function status(api, s, { all } = {}) {
|
|
|
65608
65783
|
created_at: s.published || (/* @__PURE__ */ new Date()).toISOString(),
|
|
65609
65784
|
in_reply_to_id: s.inReplyTo ? api.store.idFor(s.inReplyTo) : null,
|
|
65610
65785
|
in_reply_to_account_id: null,
|
|
65611
|
-
sensitive: !!s.spoiler,
|
|
65786
|
+
sensitive: !!(s.spoiler || s.sensitive),
|
|
65612
65787
|
spoiler_text: s.spoiler || "",
|
|
65613
65788
|
visibility: s.visibility || "public",
|
|
65614
65789
|
language: null,
|
|
@@ -65648,7 +65823,7 @@ function status(api, s, { all } = {}) {
|
|
|
65648
65823
|
acct: bare.includes("@") ? bare : host ? `${user}@${host}` : bare
|
|
65649
65824
|
};
|
|
65650
65825
|
}),
|
|
65651
|
-
tags: [],
|
|
65826
|
+
tags: (s.tags || []).map((t) => ({ name: t.name, url: t.url })),
|
|
65652
65827
|
emojis: (s.emojis || []).map((e) => ({
|
|
65653
65828
|
shortcode: e.shortcode,
|
|
65654
65829
|
url: e.url,
|
|
@@ -66580,6 +66755,7 @@ async function handle6(api, ctx) {
|
|
|
66580
66755
|
if (ready !== true) return send(422, { error: ready });
|
|
66581
66756
|
}
|
|
66582
66757
|
const spoilerText = String(body.spoiler_text || "").trim() || null;
|
|
66758
|
+
const sensitive = body.sensitive === true || body.sensitive === "true" || body.sensitive === "1";
|
|
66583
66759
|
const mediaIds = [].concat(body.media_ids || body["media_ids[]"] || []).filter(Boolean);
|
|
66584
66760
|
const media = api.store.getMedia();
|
|
66585
66761
|
const attachments = mediaIds.map((id) => media[id] && { id, ...media[id] }).filter(Boolean);
|
|
@@ -66610,7 +66786,8 @@ async function handle6(api, ctx) {
|
|
|
66610
66786
|
expiresAt: new Date(Date.now() + seconds * 1e3).toISOString(),
|
|
66611
66787
|
inReplyTo,
|
|
66612
66788
|
visibility,
|
|
66613
|
-
spoilerText
|
|
66789
|
+
spoilerText,
|
|
66790
|
+
sensitive
|
|
66614
66791
|
});
|
|
66615
66792
|
return send(200, api.status(api.store.getStatuses().find((x) => x.noteId === q.id)));
|
|
66616
66793
|
} catch (e) {
|
|
@@ -66639,7 +66816,7 @@ async function handle6(api, ctx) {
|
|
|
66639
66816
|
try {
|
|
66640
66817
|
note = await api.agent.publisher.publishNote(
|
|
66641
66818
|
body.status,
|
|
66642
|
-
{ inReplyTo, attachments, visibility, spoilerText }
|
|
66819
|
+
{ inReplyTo, attachments, visibility, spoilerText, sensitive }
|
|
66643
66820
|
);
|
|
66644
66821
|
} catch (e) {
|
|
66645
66822
|
if (e.code === "unaddressed") return send(422, { error: e.message });
|
|
@@ -66663,7 +66840,7 @@ async function handle6(api, ctx) {
|
|
|
66663
66840
|
return send(200, [{
|
|
66664
66841
|
content: s.content || "",
|
|
66665
66842
|
spoiler_text: s.spoiler || "",
|
|
66666
|
-
sensitive: !!s.spoiler,
|
|
66843
|
+
sensitive: !!(s.spoiler || s.sensitive),
|
|
66667
66844
|
created_at: s.editedAt || s.published,
|
|
66668
66845
|
account: api.account(s.actor),
|
|
66669
66846
|
media_attachments: (s.attachments || []).map((a) => api.mediaJson(a)),
|
|
@@ -66688,9 +66865,10 @@ async function handle6(api, ctx) {
|
|
|
66688
66865
|
const media = api.store.getMedia();
|
|
66689
66866
|
const attachments = mediaIds.length ? mediaIds.map((id) => media[id] && { id, ...media[id] }).filter(Boolean) : null;
|
|
66690
66867
|
const spoilerText = String(body.spoiler_text || "").trim() || null;
|
|
66868
|
+
const sensitive = body.sensitive === void 0 ? null : body.sensitive === true || body.sensitive === "true" || body.sensitive === "1";
|
|
66691
66869
|
const patched = await api.agent.publisher.updateNote(
|
|
66692
66870
|
s,
|
|
66693
|
-
{ content: body.status, spoilerText, attachments }
|
|
66871
|
+
{ content: body.status, spoilerText, sensitive, attachments }
|
|
66694
66872
|
);
|
|
66695
66873
|
return send(200, api.status(patched || s));
|
|
66696
66874
|
}
|
|
@@ -66929,7 +67107,13 @@ var MastoApi = class _MastoApi {
|
|
|
66929
67107
|
res.end(body);
|
|
66930
67108
|
return true;
|
|
66931
67109
|
};
|
|
66932
|
-
const
|
|
67110
|
+
const scheme = String(this.scheme || (req.headers?.["x-forwarded-proto"] === "https" || req.socket?.encrypted ? "https" : "http")).replace(/:.*$/, "");
|
|
67111
|
+
let self2 = url;
|
|
67112
|
+
try {
|
|
67113
|
+
self2 = new URL(`${scheme}://${req.headers?.host || "localhost"}${this.mount || ""}${pathname}${url.search || ""}`);
|
|
67114
|
+
} catch {
|
|
67115
|
+
}
|
|
67116
|
+
const ctx = { req, res, pathname, url: self2, send };
|
|
66933
67117
|
if (await handle(this, ctx)) return true;
|
|
66934
67118
|
if (!pathname.startsWith("/api/")) return false;
|
|
66935
67119
|
if (await handle2(this, ctx)) return true;
|
|
@@ -67940,6 +68124,7 @@ var hostOf = (inbox) => {
|
|
|
67940
68124
|
}
|
|
67941
68125
|
};
|
|
67942
68126
|
var aboutTheHost = (e) => !e.status || e.status >= 500 || e.status === 429;
|
|
68127
|
+
var unsalvageable = (e) => e.status === 404 || e.status === 410;
|
|
67943
68128
|
var Deliverer = class {
|
|
67944
68129
|
// passive: signing-only (viewer-mode agents) — no queue drain timer, so a
|
|
67945
68130
|
// read-only agent never mutates shared delivery state. startQueue() flips
|
|
@@ -67952,9 +68137,11 @@ var Deliverer = class {
|
|
|
67952
68137
|
edPrivate = null,
|
|
67953
68138
|
proofKeyId = null,
|
|
67954
68139
|
log: log2 = console.log,
|
|
67955
|
-
passive = false
|
|
68140
|
+
passive = false,
|
|
68141
|
+
onGone = null
|
|
67956
68142
|
}) {
|
|
67957
68143
|
this.store = store;
|
|
68144
|
+
this.onGone = onGone;
|
|
67958
68145
|
this.keyId = keyId;
|
|
67959
68146
|
this.rsaPrivate = rsaPrivate;
|
|
67960
68147
|
this.actorId = actorId;
|
|
@@ -68050,6 +68237,10 @@ var Deliverer = class {
|
|
|
68050
68237
|
await this.deliverNow(inbox, signed);
|
|
68051
68238
|
this.log(`delivered ${signed.type} \u2192 ${inbox}`);
|
|
68052
68239
|
} catch (e) {
|
|
68240
|
+
if (unsalvageable(e)) {
|
|
68241
|
+
await this._unsalvageable(inbox, e);
|
|
68242
|
+
return;
|
|
68243
|
+
}
|
|
68053
68244
|
this.log(`delivery failed (${e.message}) \u2014 queued`);
|
|
68054
68245
|
const wait = e.retryAfterMs || 6e4;
|
|
68055
68246
|
if (aboutTheHost(e)) {
|
|
@@ -68059,6 +68250,26 @@ var Deliverer = class {
|
|
|
68059
68250
|
this._enqueue({ inbox, activity: signed, attempts: 1, nextAt: Date.now() + wait });
|
|
68060
68251
|
}
|
|
68061
68252
|
}
|
|
68253
|
+
// Not retried, and on a 410 the followers who received there are dropped,
|
|
68254
|
+
// so the next fan-out stops asking; the followers collection is republished
|
|
68255
|
+
// through onGone. A 404 drops only this delivery.
|
|
68256
|
+
async _unsalvageable(inbox, e) {
|
|
68257
|
+
if (e.status !== 410) {
|
|
68258
|
+
this.log(`delivery dropped (${e.message}) \u2014 nothing answers there`);
|
|
68259
|
+
return;
|
|
68260
|
+
}
|
|
68261
|
+
const contacts = this.store.getContacts?.();
|
|
68262
|
+
const left = (contacts?.followers || []).filter((f) => f.inbox === inbox || f.sharedInbox === inbox);
|
|
68263
|
+
this.log(`${inbox} is gone (410) \u2014 delivery dropped${left.length ? `, ${left.length} follower(s) there forgotten` : ""}`);
|
|
68264
|
+
if (!left.length) return;
|
|
68265
|
+
for (const f of left) dropFollower(contacts, f.actor, "inbox-gone");
|
|
68266
|
+
this.store.setContacts(contacts);
|
|
68267
|
+
try {
|
|
68268
|
+
await this.onGone?.();
|
|
68269
|
+
} catch (err) {
|
|
68270
|
+
this.log(`followers republish after 410: ${err.message}`);
|
|
68271
|
+
}
|
|
68272
|
+
}
|
|
68062
68273
|
// The queue has a ceiling: a fan-out into a sea of down servers must not grow
|
|
68063
68274
|
// queue.json without bound. Overflow keeps the evidence as a dead letter.
|
|
68064
68275
|
_enqueue(item) {
|
|
@@ -68143,6 +68354,11 @@ var Deliverer = class {
|
|
|
68143
68354
|
changed = true;
|
|
68144
68355
|
this.log(`retry ok: ${item.activity.type} \u2192 ${item.inbox}`);
|
|
68145
68356
|
} catch (e) {
|
|
68357
|
+
if (unsalvageable(e)) {
|
|
68358
|
+
changed = true;
|
|
68359
|
+
await this._unsalvageable(item.inbox, e);
|
|
68360
|
+
continue;
|
|
68361
|
+
}
|
|
68146
68362
|
item.attempts += 1;
|
|
68147
68363
|
changed = true;
|
|
68148
68364
|
const wait = e.retryAfterMs || ladderFor(item);
|
|
@@ -70436,7 +70652,8 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
70436
70652
|
// The relay finds the account by the front's own key for it, which for a
|
|
70437
70653
|
// mail-door account is the full address, not the bare handle.
|
|
70438
70654
|
handle: doorKeyOf(config.gateway?.url) || config.handle,
|
|
70439
|
-
sessionFetch: session.fetch
|
|
70655
|
+
sessionFetch: session.fetch,
|
|
70656
|
+
onGone: () => this.publisher.publishCollections({ followers: true })
|
|
70440
70657
|
});
|
|
70441
70658
|
this.publisher = new Publisher({
|
|
70442
70659
|
config: this.store.getConfig(),
|
|
@@ -70449,7 +70666,8 @@ var BrowserAgent = class _BrowserAgent {
|
|
|
70449
70666
|
// Who a post names, resolved — the same lookup the DeviceAgent
|
|
70450
70667
|
// gives its publisher. Without it no mention from the browser ever
|
|
70451
70668
|
// resolved: a direct message went to nobody, a mention notified no one.
|
|
70452
|
-
resolveMention: (h) => resolveHandle(this, h)
|
|
70669
|
+
resolveMention: (h) => resolveHandle(this, h),
|
|
70670
|
+
resolveActor: (u) => this.intake.fetchAP(u)
|
|
70453
70671
|
});
|
|
70454
70672
|
this.atproto = new BrowserAtproto({ store: this.store, actorId: this.urls.actor, log: this.log });
|
|
70455
70673
|
this.publisher.atproto = this.atproto;
|