@fedify/fedify 2.3.0-dev.1114 → 2.3.0-dev.1119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{builder-YlEusQth.mjs → builder-Ond_h57y.mjs} +2 -2
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{deno-CF3jMgip.mjs → deno-DVsHS7rA.mjs} +1 -1
- package/dist/{docloader-BENj6vQ4.mjs → docloader-WsWfKaE5.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.d.mts +2 -0
- package/dist/federation/metrics.test.mjs +107 -0
- package/dist/federation/middleware.test.mjs +386 -6
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{http-CpzZ9zsb.js → http-CouJSFVK.js} +73 -5
- package/dist/{http-CKCgOPkX.cjs → http-CubOB9wq.cjs} +90 -4
- package/dist/{http-BmOZYc-8.mjs → http-DUV8ysti.mjs} +3 -3
- package/dist/{key-B4I8H5Lc.mjs → key-BoWaYRHm.mjs} +1 -1
- package/dist/{kv-cache-Wc5ezcVW.js → kv-cache-DBNpsneh.js} +1 -1
- package/dist/{kv-cache-DY-XWOqM.cjs → kv-cache-Dz31ATUT.cjs} +1 -1
- package/dist/{ld-B5D5THhl.mjs → ld-B5K1mSuG.mjs} +3 -3
- package/dist/{metrics-ek3ilf6c.mjs → metrics-C4attqv0.mjs} +73 -5
- package/dist/{middleware-EI7OU6BR.mjs → middleware-BDKFRjue.mjs} +1 -1
- package/dist/{middleware-EqTYPG4F.cjs → middleware-CmsDtIHI.cjs} +33 -14
- package/dist/{middleware-CuZbBw-N.js → middleware-Dtjz-hSk.js} +33 -14
- package/dist/{middleware-DlcecZMq.mjs → middleware-t0jC8I99.mjs} +40 -21
- package/dist/mod.cjs +4 -4
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-DO810N24.mjs → owner-hDxI0ufu.mjs} +2 -2
- package/dist/{proof-DIoqrKnX.cjs → proof-BUWfVr6Q.cjs} +1 -1
- package/dist/{proof-BgfyWv7b.mjs → proof-DhVuz4bc.mjs} +3 -3
- package/dist/{proof-Vd8-1EWh.js → proof-n60t8o9P.js} +1 -1
- package/dist/{send-CAYXdUTk.mjs → send-BPhyR5Oo.mjs} +3 -3
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/proof.test.mjs +1 -1
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +5 -5
|
@@ -2,7 +2,7 @@ import "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { n as RouterError, t as Router } from "./router-BT_F5748.mjs";
|
|
5
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
+
import { n as version, t as name } from "./deno-DVsHS7rA.mjs";
|
|
6
6
|
import { t as ActivityListenerSet } from "./activity-listener-BeTGV3wc.mjs";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
8
8
|
import { Tombstone, getTypeId } from "@fedify/vocab";
|
|
@@ -59,7 +59,7 @@ var FederationBuilderImpl = class {
|
|
|
59
59
|
this.collectionTypeIds = {};
|
|
60
60
|
}
|
|
61
61
|
async build(options) {
|
|
62
|
-
const { FederationImpl } = await import("./middleware-
|
|
62
|
+
const { FederationImpl } = await import("./middleware-BDKFRjue.mjs");
|
|
63
63
|
const f = new FederationImpl(options);
|
|
64
64
|
const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
|
|
65
65
|
f.router = this.router.clone();
|
|
@@ -5,7 +5,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
|
5
5
|
import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
|
|
6
6
|
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
7
7
|
import { t as MemoryKvStore } from "../kv-QHE0oeM3.mjs";
|
|
8
|
-
import { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-
|
|
8
|
+
import { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-t0jC8I99.mjs";
|
|
9
9
|
import { Follow, Person } from "@fedify/vocab";
|
|
10
10
|
import { test } from "@fedify/fixture";
|
|
11
11
|
//#region src/compat/transformers.test.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { o as validateCryptoKey } from "./key-
|
|
5
|
-
import { n as doubleKnock } from "./http-
|
|
4
|
+
import { o as validateCryptoKey } from "./key-BoWaYRHm.mjs";
|
|
5
|
+
import { n as doubleKnock } from "./http-DUV8ysti.mjs";
|
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
|
7
7
|
import { curry } from "es-toolkit";
|
|
8
8
|
import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
@@ -6,7 +6,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
|
6
6
|
import { r as assertExists } from "../std__assert-BTEgfoJo.mjs";
|
|
7
7
|
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
8
8
|
import { t as MemoryKvStore } from "../kv-QHE0oeM3.mjs";
|
|
9
|
-
import { r as createFederationBuilder } from "../builder-
|
|
9
|
+
import { r as createFederationBuilder } from "../builder-Ond_h57y.mjs";
|
|
10
10
|
import { Activity, Note, Person } from "@fedify/vocab";
|
|
11
11
|
import { test } from "@fedify/fixture";
|
|
12
12
|
//#region src/federation/builder.test.ts
|
|
@@ -8,10 +8,10 @@ import { n as assertGreaterOrEqual } from "../assert_rejects-DQP-q39h.mjs";
|
|
|
8
8
|
import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
|
|
9
9
|
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
10
10
|
import { r as parseAcceptSignature } from "../accept-CgDcxvjV.mjs";
|
|
11
|
-
import { s as signRequest } from "../http-
|
|
11
|
+
import { s as signRequest } from "../http-DUV8ysti.mjs";
|
|
12
12
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-CSYsOMFG.mjs";
|
|
13
13
|
import { t as MemoryKvStore } from "../kv-QHE0oeM3.mjs";
|
|
14
|
-
import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-
|
|
14
|
+
import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-t0jC8I99.mjs";
|
|
15
15
|
import { t as ActivityListenerSet } from "../activity-listener-BeTGV3wc.mjs";
|
|
16
16
|
import { Activity, Create, Note, Person, Tombstone } from "@fedify/vocab";
|
|
17
17
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
5
|
import "../std__assert-BTEgfoJo.mjs";
|
|
6
6
|
import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-CSYsOMFG.mjs";
|
|
7
|
-
import { r as signObject } from "../proof-
|
|
7
|
+
import { r as signObject } from "../proof-DhVuz4bc.mjs";
|
|
8
8
|
import { t as MemoryKvStore } from "../kv-QHE0oeM3.mjs";
|
|
9
|
-
import { o as createFederation } from "../middleware-
|
|
9
|
+
import { o as createFederation } from "../middleware-t0jC8I99.mjs";
|
|
10
10
|
import { Create, Follow, Person } from "@fedify/vocab";
|
|
11
11
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
12
|
//#region src/federation/idempotency.test.ts
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import "@js-temporal/polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
|
+
import "../std__assert-BTEgfoJo.mjs";
|
|
6
|
+
import { c as recordOutboxActivity, l as recordOutboxEnqueue, o as recordFanoutRecipients, s as recordInboxActivity } from "../metrics-C4attqv0.mjs";
|
|
7
|
+
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
8
|
+
//#region src/federation/metrics.test.ts
|
|
9
|
+
const noopQueue = {
|
|
10
|
+
enqueue() {
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
},
|
|
13
|
+
listen() {
|
|
14
|
+
return Promise.resolve();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
test("recordFanoutRecipients() records the recipient count with activity type", () => {
|
|
18
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
19
|
+
recordFanoutRecipients(meterProvider, 7, "https://www.w3.org/ns/activitystreams#Create");
|
|
20
|
+
const measurements = recorder.getMeasurements("activitypub.fanout.recipients");
|
|
21
|
+
assertEquals(measurements.length, 1);
|
|
22
|
+
assertEquals(measurements[0].type, "histogram");
|
|
23
|
+
assertEquals(measurements[0].value, 7);
|
|
24
|
+
assertEquals(measurements[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
25
|
+
});
|
|
26
|
+
test("recordFanoutRecipients() omits activity type when unknown", () => {
|
|
27
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
28
|
+
recordFanoutRecipients(meterProvider, 0);
|
|
29
|
+
const measurements = recorder.getMeasurements("activitypub.fanout.recipients");
|
|
30
|
+
assertEquals(measurements.length, 1);
|
|
31
|
+
assertEquals(measurements[0].value, 0);
|
|
32
|
+
assertEquals("activitypub.activity.type" in measurements[0].attributes, false);
|
|
33
|
+
});
|
|
34
|
+
test("recordInboxActivity() records counter with result and activity type", () => {
|
|
35
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
36
|
+
for (const result of [
|
|
37
|
+
"queued",
|
|
38
|
+
"processed",
|
|
39
|
+
"retried",
|
|
40
|
+
"rejected",
|
|
41
|
+
"abandoned"
|
|
42
|
+
]) recordInboxActivity(meterProvider, result, "https://www.w3.org/ns/activitystreams#Follow");
|
|
43
|
+
const measurements = recorder.getMeasurements("activitypub.inbox.activity");
|
|
44
|
+
assertEquals(measurements.length, 5);
|
|
45
|
+
for (const m of measurements) {
|
|
46
|
+
assertEquals(m.type, "counter");
|
|
47
|
+
assertEquals(m.value, 1);
|
|
48
|
+
assertEquals(m.attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Follow");
|
|
49
|
+
}
|
|
50
|
+
assertEquals(measurements.map((m) => m.attributes["activitypub.processing.result"]), [
|
|
51
|
+
"queued",
|
|
52
|
+
"processed",
|
|
53
|
+
"retried",
|
|
54
|
+
"rejected",
|
|
55
|
+
"abandoned"
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
test("recordInboxActivity() omits activity type when unknown", () => {
|
|
59
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
60
|
+
recordInboxActivity(meterProvider, "rejected");
|
|
61
|
+
const measurements = recorder.getMeasurements("activitypub.inbox.activity");
|
|
62
|
+
assertEquals(measurements.length, 1);
|
|
63
|
+
assertEquals(measurements[0].attributes["activitypub.processing.result"], "rejected");
|
|
64
|
+
assertEquals("activitypub.activity.type" in measurements[0].attributes, false);
|
|
65
|
+
});
|
|
66
|
+
test("recordOutboxEnqueue() also records activitypub.outbox.activity{queued} on initial enqueue", () => {
|
|
67
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
68
|
+
recordOutboxEnqueue(meterProvider, noopQueue, {
|
|
69
|
+
activityType: "https://www.w3.org/ns/activitystreams#Create",
|
|
70
|
+
attempt: 0
|
|
71
|
+
});
|
|
72
|
+
const queued = recorder.getMeasurements("activitypub.outbox.activity");
|
|
73
|
+
assertEquals(queued.length, 1);
|
|
74
|
+
assertEquals(queued[0].type, "counter");
|
|
75
|
+
assertEquals(queued[0].attributes["activitypub.processing.result"], "queued");
|
|
76
|
+
assertEquals(queued[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
77
|
+
});
|
|
78
|
+
test("recordOutboxEnqueue() does not record outbox.activity{queued} on retry enqueues", () => {
|
|
79
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
80
|
+
recordOutboxEnqueue(meterProvider, noopQueue, {
|
|
81
|
+
activityType: "https://www.w3.org/ns/activitystreams#Create",
|
|
82
|
+
attempt: 1
|
|
83
|
+
});
|
|
84
|
+
assertEquals(recorder.getMeasurements("activitypub.outbox.activity").length, 0);
|
|
85
|
+
});
|
|
86
|
+
test("recordOutboxActivity() records counter with result and activity type", () => {
|
|
87
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
88
|
+
for (const result of [
|
|
89
|
+
"queued",
|
|
90
|
+
"retried",
|
|
91
|
+
"abandoned"
|
|
92
|
+
]) recordOutboxActivity(meterProvider, result, "https://www.w3.org/ns/activitystreams#Announce");
|
|
93
|
+
const measurements = recorder.getMeasurements("activitypub.outbox.activity");
|
|
94
|
+
assertEquals(measurements.length, 3);
|
|
95
|
+
for (const m of measurements) {
|
|
96
|
+
assertEquals(m.type, "counter");
|
|
97
|
+
assertEquals(m.value, 1);
|
|
98
|
+
assertEquals(m.attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Announce");
|
|
99
|
+
}
|
|
100
|
+
assertEquals(measurements.map((m) => m.attributes["activitypub.processing.result"]), [
|
|
101
|
+
"queued",
|
|
102
|
+
"retried",
|
|
103
|
+
"abandoned"
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
//#endregion
|
|
107
|
+
export {};
|
|
@@ -11,14 +11,14 @@ import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
|
|
|
11
11
|
import { t as assertStrictEquals } from "../assert_strict_equals-Dmjbg-bA.mjs";
|
|
12
12
|
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
13
13
|
import { t as esm_default } from "../esm-sdtqOUPu.mjs";
|
|
14
|
-
import { l as verifyRequest, s as signRequest } from "../http-
|
|
14
|
+
import { l as verifyRequest, s as signRequest } from "../http-DUV8ysti.mjs";
|
|
15
15
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CSYsOMFG.mjs";
|
|
16
|
-
import { t as getAuthenticatedDocumentLoader } from "../docloader-
|
|
17
|
-
import { a as signJsonLd, o as verifyJsonLd, r as detachSignature } from "../ld-
|
|
18
|
-
import { t as doesActorOwnKey } from "../owner-
|
|
19
|
-
import { i as verifyObject, r as signObject } from "../proof-
|
|
16
|
+
import { t as getAuthenticatedDocumentLoader } from "../docloader-WsWfKaE5.mjs";
|
|
17
|
+
import { a as signJsonLd, o as verifyJsonLd, r as detachSignature } from "../ld-B5K1mSuG.mjs";
|
|
18
|
+
import { t as doesActorOwnKey } from "../owner-hDxI0ufu.mjs";
|
|
19
|
+
import { i as verifyObject, r as signObject } from "../proof-DhVuz4bc.mjs";
|
|
20
20
|
import { t as MemoryKvStore } from "../kv-QHE0oeM3.mjs";
|
|
21
|
-
import { i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-
|
|
21
|
+
import { i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-t0jC8I99.mjs";
|
|
22
22
|
import { configure, reset } from "@logtape/logtape";
|
|
23
23
|
import * as vocab from "@fedify/vocab";
|
|
24
24
|
import { getTypeId, lookupObject } from "@fedify/vocab";
|
|
@@ -2287,6 +2287,214 @@ test("FederationImpl.processQueuedTask()", async (t) => {
|
|
|
2287
2287
|
attempt: 1
|
|
2288
2288
|
}]);
|
|
2289
2289
|
});
|
|
2290
|
+
await t.step("records activitypub.outbox.activity retry on transient failure", async () => {
|
|
2291
|
+
const kv = new MemoryKvStore();
|
|
2292
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
2293
|
+
await new FederationImpl({
|
|
2294
|
+
kv,
|
|
2295
|
+
meterProvider,
|
|
2296
|
+
queue: {
|
|
2297
|
+
enqueue(_message, _options) {
|
|
2298
|
+
return Promise.resolve();
|
|
2299
|
+
},
|
|
2300
|
+
listen(_handler, _options) {
|
|
2301
|
+
return Promise.resolve();
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
}).processQueuedTask(void 0, {
|
|
2305
|
+
type: "outbox",
|
|
2306
|
+
id: crypto.randomUUID(),
|
|
2307
|
+
baseUrl: "https://example.com",
|
|
2308
|
+
keys: [],
|
|
2309
|
+
activity: {
|
|
2310
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2311
|
+
type: "Create",
|
|
2312
|
+
actor: "https://example.com/users/alice",
|
|
2313
|
+
object: {
|
|
2314
|
+
type: "Note",
|
|
2315
|
+
content: "test"
|
|
2316
|
+
}
|
|
2317
|
+
},
|
|
2318
|
+
activityType: "https://www.w3.org/ns/activitystreams#Create",
|
|
2319
|
+
inbox: "https://invalid-domain-that-does-not-exist.example/inbox",
|
|
2320
|
+
sharedInbox: false,
|
|
2321
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2322
|
+
attempt: 0,
|
|
2323
|
+
headers: {},
|
|
2324
|
+
traceContext: {}
|
|
2325
|
+
});
|
|
2326
|
+
const outboxLifecycle = recorder.getMeasurements("activitypub.outbox.activity");
|
|
2327
|
+
assertEquals(outboxLifecycle.length, 1);
|
|
2328
|
+
assertEquals(outboxLifecycle[0].type, "counter");
|
|
2329
|
+
assertEquals(outboxLifecycle[0].attributes["activitypub.processing.result"], "retried");
|
|
2330
|
+
assertEquals(outboxLifecycle[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
2331
|
+
});
|
|
2332
|
+
await t.step("records activitypub.outbox.activity abandoned when retry policy gives up", async () => {
|
|
2333
|
+
const kv = new MemoryKvStore();
|
|
2334
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
2335
|
+
await new FederationImpl({
|
|
2336
|
+
kv,
|
|
2337
|
+
meterProvider,
|
|
2338
|
+
queue: {
|
|
2339
|
+
enqueue(_message, _options) {
|
|
2340
|
+
return Promise.resolve();
|
|
2341
|
+
},
|
|
2342
|
+
listen(_handler, _options) {
|
|
2343
|
+
return Promise.resolve();
|
|
2344
|
+
}
|
|
2345
|
+
},
|
|
2346
|
+
outboxRetryPolicy: () => null
|
|
2347
|
+
}).processQueuedTask(void 0, {
|
|
2348
|
+
type: "outbox",
|
|
2349
|
+
id: crypto.randomUUID(),
|
|
2350
|
+
baseUrl: "https://example.com",
|
|
2351
|
+
keys: [],
|
|
2352
|
+
activity: {
|
|
2353
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2354
|
+
type: "Follow",
|
|
2355
|
+
actor: "https://example.com/users/alice",
|
|
2356
|
+
object: "https://remote.example/users/bob"
|
|
2357
|
+
},
|
|
2358
|
+
activityType: "https://www.w3.org/ns/activitystreams#Follow",
|
|
2359
|
+
inbox: "https://invalid-domain-that-does-not-exist.example/inbox",
|
|
2360
|
+
sharedInbox: false,
|
|
2361
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2362
|
+
attempt: 0,
|
|
2363
|
+
headers: {},
|
|
2364
|
+
traceContext: {}
|
|
2365
|
+
});
|
|
2366
|
+
const outboxLifecycle = recorder.getMeasurements("activitypub.outbox.activity");
|
|
2367
|
+
assertEquals(outboxLifecycle.length, 1);
|
|
2368
|
+
assertEquals(outboxLifecycle[0].type, "counter");
|
|
2369
|
+
assertEquals(outboxLifecycle[0].attributes["activitypub.processing.result"], "abandoned");
|
|
2370
|
+
assertEquals(outboxLifecycle[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Follow");
|
|
2371
|
+
});
|
|
2372
|
+
await t.step("records activitypub.inbox.activity processed on successful queued dispatch", async () => {
|
|
2373
|
+
const kv = new MemoryKvStore();
|
|
2374
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
2375
|
+
const federation = new FederationImpl({
|
|
2376
|
+
kv,
|
|
2377
|
+
meterProvider,
|
|
2378
|
+
queue: {
|
|
2379
|
+
enqueue(_message, _options) {
|
|
2380
|
+
return Promise.resolve();
|
|
2381
|
+
},
|
|
2382
|
+
listen(_handler, _options) {
|
|
2383
|
+
return Promise.resolve();
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(vocab.Create, () => {});
|
|
2388
|
+
await federation.processQueuedTask(void 0, {
|
|
2389
|
+
type: "inbox",
|
|
2390
|
+
id: crypto.randomUUID(),
|
|
2391
|
+
baseUrl: "https://example.com",
|
|
2392
|
+
activity: {
|
|
2393
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2394
|
+
type: "Create",
|
|
2395
|
+
id: "https://example.com/activities/queued-processed",
|
|
2396
|
+
actor: "https://remote.example/users/alice",
|
|
2397
|
+
object: {
|
|
2398
|
+
type: "Note",
|
|
2399
|
+
content: "Hello world"
|
|
2400
|
+
}
|
|
2401
|
+
},
|
|
2402
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2403
|
+
attempt: 0,
|
|
2404
|
+
identifier: null,
|
|
2405
|
+
traceContext: {}
|
|
2406
|
+
});
|
|
2407
|
+
const inboxLifecycle = recorder.getMeasurements("activitypub.inbox.activity");
|
|
2408
|
+
assertEquals(inboxLifecycle.length, 1);
|
|
2409
|
+
assertEquals(inboxLifecycle[0].type, "counter");
|
|
2410
|
+
assertEquals(inboxLifecycle[0].attributes["activitypub.processing.result"], "processed");
|
|
2411
|
+
assertEquals(inboxLifecycle[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
2412
|
+
});
|
|
2413
|
+
await t.step("records activitypub.inbox.activity retried on transient listener failure", async () => {
|
|
2414
|
+
const kv = new MemoryKvStore();
|
|
2415
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
2416
|
+
const federation = new FederationImpl({
|
|
2417
|
+
kv,
|
|
2418
|
+
meterProvider,
|
|
2419
|
+
queue: {
|
|
2420
|
+
enqueue(_message, _options) {
|
|
2421
|
+
return Promise.resolve();
|
|
2422
|
+
},
|
|
2423
|
+
listen(_handler, _options) {
|
|
2424
|
+
return Promise.resolve();
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
});
|
|
2428
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(vocab.Create, () => {
|
|
2429
|
+
throw new Error("Intended error for testing");
|
|
2430
|
+
});
|
|
2431
|
+
await federation.processQueuedTask(void 0, {
|
|
2432
|
+
type: "inbox",
|
|
2433
|
+
id: crypto.randomUUID(),
|
|
2434
|
+
baseUrl: "https://example.com",
|
|
2435
|
+
activity: {
|
|
2436
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2437
|
+
type: "Create",
|
|
2438
|
+
id: "https://example.com/activities/queued-retried",
|
|
2439
|
+
actor: "https://remote.example/users/alice",
|
|
2440
|
+
object: {
|
|
2441
|
+
type: "Note",
|
|
2442
|
+
content: "Hello world"
|
|
2443
|
+
}
|
|
2444
|
+
},
|
|
2445
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2446
|
+
attempt: 0,
|
|
2447
|
+
identifier: null,
|
|
2448
|
+
traceContext: {}
|
|
2449
|
+
});
|
|
2450
|
+
const inboxLifecycle = recorder.getMeasurements("activitypub.inbox.activity");
|
|
2451
|
+
assertEquals(inboxLifecycle.length, 1);
|
|
2452
|
+
assertEquals(inboxLifecycle[0].attributes["activitypub.processing.result"], "retried");
|
|
2453
|
+
assertEquals(inboxLifecycle[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
2454
|
+
});
|
|
2455
|
+
await t.step("records activitypub.inbox.activity abandoned when retry policy gives up", async () => {
|
|
2456
|
+
const kv = new MemoryKvStore();
|
|
2457
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
2458
|
+
const federation = new FederationImpl({
|
|
2459
|
+
kv,
|
|
2460
|
+
meterProvider,
|
|
2461
|
+
queue: {
|
|
2462
|
+
enqueue(_message, _options) {
|
|
2463
|
+
return Promise.resolve();
|
|
2464
|
+
},
|
|
2465
|
+
listen(_handler, _options) {
|
|
2466
|
+
return Promise.resolve();
|
|
2467
|
+
}
|
|
2468
|
+
},
|
|
2469
|
+
inboxRetryPolicy: () => null
|
|
2470
|
+
});
|
|
2471
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(vocab.Create, () => {
|
|
2472
|
+
throw new Error("Intended error for testing");
|
|
2473
|
+
});
|
|
2474
|
+
await federation.processQueuedTask(void 0, {
|
|
2475
|
+
type: "inbox",
|
|
2476
|
+
id: crypto.randomUUID(),
|
|
2477
|
+
baseUrl: "https://example.com",
|
|
2478
|
+
activity: {
|
|
2479
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
2480
|
+
type: "Create",
|
|
2481
|
+
id: "https://example.com/activities/queued-abandoned",
|
|
2482
|
+
actor: "https://remote.example/users/alice",
|
|
2483
|
+
object: {
|
|
2484
|
+
type: "Note",
|
|
2485
|
+
content: "Hello world"
|
|
2486
|
+
}
|
|
2487
|
+
},
|
|
2488
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2489
|
+
attempt: 0,
|
|
2490
|
+
identifier: null,
|
|
2491
|
+
traceContext: {}
|
|
2492
|
+
});
|
|
2493
|
+
const inboxLifecycle = recorder.getMeasurements("activitypub.inbox.activity");
|
|
2494
|
+
assertEquals(inboxLifecycle.length, 1);
|
|
2495
|
+
assertEquals(inboxLifecycle[0].attributes["activitypub.processing.result"], "abandoned");
|
|
2496
|
+
assertEquals(inboxLifecycle[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
2497
|
+
});
|
|
2290
2498
|
await t.step("records queued inbox processing duration", async () => {
|
|
2291
2499
|
const kv = new MemoryKvStore();
|
|
2292
2500
|
const [meterProvider, recorder] = createTestMeterProvider();
|
|
@@ -2447,6 +2655,10 @@ test("FederationImpl.processQueuedTask() permanent failure", async (t) => {
|
|
|
2447
2655
|
assertEquals(failures[0].value, 1);
|
|
2448
2656
|
assertEquals(failures[0].attributes["activitypub.remote.host"], "gone.example");
|
|
2449
2657
|
assertEquals(failures[0].attributes["http.response.status_code"], 410);
|
|
2658
|
+
const abandoned = recorder.getMeasurements("activitypub.outbox.activity");
|
|
2659
|
+
assertEquals(abandoned.length, 1);
|
|
2660
|
+
assertEquals(abandoned[0].attributes["activitypub.processing.result"], "abandoned");
|
|
2661
|
+
assertEquals(abandoned[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
2450
2662
|
const events = exporter.getEvents("activitypub.outbox", "activitypub.delivery.failed");
|
|
2451
2663
|
assertEquals(events.length, 1);
|
|
2452
2664
|
assertEquals(events[0].attributes?.["activitypub.remote.host"], "gone.example");
|
|
@@ -3414,6 +3626,66 @@ test("ContextImpl.sendActivity()", async (t) => {
|
|
|
3414
3626
|
});
|
|
3415
3627
|
esm_default.hardReset();
|
|
3416
3628
|
});
|
|
3629
|
+
test("ContextImpl.sendActivity() records fanout recipient metrics", async () => {
|
|
3630
|
+
const kv = new MemoryKvStore();
|
|
3631
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
3632
|
+
const queue = {
|
|
3633
|
+
messages: [],
|
|
3634
|
+
enqueue(message) {
|
|
3635
|
+
this.messages.push(message);
|
|
3636
|
+
return Promise.resolve();
|
|
3637
|
+
},
|
|
3638
|
+
async listen() {}
|
|
3639
|
+
};
|
|
3640
|
+
const federation = new FederationImpl({
|
|
3641
|
+
kv,
|
|
3642
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
3643
|
+
queue,
|
|
3644
|
+
meterProvider
|
|
3645
|
+
});
|
|
3646
|
+
federation.setActorDispatcher("/{identifier}", async (ctx, identifier) => {
|
|
3647
|
+
if (identifier !== "john") return null;
|
|
3648
|
+
const keys = await ctx.getActorKeyPairs(identifier);
|
|
3649
|
+
return new vocab.Person({
|
|
3650
|
+
id: ctx.getActorUri(identifier),
|
|
3651
|
+
preferredUsername: "john",
|
|
3652
|
+
publicKey: keys[0].cryptographicKey,
|
|
3653
|
+
assertionMethods: keys.map((k) => k.multikey)
|
|
3654
|
+
});
|
|
3655
|
+
}).setKeyPairsDispatcher((_ctx, identifier) => {
|
|
3656
|
+
if (identifier !== "john") return [];
|
|
3657
|
+
return [{
|
|
3658
|
+
privateKey: rsaPrivateKey2,
|
|
3659
|
+
publicKey: rsaPublicKey2.publicKey
|
|
3660
|
+
}, {
|
|
3661
|
+
privateKey: ed25519PrivateKey,
|
|
3662
|
+
publicKey: ed25519PublicKey.publicKey
|
|
3663
|
+
}];
|
|
3664
|
+
});
|
|
3665
|
+
const ctx = new ContextImpl({
|
|
3666
|
+
data: void 0,
|
|
3667
|
+
federation,
|
|
3668
|
+
url: new URL("https://example.com/"),
|
|
3669
|
+
documentLoader: mockDocumentLoader,
|
|
3670
|
+
contextLoader: mockDocumentLoader
|
|
3671
|
+
});
|
|
3672
|
+
const activity = new vocab.Create({
|
|
3673
|
+
id: new URL("https://example.com/activity/1"),
|
|
3674
|
+
actor: new URL("https://example.com/person")
|
|
3675
|
+
});
|
|
3676
|
+
const recipients = Array.from({ length: 7 }, (_, i) => ({
|
|
3677
|
+
id: new URL(`https://example${i + 1}.com/recipient`),
|
|
3678
|
+
inboxId: new URL(`https://example${i + 1}.com/inbox`)
|
|
3679
|
+
}));
|
|
3680
|
+
await ctx.sendActivity({ username: "john" }, recipients, activity, { fanout: "force" });
|
|
3681
|
+
assertEquals(queue.messages.length, 1);
|
|
3682
|
+
assertEquals(queue.messages[0].type, "fanout");
|
|
3683
|
+
const measurements = recorder.getMeasurements("activitypub.fanout.recipients");
|
|
3684
|
+
assertEquals(measurements.length, 1);
|
|
3685
|
+
assertEquals(measurements[0].type, "histogram");
|
|
3686
|
+
assertEquals(measurements[0].value, recipients.length);
|
|
3687
|
+
assertEquals(measurements[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
3688
|
+
});
|
|
3417
3689
|
test({
|
|
3418
3690
|
name: "ContextImpl.routeActivity()",
|
|
3419
3691
|
permissions: {
|
|
@@ -3525,6 +3797,114 @@ test({
|
|
|
3525
3797
|
assertEquals(enqueuedMetrics[0].attributes["fedify.queue.task.attempt"], 0);
|
|
3526
3798
|
}
|
|
3527
3799
|
});
|
|
3800
|
+
test({
|
|
3801
|
+
name: "ContextImpl.routeActivity() records inbox.activity lifecycle metrics",
|
|
3802
|
+
permissions: {
|
|
3803
|
+
env: true,
|
|
3804
|
+
read: true
|
|
3805
|
+
},
|
|
3806
|
+
async fn() {
|
|
3807
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
3808
|
+
const federation = new FederationImpl({
|
|
3809
|
+
kv: new MemoryKvStore(),
|
|
3810
|
+
meterProvider,
|
|
3811
|
+
queue: {
|
|
3812
|
+
enqueue() {
|
|
3813
|
+
return Promise.resolve();
|
|
3814
|
+
},
|
|
3815
|
+
listen() {
|
|
3816
|
+
return Promise.resolve();
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
});
|
|
3820
|
+
federation.setInboxListeners("/u/{identifier}/i", "/i");
|
|
3821
|
+
const ctx = new ContextImpl({
|
|
3822
|
+
url: new URL("https://example.com/"),
|
|
3823
|
+
federation,
|
|
3824
|
+
data: void 0,
|
|
3825
|
+
documentLoader: mockDocumentLoader,
|
|
3826
|
+
contextLoader: documentLoader
|
|
3827
|
+
});
|
|
3828
|
+
const signedOffer = await signObject(new vocab.Offer({
|
|
3829
|
+
id: new URL("https://example.com/offer-queued"),
|
|
3830
|
+
actor: new URL("https://example.com/person2")
|
|
3831
|
+
}), ed25519PrivateKey, ed25519Multikey.id);
|
|
3832
|
+
assert(await ctx.routeActivity(null, signedOffer));
|
|
3833
|
+
const queued = recorder.getMeasurements("activitypub.inbox.activity");
|
|
3834
|
+
assertEquals(queued.length, 1);
|
|
3835
|
+
assertEquals(queued[0].type, "counter");
|
|
3836
|
+
assertEquals(queued[0].attributes["activitypub.processing.result"], "queued");
|
|
3837
|
+
assertEquals(queued[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Offer");
|
|
3838
|
+
}
|
|
3839
|
+
});
|
|
3840
|
+
test({
|
|
3841
|
+
name: "ContextImpl.routeActivity() records inbox.activity processed without queue",
|
|
3842
|
+
permissions: {
|
|
3843
|
+
env: true,
|
|
3844
|
+
read: true
|
|
3845
|
+
},
|
|
3846
|
+
async fn() {
|
|
3847
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
3848
|
+
const federation = new FederationImpl({
|
|
3849
|
+
kv: new MemoryKvStore(),
|
|
3850
|
+
meterProvider
|
|
3851
|
+
});
|
|
3852
|
+
federation.setInboxListeners("/u/{identifier}/i", "/i").on(vocab.Offer, () => {});
|
|
3853
|
+
const ctx = new ContextImpl({
|
|
3854
|
+
url: new URL("https://example.com/"),
|
|
3855
|
+
federation,
|
|
3856
|
+
data: void 0,
|
|
3857
|
+
documentLoader: mockDocumentLoader,
|
|
3858
|
+
contextLoader: documentLoader
|
|
3859
|
+
});
|
|
3860
|
+
const signedOffer = await signObject(new vocab.Offer({
|
|
3861
|
+
id: new URL("https://example.com/offer-processed"),
|
|
3862
|
+
actor: new URL("https://example.com/person2")
|
|
3863
|
+
}), ed25519PrivateKey, ed25519Multikey.id);
|
|
3864
|
+
assert(await ctx.routeActivity(null, signedOffer));
|
|
3865
|
+
const processed = recorder.getMeasurements("activitypub.inbox.activity");
|
|
3866
|
+
assertEquals(processed.length, 1);
|
|
3867
|
+
assertEquals(processed[0].attributes["activitypub.processing.result"], "processed");
|
|
3868
|
+
assertEquals(processed[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Offer");
|
|
3869
|
+
}
|
|
3870
|
+
});
|
|
3871
|
+
test({
|
|
3872
|
+
name: "ContextImpl.routeActivity() records inbox.activity rejected for unsupported type and duplicates",
|
|
3873
|
+
permissions: {
|
|
3874
|
+
env: true,
|
|
3875
|
+
read: true
|
|
3876
|
+
},
|
|
3877
|
+
async fn() {
|
|
3878
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
3879
|
+
const federation = new FederationImpl({
|
|
3880
|
+
kv: new MemoryKvStore(),
|
|
3881
|
+
meterProvider
|
|
3882
|
+
});
|
|
3883
|
+
federation.setInboxListeners("/u/{identifier}/i", "/i").on(vocab.Offer, () => {});
|
|
3884
|
+
const ctx = new ContextImpl({
|
|
3885
|
+
url: new URL("https://example.com/"),
|
|
3886
|
+
federation,
|
|
3887
|
+
data: void 0,
|
|
3888
|
+
documentLoader: mockDocumentLoader,
|
|
3889
|
+
contextLoader: documentLoader
|
|
3890
|
+
});
|
|
3891
|
+
const signedCreate = await signObject(new vocab.Create({
|
|
3892
|
+
id: new URL("https://example.com/create-unsupported"),
|
|
3893
|
+
actor: new URL("https://example.com/person2")
|
|
3894
|
+
}), ed25519PrivateKey, ed25519Multikey.id);
|
|
3895
|
+
assert(await ctx.routeActivity(null, signedCreate));
|
|
3896
|
+
const dupOffer = await signObject(new vocab.Offer({
|
|
3897
|
+
id: new URL("https://example.com/offer-duplicate"),
|
|
3898
|
+
actor: new URL("https://example.com/person2")
|
|
3899
|
+
}), ed25519PrivateKey, ed25519Multikey.id);
|
|
3900
|
+
assert(await ctx.routeActivity(null, dupOffer));
|
|
3901
|
+
assert(await ctx.routeActivity(null, dupOffer));
|
|
3902
|
+
const rejected = recorder.getMeasurements("activitypub.inbox.activity").filter((m) => m.attributes["activitypub.processing.result"] === "rejected");
|
|
3903
|
+
assertEquals(rejected.length, 2);
|
|
3904
|
+
assertEquals(rejected[0].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Create");
|
|
3905
|
+
assertEquals(rejected[1].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Offer");
|
|
3906
|
+
}
|
|
3907
|
+
});
|
|
3528
3908
|
test("ContextImpl.getCollectionUri()", () => {
|
|
3529
3909
|
const federation = new FederationImpl({ kv: new MemoryKvStore() });
|
|
3530
3910
|
const base = "https://example.com";
|
package/dist/federation/mod.cjs
CHANGED
|
@@ -2,7 +2,7 @@ const { Temporal } = require("@js-temporal/polyfill");
|
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
4
|
require("../chunk-DDcVe30Y.cjs");
|
|
5
|
-
const require_middleware = require("../middleware-
|
|
5
|
+
const require_middleware = require("../middleware-CmsDtIHI.cjs");
|
|
6
6
|
let es_toolkit = require("es-toolkit");
|
|
7
7
|
//#region src/federation/kv.ts
|
|
8
8
|
/**
|
package/dist/federation/mod.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-
|
|
3
|
+
import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-Dtjz-hSk.js";
|
|
4
4
|
import { isEqual } from "es-toolkit";
|
|
5
5
|
//#region src/federation/kv.ts
|
|
6
6
|
/**
|
|
@@ -9,10 +9,10 @@ import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
|
|
|
9
9
|
import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
|
|
10
10
|
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
11
11
|
import { t as esm_default } from "../esm-sdtqOUPu.mjs";
|
|
12
|
-
import { l as verifyRequest } from "../http-
|
|
12
|
+
import { l as verifyRequest } from "../http-DUV8ysti.mjs";
|
|
13
13
|
import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CSYsOMFG.mjs";
|
|
14
|
-
import { t as doesActorOwnKey } from "../owner-
|
|
15
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-
|
|
14
|
+
import { t as doesActorOwnKey } from "../owner-hDxI0ufu.mjs";
|
|
15
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-BPhyR5Oo.mjs";
|
|
16
16
|
import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
|
|
17
17
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
18
18
|
//#region ../../node_modules/.pnpm/@opentelemetry+sdk-metrics@2.7.1_@opentelemetry+api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/AggregationTemporality.js
|