@fedify/fedify 2.4.0-dev.2155 → 2.4.0-dev.2159

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 (102) hide show
  1. package/dist/{builder-CxHALSwA.mjs → builder-D89oOfHC.mjs} +2 -2
  2. package/dist/{client-DpnpgjyX.mjs → client-CEnNz8O7.mjs} +7 -3
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/transformers.test.mjs +1 -1
  6. package/dist/{compound-proof-nhrXTRQO.mjs → compound-proof-B02Os8h2.mjs} +2 -2
  7. package/dist/{context-OrnjnuMq.mjs → context-B2rlkkyJ.mjs} +1 -1
  8. package/dist/{context-BMggDR-l.d.cts → context-DpsaHkk1.d.cts} +119 -2
  9. package/dist/{context-exYu7gDU.d.ts → context-JDNkUtnu.d.ts} +119 -2
  10. package/dist/{deno-YLLbRq06.mjs → deno-CE_s0yQb.mjs} +1 -1
  11. package/dist/{dist-BQvArojP.mjs → dist-DJQM4u7i.mjs} +2 -1
  12. package/dist/docloader-C5p26tpa.mjs +79 -0
  13. package/dist/federation/builder.test.mjs +1 -1
  14. package/dist/federation/compound-fanout.test.mjs +3 -3
  15. package/dist/federation/handler-compound-proof.test.mjs +4 -4
  16. package/dist/federation/handler.test.mjs +6 -6
  17. package/dist/federation/hashlink-media.test.mjs +2 -2
  18. package/dist/federation/idempotency.test.mjs +2 -2
  19. package/dist/federation/metrics.test.mjs +1 -1
  20. package/dist/federation/middleware.test.mjs +187 -10
  21. package/dist/federation/mod.cjs +1 -1
  22. package/dist/federation/mod.d.cts +3 -3
  23. package/dist/federation/mod.d.ts +3 -3
  24. package/dist/federation/mod.js +1 -1
  25. package/dist/federation/mq.test.mjs +98 -69
  26. package/dist/federation/outgoing-proof.test.mjs +5 -5
  27. package/dist/federation/portable-actor.test.mjs +3 -3
  28. package/dist/federation/portable-chain.test.mjs +3 -3
  29. package/dist/federation/portable-collection.test.mjs +4 -4
  30. package/dist/federation/portable-gateway-keys.test.mjs +4 -4
  31. package/dist/federation/portable-inbox.test.mjs +4 -4
  32. package/dist/federation/portable-parse-uri.test.d.mts +2 -0
  33. package/dist/federation/portable-parse-uri.test.mjs +348 -0
  34. package/dist/federation/portable.test.mjs +3 -3
  35. package/dist/federation/send.test.mjs +84 -3
  36. package/dist/federation/tasks/enqueue.test.mjs +3 -3
  37. package/dist/federation/tasks/tasks.test.mjs +4 -4
  38. package/dist/federation/temporal.test.mjs +1 -1
  39. package/dist/federation/webfinger.test.mjs +2 -2
  40. package/dist/{federation-D9TufmIO.cjs → federation-dBOqmJcA.cjs} +74 -10
  41. package/dist/{http-CXnNbRe_.mjs → http-DV_yW4dv.mjs} +9 -5
  42. package/dist/{http-Dk4O3WM0.js → http-nTZuTacw.js} +7 -3
  43. package/dist/{http-eqYXHRkz.cjs → http-wdsia0-O.cjs} +7 -3
  44. package/dist/{key-BZ-Z4uez.mjs → key-C6jnekFI.mjs} +2 -2
  45. package/dist/{kv-cache-DzClEalR.cjs → kv-cache-BHNtQi9e.cjs} +34 -8
  46. package/dist/{kv-cache-DZr8HTel.mjs → kv-cache-Bxm0a0Ww.mjs} +1 -1
  47. package/dist/{kv-cache-3uKc0G76.js → kv-cache-DhIl9YlV.js} +35 -9
  48. package/dist/{ld-Djxsq6JR.mjs → ld-ya2C1Xip.mjs} +4 -4
  49. package/dist/{metrics-CBHhbHgG.mjs → metrics-ByOQOHHS.mjs} +1 -1
  50. package/dist/{middleware-DDa8pCUa.mjs → middleware-Bdwd_Gg8.mjs} +70 -20
  51. package/dist/{middleware-BahNnLfD.mjs → middleware-DoVWq8mE.mjs} +1 -1
  52. package/dist/{middleware-BDUR99k3.js → middleware-WiaN4RS3.js} +74 -10
  53. package/dist/{mod-Decmur1U.d.cts → mod-DSKkTwH2.d.cts} +1 -1
  54. package/dist/{mod-CZetqT5h.d.ts → mod-DaiBrWDW.d.ts} +2 -0
  55. package/dist/{mod-D4g9ffEn.d.cts → mod-Mt4aO3DX.d.cts} +2 -0
  56. package/dist/{mod-CnGtADvO.d.ts → mod-_4K05HZF.d.ts} +1 -1
  57. package/dist/mod.cjs +5 -5
  58. package/dist/mod.d.cts +5 -5
  59. package/dist/mod.d.ts +5 -5
  60. package/dist/mod.js +5 -5
  61. package/dist/nodeinfo/client.test.mjs +49 -1
  62. package/dist/nodeinfo/handler.test.mjs +2 -2
  63. package/dist/nodeinfo/mod.cjs +1 -1
  64. package/dist/nodeinfo/mod.js +1 -1
  65. package/dist/{owner-OdWSE8am.mjs → owner-C6Nv5yyw.mjs} +3 -3
  66. package/dist/{portable-collection-BCy06GiS.js → portable-collection-B-mU1F1-.js} +1 -1
  67. package/dist/{portable-collection-wBWbuHOH.cjs → portable-collection-C65SwL2l.cjs} +1 -1
  68. package/dist/{portable-collection-DAV7Ho3m.mjs → portable-collection-uWj3hfzv.mjs} +2 -2
  69. package/dist/{portable-key-BL6K88p2.mjs → portable-key-BXsVLSMa.mjs} +1 -1
  70. package/dist/{proof-vZ04IAjq.mjs → proof-BdO3BbLh.mjs} +4 -4
  71. package/dist/{send-ylV8KCLx.mjs → send-BwANxryy.mjs} +18 -4
  72. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  73. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  74. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  75. package/dist/sig/compound-proof.test.mjs +2 -2
  76. package/dist/sig/http.test.mjs +42 -2
  77. package/dist/sig/key.test.mjs +1 -1
  78. package/dist/sig/ld.test.mjs +5 -2
  79. package/dist/sig/mod.cjs +2 -2
  80. package/dist/sig/mod.js +2 -2
  81. package/dist/sig/owner.test.mjs +1 -1
  82. package/dist/sig/portable-collection.test.mjs +2 -2
  83. package/dist/sig/portable-key.test.mjs +6 -6
  84. package/dist/sig/proof.test.mjs +3 -3
  85. package/dist/{tasks-dbCShlCb.mjs → tasks-IG3pHfvZ.mjs} +1 -1
  86. package/dist/{temporal-BTD4f_mZ.mjs → temporal-CbPfRGZq.mjs} +1 -1
  87. package/dist/testing/context.test.mjs +1 -1
  88. package/dist/testing/kv.test.mjs +3 -3
  89. package/dist/testing/mod.d.mts +118 -1
  90. package/dist/testing/mod.mjs +4 -4
  91. package/dist/{testing-CKaJhcA0.mjs → testing-C1itIjqW.mjs} +3 -3
  92. package/dist/{types-C_cNlNVK.js → types-_S2_MeLF.js} +7 -3
  93. package/dist/{types-Be2zbyeG.cjs → types-poIao40f.cjs} +7 -3
  94. package/dist/utils/docloader.test.mjs +297 -2
  95. package/dist/utils/kv-cache.test.mjs +1 -1
  96. package/dist/utils/mod.cjs +1 -1
  97. package/dist/utils/mod.d.cts +1 -1
  98. package/dist/utils/mod.d.ts +1 -1
  99. package/dist/utils/mod.js +1 -1
  100. package/dist/vocab/cjs.test.mjs +1 -1
  101. package/package.json +7 -7
  102. package/dist/docloader-vc-pSu9-.mjs +0 -53
@@ -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-vZ04IAjq.mjs";
10
+ import { a as signObject } from "../proof-BdO3BbLh.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-DDa8pCUa.mjs";
13
- import { t as verifyPortableObject } from "../portable-collection-DAV7Ho3m.mjs";
14
- import { s as verifyServedPortableObjects } from "../compound-proof-nhrXTRQO.mjs";
12
+ import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-Bdwd_Gg8.mjs";
13
+ import { t as verifyPortableObject } from "../portable-collection-uWj3hfzv.mjs";
14
+ import { s as verifyServedPortableObjects } from "../compound-proof-B02Os8h2.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, Person, Tombstone, lookupObject, traverseCollection } from "@fedify/vocab";
@@ -2,15 +2,15 @@ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
5
- import { r as createRequestContext, t as createInboxContext } from "../context-OrnjnuMq.mjs";
5
+ import { r as createRequestContext, t as createInboxContext } from "../context-B2rlkkyJ.mjs";
6
6
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
7
7
  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
- import { p as signJsonLd } from "../ld-Djxsq6JR.mjs";
11
- import { l as signRequest } from "../http-CXnNbRe_.mjs";
10
+ import { p as signJsonLd } from "../ld-ya2C1Xip.mjs";
11
+ import { l as signRequest } from "../http-DV_yW4dv.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
13
- import { f as handleInbox, n as FederationImpl, o as createFederation } from "../middleware-DDa8pCUa.mjs";
13
+ import { f as handleInbox, n as FederationImpl, o as createFederation } from "../middleware-Bdwd_Gg8.mjs";
14
14
  import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
15
15
  import { t as KvKeyCache } from "../keycache-BInaECGj.mjs";
16
16
  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-Djxsq6JR.mjs";
12
- import { a as signObject } from "../proof-vZ04IAjq.mjs";
13
- import { f as verifyRequestDetailed, l as signRequest } from "../http-CXnNbRe_.mjs";
11
+ import { p as signJsonLd } from "../ld-ya2C1Xip.mjs";
12
+ import { a as signObject } from "../proof-BdO3BbLh.mjs";
13
+ import { f as verifyRequestDetailed, l as signRequest } from "../http-DV_yW4dv.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-DDa8pCUa.mjs";
15
+ import { _ as coversDelivery, o as createFederation, v as forwardPortableInboxActivity, y as resolvePortableInboxForwardingOptions } from "../middleware-Bdwd_Gg8.mjs";
16
16
  import { Create, CryptographicKey, Follow, Note, Person, Tombstone } from "@fedify/vocab";
17
17
  import { mockDocumentLoader, test } from "@fedify/fixture";
18
18
  import { exportDidKey, formatIri, parseIri } from "@fedify/vocab-runtime";
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,348 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
+ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
+ import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
7
+ import "../std__assert-DvMDRPes.mjs";
8
+ import { t as assert } from "../assert-D1Eafmd_.mjs";
9
+ import { r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
10
+ import { o as createFederation } from "../middleware-Bdwd_Gg8.mjs";
11
+ import { Note, Object as Object$1, Person } from "@fedify/vocab";
12
+ import { test } from "@fedify/fixture";
13
+ import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
14
+ //#region src/federation/portable-parse-uri.test.ts
15
+ const did = await exportDidKey(ed25519PublicKey.publicKey);
16
+ const otherDid = await exportDidKey((await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"])).publicKey);
17
+ const encodedDid = encodeURIComponent(did);
18
+ const ORIGIN = "https://example.com";
19
+ const NOTE_TYPE_ID = new URL("https://www.w3.org/ns/activitystreams#Note");
20
+ const OBJECT_TYPE_ID = new URL("https://www.w3.org/ns/activitystreams#Object");
21
+ function createTestFederation() {
22
+ const calls = [];
23
+ const federation = createFederation({ kv: new MemoryKvStore() });
24
+ federation.setActorDispatcher("/users/{identifier}", (_ctx, identifier) => {
25
+ calls.push(`actor:${identifier}`);
26
+ return new Person({});
27
+ }).mapActorAlias("/bot", "bot").mapPortableActorId((_ctx, identifier) => {
28
+ calls.push(`portableActorId:${identifier}`);
29
+ return null;
30
+ });
31
+ federation.setObjectDispatcher(Note, "/users/{identifier}/notes/{id}", () => {
32
+ calls.push("object");
33
+ return null;
34
+ });
35
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
36
+ federation.setOutboxDispatcher("/users/{identifier}/outbox", () => null);
37
+ federation.setFollowingDispatcher("/users/{identifier}/following", () => null);
38
+ federation.setFollowersDispatcher("/users/{identifier}/followers", () => null);
39
+ federation.setLikedDispatcher("/users/{identifier}/liked", () => null);
40
+ federation.setFeaturedDispatcher("/users/{identifier}/featured", () => null);
41
+ federation.setFeaturedTagsDispatcher("/users/{identifier}/tags", () => null);
42
+ federation.setCollectionDispatcher("bookmarks", Object$1, "/users/{identifier}/bookmarks", () => null).mapPortableOwner((_ctx, values) => {
43
+ calls.push(`portableOwner:${values.identifier}`);
44
+ return values.identifier;
45
+ });
46
+ federation.setOrderedCollectionDispatcher("pins", Note, "/users/{identifier}/pins", () => null);
47
+ return {
48
+ federation,
49
+ ctx: federation.createContext(new URL(ORIGIN)),
50
+ calls
51
+ };
52
+ }
53
+ function portableId(path, authority = did) {
54
+ return parseIri(`ap+ef61://${authority}${path}`);
55
+ }
56
+ function compatibleId(path, origin = ORIGIN, authority = did) {
57
+ return new URL(`${origin}/.well-known/apgateway/${authority}${path}`);
58
+ }
59
+ function parsePortable(ctx, uri) {
60
+ return ctx.parseUri(uri, { portable: true });
61
+ }
62
+ test("Context.parseUri() does not recognize portable IDs by default", () => {
63
+ const { ctx } = createTestFederation();
64
+ for (const uri of [
65
+ portableId("/users/alice"),
66
+ parseIri(`ap://${did}/users/alice`),
67
+ compatibleId("/users/alice"),
68
+ compatibleId("/users/alice", "https://gateway.example")
69
+ ]) {
70
+ assertEquals(ctx.parseUri(uri), null, uri.href);
71
+ assertEquals(ctx.parseUri(uri, {}), null, uri.href);
72
+ assertEquals(ctx.parseUri(uri, { portable: false }), null, uri.href);
73
+ }
74
+ });
75
+ test("Context.parseUri() keeps the results of ordinary URIs", () => {
76
+ const { ctx } = createTestFederation();
77
+ for (const path of [
78
+ "/users/alice",
79
+ "/bot",
80
+ "/users/alice/notes/123",
81
+ "/inbox",
82
+ "/users/alice/inbox",
83
+ "/users/alice/outbox",
84
+ "/users/alice/following",
85
+ "/users/alice/followers",
86
+ "/users/alice/liked",
87
+ "/users/alice/featured",
88
+ "/users/alice/tags",
89
+ "/users/alice/bookmarks",
90
+ "/users/alice/pins",
91
+ "/nowhere"
92
+ ]) {
93
+ const uri = new URL(path, ORIGIN);
94
+ const result = ctx.parseUri(uri);
95
+ assertEquals(parsePortable(ctx, uri), result, path);
96
+ if (result != null) assertFalse("authority" in result, path);
97
+ }
98
+ assertEquals(parsePortable(ctx, new URL("https://other.example/users/alice")), null);
99
+ assertEquals(ctx.parseUri(null, { portable: true }), null);
100
+ });
101
+ test("Context.parseUri() recognizes portable IDs", () => {
102
+ const { ctx, calls } = createTestFederation();
103
+ const identifier = "alice";
104
+ const cases = [
105
+ ["/users/alice", {
106
+ type: "actor",
107
+ identifier,
108
+ authority: did
109
+ }],
110
+ ["/bot", {
111
+ type: "actor",
112
+ identifier: "bot",
113
+ authority: did
114
+ }],
115
+ ["/users/alice/notes/123", {
116
+ type: "object",
117
+ class: Note,
118
+ typeId: NOTE_TYPE_ID,
119
+ values: {
120
+ identifier,
121
+ id: "123"
122
+ },
123
+ authority: did
124
+ }],
125
+ ["/users/alice/inbox", {
126
+ type: "inbox",
127
+ identifier,
128
+ authority: did
129
+ }],
130
+ ["/users/alice/outbox", {
131
+ type: "outbox",
132
+ identifier,
133
+ authority: did
134
+ }],
135
+ ["/users/alice/following", {
136
+ type: "following",
137
+ identifier,
138
+ authority: did
139
+ }],
140
+ ["/users/alice/followers", {
141
+ type: "followers",
142
+ identifier,
143
+ authority: did
144
+ }],
145
+ ["/users/alice/liked", {
146
+ type: "liked",
147
+ identifier,
148
+ authority: did
149
+ }],
150
+ ["/users/alice/featured", {
151
+ type: "featured",
152
+ identifier,
153
+ authority: did
154
+ }],
155
+ ["/users/alice/tags", {
156
+ type: "featuredTags",
157
+ identifier,
158
+ authority: did
159
+ }],
160
+ ["/users/alice/bookmarks", {
161
+ type: "collection",
162
+ name: "bookmarks",
163
+ class: Object$1,
164
+ typeId: OBJECT_TYPE_ID,
165
+ values: { identifier },
166
+ authority: did
167
+ }],
168
+ ["/users/alice/pins", {
169
+ type: "orderedCollection",
170
+ name: "pins",
171
+ class: Note,
172
+ typeId: NOTE_TYPE_ID,
173
+ values: { identifier },
174
+ authority: did
175
+ }]
176
+ ];
177
+ for (const [path, expected] of cases) for (const uri of [
178
+ portableId(path),
179
+ parseIri(`ap://${did}${path}`),
180
+ new URL(`ap+ef61://${encodedDid}${path}`),
181
+ new URL(`ap://${encodedDid}${path}`),
182
+ compatibleId(path),
183
+ compatibleId(path, "https://gateway.example"),
184
+ compatibleId(path, "http://localhost:8000")
185
+ ]) assertEquals(parsePortable(ctx, uri), expected, uri.href);
186
+ assertEquals(calls, []);
187
+ });
188
+ test("Context.parseUri() round-trips the portable ID helpers", () => {
189
+ const { ctx } = createTestFederation();
190
+ const uris = [
191
+ [ctx.getPortableActorUri("alice", did), "actor"],
192
+ [ctx.getPortableObjectUri(Note, {
193
+ identifier: "alice",
194
+ id: "1"
195
+ }, did), "object"],
196
+ [ctx.getPortableInboxUri("alice", did), "inbox"],
197
+ [ctx.getPortableOutboxUri("alice", did), "outbox"],
198
+ [ctx.getPortableFollowingUri("alice", did), "following"],
199
+ [ctx.getPortableFollowersUri("alice", did), "followers"],
200
+ [ctx.getPortableLikedUri("alice", did), "liked"],
201
+ [ctx.getPortableFeaturedUri("alice", did), "featured"],
202
+ [ctx.getPortableFeaturedTagsUri("alice", did), "featuredTags"],
203
+ [ctx.getPortableCollectionUri("bookmarks", { identifier: "alice" }, did), "collection"],
204
+ [ctx.getPortableCollectionUri("pins", { identifier: "alice" }, did), "orderedCollection"]
205
+ ];
206
+ for (const [uri, type] of uris) {
207
+ const result = parsePortable(ctx, uri);
208
+ assertEquals(result?.type, type, uri.href);
209
+ assertEquals(result?.authority, did, uri.href);
210
+ }
211
+ });
212
+ test("Context.parseUri() returns any DID for the caller to check", () => {
213
+ const { ctx } = createTestFederation();
214
+ assertEquals(parsePortable(ctx, portableId("/users/alice", otherDid)), {
215
+ type: "actor",
216
+ identifier: "alice",
217
+ authority: otherDid
218
+ });
219
+ assertEquals(parsePortable(ctx, compatibleId("/users/alice", "https://gateway.example", otherDid)), {
220
+ type: "actor",
221
+ identifier: "alice",
222
+ authority: otherDid
223
+ });
224
+ assertEquals(parsePortable(ctx, portableId("/users/alice", "did:web:example.com")), {
225
+ type: "actor",
226
+ identifier: "alice",
227
+ authority: "did:web:example.com"
228
+ });
229
+ assertEquals(parsePortable(ctx, parseIri(`ap://DID:KEY:${did.slice(8)}/users/alice`)), {
230
+ type: "actor",
231
+ identifier: "alice",
232
+ authority: did
233
+ });
234
+ assertEquals(parsePortable(ctx, new URL(`ap://DID%3Akey%3A${did.slice(8)}/users/alice`)), {
235
+ type: "actor",
236
+ identifier: "alice",
237
+ authority: did
238
+ });
239
+ });
240
+ test("Context.parseUri() ignores the query and fragment of portable IDs", () => {
241
+ const { ctx } = createTestFederation();
242
+ const expected = {
243
+ type: "actor",
244
+ identifier: "alice",
245
+ authority: did
246
+ };
247
+ const hint = "@gateway=https%3A%2F%2Fgateway.example";
248
+ for (const uri of [
249
+ parseIri(`ap://${did}/users/alice?${hint}`),
250
+ portableId("/users/alice#main-key"),
251
+ new URL(`${compatibleId("/users/alice").href}?${hint}`),
252
+ new URL(`${compatibleId("/users/alice").href}#main-key`)
253
+ ]) assertEquals(parsePortable(ctx, uri), expected, uri.href);
254
+ });
255
+ test("Context.parseUri() routes portable paths like ordinary ones", () => {
256
+ const { ctx } = createTestFederation();
257
+ for (const path of [
258
+ "/users/alice/",
259
+ "/users/alice/notes/",
260
+ "/users"
261
+ ]) {
262
+ const ordinary = ctx.parseUri(new URL(path, ORIGIN));
263
+ const expected = ordinary == null ? null : {
264
+ ...ordinary,
265
+ authority: did
266
+ };
267
+ assertEquals(parsePortable(ctx, portableId(path)), expected, path);
268
+ assertEquals(parsePortable(ctx, compatibleId(path)), expected, path);
269
+ }
270
+ });
271
+ test("Context.parseUri() does not recognize unroutable portable IDs", () => {
272
+ const { ctx } = createTestFederation();
273
+ for (const uri of [
274
+ portableId("/nowhere"),
275
+ compatibleId("/nowhere"),
276
+ portableId("/inbox"),
277
+ compatibleId("/inbox"),
278
+ compatibleId("/inbox", "https://gateway.example"),
279
+ new URL(`${ORIGIN}/.well-known/apgateway`),
280
+ new URL(`${ORIGIN}/.well-known/apgateway/users/alice`),
281
+ new URL(`${ORIGIN}/.well-known/apgateway/hl:zQmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n`)
282
+ ]) assertEquals(parsePortable(ctx, uri), null, uri.href);
283
+ });
284
+ test("Context.parseUri() does not recognize malformed portable IDs", () => {
285
+ const { ctx } = createTestFederation();
286
+ const base64UrlDid = `did:key:u${did.slice(8).slice(1)}`;
287
+ for (const uri of [
288
+ new URL(`ap+ef61://${encodeURIComponent(base64UrlDid)}/users/alice`),
289
+ compatibleId("/users/alice", ORIGIN, base64UrlDid),
290
+ new URL("ap+ef61://did%3Akey/users/alice"),
291
+ new URL("ap+ef61://example.com/users/alice"),
292
+ compatibleId("/users/alice", ORIGIN, "did:"),
293
+ compatibleId("/users/alice", "https://gateway.example", "did:key"),
294
+ new URL(`ap+ef61://${encodedDid}/users/%ZZ`),
295
+ new URL(`${compatibleId("/users/").href}%ZZ`),
296
+ new URL(`ap+ef61://user:pass@${encodedDid}/users/alice`),
297
+ new URL(`https://user:pass@gateway.example/.well-known/apgateway/${did}/users/alice`)
298
+ ]) assertEquals(parsePortable(ctx, uri), null, uri.href);
299
+ });
300
+ test("Context.parseUri() prefers the application's routes", () => {
301
+ const federation = createFederation({ kv: new MemoryKvStore() });
302
+ federation.setActorDispatcher("/users/{identifier}", () => null);
303
+ federation.setObjectDispatcher(Note, "/.well-known/apgateway/{+did}/users/{identifier}/notes/{id}", () => null);
304
+ federation.setNodeInfoDispatcher(`/.well-known/apgateway/${did}/users/alice`, () => ({
305
+ software: {
306
+ name: "test",
307
+ version: "1.0.0"
308
+ },
309
+ protocols: ["activitypub"],
310
+ usage: {
311
+ users: {},
312
+ localPosts: 0,
313
+ localComments: 0
314
+ }
315
+ }));
316
+ const ctx = federation.createContext(new URL(ORIGIN));
317
+ assertEquals(parsePortable(ctx, compatibleId("/users/alice")), null);
318
+ assertEquals(parsePortable(ctx, compatibleId("/users/alice", "https://gateway.example")), {
319
+ type: "actor",
320
+ identifier: "alice",
321
+ authority: did
322
+ });
323
+ const note = parsePortable(ctx, compatibleId("/users/alice/notes/1"));
324
+ assert(note?.type === "object");
325
+ assertEquals(note.values, {
326
+ did,
327
+ identifier: "alice",
328
+ id: "1"
329
+ });
330
+ assertFalse("authority" in note);
331
+ assertEquals(parsePortable(ctx, portableId("/users/alice")), {
332
+ type: "actor",
333
+ identifier: "alice",
334
+ authority: did
335
+ });
336
+ });
337
+ test("RequestContext.parseUri() recognizes portable IDs", () => {
338
+ const { federation } = createTestFederation();
339
+ const ctx = federation.createContext(new Request(compatibleId("/users/alice")), void 0);
340
+ assertEquals(ctx.parseUri(portableId("/users/alice")), null);
341
+ assertEquals(ctx.parseUri(portableId("/users/alice", otherDid), { portable: true }), {
342
+ type: "actor",
343
+ identifier: "alice",
344
+ authority: otherDid
345
+ });
346
+ });
347
+ //#endregion
348
+ export {};
@@ -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-vZ04IAjq.mjs";
12
- import { l as signRequest } from "../http-CXnNbRe_.mjs";
11
+ import { a as signObject, c as verifyPortableObjectProof } from "../proof-BdO3BbLh.mjs";
12
+ import { l as signRequest } from "../http-DV_yW4dv.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-DDa8pCUa.mjs";
14
+ import { b as PORTABLE_OBJECT_CONTENT_TYPE, o as createFederation } from "../middleware-Bdwd_Gg8.mjs";
15
15
  import { configure, reset } from "@logtape/logtape";
16
16
  import { Collection, Create, Note, Object as Object$1, Person, Tombstone, lookupObject } from "@fedify/vocab";
17
17
  import { mockDocumentLoader, test } from "@fedify/fixture";
@@ -10,13 +10,14 @@ 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 { d as verifyRequest } from "../http-CXnNbRe_.mjs";
13
+ import { d as verifyRequest } from "../http-DV_yW4dv.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-OdWSE8am.mjs";
16
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-ylV8KCLx.mjs";
15
+ import { t as doesActorOwnKey } from "../owner-C6Nv5yyw.mjs";
16
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-BwANxryy.mjs";
17
17
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
18
18
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
19
19
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
20
+ import dns from "node:dns/promises";
20
21
  import { AggregationTemporality, InMemoryMetricExporter, MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
21
22
  //#region src/federation/send.test.ts
22
23
  test("extractInboxes()", () => {
@@ -690,5 +691,85 @@ for (const signed of [false, true]) {
690
691
  });
691
692
  });
692
693
  }
694
+ for (const signed of [false, true]) test({
695
+ name: `sendActivity() classifies DNS failures (signed: ${signed})`,
696
+ ignore: "Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted",
697
+ async fn(t) {
698
+ const activity = {
699
+ type: "Create",
700
+ id: "https://example.com/activity"
701
+ };
702
+ const destination = "https://delivery.invalid/inbox";
703
+ const publicInbox = "https://8.8.8.8/inbox";
704
+ const keys = signed ? [{
705
+ privateKey: rsaPrivateKey2,
706
+ keyId: rsaPublicKey2.id
707
+ }] : [];
708
+ for (const result of [
709
+ "throws",
710
+ "empty",
711
+ "cname",
712
+ "private"
713
+ ]) for (const redirected of [false, true]) await t.step(`${result}, redirected: ${redirected}`, async () => {
714
+ const [meterProvider, recorder] = createTestMeterProvider();
715
+ const originalLookup = dns.lookup;
716
+ const resolverError = /* @__PURE__ */ new Error("Resolver unavailable");
717
+ const lookups = [];
718
+ dns.lookup = ((hostname, options) => {
719
+ lookups.push(hostname);
720
+ assertEquals(hostname, "delivery.invalid");
721
+ assertEquals(options, { all: true });
722
+ if (result === "throws") return Promise.reject(resolverError);
723
+ return Promise.resolve(result === "empty" ? [] : [{
724
+ address: result === "private" ? "127.0.0.1" : "alias.invalid.",
725
+ family: 4
726
+ }]);
727
+ });
728
+ try {
729
+ esm_default.mockGlobal().catch(202);
730
+ if (redirected) esm_default.route(publicInbox, {
731
+ status: 307,
732
+ headers: { Location: destination }
733
+ });
734
+ const send = () => sendActivity({
735
+ activity,
736
+ activityType: "https://www.w3.org/ns/activitystreams#Create",
737
+ meterProvider,
738
+ keys,
739
+ inbox: new URL(redirected ? publicInbox : destination)
740
+ });
741
+ if (result === "private") assertEquals((await assertRejects(send, UrlError)).reason, "disallowed");
742
+ else {
743
+ const error = await assertRejects(send, FetchError);
744
+ assertEquals(error.url.href, destination);
745
+ assertInstanceOf(error.cause, UrlError);
746
+ assertEquals(error.cause.reason, "dns");
747
+ assertEquals(error.cause.cause, result === "throws" ? resolverError : void 0);
748
+ }
749
+ const expectedCount = result === "private" && !redirected ? 0 : 1;
750
+ const sent = recorder.getMeasurements("activitypub.delivery.sent");
751
+ const durations = recorder.getMeasurements("activitypub.delivery.duration");
752
+ assertEquals(sent.length, expectedCount);
753
+ assertEquals(durations.length, expectedCount);
754
+ if (expectedCount === 1) {
755
+ assertEquals(sent[0].value, 1);
756
+ assertEquals(sent[0].attributes, {
757
+ "activitypub.remote.host": redirected ? "8.8.8.8" : "delivery.invalid",
758
+ "activitypub.activity.type": "https://www.w3.org/ns/activitystreams#Create",
759
+ "activitypub.delivery.success": false
760
+ });
761
+ assertEquals(durations[0].attributes, sent[0].attributes);
762
+ assertGreaterOrEqual(durations[0].value, 0);
763
+ }
764
+ assertEquals(lookups, ["delivery.invalid"]);
765
+ assertEquals(esm_default.callHistory.calls(destination).length, 0);
766
+ assertEquals(esm_default.callHistory.calls().length, redirected ? 1 : 0);
767
+ } finally {
768
+ dns.lookup = originalLookup;
769
+ esm_default.hardReset();
770
+ }
771
+ });
772
+ }
773
+ });
693
774
  //#endregion
694
775
  export {};
@@ -2,9 +2,9 @@ import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
5
- import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-dbCShlCb.mjs";
6
- import "../../testing-CKaJhcA0.mjs";
7
- import { o as createFederation } from "../../middleware-DDa8pCUa.mjs";
5
+ import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-IG3pHfvZ.mjs";
6
+ import "../../testing-C1itIjqW.mjs";
7
+ import { o as createFederation } from "../../middleware-Bdwd_Gg8.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";
@@ -2,10 +2,10 @@ import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
5
- import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-dbCShlCb.mjs";
6
- import "../../testing-CKaJhcA0.mjs";
7
- import { o as createFederation } from "../../middleware-DDa8pCUa.mjs";
8
- import { r as createFederationBuilder } from "../../builder-CxHALSwA.mjs";
5
+ import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-IG3pHfvZ.mjs";
6
+ import "../../testing-C1itIjqW.mjs";
7
+ import { o as createFederation } from "../../middleware-Bdwd_Gg8.mjs";
8
+ import { r as createFederationBuilder } from "../../builder-D89oOfHC.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-BTD4f_mZ.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-CbPfRGZq.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 "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { r as createRequestContext } from "../context-OrnjnuMq.mjs";
4
+ import { r as createRequestContext } from "../context-B2rlkkyJ.mjs";
5
5
  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-DDa8pCUa.mjs";
9
+ import { o as createFederation, s as handleWebFinger } from "../middleware-Bdwd_Gg8.mjs";
10
10
  import { Image, Link, Person, Tombstone } from "@fedify/vocab";
11
11
  import { createTestMeterProvider, test } from "@fedify/fixture";
12
12
  import { parseIri } from "@fedify/vocab-runtime";