@fedify/fedify 0.10.0-dev.190 → 0.10.0-dev.195

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. package/CHANGES.md +41 -1
  2. package/esm/deps/jsr.io/@std/encoding/0.224.3/base64url.js +83 -0
  3. package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/mod.js +1 -1
  4. package/esm/federation/handler.js +1 -1
  5. package/esm/federation/middleware.js +435 -347
  6. package/esm/nodeinfo/types.js +1 -1
  7. package/esm/runtime/key.js +82 -0
  8. package/esm/testing/fixtures/w3id.org/security/multikey/v1 +35 -0
  9. package/esm/testing/fixtures/www.w3.org/ns/did/v1 +58 -0
  10. package/esm/vocab/application.yaml +15 -1
  11. package/esm/vocab/group.yaml +15 -1
  12. package/esm/vocab/multikey.yaml +33 -0
  13. package/esm/vocab/organization.yaml +15 -1
  14. package/esm/vocab/person.yaml +15 -1
  15. package/esm/vocab/service.yaml +15 -1
  16. package/esm/vocab/vocab.js +986 -13
  17. package/package.json +4 -1
  18. package/types/codegen/type.d.ts.map +1 -1
  19. package/types/deps/jsr.io/@std/async/{0.224.1 → 0.224.2}/delay.d.ts.map +1 -1
  20. package/types/deps/jsr.io/@std/encoding/0.224.3/base64url.d.ts +38 -0
  21. package/types/deps/jsr.io/@std/encoding/0.224.3/base64url.d.ts.map +1 -0
  22. package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/common.d.ts.map +1 -1
  23. package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/encoding.d.ts.map +1 -1
  24. package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/language.d.ts.map +1 -1
  25. package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/media_type.d.ts.map +1 -1
  26. package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/negotiation.d.ts.map +1 -1
  27. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/assert_path.d.ts.map +1 -0
  28. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/basename.d.ts.map +1 -0
  29. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/constants.d.ts.map +1 -0
  30. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/dirname.d.ts.map +1 -0
  31. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/normalize.d.ts.map +1 -0
  32. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/normalize_string.d.ts.map +1 -0
  33. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/strip_trailing_separators.d.ts.map +1 -0
  34. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/_util.d.ts.map +1 -0
  35. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/basename.d.ts.map +1 -0
  36. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/dirname.d.ts.map +1 -0
  37. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/extname.d.ts.map +1 -0
  38. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/join.d.ts.map +1 -0
  39. package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/normalize.d.ts.map +1 -0
  40. package/types/deps/jsr.io/@std/semver/0.224.3/_constants.d.ts.map +1 -0
  41. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_shared.d.ts.map +1 -1
  42. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_test_comparator_set.d.ts.map +1 -1
  43. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/can_parse.d.ts.map +1 -1
  44. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/compare.d.ts.map +1 -1
  45. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/constants.d.ts.map +1 -1
  46. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/difference.d.ts.map +1 -1
  47. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/equals.d.ts.map +1 -1
  48. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format.d.ts.map +1 -1
  49. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format_range.d.ts.map +1 -1
  50. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_or_equal.d.ts.map +1 -1
  51. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than.d.ts.map +1 -1
  52. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than_range.d.ts.map +1 -1
  53. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/increment.d.ts.map +1 -1
  54. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_range.d.ts.map +1 -1
  55. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_semver.d.ts.map +1 -1
  56. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_or_equal.d.ts.map +1 -1
  57. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than.d.ts.map +1 -1
  58. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than_range.d.ts.map +1 -1
  59. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/max_satisfying.d.ts.map +1 -1
  60. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/min_satisfying.d.ts.map +1 -1
  61. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/mod.d.ts +1 -1
  62. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/mod.d.ts.map +1 -1
  63. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/not_equals.d.ts.map +1 -1
  64. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse.d.ts.map +1 -1
  65. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse_range.d.ts.map +1 -1
  66. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_intersects.d.ts.map +1 -1
  67. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_max.d.ts.map +1 -1
  68. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_min.d.ts.map +1 -1
  69. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/satisfies.d.ts.map +1 -1
  70. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/test_range.d.ts.map +1 -1
  71. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse.d.ts.map +1 -1
  72. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse_range.d.ts.map +1 -1
  73. package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/types.d.ts.map +1 -1
  74. package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/_util.d.ts.map +1 -1
  75. package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/case.d.ts.map +1 -1
  76. package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/closest_string.d.ts.map +1 -1
  77. package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/compare_similarity.d.ts.map +1 -1
  78. package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/levenshtein_distance.d.ts.map +1 -1
  79. package/types/deps/jsr.io/@std/text/0.224.3/mod.d.ts.map +1 -0
  80. package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/word_similarity_sort.d.ts.map +1 -1
  81. package/types/deps/jsr.io/@std/url/{0.224.0 → 0.224.1}/_strip.d.ts.map +1 -1
  82. package/types/deps/jsr.io/@std/url/0.224.1/basename.d.ts.map +1 -0
  83. package/types/deps/jsr.io/@std/url/0.224.1/dirname.d.ts.map +1 -0
  84. package/types/deps/jsr.io/@std/url/0.224.1/extname.d.ts.map +1 -0
  85. package/types/deps/jsr.io/@std/url/0.224.1/join.d.ts.map +1 -0
  86. package/types/deps/jsr.io/@std/url/0.224.1/mod.d.ts.map +1 -0
  87. package/types/deps/jsr.io/@std/url/0.224.1/normalize.d.ts.map +1 -0
  88. package/types/federation/context.d.ts +6 -1
  89. package/types/federation/context.d.ts.map +1 -1
  90. package/types/federation/middleware.d.ts.map +1 -1
  91. package/types/nodeinfo/types.d.ts +1 -1
  92. package/types/runtime/key.d.ts +20 -0
  93. package/types/runtime/key.d.ts.map +1 -1
  94. package/types/vocab/vocab.d.ts +249 -0
  95. package/types/vocab/vocab.d.ts.map +1 -1
  96. package/types/deps/jsr.io/@std/assert/0.226.0/assert.d.ts.map +0 -1
  97. package/types/deps/jsr.io/@std/assert/0.226.0/assertion_error.d.ts.map +0 -1
  98. package/types/deps/jsr.io/@std/semver/0.224.2/_constants.d.ts.map +0 -1
  99. package/types/deps/jsr.io/@std/text/0.224.2/mod.d.ts.map +0 -1
  100. package/types/deps/jsr.io/@std/url/0.224.0/basename.d.ts.map +0 -1
  101. package/types/deps/jsr.io/@std/url/0.224.0/dirname.d.ts.map +0 -1
  102. package/types/deps/jsr.io/@std/url/0.224.0/extname.d.ts.map +0 -1
  103. package/types/deps/jsr.io/@std/url/0.224.0/join.d.ts.map +0 -1
  104. package/types/deps/jsr.io/@std/url/0.224.0/mod.d.ts.map +0 -1
  105. package/types/deps/jsr.io/@std/url/0.224.0/normalize.d.ts.map +0 -1
  106. /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/common.js +0 -0
  107. /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/encoding.js +0 -0
  108. /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/language.js +0 -0
  109. /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/media_type.js +0 -0
  110. /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/negotiation.js +0 -0
  111. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_constants.js +0 -0
  112. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_shared.js +0 -0
  113. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_test_comparator_set.js +0 -0
  114. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/can_parse.js +0 -0
  115. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/compare.js +0 -0
  116. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/constants.js +0 -0
  117. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/difference.js +0 -0
  118. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/equals.js +0 -0
  119. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format.js +0 -0
  120. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format_range.js +0 -0
  121. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_or_equal.js +0 -0
  122. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than.js +0 -0
  123. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than_range.js +0 -0
  124. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/increment.js +0 -0
  125. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_range.js +0 -0
  126. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_semver.js +0 -0
  127. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_or_equal.js +0 -0
  128. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than.js +0 -0
  129. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than_range.js +0 -0
  130. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/max_satisfying.js +0 -0
  131. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/min_satisfying.js +0 -0
  132. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/not_equals.js +0 -0
  133. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse.js +0 -0
  134. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse_range.js +0 -0
  135. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_intersects.js +0 -0
  136. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_max.js +0 -0
  137. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_min.js +0 -0
  138. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/satisfies.js +0 -0
  139. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/test_range.js +0 -0
  140. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse.js +0 -0
  141. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse_range.js +0 -0
  142. /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/types.js +0 -0
  143. /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/common.d.ts +0 -0
  144. /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/encoding.d.ts +0 -0
  145. /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/language.d.ts +0 -0
  146. /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/media_type.d.ts +0 -0
  147. /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/negotiation.d.ts +0 -0
  148. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_constants.d.ts +0 -0
  149. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_shared.d.ts +0 -0
  150. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_test_comparator_set.d.ts +0 -0
  151. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/can_parse.d.ts +0 -0
  152. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/compare.d.ts +0 -0
  153. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/constants.d.ts +0 -0
  154. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/difference.d.ts +0 -0
  155. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/equals.d.ts +0 -0
  156. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format.d.ts +0 -0
  157. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format_range.d.ts +0 -0
  158. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_or_equal.d.ts +0 -0
  159. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than.d.ts +0 -0
  160. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than_range.d.ts +0 -0
  161. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/increment.d.ts +0 -0
  162. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_range.d.ts +0 -0
  163. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_semver.d.ts +0 -0
  164. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_or_equal.d.ts +0 -0
  165. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than.d.ts +0 -0
  166. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than_range.d.ts +0 -0
  167. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/max_satisfying.d.ts +0 -0
  168. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/min_satisfying.d.ts +0 -0
  169. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/not_equals.d.ts +0 -0
  170. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse.d.ts +0 -0
  171. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse_range.d.ts +0 -0
  172. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_intersects.d.ts +0 -0
  173. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_max.d.ts +0 -0
  174. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_min.d.ts +0 -0
  175. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/satisfies.d.ts +0 -0
  176. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/test_range.d.ts +0 -0
  177. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse.d.ts +0 -0
  178. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse_range.d.ts +0 -0
  179. /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/types.d.ts +0 -0
@@ -5,7 +5,7 @@ import { exportJwk, importJwk, validateCryptoKey } from "../sig/key.js";
5
5
  import { getKeyOwner } from "../sig/owner.js";
6
6
  import { handleNodeInfo, handleNodeInfoJrd } from "../nodeinfo/handler.js";
7
7
  import { fetchDocumentLoader, getAuthenticatedDocumentLoader, kvCache, } from "../runtime/docloader.js";
8
- import { Activity, CryptographicKey } from "../vocab/mod.js";
8
+ import { Activity, CryptographicKey, Multikey, } from "../vocab/mod.js";
9
9
  import { handleWebFinger } from "../webfinger/handler.js";
10
10
  import { buildCollectionSynchronizationHeader } from "./collection.js";
11
11
  import { handleActor, handleCollection, handleInbox, handleObject, } from "./handler.js";
@@ -145,33 +145,12 @@ export class Federation {
145
145
  }
146
146
  logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData, activityId: activity?.id?.href });
147
147
  }
148
- async #getKeyPairsFromHandle(url, contextData, handle) {
149
- const logger = getLogger(["fedify", "federation", "actor"]);
150
- if (this.#actorCallbacks?.keyPairsDispatcher == null) {
151
- throw new Error("No actor key pairs dispatcher registered.");
152
- }
153
- const path = this.#router.build("actor", { handle });
154
- if (path == null) {
155
- logger.warn("No actor dispatcher registered.");
156
- return [];
157
- }
158
- const actorUri = new URL(path, url);
159
- const keyPairs = await this.#actorCallbacks?.keyPairsDispatcher(contextData, handle);
160
- if (keyPairs.length < 1) {
161
- logger.warn("No key pairs found for actor {handle}.", { handle });
162
- }
163
- let i = 0;
164
- const result = [];
165
- for (const keyPair of keyPairs) {
166
- result.push({
167
- ...keyPair,
168
- keyId: new URL(i == 0 ? `#main-key` : `#key-${i + 1}`, actorUri),
169
- });
170
- i++;
171
- }
172
- return result;
173
- }
174
148
  createContext(urlOrRequest, contextData) {
149
+ return urlOrRequest instanceof Request
150
+ ? this.#createContext(urlOrRequest, contextData)
151
+ : this.#createContext(urlOrRequest, contextData);
152
+ }
153
+ #createContext(urlOrRequest, contextData, opts = {}) {
175
154
  const request = urlOrRequest instanceof Request ? urlOrRequest : null;
176
155
  const url = urlOrRequest instanceof URL
177
156
  ? new URL(urlOrRequest)
@@ -183,304 +162,28 @@ export class Federation {
183
162
  }
184
163
  if (this.#treatHttps)
185
164
  url.protocol = "https:";
186
- const getRsaKeyPairFromHandle = async (handle) => {
187
- const keyPairs = await this.#getKeyPairsFromHandle(url, contextData, handle);
188
- for (const keyPair of keyPairs) {
189
- const { privateKey } = keyPair;
190
- if (privateKey.algorithm.name === "RSASSA-PKCS1-v1_5" &&
191
- privateKey.algorithm.hash
192
- .name ===
193
- "SHA-256") {
194
- return keyPair;
195
- }
196
- }
197
- getLogger(["fedify", "federation", "actor"]).warn("No RSA-PKCS#1-v1.5 SHA-256 key found for actor {handle}.", { handle });
198
- return null;
199
- };
200
- const getAuthenticatedDocumentLoader = this.#authenticatedDocumentLoaderFactory;
201
- const documentLoader = this.#documentLoader;
202
- function getDocumentLoader(identity) {
203
- if ("handle" in identity) {
204
- const keyPair = getRsaKeyPairFromHandle(identity.handle);
205
- if (keyPair == null)
206
- return documentLoader;
207
- return keyPair.then((pair) => pair == null ? documentLoader : getAuthenticatedDocumentLoader(pair));
208
- }
209
- return getAuthenticatedDocumentLoader(identity);
210
- }
211
- const context = {
165
+ const ctxOptions = {
166
+ url,
167
+ federation: this,
168
+ router: this.#router,
169
+ objectTypeIds: this.#objectTypeIds,
170
+ objectCallbacks: this.#objectCallbacks,
171
+ actorCallbacks: this.#actorCallbacks,
212
172
  data: contextData,
213
- documentLoader: this.#documentLoader,
173
+ documentLoader: opts.documentLoader ?? this.#documentLoader,
214
174
  contextLoader: this.#contextLoader,
215
- getNodeInfoUri: () => {
216
- const path = this.#router.build("nodeInfo", {});
217
- if (path == null) {
218
- throw new RouterError("No NodeInfo dispatcher registered.");
219
- }
220
- return new URL(path, url);
221
- },
222
- getActorUri: (handle) => {
223
- const path = this.#router.build("actor", { handle });
224
- if (path == null) {
225
- throw new RouterError("No actor dispatcher registered.");
226
- }
227
- return new URL(path, url);
228
- },
229
- getObjectUri: (
230
- // deno-lint-ignore no-explicit-any
231
- cls, values) => {
232
- const callbacks = this.#objectCallbacks[cls.typeId.href];
233
- if (callbacks == null) {
234
- throw new RouterError("No object dispatcher registered.");
235
- }
236
- for (const param of callbacks.parameters) {
237
- if (!(param in values)) {
238
- throw new TypeError(`Missing parameter: ${param}`);
239
- }
240
- }
241
- const path = this.#router.build(`object:${cls.typeId.href}`, values);
242
- if (path == null) {
243
- throw new RouterError("No object dispatcher registered.");
244
- }
245
- return new URL(path, url);
246
- },
247
- getOutboxUri: (handle) => {
248
- const path = this.#router.build("outbox", { handle });
249
- if (path == null) {
250
- throw new RouterError("No outbox dispatcher registered.");
251
- }
252
- return new URL(path, url);
253
- },
254
- getInboxUri: (handle) => {
255
- if (handle == null) {
256
- const path = this.#router.build("sharedInbox", {});
257
- if (path == null) {
258
- throw new RouterError("No shared inbox path registered.");
259
- }
260
- return new URL(path, url);
261
- }
262
- const path = this.#router.build("inbox", { handle });
263
- if (path == null) {
264
- throw new RouterError("No inbox path registered.");
265
- }
266
- return new URL(path, url);
267
- },
268
- getFollowingUri: (handle) => {
269
- const path = this.#router.build("following", { handle });
270
- if (path == null) {
271
- throw new RouterError("No following collection path registered.");
272
- }
273
- return new URL(path, url);
274
- },
275
- getFollowersUri: (handle) => {
276
- const path = this.#router.build("followers", { handle });
277
- if (path == null) {
278
- throw new RouterError("No followers collection path registered.");
279
- }
280
- return new URL(path, url);
281
- },
282
- parseUri: (uri) => {
283
- if (uri.origin !== url.origin)
284
- return null;
285
- const route = this.#router.route(uri.pathname);
286
- if (route == null)
287
- return null;
288
- else if (route.name === "actor") {
289
- return { type: "actor", handle: route.values.handle };
290
- }
291
- else if (route.name.startsWith("object:")) {
292
- const typeId = route.name.replace(/^object:/, "");
293
- return {
294
- type: "object",
295
- class: this.#objectTypeIds[typeId],
296
- typeId: new URL(typeId),
297
- values: route.values,
298
- };
299
- }
300
- else if (route.name === "inbox") {
301
- return { type: "inbox", handle: route.values.handle };
302
- }
303
- else if (route.name === "sharedInbox") {
304
- return { type: "inbox" };
305
- }
306
- else if (route.name === "outbox") {
307
- return { type: "outbox", handle: route.values.handle };
308
- }
309
- else if (route.name === "following") {
310
- return { type: "following", handle: route.values.handle };
311
- }
312
- else if (route.name === "followers") {
313
- return { type: "followers", handle: route.values.handle };
314
- }
315
- return null;
316
- },
317
- getHandleFromActorUri(actorUri) {
318
- getLogger(["fedify", "federation"]).warn("Context.getHandleFromActorUri() is deprecated; " +
319
- "use Context.parseUri() instead.");
320
- const result = this.parseUri(actorUri);
321
- if (result?.type === "actor")
322
- return result.handle;
323
- return null;
324
- },
325
- getActorKeyPairs: async (handle) => {
326
- let keyPairs;
327
- try {
328
- keyPairs = await this.#getKeyPairsFromHandle(url, contextData, handle);
329
- }
330
- catch (_) {
331
- getLogger(["fedify", "federation", "actor"])
332
- .warn("No actor key pairs dispatcher registered.");
333
- return [];
334
- }
335
- const owner = context.getActorUri(handle);
336
- const result = [];
337
- for (const keyPair of keyPairs) {
338
- const newPair = {
339
- ...keyPair,
340
- cryptographicKey: new CryptographicKey({
341
- id: keyPair.keyId,
342
- owner,
343
- publicKey: keyPair.publicKey,
344
- }),
345
- };
346
- result.push(newPair);
347
- }
348
- return result;
349
- },
350
- getActorKey: async (handle) => {
351
- getLogger(["fedify", "federation", "actor"]).warn("Context.getActorKey() method is deprecated; " +
352
- "use Context.getActorKeyPairs() method instead.");
353
- let keyPair;
354
- try {
355
- keyPair = await getRsaKeyPairFromHandle(handle);
356
- }
357
- catch (_) {
358
- return null;
359
- }
360
- if (keyPair == null)
361
- return null;
362
- return new CryptographicKey({
363
- id: keyPair.keyId,
364
- owner: context.getActorUri(handle),
365
- publicKey: keyPair.publicKey,
366
- });
367
- },
368
- getDocumentLoader,
369
- sendActivity: async (sender, recipients, activity, options = {}) => {
370
- let senderPair;
371
- if ("handle" in sender) {
372
- const keyPair = await getRsaKeyPairFromHandle(sender.handle);
373
- if (keyPair == null) {
374
- throw new Error(`No key pair found for actor ${sender.handle}`);
375
- }
376
- senderPair = keyPair;
377
- }
378
- else {
379
- senderPair = sender;
380
- }
381
- const opts = { ...options };
382
- let expandedRecipients;
383
- if (Array.isArray(recipients)) {
384
- expandedRecipients = recipients;
385
- }
386
- else if (recipients === "followers") {
387
- if (!("handle" in sender)) {
388
- throw new Error("If recipients is 'followers', sender must be an actor handle.");
389
- }
390
- expandedRecipients = [];
391
- for await (const recipient of this.#getFollowers(reqCtx, sender.handle)) {
392
- expandedRecipients.push(recipient);
393
- }
394
- const collectionId = this.#router.build("followers", sender);
395
- opts.collectionSync = collectionId == null
396
- ? undefined
397
- : new URL(collectionId, url).href;
398
- }
399
- else {
400
- expandedRecipients = [recipients];
401
- }
402
- return await this.sendActivity(senderPair, expandedRecipients, activity, opts);
403
- },
175
+ authenticatedDocumentLoaderFactory: this.#authenticatedDocumentLoaderFactory,
404
176
  };
405
177
  if (request == null)
406
- return context;
407
- let signedKey = undefined;
408
- let signedKeyOwner = undefined;
409
- const timeWindow = this.#signatureTimeWindow;
410
- const reqCtx = {
411
- ...context,
178
+ return new ContextImpl(ctxOptions);
179
+ return new RequestContextImpl({
180
+ ...ctxOptions,
412
181
  request,
413
- url,
414
- getActor: async (handle) => {
415
- if (this.#actorCallbacks == null ||
416
- this.#actorCallbacks.dispatcher == null) {
417
- throw new Error("No actor dispatcher registered.");
418
- }
419
- let rsaKey;
420
- try {
421
- rsaKey = await getRsaKeyPairFromHandle(handle);
422
- }
423
- catch (_) {
424
- rsaKey = null;
425
- }
426
- return await this.#actorCallbacks.dispatcher({
427
- ...reqCtx,
428
- getActor(handle2) {
429
- getLogger(["fedify", "federation"]).warn("RequestContext.getActor({getActorHandle}) is invoked from " +
430
- "the actor dispatcher ({actorDispatcherHandle}); " +
431
- "this may cause an infinite loop.", { getActorHandle: handle2, actorDispatcherHandle: handle });
432
- return reqCtx.getActor(handle2);
433
- },
434
- }, handle, rsaKey == null ? null : new CryptographicKey({
435
- id: rsaKey.keyId,
436
- owner: context.getActorUri(handle),
437
- publicKey: rsaKey.publicKey,
438
- }));
439
- },
440
- getObject: async (cls, values) => {
441
- const callbacks = this.#objectCallbacks[cls.typeId.href];
442
- if (callbacks == null) {
443
- throw new Error("No object dispatcher registered.");
444
- }
445
- for (const param of callbacks.parameters) {
446
- if (!(param in values)) {
447
- throw new TypeError(`Missing parameter: ${param}`);
448
- }
449
- }
450
- return await callbacks.dispatcher({
451
- ...reqCtx,
452
- getObject(cls2, values2) {
453
- getLogger(["fedify", "federation"]).warn("RequestContext.getObject({getObjectClass}, " +
454
- "{getObjectValues}) is invoked from the object dispatcher " +
455
- "({actorDispatcherClass}, {actorDispatcherValues}); " +
456
- "this may cause an infinite loop.", {
457
- getObjectClass: cls2.name,
458
- getObjectValues: values2,
459
- actorDispatcherClass: cls.name,
460
- actorDispatcherValues: values,
461
- });
462
- return reqCtx.getObject(cls2, values2);
463
- },
464
- }, values);
465
- },
466
- async getSignedKey() {
467
- if (signedKey !== undefined)
468
- return signedKey;
469
- return signedKey = await verifyRequest(request, {
470
- ...context,
471
- timeWindow,
472
- });
473
- },
474
- async getSignedKeyOwner() {
475
- if (signedKeyOwner !== undefined)
476
- return signedKeyOwner;
477
- const key = await this.getSignedKey();
478
- if (key == null)
479
- return signedKeyOwner = null;
480
- return signedKeyOwner = await getKeyOwner(key, context);
481
- },
482
- };
483
- return reqCtx;
182
+ signatureTimeWindow: this.#signatureTimeWindow,
183
+ followersCallbacks: this.#followersCallbacks,
184
+ invokedFromActorDispatcher: opts.invokedFromActorDispatcher,
185
+ invokedFromObjectDispatcher: opts.invokedFromObjectDispatcher,
186
+ });
484
187
  }
485
188
  /**
486
189
  * Registers a NodeInfo dispatcher.
@@ -604,6 +307,18 @@ export class Federation {
604
307
  "URI. Set the property with Context.getInboxUri().");
605
308
  }
606
309
  }
310
+ if (callbacks.keyPairsDispatcher != null) {
311
+ if (actor.publicKeyId == null) {
312
+ logger.warn("You configured a key pairs dispatcher, but the actor does " +
313
+ "not have a publicKey property. Set the property with " +
314
+ "Context.getActorKeyPairs(handle).");
315
+ }
316
+ if (actor.assertionMethodId == null) {
317
+ logger.warn("You configured a key pairs dispatcher, but the actor does " +
318
+ "not have an assertionMethod property. Set the property " +
319
+ "with Context.getActorKeyPairs(handle).");
320
+ }
321
+ }
607
322
  return actor;
608
323
  },
609
324
  };
@@ -798,29 +513,6 @@ export class Federation {
798
513
  };
799
514
  return setters;
800
515
  }
801
- async *#getFollowers(context, handle) {
802
- if (this.#followersCallbacks == null) {
803
- throw new Error("No followers collection dispatcher registered.");
804
- }
805
- const result = await this.#followersCallbacks.dispatcher(context, handle, null);
806
- if (result != null) {
807
- for (const recipient of result.items)
808
- yield recipient;
809
- return;
810
- }
811
- if (this.#followersCallbacks.firstCursor == null) {
812
- throw new Error("No first cursor dispatcher registered for followers collection.");
813
- }
814
- let cursor = await this.#followersCallbacks.firstCursor(context, handle);
815
- while (cursor != null) {
816
- const result = await this.#followersCallbacks.dispatcher(context, handle, cursor);
817
- if (result == null)
818
- break;
819
- for (const recipient of result.items)
820
- yield recipient;
821
- cursor = result.nextCursor ?? null;
822
- }
823
- }
824
516
  /**
825
517
  * Assigns the URL path for the inbox and starts setting inbox listeners.
826
518
  *
@@ -1016,7 +708,7 @@ export class Federation {
1016
708
  const response = onNotFound(request);
1017
709
  return response instanceof Promise ? await response : response;
1018
710
  }
1019
- let context = this.createContext(request, contextData);
711
+ let context = this.#createContext(request, contextData);
1020
712
  switch (route.name.replace(/:.*$/, "")) {
1021
713
  case "webfinger":
1022
714
  return await handleWebFinger(request, {
@@ -1032,6 +724,9 @@ export class Federation {
1032
724
  nodeInfoDispatcher: this.#nodeInfoDispatcher,
1033
725
  });
1034
726
  case "actor":
727
+ context = this.#createContext(request, contextData, {
728
+ invokedFromActorDispatcher: { handle: route.values.handle },
729
+ });
1035
730
  return await handleActor(request, {
1036
731
  handle: route.values.handle,
1037
732
  context,
@@ -1044,6 +739,10 @@ export class Federation {
1044
739
  case "object": {
1045
740
  const typeId = route.name.replace(/^object:/, "");
1046
741
  const callbacks = this.#objectCallbacks[typeId];
742
+ const cls = this.#objectTypeIds[typeId];
743
+ context = this.#createContext(request, contextData, {
744
+ invokedFromObjectDispatcher: { cls, values: route.values },
745
+ });
1047
746
  return await handleObject(request, {
1048
747
  values: route.values,
1049
748
  context,
@@ -1065,12 +764,11 @@ export class Federation {
1065
764
  onNotAcceptable,
1066
765
  });
1067
766
  case "inbox":
1068
- context = {
1069
- ...context,
767
+ context = this.#createContext(request, contextData, {
1070
768
  documentLoader: await context.getDocumentLoader({
1071
769
  handle: route.values.handle,
1072
770
  }),
1073
- };
771
+ });
1074
772
  // falls through
1075
773
  case "sharedInbox":
1076
774
  return await handleInbox(request, {
@@ -1121,6 +819,396 @@ export class Federation {
1121
819
  }
1122
820
  }
1123
821
  }
822
+ class ContextImpl {
823
+ #url;
824
+ #federation;
825
+ #router;
826
+ #objectTypeIds;
827
+ objectCallbacks;
828
+ actorCallbacks;
829
+ data;
830
+ documentLoader;
831
+ contextLoader;
832
+ #authenticatedDocumentLoaderFactory;
833
+ constructor({ url, federation, router, objectTypeIds, objectCallbacks, actorCallbacks, data, documentLoader, contextLoader, authenticatedDocumentLoaderFactory, }) {
834
+ this.#url = url;
835
+ this.#federation = federation;
836
+ this.#router = router;
837
+ this.#objectTypeIds = objectTypeIds;
838
+ this.objectCallbacks = objectCallbacks;
839
+ this.actorCallbacks = actorCallbacks;
840
+ this.data = data;
841
+ this.documentLoader = documentLoader;
842
+ this.contextLoader = contextLoader;
843
+ this.#authenticatedDocumentLoaderFactory =
844
+ authenticatedDocumentLoaderFactory;
845
+ }
846
+ getNodeInfoUri() {
847
+ const path = this.#router.build("nodeInfo", {});
848
+ if (path == null) {
849
+ throw new RouterError("No NodeInfo dispatcher registered.");
850
+ }
851
+ return new URL(path, this.#url);
852
+ }
853
+ getActorUri(handle) {
854
+ const path = this.#router.build("actor", { handle });
855
+ if (path == null) {
856
+ throw new RouterError("No actor dispatcher registered.");
857
+ }
858
+ return new URL(path, this.#url);
859
+ }
860
+ getObjectUri(
861
+ // deno-lint-ignore no-explicit-any
862
+ cls, values) {
863
+ const callbacks = this.objectCallbacks[cls.typeId.href];
864
+ if (callbacks == null) {
865
+ throw new RouterError("No object dispatcher registered.");
866
+ }
867
+ for (const param of callbacks.parameters) {
868
+ if (!(param in values)) {
869
+ throw new TypeError(`Missing parameter: ${param}`);
870
+ }
871
+ }
872
+ const path = this.#router.build(`object:${cls.typeId.href}`, values);
873
+ if (path == null) {
874
+ throw new RouterError("No object dispatcher registered.");
875
+ }
876
+ return new URL(path, this.#url);
877
+ }
878
+ getOutboxUri(handle) {
879
+ const path = this.#router.build("outbox", { handle });
880
+ if (path == null) {
881
+ throw new RouterError("No outbox dispatcher registered.");
882
+ }
883
+ return new URL(path, this.#url);
884
+ }
885
+ getInboxUri(handle) {
886
+ if (handle == null) {
887
+ const path = this.#router.build("sharedInbox", {});
888
+ if (path == null) {
889
+ throw new RouterError("No shared inbox path registered.");
890
+ }
891
+ return new URL(path, this.#url);
892
+ }
893
+ const path = this.#router.build("inbox", { handle });
894
+ if (path == null) {
895
+ throw new RouterError("No inbox path registered.");
896
+ }
897
+ return new URL(path, this.#url);
898
+ }
899
+ getFollowingUri(handle) {
900
+ const path = this.#router.build("following", { handle });
901
+ if (path == null) {
902
+ throw new RouterError("No following collection path registered.");
903
+ }
904
+ return new URL(path, this.#url);
905
+ }
906
+ getFollowersUri(handle) {
907
+ const path = this.#router.build("followers", { handle });
908
+ if (path == null) {
909
+ throw new RouterError("No followers collection path registered.");
910
+ }
911
+ return new URL(path, this.#url);
912
+ }
913
+ parseUri(uri) {
914
+ if (uri.origin !== this.#url.origin)
915
+ return null;
916
+ const route = this.#router.route(uri.pathname);
917
+ if (route == null)
918
+ return null;
919
+ else if (route.name === "actor") {
920
+ return { type: "actor", handle: route.values.handle };
921
+ }
922
+ else if (route.name.startsWith("object:")) {
923
+ const typeId = route.name.replace(/^object:/, "");
924
+ return {
925
+ type: "object",
926
+ class: this.#objectTypeIds[typeId],
927
+ typeId: new URL(typeId),
928
+ values: route.values,
929
+ };
930
+ }
931
+ else if (route.name === "inbox") {
932
+ return { type: "inbox", handle: route.values.handle };
933
+ }
934
+ else if (route.name === "sharedInbox") {
935
+ return { type: "inbox" };
936
+ }
937
+ else if (route.name === "outbox") {
938
+ return { type: "outbox", handle: route.values.handle };
939
+ }
940
+ else if (route.name === "following") {
941
+ return { type: "following", handle: route.values.handle };
942
+ }
943
+ else if (route.name === "followers") {
944
+ return { type: "followers", handle: route.values.handle };
945
+ }
946
+ return null;
947
+ }
948
+ getHandleFromActorUri(actorUri) {
949
+ getLogger(["fedify", "federation"]).warn("Context.getHandleFromActorUri() is deprecated; " +
950
+ "use Context.parseUri() instead.");
951
+ const result = this.parseUri(actorUri);
952
+ if (result?.type === "actor")
953
+ return result.handle;
954
+ return null;
955
+ }
956
+ async getActorKeyPairs(handle) {
957
+ let keyPairs;
958
+ try {
959
+ keyPairs = await this.getKeyPairsFromHandle(this.#url, this.data, handle);
960
+ }
961
+ catch (_) {
962
+ getLogger(["fedify", "federation", "actor"])
963
+ .warn("No actor key pairs dispatcher registered.");
964
+ return [];
965
+ }
966
+ const owner = this.getActorUri(handle);
967
+ const result = [];
968
+ for (const keyPair of keyPairs) {
969
+ const newPair = {
970
+ ...keyPair,
971
+ cryptographicKey: new CryptographicKey({
972
+ id: keyPair.keyId,
973
+ owner,
974
+ publicKey: keyPair.publicKey,
975
+ }),
976
+ multikey: new Multikey({
977
+ id: keyPair.keyId,
978
+ controller: owner,
979
+ publicKey: keyPair.publicKey,
980
+ }),
981
+ };
982
+ result.push(newPair);
983
+ }
984
+ return result;
985
+ }
986
+ async getKeyPairsFromHandle(url, contextData, handle) {
987
+ const logger = getLogger(["fedify", "federation", "actor"]);
988
+ if (this.actorCallbacks?.keyPairsDispatcher == null) {
989
+ throw new Error("No actor key pairs dispatcher registered.");
990
+ }
991
+ const path = this.#router.build("actor", { handle });
992
+ if (path == null) {
993
+ logger.warn("No actor dispatcher registered.");
994
+ return [];
995
+ }
996
+ const actorUri = new URL(path, url);
997
+ const keyPairs = await this.actorCallbacks?.keyPairsDispatcher(contextData, handle);
998
+ if (keyPairs.length < 1) {
999
+ logger.warn("No key pairs found for actor {handle}.", { handle });
1000
+ }
1001
+ let i = 0;
1002
+ const result = [];
1003
+ for (const keyPair of keyPairs) {
1004
+ result.push({
1005
+ ...keyPair,
1006
+ keyId: new URL(
1007
+ // For backwards compatibility, the first key is always the #main-key:
1008
+ i == 0 ? `#main-key` : `#key-${i + 1}`, actorUri),
1009
+ });
1010
+ i++;
1011
+ }
1012
+ return result;
1013
+ }
1014
+ async getActorKey(handle) {
1015
+ getLogger(["fedify", "federation", "actor"]).warn("Context.getActorKey() method is deprecated; " +
1016
+ "use Context.getActorKeyPairs() method instead.");
1017
+ let keyPair;
1018
+ try {
1019
+ keyPair = await this.getRsaKeyPairFromHandle(handle);
1020
+ }
1021
+ catch (_) {
1022
+ return null;
1023
+ }
1024
+ if (keyPair == null)
1025
+ return null;
1026
+ return new CryptographicKey({
1027
+ id: keyPair.keyId,
1028
+ owner: this.getActorUri(handle),
1029
+ publicKey: keyPair.publicKey,
1030
+ });
1031
+ }
1032
+ async getRsaKeyPairFromHandle(handle) {
1033
+ const keyPairs = await this.getKeyPairsFromHandle(this.#url, this.data, handle);
1034
+ for (const keyPair of keyPairs) {
1035
+ const { privateKey } = keyPair;
1036
+ if (privateKey.algorithm.name === "RSASSA-PKCS1-v1_5" &&
1037
+ privateKey.algorithm.hash
1038
+ .name ===
1039
+ "SHA-256") {
1040
+ return keyPair;
1041
+ }
1042
+ }
1043
+ getLogger(["fedify", "federation", "actor"]).warn("No RSA-PKCS#1-v1.5 SHA-256 key found for actor {handle}.", { handle });
1044
+ return null;
1045
+ }
1046
+ getDocumentLoader(identity) {
1047
+ if ("handle" in identity) {
1048
+ const keyPair = this.getRsaKeyPairFromHandle(identity.handle);
1049
+ return keyPair.then((pair) => pair == null
1050
+ ? this.documentLoader
1051
+ : this.#authenticatedDocumentLoaderFactory(pair));
1052
+ }
1053
+ return this.#authenticatedDocumentLoaderFactory(identity);
1054
+ }
1055
+ async sendActivity(sender, recipients, activity, options = {}) {
1056
+ let senderPair;
1057
+ if ("handle" in sender) {
1058
+ const keyPair = await this.getRsaKeyPairFromHandle(sender.handle);
1059
+ if (keyPair == null) {
1060
+ throw new Error(`No key pair found for actor ${sender.handle}`);
1061
+ }
1062
+ senderPair = keyPair;
1063
+ }
1064
+ else {
1065
+ senderPair = sender;
1066
+ }
1067
+ const opts = { ...options };
1068
+ let expandedRecipients;
1069
+ if (Array.isArray(recipients)) {
1070
+ expandedRecipients = recipients;
1071
+ }
1072
+ else if (recipients === "followers") {
1073
+ if (!("handle" in sender)) {
1074
+ throw new Error("If recipients is 'followers', sender must be an actor handle.");
1075
+ }
1076
+ expandedRecipients = [];
1077
+ for await (const recipient of this.getFollowers(sender.handle)) {
1078
+ expandedRecipients.push(recipient);
1079
+ }
1080
+ const collectionId = this.#router.build("followers", sender);
1081
+ opts.collectionSync = collectionId == null
1082
+ ? undefined
1083
+ : new URL(collectionId, this.#url).href;
1084
+ }
1085
+ else {
1086
+ expandedRecipients = [recipients];
1087
+ }
1088
+ return await this.#federation.sendActivity(senderPair, expandedRecipients, activity, opts);
1089
+ }
1090
+ getFollowers(_handle) {
1091
+ throw new Error('"followers" recipients are not supported in Context. ' +
1092
+ "Use RequestContext instead.");
1093
+ }
1094
+ }
1095
+ class RequestContextImpl extends ContextImpl {
1096
+ #options;
1097
+ #followersCallbacks;
1098
+ #signatureTimeWindow;
1099
+ #invokedFromActorDispatcher;
1100
+ #invokedFromObjectDispatcher;
1101
+ request;
1102
+ url;
1103
+ constructor(options) {
1104
+ super(options);
1105
+ this.#options = options;
1106
+ this.#followersCallbacks = options.followersCallbacks;
1107
+ this.#signatureTimeWindow = options.signatureTimeWindow;
1108
+ this.#invokedFromActorDispatcher = options.invokedFromActorDispatcher;
1109
+ this.#invokedFromObjectDispatcher = options.invokedFromObjectDispatcher;
1110
+ this.request = options.request;
1111
+ this.url = options.url;
1112
+ }
1113
+ async *getFollowers(handle) {
1114
+ if (this.#followersCallbacks == null) {
1115
+ throw new Error("No followers collection dispatcher registered.");
1116
+ }
1117
+ const result = await this.#followersCallbacks.dispatcher(this, handle, null);
1118
+ if (result != null) {
1119
+ for (const recipient of result.items)
1120
+ yield recipient;
1121
+ return;
1122
+ }
1123
+ if (this.#followersCallbacks.firstCursor == null) {
1124
+ throw new Error("No first cursor dispatcher registered for followers collection.");
1125
+ }
1126
+ let cursor = await this.#followersCallbacks.firstCursor(this, handle);
1127
+ while (cursor != null) {
1128
+ const result = await this.#followersCallbacks.dispatcher(this, handle, cursor);
1129
+ if (result == null)
1130
+ break;
1131
+ for (const recipient of result.items)
1132
+ yield recipient;
1133
+ cursor = result.nextCursor ?? null;
1134
+ }
1135
+ }
1136
+ async getActor(handle) {
1137
+ if (this.actorCallbacks == null ||
1138
+ this.actorCallbacks.dispatcher == null) {
1139
+ throw new Error("No actor dispatcher registered.");
1140
+ }
1141
+ if (this.#invokedFromActorDispatcher != null) {
1142
+ getLogger(["fedify", "federation"]).warn("RequestContext.getActor({getActorHandle}) is invoked from " +
1143
+ "the actor dispatcher ({actorDispatcherHandle}); " +
1144
+ "this may cause an infinite loop.", {
1145
+ getActorHandle: handle,
1146
+ actorDispatcherHandle: this.#invokedFromActorDispatcher.handle,
1147
+ });
1148
+ }
1149
+ let rsaKey;
1150
+ try {
1151
+ rsaKey = await this.getRsaKeyPairFromHandle(handle);
1152
+ }
1153
+ catch (_) {
1154
+ rsaKey = null;
1155
+ }
1156
+ return await this.actorCallbacks.dispatcher(new RequestContextImpl({
1157
+ ...this.#options,
1158
+ invokedFromActorDispatcher: { handle },
1159
+ }), handle, rsaKey == null ? null : new CryptographicKey({
1160
+ id: rsaKey.keyId,
1161
+ owner: this.getActorUri(handle),
1162
+ publicKey: rsaKey.publicKey,
1163
+ }));
1164
+ }
1165
+ async getObject(
1166
+ // deno-lint-ignore no-explicit-any
1167
+ cls, values) {
1168
+ const callbacks = this.objectCallbacks[cls.typeId.href];
1169
+ if (callbacks == null) {
1170
+ throw new Error("No object dispatcher registered.");
1171
+ }
1172
+ for (const param of callbacks.parameters) {
1173
+ if (!(param in values)) {
1174
+ throw new TypeError(`Missing parameter: ${param}`);
1175
+ }
1176
+ }
1177
+ if (this.#invokedFromObjectDispatcher != null) {
1178
+ getLogger(["fedify", "federation"]).warn("RequestContext.getObject({getObjectClass}, " +
1179
+ "{getObjectValues}) is invoked from the object dispatcher " +
1180
+ "({actorDispatcherClass}, {actorDispatcherValues}); " +
1181
+ "this may cause an infinite loop.", {
1182
+ getObjectClass: cls.name,
1183
+ getObjectValues: values,
1184
+ actorDispatcherClass: this.#invokedFromObjectDispatcher.cls.name,
1185
+ actorDispatcherValues: this.#invokedFromObjectDispatcher.values,
1186
+ });
1187
+ }
1188
+ return await callbacks.dispatcher(new RequestContextImpl({
1189
+ ...this.#options,
1190
+ invokedFromObjectDispatcher: { cls, values },
1191
+ }), values);
1192
+ }
1193
+ #signedKey = undefined;
1194
+ async getSignedKey() {
1195
+ if (this.#signedKey !== undefined)
1196
+ return this.#signedKey;
1197
+ return this.#signedKey = await verifyRequest(this.request, {
1198
+ ...this,
1199
+ timeWindow: this.#signatureTimeWindow,
1200
+ });
1201
+ }
1202
+ #signedKeyOwner = undefined;
1203
+ async getSignedKeyOwner() {
1204
+ if (this.#signedKeyOwner !== undefined)
1205
+ return this.#signedKeyOwner;
1206
+ const key = await this.getSignedKey();
1207
+ if (key == null)
1208
+ return this.#signedKeyOwner = null;
1209
+ return this.#signedKeyOwner = await getKeyOwner(key, this);
1210
+ }
1211
+ }
1124
1212
  function notFound(_request) {
1125
1213
  return new Response("Not Found", { status: 404 });
1126
1214
  }