@fedify/fedify 2.4.0-pr.936.41 → 2.5.0-dev.2271

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 (244) hide show
  1. package/README.md +50 -39
  2. package/dist/{assert-CJ1KXcvv.mjs → assert-D1Eafmd_.mjs} +1 -1
  3. package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-Z6IcEbWU.mjs} +2 -28
  4. package/dist/assert_exists-cffSpJ7X.mjs +28 -0
  5. package/dist/assert_false-CPdRnc3E.mjs +23 -0
  6. package/dist/assert_greater_or_equal-BlafOF4-.mjs +31 -0
  7. package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-mrXWWNY-.mjs} +1 -1
  8. package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-CpPK2t89.mjs} +2 -1
  9. package/dist/assert_rejects-CTptdYck.mjs +39 -0
  10. package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-CYvBmkpC.mjs} +2 -1
  11. package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BYITOvPO.mjs} +2 -1
  12. package/dist/assertion_error-B-cx0fFA.mjs +31 -0
  13. package/dist/audience-CHQ-FlLD.mjs +95 -0
  14. package/dist/{builder-DonuxRl_.mjs → builder-Bda6LK7A.mjs} +169 -14
  15. package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-XJ53md4S.mjs} +224 -17
  16. package/dist/circuit-breaker-test-utils-DW6YC_Pe.mjs +14 -0
  17. package/dist/{client-CjF5IYQS.mjs → client-CEnNz8O7.mjs} +144 -8
  18. package/dist/{codec-BBga-FWI.mjs → codec-C6rTrpIp.mjs} +26 -3
  19. package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
  20. package/dist/compat/mod.cjs +1 -1
  21. package/dist/compat/mod.d.cts +1 -1
  22. package/dist/compat/mod.d.ts +1 -1
  23. package/dist/compat/mod.js +1 -1
  24. package/dist/compat/outgoing-jsonld.test.mjs +71 -3
  25. package/dist/compat/public-audience.test.mjs +14 -5
  26. package/dist/compat/secured-document.test.d.mts +2 -0
  27. package/dist/compat/secured-document.test.mjs +58 -0
  28. package/dist/compat/transformers.test.mjs +5 -5
  29. package/dist/compound-proof-B-9mXxFW.mjs +989 -0
  30. package/dist/{context-Zii1f5xS.d.ts → context-7NyiqIcg.d.ts} +1600 -25
  31. package/dist/{context-BJXxPElm.mjs → context-8Tu9kea0.mjs} +21 -63
  32. package/dist/{context-lxX6I7as.d.cts → context-tsYPJ2Ew.d.cts} +1600 -25
  33. package/dist/{deno-BHzKn9Ig.mjs → deno-oR5aJKpU.mjs} +1 -1
  34. package/dist/dist-DJQM4u7i.mjs +189 -0
  35. package/dist/docloader-DBoYbvYW.mjs +87 -0
  36. package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
  37. package/dist/federation/audience.test.d.mts +2 -0
  38. package/dist/federation/audience.test.mjs +130 -0
  39. package/dist/federation/builder.test.mjs +54 -5
  40. package/dist/federation/circuit-breaker.test.mjs +838 -7
  41. package/dist/federation/collection.test.mjs +3 -3
  42. package/dist/federation/compound-fanout.test.d.mts +2 -0
  43. package/dist/federation/compound-fanout.test.mjs +228 -0
  44. package/dist/federation/handler-compound-proof.test.d.mts +2 -0
  45. package/dist/federation/handler-compound-proof.test.mjs +384 -0
  46. package/dist/federation/handler.test.mjs +385 -15
  47. package/dist/federation/hashlink-media.test.d.mts +2 -0
  48. package/dist/federation/hashlink-media.test.mjs +422 -0
  49. package/dist/federation/idempotency.test.mjs +6 -6
  50. package/dist/federation/inbox-report.test.d.mts +2 -0
  51. package/dist/federation/inbox-report.test.mjs +475 -0
  52. package/dist/federation/inbox.test.mjs +3 -3
  53. package/dist/federation/keycache.test.mjs +215 -14
  54. package/dist/federation/kv.test.mjs +3 -3
  55. package/dist/federation/metrics.test.mjs +5 -5
  56. package/dist/federation/middleware.test.mjs +967 -35
  57. package/dist/federation/mod.cjs +18 -261
  58. package/dist/federation/mod.d.cts +5 -5
  59. package/dist/federation/mod.d.ts +5 -5
  60. package/dist/federation/mod.js +279 -1
  61. package/dist/federation/mq.test.mjs +123 -47
  62. package/dist/federation/negotiation.test.mjs +4 -4
  63. package/dist/federation/outgoing-proof.test.d.mts +2 -0
  64. package/dist/federation/outgoing-proof.test.mjs +931 -0
  65. package/dist/federation/portable-actor.test.d.mts +2 -0
  66. package/dist/federation/portable-actor.test.mjs +496 -0
  67. package/dist/federation/portable-chain.test.d.mts +2 -0
  68. package/dist/federation/portable-chain.test.mjs +370 -0
  69. package/dist/federation/portable-collection.test.d.mts +2 -0
  70. package/dist/federation/portable-collection.test.mjs +832 -0
  71. package/dist/federation/portable-delivery.test.d.mts +2 -0
  72. package/dist/federation/portable-delivery.test.mjs +746 -0
  73. package/dist/federation/portable-gateway-keys.test.d.mts +2 -0
  74. package/dist/federation/portable-gateway-keys.test.mjs +686 -0
  75. package/dist/federation/portable-inbox.test.d.mts +2 -0
  76. package/dist/federation/portable-inbox.test.mjs +1535 -0
  77. package/dist/federation/portable-parse-uri.test.d.mts +2 -0
  78. package/dist/federation/portable-parse-uri.test.mjs +348 -0
  79. package/dist/federation/portable-route-activity.test.d.mts +2 -0
  80. package/dist/federation/portable-route-activity.test.mjs +259 -0
  81. package/dist/federation/portable.test.d.mts +2 -0
  82. package/dist/federation/portable.test.mjs +1072 -0
  83. package/dist/federation/retry.test.mjs +4 -3
  84. package/dist/federation/router.test.mjs +9 -159
  85. package/dist/federation/send.test.mjs +245 -27
  86. package/dist/federation/tasks/codec.test.mjs +4 -4
  87. package/dist/federation/tasks/enqueue.test.mjs +42 -4
  88. package/dist/federation/tasks/tasks.test.mjs +7 -6
  89. package/dist/federation/temporal.test.mjs +4 -4
  90. package/dist/federation/webfinger.test.mjs +275 -12
  91. package/dist/{middleware-FrSMAYEx.cjs → federation-CkZiAcWY.cjs} +8640 -3722
  92. package/dist/http-B5JT115n.cjs +10630 -0
  93. package/dist/http-DydWCB7R.js +6527 -0
  94. package/dist/{http-CPVMUgsW.mjs → http-siciWkmx.mjs} +397 -168
  95. package/dist/key-BspPiXqn.mjs +951 -0
  96. package/dist/key-C1nv7_jD.d.ts +1125 -0
  97. package/dist/key-DYxuyd-s.d.cts +1125 -0
  98. package/dist/key-owner-evidence-APB6NpSk.mjs +108 -0
  99. package/dist/keycache-Da8A8mZa.mjs +200 -0
  100. package/dist/{kv-D6hNiMTK.d.ts → kv-B2mzK5tl.d.cts} +3 -2
  101. package/dist/{kv-gJ8LYbxX.d.cts → kv-B2mzK5tl.d.ts} +3 -2
  102. package/dist/kv-DBRWVx3Q.mjs +104 -0
  103. package/dist/{kv-cache-KyGqJqSp.mjs → kv-cache-BF1hnW65.mjs} +1 -1
  104. package/dist/{kv-cache-5bnX3-Jq.js → kv-cache-CTQr1tbn.js} +54 -16
  105. package/dist/{kv-cache-DQ29C4XL.cjs → kv-cache-DCac1nEU.cjs} +55 -19
  106. package/dist/{ld-Cj9F_5AP.mjs → ld-CzaP-hJl.mjs} +44 -12
  107. package/dist/map-local-context-conflict-DOYEEJ3u.mjs +204 -0
  108. package/dist/map-local-create-note-B0SeXJms.mjs +214 -0
  109. package/dist/{metrics-DMk-nTxI.mjs → metrics-CYg9yxBL.mjs} +3 -2
  110. package/dist/metrics-DVKYY6K1.d.cts +36 -0
  111. package/dist/metrics-DoRtI0qU.d.ts +36 -0
  112. package/dist/{middleware-Y-kpsp6S.js → middleware-CsDDXKuL.js} +5493 -1103
  113. package/dist/{middleware-CvwTUgnP.mjs → middleware-D7_LO-Vs.mjs} +3157 -272
  114. package/dist/{middleware-CEctxBN6.mjs → middleware-DWjrfpcz.mjs} +1 -1
  115. package/dist/{mod-gOlPDuT8.d.cts → mod-BojP2Bw3.d.cts} +1 -1
  116. package/dist/mod-CavhAgLw.d.cts +320 -0
  117. package/dist/mod-CtpF2BU5.d.ts +320 -0
  118. package/dist/{mod-BtEaIEcD.d.cts → mod-DEZXwV6J.d.ts} +10 -4
  119. package/dist/{mod-UMmveiO0.d.ts → mod-Dw-nPxQh.d.cts} +10 -4
  120. package/dist/{mod-DLc2Ot6f.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
  121. package/dist/mod.cjs +40 -39
  122. package/dist/mod.d.cts +10 -11
  123. package/dist/mod.d.ts +10 -11
  124. package/dist/mod.js +9 -9
  125. package/dist/{mq-llHKivST.mjs → mq-Dm1Wmtm-.mjs} +6 -0
  126. package/dist/nodeinfo/client.test.mjs +139 -4
  127. package/dist/nodeinfo/handler.test.mjs +5 -5
  128. package/dist/nodeinfo/mod.cjs +1 -1
  129. package/dist/nodeinfo/mod.d.cts +1 -1
  130. package/dist/nodeinfo/mod.d.ts +1 -1
  131. package/dist/nodeinfo/mod.js +1 -1
  132. package/dist/nodeinfo/types.test.mjs +4 -4
  133. package/dist/otel/exporter.test.mjs +3 -3
  134. package/dist/otel/mod.cjs +0 -1
  135. package/dist/otel/mod.d.cts +1 -1
  136. package/dist/otel/mod.d.ts +1 -1
  137. package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-4IJqPYos.mjs} +12 -10
  138. package/dist/{owner-DEvZuyOE.d.cts → owner-BF0L9cMt.d.cts} +11 -0
  139. package/dist/{owner-CnngXDNJ.d.ts → owner-BSzr5-I8.d.ts} +11 -0
  140. package/dist/owner-DYgfTFHy.mjs +267 -0
  141. package/dist/portable-collection-BpsVsRov.js +670 -0
  142. package/dist/portable-collection-C74P1vtC.mjs +418 -0
  143. package/dist/portable-collection-DZd2bm7M.cjs +686 -0
  144. package/dist/portable-key-BIMLrAP4.mjs +549 -0
  145. package/dist/portable-key-id-Ca30-hOa.mjs +248 -0
  146. package/dist/proof-Dxy6ByqI.mjs +1299 -0
  147. package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--klBIOk1.mjs} +15 -10
  148. package/dist/router-u5O5KCQr.mjs +156 -0
  149. package/dist/runtime/mod.cjs +1 -1
  150. package/dist/runtime/mod.js +1 -1
  151. package/dist/secured-document-CxzZZ9nL.mjs +35 -0
  152. package/dist/send-C4dDpAxm.mjs +495 -0
  153. package/dist/sig/accept.test.mjs +1 -1
  154. package/dist/sig/compound-proof-discovery.test.d.mts +2 -0
  155. package/dist/sig/compound-proof-discovery.test.mjs +331 -0
  156. package/dist/sig/compound-proof-producer.test.d.mts +2 -0
  157. package/dist/sig/compound-proof-producer.test.mjs +107 -0
  158. package/dist/sig/compound-proof-verification.test.d.mts +2 -0
  159. package/dist/sig/compound-proof-verification.test.mjs +833 -0
  160. package/dist/sig/compound-proof.test.d.mts +2 -0
  161. package/dist/sig/compound-proof.test.mjs +411 -0
  162. package/dist/sig/http.test.mjs +702 -31
  163. package/dist/sig/key-owner-evidence.test.d.mts +2 -0
  164. package/dist/sig/key-owner-evidence.test.mjs +100 -0
  165. package/dist/sig/key.test.mjs +220 -8
  166. package/dist/sig/ld.test.mjs +65 -7
  167. package/dist/sig/mitra-receive-interop.test.d.mts +2 -0
  168. package/dist/sig/mitra-receive-interop.test.mjs +260 -0
  169. package/dist/sig/mitra-send-interop.test.d.mts +2 -0
  170. package/dist/sig/mitra-send-interop.test.mjs +461 -0
  171. package/dist/sig/mod.cjs +18 -16
  172. package/dist/sig/mod.d.cts +4 -4
  173. package/dist/sig/mod.d.ts +4 -4
  174. package/dist/sig/mod.js +3 -3
  175. package/dist/sig/owner.test.mjs +108 -9
  176. package/dist/sig/portable-collection.test.d.mts +2 -0
  177. package/dist/sig/portable-collection.test.mjs +697 -0
  178. package/dist/sig/portable-interop.test.d.mts +2 -0
  179. package/dist/sig/portable-interop.test.mjs +156 -0
  180. package/dist/sig/portable-key-id.test.d.mts +2 -0
  181. package/dist/sig/portable-key-id.test.mjs +67 -0
  182. package/dist/sig/portable-key.test.d.mts +2 -0
  183. package/dist/sig/portable-key.test.mjs +1439 -0
  184. package/dist/sig/proof.test.mjs +1753 -16
  185. package/dist/sig/tootik-request-interop.test.d.mts +2 -0
  186. package/dist/sig/tootik-request-interop.test.mjs +190 -0
  187. package/dist/sig/tootik-send-interop.test.d.mts +2 -0
  188. package/dist/sig/tootik-send-interop.test.mjs +331 -0
  189. package/dist/sig/verification.test.d.mts +2 -0
  190. package/dist/sig/verification.test.mjs +310 -0
  191. package/dist/std__assert-DvMDRPes.mjs +8 -0
  192. package/dist/{testing-CO7ftO8A.mjs → tasks-IG3pHfvZ.mjs} +2 -2
  193. package/dist/{temporal-fsXO5IvD.mjs → temporal-D0KJI4NF.mjs} +3 -3
  194. package/dist/testing/context.test.d.mts +2 -0
  195. package/dist/testing/context.test.mjs +19 -0
  196. package/dist/testing/kv.test.d.mts +2 -0
  197. package/dist/testing/kv.test.mjs +101 -0
  198. package/dist/testing/mod.d.mts +1617 -48
  199. package/dist/testing/mod.mjs +6 -3
  200. package/dist/testing-t8xDeJZ5.mjs +8 -0
  201. package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
  202. package/dist/{types-DC1pNgJA.js → types-_S2_MeLF.js} +144 -8
  203. package/dist/{types-CPym83zc.cjs → types-poIao40f.cjs} +160 -7
  204. package/dist/utils/docloader.test.mjs +530 -8
  205. package/dist/utils/kv-cache.test.mjs +2 -2
  206. package/dist/utils/mod.cjs +1 -1
  207. package/dist/utils/mod.d.cts +1 -1
  208. package/dist/utils/mod.d.ts +1 -1
  209. package/dist/utils/mod.js +1 -1
  210. package/dist/verification-C3954hQh.mjs +5 -0
  211. package/dist/verification-Dpfcw8ml.mjs +105 -0
  212. package/dist/vocab/cjs.test.mjs +1 -1
  213. package/package.json +8 -8
  214. package/dist/assert_rejects-EgstcQy5.mjs +0 -82
  215. package/dist/docloader-e1khNUKy.mjs +0 -49
  216. package/dist/http-CsmQ-BQ4.js +0 -2483
  217. package/dist/http-VyDTd4G3.d.cts +0 -553
  218. package/dist/http-hlIjmewS.cjs +0 -2694
  219. package/dist/http-lf8Hsd91.d.ts +0 -553
  220. package/dist/key-DfPsX8hv.mjs +0 -453
  221. package/dist/keycache-_DHqz5z_.mjs +0 -97
  222. package/dist/metrics-CoUJLQhZ.d.cts +0 -276
  223. package/dist/metrics-Dc4jR-IC.d.ts +0 -276
  224. package/dist/mod-B0hW12_O.d.cts +0 -307
  225. package/dist/mod-COIAjwRS.d.ts +0 -307
  226. package/dist/owner-Br9YYzn_.mjs +0 -120
  227. package/dist/proof-B4ngoaNQ.mjs +0 -316
  228. package/dist/proof-B6E0XUXJ.js +0 -1415
  229. package/dist/proof-NN4xCQGL.cjs +0 -1562
  230. package/dist/send-CxHKUNzv.mjs +0 -239
  231. package/dist/std__assert-B2NnY9bq.mjs +0 -79
  232. package/dist/temporal-polyfill-BCdTHexd.cjs +0 -3565
  233. /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
  234. /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
  235. /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
  236. /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
  237. /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
  238. /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
  239. /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
  240. /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
  241. /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
  242. /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
  243. /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
  244. /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
@@ -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-D7_LO-Vs.mjs";
11
+ import { Note, Object as Object$1, Person } from "@fedify/vocab";
12
+ import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
13
+ import { test } from "@fedify/fixture";
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 {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,259 @@
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 { a as signObject } from "../proof-Dxy6ByqI.mjs";
10
+ import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
11
+ import { o as createFederation } from "../middleware-D7_LO-Vs.mjs";
12
+ import { Create, Note } from "@fedify/vocab";
13
+ import { exportDidKey, formatIri, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
14
+ import { mockDocumentLoader, test } from "@fedify/fixture";
15
+ //#region src/federation/portable-route-activity.test.ts
16
+ const did = await exportDidKey(ed25519PublicKey.publicKey);
17
+ const keyId = new URL(`${did}#${did.slice(8)}`);
18
+ const otherKeyPair = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
19
+ const otherDid = await exportDidKey(otherKeyPair.publicKey);
20
+ const otherKeyId = new URL(`${otherDid}#${otherDid.slice(8)}`);
21
+ const GATEWAY = "https://gw.example";
22
+ function portableId(authority, path) {
23
+ return parseIri(`ap+ef61://${authority}${path}`);
24
+ }
25
+ function compatibleId(authority, path, gateway = GATEWAY) {
26
+ return new URL(`${gateway}/.well-known/apgateway/${authority}${path}`);
27
+ }
28
+ async function sign(activity, signer) {
29
+ return await signObject(activity, signer === "did" ? ed25519PrivateKey : otherKeyPair.privateKey, signer === "did" ? keyId : otherKeyId, { contextLoader: mockDocumentLoader });
30
+ }
31
+ /**
32
+ * Routes a `Create` activity with the given ID through
33
+ * `Context.routeActivity()`. Unless `activity` is an `Activity`, the routed
34
+ * activity has no proof, so that it is trusted only if it can be dereferenced
35
+ * by its ID, which serves `served`.
36
+ * @returns Whether the activity was routed, and the IDs of the actors of
37
+ * the activities that reached the inbox listener.
38
+ */
39
+ async function route(activity, { served, actors } = {}) {
40
+ const documents = /* @__PURE__ */ new Map();
41
+ if (served != null && activity instanceof URL) {
42
+ const document = await served.toJsonLd({ contextLoader: mockDocumentLoader });
43
+ documents.set(activity.href, document);
44
+ if (activity.protocol === "ap+ef61:") {
45
+ documents.set(formatIri(activity), document);
46
+ documents.set(toCompatibleEf61Id(activity, GATEWAY).href, document);
47
+ }
48
+ }
49
+ const documentLoader = (url, options) => {
50
+ if (documents.has(url)) return Promise.resolve({
51
+ contextUrl: null,
52
+ documentUrl: url,
53
+ document: documents.get(url)
54
+ });
55
+ return mockDocumentLoader(url, options);
56
+ };
57
+ const federation = createFederation({
58
+ kv: new MemoryKvStore(),
59
+ documentLoaderFactory: () => documentLoader,
60
+ contextLoaderFactory: () => mockDocumentLoader
61
+ });
62
+ const received = [];
63
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Create, (_ctx, create) => {
64
+ received.push(...create.actorIds.map((id) => id.href));
65
+ });
66
+ return {
67
+ routed: await federation.createContext(new URL("https://example.com/")).routeActivity(null, activity instanceof URL ? new Create({
68
+ id: activity,
69
+ actors: [...actors ?? served?.actorIds ?? []]
70
+ }) : activity),
71
+ received
72
+ };
73
+ }
74
+ async function portableCreate(id, actors, signer = "did") {
75
+ return await sign(new Create({
76
+ id,
77
+ actors: [...actors],
78
+ object: new Note({ content: "Hello" })
79
+ }), signer);
80
+ }
81
+ test("Context.routeActivity() rejects portable activities attributed to other DIDs", async (t) => {
82
+ const cases = [
83
+ [
84
+ "ap+ef61: IDs",
85
+ portableId(did, "/activities/1"),
86
+ [portableId(otherDid, "/actor")]
87
+ ],
88
+ [
89
+ "compatible identifiers on one gateway",
90
+ compatibleId(did, "/activities/1"),
91
+ [compatibleId(otherDid, "/actor")]
92
+ ],
93
+ [
94
+ "compatible activity ID and ap+ef61: actor ID",
95
+ compatibleId(did, "/activities/1"),
96
+ [portableId(otherDid, "/actor")]
97
+ ],
98
+ [
99
+ "ap+ef61: activity ID and compatible actor ID",
100
+ portableId(did, "/activities/1"),
101
+ [compatibleId(otherDid, "/actor")]
102
+ ],
103
+ [
104
+ "one of multiple actors with another DID",
105
+ portableId(did, "/activities/1"),
106
+ [portableId(did, "/actor"), portableId(otherDid, "/actor")]
107
+ ],
108
+ [
109
+ "ap+ef61: activity ID and HTTP(S) actor ID",
110
+ portableId(did, "/activities/1"),
111
+ [new URL("https://example.com/person")]
112
+ ],
113
+ [
114
+ "compatible activity ID and HTTP(S) actor ID on the gateway",
115
+ compatibleId(did, "/activities/1"),
116
+ [new URL(`${GATEWAY}/users/alice`)]
117
+ ]
118
+ ];
119
+ for (const [name, id, actors] of cases) await t.step(name, async () => {
120
+ const served = await portableCreate(id, actors);
121
+ assertEquals(await route(id, { served }), {
122
+ routed: false,
123
+ received: []
124
+ });
125
+ assertEquals(await route(served), {
126
+ routed: false,
127
+ received: []
128
+ });
129
+ });
130
+ });
131
+ test("Context.routeActivity() accepts portable activities of the same DID", async (t) => {
132
+ const cases = [
133
+ [
134
+ "ap+ef61: IDs",
135
+ portableId(did, "/activities/1"),
136
+ [portableId(did, "/actor")]
137
+ ],
138
+ [
139
+ "compatible identifiers on one gateway",
140
+ compatibleId(did, "/activities/1"),
141
+ [compatibleId(did, "/actor")]
142
+ ],
143
+ [
144
+ "compatible identifiers on different gateways",
145
+ compatibleId(did, "/activities/1"),
146
+ [compatibleId(did, "/actor", "https://gw2.example")]
147
+ ],
148
+ [
149
+ "compatible activity ID and ap+ef61: actor ID",
150
+ compatibleId(did, "/activities/1"),
151
+ [portableId(did, "/actor")]
152
+ ],
153
+ [
154
+ "ap+ef61: activity ID and compatible actor ID",
155
+ portableId(did, "/activities/1"),
156
+ [compatibleId(did, "/actor")]
157
+ ],
158
+ [
159
+ "actor ID with a location hint",
160
+ portableId(did, "/activities/1"),
161
+ [portableId(did, `/actor?@gateway=${encodeURIComponent(GATEWAY)}`)]
162
+ ]
163
+ ];
164
+ for (const [name, id, actors] of cases) await t.step(name, async () => {
165
+ const served = await portableCreate(id, actors);
166
+ const expected = {
167
+ routed: true,
168
+ received: actors.map((actor) => actor.href)
169
+ };
170
+ assertEquals(await route(id, { served }), expected);
171
+ assertEquals(await route(served), expected);
172
+ });
173
+ });
174
+ test("Context.routeActivity() compares portable activity IDs canonically", async (t) => {
175
+ const id = portableId(did, "/activities/1");
176
+ const actors = [portableId(did, "/actor")];
177
+ await t.step("compatible ID serving an ap+ef61: ID", async () => {
178
+ const served = await portableCreate(id, actors);
179
+ const { routed } = await route(compatibleId(did, "/activities/1"), { served });
180
+ assert(routed);
181
+ });
182
+ await t.step("ap+ef61: ID serving a compatible ID", async () => {
183
+ const served = await portableCreate(compatibleId(did, "/activities/1"), actors);
184
+ const { routed } = await route(id, { served });
185
+ assert(routed);
186
+ });
187
+ await t.step("ap+ef61: ID with a location hint", async () => {
188
+ const served = await portableCreate(id, actors);
189
+ const { routed } = await route(portableId(did, `/activities/1?@gateway=${encodeURIComponent(GATEWAY)}`), { served });
190
+ assert(routed);
191
+ });
192
+ await t.step("another path of the same DID", async () => {
193
+ const served = await portableCreate(portableId(did, "/activities/2"), actors);
194
+ const { routed } = await route(id, { served });
195
+ assertFalse(routed);
196
+ });
197
+ await t.step("another fragment of the same DID", async () => {
198
+ const served = await portableCreate(portableId(did, "/activities/1#other"), actors);
199
+ const { routed } = await route(id, { served });
200
+ assertFalse(routed);
201
+ });
202
+ });
203
+ test("Context.routeActivity() rejects proofs of other DIDs for portable actors", async () => {
204
+ const id = portableId(did, "/activities/1");
205
+ const actors = [portableId(did, "/actor")];
206
+ assertEquals(await route(await portableCreate(id, actors, "otherDid")), {
207
+ routed: false,
208
+ received: []
209
+ });
210
+ assertEquals(await route(await portableCreate(id, actors)), {
211
+ routed: true,
212
+ received: actors.map((actor) => actor.href)
213
+ });
214
+ });
215
+ test("Context.routeActivity() keeps comparing web origins of HTTP(S) IDs", async (t) => {
216
+ const cases = [
217
+ [
218
+ "same origin",
219
+ new URL("https://remote.example/activities/1"),
220
+ new URL("https://remote.example/users/alice"),
221
+ true
222
+ ],
223
+ [
224
+ "same origin with an explicit default port",
225
+ new URL("https://remote.example/activities/1"),
226
+ new URL("https://remote.example:443/users/alice"),
227
+ true
228
+ ],
229
+ [
230
+ "another origin",
231
+ new URL("https://remote.example/activities/1"),
232
+ new URL("https://other.example/users/alice"),
233
+ false
234
+ ],
235
+ [
236
+ "another port",
237
+ new URL("https://remote.example/activities/1"),
238
+ new URL("https://remote.example:8443/users/alice"),
239
+ false
240
+ ],
241
+ [
242
+ "compatible actor ID on the same host",
243
+ new URL("https://remote.example/activities/1"),
244
+ compatibleId(did, "/actor", "https://remote.example"),
245
+ false
246
+ ]
247
+ ];
248
+ for (const [name, id, actor, expected] of cases) await t.step(name, async () => {
249
+ const served = new Create({
250
+ id,
251
+ actor
252
+ });
253
+ const { routed, received } = await route(id, { served });
254
+ assertEquals(routed, expected);
255
+ assertEquals(received, expected ? [actor.href] : []);
256
+ });
257
+ });
258
+ //#endregion
259
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";