@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,549 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { a as getRawCanonicalPortableId, c as isPortableActorDocument, d as isPortableUri, n as getCanonicalPortableId, p as isSamePublicKey } from "./portable-key-id-Ca30-hOa.mjs";
5
+ import { u as verifyPortableObjectProofWithRoot } from "./proof-Dxy6ByqI.mjs";
6
+ import { getLogger } from "@logtape/logtape";
7
+ import { CryptographicKey, Multikey, Object as Object$1 } from "@fedify/vocab";
8
+ import { FetchError, formatIri, getDocumentLoader, isGatewayUrl, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
9
+ import { getPortableGatewayCandidates } from "@fedify/vocab-runtime/internal/portable-dereference";
10
+ //#region src/sig/portable-key.ts
11
+ const logger = getLogger([
12
+ "fedify",
13
+ "sig",
14
+ "key"
15
+ ]);
16
+ const SEC = "https://w3id.org/security#";
17
+ /**
18
+ * Verifies that the signed document of a portable actor vouches for
19
+ * the gateway key with the given ID.
20
+ *
21
+ * A gateway key is accepted only if:
22
+ *
23
+ * - the document is an actor whose portable ID, i.e., its `ap:` ID or
24
+ * the canonical portable ID of its compatible identifier, is the one
25
+ * the key ID is the compatible identifier of;
26
+ * - the document has a valid [FEP-8b32] Object Integrity Proof made by
27
+ * the DID of its ID, i.e., it satisfies the [FEP-ef61] proof policy;
28
+ * - the document embeds a `Multikey` with that ID in its `assertionMethod`,
29
+ * whose `controller` is the actor, as [FEP-521a] requires, and a key
30
+ * embedded in its `publicKey` with the same ID agrees with it; or, if
31
+ * no `assertionMethod` entry has that ID, the document embeds
32
+ * a `CryptographicKey` with that ID in its `publicKey`, whose `owner` is
33
+ * the actor, as some publishers, e.g., tootik, list their RSA keys only
34
+ * there;
35
+ * - no more than one entry of either property has that ID; and
36
+ * - the key ID's origin is one of the actor's `gateways`.
37
+ *
38
+ * An entry has the key's ID if its ID is the key ID, or any compatible
39
+ * identifier on the same gateway, or the `ap:` or `ap+ef61:` URI, with the
40
+ * same canonical portable ID. Some publishers, e.g., Mitra, list the keys
41
+ * of portable actors under `ap:` URIs, but sign requests with compatible key
42
+ * IDs on their own gateways. IDs are compared as they are written in
43
+ * the signed document, so an ID that URL parsing would turn into another,
44
+ * e.g., by resolving dot segments, has no match.
45
+ *
46
+ * [FEP-8b32]: https://w3id.org/fep/8b32
47
+ * [FEP-ef61]: https://w3id.org/fep/ef61
48
+ * [FEP-521a]: https://w3id.org/fep/521a
49
+ *
50
+ * @param document The raw JSON-LD document served at the key ID.
51
+ * @param actor The parsed document, which must be a portable actor.
52
+ * @param keyId The key ID, which must be a compatible key ID.
53
+ * @param options Loaders and telemetry providers.
54
+ * @returns The verification result, which is never `legacy`.
55
+ * @internal
56
+ */
57
+ function verifyPortableGatewayKeyDocument(document, actor, keyId, options = {}) {
58
+ return verifyPortableKeyDocument(document, actor, keyId, true, options);
59
+ }
60
+ /**
61
+ * Verifies that the signed document of a portable actor vouches for the key
62
+ * with the given ID, either a gateway key at a compatible key ID, or a key of
63
+ * the actor itself at an `ap:` or `ap+ef61:` key ID. See
64
+ * {@link verifyPortableGatewayKeyDocument} for the rules; a key of the actor
65
+ * itself names no gateway, so instead of the key ID's origin being one of
66
+ * the actor's `gateways`, the actor only has to have a valid gateway, as
67
+ * FEP-ef61 requires of every portable actor.
68
+ */
69
+ async function verifyPortableKeyDocument(document, actor, keyId, isGatewayKey, options) {
70
+ const reject = (reason) => {
71
+ logger.debug("Failed to verify key {keyId} of the portable actor {actorId}: {reason}", {
72
+ keyId: formatIri(keyId),
73
+ actorId: actor.id?.href,
74
+ reason
75
+ });
76
+ return {
77
+ type: "rejected",
78
+ reason
79
+ };
80
+ };
81
+ const keyBase = new URL(keyId.href);
82
+ keyBase.hash = "";
83
+ const claimedActorId = getCanonicalPortableId(keyBase);
84
+ const claimedKeyId = getCanonicalPortableId(keyId);
85
+ const actorId = actor.id == null ? null : getCanonicalPortableId(actor.id);
86
+ if (claimedActorId == null || claimedKeyId == null || actorId == null) return reject("The key ID or the actor ID is not a valid portable ID.");
87
+ if (claimedActorId !== actorId) return reject("The key ID is not a compatible identifier of the actor it dereferences to.");
88
+ let verification;
89
+ try {
90
+ verification = await verifyPortableObjectProofWithRoot(document, {
91
+ documentLoader: options.documentLoader,
92
+ contextLoader: options.contextLoader,
93
+ keyCache: options.keyCache,
94
+ tracerProvider: options.tracerProvider,
95
+ meterProvider: options.meterProvider
96
+ });
97
+ } catch (error) {
98
+ if (!(error instanceof TypeError)) throw error;
99
+ return reject(`The actor document is malformed: ${error.message}`);
100
+ }
101
+ const { result, root, expires } = verification;
102
+ if (!result.verified) return reject("The actor document does not satisfy the FEP-ef61 proof policy: " + result.reason.type);
103
+ if (root == null) return reject("The actor document could not be expanded.");
104
+ const refuse = (url) => Promise.reject(/* @__PURE__ */ new Error(`Refusing to fetch ${url}.`));
105
+ const parseEmbeddedPublicKey = async (node) => {
106
+ if (isReference(node)) return null;
107
+ let key;
108
+ try {
109
+ key = await CryptographicKey.fromJsonLd(node, {
110
+ documentLoader: refuse,
111
+ contextLoader: options.contextLoader,
112
+ tracerProvider: options.tracerProvider
113
+ });
114
+ } catch (error) {
115
+ if (!(error instanceof TypeError)) throw error;
116
+ return null;
117
+ }
118
+ return key.publicKey == null ? null : key;
119
+ };
120
+ let verifiedActor;
121
+ try {
122
+ verifiedActor = await Object$1.fromJsonLd(root, {
123
+ documentLoader: refuse,
124
+ contextLoader: options.contextLoader,
125
+ tracerProvider: options.tracerProvider
126
+ });
127
+ } catch (error) {
128
+ if (!(error instanceof TypeError)) throw error;
129
+ return reject("The verified actor document is malformed.");
130
+ }
131
+ const rootId = root["@id"];
132
+ if (!isPortableActorDocument(verifiedActor) || getCanonicalPortableId(verifiedActor.id) !== claimedActorId || typeof rootId !== "string" || getRawCanonicalPortableId(rootId) !== claimedActorId) return reject("The key ID is not a compatible identifier of the actor whose document is signed.");
133
+ actor = verifiedActor;
134
+ const findKeyNodes = (values) => findNodes(values, claimedKeyId, isGatewayKey ? keyId.origin : null);
135
+ const assertionNodes = findKeyNodes(root[`${SEC}assertionMethod`]);
136
+ const publicKeyNodes = findKeyNodes(root[`${SEC}publicKey`]);
137
+ if (assertionNodes.length > 1 || publicKeyNodes.length > 1) return reject("The actor document has more than one key with the ID.");
138
+ let publicKey;
139
+ if (assertionNodes.length > 0) {
140
+ if (isReference(assertionNodes[0])) return reject("The actor document does not embed the key in its assertionMethod.");
141
+ let multikey;
142
+ try {
143
+ multikey = await Multikey.fromJsonLd(assertionNodes[0], {
144
+ documentLoader: refuse,
145
+ contextLoader: options.contextLoader,
146
+ tracerProvider: options.tracerProvider
147
+ });
148
+ } catch (error) {
149
+ if (!(error instanceof TypeError)) throw error;
150
+ return reject("The key in the assertionMethod is malformed.");
151
+ }
152
+ if (multikey.publicKey == null) return reject("The key in the assertionMethod has no public key.");
153
+ if (multikey.controllerId == null || getCanonicalPortableId(multikey.controllerId) !== actorId) return reject("The controller of the key is not the actor.");
154
+ publicKey = multikey.publicKey;
155
+ if (publicKeyNodes.length > 0 && !isReference(publicKeyNodes[0])) {
156
+ const embedded = await parseEmbeddedPublicKey(publicKeyNodes[0]);
157
+ if (embedded == null || !await isSamePublicKey(embedded.publicKey, publicKey) || embedded.ownerId != null && getCanonicalPortableId(embedded.ownerId) !== actorId) return reject("The key in the publicKey does not agree with the one in the assertionMethod.");
158
+ }
159
+ } else {
160
+ if (publicKeyNodes.length < 1) return reject("The actor document embeds the key in neither its assertionMethod nor its publicKey.");
161
+ const embedded = await parseEmbeddedPublicKey(publicKeyNodes[0]);
162
+ if (embedded == null) return reject("The key in the publicKey is not embedded or malformed.");
163
+ if (embedded.ownerId == null || getCanonicalPortableId(embedded.ownerId) !== actorId) return reject("The owner of the key is not the actor.");
164
+ publicKey = embedded.publicKey;
165
+ }
166
+ if (isGatewayKey) {
167
+ if (!actor.gateways.some((gateway) => isGatewayUrl(gateway) && gateway.origin === keyId.origin)) return reject(`The gateway ${keyId.origin} is not listed in the actor's gateways.`);
168
+ } else if (!actor.gateways.some(isGatewayUrl)) return reject("The actor has no valid gateways.");
169
+ const key = new CryptographicKey({
170
+ id: keyId,
171
+ owner: actor.id,
172
+ publicKey
173
+ });
174
+ return {
175
+ type: "verified",
176
+ actor,
177
+ key,
178
+ document: root,
179
+ ...expires == null ? {} : { expires }
180
+ };
181
+ }
182
+ /**
183
+ * Fetches the document at a compatible key ID, and resolves the key as
184
+ * a gateway key if the document is a portable actor.
185
+ *
186
+ * @param keyId The key ID, which must be a compatible key ID.
187
+ * @param options Loaders and telemetry providers.
188
+ * @returns `legacy` if the document cannot be fetched or is not a portable
189
+ * actor, which leaves the key to the usual resolution; otherwise,
190
+ * the result of {@link verifyPortableGatewayKeyDocument}.
191
+ * @internal
192
+ */
193
+ async function fetchPortableGatewayKey(keyId, options = {}) {
194
+ const documentLoader = options.documentLoader ?? getDocumentLoader();
195
+ const contextLoader = options.contextLoader ?? getDocumentLoader();
196
+ let remoteDocument;
197
+ try {
198
+ remoteDocument = await documentLoader(keyId.href);
199
+ } catch (error) {
200
+ logger.debug("Failed to fetch key {keyId}: {error}", {
201
+ keyId: keyId.href,
202
+ error
203
+ });
204
+ return { type: "legacy" };
205
+ }
206
+ let object;
207
+ try {
208
+ object = await Object$1.fromJsonLd(remoteDocument.document, {
209
+ documentLoader,
210
+ contextLoader,
211
+ tracerProvider: options.tracerProvider
212
+ });
213
+ } catch (error) {
214
+ if (!(error instanceof TypeError)) throw error;
215
+ return { type: "legacy" };
216
+ }
217
+ if (!isPortableActorDocument(object)) return { type: "legacy" };
218
+ return await verifyPortableGatewayKeyDocument(remoteDocument.document, object, keyId, {
219
+ ...options,
220
+ documentLoader,
221
+ contextLoader
222
+ });
223
+ }
224
+ /**
225
+ * The maximum number of `@gateway` location hints followed for one `ap:` or
226
+ * `ap+ef61:` key ID. Whoever made a signature chooses the hints of its key
227
+ * ID, so fewer of them are followed than when dereferencing objects, which
228
+ * bounds the gateways one signature can make Fedify ask. Further hints are
229
+ * ignored.
230
+ */
231
+ const MAX_KEY_ID_GATEWAY_HINTS = 3;
232
+ /**
233
+ * The default of {@link PortableActorKeyOptions.lookupTimeout}, in
234
+ * milliseconds.
235
+ */
236
+ const DEFAULT_KEY_ID_LOOKUP_TIMEOUT = 1e4;
237
+ /** The longest delay that `setTimeout()` supports, in milliseconds. */
238
+ const MAX_TIMEOUT = 2 ** 31 - 1;
239
+ /**
240
+ * Resolves a key of a portable actor at an `ap:` or `ap+ef61:` key ID, e.g.,
241
+ * `ap://did:key:z6Mk…/actor#main-key`. Such a key ID names no gateway, so
242
+ * the actor's document, i.e., the key ID without its fragment, is fetched
243
+ * through the gateways in its `@gateway` location hints, one after another,
244
+ * until one of them serves a document that vouches for the key. Without
245
+ * location hints, the document loader is asked for the `ap:` URI itself,
246
+ * which only a custom document loader can resolve. The hints are chosen by
247
+ * whoever made the signature, but that does not matter: only a document with
248
+ * a valid proof by the DID of the key ID vouches for the key.
249
+ *
250
+ * The key is taken as a key of the actor itself, not of a gateway, so it is
251
+ * accepted by the rules of {@link verifyPortableGatewayKeyDocument}, except
252
+ * that it is listed under the `ap:` or `ap+ef61:` URI with the same canonical
253
+ * ID as the key ID, and that the actor only has to have a valid gateway.
254
+ * The key ID must have a fragment; a key served as a standalone document is
255
+ * not supported.
256
+ *
257
+ * If no gateway serves a document that vouches for the key, the result is
258
+ * `rejected` only if every gateway served a document that definitely does not
259
+ * vouch for it. If every gateway failed to serve a document at all, the
260
+ * result is a cacheable `unavailable`, with the error of the first gateway if
261
+ * every gateway responded with the same HTTP status, so that the status
262
+ * describes the whole lookup. Otherwise, e.g., if one gateway was
263
+ * unreachable and another served an invalid document, nothing tells what the
264
+ * unreachable gateway would have served, so the result is an `unavailable`
265
+ * that is not cacheable.
266
+ *
267
+ * Only the first {@link MAX_KEY_ID_GATEWAY_HINTS} location hints are
268
+ * followed, and all the gateways share one timeout, see
269
+ * {@link PortableActorKeyOptions.lookupTimeout}. A gateway that does not
270
+ * serve the document in time failed to serve it, like an unreachable one,
271
+ * with an error that has no HTTP status. A gateway whose document could not
272
+ * be processed in time, and the gateways left untried, make the result an
273
+ * `unavailable` that is not cacheable. A result that arrives after
274
+ * the timeout is ignored, even if it vouches for the key.
275
+ *
276
+ * @param keyId The key ID, which must be an `ap:` or `ap+ef61:` URI.
277
+ * @param options Loaders, telemetry providers, and the timeout.
278
+ * @returns The resolution, which is never `legacy`.
279
+ * @throws {RangeError} If the timeout is not a non-negative number of
280
+ * milliseconds that `setTimeout()` supports.
281
+ * @internal
282
+ */
283
+ async function resolvePortableActorKey(keyId, options = {}) {
284
+ const lookupTimeout = options.lookupTimeout ?? DEFAULT_KEY_ID_LOOKUP_TIMEOUT;
285
+ if (!Number.isFinite(lookupTimeout) || lookupTimeout < 0 || lookupTimeout > MAX_TIMEOUT) throw new RangeError(`The lookup timeout must be between 0 and ${MAX_TIMEOUT} ms: ${lookupTimeout}`);
286
+ const documentLoader = options.documentLoader ?? getDocumentLoader();
287
+ const contextLoader = options.contextLoader ?? getDocumentLoader();
288
+ let formattedKeyId = keyId.href;
289
+ const reject = (reason) => {
290
+ logger.debug("Failed to verify key {keyId} of a portable actor: {reason}", {
291
+ keyId: formattedKeyId,
292
+ reason
293
+ });
294
+ return {
295
+ type: "rejected",
296
+ reason
297
+ };
298
+ };
299
+ if (!isPortableUri(keyId) || getCanonicalPortableId(keyId) == null) return reject("The key ID is not a valid ap: or ap+ef61: URI.");
300
+ formattedKeyId = formatIri(keyId);
301
+ if (keyId.hash === "" || keyId.hash === "#") return reject("The key ID has no fragment; only keys embedded in actor documents are supported.");
302
+ const actorUrl = new URL(keyId.href);
303
+ actorUrl.hash = "";
304
+ const gateways = getPortableGatewayCandidates(actorUrl);
305
+ if (gateways.length > MAX_KEY_ID_GATEWAY_HINTS) logger.debug("Following only the first {max} location hints of key {keyId}.", {
306
+ keyId: formattedKeyId,
307
+ max: MAX_KEY_ID_GATEWAY_HINTS
308
+ });
309
+ const requestUrls = [];
310
+ for (const gateway of gateways.slice(0, MAX_KEY_ID_GATEWAY_HINTS)) try {
311
+ requestUrls.push(toCompatibleEf61Id(actorUrl, gateway).href);
312
+ } catch (error) {
313
+ if (!(error instanceof TypeError)) throw error;
314
+ return reject(`The key ID cannot be fetched from gateways: ${error}`);
315
+ }
316
+ if (requestUrls.length < 1) requestUrls.push(formatIri(actorUrl));
317
+ const controller = new AbortController();
318
+ const timeoutError = new DOMException(`Timed out looking up the actor of key ${formattedKeyId} after ${lookupTimeout} ms.`, "TimeoutError");
319
+ let timer;
320
+ if (lookupTimeout > 0) timer = setTimeout(() => controller.abort(timeoutError), lookupTimeout);
321
+ else controller.abort(timeoutError);
322
+ const { signal } = controller;
323
+ const attemptOptions = {
324
+ ...options,
325
+ documentLoader: withDeadline(documentLoader, signal),
326
+ contextLoader: withDeadline(contextLoader, signal),
327
+ signal,
328
+ formattedKeyId
329
+ };
330
+ const attempts = [];
331
+ try {
332
+ for (const url of requestUrls) {
333
+ if (signal.aborted) {
334
+ logger.debug("Ran out of time looking up the actor of key {keyId}; gateways from {url} on were not tried.", {
335
+ keyId: formattedKeyId,
336
+ url
337
+ });
338
+ attempts.push({
339
+ type: "indeterminate",
340
+ error: signal.reason
341
+ });
342
+ break;
343
+ }
344
+ const state = { processing: false };
345
+ let attempt;
346
+ try {
347
+ attempt = await raceAbort(attemptGateway(url, keyId, attemptOptions, state), signal);
348
+ } catch (error) {
349
+ if (!signal.aborted) throw error;
350
+ logger.debug("Ran out of time looking up the actor of key {keyId} at {url}.", {
351
+ keyId: formattedKeyId,
352
+ url
353
+ });
354
+ attempts.push({
355
+ type: state.processing ? "indeterminate" : "retrievalFailed",
356
+ error: signal.reason
357
+ });
358
+ continue;
359
+ }
360
+ if (attempt.type === "verified") return attempt;
361
+ attempts.push(attempt);
362
+ }
363
+ } finally {
364
+ clearTimeout(timer);
365
+ }
366
+ const rejections = attempts.flatMap((a) => a.type === "rejected" ? [a.reason] : []);
367
+ if (rejections.length === attempts.length) return reject(rejections.join(" "));
368
+ const errors = attempts.flatMap((a) => a.type === "rejected" ? [] : [a.error]);
369
+ const aggregate = errors.length === 1 ? errors[0] : new AggregateError(errors, `Failed to fetch the actor of key ${formattedKeyId} from any of its gateways: ${requestUrls.join(", ")}`);
370
+ if (attempts.every((a) => a.type === "retrievalFailed")) {
371
+ const statuses = errors.map((error) => error instanceof FetchError ? error.response?.status : void 0);
372
+ return {
373
+ type: "unavailable",
374
+ error: statuses[0] != null && statuses.every((status) => status === statuses[0]) ? errors[0] : aggregate,
375
+ cacheable: true
376
+ };
377
+ }
378
+ return {
379
+ type: "unavailable",
380
+ error: new Error(`Could not determine whether the actor of key ${formattedKeyId} vouches for it.`, { cause: aggregate }),
381
+ cacheable: false
382
+ };
383
+ }
384
+ /**
385
+ * Fetches the actor's document of a key at an `ap:` or `ap+ef61:` key ID from
386
+ * one gateway, and tells whether it vouches for the key. It never rejects;
387
+ * the caller races it against the lookup's timeout, and ignores what it
388
+ * resolves to after the timeout.
389
+ */
390
+ async function attemptGateway(url, keyId, options, state) {
391
+ const { documentLoader, contextLoader, signal, formattedKeyId } = options;
392
+ let remoteDocument;
393
+ try {
394
+ remoteDocument = await documentLoader(url);
395
+ } catch (error) {
396
+ logger.debug("Failed to fetch the actor of key {keyId} from {url}: {error}", {
397
+ keyId: formattedKeyId,
398
+ url,
399
+ error
400
+ });
401
+ return {
402
+ type: "retrievalFailed",
403
+ error
404
+ };
405
+ }
406
+ if (signal.aborted) return {
407
+ type: "indeterminate",
408
+ error: signal.reason
409
+ };
410
+ state.processing = true;
411
+ let resolution;
412
+ try {
413
+ const object = await Object$1.fromJsonLd(remoteDocument.document, {
414
+ documentLoader,
415
+ contextLoader,
416
+ tracerProvider: options.tracerProvider
417
+ });
418
+ signal.throwIfAborted();
419
+ resolution = isPortableActorDocument(object) ? await verifyPortableKeyDocument(remoteDocument.document, object, keyId, false, options) : {
420
+ type: "rejected",
421
+ reason: "The document is not a portable actor."
422
+ };
423
+ } catch (error) {
424
+ if (error instanceof TypeError && !signal.aborted) resolution = {
425
+ type: "rejected",
426
+ reason: `The document is malformed: ${error.message}`
427
+ };
428
+ else {
429
+ logger.debug("Failed to process the actor of key {keyId} served from {url}: {error}", {
430
+ keyId: formattedKeyId,
431
+ url,
432
+ error
433
+ });
434
+ return {
435
+ type: "indeterminate",
436
+ error
437
+ };
438
+ }
439
+ }
440
+ if (resolution.type === "verified") return resolution;
441
+ const reason = resolution.type === "rejected" ? resolution.reason : "The document is not a portable actor.";
442
+ logger.debug("The actor document served from {url} does not vouch for key {keyId}: {reason}", {
443
+ keyId: formattedKeyId,
444
+ url,
445
+ reason
446
+ });
447
+ return {
448
+ type: "rejected",
449
+ reason
450
+ };
451
+ }
452
+ /**
453
+ * Wraps a document loader so that it gives up when the given signal aborts,
454
+ * as well as when the signal in its own options does. The wrapped loader is
455
+ * passed a signal that aborts in either case, but in case it ignores that
456
+ * signal, what it resolves to after the abort is ignored, and the wrapper
457
+ * rejects with the reason of the abort at once. After the abort, the wrapped
458
+ * loader is not called at all.
459
+ */
460
+ function withDeadline(loader, signal) {
461
+ return (url, options) => {
462
+ const combined = combineSignals(signal, options?.signal);
463
+ return raceAbort(combined.signal.aborted ? Promise.reject(combined.signal.reason) : new Promise((resolve) => resolve(loader(url, {
464
+ ...options,
465
+ signal: combined.signal
466
+ }))), combined.signal).finally(combined.dispose);
467
+ };
468
+ }
469
+ /**
470
+ * Makes a signal that aborts when either of the given signals does, with
471
+ * the reason of the one that aborted first. `AbortSignal.any()` is not
472
+ * available on every supported runtime, e.g., Bun before 1.1.4.
473
+ * @returns The signal, and a function that stops listening to the given
474
+ * signals.
475
+ */
476
+ function combineSignals(signal, other) {
477
+ if (other == null || other === signal) return {
478
+ signal,
479
+ dispose() {}
480
+ };
481
+ const controller = new AbortController();
482
+ const sources = [signal, other];
483
+ const dispose = () => {
484
+ for (const source of sources) source.removeEventListener("abort", onAbort);
485
+ };
486
+ function onAbort() {
487
+ dispose();
488
+ controller.abort(this.reason);
489
+ }
490
+ const aborted = sources.find((source) => source.aborted);
491
+ if (aborted == null) for (const source of sources) source.addEventListener("abort", onAbort);
492
+ else controller.abort(aborted.reason);
493
+ return {
494
+ signal: controller.signal,
495
+ dispose
496
+ };
497
+ }
498
+ /**
499
+ * Waits for the given promise unless the given signal aborts first, in which
500
+ * case it rejects with the reason of the abort. If the signal has aborted by
501
+ * the time the promise settles, the abort wins as well, so that nothing that
502
+ * settles after it counts.
503
+ */
504
+ function raceAbort(promise, signal) {
505
+ return new Promise((resolve, reject) => {
506
+ const onAbort = () => reject(signal.reason);
507
+ const settle = (settled) => {
508
+ signal.removeEventListener("abort", onAbort);
509
+ if (signal.aborted) onAbort();
510
+ else settled();
511
+ };
512
+ promise.then((value) => settle(() => resolve(value)), (error) => settle(() => reject(error)));
513
+ if (signal.aborted) onAbort();
514
+ else signal.addEventListener("abort", onAbort, { once: true });
515
+ });
516
+ }
517
+ /**
518
+ * Finds the nodes that stand for the key with the given canonical portable
519
+ * ID. A node whose ID is an `ap:` or `ap+ef61:` URI stands for the key
520
+ * itself, e.g., Mitra lists the keys of portable actors that way while
521
+ * signing requests with compatible key IDs. A node whose ID is a compatible
522
+ * identifier stands for a gateway's key, so it matches only a compatible key
523
+ * ID on the same gateway; a key ID that names no gateway never selects
524
+ * a gateway's key, even with the same fragment.
525
+ * @param values The expanded values of a key property.
526
+ * @param canonicalKeyId The canonical portable ID of the key ID.
527
+ * @param gateway The origin of the gateway whose key the key ID names, or
528
+ * `null` if the key ID names no gateway.
529
+ */
530
+ function findNodes(values, canonicalKeyId, gateway) {
531
+ if (!Array.isArray(values)) return [];
532
+ const nodes = [];
533
+ for (const value of values) {
534
+ if (value == null || typeof value !== "object" || Array.isArray(value)) continue;
535
+ const node = value;
536
+ const nodeId = node["@id"];
537
+ if (typeof nodeId !== "string") continue;
538
+ if (getRawCanonicalPortableId(nodeId) !== canonicalKeyId) continue;
539
+ const parsed = parseIri(nodeId);
540
+ if (isPortableUri(parsed) || parsed.origin === gateway) nodes.push(node);
541
+ }
542
+ return nodes;
543
+ }
544
+ /** A node with nothing but an `@id` is a reference, not an embedded key. */
545
+ function isReference(node) {
546
+ return Object.keys(node).every((key) => key === "@id");
547
+ }
548
+ //#endregion
549
+ export { resolvePortableActorKey as n, verifyPortableGatewayKeyDocument as r, fetchPortableGatewayKey as t };