@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
@@ -1,9 +1,9 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { t as assertEquals } from "../assert_equals-BV7oLMxm.mjs";
5
- import "../std__assert-B2NnY9bq.mjs";
6
- import { n as digest, t as buildCollectionSynchronizationHeader } from "../collection-BIhlldqK.mjs";
4
+ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
+ import "../std__assert-DvMDRPes.mjs";
6
+ import { n as digest, t as buildCollectionSynchronizationHeader } from "../collection-CnnP1UAv.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  import { decodeHex } from "byte-encodings/hex";
9
9
  //#region src/federation/collection.test.ts
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,228 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
5
+ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
+ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
+ import "../std__assert-DvMDRPes.mjs";
8
+ import { t as assert } from "../assert-D1Eafmd_.mjs";
9
+ import { n as exportJwk } from "../key-BspPiXqn.mjs";
10
+ import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
11
+ import { a as signObject, d as verifyProof } from "../proof-Dxy6ByqI.mjs";
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-D7_LO-Vs.mjs";
14
+ import { Create, DataIntegrityProof, Note } from "@fedify/vocab";
15
+ import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
16
+ import { mockDocumentLoader, test } from "@fedify/fixture";
17
+ import serialize from "json-canon";
18
+ //#region src/federation/compound-fanout.test.ts
19
+ const options = {
20
+ contextLoader: mockDocumentLoader,
21
+ documentLoader: mockDocumentLoader
22
+ };
23
+ const context = ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"];
24
+ const childContext = [...context, { ex: "https://example.com/ns#" }];
25
+ async function parseProof(document) {
26
+ const proof = document.proof;
27
+ return await DataIntegrityProof.fromJsonLd({
28
+ "@context": document["@context"],
29
+ ...proof
30
+ }, options);
31
+ }
32
+ test("fanout delivery keeps an embedded signed child intact", async () => {
33
+ const created = Temporal.Instant.from("2023-02-24T23:36:38Z");
34
+ const childDid = await exportDidKey(ed25519PublicKey.publicKey);
35
+ const childKeyId = new URL(`${childDid}#${childDid.substring(8)}`);
36
+ const child = await signObject(new Note({
37
+ id: parseIri(`ap+ef61://${childDid}/objects/fanned-out`),
38
+ attribution: parseIri(`ap+ef61://${childDid}/actor`),
39
+ content: "A portable note carried through the fanout queue"
40
+ }), ed25519PrivateKey, childKeyId, {
41
+ ...options,
42
+ context: childContext,
43
+ created
44
+ });
45
+ const compound = await normalizeOutgoingActivityJsonLd(await (await signObject(new Create({
46
+ id: new URL("https://example.com/activities/fanned-out"),
47
+ actor: new URL("https://example.com/person"),
48
+ object: child
49
+ }), ed25519PrivateKey, childKeyId, {
50
+ ...options,
51
+ context,
52
+ created
53
+ })).toJsonLd({
54
+ format: "compact",
55
+ ...options,
56
+ context
57
+ }), mockDocumentLoader, { preserveNestedSecuredDocuments: true });
58
+ const securedChild = compound.object;
59
+ assertEquals(securedChild["@context"], childContext);
60
+ esm_default.spyGlobal();
61
+ try {
62
+ let delivered = null;
63
+ esm_default.post("https://example.com/inbox", async (cl) => {
64
+ delivered = await cl.request.json();
65
+ return new Response(null, { status: 202 });
66
+ });
67
+ const federation = new FederationImpl({
68
+ kv: new MemoryKvStore(),
69
+ contextLoaderFactory: () => mockDocumentLoader
70
+ });
71
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
72
+ const message = {
73
+ type: "fanout",
74
+ id: crypto.randomUUID(),
75
+ baseUrl: "https://example.com",
76
+ keys: [{
77
+ keyId: rsaPublicKey2.id.href,
78
+ privateKey: await exportJwk(rsaPrivateKey2)
79
+ }],
80
+ inboxes: { "https://example.com/inbox": {
81
+ actorIds: ["https://example.com/recipient"],
82
+ sharedInbox: false
83
+ } },
84
+ activity: compound,
85
+ activityId: "https://example.com/activities/fanned-out",
86
+ activityType: "https://www.w3.org/ns/activitystreams#Create",
87
+ normalizeExistingProofs: true,
88
+ traceContext: {}
89
+ };
90
+ await federation.processQueuedTask(void 0, message);
91
+ assert(delivered != null);
92
+ const body = delivered;
93
+ delete body.signature;
94
+ assertEquals(serialize(body), serialize(compound));
95
+ const embedded = body.object;
96
+ assertEquals((await verifyProof(embedded, await parseProof(embedded), options))?.id, childKeyId);
97
+ assertEquals((await verifyProof(body, await parseProof(body), options))?.id, childKeyId);
98
+ } finally {
99
+ esm_default.hardReset();
100
+ }
101
+ });
102
+ test("forced fanout signs before serializing so the child survives", async () => {
103
+ const created = Temporal.Instant.from("2023-02-24T23:36:38Z");
104
+ const childDid = await exportDidKey(ed25519PublicKey.publicKey);
105
+ const childKeyId = new URL(`${childDid}#${childDid.substring(8)}`);
106
+ const child = await signObject(new Note({
107
+ id: parseIri(`ap+ef61://${childDid}/objects/forced-fanout`),
108
+ attribution: parseIri(`ap+ef61://${childDid}/actor`),
109
+ content: "A portable note sent through a forced fanout"
110
+ }), ed25519PrivateKey, childKeyId, {
111
+ ...options,
112
+ context: childContext,
113
+ created
114
+ });
115
+ const activity = new Create({
116
+ id: new URL("https://example.com/activities/forced-fanout"),
117
+ actor: new URL("https://example.com/person"),
118
+ object: child
119
+ });
120
+ esm_default.spyGlobal();
121
+ try {
122
+ let delivered = null;
123
+ esm_default.post("https://example.com/inbox", async (cl) => {
124
+ delivered = await cl.request.json();
125
+ return new Response(null, { status: 202 });
126
+ });
127
+ const queued = [];
128
+ const federation = new FederationImpl({
129
+ kv: new MemoryKvStore(),
130
+ queue: {
131
+ enqueue(message) {
132
+ queued.push(message);
133
+ return Promise.resolve();
134
+ },
135
+ listen() {
136
+ return Promise.resolve();
137
+ }
138
+ },
139
+ manuallyStartQueue: true,
140
+ contextLoaderFactory: () => mockDocumentLoader
141
+ });
142
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
143
+ await federation.createContext(new URL("https://example.com/")).sendActivity({
144
+ keyId: childKeyId,
145
+ privateKey: ed25519PrivateKey
146
+ }, {
147
+ id: new URL("https://example.com/users/bob"),
148
+ inboxId: new URL("https://example.com/inbox")
149
+ }, activity, { fanout: "force" });
150
+ assertEquals(queued.length, 1);
151
+ for (let i = 0; i < queued.length; i++) await federation.processQueuedTask(void 0, queued[i]);
152
+ assert(delivered != null);
153
+ const body = delivered;
154
+ delete body.signature;
155
+ const embedded = body.object;
156
+ assertEquals(embedded["@context"], childContext);
157
+ assertEquals((await verifyProof(embedded, await parseProof(embedded), options))?.id, childKeyId);
158
+ assertEquals((await verifyProof(body, await parseProof(body), options))?.id, childKeyId);
159
+ } finally {
160
+ esm_default.hardReset();
161
+ }
162
+ });
163
+ test("an already signed activity keeps its single proof through fanout", async () => {
164
+ const created = Temporal.Instant.from("2023-02-24T23:36:38Z");
165
+ const childDid = await exportDidKey(ed25519PublicKey.publicKey);
166
+ const childKeyId = new URL(`${childDid}#${childDid.substring(8)}`);
167
+ const child = await signObject(new Note({
168
+ id: parseIri(`ap+ef61://${childDid}/objects/pre-signed`),
169
+ attribution: parseIri(`ap+ef61://${childDid}/actor`),
170
+ content: "A portable note in a pre-signed activity"
171
+ }), ed25519PrivateKey, childKeyId, {
172
+ ...options,
173
+ context: childContext,
174
+ created
175
+ });
176
+ const activity = await signObject(new Create({
177
+ id: new URL("https://example.com/activities/pre-signed"),
178
+ actor: new URL("https://example.com/person"),
179
+ object: child
180
+ }), ed25519PrivateKey, childKeyId, {
181
+ ...options,
182
+ created
183
+ });
184
+ esm_default.spyGlobal();
185
+ try {
186
+ let delivered = null;
187
+ esm_default.post("https://example.com/inbox", async (cl) => {
188
+ delivered = await cl.request.json();
189
+ return new Response(null, { status: 202 });
190
+ });
191
+ const queued = [];
192
+ const federation = new FederationImpl({
193
+ kv: new MemoryKvStore(),
194
+ queue: {
195
+ enqueue(message) {
196
+ queued.push(message);
197
+ return Promise.resolve();
198
+ },
199
+ listen() {
200
+ return Promise.resolve();
201
+ }
202
+ },
203
+ manuallyStartQueue: true,
204
+ contextLoaderFactory: () => mockDocumentLoader
205
+ });
206
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox");
207
+ await federation.createContext(new URL("https://example.com/")).sendActivity({
208
+ keyId: childKeyId,
209
+ privateKey: ed25519PrivateKey
210
+ }, {
211
+ id: new URL("https://example.com/users/bob"),
212
+ inboxId: new URL("https://example.com/inbox")
213
+ }, activity, { fanout: "force" });
214
+ for (let i = 0; i < queued.length; i++) await federation.processQueuedTask(void 0, queued[i]);
215
+ assert(delivered != null);
216
+ const body = delivered;
217
+ delete body.signature;
218
+ assert(!Array.isArray(body.proof));
219
+ assertEquals((await verifyProof(body, await parseProof(body), options))?.id, childKeyId);
220
+ const embedded = body.object;
221
+ assertEquals(embedded["@context"], childContext);
222
+ assertEquals((await verifyProof(embedded, await parseProof(embedded), options))?.id, childKeyId);
223
+ } finally {
224
+ esm_default.hardReset();
225
+ }
226
+ });
227
+ //#endregion
228
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";
@@ -0,0 +1,384 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { r as createRequestContext, t as createInboxContext } from "../context-8Tu9kea0.mjs";
5
+ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
+ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
+ import "../std__assert-DvMDRPes.mjs";
8
+ import { a as compactJsonLd, m as verifyCompactJsonLd, p as signJsonLd } from "../ld-CzaP-hJl.mjs";
9
+ import { l as signRequest } from "../http-siciWkmx.mjs";
10
+ import { a as rsaPrivateKey3, c as rsaPublicKey3, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
11
+ import { f as handleInbox, o as createFederation } from "../middleware-D7_LO-Vs.mjs";
12
+ import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
13
+ import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
14
+ import { Create, CryptographicKey, Multikey, Person, Update } from "@fedify/vocab";
15
+ import { encodeMultibase, exportDidKey, parseIri } from "@fedify/vocab-runtime";
16
+ import { mockDocumentLoader, test } from "@fedify/fixture";
17
+ import serialize from "json-canon";
18
+ //#region src/federation/handler-compound-proof.test.ts
19
+ async function sha256(value) {
20
+ return new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)));
21
+ }
22
+ async function secureDocument(unsecuredDocument, privateJwk, verificationMethod) {
23
+ const proofConfiguration = {
24
+ "@context": unsecuredDocument["@context"],
25
+ type: "DataIntegrityProof",
26
+ cryptosuite: "eddsa-jcs-2022",
27
+ verificationMethod,
28
+ proofPurpose: "assertionMethod",
29
+ created: "2023-02-24T23:36:38Z"
30
+ };
31
+ const proofHash = await sha256(serialize(proofConfiguration));
32
+ const documentHash = await sha256(serialize(unsecuredDocument));
33
+ const input = /* @__PURE__ */ new Uint8Array(64);
34
+ input.set(proofHash);
35
+ input.set(documentHash, proofHash.length);
36
+ const privateKey = await crypto.subtle.importKey("jwk", privateJwk, "Ed25519", false, ["sign"]);
37
+ const signature = new Uint8Array(await crypto.subtle.sign("Ed25519", privateKey, input));
38
+ return {
39
+ ...unsecuredDocument,
40
+ proof: {
41
+ ...proofConfiguration,
42
+ proofValue: new TextDecoder().decode(encodeMultibase("base58btc", signature))
43
+ }
44
+ };
45
+ }
46
+ function requestFor(body) {
47
+ return new Request("https://example.com/inbox", {
48
+ method: "POST",
49
+ body: JSON.stringify(body)
50
+ });
51
+ }
52
+ async function handle(requestOrBody, dispatched) {
53
+ const request = requestOrBody instanceof Request ? requestOrBody : requestFor(requestOrBody);
54
+ const context = createRequestContext({
55
+ federation: createFederation({ kv: new MemoryKvStore() }),
56
+ request,
57
+ url: new URL(request.url),
58
+ data: void 0,
59
+ documentLoader: mockDocumentLoader,
60
+ contextLoader: mockDocumentLoader
61
+ });
62
+ const inboxListeners = new ActivityListenerSet();
63
+ const count = () => {
64
+ if (dispatched != null) dispatched.count++;
65
+ };
66
+ inboxListeners.add(Create, count);
67
+ inboxListeners.add(Update, count);
68
+ return await handleInbox(request, {
69
+ recipient: null,
70
+ context,
71
+ inboxContextFactory() {
72
+ return createInboxContext({
73
+ ...context,
74
+ clone: void 0
75
+ });
76
+ },
77
+ kv: new MemoryKvStore(),
78
+ kvPrefixes: {
79
+ activityIdempotence: ["_fedify", "activityIdempotence"],
80
+ publicKey: ["_fedify", "publicKey"],
81
+ acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
82
+ },
83
+ actorDispatcher() {
84
+ return null;
85
+ },
86
+ inboxListeners,
87
+ onNotFound() {
88
+ return new Response("Not found", { status: 404 });
89
+ },
90
+ signatureTimeWindow: { minutes: 5 },
91
+ skipSignatureVerification: false
92
+ });
93
+ }
94
+ test("handleInbox() enforces portable compound proofs atomically", async () => {
95
+ const valid = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
96
+ assertEquals((await handle(valid)).status, 202);
97
+ const unsignedInner = structuredClone(map_local_create_note_default.documents.outerUnsecuredDocument);
98
+ delete unsignedInner.object.proof;
99
+ const rejected = await handle(await secureDocument(unsignedInner, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod));
100
+ assertEquals([rejected.status, await rejected.text()], [401, "Failed to verify compound portable Object Integrity Proofs."]);
101
+ const tamperedInner = structuredClone(map_local_create_note_default.documents.outerUnsecuredDocument);
102
+ tamperedInner.object.content = "Tampered";
103
+ assertEquals((await handle(await secureDocument(tamperedInner, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod))).status, 401);
104
+ const changedAfterSigning = structuredClone(valid);
105
+ changedAfterSigning.summary = "Added after signing";
106
+ assertEquals((await handle(changedAfterSigning)).status, 401);
107
+ });
108
+ test("handleInbox() enforces compounds after alternate outer auth", async () => {
109
+ const createBody = (object) => ({
110
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
111
+ id: "https://example.com/activities/compound",
112
+ type: "Create",
113
+ actor: rsaPublicKey3.ownerId.href,
114
+ object
115
+ });
116
+ const validInner = structuredClone(map_local_create_note_default.documents.securedInner);
117
+ const unsignedInner = structuredClone(validInner);
118
+ delete unsignedInner.proof;
119
+ for (const authentication of ["http", "ld"]) {
120
+ const sign = async (body) => {
121
+ if (authentication === "http") return await signRequest(requestFor(body), rsaPrivateKey3, rsaPublicKey3.id);
122
+ return requestFor(await signJsonLd(body, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader }));
123
+ };
124
+ const acceptedDispatch = { count: 0 };
125
+ assertEquals([(await handle(await sign(createBody(structuredClone(validInner))), acceptedDispatch)).status, acceptedDispatch.count], [202, 1]);
126
+ const rejectedDispatch = { count: 0 };
127
+ assertEquals([(await handle(await sign(createBody(structuredClone(unsignedInner))), rejectedDispatch)).status, rejectedDispatch.count], [401, 0]);
128
+ }
129
+ const proofSetInner = structuredClone(validInner);
130
+ proofSetInner.proof = [proofSetInner.proof];
131
+ const unsupportedDispatch = { count: 0 };
132
+ assertEquals([(await handle(await signRequest(requestFor(createBody(proofSetInner)), rsaPrivateKey3, rsaPublicKey3.id), unsupportedDispatch)).status, unsupportedDispatch.count], [401, 0]);
133
+ });
134
+ test("handleInbox() preserves ordinary top-level proof behavior", async () => {
135
+ assertEquals((await handle(await secureDocument({
136
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
137
+ id: "https://example.com/activities/ordinary",
138
+ type: "Create",
139
+ actor: map_local_create_note_default.keys.outer.controller,
140
+ object: {
141
+ id: "https://example.com/notes/ordinary",
142
+ type: "Note",
143
+ attributedTo: map_local_create_note_default.keys.outer.controller,
144
+ content: "An ordinary note"
145
+ }
146
+ }, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod))).status, 202);
147
+ });
148
+ async function signLd(document) {
149
+ return await signJsonLd(document, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader });
150
+ }
151
+ async function isLdSignatureVerified(document) {
152
+ return await verifyCompactJsonLd(await compactJsonLd(document, mockDocumentLoader), {
153
+ contextLoader: mockDocumentLoader,
154
+ documentLoader: mockDocumentLoader
155
+ });
156
+ }
157
+ async function handleDispatched(body) {
158
+ const dispatched = { count: 0 };
159
+ const response = await handle(body, dispatched);
160
+ return [
161
+ response.status,
162
+ await response.text(),
163
+ dispatched.count
164
+ ];
165
+ }
166
+ const COMPOUND_FAILURE = "Failed to verify compound portable Object Integrity Proofs.";
167
+ test("handleInbox() ignores a portable activity's Linked Data Signature", async () => {
168
+ const signed = await signLd(structuredClone(map_local_create_note_default.documents.finalSecuredCompound));
169
+ assertEquals(await isLdSignatureVerified(signed), false);
170
+ assertEquals(await handleDispatched(signed), [
171
+ 202,
172
+ "",
173
+ 1
174
+ ]);
175
+ const bogus = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
176
+ bogus.signature = {
177
+ type: "RsaSignature2017",
178
+ creator: "https://example.com/keys/1",
179
+ created: "2023-02-24T23:36:38Z",
180
+ signatureValue: "not-a-signature"
181
+ };
182
+ assertEquals(await handleDispatched(bogus), [
183
+ 202,
184
+ "",
185
+ 1
186
+ ]);
187
+ const tampered = structuredClone(map_local_create_note_default.documents.finalSecuredCompound);
188
+ tampered.object = {
189
+ ...tampered.object,
190
+ content: "Tampered"
191
+ };
192
+ assertEquals((await handleDispatched(await signLd(tampered)))[0], 401);
193
+ const invalidInner = structuredClone(map_local_create_note_default.documents.outerUnsecuredDocument);
194
+ invalidInner.object.content = "Tampered";
195
+ assertEquals(await handleDispatched(await signLd(await secureDocument(invalidInner, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod))), [
196
+ 401,
197
+ COMPOUND_FAILURE,
198
+ 0
199
+ ]);
200
+ });
201
+ test("handleInbox() verifies compounds with verified Linked Data Signatures", async () => {
202
+ const createBody = (object) => ({
203
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
204
+ id: "https://example.com/activities/compound-ld",
205
+ type: "Create",
206
+ actor: rsaPublicKey3.ownerId.href,
207
+ object
208
+ });
209
+ const privateJwk = await crypto.subtle.exportKey("jwk", ed25519PrivateKey);
210
+ const sign = async (object) => await signLd(await secureDocument(createBody(object), privateJwk, ed25519PublicKey.id.href));
211
+ const valid = await sign(structuredClone(map_local_create_note_default.documents.securedInner));
212
+ assertEquals(await isLdSignatureVerified(valid), true);
213
+ assertEquals(await handleDispatched(valid), [
214
+ 202,
215
+ "",
216
+ 1
217
+ ]);
218
+ const invalidInner = structuredClone(map_local_create_note_default.documents.securedInner);
219
+ invalidInner.content = "Tampered";
220
+ const invalid = await sign(invalidInner);
221
+ assertEquals(await isLdSignatureVerified(invalid), true);
222
+ assertEquals(await handleDispatched(invalid), [
223
+ 401,
224
+ COMPOUND_FAILURE,
225
+ 0
226
+ ]);
227
+ });
228
+ test("handleInbox() keeps embedded signature properties in proof inputs", async () => {
229
+ const innerWithSignature = await secureDocument({
230
+ ...map_local_create_note_default.documents.innerUnsecuredDocument,
231
+ "@context": ["https://w3id.org/security/v1", ...map_local_create_note_default.documents.innerUnsecuredDocument["@context"]],
232
+ signature: {
233
+ type: "https://example.com/ns#ExampleSignature",
234
+ signatureValue: "covered by the inner proof"
235
+ }
236
+ }, map_local_create_note_default.keys.inner.testPrivateKeyJwk, map_local_create_note_default.keys.inner.verificationMethod);
237
+ const sign = async (object) => await signLd(await secureDocument({
238
+ ...map_local_create_note_default.documents.outerUnsecuredDocument,
239
+ object
240
+ }, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod));
241
+ assertEquals(await handleDispatched(await sign(structuredClone(innerWithSignature))), [
242
+ 202,
243
+ "",
244
+ 1
245
+ ]);
246
+ const innerWithoutSignature = structuredClone(innerWithSignature);
247
+ delete innerWithoutSignature.signature;
248
+ assertEquals(await handleDispatched(await sign(innerWithoutSignature)), [
249
+ 401,
250
+ COMPOUND_FAILURE,
251
+ 0
252
+ ]);
253
+ });
254
+ test("handleInbox() inspects compounds without the Linked Data Signature", async () => {
255
+ const activity = await secureDocument({
256
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
257
+ id: "https://example.com/activities/ordinary-ld",
258
+ type: "Create",
259
+ actor: map_local_create_note_default.keys.outer.controller,
260
+ object: {
261
+ id: "https://example.com/notes/ordinary-ld",
262
+ type: "Note",
263
+ attributedTo: map_local_create_note_default.keys.outer.controller,
264
+ content: "An ordinary note"
265
+ }
266
+ }, map_local_create_note_default.keys.outer.testPrivateKeyJwk, map_local_create_note_default.keys.outer.verificationMethod);
267
+ activity.signature = {
268
+ id: map_local_create_note_default.documents.innerUnsecuredDocument.id,
269
+ type: "RsaSignature2017",
270
+ creator: "https://example.com/keys/1",
271
+ created: "2023-02-24T23:36:38Z",
272
+ signatureValue: "not-a-signature"
273
+ };
274
+ assertEquals(await handleDispatched(activity), [
275
+ 202,
276
+ "",
277
+ 1
278
+ ]);
279
+ });
280
+ test("handleInbox() accepts portable actors with keys at ap: URIs", async () => {
281
+ const did = await exportDidKey(ed25519PublicKey.publicKey);
282
+ const verificationMethod = `${did}#${did.slice(8)}`;
283
+ const privateJwk = await crypto.subtle.exportKey("jwk", ed25519PrivateKey);
284
+ const actorId = `ap://${did}/actor`;
285
+ const inboxId = `https://gw.example/.well-known/apgateway/${did}/actor/inbox`;
286
+ const actor = await new Person({
287
+ id: parseIri(actorId),
288
+ inbox: new URL(inboxId),
289
+ gateways: [new URL("https://gw.example")],
290
+ publicKey: new CryptographicKey({
291
+ id: parseIri(`${actorId}#main-key`),
292
+ owner: parseIri(actorId),
293
+ publicKey: rsaPublicKey2.publicKey
294
+ }),
295
+ assertionMethods: [new Multikey({
296
+ id: parseIri(`${actorId}#ed25519-key`),
297
+ controller: parseIri(actorId),
298
+ publicKey: ed25519PublicKey.publicKey
299
+ })]
300
+ }).toJsonLd({
301
+ format: "compact",
302
+ contextLoader: mockDocumentLoader
303
+ });
304
+ const update = async (keyBase) => {
305
+ const [multikey] = actor.assertionMethod;
306
+ return await secureDocument({
307
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
308
+ id: `ap://${did}/activities/update`,
309
+ type: "Update",
310
+ actor: actorId,
311
+ object: await secureDocument({
312
+ ...actor,
313
+ id: actorId,
314
+ publicKey: {
315
+ ...actor.publicKey,
316
+ id: `${keyBase}#main-key`,
317
+ owner: actorId
318
+ },
319
+ assertionMethod: [{
320
+ ...multikey,
321
+ id: `${keyBase}#ed25519-key`,
322
+ controller: actorId
323
+ }]
324
+ }, privateJwk, verificationMethod)
325
+ }, privateJwk, verificationMethod);
326
+ };
327
+ for (const keyBase of [actorId, `ap+ef61://${encodeURIComponent(did)}/actor`]) {
328
+ const dispatched = { count: 0 };
329
+ assertEquals([(await handle(await update(keyBase), dispatched)).status, dispatched.count], [202, 1], keyBase);
330
+ }
331
+ const dispatched = { count: 0 };
332
+ assertEquals([(await handle(await update("ap://did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK/actor"), dispatched)).status, dispatched.count], [401, 0]);
333
+ });
334
+ test("handleInbox() rejects embedded portable actors without valid gateways", async () => {
335
+ const did = await exportDidKey(ed25519PublicKey.publicKey);
336
+ const verificationMethod = `${did}#${did.slice(8)}`;
337
+ const privateJwk = await crypto.subtle.exportKey("jwk", ed25519PrivateKey);
338
+ const actorId = `ap://${did}/actor`;
339
+ const update = async (gateways) => await secureDocument({
340
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
341
+ id: `ap://${did}/activities/update`,
342
+ type: "Update",
343
+ actor: actorId,
344
+ object: await secureDocument({
345
+ "@context": [
346
+ "https://www.w3.org/ns/activitystreams",
347
+ "https://w3id.org/security/data-integrity/v1",
348
+ "https://w3id.org/fep/ef61"
349
+ ],
350
+ id: actorId,
351
+ type: "Person",
352
+ inbox: `${actorId}/inbox`,
353
+ outbox: `${actorId}/outbox`,
354
+ ...gateways
355
+ }, privateJwk, verificationMethod)
356
+ }, privateJwk, verificationMethod);
357
+ for (const [gateways, status, count] of [
358
+ [
359
+ { gateways: ["https://gw.example"] },
360
+ 202,
361
+ 1
362
+ ],
363
+ [
364
+ {},
365
+ 401,
366
+ 0
367
+ ],
368
+ [
369
+ { gateways: [] },
370
+ 401,
371
+ 0
372
+ ],
373
+ [
374
+ { gateways: ["https://gw.example/path"] },
375
+ 400,
376
+ 0
377
+ ]
378
+ ]) {
379
+ const dispatched = { count: 0 };
380
+ assertEquals([(await handle(await update(gateways), dispatched)).status, dispatched.count], [status, count], JSON.stringify(gateways));
381
+ }
382
+ });
383
+ //#endregion
384
+ export {};