@fedify/fedify 2.3.9-dev.2048 → 2.3.10
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-BfY2wZ4V.mjs → builder-Bal9bfuM.mjs} +10 -2
- package/dist/{client-DYDoDpMa.mjs → client-Ccbsek6u.mjs} +7 -3
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-CAKnQy1x.d.ts → context-BWbdE_MZ.d.ts} +1 -1
- package/dist/{context-BepjCFHN.mjs → context-CxbCIptr.mjs} +2 -1
- package/dist/{context-CSIggG9_.d.cts → context-MOrCC7yl.d.cts} +1 -1
- package/dist/{deno-ayal9buz.mjs → deno-DUgFsCmM.mjs} +1 -1
- package/dist/docloader-w72ipM49.mjs +79 -0
- package/dist/federation/builder.test.mjs +38 -2
- package/dist/federation/circuit-breaker.test.mjs +1 -1
- package/dist/federation/collection.test.mjs +1 -1
- package/dist/federation/handler.test.mjs +7 -7
- package/dist/federation/idempotency.test.mjs +3 -3
- package/dist/federation/kv.test.mjs +1 -1
- package/dist/federation/metrics.test.mjs +2 -2
- package/dist/federation/middleware.test.mjs +302 -8
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +98 -43
- package/dist/federation/negotiation.test.mjs +1 -1
- package/dist/federation/router.test.mjs +1 -1
- package/dist/federation/send.test.mjs +85 -4
- package/dist/federation/temporal.test.mjs +2 -2
- package/dist/federation/webfinger.test.mjs +3 -3
- package/dist/{http-B2u6PvNX.mjs → http-BNDHRNJP.mjs} +8 -4
- package/dist/{http-BPPCh0xN.js → http-BY8PpQzP.js} +6 -2
- package/dist/{http-BGP2QCbe.cjs → http-CTYio50W.cjs} +6 -2
- package/dist/{key-C9DkrWwb.mjs → key-BbuFo4CW.mjs} +2 -2
- package/dist/{kv-D6hNiMTK.d.ts → kv-CZKl1IUS.d.cts} +3 -2
- package/dist/{kv-gJ8LYbxX.d.cts → kv-CxaI4eGo.d.ts} +3 -2
- package/dist/{kv-cache-qJSjfLGm.mjs → kv-cache-CNZOaZYq.mjs} +1 -1
- package/dist/{kv-cache-C1TC_e9y.cjs → kv-cache-CrcqGkT-.cjs} +34 -8
- package/dist/{kv-cache-Cl1q1aA9.js → kv-cache-DNnjnc9t.js} +35 -9
- package/dist/{ld-DNl7KwLY.mjs → ld-BYV2Luge.mjs} +4 -4
- package/dist/{metrics-Ce55disp.mjs → metrics-QrVsKSG9.mjs} +1 -1
- package/dist/{middleware-C96wm_7x.mjs → middleware-54KSGAQA.mjs} +1 -1
- package/dist/{middleware-C_utyVa_.js → middleware-B4FNnN3W.js} +36 -11
- package/dist/{middleware-BCG2BX1n.mjs → middleware-Bj9s3J_0.mjs} +22 -19
- package/dist/{middleware-BZojifnF.cjs → middleware-Dv-L7Plm.cjs} +36 -11
- package/dist/{mod-PVR75MeU.d.cts → mod-B6PwkUMU.d.cts} +1 -1
- package/dist/{mod-DHgc4ygG.d.ts → mod-Br8yJNcE.d.ts} +3 -1
- package/dist/{mod-1TuoXkGs.d.cts → mod-Co1M3Ux0.d.cts} +3 -1
- package/dist/{mod-CFuyfw3c.d.ts → mod-LmD03uRA.d.ts} +1 -1
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +4 -4
- package/dist/mod.d.ts +4 -4
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +50 -2
- package/dist/nodeinfo/handler.test.mjs +3 -3
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/nodeinfo/types.test.mjs +1 -1
- package/dist/otel/exporter.test.mjs +1 -1
- package/dist/otel/mod.d.cts +1 -1
- package/dist/otel/mod.d.ts +1 -1
- package/dist/{owner-DqqDBw-l.mjs → owner-BsBzJADO.mjs} +2 -2
- package/dist/{proof-Dz9ObvUq.js → proof-BxS2Xa8B.js} +2 -2
- package/dist/{proof-I6YcKRRq.cjs → proof-CGKQFT2M.cjs} +2 -2
- package/dist/{proof-D5vP7VIN.mjs → proof-cXVkPBIU.mjs} +3 -3
- package/dist/{send-CBtR8DQK.mjs → send-omt23uTA.mjs} +18 -4
- package/dist/sig/http.test.mjs +43 -3
- package/dist/sig/key.test.mjs +2 -2
- package/dist/sig/ld.test.mjs +5 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +2 -2
- package/dist/sig/proof.test.mjs +2 -2
- package/dist/{std__assert-BBjXFNOb.mjs → std__assert-BvGuO4xO.mjs} +2 -27
- package/dist/{temporal-B9t2tY9A.mjs → temporal-ZUAQe754.mjs} +1 -1
- package/dist/testing/mod.mjs +1 -1
- package/dist/{types-DWs-qZaY.cjs → types-DRPjmHVV.cjs} +7 -3
- package/dist/{types-sXITXcWa.js → types-miAYfiGO.js} +7 -3
- package/dist/utils/docloader.test.mjs +299 -3
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/dist/vocab/cjs.test.mjs +1 -1
- package/package.json +7 -7
- package/dist/docloader-C-JrNL14.mjs +0 -53
|
@@ -3,21 +3,21 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as esm_default } from "../esm-vrlUxr60.mjs";
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { n as assertExists } from "../std__assert-BvGuO4xO.mjs";
|
|
7
7
|
import { r as assertFalse, t as assertRejects } from "../assert_rejects-DN60FHPX.mjs";
|
|
8
8
|
import { n as assertInstanceOf, t as assert } from "../assert-jSdZMYam.mjs";
|
|
9
9
|
import { t as assertThrows } from "../assert_throws-BOkhLGYc.mjs";
|
|
10
10
|
import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
|
|
11
11
|
import { t as assertStrictEquals } from "../assert_strict_equals-XEgZAlrj.mjs";
|
|
12
|
-
import { g as recordInboxActivity } from "../metrics-
|
|
13
|
-
import { l as verifyRequest, s as signRequest } from "../http-
|
|
12
|
+
import { g as recordInboxActivity } from "../metrics-QrVsKSG9.mjs";
|
|
13
|
+
import { l as verifyRequest, s as signRequest } from "../http-BNDHRNJP.mjs";
|
|
14
14
|
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
|
|
15
|
-
import { t as getAuthenticatedDocumentLoader } from "../docloader-
|
|
16
|
-
import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-
|
|
17
|
-
import { t as doesActorOwnKey } from "../owner-
|
|
18
|
-
import { i as verifyObject, r as signObject } from "../proof-
|
|
15
|
+
import { t as getAuthenticatedDocumentLoader } from "../docloader-w72ipM49.mjs";
|
|
16
|
+
import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-BYV2Luge.mjs";
|
|
17
|
+
import { t as doesActorOwnKey } from "../owner-BsBzJADO.mjs";
|
|
18
|
+
import { i as verifyObject, r as signObject } from "../proof-cXVkPBIU.mjs";
|
|
19
19
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
20
|
-
import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-
|
|
20
|
+
import { g as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-Bj9s3J_0.mjs";
|
|
21
21
|
import { t as CircuitBreaker } from "../circuit-breaker-CDABzqVf.mjs";
|
|
22
22
|
import { t as markCircuitBreakerLegacySweepDone } from "../circuit-breaker-test-utils-Dm5MM6l9.mjs";
|
|
23
23
|
import { configure, reset } from "@logtape/logtape";
|
|
@@ -27,6 +27,7 @@ import { Create, Offer, Person, getTypeId, lookupObject } from "@fedify/vocab";
|
|
|
27
27
|
import { SpanStatusCode, metrics } from "@opentelemetry/api";
|
|
28
28
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
29
29
|
import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
30
|
+
import dns from "node:dns/promises";
|
|
30
31
|
import serialize from "json-canon";
|
|
31
32
|
import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
|
|
32
33
|
//#region ../fixture/src/fixtures/example.com/create.json
|
|
@@ -1168,6 +1169,31 @@ test({
|
|
|
1168
1169
|
esm_default.hardReset();
|
|
1169
1170
|
}
|
|
1170
1171
|
});
|
|
1172
|
+
test("Federation.fetch() [rfc9421] multiple POST signatures", async (t) => {
|
|
1173
|
+
for (const validSecond of [true, false]) await t.step(validSecond ? "valid second signature" : "both signatures invalid", async () => {
|
|
1174
|
+
const inbox = [];
|
|
1175
|
+
const federation = createFederation({
|
|
1176
|
+
kv: new MemoryKvStore(),
|
|
1177
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
1178
|
+
contextLoaderFactory: () => mockDocumentLoader
|
|
1179
|
+
});
|
|
1180
|
+
federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => new vocab.Person({ id: ctx.getActorUri(identifier) }));
|
|
1181
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(vocab.Create, (_ctx, activity) => {
|
|
1182
|
+
inbox.push(activity.id.href);
|
|
1183
|
+
});
|
|
1184
|
+
const signed = await signRequest(new Request("https://example.com/inbox", {
|
|
1185
|
+
method: "POST",
|
|
1186
|
+
headers: { "Content-Type": "application/activity+json" },
|
|
1187
|
+
body: JSON.stringify(create_default)
|
|
1188
|
+
}), rsaPrivateKey2, rsaPublicKey2.id, { spec: "rfc9421" });
|
|
1189
|
+
const input = signed.headers.get("Signature-Input");
|
|
1190
|
+
const signature = signed.headers.get("Signature");
|
|
1191
|
+
signed.headers.set("Signature-Input", `${input}, ${input.replace(/^sig1=/, "sig2=")}`);
|
|
1192
|
+
signed.headers.set("Signature", `sig1=:AAAAAA==:, ${validSecond ? signature.replace(/^sig1=/, "sig2=") : "sig2=:AAAAAA==:"}`);
|
|
1193
|
+
assertEquals((await federation.fetch(signed, { contextData: void 0 })).status, validSecond ? 202 : 401);
|
|
1194
|
+
assertEquals(inbox, validSecond ? [id$2] : []);
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1171
1197
|
test("Federation.fetch()", async (t) => {
|
|
1172
1198
|
esm_default.spyGlobal();
|
|
1173
1199
|
esm_default.get("https://example.com/key2", {
|
|
@@ -2655,6 +2681,7 @@ test("FederationImpl.processQueuedTask()", async (t) => {
|
|
|
2655
2681
|
const kv = new MemoryKvStore();
|
|
2656
2682
|
const queuedMessages = [];
|
|
2657
2683
|
const federation = new FederationImpl({
|
|
2684
|
+
allowPrivateAddress: true,
|
|
2658
2685
|
kv,
|
|
2659
2686
|
queue: {
|
|
2660
2687
|
nativeRetrial: true,
|
|
@@ -2717,6 +2744,7 @@ test("FederationImpl.processQueuedTask()", async (t) => {
|
|
|
2717
2744
|
const kv = new MemoryKvStore();
|
|
2718
2745
|
let queuedMessages = [];
|
|
2719
2746
|
const federation = new FederationImpl({
|
|
2747
|
+
allowPrivateAddress: true,
|
|
2720
2748
|
kv,
|
|
2721
2749
|
queue: {
|
|
2722
2750
|
enqueue(message, _options) {
|
|
@@ -4484,6 +4512,157 @@ test("FederationImpl.processQueuedTask()", async (t) => {
|
|
|
4484
4512
|
assertEquals(queuedMessages, []);
|
|
4485
4513
|
});
|
|
4486
4514
|
});
|
|
4515
|
+
test({
|
|
4516
|
+
name: "FederationImpl.processQueuedTask() accounts for destination validation failures",
|
|
4517
|
+
ignore: "Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted",
|
|
4518
|
+
async fn(t) {
|
|
4519
|
+
for (const result of [
|
|
4520
|
+
"throws",
|
|
4521
|
+
"empty",
|
|
4522
|
+
"private"
|
|
4523
|
+
]) for (const failureThreshold of [1, 5]) for (const redirected of [false, true]) await t.step(`${result}, threshold: ${failureThreshold}, redirected: ${redirected}`, async () => {
|
|
4524
|
+
const destination = "https://delivery.invalid/inbox";
|
|
4525
|
+
const publicInbox = "https://8.8.8.8/inbox";
|
|
4526
|
+
const queuedMessages = [];
|
|
4527
|
+
const delays = [];
|
|
4528
|
+
const errors = [];
|
|
4529
|
+
const activities = [];
|
|
4530
|
+
const attempts = [];
|
|
4531
|
+
const delay = Temporal.Duration.from({ seconds: 5 });
|
|
4532
|
+
const kv = new MemoryKvStore();
|
|
4533
|
+
await markCircuitBreakerLegacySweepDone(kv);
|
|
4534
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
4535
|
+
const recoveryDelay = Temporal.Duration.from({ minutes: 30 });
|
|
4536
|
+
const federation = new FederationImpl({
|
|
4537
|
+
kv,
|
|
4538
|
+
meterProvider,
|
|
4539
|
+
circuitBreaker: {
|
|
4540
|
+
failureThreshold,
|
|
4541
|
+
recoveryDelay
|
|
4542
|
+
},
|
|
4543
|
+
queue: {
|
|
4544
|
+
enqueue(message, options) {
|
|
4545
|
+
queuedMessages.push(message);
|
|
4546
|
+
delays.push(options?.delay);
|
|
4547
|
+
return Promise.resolve();
|
|
4548
|
+
},
|
|
4549
|
+
listen() {
|
|
4550
|
+
return Promise.resolve();
|
|
4551
|
+
}
|
|
4552
|
+
},
|
|
4553
|
+
documentLoaderFactory: () => mockDocumentLoader,
|
|
4554
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
4555
|
+
onOutboxError(error, activity) {
|
|
4556
|
+
errors.push(error);
|
|
4557
|
+
activities.push(activity);
|
|
4558
|
+
},
|
|
4559
|
+
outboxRetryPolicy(options) {
|
|
4560
|
+
attempts.push(options.attempts);
|
|
4561
|
+
return delay;
|
|
4562
|
+
}
|
|
4563
|
+
});
|
|
4564
|
+
const message = {
|
|
4565
|
+
type: "outbox",
|
|
4566
|
+
id: crypto.randomUUID(),
|
|
4567
|
+
baseUrl: "https://example.com",
|
|
4568
|
+
keys: [],
|
|
4569
|
+
activity: {
|
|
4570
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
4571
|
+
type: "Create",
|
|
4572
|
+
id: "https://example.com/activities/dns-failure",
|
|
4573
|
+
actor: "https://example.com/users/alice",
|
|
4574
|
+
object: {
|
|
4575
|
+
type: "Note",
|
|
4576
|
+
content: "test"
|
|
4577
|
+
}
|
|
4578
|
+
},
|
|
4579
|
+
activityType: "https://www.w3.org/ns/activitystreams#Create",
|
|
4580
|
+
inbox: redirected ? publicInbox : destination,
|
|
4581
|
+
sharedInbox: false,
|
|
4582
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4583
|
+
attempt: 0,
|
|
4584
|
+
headers: {},
|
|
4585
|
+
traceContext: {}
|
|
4586
|
+
};
|
|
4587
|
+
const originalLookup = dns.lookup;
|
|
4588
|
+
const resolverError = /* @__PURE__ */ new Error("Resolver unavailable");
|
|
4589
|
+
const lookups = [];
|
|
4590
|
+
dns.lookup = ((hostname) => {
|
|
4591
|
+
lookups.push(hostname);
|
|
4592
|
+
assertEquals(hostname, "delivery.invalid");
|
|
4593
|
+
if (result === "throws") return Promise.reject(resolverError);
|
|
4594
|
+
return Promise.resolve(result === "empty" ? [] : [{
|
|
4595
|
+
address: "127.0.0.1",
|
|
4596
|
+
family: 4
|
|
4597
|
+
}]);
|
|
4598
|
+
});
|
|
4599
|
+
try {
|
|
4600
|
+
esm_default.mockGlobal().catch(202);
|
|
4601
|
+
if (redirected) esm_default.route(publicInbox, {
|
|
4602
|
+
status: 307,
|
|
4603
|
+
headers: { Location: destination }
|
|
4604
|
+
});
|
|
4605
|
+
await federation.processQueuedTask(void 0, message);
|
|
4606
|
+
assertEquals(errors.length, 1);
|
|
4607
|
+
const error = errors[0];
|
|
4608
|
+
if (result === "private") {
|
|
4609
|
+
assertInstanceOf(error, UrlError);
|
|
4610
|
+
assertEquals(error.reason, "disallowed");
|
|
4611
|
+
} else {
|
|
4612
|
+
assertInstanceOf(error, FetchError);
|
|
4613
|
+
assertEquals(error.url.href, destination);
|
|
4614
|
+
assertInstanceOf(error.cause, UrlError);
|
|
4615
|
+
assertEquals(error.cause.reason, "dns");
|
|
4616
|
+
assertStrictEquals(error.cause.cause, result === "throws" ? resolverError : void 0);
|
|
4617
|
+
}
|
|
4618
|
+
assertEquals(activities.length, 1);
|
|
4619
|
+
assertInstanceOf(activities[0], Create);
|
|
4620
|
+
assertEquals(activities[0].id?.href, "https://example.com/activities/dns-failure");
|
|
4621
|
+
assertEquals(attempts, [0]);
|
|
4622
|
+
const host = new URL(message.inbox).host;
|
|
4623
|
+
const state = await kv.get([
|
|
4624
|
+
"_fedify",
|
|
4625
|
+
"circuit",
|
|
4626
|
+
host
|
|
4627
|
+
]);
|
|
4628
|
+
if (result === "private") assertEquals(state, void 0);
|
|
4629
|
+
else {
|
|
4630
|
+
assertExists(state);
|
|
4631
|
+
assertEquals(state.failures.length, 1);
|
|
4632
|
+
assertEquals(state.state, failureThreshold === 1 ? "open" : "closed");
|
|
4633
|
+
}
|
|
4634
|
+
if (result !== "private" && failureThreshold === 1) {
|
|
4635
|
+
assertEquals(queuedMessages.length, 1);
|
|
4636
|
+
const held = queuedMessages[0];
|
|
4637
|
+
assertEquals(held.attempt, 0);
|
|
4638
|
+
assertEquals(held.circuitHeld, true);
|
|
4639
|
+
assertExists(held.circuitHeldSince);
|
|
4640
|
+
assertEquals(delays, [recoveryDelay]);
|
|
4641
|
+
} else {
|
|
4642
|
+
assertEquals(queuedMessages, [{
|
|
4643
|
+
...message,
|
|
4644
|
+
attempt: 1
|
|
4645
|
+
}]);
|
|
4646
|
+
assertEquals(delays, [delay]);
|
|
4647
|
+
}
|
|
4648
|
+
const expectedCount = result === "private" && !redirected ? 0 : 1;
|
|
4649
|
+
const sent = recorder.getMeasurements("activitypub.delivery.sent");
|
|
4650
|
+
assertEquals(sent.length, expectedCount);
|
|
4651
|
+
if (expectedCount === 1) {
|
|
4652
|
+
assertEquals(sent[0].value, 1);
|
|
4653
|
+
assertEquals(sent[0].attributes["activitypub.delivery.success"], false);
|
|
4654
|
+
assertEquals(sent[0].attributes["activitypub.remote.host"], host);
|
|
4655
|
+
}
|
|
4656
|
+
assertEquals(lookups, ["delivery.invalid"]);
|
|
4657
|
+
assertEquals(esm_default.callHistory.calls(destination).length, 0);
|
|
4658
|
+
assertEquals(esm_default.callHistory.calls().length, redirected ? 1 : 0);
|
|
4659
|
+
} finally {
|
|
4660
|
+
dns.lookup = originalLookup;
|
|
4661
|
+
esm_default.hardReset();
|
|
4662
|
+
}
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
});
|
|
4487
4666
|
test("FederationImpl.processQueuedTask() permanent failure", async (t) => {
|
|
4488
4667
|
esm_default.spyGlobal();
|
|
4489
4668
|
esm_default.post("https://gone.example/inbox", {
|
|
@@ -6656,6 +6835,80 @@ test({
|
|
|
6656
6835
|
assertEquals(rejected[1].attributes["activitypub.activity.type"], "https://www.w3.org/ns/activitystreams#Offer");
|
|
6657
6836
|
}
|
|
6658
6837
|
});
|
|
6838
|
+
for (const queued of [false, true]) test(`ContextImpl.routeActivity() routes the fetched document (queued: ${queued})`, async () => {
|
|
6839
|
+
const id = new URL("https://example.com/verified-create");
|
|
6840
|
+
const genuine = new Create({
|
|
6841
|
+
id,
|
|
6842
|
+
actor: new URL("https://example.com/person"),
|
|
6843
|
+
object: new vocab.Note({ content: "Genuine content" }),
|
|
6844
|
+
to: new URL("https://example.com/recipient")
|
|
6845
|
+
});
|
|
6846
|
+
const genuineJson = await genuine.toJsonLd({ contextLoader: mockDocumentLoader });
|
|
6847
|
+
const forged = new Create({
|
|
6848
|
+
id,
|
|
6849
|
+
actor: new URL("https://victim.example/actor"),
|
|
6850
|
+
object: new vocab.Note({ content: "Forged content" }),
|
|
6851
|
+
to: new URL("https://attacker.example/recipient")
|
|
6852
|
+
});
|
|
6853
|
+
const messages = [];
|
|
6854
|
+
const queue = {
|
|
6855
|
+
enqueue(message) {
|
|
6856
|
+
messages.push(message);
|
|
6857
|
+
return Promise.resolve();
|
|
6858
|
+
},
|
|
6859
|
+
async listen() {}
|
|
6860
|
+
};
|
|
6861
|
+
const federation = createFederation({
|
|
6862
|
+
kv: new MemoryKvStore(),
|
|
6863
|
+
documentLoaderFactory: () => async (url, options) => url === id.href ? {
|
|
6864
|
+
document: genuineJson,
|
|
6865
|
+
documentUrl: id.href,
|
|
6866
|
+
contextUrl: null
|
|
6867
|
+
} : await mockDocumentLoader(url, options),
|
|
6868
|
+
contextLoaderFactory: () => mockDocumentLoader,
|
|
6869
|
+
queue: {
|
|
6870
|
+
inbox: queued ? queue : void 0,
|
|
6871
|
+
outbox: queue
|
|
6872
|
+
},
|
|
6873
|
+
manuallyStartQueue: true
|
|
6874
|
+
});
|
|
6875
|
+
const received = [];
|
|
6876
|
+
const contextDocuments = [];
|
|
6877
|
+
federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, async (ctx, activity) => {
|
|
6878
|
+
received.push(await activity.toJsonLd({ contextLoader: mockDocumentLoader }));
|
|
6879
|
+
assertInstanceOf(ctx, InboxContextImpl);
|
|
6880
|
+
contextDocuments.push(ctx.activity);
|
|
6881
|
+
await ctx.forwardActivity({
|
|
6882
|
+
privateKey: ed25519PrivateKey,
|
|
6883
|
+
keyId: ed25519Multikey.id
|
|
6884
|
+
}, {
|
|
6885
|
+
id: new URL("https://example.com/recipient"),
|
|
6886
|
+
inboxId: new URL("https://example.com/inbox")
|
|
6887
|
+
});
|
|
6888
|
+
});
|
|
6889
|
+
const ctx = federation.createContext(new URL("https://local.example/"));
|
|
6890
|
+
async function processInbox() {
|
|
6891
|
+
const inboxMessages = messages.filter((message) => message.type === "inbox");
|
|
6892
|
+
for (const message of inboxMessages) await federation.processQueuedTask(void 0, message);
|
|
6893
|
+
}
|
|
6894
|
+
assert(await ctx.routeActivity(null, forged));
|
|
6895
|
+
if (queued) {
|
|
6896
|
+
assertEquals(received, []);
|
|
6897
|
+
assertEquals(messages.length, 1);
|
|
6898
|
+
assertEquals(messages[0].activity, genuineJson);
|
|
6899
|
+
await processInbox();
|
|
6900
|
+
}
|
|
6901
|
+
assertEquals(received, [genuineJson]);
|
|
6902
|
+
assertEquals(contextDocuments, [genuineJson]);
|
|
6903
|
+
const forwarded = messages.filter((message) => message.type === "outbox");
|
|
6904
|
+
assertEquals(forwarded.length, 1);
|
|
6905
|
+
assertEquals(forwarded[0].activity, genuineJson);
|
|
6906
|
+
assert(await ctx.routeActivity(null, genuine));
|
|
6907
|
+
if (queued) await processInbox();
|
|
6908
|
+
assertEquals(received, [genuineJson]);
|
|
6909
|
+
assertEquals(contextDocuments, [genuineJson]);
|
|
6910
|
+
assertEquals(messages.filter((message) => message.type === "outbox").length, 1);
|
|
6911
|
+
});
|
|
6659
6912
|
test("ContextImpl.routeActivity() marks queued signed activities as non-LDS", async () => {
|
|
6660
6913
|
let queuedMessage = null;
|
|
6661
6914
|
const federation = new FederationImpl({
|
|
@@ -6715,6 +6968,47 @@ test("ContextImpl.getCollectionUri()", () => {
|
|
|
6715
6968
|
assertThrows(() => ctx.getCollectionUri(Symbol(notReg), values));
|
|
6716
6969
|
assertThrows(() => ctx.getCollectionUri(Symbol.for(notReg), values));
|
|
6717
6970
|
});
|
|
6971
|
+
test("symbol-named custom collections are served and parsed", async () => {
|
|
6972
|
+
const federation = createFederation({ kv: new MemoryKvStore() });
|
|
6973
|
+
const names = [Symbol("bookmarks"), Symbol("bookmarks")];
|
|
6974
|
+
const paths = ["bookmarks", "ordered-bookmarks"];
|
|
6975
|
+
const calls = [];
|
|
6976
|
+
federation.setCollectionDispatcher(names[0], vocab.Object, "/users/{identifier}/bookmarks", (_ctx, _values, cursor) => {
|
|
6977
|
+
calls.push([0, cursor]);
|
|
6978
|
+
return { items: [] };
|
|
6979
|
+
});
|
|
6980
|
+
federation.setOrderedCollectionDispatcher(names[1], vocab.Object, "/users/{identifier}/ordered-bookmarks", (_ctx, _values, cursor) => {
|
|
6981
|
+
calls.push([1, cursor]);
|
|
6982
|
+
return { items: [] };
|
|
6983
|
+
});
|
|
6984
|
+
const ctx = federation.createContext(new URL("https://example.com/"));
|
|
6985
|
+
for (const [index, name] of names.entries()) {
|
|
6986
|
+
const uri = ctx.getCollectionUri(name, { identifier: "alice" });
|
|
6987
|
+
assertEquals(uri.pathname, `/users/alice/${paths[index]}`);
|
|
6988
|
+
const parsed = ctx.parseUri(uri);
|
|
6989
|
+
assertEquals(parsed?.type, index === 0 ? "collection" : "orderedCollection");
|
|
6990
|
+
if (parsed?.type !== "collection" && parsed?.type !== "orderedCollection") throw new Error("Expected a custom collection URI");
|
|
6991
|
+
assertStrictEquals(parsed.name, name);
|
|
6992
|
+
assertStrictEquals(parsed.class, vocab.Object);
|
|
6993
|
+
assertEquals(parsed.typeId, vocab.Object.typeId);
|
|
6994
|
+
assertEquals(parsed.values, { identifier: "alice" });
|
|
6995
|
+
for (const cursor of [null, "next"]) {
|
|
6996
|
+
const pageUri = new URL(uri);
|
|
6997
|
+
if (cursor != null) pageUri.searchParams.set("cursor", cursor);
|
|
6998
|
+
const response = await federation.fetch(new Request(pageUri, { headers: { accept: "application/activity+json" } }), { contextData: void 0 });
|
|
6999
|
+
assertEquals(response.status, 200);
|
|
7000
|
+
const body = await response.json();
|
|
7001
|
+
assert(body !== null && typeof body === "object" && "type" in body);
|
|
7002
|
+
assertEquals(body.type, `${index === 0 ? "Collection" : "OrderedCollection"}${cursor == null ? "" : "Page"}`);
|
|
7003
|
+
}
|
|
7004
|
+
}
|
|
7005
|
+
assertEquals(calls, [
|
|
7006
|
+
[0, null],
|
|
7007
|
+
[0, "next"],
|
|
7008
|
+
[1, null],
|
|
7009
|
+
[1, "next"]
|
|
7010
|
+
]);
|
|
7011
|
+
});
|
|
6718
7012
|
test("InboxContextImpl.forwardActivity()", async (t) => {
|
|
6719
7013
|
esm_default.spyGlobal();
|
|
6720
7014
|
let verified = null;
|
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-Dv-L7Plm.cjs");
|
|
6
6
|
let _logtape_logtape = require("@logtape/logtape");
|
|
7
7
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
8
8
|
let es_toolkit = require("es-toolkit");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { $ as CustomCollectionCursor, A as Rfc6570Expression, At as parseCircuitBreakerKvState, B as createFederationBuilder, C as FederationStartQueueOptions, Ct as CircuitBreakerFailurePolicy, D as InboxListenerSetters, Dt as CircuitBreakerStateChange, E as InboxChallengePolicy, Et as CircuitBreakerState, F as CreateExponentialBackoffPolicyOptions, G as ActorDispatcher, H as respondWithObject, I as RetryContext, J as AuthorizePredicate, K as ActorHandleMapper, L as RetryPolicy, M as FederationOrigin, N as FederationQueueOptions, O as ObjectCallbackSetters, Ot as NormalizedCircuitBreakerOptions, P as createFederation, Q as CustomCollectionCounter, R as createExponentialBackoffPolicy, S as FederationOptions, St as CircuitBreakerCreateOptions, T as IdempotencyStrategy, Tt as CircuitBreakerOptions, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as ActorAliasMapper, X as CollectionCursor, Y as CollectionCounter, Z as CollectionDispatcher, _ as Federatable, _t as buildCollectionSynchronizationHeader, a as GetSignedKeyOptions, at as ObjectDispatcher, b as FederationBuilder, bt as CircuitBreakerActivityDrop, c as ParseUriResult, ct as OutboxListenerErrorHandler, d as SendActivityOptions, dt as UnverifiedActivityHandler, et as CustomCollectionDispatcher, f as SendActivityOptionsForCollection, ft as UnverifiedActivityReason, g as CustomCollectionCallbackSetters, gt as PageItems, h as ConstructorWithTypeId, ht as SenderKeyPair, i as GetActorOptions, it as ObjectAuthorizePredicate, j as FederationKvPrefixes, k as OutboxListenerSetters, kt as normalizeCircuitBreakerOptions, l as RequestContext, lt as OutboxPermanentFailureHandler, m as CollectionCallbackSetters, mt as SendActivityError, n as Context, nt as InboxListener, o as InboxContext, ot as OutboxErrorHandler, p as ActorCallbackSetters, pt as WebFingerLinksDispatcher, q as ActorKeyPairsDispatcher, r as ForwardActivityOptions, rt as NodeInfoDispatcher, s as OutboxContext, st as OutboxListener, t as ActorKeyPair, tt as InboxErrorHandler, u as RouteActivityOptions, ut as SharedInboxKeyDispatcher, v as Federation, vt as digest, w as IdempotencyKeyCallback, wt as CircuitBreakerKvState, x as FederationFetchOptions, xt as CircuitBreakerBeforeSendDecision, y as FederationBenchmarkOptions, yt as CircuitBreaker, z as Message } from "../context-
|
|
3
|
-
import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "../kv-
|
|
2
|
+
import { $ as CustomCollectionCursor, A as Rfc6570Expression, At as parseCircuitBreakerKvState, B as createFederationBuilder, C as FederationStartQueueOptions, Ct as CircuitBreakerFailurePolicy, D as InboxListenerSetters, Dt as CircuitBreakerStateChange, E as InboxChallengePolicy, Et as CircuitBreakerState, F as CreateExponentialBackoffPolicyOptions, G as ActorDispatcher, H as respondWithObject, I as RetryContext, J as AuthorizePredicate, K as ActorHandleMapper, L as RetryPolicy, M as FederationOrigin, N as FederationQueueOptions, O as ObjectCallbackSetters, Ot as NormalizedCircuitBreakerOptions, P as createFederation, Q as CustomCollectionCounter, R as createExponentialBackoffPolicy, S as FederationOptions, St as CircuitBreakerCreateOptions, T as IdempotencyStrategy, Tt as CircuitBreakerOptions, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as ActorAliasMapper, X as CollectionCursor, Y as CollectionCounter, Z as CollectionDispatcher, _ as Federatable, _t as buildCollectionSynchronizationHeader, a as GetSignedKeyOptions, at as ObjectDispatcher, b as FederationBuilder, bt as CircuitBreakerActivityDrop, c as ParseUriResult, ct as OutboxListenerErrorHandler, d as SendActivityOptions, dt as UnverifiedActivityHandler, et as CustomCollectionDispatcher, f as SendActivityOptionsForCollection, ft as UnverifiedActivityReason, g as CustomCollectionCallbackSetters, gt as PageItems, h as ConstructorWithTypeId, ht as SenderKeyPair, i as GetActorOptions, it as ObjectAuthorizePredicate, j as FederationKvPrefixes, k as OutboxListenerSetters, kt as normalizeCircuitBreakerOptions, l as RequestContext, lt as OutboxPermanentFailureHandler, m as CollectionCallbackSetters, mt as SendActivityError, n as Context, nt as InboxListener, o as InboxContext, ot as OutboxErrorHandler, p as ActorCallbackSetters, pt as WebFingerLinksDispatcher, q as ActorKeyPairsDispatcher, r as ForwardActivityOptions, rt as NodeInfoDispatcher, s as OutboxContext, st as OutboxListener, t as ActorKeyPair, tt as InboxErrorHandler, u as RouteActivityOptions, ut as SharedInboxKeyDispatcher, v as Federation, vt as digest, w as IdempotencyKeyCallback, wt as CircuitBreakerKvState, x as FederationFetchOptions, xt as CircuitBreakerBeforeSendDecision, y as FederationBenchmarkOptions, yt as CircuitBreaker, z as Message } from "../context-MOrCC7yl.cjs";
|
|
3
|
+
import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "../kv-CZKl1IUS.cjs";
|
|
4
4
|
import { a as MessageQueueEnqueueOptions, i as MessageQueueDepth, n as InProcessMessageQueueOptions, o as MessageQueueListenOptions, r as MessageQueue, s as ParallelMessageQueue, t as InProcessMessageQueue } from "../mq-D8uSFzxe.cjs";
|
|
5
|
-
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "../mod-
|
|
5
|
+
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "../mod-B6PwkUMU.cjs";
|
|
6
6
|
export { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CircuitBreaker, CircuitBreakerActivityDrop, CircuitBreakerBeforeSendDecision, CircuitBreakerCreateOptions, CircuitBreakerFailurePolicy, CircuitBreakerKvState, CircuitBreakerOptions, CircuitBreakerState, CircuitBreakerStateChange, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBenchmarkOptions, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetActorOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxChallengePolicy, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, MemoryKvStore, Message, MessageQueue, MessageQueueDepth, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, NormalizedCircuitBreakerOptions, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxContext, OutboxErrorHandler, OutboxListener, OutboxListenerErrorHandler, OutboxListenerSetters, OutboxPermanentFailureHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityError, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, UnverifiedActivityHandler, UnverifiedActivityReason, WebFingerHandlerParameters, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, normalizeCircuitBreakerOptions, parseCircuitBreakerKvState, respondWithObject, respondWithObjectIfAcceptable };
|
package/dist/federation/mod.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { $ as CustomCollectionCursor, A as Rfc6570Expression, At as parseCircuitBreakerKvState, B as createFederationBuilder, C as FederationStartQueueOptions, Ct as CircuitBreakerFailurePolicy, D as InboxListenerSetters, Dt as CircuitBreakerStateChange, E as InboxChallengePolicy, Et as CircuitBreakerState, F as CreateExponentialBackoffPolicyOptions, G as ActorDispatcher, H as respondWithObject, I as RetryContext, J as AuthorizePredicate, K as ActorHandleMapper, L as RetryPolicy, M as FederationOrigin, N as FederationQueueOptions, O as ObjectCallbackSetters, Ot as NormalizedCircuitBreakerOptions, P as createFederation, Q as CustomCollectionCounter, R as createExponentialBackoffPolicy, S as FederationOptions, St as CircuitBreakerCreateOptions, T as IdempotencyStrategy, Tt as CircuitBreakerOptions, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as ActorAliasMapper, X as CollectionCursor, Y as CollectionCounter, Z as CollectionDispatcher, _ as Federatable, _t as buildCollectionSynchronizationHeader, a as GetSignedKeyOptions, at as ObjectDispatcher, b as FederationBuilder, bt as CircuitBreakerActivityDrop, c as ParseUriResult, ct as OutboxListenerErrorHandler, d as SendActivityOptions, dt as UnverifiedActivityHandler, et as CustomCollectionDispatcher, f as SendActivityOptionsForCollection, ft as UnverifiedActivityReason, g as CustomCollectionCallbackSetters, gt as PageItems, h as ConstructorWithTypeId, ht as SenderKeyPair, i as GetActorOptions, it as ObjectAuthorizePredicate, j as FederationKvPrefixes, k as OutboxListenerSetters, kt as normalizeCircuitBreakerOptions, l as RequestContext, lt as OutboxPermanentFailureHandler, m as CollectionCallbackSetters, mt as SendActivityError, n as Context, nt as InboxListener, o as InboxContext, ot as OutboxErrorHandler, p as ActorCallbackSetters, pt as WebFingerLinksDispatcher, q as ActorKeyPairsDispatcher, r as ForwardActivityOptions, rt as NodeInfoDispatcher, s as OutboxContext, st as OutboxListener, t as ActorKeyPair, tt as InboxErrorHandler, u as RouteActivityOptions, ut as SharedInboxKeyDispatcher, v as Federation, vt as digest, w as IdempotencyKeyCallback, wt as CircuitBreakerKvState, x as FederationFetchOptions, xt as CircuitBreakerBeforeSendDecision, y as FederationBenchmarkOptions, yt as CircuitBreaker, z as Message } from "../context-
|
|
3
|
-
import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "../kv-
|
|
2
|
+
import { $ as CustomCollectionCursor, A as Rfc6570Expression, At as parseCircuitBreakerKvState, B as createFederationBuilder, C as FederationStartQueueOptions, Ct as CircuitBreakerFailurePolicy, D as InboxListenerSetters, Dt as CircuitBreakerStateChange, E as InboxChallengePolicy, Et as CircuitBreakerState, F as CreateExponentialBackoffPolicyOptions, G as ActorDispatcher, H as respondWithObject, I as RetryContext, J as AuthorizePredicate, K as ActorHandleMapper, L as RetryPolicy, M as FederationOrigin, N as FederationQueueOptions, O as ObjectCallbackSetters, Ot as NormalizedCircuitBreakerOptions, P as createFederation, Q as CustomCollectionCounter, R as createExponentialBackoffPolicy, S as FederationOptions, St as CircuitBreakerCreateOptions, T as IdempotencyStrategy, Tt as CircuitBreakerOptions, U as respondWithObjectIfAcceptable, V as RespondWithObjectOptions, W as ActorAliasMapper, X as CollectionCursor, Y as CollectionCounter, Z as CollectionDispatcher, _ as Federatable, _t as buildCollectionSynchronizationHeader, a as GetSignedKeyOptions, at as ObjectDispatcher, b as FederationBuilder, bt as CircuitBreakerActivityDrop, c as ParseUriResult, ct as OutboxListenerErrorHandler, d as SendActivityOptions, dt as UnverifiedActivityHandler, et as CustomCollectionDispatcher, f as SendActivityOptionsForCollection, ft as UnverifiedActivityReason, g as CustomCollectionCallbackSetters, gt as PageItems, h as ConstructorWithTypeId, ht as SenderKeyPair, i as GetActorOptions, it as ObjectAuthorizePredicate, j as FederationKvPrefixes, k as OutboxListenerSetters, kt as normalizeCircuitBreakerOptions, l as RequestContext, lt as OutboxPermanentFailureHandler, m as CollectionCallbackSetters, mt as SendActivityError, n as Context, nt as InboxListener, o as InboxContext, ot as OutboxErrorHandler, p as ActorCallbackSetters, pt as WebFingerLinksDispatcher, q as ActorKeyPairsDispatcher, r as ForwardActivityOptions, rt as NodeInfoDispatcher, s as OutboxContext, st as OutboxListener, t as ActorKeyPair, tt as InboxErrorHandler, u as RouteActivityOptions, ut as SharedInboxKeyDispatcher, v as Federation, vt as digest, w as IdempotencyKeyCallback, wt as CircuitBreakerKvState, x as FederationFetchOptions, xt as CircuitBreakerBeforeSendDecision, y as FederationBenchmarkOptions, yt as CircuitBreaker, z as Message } from "../context-BWbdE_MZ.js";
|
|
3
|
+
import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "../kv-CxaI4eGo.js";
|
|
4
4
|
import { a as MessageQueueEnqueueOptions, i as MessageQueueDepth, n as InProcessMessageQueueOptions, o as MessageQueueListenOptions, r as MessageQueue, s as ParallelMessageQueue, t as InProcessMessageQueue } from "../mq-D-nlpY04.js";
|
|
5
|
-
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "../mod-
|
|
5
|
+
import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "../mod-LmD03uRA.js";
|
|
6
6
|
export { ActorAliasMapper, ActorCallbackSetters, ActorDispatcher, ActorHandleMapper, ActorKeyPair, ActorKeyPairsDispatcher, AuthorizePredicate, CircuitBreaker, CircuitBreakerActivityDrop, CircuitBreakerBeforeSendDecision, CircuitBreakerCreateOptions, CircuitBreakerFailurePolicy, CircuitBreakerKvState, CircuitBreakerOptions, CircuitBreakerState, CircuitBreakerStateChange, CollectionCallbackSetters, CollectionCounter, CollectionCursor, CollectionDispatcher, ConstructorWithTypeId, Context, CreateExponentialBackoffPolicyOptions, CustomCollectionCallbackSetters, CustomCollectionCounter, CustomCollectionCursor, CustomCollectionDispatcher, Federatable, Federation, FederationBenchmarkOptions, FederationBuilder, FederationFetchOptions, FederationKvPrefixes, FederationOptions, FederationOrigin, FederationQueueOptions, FederationStartQueueOptions, ForwardActivityOptions, GetActorOptions, GetSignedKeyOptions, IdempotencyKeyCallback, IdempotencyStrategy, InProcessMessageQueue, InProcessMessageQueueOptions, InboxChallengePolicy, InboxContext, InboxErrorHandler, InboxListener, InboxListenerSetters, KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions, MemoryKvStore, Message, MessageQueue, MessageQueueDepth, MessageQueueEnqueueOptions, MessageQueueListenOptions, NodeInfoDispatcher, NormalizedCircuitBreakerOptions, ObjectAuthorizePredicate, ObjectCallbackSetters, ObjectDispatcher, OutboxContext, OutboxErrorHandler, OutboxListener, OutboxListenerErrorHandler, OutboxListenerSetters, OutboxPermanentFailureHandler, PageItems, ParallelMessageQueue, ParseUriResult, RequestContext, RespondWithObjectOptions, RetryContext, RetryPolicy, Rfc6570Expression, RouteActivityOptions, Router, RouterError, RouterOptions, RouterRouteResult, SendActivityError, SendActivityOptions, SendActivityOptionsForCollection, SenderKeyPair, SharedInboxKeyDispatcher, UnverifiedActivityHandler, UnverifiedActivityReason, WebFingerHandlerParameters, WebFingerLinksDispatcher, buildCollectionSynchronizationHeader, createExponentialBackoffPolicy, createFederation, createFederationBuilder, digest, handleWebFinger, normalizeCircuitBreakerOptions, parseCircuitBreakerKvState, respondWithObject, respondWithObjectIfAcceptable };
|
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 SendActivityError, c as buildCollectionSynchronizationHeader, d as normalizeCircuitBreakerOptions, f as parseCircuitBreakerKvState, i as createExponentialBackoffPolicy, l as digest, o as respondWithObject, p as createFederationBuilder, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as CircuitBreaker } from "../middleware-
|
|
3
|
+
import { a as SendActivityError, c as buildCollectionSynchronizationHeader, d as normalizeCircuitBreakerOptions, f as parseCircuitBreakerKvState, i as createExponentialBackoffPolicy, l as digest, o as respondWithObject, p as createFederationBuilder, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as CircuitBreaker } from "../middleware-B4FNnN3W.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { Router as Router$1, RouterError as RouterError$1, assertPath, isPath } from "@fedify/uri-template";
|
|
6
6
|
import { isEqual } from "es-toolkit";
|
|
@@ -2,7 +2,7 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
-
import
|
|
5
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
6
6
|
import { n as assertGreaterOrEqual, r as assertFalse } from "../assert_rejects-DN60FHPX.mjs";
|
|
7
7
|
import { t as assert } from "../assert-jSdZMYam.mjs";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
@@ -294,50 +294,105 @@ test("InProcessMessageQueue", async (t) => {
|
|
|
294
294
|
const listening = mq.listen((message) => {
|
|
295
295
|
messages.push(message);
|
|
296
296
|
}, controller);
|
|
297
|
-
|
|
298
|
-
await
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
297
|
+
try {
|
|
298
|
+
await t.step("enqueue()", async () => {
|
|
299
|
+
await mq.enqueue("Hello, world!");
|
|
300
|
+
});
|
|
301
|
+
await waitFor(() => messages.length > 0, 15e3);
|
|
302
|
+
await t.step("listen()", () => {
|
|
303
|
+
assertEquals(messages, ["Hello, world!"]);
|
|
304
|
+
});
|
|
305
|
+
await t.step("enqueue() with delay", async () => {
|
|
306
|
+
const { timers, enqueued } = captureTimers(() => mq.enqueue("Delayed message", { delay: Temporal.Duration.from({ seconds: 3 }) }));
|
|
307
|
+
await enqueued;
|
|
308
|
+
assertEquals(timers.length, 1);
|
|
309
|
+
assertEquals(timers[0].delay, 3e3);
|
|
310
|
+
await mq.enqueue("Single delay marker");
|
|
311
|
+
await waitFor(() => messages.includes("Single delay marker"), 15e3);
|
|
312
|
+
assertEquals(messages, ["Hello, world!", "Single delay marker"]);
|
|
313
|
+
timers[0].callback();
|
|
314
|
+
});
|
|
315
|
+
await waitFor(() => messages.length >= 3, 15e3);
|
|
316
|
+
await t.step("listen() with delay", () => {
|
|
317
|
+
assertEquals(messages, [
|
|
318
|
+
"Hello, world!",
|
|
319
|
+
"Single delay marker",
|
|
320
|
+
"Delayed message"
|
|
321
|
+
]);
|
|
322
|
+
});
|
|
323
|
+
while (messages.length > 0) messages.pop();
|
|
324
|
+
await t.step("enqueueMany()", async () => {
|
|
325
|
+
const testMessages = Array.from({ length: 5 }, (_, i) => `Batch message ${i}!`);
|
|
326
|
+
await mq.enqueueMany(testMessages);
|
|
327
|
+
});
|
|
328
|
+
await waitFor(() => messages.length >= 5, 15e3);
|
|
329
|
+
await t.step("listen() [multiple]", () => {
|
|
330
|
+
assertEquals(messages.length, 5);
|
|
331
|
+
for (let i = 0; i < 5; i++) assertEquals(messages[i], `Batch message ${i}!`);
|
|
332
|
+
});
|
|
333
|
+
while (messages.length > 0) messages.pop();
|
|
334
|
+
await t.step("enqueueMany() with delay", async () => {
|
|
335
|
+
const testMessages = Array.from({ length: 3 }, (_, i) => `Delayed batch ${i}!`);
|
|
336
|
+
const { timers, enqueued } = captureTimers(() => mq.enqueueMany(testMessages, { delay: Temporal.Duration.from({ seconds: 2 }) }));
|
|
337
|
+
await enqueued;
|
|
338
|
+
assertEquals(timers.length, 1);
|
|
339
|
+
assertEquals(timers[0].delay, 2e3);
|
|
340
|
+
await mq.enqueue("Batch delay marker");
|
|
341
|
+
await waitFor(() => messages.includes("Batch delay marker"), 15e3);
|
|
342
|
+
assertEquals(messages, ["Batch delay marker"]);
|
|
343
|
+
timers[0].callback();
|
|
344
|
+
});
|
|
345
|
+
await waitFor(() => messages.length >= 4, 15e3);
|
|
346
|
+
await t.step("listen() [delayed multiple]", () => {
|
|
347
|
+
assertEquals(messages.length, 4);
|
|
348
|
+
assertEquals(messages[0], "Batch delay marker");
|
|
349
|
+
for (let i = 0; i < 3; i++) assertEquals(messages[i + 1], `Delayed batch ${i}!`);
|
|
350
|
+
});
|
|
351
|
+
} finally {
|
|
352
|
+
controller.abort();
|
|
353
|
+
await listening;
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
function captureTimers(enqueue) {
|
|
357
|
+
const timers = [];
|
|
358
|
+
const original = globalThis.setTimeout;
|
|
359
|
+
globalThis.setTimeout = ((callback, delay) => {
|
|
360
|
+
timers.push({
|
|
361
|
+
callback,
|
|
362
|
+
delay
|
|
363
|
+
});
|
|
364
|
+
return 0;
|
|
332
365
|
});
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
366
|
+
try {
|
|
367
|
+
return {
|
|
368
|
+
timers,
|
|
369
|
+
enqueued: enqueue()
|
|
370
|
+
};
|
|
371
|
+
} finally {
|
|
372
|
+
globalThis.setTimeout = original;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
test("InProcessMessageQueue real delay", async (t) => {
|
|
376
|
+
for (const batch of [false, true]) await t.step(batch ? "enqueueMany()" : "enqueue()", async () => {
|
|
377
|
+
const mq = new InProcessMessageQueue();
|
|
378
|
+
const messages = [];
|
|
379
|
+
const controller = new AbortController();
|
|
380
|
+
const listening = mq.listen((message) => {
|
|
381
|
+
messages.push(message);
|
|
382
|
+
}, controller);
|
|
383
|
+
try {
|
|
384
|
+
const delayed = batch ? ["Delayed 1", "Delayed 2"] : ["Delayed"];
|
|
385
|
+
const options = { delay: Temporal.Duration.from({ milliseconds: 100 }) };
|
|
386
|
+
if (batch) await mq.enqueueMany(delayed, options);
|
|
387
|
+
else await mq.enqueue(delayed[0], options);
|
|
388
|
+
await mq.enqueue("Immediate");
|
|
389
|
+
await waitFor(() => messages.length >= delayed.length + 1, 15e3);
|
|
390
|
+
assertEquals(messages, ["Immediate", ...delayed]);
|
|
391
|
+
} finally {
|
|
392
|
+
controller.abort();
|
|
393
|
+
await listening;
|
|
394
|
+
}
|
|
338
395
|
});
|
|
339
|
-
controller.abort();
|
|
340
|
-
await listening;
|
|
341
396
|
});
|
|
342
397
|
test("InProcessMessageQueue.getDepth()", async () => {
|
|
343
398
|
const mq = new InProcessMessageQueue();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import "../std__assert-
|
|
4
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
5
5
|
import { r as assertFalse } from "../assert_rejects-DN60FHPX.mjs";
|
|
6
6
|
import { t as assert } from "../assert-jSdZMYam.mjs";
|
|
7
7
|
import { t as acceptsJsonLd } from "../negotiation-CDW-_gUU.mjs";
|
|
@@ -2,7 +2,7 @@ import "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
6
6
|
import { r as assertFalse } from "../assert_rejects-DN60FHPX.mjs";
|
|
7
7
|
import { t as assert } from "../assert-jSdZMYam.mjs";
|
|
8
8
|
import { t as assertThrows } from "../assert_throws-BOkhLGYc.mjs";
|