@fedify/fedify 2.4.0-dev.1583 → 2.4.0-dev.1618

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 (158) hide show
  1. package/dist/{accept-CgDcxvjV.mjs → accept-8PiolDP7.mjs} +1 -1
  2. package/dist/{activity-listener-BeTGV3wc.mjs → activity-listener-BtTSxZWA.mjs} +1 -1
  3. package/dist/{assert-DvVHoIZ0.mjs → assert-CJ1KXcvv.mjs} +2 -2
  4. package/dist/{assert_equals-PBVKNMJd.mjs → assert_equals-BV7oLMxm.mjs} +1 -1
  5. package/dist/{assert_instance_of-CxoC8bVq.mjs → assert_instance_of-Q9KTBnrf.mjs} +2 -2
  6. package/dist/{assert_not_equals-C8vcTTA3.mjs → assert_not_equals-EPJPoxuf.mjs} +2 -2
  7. package/dist/{assert_rejects-DAdaaIWM.mjs → assert_rejects-EgstcQy5.mjs} +3 -3
  8. package/dist/{assert_strict_equals-BzqyKuYL.mjs → assert_strict_equals-Ec20AUwa.mjs} +2 -2
  9. package/dist/{assert_throws-zN79lgIC.mjs → assert_throws-NiFfqbnH.mjs} +2 -2
  10. package/dist/{builder-Doj1FmsW.mjs → builder-C4a7TAvq.mjs} +26 -4
  11. package/dist/{chunk-DDcVe30Y.cjs → chunk-Czpeue4M.cjs} +0 -1
  12. package/dist/{chunk-CRNNMoPX.js → chunk-D5IdezsZ.js} +1 -1
  13. package/dist/{circuit-breaker-hJBB6jwA.mjs → circuit-breaker-CBGyz5F7.mjs} +1 -1
  14. package/dist/{client-B_A6mfn3.mjs → client-CjF5IYQS.mjs} +1 -1
  15. package/dist/{codec-nfBU4Up4.mjs → codec-BBga-FWI.mjs} +1 -1
  16. package/dist/{collection-Dp5ky45w.mjs → collection-BIhlldqK.mjs} +1 -1
  17. package/dist/compat/mod.cjs +1 -2
  18. package/dist/compat/mod.d.cts +1 -1
  19. package/dist/compat/mod.d.ts +1 -1
  20. package/dist/compat/mod.js +2 -2
  21. package/dist/compat/outgoing-jsonld.test.d.mts +1 -1
  22. package/dist/compat/outgoing-jsonld.test.mjs +4 -4
  23. package/dist/compat/public-audience.test.d.mts +1 -1
  24. package/dist/compat/public-audience.test.mjs +4 -4
  25. package/dist/compat/transformers.test.d.mts +1 -1
  26. package/dist/compat/transformers.test.mjs +6 -6
  27. package/dist/{context-BBRTgkrs.mjs → context-BJXxPElm.mjs} +3 -2
  28. package/dist/{context-GfQA-CqB.d.ts → context-Zii1f5xS.d.ts} +97 -1
  29. package/dist/{context-BLe0NPtZ.d.cts → context-lxX6I7as.d.cts} +97 -1
  30. package/dist/{deno-Bkl9jNVs.mjs → deno-DepnPEgI.mjs} +2 -2
  31. package/dist/{docloader-f1XFaWWh.mjs → docloader-C_IefWRb.mjs} +3 -3
  32. package/dist/{esm-vrlUxr60.mjs → esm-yYQfPhIY.mjs} +1 -1
  33. package/dist/federation/builder.test.d.mts +1 -1
  34. package/dist/federation/builder.test.mjs +16 -6
  35. package/dist/federation/circuit-breaker.test.d.mts +1 -1
  36. package/dist/federation/circuit-breaker.test.mjs +6 -6
  37. package/dist/federation/collection.test.d.mts +1 -1
  38. package/dist/federation/collection.test.mjs +4 -4
  39. package/dist/federation/handler.test.d.mts +1 -1
  40. package/dist/federation/handler.test.mjs +14 -14
  41. package/dist/federation/idempotency.test.d.mts +1 -1
  42. package/dist/federation/idempotency.test.mjs +7 -7
  43. package/dist/federation/inbox.test.d.mts +1 -1
  44. package/dist/federation/inbox.test.mjs +4 -4
  45. package/dist/federation/keycache.test.d.mts +1 -1
  46. package/dist/federation/keycache.test.mjs +6 -6
  47. package/dist/federation/kv.test.d.mts +1 -1
  48. package/dist/federation/kv.test.mjs +4 -4
  49. package/dist/federation/metrics.test.d.mts +1 -1
  50. package/dist/federation/metrics.test.mjs +5 -5
  51. package/dist/federation/middleware.test.d.mts +1 -1
  52. package/dist/federation/middleware.test.mjs +470 -22
  53. package/dist/federation/mod.cjs +4 -3
  54. package/dist/federation/mod.d.cts +3 -3
  55. package/dist/federation/mod.d.ts +3 -3
  56. package/dist/federation/mod.js +2 -2
  57. package/dist/federation/mq.test.d.mts +1 -1
  58. package/dist/federation/mq.test.mjs +6 -6
  59. package/dist/federation/negotiation.test.d.mts +1 -1
  60. package/dist/federation/negotiation.test.mjs +5 -5
  61. package/dist/federation/retry.test.d.mts +1 -1
  62. package/dist/federation/retry.test.mjs +4 -4
  63. package/dist/federation/router.test.d.mts +1 -1
  64. package/dist/federation/router.test.mjs +6 -6
  65. package/dist/federation/send.test.d.mts +1 -1
  66. package/dist/federation/send.test.mjs +12 -12
  67. package/dist/federation/tasks/codec.test.d.mts +1 -1
  68. package/dist/federation/tasks/codec.test.mjs +2 -2
  69. package/dist/federation/tasks/enqueue.test.d.mts +1 -1
  70. package/dist/federation/tasks/enqueue.test.mjs +5 -5
  71. package/dist/federation/tasks/tasks.test.d.mts +1 -1
  72. package/dist/federation/tasks/tasks.test.mjs +7 -7
  73. package/dist/federation/temporal.test.d.mts +1 -1
  74. package/dist/federation/temporal.test.mjs +5 -5
  75. package/dist/federation/webfinger.test.d.mts +1 -1
  76. package/dist/federation/webfinger.test.mjs +7 -7
  77. package/dist/{http-BBkdx4Kd.mjs → http-CT7tcVBN.mjs} +5 -5
  78. package/dist/{http-jCq51U37.cjs → http-D5ylizT7.cjs} +4 -3
  79. package/dist/{http-CphHLbTC.js → http-DD07Erm7.js} +2 -2
  80. package/dist/{key-BBKNHoB9.mjs → key-C3t-PS3M.mjs} +3 -3
  81. package/dist/{keycache-CaOR6NYg.mjs → keycache-_DHqz5z_.mjs} +1 -1
  82. package/dist/{keys-CSYsOMFG.mjs → keys-Dbyd7bhl.mjs} +1 -1
  83. package/dist/{kv-CIvR3Non.mjs → kv-CXBAok3k.mjs} +1 -1
  84. package/dist/{kv-cache-D3KdEZAg.js → kv-cache-Bvv7Wp8h.js} +2 -2
  85. package/dist/{kv-cache-SVUAUYIs.mjs → kv-cache-CK273Baz.mjs} +2 -2
  86. package/dist/{kv-cache-B9Zvq4xB.cjs → kv-cache-DbFdquuM.cjs} +4 -3
  87. package/dist/{ld-DLyXLPDS.mjs → ld-D8wAPjxC.mjs} +4 -4
  88. package/dist/{metrics-BVRrGEXj.mjs → metrics-D1NrDWq-.mjs} +2 -2
  89. package/dist/{middleware-1AyOpRqz.mjs → middleware-BRunoF2v.mjs} +244 -24
  90. package/dist/{middleware-CzWfDAQY.mjs → middleware-CaP0k3AD.mjs} +2 -2
  91. package/dist/{middleware-B1jZw4xB.js → middleware-kQ-qwTDh.js} +249 -7
  92. package/dist/{middleware-DszmUorL.cjs → middleware-nmfhGFtV.cjs} +290 -39
  93. package/dist/{mod-Cmr39iWq.d.ts → mod-DLc2Ot6f.d.ts} +1 -1
  94. package/dist/{mod-B8Efb9qp.d.cts → mod-gOlPDuT8.d.cts} +1 -1
  95. package/dist/mod.cjs +7 -8
  96. package/dist/mod.d.cts +4 -4
  97. package/dist/mod.d.ts +4 -4
  98. package/dist/mod.js +8 -8
  99. package/dist/{mq-03vS-C9P.mjs → mq-llHKivST.mjs} +1 -1
  100. package/dist/{negotiation-m_L2nyS3.mjs → negotiation-RuwrPFqi.mjs} +1 -1
  101. package/dist/nodeinfo/client.test.d.mts +1 -1
  102. package/dist/nodeinfo/client.test.mjs +5 -5
  103. package/dist/nodeinfo/handler.test.d.mts +1 -1
  104. package/dist/nodeinfo/handler.test.mjs +6 -6
  105. package/dist/nodeinfo/mod.cjs +1 -2
  106. package/dist/nodeinfo/mod.js +2 -2
  107. package/dist/nodeinfo/types.test.d.mts +1 -1
  108. package/dist/nodeinfo/types.test.mjs +5 -5
  109. package/dist/otel/exporter.test.d.mts +1 -1
  110. package/dist/otel/exporter.test.mjs +4 -4
  111. package/dist/otel/mod.cjs +1 -2
  112. package/dist/otel/mod.js +1 -1
  113. package/dist/{outgoing-jsonld-BNL8AC14.mjs → outgoing-jsonld-URuqsICz.mjs} +2 -2
  114. package/dist/{owner-BukzQW4T.mjs → owner-BjdNNmKF.mjs} +3 -3
  115. package/dist/{proof-IDHRILpm.js → proof--HiDnLAE.js} +15 -4
  116. package/dist/{proof-BVglyYRW.mjs → proof-1Xy5Ur-T.mjs} +19 -7
  117. package/dist/{proof-DkeR_Fsm.cjs → proof-DcOiiLDF.cjs} +16 -4
  118. package/dist/{public-audience-c9zmYKgA.mjs → public-audience-Cr5MsnMq.mjs} +1 -1
  119. package/dist/{retry-DI4O-zYl.mjs → retry-DgFqeS5k.mjs} +1 -1
  120. package/dist/runtime/mod.cjs +1 -2
  121. package/dist/runtime/mod.js +2 -2
  122. package/dist/{send-D04Hz77b.mjs → send-ClJ4UcZL.mjs} +4 -4
  123. package/dist/sig/accept.test.d.mts +1 -1
  124. package/dist/sig/accept.test.mjs +2 -2
  125. package/dist/sig/http.test.d.mts +1 -1
  126. package/dist/sig/http.test.mjs +10 -10
  127. package/dist/sig/key.test.d.mts +1 -1
  128. package/dist/sig/key.test.mjs +7 -7
  129. package/dist/sig/ld.test.d.mts +1 -1
  130. package/dist/sig/ld.test.mjs +8 -8
  131. package/dist/sig/mod.cjs +2 -3
  132. package/dist/sig/mod.js +3 -3
  133. package/dist/sig/owner.test.d.mts +1 -1
  134. package/dist/sig/owner.test.mjs +7 -7
  135. package/dist/sig/proof.test.d.mts +1 -1
  136. package/dist/sig/proof.test.mjs +146 -9
  137. package/dist/{std__assert-Dh6uLf_q.mjs → std__assert-B2NnY9bq.mjs} +5 -5
  138. package/dist/temporal-polyfill-BCdTHexd.cjs +3565 -0
  139. package/dist/{temporal-xcPM9mxe.mjs → temporal-wA-b1IAz.mjs} +2 -2
  140. package/dist/testing/mod.d.mts +97 -1
  141. package/dist/testing/mod.mjs +3 -3
  142. package/dist/{testing-Dy-WhqpN.mjs → testing-CO7ftO8A.mjs} +3 -3
  143. package/dist/{transformers-NeAONrAq.cjs → transformers-DGwl-hD1.cjs} +1 -2
  144. package/dist/{transformers-BGMIq1cs.js → transformers-DXze6FUB.js} +1 -1
  145. package/dist/{types-KC4QAoxe.cjs → types-CPym83zc.cjs} +1 -2
  146. package/dist/{types-BFowWFTT.mjs → types-CrtdVuDP.mjs} +1 -1
  147. package/dist/{types-CAY3OdLq.js → types-DC1pNgJA.js} +1 -1
  148. package/dist/utils/docloader.test.d.mts +1 -1
  149. package/dist/utils/docloader.test.mjs +8 -8
  150. package/dist/utils/kv-cache.test.d.mts +1 -1
  151. package/dist/utils/kv-cache.test.mjs +2 -2
  152. package/dist/utils/mod.cjs +1 -2
  153. package/dist/utils/mod.js +2 -2
  154. package/dist/vocab/cjs.test.d.mts +1 -1
  155. package/dist/vocab/cjs.test.mjs +14 -1
  156. package/dist/vocab/mod.cjs +0 -1
  157. package/dist/vocab/mod.js +1 -1
  158. package/package.json +8 -8
@@ -1,29 +1,29 @@
1
- import { Temporal } from "@js-temporal/polyfill";
1
+ import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Bkl9jNVs.mjs";
5
- import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-BVRrGEXj.mjs";
6
- import { t as formatAcceptSignature } from "./accept-CgDcxvjV.mjs";
7
- import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-BBKNHoB9.mjs";
8
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-BBkdx4Kd.mjs";
9
- import { t as getAuthenticatedDocumentLoader } from "./docloader-f1XFaWWh.mjs";
10
- import { n as kvCache } from "./kv-cache-SVUAUYIs.mjs";
11
- import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-DLyXLPDS.mjs";
12
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-BukzQW4T.mjs";
13
- import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-BNL8AC14.mjs";
14
- import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-BVglyYRW.mjs";
15
- import { t as getNodeInfo } from "./client-B_A6mfn3.mjs";
16
- import { t as nodeInfoToJson } from "./types-BFowWFTT.mjs";
17
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-D04Hz77b.mjs";
18
- import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-Doj1FmsW.mjs";
19
- import { t as CircuitBreaker } from "./circuit-breaker-hJBB6jwA.mjs";
20
- import { t as buildCollectionSynchronizationHeader } from "./collection-Dp5ky45w.mjs";
21
- import { t as KvKeyCache } from "./keycache-CaOR6NYg.mjs";
22
- import { t as acceptsJsonLd } from "./negotiation-m_L2nyS3.mjs";
23
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-xcPM9mxe.mjs";
24
- import { t as createExponentialBackoffPolicy } from "./retry-DI4O-zYl.mjs";
25
- import { t as TaskCodec } from "./codec-nfBU4Up4.mjs";
26
- import { n as ParallelMessageQueue } from "./mq-03vS-C9P.mjs";
4
+ import { n as version, t as name } from "./deno-DepnPEgI.mjs";
5
+ import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-D1NrDWq-.mjs";
6
+ import { t as formatAcceptSignature } from "./accept-8PiolDP7.mjs";
7
+ import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-C3t-PS3M.mjs";
8
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-CT7tcVBN.mjs";
9
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-C_IefWRb.mjs";
10
+ import { n as kvCache } from "./kv-cache-CK273Baz.mjs";
11
+ import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-D8wAPjxC.mjs";
12
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-BjdNNmKF.mjs";
13
+ import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-URuqsICz.mjs";
14
+ import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-1Xy5Ur-T.mjs";
15
+ import { t as getNodeInfo } from "./client-CjF5IYQS.mjs";
16
+ import { t as nodeInfoToJson } from "./types-CrtdVuDP.mjs";
17
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-ClJ4UcZL.mjs";
18
+ import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-C4a7TAvq.mjs";
19
+ import { t as CircuitBreaker } from "./circuit-breaker-CBGyz5F7.mjs";
20
+ import { t as buildCollectionSynchronizationHeader } from "./collection-BIhlldqK.mjs";
21
+ import { t as KvKeyCache } from "./keycache-_DHqz5z_.mjs";
22
+ import { t as acceptsJsonLd } from "./negotiation-RuwrPFqi.mjs";
23
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-wA-b1IAz.mjs";
24
+ import { t as createExponentialBackoffPolicy } from "./retry-DgFqeS5k.mjs";
25
+ import { t as TaskCodec } from "./codec-BBga-FWI.mjs";
26
+ import { n as ParallelMessageQueue } from "./mq-llHKivST.mjs";
27
27
  import { getLogger, withContext } from "@logtape/logtape";
28
28
  import { RouterError } from "@fedify/uri-template";
29
29
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
@@ -1025,6 +1025,193 @@ async function handleOutbox(request, { identifier, context: ctx, outboxContextFa
1025
1025
  headers: { "Content-Type": "text/plain; charset=utf-8" }
1026
1026
  });
1027
1027
  }
1028
+ const plainTextHeaders = { "Content-Type": "text/plain; charset=utf-8" };
1029
+ /**
1030
+ * Handles a media upload request (the [ActivityPub Media Upload
1031
+ * extension](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload)).
1032
+ * @template TContextData The context data to pass to the context.
1033
+ * @param request The HTTP request.
1034
+ * @param parameters The parameters for handling the request.
1035
+ * @returns A promise that resolves to an HTTP response.
1036
+ * @since 2.4.0
1037
+ */
1038
+ async function handleMediaUpload(request, { identifier, context: ctx, mediaUploaderCallback, actorDispatcher, authorizePredicate, isRegisteredObjectUri, onUnauthorized, onNotFound }) {
1039
+ const logger = getLogger([
1040
+ "fedify",
1041
+ "federation",
1042
+ "mediaUploader"
1043
+ ]);
1044
+ if (request.bodyUsed) {
1045
+ logger.error("Request body has already been read.", { identifier });
1046
+ return new Response("Internal server error.", {
1047
+ status: 500,
1048
+ headers: plainTextHeaders
1049
+ });
1050
+ } else if (request.body?.locked) {
1051
+ logger.error("Request body is locked.", { identifier });
1052
+ return new Response("Internal server error.", {
1053
+ status: 500,
1054
+ headers: plainTextHeaders
1055
+ });
1056
+ }
1057
+ const contentType = request.headers.get("content-type");
1058
+ if (contentType == null || !contentType.toLowerCase().startsWith("multipart/form-data")) {
1059
+ logger.error("The media upload request is not multipart/form-data (got {contentType}).", {
1060
+ identifier,
1061
+ contentType
1062
+ });
1063
+ return new Response("Unsupported media type.", {
1064
+ status: 415,
1065
+ headers: plainTextHeaders
1066
+ });
1067
+ }
1068
+ if (actorDispatcher == null) {
1069
+ logger.error("Actor dispatcher is not set.", { identifier });
1070
+ return await onNotFound(request);
1071
+ }
1072
+ const actor = await actorDispatcher(ctx, identifier);
1073
+ if (actor == null || actor instanceof Tombstone) {
1074
+ logger.error("Actor {identifier} not found.", { identifier });
1075
+ return await onNotFound(request);
1076
+ }
1077
+ if (authorizePredicate != null) {
1078
+ let body;
1079
+ try {
1080
+ body = await request.arrayBuffer();
1081
+ } catch (error) {
1082
+ logger.error("Failed to read the multipart/form-data body:\n{error}", {
1083
+ identifier,
1084
+ error
1085
+ });
1086
+ return new Response("Invalid multipart/form-data body.", {
1087
+ status: 400,
1088
+ headers: plainTextHeaders
1089
+ });
1090
+ }
1091
+ const rebuild = () => new Request(request.url, {
1092
+ method: request.method,
1093
+ headers: request.headers,
1094
+ body: body.byteLength > 0 ? body : void 0
1095
+ });
1096
+ const authorizeContext = ctx.clone(ctx.data);
1097
+ authorizeContext.request = rebuild();
1098
+ if (!await authorizePredicate(authorizeContext, identifier)) return await onUnauthorized(rebuild());
1099
+ request = rebuild();
1100
+ }
1101
+ let form;
1102
+ try {
1103
+ form = await request.formData();
1104
+ } catch (error) {
1105
+ logger.error("Failed to parse the multipart/form-data body:\n{error}", {
1106
+ identifier,
1107
+ error
1108
+ });
1109
+ return new Response("Invalid multipart/form-data body.", {
1110
+ status: 400,
1111
+ headers: plainTextHeaders
1112
+ });
1113
+ }
1114
+ const file = form.get("file");
1115
+ if (!(file instanceof File)) {
1116
+ logger.error("The media upload request has no file field.", { identifier });
1117
+ return new Response("Missing file field.", {
1118
+ status: 400,
1119
+ headers: plainTextHeaders
1120
+ });
1121
+ }
1122
+ const objectField = form.get("object");
1123
+ if (objectField == null) {
1124
+ logger.error("The media upload request has no object field.", { identifier });
1125
+ return new Response("Missing object field.", {
1126
+ status: 400,
1127
+ headers: plainTextHeaders
1128
+ });
1129
+ }
1130
+ let object;
1131
+ try {
1132
+ const objectText = typeof objectField === "string" ? objectField : await objectField.text();
1133
+ object = await Object$1.fromJsonLd(JSON.parse(objectText), ctx);
1134
+ } catch (error) {
1135
+ logger.error("Failed to parse the object field:\n{error}", {
1136
+ identifier,
1137
+ error
1138
+ });
1139
+ return new Response("Invalid object field.", {
1140
+ status: 400,
1141
+ headers: plainTextHeaders
1142
+ });
1143
+ }
1144
+ let result;
1145
+ try {
1146
+ result = await mediaUploaderCallback(ctx, identifier, file, object);
1147
+ } catch (error) {
1148
+ logger.error("Failed to process the media upload:\n{error}", {
1149
+ identifier,
1150
+ error
1151
+ });
1152
+ return new Response("Internal server error.", {
1153
+ status: 500,
1154
+ headers: plainTextHeaders
1155
+ });
1156
+ }
1157
+ if (result == null) {
1158
+ logger.error("The media uploader callback for {identifier} returned null or undefined; it must return an object (201 Created) or a URL (202 Accepted).", { identifier });
1159
+ return new Response("Internal server error.", {
1160
+ status: 500,
1161
+ headers: plainTextHeaders
1162
+ });
1163
+ }
1164
+ const warnUnlessRegistered = (target) => {
1165
+ if (isRegisteredObjectUri(target)) return;
1166
+ logger.warn("The media uploader callback for {identifier} returned {target}, which does not point at a registered object dispatcher route; derive the returned id/URL from Context.getObjectUri(). The upload still succeeded.", {
1167
+ identifier,
1168
+ target: target.href
1169
+ });
1170
+ };
1171
+ if (result instanceof URL) {
1172
+ warnUnlessRegistered(result);
1173
+ logger.info("The media upload for {identifier} is still being processed; responding 202 Accepted.", {
1174
+ identifier,
1175
+ location: result.href
1176
+ });
1177
+ return new Response(null, {
1178
+ status: 202,
1179
+ headers: { Location: result.href }
1180
+ });
1181
+ }
1182
+ if (result.id == null) {
1183
+ logger.error("The media uploader callback for {identifier} returned an object without an id, so a 201 Created response cannot include the required Location header. Set the object's id with Context.getObjectUri(), or return the eventual URL to respond 202 Accepted instead.", { identifier });
1184
+ return new Response("Internal server error.", {
1185
+ status: 500,
1186
+ headers: plainTextHeaders
1187
+ });
1188
+ }
1189
+ warnUnlessRegistered(result.id);
1190
+ let jsonLd;
1191
+ try {
1192
+ jsonLd = await result.toJsonLd(ctx);
1193
+ } catch (error) {
1194
+ logger.error("Failed to serialize the uploaded object to JSON-LD:\n{error}", {
1195
+ identifier,
1196
+ error
1197
+ });
1198
+ return new Response("Internal server error.", {
1199
+ status: 500,
1200
+ headers: plainTextHeaders
1201
+ });
1202
+ }
1203
+ logger.info("The media upload for {identifier} is ready; responding 201 Created.", {
1204
+ identifier,
1205
+ location: result.id.href
1206
+ });
1207
+ return new Response(JSON.stringify(jsonLd), {
1208
+ status: 201,
1209
+ headers: {
1210
+ "Content-Type": "application/activity+json",
1211
+ Location: result.id.href
1212
+ }
1213
+ });
1214
+ }
1028
1215
  /**
1029
1216
  * Handles an inbox request for ActivityPub activities.
1030
1217
  * @template TContextData The context data to pass to the context.
@@ -2641,6 +2828,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2641
2828
  benchmarkMode;
2642
2829
  benchmarkMetricReader;
2643
2830
  benchmarkTriggerOptions;
2831
+ #mediaUploaderNoAuthWarned = false;
2644
2832
  #queueDepthGaugeSourceId = `fedify-${(++nextQueueDepthGaugeSourceId).toString(36)}`;
2645
2833
  #queueDepthGaugeEntries = [];
2646
2834
  #queueDepthGaugeMeterProvider;
@@ -3956,6 +4144,32 @@ var FederationImpl = class extends FederationBuilderImpl {
3956
4144
  });
3957
4145
  case "benchmarkStats": return await handleBenchmarkStats(request, this.benchmarkMetricReader);
3958
4146
  case "benchmarkTrigger": return await handleBenchmarkTrigger(request, context, this.benchmarkTriggerOptions);
4147
+ case "mediaUploader":
4148
+ if (request.method !== "POST" || this.mediaUploaderCallback == null) return new Response("Method not allowed.", {
4149
+ status: 405,
4150
+ headers: {
4151
+ Allow: "POST",
4152
+ "Content-Type": "text/plain; charset=utf-8"
4153
+ }
4154
+ });
4155
+ if (this.mediaUploaderAuthorizePredicate == null && !this.#mediaUploaderNoAuthWarned) {
4156
+ this.#mediaUploaderNoAuthWarned = true;
4157
+ getLogger([
4158
+ "fedify",
4159
+ "federation",
4160
+ "mediaUploader"
4161
+ ]).warn("The media uploader is registered without an authorize() hook, so it accepts uploads from anyone who can reach the endpoint. Protect it with .authorize() unless a public upload endpoint is intended.");
4162
+ }
4163
+ return await handleMediaUpload(request, {
4164
+ identifier: route.values.identifier,
4165
+ context,
4166
+ mediaUploaderCallback: this.mediaUploaderCallback,
4167
+ actorDispatcher: this.actorCallbacks?.dispatcher,
4168
+ authorizePredicate: this.mediaUploaderAuthorizePredicate,
4169
+ isRegisteredObjectUri: (uri) => context.parseUri(uri)?.type === "object",
4170
+ onUnauthorized,
4171
+ onNotFound
4172
+ });
3959
4173
  }
3960
4174
  if (request.method !== "POST" && !acceptsJsonLd(request)) {
3961
4175
  metricState.endpoint = "not_acceptable";
@@ -4190,6 +4404,7 @@ function getEndpointCategory(routeName) {
4190
4404
  case "inbox": return "inbox";
4191
4405
  case "sharedInbox": return "shared_inbox";
4192
4406
  case "outbox": return "outbox";
4407
+ case "mediaUploader": return "media_upload";
4193
4408
  case "following": return "following";
4194
4409
  case "followers": return "followers";
4195
4410
  case "liked": return "liked";
@@ -4323,6 +4538,11 @@ var ContextImpl = class ContextImpl {
4323
4538
  if (path == null) throw new RouterError("No outbox dispatcher registered.");
4324
4539
  return new URL(path, this.canonicalOrigin);
4325
4540
  }
4541
+ getMediaUploaderUri(identifier) {
4542
+ const path = this.federation.router.build("mediaUploader", { identifier });
4543
+ if (path == null) throw new RouterError("No media uploader registered.");
4544
+ return new URL(path, this.canonicalOrigin);
4545
+ }
4326
4546
  getInboxUri(identifier) {
4327
4547
  if (identifier == null) {
4328
4548
  const path = this.federation.router.build("sharedInbox", {});
@@ -1,5 +1,5 @@
1
- import "@js-temporal/polyfill";
1
+ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as FederationImpl } from "./middleware-1AyOpRqz.mjs";
4
+ import { n as FederationImpl } from "./middleware-BRunoF2v.mjs";
5
5
  export { FederationImpl };
@@ -1,11 +1,11 @@
1
- import { Temporal } from "@js-temporal/polyfill";
1
+ import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { t as __exportAll } from "./chunk-CRNNMoPX.js";
4
- import { r as getDefaultActivityTransformers } from "./transformers-BGMIq1cs.js";
5
- import { A as formatAcceptSignature, D as recordOutboxEnqueue, E as recordOutboxActivity, F as version, O as recordWebFingerHandle, P as name, S as recordCollectionTotalItems, T as recordInboxActivity, a as verifyRequestDetailed, b as recordCollectionPageItems, d as validateCryptoKey, f as getDurationMs, g as isAbortError, h as instrumentDocumentLoader, i as verifyRequest, k as registerQueueDepthGauge, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, v as recordCircuitBreakerStateChange, w as recordFanoutRecipients, x as recordCollectionRequest, y as recordCollectionDispatchDuration } from "./http-CphHLbTC.js";
6
- import { _ as hasSignatureLike, b as signJsonLd, c as getKeyOwner, f as compactJsonLd, g as hasSignature, h as getNormalizationContextLoader, i as verifyObject, l as InvalidContextReferenceError, m as detachSignature, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, r as signObject, s as doesActorOwnKey, u as assertSafeJsonLd, v as isClearlyMalformedContextReference, w as wrapContextLoaderForJsonLd, x as verifyCompactJsonLd, y as isInvalidUrlTypeError } from "./proof-IDHRILpm.js";
7
- import { n as getNodeInfo, t as nodeInfoToJson } from "./types-CAY3OdLq.js";
8
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-D3KdEZAg.js";
3
+ import { t as __exportAll } from "./chunk-D5IdezsZ.js";
4
+ import { r as getDefaultActivityTransformers } from "./transformers-DXze6FUB.js";
5
+ import { A as formatAcceptSignature, D as recordOutboxEnqueue, E as recordOutboxActivity, F as version, O as recordWebFingerHandle, P as name, S as recordCollectionTotalItems, T as recordInboxActivity, a as verifyRequestDetailed, b as recordCollectionPageItems, d as validateCryptoKey, f as getDurationMs, g as isAbortError, h as instrumentDocumentLoader, i as verifyRequest, k as registerQueueDepthGauge, m as getRemoteHost, n as parseRfc9421SignatureInput, o as exportJwk, p as getFederationMetrics, t as doubleKnock, u as importJwk, v as recordCircuitBreakerStateChange, w as recordFanoutRecipients, x as recordCollectionRequest, y as recordCollectionDispatchDuration } from "./http-DD07Erm7.js";
6
+ import { _ as hasSignatureLike, b as signJsonLd, c as getKeyOwner, f as compactJsonLd, g as hasSignature, h as getNormalizationContextLoader, i as verifyObject, l as InvalidContextReferenceError, m as detachSignature, n as hasProofLike, o as normalizeOutgoingActivityJsonLd, r as signObject, s as doesActorOwnKey, u as assertSafeJsonLd, v as isClearlyMalformedContextReference, w as wrapContextLoaderForJsonLd, x as verifyCompactJsonLd, y as isInvalidUrlTypeError } from "./proof--HiDnLAE.js";
7
+ import { n as getNodeInfo, t as nodeInfoToJson } from "./types-DC1pNgJA.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-Bvv7Wp8h.js";
9
9
  import { getLogger, withContext } from "@logtape/logtape";
10
10
  import { Router, RouterError, assertPath } from "@fedify/uri-template";
11
11
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
@@ -102,6 +102,9 @@ var FederationBuilderImpl = class {
102
102
  inboxErrorHandler;
103
103
  outboxListenerErrorHandler;
104
104
  outboxAuthorizePredicate;
105
+ mediaUploaderPath;
106
+ mediaUploaderCallback;
107
+ mediaUploaderAuthorizePredicate;
105
108
  sharedInboxKeyDispatcher;
106
109
  unverifiedActivityHandler;
107
110
  outboxPermanentFailureHandler;
@@ -154,6 +157,9 @@ var FederationBuilderImpl = class {
154
157
  f.inboxErrorHandler = this.inboxErrorHandler;
155
158
  f.outboxListenerErrorHandler = this.outboxListenerErrorHandler;
156
159
  f.outboxAuthorizePredicate = this.outboxAuthorizePredicate;
160
+ f.mediaUploaderPath = this.mediaUploaderPath;
161
+ f.mediaUploaderCallback = this.mediaUploaderCallback;
162
+ f.mediaUploaderAuthorizePredicate = this.mediaUploaderAuthorizePredicate;
157
163
  f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
158
164
  f.unverifiedActivityHandler = this.unverifiedActivityHandler;
159
165
  f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
@@ -232,6 +238,10 @@ var FederationBuilderImpl = class {
232
238
  if (actor.publicKeyId == null) logger.warn("You configured a key pairs dispatcher, but the actor does not have a publicKey property. Set the property with Context.getActorKeyPairs(identifier).");
233
239
  if (actor.assertionMethodId == null) logger.warn("You configured a key pairs dispatcher, but the actor does not have an assertionMethod property. Set the property with Context.getActorKeyPairs(identifier).");
234
240
  }
241
+ if (this.mediaUploaderCallback != null) {
242
+ if (actor.endpoints == null || actor.endpoints.uploadMedia == null) logger.warn("You configured a media uploader, but the actor does not have a endpoints.uploadMedia property. Set the property with Context.getMediaUploaderUri(identifier).");
243
+ else if (actor.endpoints.uploadMedia.href != context.getMediaUploaderUri(identifier).href) logger.warn("You configured a media uploader, but the actor's endpoints.uploadMedia property does not match the media upload endpoint URI. Set the property with Context.getMediaUploaderUri(identifier).");
244
+ }
235
245
  return actor;
236
246
  } };
237
247
  this.actorCallbacks = callbacks;
@@ -444,6 +454,18 @@ var FederationBuilderImpl = class {
444
454
  };
445
455
  return setters;
446
456
  }
457
+ setMediaUploader(path, callback) {
458
+ if (this.mediaUploaderCallback != null) throw new RouterError("Media uploader already set.");
459
+ assertPath(path);
460
+ this.router.add(path, "mediaUploader", identifierSingular);
461
+ this.mediaUploaderPath = path;
462
+ this.mediaUploaderCallback = callback;
463
+ const setters = { authorize: (predicate) => {
464
+ this.mediaUploaderAuthorizePredicate = predicate;
465
+ return setters;
466
+ } };
467
+ return setters;
468
+ }
447
469
  setFollowingDispatcher(path, dispatcher) {
448
470
  if (this.router.has("following")) throw new RouterError("Following collection dispatcher already set.");
449
471
  assertPath(path);
@@ -2014,6 +2036,193 @@ async function handleOutbox(request, { identifier, context: ctx, outboxContextFa
2014
2036
  headers: { "Content-Type": "text/plain; charset=utf-8" }
2015
2037
  });
2016
2038
  }
2039
+ const plainTextHeaders = { "Content-Type": "text/plain; charset=utf-8" };
2040
+ /**
2041
+ * Handles a media upload request (the [ActivityPub Media Upload
2042
+ * extension](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload)).
2043
+ * @template TContextData The context data to pass to the context.
2044
+ * @param request The HTTP request.
2045
+ * @param parameters The parameters for handling the request.
2046
+ * @returns A promise that resolves to an HTTP response.
2047
+ * @since 2.4.0
2048
+ */
2049
+ async function handleMediaUpload(request, { identifier, context: ctx, mediaUploaderCallback, actorDispatcher, authorizePredicate, isRegisteredObjectUri, onUnauthorized, onNotFound }) {
2050
+ const logger = getLogger([
2051
+ "fedify",
2052
+ "federation",
2053
+ "mediaUploader"
2054
+ ]);
2055
+ if (request.bodyUsed) {
2056
+ logger.error("Request body has already been read.", { identifier });
2057
+ return new Response("Internal server error.", {
2058
+ status: 500,
2059
+ headers: plainTextHeaders
2060
+ });
2061
+ } else if (request.body?.locked) {
2062
+ logger.error("Request body is locked.", { identifier });
2063
+ return new Response("Internal server error.", {
2064
+ status: 500,
2065
+ headers: plainTextHeaders
2066
+ });
2067
+ }
2068
+ const contentType = request.headers.get("content-type");
2069
+ if (contentType == null || !contentType.toLowerCase().startsWith("multipart/form-data")) {
2070
+ logger.error("The media upload request is not multipart/form-data (got {contentType}).", {
2071
+ identifier,
2072
+ contentType
2073
+ });
2074
+ return new Response("Unsupported media type.", {
2075
+ status: 415,
2076
+ headers: plainTextHeaders
2077
+ });
2078
+ }
2079
+ if (actorDispatcher == null) {
2080
+ logger.error("Actor dispatcher is not set.", { identifier });
2081
+ return await onNotFound(request);
2082
+ }
2083
+ const actor = await actorDispatcher(ctx, identifier);
2084
+ if (actor == null || actor instanceof Tombstone) {
2085
+ logger.error("Actor {identifier} not found.", { identifier });
2086
+ return await onNotFound(request);
2087
+ }
2088
+ if (authorizePredicate != null) {
2089
+ let body;
2090
+ try {
2091
+ body = await request.arrayBuffer();
2092
+ } catch (error) {
2093
+ logger.error("Failed to read the multipart/form-data body:\n{error}", {
2094
+ identifier,
2095
+ error
2096
+ });
2097
+ return new Response("Invalid multipart/form-data body.", {
2098
+ status: 400,
2099
+ headers: plainTextHeaders
2100
+ });
2101
+ }
2102
+ const rebuild = () => new Request(request.url, {
2103
+ method: request.method,
2104
+ headers: request.headers,
2105
+ body: body.byteLength > 0 ? body : void 0
2106
+ });
2107
+ const authorizeContext = ctx.clone(ctx.data);
2108
+ authorizeContext.request = rebuild();
2109
+ if (!await authorizePredicate(authorizeContext, identifier)) return await onUnauthorized(rebuild());
2110
+ request = rebuild();
2111
+ }
2112
+ let form;
2113
+ try {
2114
+ form = await request.formData();
2115
+ } catch (error) {
2116
+ logger.error("Failed to parse the multipart/form-data body:\n{error}", {
2117
+ identifier,
2118
+ error
2119
+ });
2120
+ return new Response("Invalid multipart/form-data body.", {
2121
+ status: 400,
2122
+ headers: plainTextHeaders
2123
+ });
2124
+ }
2125
+ const file = form.get("file");
2126
+ if (!(file instanceof File)) {
2127
+ logger.error("The media upload request has no file field.", { identifier });
2128
+ return new Response("Missing file field.", {
2129
+ status: 400,
2130
+ headers: plainTextHeaders
2131
+ });
2132
+ }
2133
+ const objectField = form.get("object");
2134
+ if (objectField == null) {
2135
+ logger.error("The media upload request has no object field.", { identifier });
2136
+ return new Response("Missing object field.", {
2137
+ status: 400,
2138
+ headers: plainTextHeaders
2139
+ });
2140
+ }
2141
+ let object;
2142
+ try {
2143
+ const objectText = typeof objectField === "string" ? objectField : await objectField.text();
2144
+ object = await Object$1.fromJsonLd(JSON.parse(objectText), ctx);
2145
+ } catch (error) {
2146
+ logger.error("Failed to parse the object field:\n{error}", {
2147
+ identifier,
2148
+ error
2149
+ });
2150
+ return new Response("Invalid object field.", {
2151
+ status: 400,
2152
+ headers: plainTextHeaders
2153
+ });
2154
+ }
2155
+ let result;
2156
+ try {
2157
+ result = await mediaUploaderCallback(ctx, identifier, file, object);
2158
+ } catch (error) {
2159
+ logger.error("Failed to process the media upload:\n{error}", {
2160
+ identifier,
2161
+ error
2162
+ });
2163
+ return new Response("Internal server error.", {
2164
+ status: 500,
2165
+ headers: plainTextHeaders
2166
+ });
2167
+ }
2168
+ if (result == null) {
2169
+ logger.error("The media uploader callback for {identifier} returned null or undefined; it must return an object (201 Created) or a URL (202 Accepted).", { identifier });
2170
+ return new Response("Internal server error.", {
2171
+ status: 500,
2172
+ headers: plainTextHeaders
2173
+ });
2174
+ }
2175
+ const warnUnlessRegistered = (target) => {
2176
+ if (isRegisteredObjectUri(target)) return;
2177
+ logger.warn("The media uploader callback for {identifier} returned {target}, which does not point at a registered object dispatcher route; derive the returned id/URL from Context.getObjectUri(). The upload still succeeded.", {
2178
+ identifier,
2179
+ target: target.href
2180
+ });
2181
+ };
2182
+ if (result instanceof URL) {
2183
+ warnUnlessRegistered(result);
2184
+ logger.info("The media upload for {identifier} is still being processed; responding 202 Accepted.", {
2185
+ identifier,
2186
+ location: result.href
2187
+ });
2188
+ return new Response(null, {
2189
+ status: 202,
2190
+ headers: { Location: result.href }
2191
+ });
2192
+ }
2193
+ if (result.id == null) {
2194
+ logger.error("The media uploader callback for {identifier} returned an object without an id, so a 201 Created response cannot include the required Location header. Set the object's id with Context.getObjectUri(), or return the eventual URL to respond 202 Accepted instead.", { identifier });
2195
+ return new Response("Internal server error.", {
2196
+ status: 500,
2197
+ headers: plainTextHeaders
2198
+ });
2199
+ }
2200
+ warnUnlessRegistered(result.id);
2201
+ let jsonLd;
2202
+ try {
2203
+ jsonLd = await result.toJsonLd(ctx);
2204
+ } catch (error) {
2205
+ logger.error("Failed to serialize the uploaded object to JSON-LD:\n{error}", {
2206
+ identifier,
2207
+ error
2208
+ });
2209
+ return new Response("Internal server error.", {
2210
+ status: 500,
2211
+ headers: plainTextHeaders
2212
+ });
2213
+ }
2214
+ logger.info("The media upload for {identifier} is ready; responding 201 Created.", {
2215
+ identifier,
2216
+ location: result.id.href
2217
+ });
2218
+ return new Response(JSON.stringify(jsonLd), {
2219
+ status: 201,
2220
+ headers: {
2221
+ "Content-Type": "application/activity+json",
2222
+ Location: result.id.href
2223
+ }
2224
+ });
2225
+ }
2017
2226
  /**
2018
2227
  * Handles an inbox request for ActivityPub activities.
2019
2228
  * @template TContextData The context data to pass to the context.
@@ -4517,6 +4726,7 @@ var FederationImpl = class extends FederationBuilderImpl {
4517
4726
  benchmarkMode;
4518
4727
  benchmarkMetricReader;
4519
4728
  benchmarkTriggerOptions;
4729
+ #mediaUploaderNoAuthWarned = false;
4520
4730
  #queueDepthGaugeSourceId = `fedify-${(++nextQueueDepthGaugeSourceId).toString(36)}`;
4521
4731
  #queueDepthGaugeEntries = [];
4522
4732
  #queueDepthGaugeMeterProvider;
@@ -5832,6 +6042,32 @@ var FederationImpl = class extends FederationBuilderImpl {
5832
6042
  });
5833
6043
  case "benchmarkStats": return await handleBenchmarkStats(request, this.benchmarkMetricReader);
5834
6044
  case "benchmarkTrigger": return await handleBenchmarkTrigger(request, context, this.benchmarkTriggerOptions);
6045
+ case "mediaUploader":
6046
+ if (request.method !== "POST" || this.mediaUploaderCallback == null) return new Response("Method not allowed.", {
6047
+ status: 405,
6048
+ headers: {
6049
+ Allow: "POST",
6050
+ "Content-Type": "text/plain; charset=utf-8"
6051
+ }
6052
+ });
6053
+ if (this.mediaUploaderAuthorizePredicate == null && !this.#mediaUploaderNoAuthWarned) {
6054
+ this.#mediaUploaderNoAuthWarned = true;
6055
+ getLogger([
6056
+ "fedify",
6057
+ "federation",
6058
+ "mediaUploader"
6059
+ ]).warn("The media uploader is registered without an authorize() hook, so it accepts uploads from anyone who can reach the endpoint. Protect it with .authorize() unless a public upload endpoint is intended.");
6060
+ }
6061
+ return await handleMediaUpload(request, {
6062
+ identifier: route.values.identifier,
6063
+ context,
6064
+ mediaUploaderCallback: this.mediaUploaderCallback,
6065
+ actorDispatcher: this.actorCallbacks?.dispatcher,
6066
+ authorizePredicate: this.mediaUploaderAuthorizePredicate,
6067
+ isRegisteredObjectUri: (uri) => context.parseUri(uri)?.type === "object",
6068
+ onUnauthorized,
6069
+ onNotFound
6070
+ });
5835
6071
  }
5836
6072
  if (request.method !== "POST" && !acceptsJsonLd(request)) {
5837
6073
  metricState.endpoint = "not_acceptable";
@@ -6066,6 +6302,7 @@ function getEndpointCategory(routeName) {
6066
6302
  case "inbox": return "inbox";
6067
6303
  case "sharedInbox": return "shared_inbox";
6068
6304
  case "outbox": return "outbox";
6305
+ case "mediaUploader": return "media_upload";
6069
6306
  case "following": return "following";
6070
6307
  case "followers": return "followers";
6071
6308
  case "liked": return "liked";
@@ -6199,6 +6436,11 @@ var ContextImpl = class ContextImpl {
6199
6436
  if (path == null) throw new RouterError("No outbox dispatcher registered.");
6200
6437
  return new URL(path, this.canonicalOrigin);
6201
6438
  }
6439
+ getMediaUploaderUri(identifier) {
6440
+ const path = this.federation.router.build("mediaUploader", { identifier });
6441
+ if (path == null) throw new RouterError("No media uploader registered.");
6442
+ return new URL(path, this.canonicalOrigin);
6443
+ }
6202
6444
  getInboxUri(identifier) {
6203
6445
  if (identifier == null) {
6204
6446
  const path = this.federation.router.build("sharedInbox", {});