@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,697 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
5
+ import { t as verifyPortableObject } from "../portable-collection-C74P1vtC.mjs";
6
+ import { Collection, CollectionPage, Note, OrderedCollection, Person, traverseCollection } from "@fedify/vocab";
7
+ import { FetchError, encodeMultibase, exportDidKey, preloadedContexts } from "@fedify/vocab-runtime";
8
+ import { test } from "@fedify/fixture";
9
+ import { deepStrictEqual, ok, strictEqual } from "node:assert/strict";
10
+ import serialize from "json-canon";
11
+ //#region src/sig/portable-collection.test.ts
12
+ const AS = "https://www.w3.org/ns/activitystreams#";
13
+ const context = [
14
+ "https://www.w3.org/ns/activitystreams",
15
+ "https://w3id.org/security/data-integrity/v1",
16
+ "https://w3id.org/fep/ef61"
17
+ ];
18
+ const did = await exportDidKey(ed25519PublicKey.publicKey);
19
+ const keyId = `${did}#${did.slice(8)}`;
20
+ const otherKeyPair = await crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
21
+ const otherDid = await exportDidKey(otherKeyPair.publicKey);
22
+ const actorId = `ap://${did}/actor`;
23
+ const outboxId = `ap://${did}/actor/outbox`;
24
+ const gw1 = "https://gw1.example";
25
+ const gw2 = "https://gw2.example";
26
+ const evil = "https://evil.example";
27
+ function gatewayUrl(gateway, id) {
28
+ return `${gateway}/.well-known/apgateway/${id.replace(/^ap:\/\//, "")}`;
29
+ }
30
+ async function sign(document, privateKey = ed25519PrivateKey, verificationMethod = keyId) {
31
+ const proofConfig = {
32
+ "@context": document["@context"],
33
+ type: "DataIntegrityProof",
34
+ cryptosuite: "eddsa-jcs-2022",
35
+ verificationMethod,
36
+ proofPurpose: "assertionMethod",
37
+ created: "2023-02-24T23:36:38Z"
38
+ };
39
+ const encoder = new TextEncoder();
40
+ const proofDigest = await crypto.subtle.digest("SHA-256", encoder.encode(serialize(proofConfig)));
41
+ const messageDigest = await crypto.subtle.digest("SHA-256", encoder.encode(serialize(document)));
42
+ const digest = /* @__PURE__ */ new Uint8Array(64);
43
+ digest.set(new Uint8Array(proofDigest), 0);
44
+ digest.set(new Uint8Array(messageDigest), 32);
45
+ const signature = await crypto.subtle.sign("Ed25519", privateKey, digest);
46
+ return {
47
+ ...document,
48
+ proof: {
49
+ ...proofConfig,
50
+ proofValue: new TextDecoder().decode(encodeMultibase("base58btc", new Uint8Array(signature)))
51
+ }
52
+ };
53
+ }
54
+ function actor(overrides = {}) {
55
+ return {
56
+ "@context": context,
57
+ id: actorId,
58
+ type: "Person",
59
+ inbox: `ap://${did}/actor/inbox`,
60
+ outbox: outboxId,
61
+ followers: `ap://${did}/actor/followers`,
62
+ gateways: [gw1, gw2],
63
+ ...overrides
64
+ };
65
+ }
66
+ function outbox(overrides = {}) {
67
+ return {
68
+ "@context": context,
69
+ id: outboxId,
70
+ type: "OrderedCollection",
71
+ totalItems: 0,
72
+ ...overrides
73
+ };
74
+ }
75
+ const contextLoader = (url) => {
76
+ const document = preloadedContexts[url];
77
+ if (document == null) return Promise.reject(/* @__PURE__ */ new Error(`No context: ${url}`));
78
+ return Promise.resolve({
79
+ contextUrl: null,
80
+ documentUrl: url,
81
+ document
82
+ });
83
+ };
84
+ function createLoader(responses) {
85
+ const fetched = [];
86
+ const loader = (url) => {
87
+ fetched.push(url);
88
+ let response = responses[url];
89
+ let documentUrl = url;
90
+ if (response != null && "redirect" in response) {
91
+ documentUrl = response.redirect;
92
+ response = responses[documentUrl];
93
+ }
94
+ if (response == null) return Promise.reject(URL.canParse(url) ? new FetchError(url, "HTTP 404", new Response(null, { status: 404 })) : /* @__PURE__ */ new Error(`Not found: ${url}`));
95
+ return Promise.resolve({
96
+ contextUrl: null,
97
+ documentUrl,
98
+ document: structuredClone(response)
99
+ });
100
+ };
101
+ return Object.assign(loader, { fetched });
102
+ }
103
+ function linkFrom(object, property, overrides = {}) {
104
+ return {
105
+ object,
106
+ id: object.id ?? null,
107
+ property: AS + property,
108
+ ...overrides
109
+ };
110
+ }
111
+ async function verify(document, options) {
112
+ return await verifyPortableObject(document, {
113
+ contextLoader,
114
+ ...options
115
+ });
116
+ }
117
+ function assertFailure(result, type) {
118
+ ok(!result.verified, `expected ${type}, got ${JSON.stringify(result)}`);
119
+ strictEqual(result.reason.type, type);
120
+ }
121
+ const signedActor = await sign(actor());
122
+ const actorObject = await Person.fromJsonLd(signedActor, { contextLoader });
123
+ test("verifyPortableObject() trusts unsecured collections from listed gateways", async () => {
124
+ const documentLoader = createLoader({ [gatewayUrl(gw1, actorId)]: signedActor });
125
+ const result = await verify(outbox(), {
126
+ documentLoader,
127
+ documentUrl: new URL(gatewayUrl(gw2, outboxId)),
128
+ gatewayHints: [new URL(gw1)],
129
+ referrer: linkFrom(actorObject, "outbox")
130
+ });
131
+ ok(result.verified);
132
+ strictEqual(result.method, "gateway");
133
+ if (result.method !== "gateway") return;
134
+ deepStrictEqual(result.gateway, new URL(gw2));
135
+ strictEqual(result.owner.href, actorObject.id?.href);
136
+ deepStrictEqual(documentLoader.fetched, [gatewayUrl(gw1, actorId)]);
137
+ });
138
+ test("verifyPortableObject() rejects unsecured collections from unlisted gateways", async () => {
139
+ const result = await verify(outbox(), {
140
+ documentLoader: createLoader({ [gatewayUrl(gw1, actorId)]: signedActor }),
141
+ documentUrl: new URL(gatewayUrl(evil, outboxId)),
142
+ gatewayHints: [new URL(evil), new URL(gw1)],
143
+ referrer: linkFrom(actorObject, "outbox")
144
+ });
145
+ assertFailure(result, "untrustedGateway");
146
+ if (result.verified || result.reason.type !== "untrustedGateway") return;
147
+ deepStrictEqual(result.reason.gateway, new URL(evil));
148
+ deepStrictEqual(result.reason.gateways, [new URL(gw1), new URL(gw2)]);
149
+ });
150
+ test("verifyPortableObject() uses the actual fetch source", async () => {
151
+ const documentLoader = createLoader({ [gatewayUrl(gw1, actorId)]: signedActor });
152
+ const referrer = linkFrom(actorObject, "outbox");
153
+ const gateways = [new URL(gw1)];
154
+ assertFailure(await verify(outbox(), {
155
+ documentLoader,
156
+ gateways,
157
+ referrer
158
+ }), "unknownCollectionSource");
159
+ assertFailure(await verify(outbox(), {
160
+ documentLoader,
161
+ gateways,
162
+ referrer,
163
+ documentUrl: new URL("https://gw1.example/cdn/outbox.json")
164
+ }), "unknownCollectionSource");
165
+ assertFailure(await verify(outbox(), {
166
+ documentLoader,
167
+ gateways,
168
+ referrer,
169
+ documentUrl: new URL(gatewayUrl(gw1, `ap://${did}/actor/inbox`))
170
+ }), "unknownCollectionSource");
171
+ assertFailure(await verify(outbox(), {
172
+ documentLoader,
173
+ gateways,
174
+ referrer,
175
+ documentUrl: new URL(`https://user@gw1.example/.well-known/apgateway/${did}/actor/outbox`)
176
+ }), "unknownCollectionSource");
177
+ const result = await verify(outbox({ id: gatewayUrl(evil, outboxId) }), {
178
+ documentLoader,
179
+ gateways,
180
+ referrer,
181
+ documentUrl: new URL(gatewayUrl(gw1, outboxId))
182
+ });
183
+ ok(result.verified);
184
+ strictEqual(result.method, "gateway");
185
+ if (result.method !== "gateway") return;
186
+ deepStrictEqual(result.gateway, new URL(gw1));
187
+ assertFailure(await verify(outbox({ id: `${evil}/outbox` }), {
188
+ documentLoader,
189
+ gateways,
190
+ referrer,
191
+ documentUrl: new URL(gatewayUrl(gw1, outboxId))
192
+ }), "notPortableObject");
193
+ });
194
+ test("verifyPortableObject() trusts unsecured collections of compatible-ID actors", async () => {
195
+ const compatibleActorId = gatewayUrl(gw1, actorId);
196
+ const compatibleOutboxId = gatewayUrl(gw1, outboxId);
197
+ const signed = await sign(actor({
198
+ id: compatibleActorId,
199
+ inbox: gatewayUrl(gw1, `ap://${did}/actor/inbox`),
200
+ outbox: compatibleOutboxId,
201
+ followers: gatewayUrl(gw1, `ap://${did}/actor/followers`)
202
+ }));
203
+ const collection = outbox({
204
+ id: compatibleOutboxId,
205
+ attributedTo: compatibleActorId
206
+ });
207
+ const documentLoader = createLoader({ [compatibleActorId]: signed });
208
+ const result = await verify(collection, {
209
+ documentLoader,
210
+ documentUrl: new URL(compatibleOutboxId)
211
+ });
212
+ ok(result.verified, JSON.stringify(result));
213
+ strictEqual(result.method, "gateway");
214
+ if (result.method !== "gateway") return;
215
+ deepStrictEqual(result.gateway, new URL(gw1));
216
+ deepStrictEqual(documentLoader.fetched, [compatibleActorId]);
217
+ const noGateways = createLoader({ [compatibleActorId]: signed });
218
+ assertFailure(await verify(collection, {
219
+ documentLoader: noGateways,
220
+ documentUrl: new URL(compatibleOutboxId),
221
+ gateways: []
222
+ }), "collectionOwnerUnavailable");
223
+ deepStrictEqual(noGateways.fetched.filter((url) => url === compatibleActorId), []);
224
+ assertFailure(await verify(collection, {
225
+ documentLoader: createLoader({ [compatibleActorId]: actor({
226
+ id: compatibleActorId,
227
+ outbox: compatibleOutboxId
228
+ }) }),
229
+ documentUrl: new URL(compatibleOutboxId)
230
+ }), "collectionOwnerUnavailable");
231
+ });
232
+ test("verifyPortableObject() requires an unambiguous owner", async () => {
233
+ const base = {
234
+ documentLoader: createLoader({ [gatewayUrl(gw1, actorId)]: signedActor }),
235
+ documentUrl: new URL(gatewayUrl(gw1, outboxId)),
236
+ gateways: [new URL(gw1)]
237
+ };
238
+ assertFailure(await verify(outbox(), base), "unknownCollectionOwner");
239
+ const note = await Note.fromJsonLd({
240
+ "@context": context,
241
+ id: `ap://${did}/notes/1`,
242
+ type: "Note"
243
+ }, { contextLoader });
244
+ assertFailure(await verify(outbox(), {
245
+ ...base,
246
+ referrer: linkFrom(note, "replies")
247
+ }), "unknownCollectionOwner");
248
+ const attributed = await verify(outbox({ attributedTo: actorId }), base);
249
+ ok(attributed.verified);
250
+ strictEqual(attributed.method, "gateway");
251
+ assertFailure(await verify(outbox({ attributedTo: [actorId, `ap://${did}/actor2`] }), base), "conflictingCollectionOwners");
252
+ assertFailure(await verify(outbox({ attributedTo: `ap://${did}/actor2` }), {
253
+ ...base,
254
+ referrer: linkFrom(actorObject, "outbox")
255
+ }), "conflictingCollectionOwners");
256
+ assertFailure(await verify(outbox({ attributedTo: "https://example.com/actor" }), base), "unknownCollectionOwner");
257
+ });
258
+ test("verifyPortableObject() requires the owner to share the collection's DID", async () => {
259
+ const foreignActorId = `ap://${otherDid}/actor`;
260
+ const foreignActor = await sign(actor({
261
+ id: foreignActorId,
262
+ gateways: [evil]
263
+ }), otherKeyPair.privateKey, `${otherDid}#${otherDid.slice(8)}`);
264
+ const foreignActorObject = await Person.fromJsonLd(foreignActor, { contextLoader });
265
+ const documentLoader = createLoader({ [gatewayUrl(evil, foreignActorId)]: foreignActor });
266
+ const base = {
267
+ documentLoader,
268
+ documentUrl: new URL(gatewayUrl(evil, outboxId)),
269
+ gateways: [new URL(evil)]
270
+ };
271
+ assertFailure(await verify(outbox(), {
272
+ ...base,
273
+ referrer: linkFrom(foreignActorObject, "outbox")
274
+ }), "unknownCollectionOwner");
275
+ assertFailure(await verify(outbox({ attributedTo: foreignActorId }), base), "unknownCollectionOwner");
276
+ deepStrictEqual(documentLoader.fetched, []);
277
+ });
278
+ test("verifyPortableObject() confirms the owner with its actor document", async () => {
279
+ const documentUrl = new URL(gatewayUrl(gw1, outboxId));
280
+ const gateways = [new URL(gw1)];
281
+ const referrer = linkFrom(actorObject, "outbox");
282
+ assertFailure(await verify(outbox(), {
283
+ documentLoader: createLoader({}),
284
+ documentUrl,
285
+ gateways,
286
+ referrer
287
+ }), "collectionOwnerUnavailable");
288
+ assertFailure(await verify(outbox(), {
289
+ documentLoader: createLoader({ [gatewayUrl(gw1, actorId)]: actor() }),
290
+ documentUrl,
291
+ gateways,
292
+ referrer
293
+ }), "collectionOwnerUnavailable");
294
+ const { gateways: _, ...noGateways } = actor();
295
+ assertFailure(await verify(outbox(), {
296
+ documentLoader: createLoader({ [gatewayUrl(gw1, actorId)]: await sign(noGateways) }),
297
+ documentUrl,
298
+ gateways,
299
+ referrer
300
+ }), "collectionOwnerUnavailable");
301
+ const otherCollectionId = `ap://${did}/collections/1`;
302
+ assertFailure(await verify(outbox({
303
+ id: otherCollectionId,
304
+ attributedTo: actorId
305
+ }), {
306
+ documentLoader: createLoader({ [gatewayUrl(gw1, actorId)]: signedActor }),
307
+ documentUrl: new URL(gatewayUrl(gw1, otherCollectionId)),
308
+ gateways
309
+ }), "collectionNotListedByOwner");
310
+ const emptyLoader = createLoader({ [gatewayUrl(gw1, actorId)]: signedActor });
311
+ assertFailure(await verify(outbox(), {
312
+ documentLoader: emptyLoader,
313
+ documentUrl,
314
+ gateways: [],
315
+ gatewayHints: [new URL(gw1)],
316
+ referrer
317
+ }), "collectionOwnerUnavailable");
318
+ deepStrictEqual(emptyLoader.fetched, [`ap+ef61://${did}/actor`]);
319
+ });
320
+ test("verifyPortableObject() reuses a verified referring actor", async () => {
321
+ const documentLoader = createLoader({});
322
+ const result = await verify(outbox(), {
323
+ documentLoader,
324
+ documentUrl: new URL(gatewayUrl(gw1, outboxId)),
325
+ referrer: linkFrom(actorObject, "outbox", { acceptance: "verified" })
326
+ });
327
+ ok(result.verified);
328
+ strictEqual(result.method, "gateway");
329
+ deepStrictEqual(documentLoader.fetched, []);
330
+ });
331
+ test("verifyPortableObject() does not downgrade signed collections", async () => {
332
+ const documentLoader = createLoader({});
333
+ const signedOutbox = await sign(outbox());
334
+ const result = await verify(signedOutbox, {
335
+ documentLoader,
336
+ referrer: linkFrom(actorObject, "outbox")
337
+ });
338
+ ok(result.verified);
339
+ strictEqual(result.method, "proof");
340
+ assertFailure(await verify({
341
+ ...signedOutbox,
342
+ totalItems: 1
343
+ }, {
344
+ documentLoader: createLoader({ [gatewayUrl(gw1, actorId)]: signedActor }),
345
+ documentUrl: new URL(gatewayUrl(gw1, outboxId)),
346
+ gateways: [new URL(gw1)],
347
+ referrer: linkFrom(actorObject, "outbox")
348
+ }), "invalidProof");
349
+ assertFailure(await verify(outbox({ id: "https://example.com/outbox" }), { documentLoader }), "notPortableObject");
350
+ });
351
+ test("verifyPortableObject() checks pages against their collection", async () => {
352
+ const documentLoader = createLoader({ [gatewayUrl(gw1, actorId)]: signedActor });
353
+ const collectionResult = await verify(outbox(), {
354
+ documentLoader,
355
+ documentUrl: new URL(gatewayUrl(gw1, outboxId)),
356
+ gateways: [new URL(gw1)],
357
+ referrer: linkFrom(actorObject, "outbox")
358
+ });
359
+ ok(collectionResult.verified);
360
+ const collection = await OrderedCollection.fromJsonLd(outbox(), { contextLoader });
361
+ const collectionContext = collectionResult.collectionContext;
362
+ const pageId = `ap://${did}/actor/outbox?page=1`;
363
+ const page = (overrides = {}) => ({
364
+ "@context": context,
365
+ id: pageId,
366
+ type: "OrderedCollectionPage",
367
+ partOf: outboxId,
368
+ orderedItems: [],
369
+ ...overrides
370
+ });
371
+ const pageReferrer = linkFrom(collection, "first", { collectionContext });
372
+ const fetchedBefore = documentLoader.fetched.length;
373
+ const accepted = await verify(page(), {
374
+ documentLoader,
375
+ documentUrl: new URL(gatewayUrl(gw2, pageId)),
376
+ referrer: pageReferrer
377
+ });
378
+ ok(accepted.verified);
379
+ strictEqual(accepted.method, "gateway");
380
+ strictEqual(documentLoader.fetched.length, fetchedBefore);
381
+ assertFailure(await verify(page(), {
382
+ documentLoader,
383
+ documentUrl: new URL(gatewayUrl(evil, pageId)),
384
+ referrer: pageReferrer
385
+ }), "untrustedGateway");
386
+ assertFailure(await verify(page({ partOf: `ap://${did}/actor/inbox` }), {
387
+ documentLoader,
388
+ documentUrl: new URL(gatewayUrl(gw1, pageId)),
389
+ referrer: pageReferrer
390
+ }), "collectionPageMismatch");
391
+ const foreignPageId = `ap://${otherDid}/outbox?page=1`;
392
+ assertFailure(await verify(page({
393
+ id: foreignPageId,
394
+ partOf: void 0
395
+ }), {
396
+ documentLoader,
397
+ documentUrl: new URL(gatewayUrl(gw1, foreignPageId)),
398
+ referrer: pageReferrer
399
+ }), "collectionPageMismatch");
400
+ assertFailure(await verify(page({ attributedTo: `ap://${did}/actor2` }), {
401
+ documentLoader,
402
+ documentUrl: new URL(gatewayUrl(gw1, pageId)),
403
+ referrer: pageReferrer
404
+ }), "conflictingCollectionOwners");
405
+ assertFailure(await verify(page({ attributedTo: actorId }), {
406
+ documentLoader,
407
+ documentUrl: new URL(gatewayUrl(gw1, pageId)),
408
+ referrer: linkFrom(collection, "first")
409
+ }), "unknownCollectionOwner");
410
+ assertFailure(await verify(page(), {
411
+ documentLoader,
412
+ documentUrl: new URL(gatewayUrl(gw1, pageId)),
413
+ referrer: linkFrom(collection, "first", { collectionContext: {
414
+ rootId: outboxId,
415
+ owner: null
416
+ } })
417
+ }), "unknownCollectionOwner");
418
+ });
419
+ test("verifyPortableObject() checks intermediate pages", async () => {
420
+ const documentLoader = createLoader({ [gatewayUrl(gw1, actorId)]: signedActor });
421
+ const collection = await OrderedCollection.fromJsonLd(outbox(), { contextLoader });
422
+ const intermediate = async (partOf) => await CollectionPage.fromJsonLd({
423
+ "@context": context,
424
+ id: `ap://${did}/actor/outbox?page=1`,
425
+ type: "OrderedCollectionPage",
426
+ partOf
427
+ }, { contextLoader });
428
+ const pageId = `ap://${did}/actor/outbox?page=2`;
429
+ const page = {
430
+ "@context": context,
431
+ id: pageId,
432
+ type: "OrderedCollectionPage",
433
+ partOf: outboxId,
434
+ orderedItems: []
435
+ };
436
+ const chain = (first) => linkFrom(first, "next", { referrer: linkFrom(collection, "first", { referrer: linkFrom(actorObject, "outbox") }) });
437
+ const options = {
438
+ documentLoader,
439
+ documentUrl: new URL(gatewayUrl(gw1, pageId)),
440
+ gateways: [new URL(gw1)]
441
+ };
442
+ const result = await verify(page, {
443
+ ...options,
444
+ referrer: chain(await intermediate(outboxId))
445
+ });
446
+ ok(result.verified);
447
+ strictEqual(result.method, "gateway");
448
+ assertFailure(await verify(page, {
449
+ ...options,
450
+ referrer: chain(await intermediate(`ap://${did}/actor/inbox`))
451
+ }), "collectionPageMismatch");
452
+ assertFailure(await verify(page, {
453
+ ...options,
454
+ referrer: linkFrom(await intermediate(outboxId), "next", { truncated: true })
455
+ }), "unknownCollectionOwner");
456
+ });
457
+ test("verifyPortableObject() works with property accessors", async () => {
458
+ const itemId = `ap://${did}/notes/1`;
459
+ const note = await sign({
460
+ "@context": context,
461
+ id: itemId,
462
+ type: "Note",
463
+ attributedTo: actorId,
464
+ content: "Hello"
465
+ });
466
+ const page1Id = `ap://${did}/actor/outbox?page=1`;
467
+ const page2Id = `ap://${did}/actor/outbox?page=2`;
468
+ const page1 = {
469
+ id: page1Id,
470
+ type: "OrderedCollectionPage",
471
+ partOf: outboxId,
472
+ orderedItems: [{
473
+ id: itemId,
474
+ type: "Note",
475
+ content: "Forged"
476
+ }],
477
+ next: page2Id
478
+ };
479
+ const options = {
480
+ documentLoader: createLoader({
481
+ [gatewayUrl(gw1, actorId)]: signedActor,
482
+ [gatewayUrl(gw1, outboxId)]: outbox({ first: page1 }),
483
+ [gatewayUrl(gw1, page1Id)]: {
484
+ "@context": context,
485
+ ...page1
486
+ },
487
+ [gatewayUrl(gw1, page2Id)]: {
488
+ "@context": context,
489
+ id: page2Id,
490
+ type: "OrderedCollectionPage",
491
+ partOf: outboxId,
492
+ orderedItems: [itemId]
493
+ },
494
+ [gatewayUrl(gw1, itemId)]: note
495
+ }),
496
+ contextLoader,
497
+ gateways: [gw1],
498
+ verifyPortableObject
499
+ };
500
+ const create = await Collection.fromJsonLd({
501
+ "@context": context,
502
+ type: "Collection",
503
+ items: [actorId]
504
+ }, { contextLoader });
505
+ const items = await Array.fromAsync(create.getItems(options));
506
+ strictEqual(items.length, 1);
507
+ ok(items[0] instanceof Person);
508
+ const outboxCollection = await items[0].getOutbox(options);
509
+ ok(outboxCollection instanceof OrderedCollection);
510
+ deepStrictEqual((await Array.fromAsync(traverseCollection(outboxCollection, options))).map((item) => item instanceof Note ? item.content : null), ["Hello", "Hello"]);
511
+ const evilLoader = createLoader({
512
+ [gatewayUrl(evil, actorId)]: signedActor,
513
+ [gatewayUrl(evil, outboxId)]: outbox()
514
+ });
515
+ strictEqual(await (await Person.fromJsonLd(signedActor, { contextLoader })).getOutbox({
516
+ ...options,
517
+ documentLoader: evilLoader,
518
+ gateways: [evil]
519
+ }), null);
520
+ });
521
+ test("verifyPortableObject() lets pages inherit owners across signed and embedded collections", async () => {
522
+ const page1Id = `ap://${did}/actor/outbox?page=1`;
523
+ const page1 = {
524
+ "@context": context,
525
+ id: page1Id,
526
+ type: "OrderedCollectionPage",
527
+ partOf: outboxId,
528
+ orderedItems: []
529
+ };
530
+ const fetchPerson = async (options) => {
531
+ const wrapper = await Collection.fromJsonLd({
532
+ "@context": context,
533
+ type: "Collection",
534
+ items: [actorId]
535
+ }, { contextLoader });
536
+ const [person] = await Array.fromAsync(wrapper.getItems(options));
537
+ ok(person instanceof Person);
538
+ return person;
539
+ };
540
+ const signedOutbox = await sign(outbox({ first: page1Id }));
541
+ let documentLoader = createLoader({
542
+ [gatewayUrl(gw1, actorId)]: signedActor,
543
+ [gatewayUrl(gw1, outboxId)]: signedOutbox,
544
+ [gatewayUrl(gw2, page1Id)]: page1
545
+ });
546
+ let options = {
547
+ documentLoader,
548
+ contextLoader,
549
+ gateways: [gw1, gw2],
550
+ verifyPortableObject
551
+ };
552
+ let person = await fetchPerson(options);
553
+ let collection = await person.getOutbox(options);
554
+ ok(collection instanceof OrderedCollection);
555
+ ok(await collection.getFirst(options) instanceof CollectionPage);
556
+ const embeddingActor = await sign(actor({ outbox: {
557
+ id: outboxId,
558
+ type: "OrderedCollection",
559
+ first: page1Id
560
+ } }));
561
+ documentLoader = createLoader({
562
+ [gatewayUrl(gw1, actorId)]: embeddingActor,
563
+ [gatewayUrl(gw2, page1Id)]: page1,
564
+ [gatewayUrl(evil, page1Id)]: page1
565
+ });
566
+ options = {
567
+ ...options,
568
+ documentLoader
569
+ };
570
+ person = await fetchPerson(options);
571
+ collection = await person.getOutbox(options);
572
+ ok(collection instanceof OrderedCollection);
573
+ ok(await collection.getFirst(options) instanceof CollectionPage);
574
+ deepStrictEqual(documentLoader.fetched, [
575
+ gatewayUrl(gw1, actorId),
576
+ gatewayUrl(gw1, page1Id),
577
+ gatewayUrl(gw2, page1Id)
578
+ ]);
579
+ person = await fetchPerson(options);
580
+ collection = await person.getOutbox(options);
581
+ ok(collection instanceof OrderedCollection);
582
+ strictEqual(await collection.getFirst({
583
+ ...options,
584
+ gateways: [evil]
585
+ }), null);
586
+ });
587
+ test("verifyPortableObject() reuses a verified actor for pages of signed collections", async () => {
588
+ const documentLoader = createLoader({});
589
+ const signed = await verify(await sign(outbox()), {
590
+ documentLoader,
591
+ referrer: linkFrom(actorObject, "outbox", { acceptance: "verified" })
592
+ });
593
+ ok(signed.verified);
594
+ strictEqual(signed.method, "proof");
595
+ const collection = await OrderedCollection.fromJsonLd(outbox(), { contextLoader });
596
+ const pageId = `ap://${did}/actor/outbox?page=1`;
597
+ const result = await verify({
598
+ "@context": context,
599
+ id: pageId,
600
+ type: "OrderedCollectionPage",
601
+ partOf: outboxId,
602
+ orderedItems: []
603
+ }, {
604
+ documentLoader,
605
+ documentUrl: new URL(gatewayUrl(gw1, pageId)),
606
+ referrer: linkFrom(collection, "first", { collectionContext: signed.collectionContext })
607
+ });
608
+ ok(result.verified);
609
+ strictEqual(result.method, "gateway");
610
+ deepStrictEqual(documentLoader.fetched, []);
611
+ });
612
+ test("verifyPortableObject() fetches the owner through attributedTo hints", async () => {
613
+ const documentLoader = createLoader({ [gatewayUrl(gw2, actorId)]: signedActor });
614
+ const result = await verify(outbox({ attributedTo: `${actorId}?@gateway=${encodeURIComponent(gw2)}` }), {
615
+ documentLoader,
616
+ documentUrl: new URL(gatewayUrl(gw1, outboxId)),
617
+ gatewayHints: [new URL(gw1)]
618
+ });
619
+ ok(result.verified);
620
+ strictEqual(result.method, "gateway");
621
+ deepStrictEqual(documentLoader.fetched, [gatewayUrl(gw2, actorId)]);
622
+ });
623
+ test("verifyPortableObject() applies to compatible identifiers in accessors", async (t) => {
624
+ const noteId = `ap://${did}/notes/1`;
625
+ const signedNote = await sign({
626
+ "@context": context,
627
+ id: noteId,
628
+ type: "Note",
629
+ attributedTo: actorId,
630
+ content: "Hello"
631
+ });
632
+ const forgedNote = {
633
+ "@context": context,
634
+ id: noteId,
635
+ type: "Note",
636
+ attributedTo: actorId,
637
+ content: "Forged"
638
+ };
639
+ const collection = (items) => Collection.fromJsonLd({
640
+ "@context": context,
641
+ id: "https://example.com/collection",
642
+ type: "Collection",
643
+ items
644
+ }, { contextLoader });
645
+ const getItems = async (items, documentLoader) => await Array.fromAsync((await collection(items)).getItems({
646
+ documentLoader,
647
+ contextLoader,
648
+ verifyPortableObject
649
+ }));
650
+ await t.step("signed objects", async () => {
651
+ const documentLoader = createLoader({ [gatewayUrl(gw1, noteId)]: signedNote });
652
+ deepStrictEqual((await getItems([gatewayUrl(gw1, noteId)], documentLoader)).map((item) => item instanceof Note ? item.content : null), ["Hello"]);
653
+ });
654
+ await t.step("forged objects", async () => {
655
+ const documentLoader = createLoader({
656
+ [gatewayUrl(evil, noteId)]: forgedNote,
657
+ [gatewayUrl(evil, `ap://${did}/notes/2`)]: {
658
+ ...forgedNote,
659
+ id: gatewayUrl(evil, `ap://${did}/notes/2`),
660
+ attributedTo: gatewayUrl(evil, actorId)
661
+ },
662
+ "https://evil.example/notes/1": { redirect: gatewayUrl(evil, noteId) },
663
+ [gatewayUrl("https://example.com", noteId)]: forgedNote
664
+ });
665
+ deepStrictEqual(await getItems([
666
+ gatewayUrl(evil, noteId),
667
+ gatewayUrl(evil, `ap://${did}/notes/2`),
668
+ "https://evil.example/notes/1",
669
+ {
670
+ id: gatewayUrl("https://example.com", noteId),
671
+ type: "Note",
672
+ content: "Forged"
673
+ }
674
+ ], documentLoader), []);
675
+ });
676
+ await t.step("unsecured collections", async () => {
677
+ const unsecured = outbox({ attributedTo: actorId });
678
+ const documentLoader = createLoader({
679
+ [gatewayUrl(gw1, actorId)]: signedActor,
680
+ [gatewayUrl(gw1, outboxId)]: unsecured,
681
+ [gatewayUrl(evil, actorId)]: signedActor,
682
+ [gatewayUrl(evil, outboxId)]: unsecured
683
+ });
684
+ const items = await getItems([gatewayUrl(gw1, outboxId)], documentLoader);
685
+ strictEqual(items.length, 1);
686
+ ok(items[0] instanceof OrderedCollection);
687
+ deepStrictEqual(documentLoader.fetched, [gatewayUrl(gw1, outboxId), gatewayUrl(gw1, actorId)]);
688
+ deepStrictEqual(await getItems([gatewayUrl(evil, outboxId)], documentLoader), []);
689
+ deepStrictEqual(await getItems([gatewayUrl(gw1, outboxId)], createLoader({
690
+ [gatewayUrl(gw1, actorId)]: signedActor,
691
+ [gatewayUrl(gw1, outboxId)]: { redirect: gatewayUrl(evil, outboxId) },
692
+ [gatewayUrl(evil, outboxId)]: unsecured
693
+ })), []);
694
+ });
695
+ });
696
+ //#endregion
697
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";