@fedify/fedify 2.4.0-dev.2054 → 2.4.0-dev.2056

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 (75) hide show
  1. package/dist/{builder-C86Z3BKs.mjs → builder-D_XU_Fw2.mjs} +8 -2
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{compound-proof-BcZqTCwy.mjs → compound-proof-CwTySDnn.mjs} +1 -1
  6. package/dist/{context-BJPHhBmD.d.ts → context-D_gg4M0n.d.ts} +102 -1
  7. package/dist/{context-Bh_mJfbD.d.cts → context-b36TpPoG.d.cts} +102 -1
  8. package/dist/{deno-Bz6KTcon.mjs → deno-BtpLWN2L.mjs} +1 -1
  9. package/dist/{docloader-DS54zLD1.mjs → docloader-CJADEtqb.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +1 -1
  11. package/dist/federation/compound-fanout.test.mjs +3 -3
  12. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  13. package/dist/federation/handler.test.mjs +5 -5
  14. package/dist/federation/hashlink-media.test.d.mts +2 -0
  15. package/dist/federation/hashlink-media.test.mjs +422 -0
  16. package/dist/federation/idempotency.test.mjs +2 -2
  17. package/dist/federation/metrics.test.mjs +1 -1
  18. package/dist/federation/middleware.test.mjs +7 -7
  19. package/dist/federation/mod.cjs +1 -1
  20. package/dist/federation/mod.d.cts +3 -3
  21. package/dist/federation/mod.d.ts +3 -3
  22. package/dist/federation/mod.js +1 -1
  23. package/dist/federation/outgoing-proof.test.mjs +5 -5
  24. package/dist/federation/portable.test.mjs +3 -3
  25. package/dist/federation/router.test.mjs +2 -152
  26. package/dist/federation/send.test.mjs +3 -3
  27. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  28. package/dist/federation/tasks/tasks.test.mjs +2 -2
  29. package/dist/federation/temporal.test.mjs +1 -1
  30. package/dist/federation/webfinger.test.mjs +1 -1
  31. package/dist/{federation-su1OEWsM.cjs → federation-DDwszIrk.cjs} +139 -21
  32. package/dist/{http-Ty805y3A.mjs → http-CotELN-4.mjs} +3 -3
  33. package/dist/{http-LmUsMjK_.cjs → http-DW5mt8FG.cjs} +1 -1
  34. package/dist/{http-C3WGK1dL.js → http-iLCx9vk-.js} +1 -1
  35. package/dist/{key-BMc-2eW0.mjs → key-DrCNkH3Y.mjs} +2 -2
  36. package/dist/{kv-cache-CuaZnk6r.cjs → kv-cache-LOyq_Ty6.cjs} +1 -1
  37. package/dist/{kv-cache-BhPPtHg7.mjs → kv-cache-a1ghEA4h.mjs} +1 -1
  38. package/dist/{kv-cache-BcHCmrC8.js → kv-cache-bXLDSaRu.js} +1 -1
  39. package/dist/{ld-OpXWt-80.mjs → ld-BPG92sC0.mjs} +3 -3
  40. package/dist/{metrics-CTAe8lYz.mjs → metrics-DxVi0UJD.mjs} +1 -1
  41. package/dist/{middleware-_CMav1o7.js → middleware-1nes8_U-.js} +140 -22
  42. package/dist/{middleware-C-JLcqy7.mjs → middleware-BIPftTly.mjs} +1 -1
  43. package/dist/{middleware-BtwW3G41.mjs → middleware-DohUu-Tz.mjs} +145 -33
  44. package/dist/{mod-DU8aVzQJ.d.cts → mod-Bfenkg_c.d.cts} +1 -1
  45. package/dist/{mod-BofpDIcU.d.ts → mod-BhMvsgdF.d.ts} +1 -1
  46. package/dist/mod.cjs +4 -4
  47. package/dist/mod.d.cts +4 -4
  48. package/dist/mod.d.ts +4 -4
  49. package/dist/mod.js +4 -4
  50. package/dist/nodeinfo/handler.test.mjs +1 -1
  51. package/dist/{owner-RxB_eQpU.mjs → owner-BN7Mqjso.mjs} +2 -2
  52. package/dist/{owner-Bcm4pLXw.cjs → owner-C2ER4VD7.cjs} +1 -1
  53. package/dist/{owner-B-TLr-1I.js → owner-SBgt2USF.js} +1 -1
  54. package/dist/{proof-CVf2XbEa.mjs → proof-BigVZwkl.mjs} +4 -4
  55. package/dist/router-u5O5KCQr.mjs +156 -0
  56. package/dist/{send-DF9KzhJH.mjs → send-CvatEhyY.mjs} +3 -3
  57. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  58. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  59. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  60. package/dist/sig/compound-proof.test.mjs +2 -2
  61. package/dist/sig/http.test.mjs +2 -2
  62. package/dist/sig/key.test.mjs +1 -1
  63. package/dist/sig/ld.test.mjs +2 -2
  64. package/dist/sig/mod.cjs +2 -2
  65. package/dist/sig/mod.js +2 -2
  66. package/dist/sig/owner.test.mjs +1 -1
  67. package/dist/sig/proof.test.mjs +3 -3
  68. package/dist/{temporal-B6U3fEUW.mjs → temporal-DV5jeBbR.mjs} +1 -1
  69. package/dist/testing/kv.test.mjs +1 -1
  70. package/dist/testing/mod.d.mts +101 -0
  71. package/dist/utils/docloader.test.mjs +2 -2
  72. package/dist/utils/kv-cache.test.mjs +1 -1
  73. package/dist/utils/mod.cjs +1 -1
  74. package/dist/utils/mod.js +1 -1
  75. package/package.json +6 -6
@@ -6,159 +6,9 @@ import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
6
6
  import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
8
  import { t as assert } from "../assert-D1Eafmd_.mjs";
9
- import { getLogger } from "@logtape/logtape";
10
- import { Router, RouterError, assertPath, isPath } from "@fedify/uri-template";
9
+ import { n as RouterError$1, t as Router$1 } from "../router-u5O5KCQr.mjs";
10
+ import { RouterError } from "@fedify/uri-template";
11
11
  import { test } from "@fedify/fixture";
12
- //#region src/federation/router.ts
13
- const logger = getLogger([
14
- "fedify",
15
- "federation",
16
- "router",
17
- "deprecated"
18
- ]);
19
- let deprecationWarned = false;
20
- function warnDeprecated() {
21
- if (deprecationWarned) return;
22
- deprecationWarned = true;
23
- logger.warn("The `Router` and `RouterError` classes from `@fedify/fedify` are deprecated. Please use `Router` from `@fedify/uri-template` instead.");
24
- }
25
- /**
26
- * URL router and constructor based on URI Template
27
- * ([RFC 6570](https://tools.ietf.org/html/rfc6570)).
28
- *
29
- * @deprecated Import `Router` from `@fedify/uri-template` instead. This class
30
- * remains only for compatibility with older Fedify code. The
31
- * `@fedify/uri-template` router is the replacement implementation
32
- * and should be used directly in new code.
33
- */
34
- var Router$1 = class Router$1 {
35
- #router;
36
- /**
37
- * Create a new {@link Router}.
38
- * @param options Options for the router.
39
- * @deprecated Use `new Router(options)` from `@fedify/uri-template`
40
- * instead.
41
- */
42
- constructor(options) {
43
- this.#router = convertRouterError(() => new Router(options));
44
- }
45
- /**
46
- * Whether to ignore trailing slashes when matching paths.
47
- * @deprecated Use `Router` from `@fedify/uri-template` instead. This
48
- * accessor forwards to the underlying `@fedify/uri-template`
49
- * router so that post-construction mutation keeps working as
50
- * in older Fedify code.
51
- */
52
- get trailingSlashInsensitive() {
53
- return this.#router.trailingSlashInsensitive;
54
- }
55
- set trailingSlashInsensitive(value) {
56
- this.#router.trailingSlashInsensitive = value;
57
- }
58
- /**
59
- * Clones this router.
60
- * @deprecated Use `Router` from `@fedify/uri-template` instead.
61
- */
62
- clone() {
63
- return convertRouterError(() => {
64
- const clone = new Router$1();
65
- clone.#router = this.#router.clone();
66
- return clone;
67
- });
68
- }
69
- /**
70
- * Checks if a path name exists in the router.
71
- * @param name The name of the path.
72
- * @returns `true` if the path name exists, otherwise `false`.
73
- * @deprecated Use `Router` from `@fedify/uri-template` instead.
74
- */
75
- has(name) {
76
- return convertRouterError(() => this.#router.has(name));
77
- }
78
- /**
79
- * Adds a new path rule to the router.
80
- * @param template The path pattern.
81
- * @param name The name of the path.
82
- * @returns The names of the variables in the path pattern.
83
- * @deprecated Use `Router` from `@fedify/uri-template` instead. In this
84
- * compatibility class, `add()` both registers the route and
85
- * returns the variables in the path pattern. In
86
- * `@fedify/uri-template`, these two responsibilities are split:
87
- * `router.add(template, name)` registers the route and returns
88
- * `void`, while the pure static method
89
- * `Router.variables(template)` returns the variable names. To
90
- * migrate, call `Router.variables(template)` when variables are
91
- * needed, then call `router.add(template, name)` to register the
92
- * route.
93
- */
94
- add(template, name) {
95
- return convertRouterError(() => {
96
- assertPath(template);
97
- this.#router.add(template, name);
98
- return Router.variables(template);
99
- });
100
- }
101
- /**
102
- * Resolves a path name and values from a URL, if any match.
103
- * @param url The URL to resolve.
104
- * @returns The name of the path and its values, if any match. Otherwise,
105
- * `null`.
106
- * @deprecated Use `Router` from `@fedify/uri-template` instead. Unlike the
107
- * stricter `@fedify/uri-template` router, this compatibility
108
- * method keeps the old Fedify 2.x contract of returning `null`
109
- * (rather than throwing) for inputs that are not router paths.
110
- */
111
- route(url) {
112
- return convertRouterError(() => {
113
- if (!isPath(url)) return null;
114
- return this.#router.route(url);
115
- });
116
- }
117
- /**
118
- * Constructs a URL/path from a path name and values.
119
- * @param name The name of the path.
120
- * @param values The values to expand the path with.
121
- * @returns The URL/path, if the name exists. Otherwise, `null`.
122
- * @deprecated Use `Router` from `@fedify/uri-template` instead.
123
- */
124
- build(name, values) {
125
- return convertRouterError(() => this.#router.build(name, values));
126
- }
127
- };
128
- /**
129
- * An error thrown by the {@link Router}.
130
- * @deprecated Import `RouterError` from `@fedify/uri-template` instead.
131
- */
132
- var RouterError$1 = class extends RouterError {
133
- /**
134
- * Treats every `RouterError` from `@fedify/uri-template` as an instance of
135
- * this deprecated class.
136
- *
137
- * @deprecated Import `RouterError` from `@fedify/uri-template` instead.
138
- */
139
- static [Symbol.hasInstance](instance) {
140
- return instance instanceof RouterError;
141
- }
142
- /**
143
- * Create a new {@link RouterError}.
144
- * @param message The error message.
145
- * @deprecated Import `RouterError` from `@fedify/uri-template` instead.
146
- */
147
- constructor(message) {
148
- super(message);
149
- warnDeprecated();
150
- }
151
- };
152
- function convertRouterError(func) {
153
- try {
154
- warnDeprecated();
155
- return func();
156
- } catch (error) {
157
- if (error instanceof RouterError) throw new RouterError$1(error.message);
158
- throw error;
159
- }
160
- }
161
- //#endregion
162
12
  //#region src/federation/router.test.ts
163
13
  function setUp(options = {}) {
164
14
  const router = new Router$1(options);
@@ -10,10 +10,10 @@ import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
10
10
  import "../std__assert-DvMDRPes.mjs";
11
11
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
12
12
  import { t as assert } from "../assert-D1Eafmd_.mjs";
13
- import { l as verifyRequest } from "../http-Ty805y3A.mjs";
13
+ import { l as verifyRequest } from "../http-CotELN-4.mjs";
14
14
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
15
- import { t as doesActorOwnKey } from "../owner-RxB_eQpU.mjs";
16
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-DF9KzhJH.mjs";
15
+ import { t as doesActorOwnKey } from "../owner-BN7Mqjso.mjs";
16
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-CvatEhyY.mjs";
17
17
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
18
18
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
19
19
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
5
5
  import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-dbCShlCb.mjs";
6
6
  import "../../testing-By1YYsIK.mjs";
7
- import { o as createFederation } from "../../middleware-BtwW3G41.mjs";
7
+ import { o as createFederation } from "../../middleware-DohUu-Tz.mjs";
8
8
  import { n as ParallelMessageQueue } from "../../mq-Dm1Wmtm-.mjs";
9
9
  import { configure, reset } from "@logtape/logtape";
10
10
  import { delay } from "es-toolkit";
@@ -4,8 +4,8 @@ globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../../kv-DCHkaJ9-.mjs";
5
5
  import { a as numberSchema, i as makeSchema, n as baseOptions, o as stringSchema, t as MockQueue } from "../../tasks-dbCShlCb.mjs";
6
6
  import "../../testing-By1YYsIK.mjs";
7
- import { o as createFederation } from "../../middleware-BtwW3G41.mjs";
8
- import { r as createFederationBuilder } from "../../builder-C86Z3BKs.mjs";
7
+ import { o as createFederation } from "../../middleware-DohUu-Tz.mjs";
8
+ import { r as createFederationBuilder } from "../../builder-D_XU_Fw2.mjs";
9
9
  import { t as TaskCodec } from "../../codec-C6rTrpIp.mjs";
10
10
  import { t as InProcessMessageQueue } from "../../mq-Dm1Wmtm-.mjs";
11
11
  import { W3CTraceContextPropagator } from "@opentelemetry/core";
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
6
  import { t as assert } from "../assert-D1Eafmd_.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-B6U3fEUW.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-DV5jeBbR.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/federation/temporal.test.ts
10
10
  test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
@@ -6,7 +6,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import { t as assertNotEquals } from "../assert_not_equals-CpPK2t89.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
- import { o as createFederation, s as handleWebFinger } from "../middleware-BtwW3G41.mjs";
9
+ import { o as createFederation, s as handleWebFinger } from "../middleware-DohUu-Tz.mjs";
10
10
  import { Image, Link, Person, Tombstone } from "@fedify/vocab";
11
11
  import { createTestMeterProvider, test } from "@fedify/fixture";
12
12
  import { parseIri } from "@fedify/vocab-runtime";
@@ -1,10 +1,10 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
2
  const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
3
3
  const require_transformers = require("./transformers-BalCrSsr.cjs");
4
- const require_http = require("./http-LmUsMjK_.cjs");
4
+ const require_http = require("./http-DW5mt8FG.cjs");
5
5
  const require_types = require("./types-Be2zbyeG.cjs");
6
- const require_owner = require("./owner-Bcm4pLXw.cjs");
7
- const require_kv_cache = require("./kv-cache-CuaZnk6r.cjs");
6
+ const require_owner = require("./owner-C2ER4VD7.cjs");
7
+ const require_kv_cache = require("./kv-cache-LOyq_Ty6.cjs");
8
8
  let _logtape_logtape = require("@logtape/logtape");
9
9
  let _fedify_uri_template = require("@fedify/uri-template");
10
10
  let _fedify_vocab = require("@fedify/vocab");
@@ -105,6 +105,7 @@ var FederationBuilderImpl = class {
105
105
  mediaUploaderPath;
106
106
  mediaUploaderCallback;
107
107
  mediaUploaderAuthorizePredicate;
108
+ hashlinkMediaDispatcher;
108
109
  sharedInboxKeyDispatcher;
109
110
  unverifiedActivityHandler;
110
111
  outboxPermanentFailureHandler;
@@ -162,6 +163,7 @@ var FederationBuilderImpl = class {
162
163
  f.mediaUploaderPath = this.mediaUploaderPath;
163
164
  f.mediaUploaderCallback = this.mediaUploaderCallback;
164
165
  f.mediaUploaderAuthorizePredicate = this.mediaUploaderAuthorizePredicate;
166
+ f.hashlinkMediaDispatcher = this.hashlinkMediaDispatcher;
165
167
  f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
166
168
  f.unverifiedActivityHandler = this.unverifiedActivityHandler;
167
169
  f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
@@ -469,6 +471,10 @@ var FederationBuilderImpl = class {
469
471
  } };
470
472
  return setters;
471
473
  }
474
+ setHashlinkMediaDispatcher(dispatcher) {
475
+ if (this.hashlinkMediaDispatcher != null) throw new _fedify_uri_template.RouterError("Hashlink media dispatcher already set.");
476
+ this.hashlinkMediaDispatcher = dispatcher;
477
+ }
472
478
  setFollowingDispatcher(path, dispatcher) {
473
479
  if (this.router.has("following")) throw new _fedify_uri_template.RouterError("Following collection dispatcher already set.");
474
480
  (0, _fedify_uri_template.assertPath)(path);
@@ -2199,6 +2205,12 @@ function acceptsJsonLd(request) {
2199
2205
  const PORTABLE_OBJECT_CONTENT_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
2200
2206
  const GATEWAY_OBJECT_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
2201
2207
  const BARE_DID_PATTERN = /^did:[a-z0-9]+:[^/?#]+$/i;
2208
+ const GATEWAY_PATH_PREFIX = "/.well-known/apgateway/";
2209
+ const HASHLINK_SCHEME_PATTERN = /^hl(?::|%3A)/i;
2210
+ /**
2211
+ * The route template of hashlink media requests, used for metrics and traces.
2212
+ */
2213
+ const HASHLINK_MEDIA_ROUTE_TEMPLATE = "/.well-known/apgateway/hl:{digestMultibase}";
2202
2214
  /**
2203
2215
  * Recognizes an FEP-ef61 gateway request for a portable object, e.g.,
2204
2216
  * `GET /.well-known/apgateway/did:key:z6Mk.../notes/123`.
@@ -2253,6 +2265,55 @@ function buildPortableUri(authority, path) {
2253
2265
  (0, _fedify_vocab_runtime.getFe34Origin)(authority);
2254
2266
  return (0, _fedify_vocab_runtime.parseIri)(`ap+ef61://${authority}${path}`);
2255
2267
  }
2268
+ /**
2269
+ * Recognizes an FEP-ef61 gateway request for a resource addressed by
2270
+ * a hashlink, e.g.,
2271
+ * `GET /.well-known/apgateway/hl:zQmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n`.
2272
+ *
2273
+ * The rest of the path, including any slashes, is taken as the hashlink, as
2274
+ * a digest in a multibase encoding like base64 may contain slashes. The query
2275
+ * is not part of the hashlink, so it is ignored here.
2276
+ *
2277
+ * @param url The request URL.
2278
+ * @returns The parsed request, or `null` if the URL is not a gateway request
2279
+ * for a hashlink, e.g., a request for a portable object.
2280
+ */
2281
+ function parseHashlinkGatewayRequest(url) {
2282
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
2283
+ if (!url.pathname.startsWith(GATEWAY_PATH_PREFIX)) return null;
2284
+ const encoded = url.pathname.slice(23);
2285
+ if (!HASHLINK_SCHEME_PATTERN.test(encoded)) return null;
2286
+ let digestMultibase;
2287
+ let digest;
2288
+ let multihash;
2289
+ try {
2290
+ let hashlink;
2291
+ try {
2292
+ hashlink = decodeURIComponent(encoded);
2293
+ } catch (error) {
2294
+ throw new TypeError("Invalid percent-encoding in the hashlink.", { cause: error });
2295
+ }
2296
+ ({digestMultibase} = (0, _fedify_vocab_runtime.parseHashlink)(hashlink));
2297
+ ({digest} = (0, _fedify_vocab_runtime.parseDigestMultibase)(digestMultibase));
2298
+ multihash = (0, _fedify_vocab_runtime.decodeMultibase)(digestMultibase);
2299
+ } catch (error) {
2300
+ if (error instanceof TypeError) return {
2301
+ type: "malformed",
2302
+ error
2303
+ };
2304
+ throw error;
2305
+ }
2306
+ return {
2307
+ type: "media",
2308
+ media: Object.freeze({
2309
+ hashlink: `hl:${digestMultibase}`,
2310
+ digestMultibase,
2311
+ algorithm: "sha2-256",
2312
+ digest,
2313
+ multihash
2314
+ })
2315
+ };
2316
+ }
2256
2317
  //#endregion
2257
2318
  //#region src/federation/temporal.ts
2258
2319
  const Temporal$7 = require_http.Temporal;
@@ -6997,7 +7058,7 @@ var FederationImpl = class extends FederationBuilderImpl {
6997
7058
  tracer,
6998
7059
  metricState
6999
7060
  });
7000
- if (acceptsJsonLd(request)) response.headers.set("Vary", "Accept");
7061
+ if (metricState.endpoint !== "hashlink_media" && acceptsJsonLd(request)) response.headers.set("Vary", "Accept");
7001
7062
  } catch (error) {
7002
7063
  this.metrics.recordHttpServerRequest(request.method, metricState.endpoint ?? "error", require_http.getDurationMs(metricStart), { routeTemplate: metricState.routeTemplate });
7003
7064
  span.setStatus({
@@ -7048,24 +7109,22 @@ var FederationImpl = class extends FederationBuilderImpl {
7048
7109
  const url = new URL(request.url);
7049
7110
  const route = this.router.route(url.pathname);
7050
7111
  if (route == null) {
7112
+ const media = this.hashlinkMediaDispatcher == null ? null : parseHashlinkGatewayRequest(url);
7113
+ if (media != null) return await this.#fetchHashlinkMedia(request, media, {
7114
+ onNotFound,
7115
+ contextData,
7116
+ span,
7117
+ metricState
7118
+ });
7051
7119
  const portable = request.method === "GET" || request.method === "HEAD" ? parsePortableGatewayRequest(url) : null;
7052
- if (portable != null) {
7053
- const response = await this.#fetchPortableObject(request, portable, {
7054
- onNotFound,
7055
- onNotAcceptable,
7056
- onUnauthorized,
7057
- contextData,
7058
- span,
7059
- metricState
7060
- });
7061
- if (request.method !== "HEAD" || response.body == null) return response;
7062
- await response.body.cancel();
7063
- return new Response(null, {
7064
- status: response.status,
7065
- statusText: response.statusText,
7066
- headers: response.headers
7067
- });
7068
- }
7120
+ if (portable != null) return await discardBodyForHead(request, await this.#fetchPortableObject(request, portable, {
7121
+ onNotFound,
7122
+ onNotAcceptable,
7123
+ onUnauthorized,
7124
+ contextData,
7125
+ span,
7126
+ metricState
7127
+ }));
7069
7128
  metricState.endpoint = "not_found";
7070
7129
  return await onNotFound(request);
7071
7130
  }
@@ -7389,8 +7448,67 @@ var FederationImpl = class extends FederationBuilderImpl {
7389
7448
  onNotFound
7390
7449
  });
7391
7450
  }
7451
+ async #fetchHashlinkMedia(request, media, { onNotFound, contextData, span, metricState }) {
7452
+ metricState.endpoint = "hashlink_media";
7453
+ metricState.routeTemplate = HASHLINK_MEDIA_ROUTE_TEMPLATE;
7454
+ span.updateName(`${request.method} ${HASHLINK_MEDIA_ROUTE_TEMPLATE}`);
7455
+ if (request.method !== "GET" && request.method !== "HEAD") return new Response("Method not allowed.", {
7456
+ status: 405,
7457
+ headers: {
7458
+ Allow: "GET, HEAD",
7459
+ "Content-Type": "text/plain; charset=utf-8"
7460
+ }
7461
+ });
7462
+ if (media.type === "malformed") {
7463
+ (0, _logtape_logtape.getLogger)([
7464
+ "fedify",
7465
+ "federation",
7466
+ "hashlinkMedia"
7467
+ ]).debug("Malformed FEP-ef61 hashlink media request {url}: {error}", {
7468
+ url: request.url,
7469
+ error: media.error
7470
+ });
7471
+ return new Response(request.method === "HEAD" ? null : "Malformed hashlink.", {
7472
+ status: 400,
7473
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
7474
+ });
7475
+ }
7476
+ const context = this.#createContext(request, contextData);
7477
+ const response = await this.hashlinkMediaDispatcher(context, media.media);
7478
+ if (response == null) return await onNotFound(request);
7479
+ return await discardBodyForHead(request, response, false);
7480
+ }
7392
7481
  };
7393
7482
  const PORTABLE_GATEWAY_ROUTE_PREFIX = "/.well-known/apgateway/{did}";
7483
+ /**
7484
+ * Removes the body of a response to a `HEAD` request.
7485
+ * @param request The request.
7486
+ * @param response The response to the request.
7487
+ * @param waitForCancel Whether to wait until the discarded body is cancelled.
7488
+ * @returns The response without a body if the request is a `HEAD` request,
7489
+ * or the response itself otherwise.
7490
+ */
7491
+ async function discardBodyForHead(request, response, waitForCancel = true) {
7492
+ if (request.method !== "HEAD" || response.body == null) return response;
7493
+ const cancelled = response.body.cancel();
7494
+ if (waitForCancel) await cancelled;
7495
+ else cancelled.catch((error) => {
7496
+ (0, _logtape_logtape.getLogger)([
7497
+ "fedify",
7498
+ "federation",
7499
+ "http"
7500
+ ]).debug("Failed to cancel the body of a response to {method} {url}: {error}", {
7501
+ method: request.method,
7502
+ url: request.url,
7503
+ error
7504
+ });
7505
+ });
7506
+ return new Response(null, {
7507
+ status: response.status,
7508
+ statusText: response.statusText,
7509
+ headers: response.headers
7510
+ });
7511
+ }
7394
7512
  function getEndpointCategory(routeName) {
7395
7513
  if (routeName.startsWith("object:")) return "object";
7396
7514
  if (routeName.startsWith("collection:") || routeName.startsWith("orderedCollection:")) return "collection";
@@ -1,10 +1,10 @@
1
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-Bz6KTcon.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-CTAe8lYz.mjs";
4
+ import { n as version, t as name } from "./deno-BtpLWN2L.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-DxVi0UJD.mjs";
6
6
  import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPVqki78.mjs";
7
- import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-BMc-2eW0.mjs";
7
+ import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-DrCNkH3Y.mjs";
8
8
  import { getLogger } from "@logtape/logtape";
9
9
  import { CryptographicKey } from "@fedify/vocab";
10
10
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -9,7 +9,7 @@ let byte_encodings_base64 = require("byte-encodings/base64");
9
9
  let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
10
10
  //#region deno.json
11
11
  var name = "@fedify/fedify";
12
- var version = "2.4.0-dev.2054+1756e190";
12
+ var version = "2.4.0-dev.2056+e6394f3b";
13
13
  //#endregion
14
14
  //#region ../../node_modules/.pnpm/temporal-polyfill@1.0.1/node_modules/temporal-polyfill/chunks/root.js
15
15
  const NativeTemporal = globalThis.Temporal;
@@ -10,7 +10,7 @@ import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
10
10
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
11
11
  //#region deno.json
12
12
  var name = "@fedify/fedify";
13
- var version = "2.4.0-dev.2054+1756e190";
13
+ var version = "2.4.0-dev.2056+e6394f3b";
14
14
  //#endregion
15
15
  //#region src/sig/accept.ts
16
16
  /**
@@ -1,8 +1,8 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Bz6KTcon.mjs";
5
- import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-CTAe8lYz.mjs";
4
+ import { n as version, t as name } from "./deno-BtpLWN2L.mjs";
5
+ import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-DxVi0UJD.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { CryptographicKey, Multikey, Object as Object$1, isActor } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,5 +1,5 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
- const require_http = require("./http-LmUsMjK_.cjs");
2
+ const require_http = require("./http-DW5mt8FG.cjs");
3
3
  let _logtape_logtape = require("@logtape/logtape");
4
4
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
5
5
  let es_toolkit = require("es-toolkit");
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { p as recordDocumentCache } from "./metrics-CTAe8lYz.mjs";
4
+ import { p as recordDocumentCache } from "./metrics-DxVi0UJD.mjs";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  import { preloadedContexts } from "@fedify/vocab-runtime";
7
7
  //#region src/utils/kv-cache.ts
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { T as recordDocumentCache, f as validateCryptoKey, t as doubleKnock } from "./http-C3WGK1dL.js";
3
+ import { T as recordDocumentCache, f as validateCryptoKey, t as doubleKnock } from "./http-iLCx9vk-.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
6
6
  import { curry } from "es-toolkit";
@@ -1,9 +1,9 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Bz6KTcon.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-CTAe8lYz.mjs";
6
- import { r as fetchKey, s as validateCryptoKey } from "./key-BMc-2eW0.mjs";
4
+ import { n as version, t as name } from "./deno-BtpLWN2L.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-DxVi0UJD.mjs";
6
+ import { r as fetchKey, s as validateCryptoKey } from "./key-DrCNkH3Y.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
9
9
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,7 +1,7 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Bz6KTcon.mjs";
4
+ import { n as version, t as name } from "./deno-BtpLWN2L.mjs";
5
5
  import { metrics } from "@opentelemetry/api";
6
6
  import { FetchError } from "@fedify/vocab-runtime";
7
7
  //#region src/federation/metrics.ts