@fedify/fedify 2.4.0-dev.2212 → 2.4.0-dev.2228

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.
Files changed (92) hide show
  1. package/dist/{builder-CKAVFvcH.mjs → builder-Wx64uCV2.mjs} +10 -2
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{compound-proof-BP5SOdXv.mjs → compound-proof-B647GUyL.mjs} +2 -2
  6. package/dist/{context-Bl_Y7T_3.d.cts → context-B6X-t8gI.d.cts} +1 -1
  7. package/dist/{context-DMd11IAu.d.ts → context-CtI307_A.d.ts} +1 -1
  8. package/dist/{deno-BKp9fYB3.mjs → deno-9flirCWb.mjs} +1 -1
  9. package/dist/{docloader-BjJreYQU.mjs → docloader-pjAbs1lc.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +37 -1
  11. package/dist/federation/compound-fanout.test.mjs +3 -3
  12. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  13. package/dist/federation/handler.test.mjs +5 -5
  14. package/dist/federation/hashlink-media.test.mjs +2 -2
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/metrics.test.mjs +1 -1
  17. package/dist/federation/middleware.test.mjs +123 -7
  18. package/dist/federation/mod.cjs +1 -1
  19. package/dist/federation/mod.d.cts +3 -3
  20. package/dist/federation/mod.d.ts +3 -3
  21. package/dist/federation/mod.js +1 -1
  22. package/dist/federation/outgoing-proof.test.mjs +248 -6
  23. package/dist/federation/portable-actor.test.mjs +3 -3
  24. package/dist/federation/portable-chain.test.mjs +3 -3
  25. package/dist/federation/portable-collection.test.mjs +4 -4
  26. package/dist/federation/portable-delivery.test.mjs +2 -2
  27. package/dist/federation/portable-gateway-keys.test.mjs +4 -4
  28. package/dist/federation/portable-inbox.test.mjs +4 -4
  29. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  30. package/dist/federation/portable-route-activity.test.mjs +2 -2
  31. package/dist/federation/portable.test.mjs +80 -3
  32. package/dist/federation/send.test.mjs +3 -3
  33. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  34. package/dist/federation/tasks/tasks.test.mjs +2 -2
  35. package/dist/federation/temporal.test.mjs +1 -1
  36. package/dist/federation/webfinger.test.mjs +1 -1
  37. package/dist/{federation-B-0EXZPH.cjs → federation-DiycMKQx.cjs} +262 -36
  38. package/dist/{http-Di8yiDMY.js → http-CHqz_Jp6.js} +1 -1
  39. package/dist/{http-ByBlnY3D.cjs → http-DC5DyShJ.cjs} +1 -1
  40. package/dist/{http--IGUT1mg.mjs → http-DysQjjJe.mjs} +4 -4
  41. package/dist/{key-ChfV9C3v.mjs → key-TtgqklZ1.mjs} +2 -2
  42. package/dist/{kv-BhNbN8Ed.d.cts → kv-B2mzK5tl.d.cts} +3 -2
  43. package/dist/{kv-BhNbN8Ed.d.ts → kv-B2mzK5tl.d.ts} +3 -2
  44. package/dist/{kv-cache-DFIUR-E5.mjs → kv-cache-B7g5Tvl0.mjs} +1 -1
  45. package/dist/{kv-cache-DpwDPt9V.js → kv-cache-CgYK8siN.js} +1 -1
  46. package/dist/{kv-cache-BDwUzxBJ.cjs → kv-cache-CsNDrjVT.cjs} +1 -1
  47. package/dist/{ld-DBBVabBC.mjs → ld-CHYUee-s.mjs} +3 -3
  48. package/dist/{metrics-DPQKFkiJ.mjs → metrics-X1lpKWRK.mjs} +1 -1
  49. package/dist/{middleware-VXGLiHtY.js → middleware-D2scpP9Q.js} +262 -36
  50. package/dist/{middleware-BW7-tHAG.mjs → middleware-DoAk0Q3s.mjs} +251 -33
  51. package/dist/{middleware-G9zpxUsD.mjs → middleware-ndPtgYYI.mjs} +1 -1
  52. package/dist/{mod-C5Mwx5ty.d.ts → mod-B8MI1mpi.d.ts} +1 -1
  53. package/dist/{mod-BVNGHgVr.d.ts → mod-BhTaDR5H.d.ts} +1 -1
  54. package/dist/{mod-Cyy1Wahy.d.cts → mod-C7_2j7VT.d.cts} +1 -1
  55. package/dist/{mod-Dp3Ij7sw.d.cts → mod-DFL6IxYQ.d.cts} +1 -1
  56. package/dist/mod.cjs +4 -4
  57. package/dist/mod.d.cts +4 -4
  58. package/dist/mod.d.ts +4 -4
  59. package/dist/mod.js +4 -4
  60. package/dist/nodeinfo/handler.test.mjs +1 -1
  61. package/dist/otel/mod.d.cts +1 -1
  62. package/dist/otel/mod.d.ts +1 -1
  63. package/dist/{owner-BW5VsXG3.mjs → owner-BP_ouAvi.mjs} +3 -3
  64. package/dist/{portable-collection-FTPa5D91.js → portable-collection-4SnmDcTc.js} +1 -1
  65. package/dist/{portable-collection-CXcAlwei.cjs → portable-collection-Cz9K1Tas.cjs} +1 -1
  66. package/dist/{portable-collection-Ct99JsBe.mjs → portable-collection-SqxusPj7.mjs} +2 -2
  67. package/dist/{portable-key-U3lW06QJ.mjs → portable-key-DYr6MZUl.mjs} +1 -1
  68. package/dist/{proof-MFz2960e.mjs → proof-B-iLzyva.mjs} +4 -4
  69. package/dist/{send-Duj8xys1.mjs → send-D0nNms3Q.mjs} +3 -3
  70. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  71. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  72. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  73. package/dist/sig/compound-proof.test.mjs +2 -2
  74. package/dist/sig/http.test.mjs +2 -2
  75. package/dist/sig/key.test.mjs +1 -1
  76. package/dist/sig/ld.test.mjs +2 -2
  77. package/dist/sig/mod.cjs +2 -2
  78. package/dist/sig/mod.js +2 -2
  79. package/dist/sig/owner.test.mjs +1 -1
  80. package/dist/sig/portable-collection.test.mjs +1 -1
  81. package/dist/sig/portable-key.test.mjs +6 -6
  82. package/dist/sig/proof.test.mjs +5 -3
  83. package/dist/{temporal-CVcPKB1N.mjs → temporal-39Ixir8d.mjs} +1 -1
  84. package/dist/testing/kv.test.mjs +1 -1
  85. package/dist/testing/mod.d.mts +3 -2
  86. package/dist/utils/docloader.test.mjs +2 -2
  87. package/dist/utils/kv-cache.test.mjs +1 -1
  88. package/dist/utils/mod.cjs +1 -1
  89. package/dist/utils/mod.d.cts +1 -1
  90. package/dist/utils/mod.d.ts +1 -1
  91. package/dist/utils/mod.js +1 -1
  92. package/package.json +6 -6
@@ -7,14 +7,14 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
8
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
9
9
  import { t as assert } from "../assert-D1Eafmd_.mjs";
10
- import { n as exportJwk } from "../key-ChfV9C3v.mjs";
11
- import { s as detachSignature } from "../ld-DBBVabBC.mjs";
12
- import { a as signObject } from "../proof-MFz2960e.mjs";
10
+ import { n as exportJwk } from "../key-TtgqklZ1.mjs";
11
+ import { s as detachSignature } from "../ld-CHYUee-s.mjs";
12
+ import { a as signObject } from "../proof-B-iLzyva.mjs";
13
13
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
14
- import { c as assertPortableActorActivity, n as FederationImpl, o as createFederation } from "../middleware-BW7-tHAG.mjs";
15
- import { a as verifyCompoundPortableObjectProofs } from "../compound-proof-BP5SOdXv.mjs";
14
+ import { C as getEmbeddedFirstGateway, S as getCompactRootId, c as assertPortableActorActivity, n as FederationImpl, o as createFederation, w as warnCompatibleIdsInJson } from "../middleware-DoAk0Q3s.mjs";
15
+ import { a as verifyCompoundPortableObjectProofs } from "../compound-proof-B647GUyL.mjs";
16
16
  import { configure, reset } from "@logtape/logtape";
17
- import { Create, Note, Person } from "@fedify/vocab";
17
+ import { Announce, Create, Note, Person } from "@fedify/vocab";
18
18
  import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
19
19
  import { mockDocumentLoader, test } from "@fedify/fixture";
20
20
  //#region src/federation/outgoing-proof.test.ts
@@ -620,6 +620,248 @@ async function captureLogs(run) {
620
620
  }
621
621
  return records.filter((record) => record.category[0] === "fedify");
622
622
  }
623
+ test("sendActivity warns for compatible IDs off the actor's first gateway", async () => {
624
+ const owner = await didKey();
625
+ const ownerId = parseIri(`ap://${owner.did}/users/alice`);
626
+ const federation = createTestFederation();
627
+ let actorCalls = 0;
628
+ federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => {
629
+ actorCalls++;
630
+ return identifier === "alice" ? new Person({
631
+ id: ownerId,
632
+ gateways: [new URL("https://primary.example"), new URL("https://secondary.example")]
633
+ }) : null;
634
+ });
635
+ const ctx = federation.createContext(new Request("https://example.com/"), void 0);
636
+ const child = await signObject(new Note({
637
+ id: compatibleId(owner.did, "/notes/1", "https://secondary.example"),
638
+ attribution: ownerId,
639
+ content: "Hello"
640
+ }), owner.privateKey, owner.keyId, {
641
+ ...options,
642
+ context: childContext,
643
+ created
644
+ });
645
+ const activity = new Create({
646
+ id: compatibleId(owner.did, "/activities/1", "https://secondary.example"),
647
+ actor: ownerId,
648
+ object: child
649
+ });
650
+ const records = await captureLogs(async () => {
651
+ const { bodies } = await capture(() => ctx.sendActivity([rsaKey, sender(owner)], recipient, activity));
652
+ assertEquals(bodies.length, 1);
653
+ assertEquals(bodies[0].id, activity.id?.href);
654
+ });
655
+ assertEquals(records.filter((r) => r.properties.kind === "activity").length, 1);
656
+ assertEquals(records.filter((r) => r.properties.kind === "object").length, 1);
657
+ assertEquals(actorCalls, 1);
658
+ });
659
+ test("sendActivity skips first-gateway and portable IDs", async () => {
660
+ const owner = await didKey();
661
+ const ownerId = parseIri(`ap://${owner.did}/users/alice`);
662
+ const federation = createTestFederation();
663
+ federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => identifier === "alice" ? new Person({
664
+ id: ownerId,
665
+ gateways: [new URL("https://primary.example")]
666
+ }) : null);
667
+ const ctx = federation.createContext(new Request("https://example.com/"), void 0);
668
+ for (const id of [compatibleId(owner.did, "/activities/first", "https://primary.example"), parseIri(`ap://${owner.did}/activities/portable`)]) assertEquals((await captureLogs(() => capture(() => ctx.sendActivity([rsaKey, sender(owner)], recipient, new Create({
669
+ id,
670
+ actor: ownerId
671
+ }))))).filter((r) => r.properties.kind != null), []);
672
+ const plain = federation.createContext(new URL("https://example.com/"));
673
+ assertEquals((await captureLogs(() => capture(() => plain.sendActivity([rsaKey, sender(owner)], recipient, new Create({
674
+ id: compatibleId(owner.did, "/activities/unknown-gateway", "https://secondary.example"),
675
+ actor: ownerId
676
+ }))))).filter((r) => r.properties.kind != null), []);
677
+ });
678
+ test("fanout warns once before enqueueing a compatible activity", async () => {
679
+ const owner = await didKey();
680
+ const ownerId = parseIri(`ap://${owner.did}/users/alice`);
681
+ const { queue, queued } = createQueue();
682
+ const federation = createTestFederation(queue);
683
+ federation.setActorDispatcher("/users/{identifier}", () => new Person({
684
+ id: ownerId,
685
+ gateways: [new URL("https://primary.example")]
686
+ }));
687
+ const ctx = federation.createContext(new Request("https://example.com/"), void 0);
688
+ assertEquals((await captureLogs(() => capture(async () => {
689
+ await ctx.sendActivity([rsaKey, sender(owner)], recipient, new Create({
690
+ id: compatibleId(owner.did, "/activities/fanout", "https://secondary.example"),
691
+ actor: ownerId
692
+ }), { fanout: "force" });
693
+ assertEquals(queued.length, 1);
694
+ for (let i = 0; i < queued.length; i++) await federation.processQueuedTask(void 0, queued[i]);
695
+ }))).filter((r) => r.properties.kind === "activity").length, 1);
696
+ });
697
+ test("an embedded actor supplies gateways to a plain send context", async () => {
698
+ const owner = await didKey();
699
+ const ownerId = parseIri(`ap://${owner.did}/users/alice`);
700
+ const ctx = new FederationImpl({
701
+ kv: new MemoryKvStore(),
702
+ contextLoaderFactory: () => mockDocumentLoader,
703
+ documentLoaderFactory: () => mockDocumentLoader,
704
+ activityTransformers: []
705
+ }).createContext(new URL("https://example.com/"));
706
+ const activity = await signObject(new Create({
707
+ id: compatibleId(owner.did, "/activities/embedded-actor", "https://secondary.example"),
708
+ actor: new Person({
709
+ id: ownerId,
710
+ gateways: [new URL("https://primary.example")]
711
+ })
712
+ }), owner.privateKey, owner.keyId, {
713
+ ...options,
714
+ created
715
+ });
716
+ assertEquals(getEmbeddedFirstGateway(await activity.toJsonLd({
717
+ format: "compact",
718
+ contextLoader: mockDocumentLoader
719
+ }), ownerId, owner.did)?.origin, "https://primary.example");
720
+ assertEquals((await captureLogs(() => capture(() => ctx.sendActivity([rsaKey, sender(owner)], recipient, activity)))).filter((r) => r.properties.kind === "activity").length, 1);
721
+ });
722
+ test("sendActivity checks a locally owned embedded object independently", async () => {
723
+ const alice = await didKey();
724
+ const bob = await didKey();
725
+ const aliceId = parseIri(`ap://${alice.did}/users/alice`);
726
+ const bobId = parseIri(`ap://${bob.did}/users/bob`);
727
+ const federation = createTestFederation();
728
+ federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => identifier === "alice" ? new Person({
729
+ id: aliceId,
730
+ gateways: [new URL("https://primary.example")]
731
+ }) : identifier === "bob" ? new Person({
732
+ id: bobId,
733
+ gateways: [new URL("https://bob.example")]
734
+ }) : null);
735
+ const child = await signObject(new Note({
736
+ id: compatibleId(bob.did, "/notes/1", "https://secondary.example"),
737
+ attribution: bobId,
738
+ content: "Bob's note"
739
+ }), bob.privateKey, bob.keyId, {
740
+ ...options,
741
+ context: childContext,
742
+ created
743
+ });
744
+ const ctx = federation.createContext(new Request("https://example.com/"), void 0);
745
+ assertEquals((await captureLogs(() => capture(() => ctx.sendActivity([rsaKey, sender(alice)], recipient, new Announce({
746
+ id: parseIri(`ap://${alice.did}/activities/announce`),
747
+ actor: aliceId,
748
+ object: child
749
+ }))))).filter((r) => r.properties.kind === "object").map((r) => r.properties.id), [child.id?.href]);
750
+ });
751
+ test("sendActivity in an actor dispatcher does not re-enter it", async () => {
752
+ const owner = await didKey();
753
+ const ownerId = parseIri(`ap://${owner.did}/users/alice`);
754
+ const federation = createTestFederation();
755
+ let calls = 0;
756
+ federation.setActorDispatcher("/users/{identifier}", async (ctx) => {
757
+ calls++;
758
+ await ctx.sendActivity([rsaKey, sender(owner)], recipient, new Create({
759
+ id: compatibleId(owner.did, "/activities/from-dispatcher", "https://secondary.example"),
760
+ actor: ownerId
761
+ }));
762
+ return new Person({
763
+ id: ownerId,
764
+ gateways: [new URL("https://primary.example")]
765
+ });
766
+ });
767
+ await capture(async () => {
768
+ assertEquals((await federation.fetch(new Request("https://example.com/users/alice", { headers: { Accept: "application/activity+json" } }), { contextData: void 0 })).status, 200);
769
+ });
770
+ assertEquals(calls, 1);
771
+ });
772
+ test("sendActivity still delivers when the diagnostic actor lookup fails", async () => {
773
+ const owner = await didKey();
774
+ const ownerId = parseIri(`ap://${owner.did}/users/alice`);
775
+ const federation = createTestFederation();
776
+ federation.setActorDispatcher("/users/{identifier}", () => {
777
+ throw new Error("Actor storage unavailable");
778
+ });
779
+ const ctx = federation.createContext(new Request("https://example.com/"), void 0);
780
+ const id = compatibleId(owner.did, "/activities/lookup-fails", "https://secondary.example");
781
+ assertEquals((await captureLogs(async () => {
782
+ const { bodies } = await capture(() => ctx.sendActivity([rsaKey, sender(owner)], recipient, new Create({
783
+ id,
784
+ actor: ownerId
785
+ })));
786
+ assertEquals(bodies.length, 1);
787
+ assertEquals(bodies[0].id, id.href);
788
+ })).filter((r) => r.properties.kind != null), []);
789
+ });
790
+ test("warning checks later local actors when the first is missing", async () => {
791
+ const owner = await didKey();
792
+ const missing = parseIri(`ap://${owner.did}/users/missing`);
793
+ const alice = parseIri(`ap://${owner.did}/users/alice`);
794
+ const federation = createTestFederation();
795
+ federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => identifier === "alice" ? new Person({
796
+ id: alice,
797
+ gateways: [new URL("https://primary.example")]
798
+ }) : null);
799
+ const ctx = federation.createContext(new Request("https://example.com/"), void 0);
800
+ assertEquals((await captureLogs(() => warnCompatibleIdsInJson({
801
+ id: compatibleId(owner.did, "/activities/multi", "https://secondary.example").href,
802
+ type: "Create",
803
+ actor: [missing.href, alice.href]
804
+ }, ctx))).filter((r) => r.properties.kind === "activity").length, 1);
805
+ });
806
+ test("warning ignores an embedded gateway key", async () => {
807
+ const owner = await didKey();
808
+ const actorId = parseIri(`ap://${owner.did}/users/alice`);
809
+ const records = await captureLogs(() => warnCompatibleIdsInJson({
810
+ id: compatibleId(owner.did, "/activities/1", "https://secondary.example").href,
811
+ type: "Create",
812
+ actor: {
813
+ id: actorId.href,
814
+ type: "Person",
815
+ gateways: ["https://primary.example"],
816
+ publicKey: [{
817
+ id: compatibleId(owner.did, "/users/alice/keys/1", "https://secondary.example").href,
818
+ type: ["CryptographicKey"]
819
+ }, {
820
+ id: compatibleId(owner.did, "/users/alice/keys/2", "https://secondary.example").href,
821
+ type: "sec:Key"
822
+ }]
823
+ }
824
+ }));
825
+ assertEquals(records.filter((r) => r.properties.kind === "activity").length, 1);
826
+ assertEquals(records.filter((r) => r.properties.kind === "object"), []);
827
+ });
828
+ test("warning resolves a signed child's local ownership alias", async () => {
829
+ const alice = await didKey();
830
+ const bob = await didKey();
831
+ const aliceId = parseIri(`ap://${alice.did}/users/alice`);
832
+ const bobId = parseIri(`ap://${bob.did}/users/bob`);
833
+ const federation = createTestFederation();
834
+ federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => identifier === "bob" ? new Person({
835
+ id: bobId,
836
+ gateways: [new URL("https://primary.example")]
837
+ }) : null);
838
+ const ctx = federation.createContext(new Request("https://example.com/"), void 0);
839
+ assertEquals((await captureLogs(() => warnCompatibleIdsInJson({
840
+ id: parseIri(`ap://${alice.did}/activities/announce`).href,
841
+ type: "Announce",
842
+ actor: aliceId.href,
843
+ object: {
844
+ "@context": ["https://www.w3.org/ns/activitystreams", {
845
+ by: {
846
+ "@id": "as:attributedTo",
847
+ "@type": "@id"
848
+ },
849
+ identifier: "@id"
850
+ }],
851
+ identifier: compatibleId(bob.did, "/notes/1", "https://secondary.example").href,
852
+ type: "Note",
853
+ by: bobId.href
854
+ }
855
+ }, ctx))).filter((r) => r.properties.kind === "object").length, 1);
856
+ });
857
+ test("warning reads an inline alias of the serialized root ID", async () => {
858
+ const id = compatibleId((await didKey()).did, "/notes/1", "https://secondary.example");
859
+ assertEquals(getCompactRootId({
860
+ "@context": ["https://www.w3.org/ns/activitystreams", { identifier: "@id" }],
861
+ identifier: id.href,
862
+ type: "Note"
863
+ })?.href, id.href);
864
+ });
623
865
  test("an activity on another gateway than its compatible-ID actor is warned about", async () => {
624
866
  const owner = await didKey();
625
867
  const warnings = (activity) => captureLogs(() => assertPortableActorActivity(activity)).then((records) => records.map((r) => r.properties.activityId));
@@ -8,10 +8,10 @@ import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
8
8
  import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
9
9
  import "../std__assert-DvMDRPes.mjs";
10
10
  import { t as assert } from "../assert-D1Eafmd_.mjs";
11
- import { a as signObject, c as verifyPortableObjectProof } from "../proof-MFz2960e.mjs";
12
- import { f as verifyRequest, l as signRequest } from "../http--IGUT1mg.mjs";
11
+ import { a as signObject, c as verifyPortableObjectProof } from "../proof-B-iLzyva.mjs";
12
+ import { f as verifyRequest, l as signRequest } from "../http-DysQjjJe.mjs";
13
13
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
14
- import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-BW7-tHAG.mjs";
14
+ import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-DoAk0Q3s.mjs";
15
15
  import { n as RouterError } from "../router-u5O5KCQr.mjs";
16
16
  import { configure, reset } from "@logtape/logtape";
17
17
  import { Note, Person, Tombstone, lookupObject } from "@fedify/vocab";
@@ -7,10 +7,10 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
9
  import { t as assert } from "../assert-D1Eafmd_.mjs";
10
- import { n as exportJwk } from "../key-ChfV9C3v.mjs";
11
- import { c as verifyPortableObjectProof, s as verifyObject } from "../proof-MFz2960e.mjs";
10
+ import { n as exportJwk } from "../key-TtgqklZ1.mjs";
11
+ import { c as verifyPortableObjectProof, s as verifyObject } from "../proof-B-iLzyva.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
13
- import { n as FederationImpl } from "../middleware-BW7-tHAG.mjs";
13
+ import { n as FederationImpl } from "../middleware-DoAk0Q3s.mjs";
14
14
  import { Create, DataIntegrityProof, Note, OrderedCollection, Person, traverseCollection } from "@fedify/vocab";
15
15
  import { FetchError, encodeMultibase, exportDidKey } from "@fedify/vocab-runtime";
16
16
  import { mockDocumentLoader, test } from "@fedify/fixture";
@@ -7,11 +7,11 @@ import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
9
  import { t as assert } from "../assert-D1Eafmd_.mjs";
10
- import { a as signObject } from "../proof-MFz2960e.mjs";
10
+ import { a as signObject } from "../proof-B-iLzyva.mjs";
11
11
  import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
12
- import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-BW7-tHAG.mjs";
13
- import { t as verifyPortableObject } from "../portable-collection-Ct99JsBe.mjs";
14
- import { s as verifyServedPortableObjects } from "../compound-proof-BP5SOdXv.mjs";
12
+ import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-DoAk0Q3s.mjs";
13
+ import { t as verifyPortableObject } from "../portable-collection-SqxusPj7.mjs";
14
+ import { s as verifyServedPortableObjects } from "../compound-proof-B647GUyL.mjs";
15
15
  import { n as RouterError } from "../router-u5O5KCQr.mjs";
16
16
  import { configure, reset } from "@logtape/logtape";
17
17
  import { Create, Note, Object as Object$1, OrderedCollection, PUBLIC_COLLECTION, Person, Tombstone, lookupObject, traverseCollection } from "@fedify/vocab";
@@ -4,8 +4,8 @@ globalThis.addEventListener = () => {};
4
4
  import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { i as rsaPrivateKey2, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
7
- import { n as FederationImpl, r as InboxContextImpl } from "../middleware-BW7-tHAG.mjs";
8
- import { n as extractInboxes, t as SendActivityError } from "../send-Duj8xys1.mjs";
7
+ import { n as FederationImpl, r as InboxContextImpl } from "../middleware-DoAk0Q3s.mjs";
8
+ import { n as extractInboxes, t as SendActivityError } from "../send-D0nNms3Q.mjs";
9
9
  import { Create, Endpoints, Note, Person } from "@fedify/vocab";
10
10
  import { parseIri } from "@fedify/vocab-runtime";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
@@ -8,11 +8,11 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
9
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
10
10
  import { r as getVerifiedKeyOwnerEvidence } from "../key-owner-evidence-APB6NpSk.mjs";
11
- import { p as signJsonLd } from "../ld-DBBVabBC.mjs";
12
- import { f as verifyRequest, l as signRequest, p as verifyRequestDetailed } from "../http--IGUT1mg.mjs";
11
+ import { p as signJsonLd } from "../ld-CHYUee-s.mjs";
12
+ import { f as verifyRequest, l as signRequest, p as verifyRequestDetailed } from "../http-DysQjjJe.mjs";
13
13
  import { a as rsaPrivateKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
14
- import { f as handleInbox, n as FederationImpl, o as createFederation } from "../middleware-BW7-tHAG.mjs";
15
- import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-BW5VsXG3.mjs";
14
+ import { f as handleInbox, n as FederationImpl, o as createFederation } from "../middleware-DoAk0Q3s.mjs";
15
+ import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-BP_ouAvi.mjs";
16
16
  import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
17
17
  import { t as KvKeyCache } from "../keycache-Da8A8mZa.mjs";
18
18
  import { Create, CryptographicKey, Person } from "@fedify/vocab";
@@ -8,11 +8,11 @@ import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
8
8
  import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
9
9
  import "../std__assert-DvMDRPes.mjs";
10
10
  import { t as assert } from "../assert-D1Eafmd_.mjs";
11
- import { p as signJsonLd } from "../ld-DBBVabBC.mjs";
12
- import { a as signObject } from "../proof-MFz2960e.mjs";
13
- import { l as signRequest, p as verifyRequestDetailed } from "../http--IGUT1mg.mjs";
11
+ import { p as signJsonLd } from "../ld-CHYUee-s.mjs";
12
+ import { a as signObject } from "../proof-B-iLzyva.mjs";
13
+ import { l as signRequest, p as verifyRequestDetailed } from "../http-DysQjjJe.mjs";
14
14
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
15
- import { _ as coversDelivery, o as createFederation, v as forwardPortableInboxActivity, y as resolvePortableInboxForwardingOptions } from "../middleware-BW7-tHAG.mjs";
15
+ import { _ as coversDelivery, o as createFederation, v as forwardPortableInboxActivity, y as resolvePortableInboxForwardingOptions } from "../middleware-DoAk0Q3s.mjs";
16
16
  import { Create, CryptographicKey, Follow, Note, Person, Tombstone } from "@fedify/vocab";
17
17
  import { exportDidKey, formatIri, parseIri } from "@fedify/vocab-runtime";
18
18
  import { mockDocumentLoader, test } from "@fedify/fixture";
@@ -7,7 +7,7 @@ import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
8
  import { t as assert } from "../assert-D1Eafmd_.mjs";
9
9
  import { r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
10
- import { o as createFederation } from "../middleware-BW7-tHAG.mjs";
10
+ import { o as createFederation } from "../middleware-DoAk0Q3s.mjs";
11
11
  import { Note, Object as Object$1, Person } from "@fedify/vocab";
12
12
  import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
13
13
  import { test } from "@fedify/fixture";
@@ -6,9 +6,9 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
8
  import { t as assert } from "../assert-D1Eafmd_.mjs";
9
- import { a as signObject } from "../proof-MFz2960e.mjs";
9
+ import { a as signObject } from "../proof-B-iLzyva.mjs";
10
10
  import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
11
- import { o as createFederation } from "../middleware-BW7-tHAG.mjs";
11
+ import { o as createFederation } from "../middleware-DoAk0Q3s.mjs";
12
12
  import { Create, Note } from "@fedify/vocab";
13
13
  import { exportDidKey, formatIri, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
14
14
  import { mockDocumentLoader, test } from "@fedify/fixture";
@@ -8,10 +8,10 @@ import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
9
  import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
10
10
  import { t as assert } from "../assert-D1Eafmd_.mjs";
11
- import { a as signObject, c as verifyPortableObjectProof } from "../proof-MFz2960e.mjs";
12
- import { l as signRequest } from "../http--IGUT1mg.mjs";
11
+ import { a as signObject, c as verifyPortableObjectProof } from "../proof-B-iLzyva.mjs";
12
+ import { l as signRequest } from "../http-DysQjjJe.mjs";
13
13
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
14
- import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-BW7-tHAG.mjs";
14
+ import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-DoAk0Q3s.mjs";
15
15
  import { configure, reset } from "@logtape/logtape";
16
16
  import { Collection, Create, Note, Object as Object$1, PUBLIC_COLLECTION, Person, Tombstone, lookupObject } from "@fedify/vocab";
17
17
  import { exportDidKey, formatIri, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
@@ -865,6 +865,83 @@ async function captureWarnings(run) {
865
865
  }
866
866
  return records.filter((record) => record.category[0] === "fedify");
867
867
  }
868
+ test("object responses warn about compatible IDs off the owner's first gateway", async () => {
869
+ const federation = createTestFederation();
870
+ const ownerId = parseIri(`ap://${did}/users/alice`);
871
+ const gateways = [new URL("https://primary.example"), new URL("https://secondary.example")];
872
+ const secondary = await sign(new Note({
873
+ id: toCompatibleEf61Id(parseIri(`ap://${did}/notes/secondary`), gateways[1]),
874
+ attribution: ownerId,
875
+ to: PUBLIC_COLLECTION,
876
+ content: "Hello"
877
+ }));
878
+ let actorCalls = 0;
879
+ let actorLookupFails = false;
880
+ federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => {
881
+ actorCalls++;
882
+ if (actorLookupFails) throw new Error("Actor storage unavailable");
883
+ return identifier === "alice" ? new Person({
884
+ id: ownerId,
885
+ gateways
886
+ }) : null;
887
+ });
888
+ federation.setObjectDispatcher(Note, "/notes/{id}", async (_ctx, values) => {
889
+ if (values.id === "secondary") return secondary;
890
+ const note = new Note({
891
+ id: values.id === "portable" ? parseIri(`ap://${did}/notes/portable`) : values.id === "ordinary" ? new URL("https://example.com/notes/ordinary") : toCompatibleEf61Id(parseIri(`ap://${did}/notes/${values.id}`), values.id === "secondary" || values.id === "retained" ? gateways[1] : gateways[0]),
892
+ attribution: ownerId,
893
+ to: PUBLIC_COLLECTION,
894
+ content: "Hello"
895
+ });
896
+ if (values.id === "retained") {
897
+ const raw = await (await sign(note)).toJsonLd({
898
+ format: "compact",
899
+ contextLoader: mockDocumentLoader
900
+ });
901
+ const parsed = await Note.fromJsonLd(raw, {
902
+ contextLoader: mockDocumentLoader,
903
+ documentLoader: mockDocumentLoader
904
+ });
905
+ parsed.id.hostname = "primary.example";
906
+ return parsed;
907
+ }
908
+ return note;
909
+ });
910
+ federation.setObjectDispatcher(Create, "/activities/{id}", (_ctx, values) => new Create({
911
+ id: toCompatibleEf61Id(parseIri(`ap://${did}/activities/${values.id}`), gateways[1]),
912
+ actor: ownerId
913
+ }));
914
+ for (const [id, expected] of [
915
+ ["secondary", 1],
916
+ ["retained", 1],
917
+ ["primary", 0],
918
+ ["portable", 0],
919
+ ["ordinary", 0]
920
+ ]) assertEquals((await captureWarnings(async () => {
921
+ const response = await federation.fetch(new Request(`https://example.com/notes/${id}`, { headers: { Accept: ACCEPT } }), { contextData: void 0 });
922
+ assertEquals(response.status, 200);
923
+ const body = await response.json();
924
+ assertEquals(body.type, "Note");
925
+ if (id === "retained") assertEquals(body.id, `https://secondary.example/.well-known/apgateway/${did}/notes/retained`);
926
+ })).filter((r) => r.properties.kind === "object").length, expected, id);
927
+ assertEquals((await captureWarnings(async () => {
928
+ assertEquals((await federation.fetch(new Request("https://example.com/activities/1", { headers: { Accept: ACCEPT } }), { contextData: void 0 })).status, 200);
929
+ })).filter((r) => r.properties.kind === "activity").length, 1);
930
+ for (const id of ["secondary", "retained"]) assertEquals((await captureWarnings(async () => {
931
+ assertEquals((await federation.fetch(gatewayRequest(`/notes/${id}`), { contextData: void 0 })).status, 200);
932
+ })).filter((r) => r.properties.kind === "object").length, 1);
933
+ actorLookupFails = true;
934
+ const expectedBody = await secondary.toJsonLd({
935
+ format: "compact",
936
+ contextLoader: mockDocumentLoader
937
+ });
938
+ for (const request of [new Request("https://example.com/notes/secondary", { headers: { Accept: ACCEPT } }), gatewayRequest("/notes/secondary")]) assertEquals((await captureWarnings(async () => {
939
+ const response = await federation.fetch(request, { contextData: void 0 });
940
+ assertEquals(response.status, 200);
941
+ assertEquals(await response.json(), expectedBody);
942
+ })).filter((r) => r.properties.kind != null), []);
943
+ assertEquals(actorCalls, 8);
944
+ });
868
945
  test("Federation.fetch() serves compatible-ID actors", async (t) => {
869
946
  const federation = createTestFederation();
870
947
  const actorGateways = {
@@ -10,10 +10,10 @@ import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
10
10
  import "../std__assert-DvMDRPes.mjs";
11
11
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
12
12
  import { t as assert } from "../assert-D1Eafmd_.mjs";
13
- import { f as verifyRequest } from "../http--IGUT1mg.mjs";
13
+ import { f as verifyRequest } from "../http-DysQjjJe.mjs";
14
14
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
15
- import { t as doesActorOwnKey } from "../owner-BW5VsXG3.mjs";
16
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-Duj8xys1.mjs";
15
+ import { t as doesActorOwnKey } from "../owner-BP_ouAvi.mjs";
16
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-D0nNms3Q.mjs";
17
17
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
18
18
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
19
19
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
5
5
  import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-IG3pHfvZ.mjs";
6
6
  import "../../testing-t8xDeJZ5.mjs";
7
- import { o as createFederation } from "../../middleware-BW7-tHAG.mjs";
7
+ import { o as createFederation } from "../../middleware-DoAk0Q3s.mjs";
8
8
  import { n as ParallelMessageQueue } from "../../mq-Dm1Wmtm-.mjs";
9
9
  import { configure, reset } from "@logtape/logtape";
10
10
  import { delay } from "es-toolkit";
@@ -4,8 +4,8 @@ globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
5
5
  import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-IG3pHfvZ.mjs";
6
6
  import "../../testing-t8xDeJZ5.mjs";
7
- import { o as createFederation } from "../../middleware-BW7-tHAG.mjs";
8
- import { r as createFederationBuilder } from "../../builder-CKAVFvcH.mjs";
7
+ import { o as createFederation } from "../../middleware-DoAk0Q3s.mjs";
8
+ import { r as createFederationBuilder } from "../../builder-Wx64uCV2.mjs";
9
9
  import { t as TaskCodec } from "../../codec-C6rTrpIp.mjs";
10
10
  import { t as InProcessMessageQueue } from "../../mq-Dm1Wmtm-.mjs";
11
11
  import { W3CTraceContextPropagator } from "@opentelemetry/core";
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
6
  import { t as assert } from "../assert-D1Eafmd_.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-CVcPKB1N.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-39Ixir8d.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 () => {
@@ -6,7 +6,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
- import { o as createFederation, s as handleWebFinger } from "../middleware-BW7-tHAG.mjs";
9
+ import { o as createFederation, s as handleWebFinger } from "../middleware-DoAk0Q3s.mjs";
10
10
  import { Image, Link, Person, Tombstone } from "@fedify/vocab";
11
11
  import { parseIri } from "@fedify/vocab-runtime";
12
12
  import { createTestMeterProvider, test } from "@fedify/fixture";