@fedify/fedify 2.3.9-dev.2048 → 2.3.9
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-Dm72xfCy.mjs} +2 -2
- package/dist/{client-DYDoDpMa.mjs → client-Ccbsek6u.mjs} +7 -3
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{context-BepjCFHN.mjs → context-CxbCIptr.mjs} +2 -1
- package/dist/{deno-ayal9buz.mjs → deno-Deo5dZf_.mjs} +1 -1
- package/dist/docloader-BAVVKXPl.mjs +79 -0
- package/dist/federation/builder.test.mjs +2 -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 +187 -8
- package/dist/federation/mod.cjs +1 -1
- 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-BqObaL32.mjs} +8 -4
- package/dist/{http-BGP2QCbe.cjs → http-CjvY7wBM.cjs} +6 -2
- package/dist/{http-BPPCh0xN.js → http-DuWPL6ic.js} +6 -2
- package/dist/{key-C9DkrWwb.mjs → key-D1TEfV4d.mjs} +2 -2
- package/dist/{kv-cache-C1TC_e9y.cjs → kv-cache-C_4ufNbl.cjs} +34 -8
- package/dist/{kv-cache-qJSjfLGm.mjs → kv-cache-DGnB9ZX_.mjs} +1 -1
- package/dist/{kv-cache-Cl1q1aA9.js → kv-cache-w0De7ODp.js} +35 -9
- package/dist/{ld-DNl7KwLY.mjs → ld-DnGqAKWi.mjs} +4 -4
- package/dist/{metrics-Ce55disp.mjs → metrics-jUJmtE2u.mjs} +1 -1
- package/dist/{middleware-C_utyVa_.js → middleware-A-SjayRO.js} +19 -5
- package/dist/{middleware-BZojifnF.cjs → middleware-BW5W0CTK.cjs} +19 -5
- package/dist/{middleware-C96wm_7x.mjs → middleware-C70IPcBw.mjs} +1 -1
- package/dist/{middleware-BCG2BX1n.mjs → middleware-XCr66VRe.mjs} +13 -13
- package/dist/{mod-DHgc4ygG.d.ts → mod-CQ8EjnN5.d.ts} +2 -0
- package/dist/{mod-1TuoXkGs.d.cts → mod-EbRFjVlY.d.cts} +2 -0
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +1 -1
- package/dist/mod.d.ts +1 -1
- 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/{owner-DqqDBw-l.mjs → owner-C4iDnghj.mjs} +2 -2
- package/dist/{proof-D5vP7VIN.mjs → proof-CpU6r25U.mjs} +3 -3
- package/dist/{proof-Dz9ObvUq.js → proof-DqO9zYcc.js} +2 -2
- package/dist/{proof-I6YcKRRq.cjs → proof-DvVA2Bmk.cjs} +2 -2
- package/dist/{send-CBtR8DQK.mjs → send-YVatAj-a.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-BJ-5wxWU.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 +8 -8
- package/dist/docloader-C-JrNL14.mjs +0 -53
|
@@ -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";
|
|
@@ -3,17 +3,18 @@ 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 "../std__assert-
|
|
6
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
7
7
|
import { n as assertGreaterOrEqual, 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 assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
|
|
10
|
-
import { l as verifyRequest } from "../http-
|
|
10
|
+
import { l as verifyRequest } from "../http-BqObaL32.mjs";
|
|
11
11
|
import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
|
|
12
|
-
import { t as doesActorOwnKey } from "../owner-
|
|
13
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-
|
|
12
|
+
import { t as doesActorOwnKey } from "../owner-C4iDnghj.mjs";
|
|
13
|
+
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-YVatAj-a.mjs";
|
|
14
14
|
import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
|
|
15
15
|
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
16
16
|
import { FetchError, UrlError } from "@fedify/vocab-runtime";
|
|
17
|
+
import dns from "node:dns/promises";
|
|
17
18
|
import { AggregationTemporality, InMemoryMetricExporter, MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
18
19
|
//#region src/federation/send.test.ts
|
|
19
20
|
test("extractInboxes()", () => {
|
|
@@ -687,5 +688,85 @@ for (const signed of [false, true]) {
|
|
|
687
688
|
});
|
|
688
689
|
});
|
|
689
690
|
}
|
|
691
|
+
for (const signed of [false, true]) test({
|
|
692
|
+
name: `sendActivity() classifies DNS failures (signed: ${signed})`,
|
|
693
|
+
ignore: "Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted",
|
|
694
|
+
async fn(t) {
|
|
695
|
+
const activity = {
|
|
696
|
+
type: "Create",
|
|
697
|
+
id: "https://example.com/activity"
|
|
698
|
+
};
|
|
699
|
+
const destination = "https://delivery.invalid/inbox";
|
|
700
|
+
const publicInbox = "https://8.8.8.8/inbox";
|
|
701
|
+
const keys = signed ? [{
|
|
702
|
+
privateKey: rsaPrivateKey2,
|
|
703
|
+
keyId: rsaPublicKey2.id
|
|
704
|
+
}] : [];
|
|
705
|
+
for (const result of [
|
|
706
|
+
"throws",
|
|
707
|
+
"empty",
|
|
708
|
+
"cname",
|
|
709
|
+
"private"
|
|
710
|
+
]) for (const redirected of [false, true]) await t.step(`${result}, redirected: ${redirected}`, async () => {
|
|
711
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
712
|
+
const originalLookup = dns.lookup;
|
|
713
|
+
const resolverError = /* @__PURE__ */ new Error("Resolver unavailable");
|
|
714
|
+
const lookups = [];
|
|
715
|
+
dns.lookup = ((hostname, options) => {
|
|
716
|
+
lookups.push(hostname);
|
|
717
|
+
assertEquals(hostname, "delivery.invalid");
|
|
718
|
+
assertEquals(options, { all: true });
|
|
719
|
+
if (result === "throws") return Promise.reject(resolverError);
|
|
720
|
+
return Promise.resolve(result === "empty" ? [] : [{
|
|
721
|
+
address: result === "private" ? "127.0.0.1" : "alias.invalid.",
|
|
722
|
+
family: 4
|
|
723
|
+
}]);
|
|
724
|
+
});
|
|
725
|
+
try {
|
|
726
|
+
esm_default.mockGlobal().catch(202);
|
|
727
|
+
if (redirected) esm_default.route(publicInbox, {
|
|
728
|
+
status: 307,
|
|
729
|
+
headers: { Location: destination }
|
|
730
|
+
});
|
|
731
|
+
const send = () => sendActivity({
|
|
732
|
+
activity,
|
|
733
|
+
activityType: "https://www.w3.org/ns/activitystreams#Create",
|
|
734
|
+
meterProvider,
|
|
735
|
+
keys,
|
|
736
|
+
inbox: new URL(redirected ? publicInbox : destination)
|
|
737
|
+
});
|
|
738
|
+
if (result === "private") assertEquals((await assertRejects(send, UrlError)).reason, "disallowed");
|
|
739
|
+
else {
|
|
740
|
+
const error = await assertRejects(send, FetchError);
|
|
741
|
+
assertEquals(error.url.href, destination);
|
|
742
|
+
assertInstanceOf(error.cause, UrlError);
|
|
743
|
+
assertEquals(error.cause.reason, "dns");
|
|
744
|
+
assertEquals(error.cause.cause, result === "throws" ? resolverError : void 0);
|
|
745
|
+
}
|
|
746
|
+
const expectedCount = result === "private" && !redirected ? 0 : 1;
|
|
747
|
+
const sent = recorder.getMeasurements("activitypub.delivery.sent");
|
|
748
|
+
const durations = recorder.getMeasurements("activitypub.delivery.duration");
|
|
749
|
+
assertEquals(sent.length, expectedCount);
|
|
750
|
+
assertEquals(durations.length, expectedCount);
|
|
751
|
+
if (expectedCount === 1) {
|
|
752
|
+
assertEquals(sent[0].value, 1);
|
|
753
|
+
assertEquals(sent[0].attributes, {
|
|
754
|
+
"activitypub.remote.host": redirected ? "8.8.8.8" : "delivery.invalid",
|
|
755
|
+
"activitypub.activity.type": "https://www.w3.org/ns/activitystreams#Create",
|
|
756
|
+
"activitypub.delivery.success": false
|
|
757
|
+
});
|
|
758
|
+
assertEquals(durations[0].attributes, sent[0].attributes);
|
|
759
|
+
assertGreaterOrEqual(durations[0].value, 0);
|
|
760
|
+
}
|
|
761
|
+
assertEquals(lookups, ["delivery.invalid"]);
|
|
762
|
+
assertEquals(esm_default.callHistory.calls(destination).length, 0);
|
|
763
|
+
assertEquals(esm_default.callHistory.calls().length, redirected ? 1 : 0);
|
|
764
|
+
} finally {
|
|
765
|
+
dns.lookup = originalLookup;
|
|
766
|
+
esm_default.hardReset();
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
});
|
|
690
771
|
//#endregion
|
|
691
772
|
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
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
|
-
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-
|
|
7
|
+
import { t as hasMalformedKnownTemporalLiteral } from "../temporal-BJ-5wxWU.mjs";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
9
9
|
//#region src/federation/temporal.test.ts
|
|
10
10
|
test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { r as createRequestContext } from "../context-
|
|
4
|
+
import { r as createRequestContext } from "../context-CxbCIptr.mjs";
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-C-ZRDbaf.mjs";
|
|
6
|
-
import "../std__assert-
|
|
6
|
+
import "../std__assert-BvGuO4xO.mjs";
|
|
7
7
|
import { t as assertNotEquals } from "../assert_not_equals-DkVK8oqV.mjs";
|
|
8
8
|
import { t as MemoryKvStore } from "../kv-CX8qfaad.mjs";
|
|
9
|
-
import { o as createFederation, s as handleWebFinger } from "../middleware-
|
|
9
|
+
import { o as createFederation, s as handleWebFinger } from "../middleware-XCr66VRe.mjs";
|
|
10
10
|
import { Image, Link, Person, Tombstone } from "@fedify/vocab";
|
|
11
11
|
import { createTestMeterProvider, test } from "@fedify/fixture";
|
|
12
12
|
//#region src/federation/webfinger.test.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-
|
|
4
|
+
import { n as version, t as name } from "./deno-Deo5dZf_.mjs";
|
|
5
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-jUJmtE2u.mjs";
|
|
6
6
|
import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-Dd__NiUL.mjs";
|
|
7
|
-
import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-
|
|
7
|
+
import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-D1TEfV4d.mjs";
|
|
8
8
|
import { getLogger } from "@logtape/logtape";
|
|
9
9
|
import { CryptographicKey } from "@fedify/vocab";
|
|
10
10
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -718,6 +718,8 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
718
718
|
failure = result;
|
|
719
719
|
failureAlgorithm = algorithm;
|
|
720
720
|
};
|
|
721
|
+
let body = null;
|
|
722
|
+
let digestValid = null;
|
|
721
723
|
for (const sigName of signatureNames) {
|
|
722
724
|
if (!signatures[sigName]) {
|
|
723
725
|
setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
|
|
@@ -769,7 +771,9 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
769
771
|
setFailure(invalidSignatureResult(keyId));
|
|
770
772
|
continue;
|
|
771
773
|
}
|
|
772
|
-
|
|
774
|
+
body ??= await request.arrayBuffer();
|
|
775
|
+
digestValid ??= await verifyRfc9421ContentDigest(contentDigestHeader, body);
|
|
776
|
+
if (!digestValid) {
|
|
773
777
|
logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
|
|
774
778
|
setFailure(invalidSignatureResult(keyId));
|
|
775
779
|
continue;
|
|
@@ -11,7 +11,7 @@ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conve
|
|
|
11
11
|
let byte_encodings_base64 = require("byte-encodings/base64");
|
|
12
12
|
//#region deno.json
|
|
13
13
|
var name = "@fedify/fedify";
|
|
14
|
-
var version = "2.3.9
|
|
14
|
+
var version = "2.3.9";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/sig/accept.ts
|
|
17
17
|
/**
|
|
@@ -2190,6 +2190,8 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
2190
2190
|
failure = result;
|
|
2191
2191
|
failureAlgorithm = algorithm;
|
|
2192
2192
|
};
|
|
2193
|
+
let body = null;
|
|
2194
|
+
let digestValid = null;
|
|
2193
2195
|
for (const sigName of signatureNames) {
|
|
2194
2196
|
if (!signatures[sigName]) {
|
|
2195
2197
|
setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
|
|
@@ -2241,7 +2243,9 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
2241
2243
|
setFailure(invalidSignatureResult(keyId));
|
|
2242
2244
|
continue;
|
|
2243
2245
|
}
|
|
2244
|
-
|
|
2246
|
+
body ??= await request.arrayBuffer();
|
|
2247
|
+
digestValid ??= await verifyRfc9421ContentDigest(contentDigestHeader, body);
|
|
2248
|
+
if (!digestValid) {
|
|
2245
2249
|
logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
|
|
2246
2250
|
setFailure(invalidSignatureResult(keyId));
|
|
2247
2251
|
continue;
|
|
@@ -10,7 +10,7 @@ import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } fro
|
|
|
10
10
|
import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
|
|
11
11
|
//#region deno.json
|
|
12
12
|
var name = "@fedify/fedify";
|
|
13
|
-
var version = "2.3.9
|
|
13
|
+
var version = "2.3.9";
|
|
14
14
|
//#endregion
|
|
15
15
|
//#region src/sig/accept.ts
|
|
16
16
|
/**
|
|
@@ -2189,6 +2189,8 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
2189
2189
|
failure = result;
|
|
2190
2190
|
failureAlgorithm = algorithm;
|
|
2191
2191
|
};
|
|
2192
|
+
let body = null;
|
|
2193
|
+
let digestValid = null;
|
|
2192
2194
|
for (const sigName of signatureNames) {
|
|
2193
2195
|
if (!signatures[sigName]) {
|
|
2194
2196
|
setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
|
|
@@ -2240,7 +2242,9 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
|
|
|
2240
2242
|
setFailure(invalidSignatureResult(keyId));
|
|
2241
2243
|
continue;
|
|
2242
2244
|
}
|
|
2243
|
-
|
|
2245
|
+
body ??= await request.arrayBuffer();
|
|
2246
|
+
digestValid ??= await verifyRfc9421ContentDigest(contentDigestHeader, body);
|
|
2247
|
+
if (!digestValid) {
|
|
2244
2248
|
logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
|
|
2245
2249
|
setFailure(invalidSignatureResult(keyId));
|
|
2246
2250
|
continue;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-
|
|
4
|
+
import { n as version, t as name } from "./deno-Deo5dZf_.mjs";
|
|
5
|
+
import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-jUJmtE2u.mjs";
|
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
|
7
7
|
import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
8
8
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { Temporal } = require("@js-temporal/polyfill");
|
|
2
2
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
3
3
|
require("./chunk-DDcVe30Y.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
4
|
+
const require_http = require("./http-CjvY7wBM.cjs");
|
|
5
5
|
let _logtape_logtape = require("@logtape/logtape");
|
|
6
6
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
7
7
|
let es_toolkit = require("es-toolkit");
|
|
@@ -11,10 +11,13 @@ const logger$1 = (0, _logtape_logtape.getLogger)([
|
|
|
11
11
|
"utils",
|
|
12
12
|
"docloader"
|
|
13
13
|
]);
|
|
14
|
+
const DEFAULT_MAX_REDIRECTION = 20;
|
|
14
15
|
/**
|
|
15
16
|
* Gets an authenticated {@link DocumentLoader} for the given identity.
|
|
16
17
|
* Note that an authenticated document loader intentionally does not cache
|
|
17
18
|
* the fetched documents.
|
|
19
|
+
* At most 20 HTTP redirects and alternate document links are followed in total
|
|
20
|
+
* per call. Revisiting a URL within that chain throws a {@link FetchError}.
|
|
18
21
|
* @param identity The identity to get the document loader for.
|
|
19
22
|
* The actor's key pair.
|
|
20
23
|
* @param options The options for the document loader.
|
|
@@ -24,29 +27,52 @@ const logger$1 = (0, _logtape_logtape.getLogger)([
|
|
|
24
27
|
*/
|
|
25
28
|
function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, tracerProvider } = {}) {
|
|
26
29
|
require_http.validateCryptoKey(identity.privateKey);
|
|
27
|
-
async function load(url, options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
31
|
+
options?.signal?.throwIfAborted();
|
|
32
|
+
let currentUrl = new URL(url).href;
|
|
33
|
+
await validateUrl(currentUrl);
|
|
34
|
+
visited.add(currentUrl);
|
|
35
|
+
const originalRequest = (0, _fedify_vocab_runtime.createActivityPubRequest)(currentUrl, { userAgent });
|
|
36
|
+
function follow(nextUrl) {
|
|
37
|
+
options?.signal?.throwIfAborted();
|
|
38
|
+
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new _fedify_vocab_runtime.FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
|
|
39
|
+
if (visited.has(nextUrl)) throw new _fedify_vocab_runtime.FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
|
|
40
|
+
redirected++;
|
|
41
|
+
}
|
|
42
|
+
const response = await require_http.doubleKnock(originalRequest, identity, {
|
|
30
43
|
maxRedirection,
|
|
31
44
|
specDeterminer,
|
|
32
45
|
log: (0, es_toolkit.curry)(_fedify_vocab_runtime.logRequest)(logger$1),
|
|
33
46
|
tracerProvider,
|
|
34
47
|
signal: options?.signal,
|
|
35
|
-
validateRedirect:
|
|
36
|
-
|
|
48
|
+
validateRedirect: async (nextUrl) => {
|
|
49
|
+
follow(nextUrl);
|
|
50
|
+
await validateUrl(nextUrl);
|
|
51
|
+
visited.add(nextUrl);
|
|
52
|
+
currentUrl = nextUrl;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return (0, _fedify_vocab_runtime.getRemoteDocument)(currentUrl, response, (alternateUrl) => {
|
|
56
|
+
follow(alternateUrl);
|
|
57
|
+
return load(alternateUrl, options, redirected, visited);
|
|
58
|
+
});
|
|
37
59
|
}
|
|
38
60
|
async function validateUrl(url) {
|
|
39
61
|
if (!allowPrivateAddress) try {
|
|
40
62
|
await (0, _fedify_vocab_runtime.validatePublicUrl)(url);
|
|
41
63
|
} catch (error) {
|
|
42
|
-
if (error instanceof _fedify_vocab_runtime.UrlError) logger$1.
|
|
64
|
+
if (error instanceof _fedify_vocab_runtime.UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
|
|
65
|
+
url,
|
|
66
|
+
error
|
|
67
|
+
});
|
|
68
|
+
else logger$1.error("Disallowed private URL: {url}", {
|
|
43
69
|
url,
|
|
44
70
|
error
|
|
45
71
|
});
|
|
46
72
|
throw error;
|
|
47
73
|
}
|
|
48
74
|
}
|
|
49
|
-
return load;
|
|
75
|
+
return (url, options) => load(url, options);
|
|
50
76
|
}
|
|
51
77
|
//#endregion
|
|
52
78
|
//#region src/utils/kv-cache.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { p as recordDocumentCache } from "./metrics-
|
|
4
|
+
import { p as recordDocumentCache } from "./metrics-jUJmtE2u.mjs";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/utils/kv-cache.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { T as recordDocumentCache, f as validateCryptoKey, t as doubleKnock } from "./http-
|
|
3
|
+
import { T as recordDocumentCache, f as validateCryptoKey, t as doubleKnock } from "./http-DuWPL6ic.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
|
-
import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
5
|
+
import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
|
|
6
6
|
import { curry } from "es-toolkit";
|
|
7
7
|
//#region src/utils/docloader.ts
|
|
8
8
|
const logger$1 = getLogger([
|
|
@@ -10,10 +10,13 @@ const logger$1 = getLogger([
|
|
|
10
10
|
"utils",
|
|
11
11
|
"docloader"
|
|
12
12
|
]);
|
|
13
|
+
const DEFAULT_MAX_REDIRECTION = 20;
|
|
13
14
|
/**
|
|
14
15
|
* Gets an authenticated {@link DocumentLoader} for the given identity.
|
|
15
16
|
* Note that an authenticated document loader intentionally does not cache
|
|
16
17
|
* the fetched documents.
|
|
18
|
+
* At most 20 HTTP redirects and alternate document links are followed in total
|
|
19
|
+
* per call. Revisiting a URL within that chain throws a {@link FetchError}.
|
|
17
20
|
* @param identity The identity to get the document loader for.
|
|
18
21
|
* The actor's key pair.
|
|
19
22
|
* @param options The options for the document loader.
|
|
@@ -23,29 +26,52 @@ const logger$1 = getLogger([
|
|
|
23
26
|
*/
|
|
24
27
|
function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, tracerProvider } = {}) {
|
|
25
28
|
validateCryptoKey(identity.privateKey);
|
|
26
|
-
async function load(url, options) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
30
|
+
options?.signal?.throwIfAborted();
|
|
31
|
+
let currentUrl = new URL(url).href;
|
|
32
|
+
await validateUrl(currentUrl);
|
|
33
|
+
visited.add(currentUrl);
|
|
34
|
+
const originalRequest = createActivityPubRequest(currentUrl, { userAgent });
|
|
35
|
+
function follow(nextUrl) {
|
|
36
|
+
options?.signal?.throwIfAborted();
|
|
37
|
+
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
|
|
38
|
+
if (visited.has(nextUrl)) throw new FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
|
|
39
|
+
redirected++;
|
|
40
|
+
}
|
|
41
|
+
const response = await doubleKnock(originalRequest, identity, {
|
|
29
42
|
maxRedirection,
|
|
30
43
|
specDeterminer,
|
|
31
44
|
log: curry(logRequest)(logger$1),
|
|
32
45
|
tracerProvider,
|
|
33
46
|
signal: options?.signal,
|
|
34
|
-
validateRedirect:
|
|
35
|
-
|
|
47
|
+
validateRedirect: async (nextUrl) => {
|
|
48
|
+
follow(nextUrl);
|
|
49
|
+
await validateUrl(nextUrl);
|
|
50
|
+
visited.add(nextUrl);
|
|
51
|
+
currentUrl = nextUrl;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return getRemoteDocument(currentUrl, response, (alternateUrl) => {
|
|
55
|
+
follow(alternateUrl);
|
|
56
|
+
return load(alternateUrl, options, redirected, visited);
|
|
57
|
+
});
|
|
36
58
|
}
|
|
37
59
|
async function validateUrl(url) {
|
|
38
60
|
if (!allowPrivateAddress) try {
|
|
39
61
|
await validatePublicUrl(url);
|
|
40
62
|
} catch (error) {
|
|
41
|
-
if (error instanceof UrlError) logger$1.
|
|
63
|
+
if (error instanceof UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
|
|
64
|
+
url,
|
|
65
|
+
error
|
|
66
|
+
});
|
|
67
|
+
else logger$1.error("Disallowed private URL: {url}", {
|
|
42
68
|
url,
|
|
43
69
|
error
|
|
44
70
|
});
|
|
45
71
|
throw error;
|
|
46
72
|
}
|
|
47
73
|
}
|
|
48
|
-
return load;
|
|
74
|
+
return (url, options) => load(url, options);
|
|
49
75
|
}
|
|
50
76
|
//#endregion
|
|
51
77
|
//#region src/utils/kv-cache.ts
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-
|
|
6
|
-
import { r as fetchKey, s as validateCryptoKey } from "./key-
|
|
4
|
+
import { n as version, t as name } from "./deno-Deo5dZf_.mjs";
|
|
5
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-jUJmtE2u.mjs";
|
|
6
|
+
import { r as fetchKey, s as validateCryptoKey } from "./key-D1TEfV4d.mjs";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
8
8
|
import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
|
|
9
9
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -206,7 +206,7 @@ function normalizeContextReference(reference, baseUrl) {
|
|
|
206
206
|
/** @internal */
|
|
207
207
|
function isInvalidUrlTypeError(error) {
|
|
208
208
|
const code = error.code;
|
|
209
|
-
return error instanceof TypeError && (code === "ERR_INVALID_URL" || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
|
|
209
|
+
return error instanceof TypeError && (code === "ERR_INVALID_URL" || error.message === "Invalid URL string." || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
|
|
210
210
|
}
|
|
211
211
|
async function applyGraphAliasContext(state, context, documentLoader, remoteContextCache, baseUrl = null, processingContexts = /* @__PURE__ */ new Set()) {
|
|
212
212
|
if (context === null) return createGraphAliasContextState();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
4
|
+
import { n as version, t as name } from "./deno-Deo5dZf_.mjs";
|
|
5
5
|
import { metrics } from "@opentelemetry/api";
|
|
6
6
|
import { FetchError } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/federation/metrics.ts
|