@fedify/fedify 2.5.0-dev.2446 → 2.5.0-dev.2448

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 (117) hide show
  1. package/dist/{activity-listener-C-BSbKI7.mjs → activity-listener-CELFAodc.mjs} +8 -4
  2. package/dist/{builder-Da8b9O6v.mjs → builder-DbC8bn6k.mjs} +5 -5
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/transformers.test.mjs +1 -1
  6. package/dist/{compound-proof-D1djO5fa.mjs → compound-proof-heeXW2_A.mjs} +2 -2
  7. package/dist/{context-CQ9H8vNW.d.cts → context-BazwQrHn.d.cts} +304 -4
  8. package/dist/{context-8Tu9kea0.mjs → context-COHd1Rsh.mjs} +2 -0
  9. package/dist/{context-BdB54V-A.d.ts → context-GlHrzdSP.d.ts} +304 -4
  10. package/dist/{deno-eNpfj4oR.mjs → deno-Dpewt_jJ.mjs} +1 -1
  11. package/dist/{docloader-B-0nqOB1.mjs → docloader-CpsDyqVN.mjs} +2 -2
  12. package/dist/federation/builder.test.mjs +1 -1
  13. package/dist/federation/compound-fanout.test.mjs +3 -3
  14. package/dist/federation/handler-compound-proof.test.mjs +5 -5
  15. package/dist/federation/handler.test.mjs +21 -6
  16. package/dist/federation/hashlink-media.test.mjs +2 -2
  17. package/dist/federation/idempotency.test.mjs +2 -2
  18. package/dist/federation/inbox-actorless.test.d.mts +2 -0
  19. package/dist/federation/inbox-actorless.test.mjs +487 -0
  20. package/dist/federation/inbox-authentication.test.d.mts +2 -0
  21. package/dist/federation/inbox-authentication.test.mjs +116 -0
  22. package/dist/federation/inbox-report.test.mjs +5 -5
  23. package/dist/federation/inbox-view.test.d.mts +2 -0
  24. package/dist/federation/inbox-view.test.mjs +369 -0
  25. package/dist/federation/inbox.test.mjs +22 -12
  26. package/dist/federation/metrics.test.mjs +1 -1
  27. package/dist/federation/middleware.test.mjs +9 -9
  28. package/dist/federation/mod.cjs +2 -1
  29. package/dist/federation/mod.d.cts +3 -3
  30. package/dist/federation/mod.d.ts +3 -3
  31. package/dist/federation/mod.js +8 -2
  32. package/dist/federation/mq.test.mjs +36 -1
  33. package/dist/federation/outgoing-proof.test.mjs +4 -4
  34. package/dist/federation/portable-actor.test.mjs +3 -3
  35. package/dist/federation/portable-chain.test.mjs +3 -3
  36. package/dist/federation/portable-collection.test.mjs +4 -4
  37. package/dist/federation/portable-delivery.test.mjs +2 -2
  38. package/dist/federation/portable-gateway-keys.test.mjs +6 -6
  39. package/dist/federation/portable-inbox.test.mjs +4 -4
  40. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  41. package/dist/federation/portable-route-activity.test.mjs +2 -2
  42. package/dist/federation/portable.test.mjs +3 -3
  43. package/dist/federation/send.test.mjs +3 -3
  44. package/dist/federation/tasks/enqueue.test.mjs +3 -3
  45. package/dist/federation/tasks/tasks.test.mjs +4 -4
  46. package/dist/federation/temporal.test.mjs +1 -1
  47. package/dist/federation/webfinger.test.mjs +2 -2
  48. package/dist/{federation-BVH7drJA.cjs → federation-D8YexkP1.cjs} +1172 -131
  49. package/dist/{http-DlNxxW_5.js → http-BBr-VLil.js} +143 -16
  50. package/dist/{http-BUJ5SFh6.mjs → http-BZkyQIkb.mjs} +4 -4
  51. package/dist/{http-BfW3wyQ4.cjs → http-CAl7Num5.cjs} +161 -16
  52. package/dist/inbox-authentication-DflPYlLL.mjs +171 -0
  53. package/dist/inbox-view-NE9wXdLo.mjs +385 -0
  54. package/dist/inbox-wDuMKaFW.mjs +312 -0
  55. package/dist/{kv-cache-D_m3A241.mjs → kv-cache-CTQSfkmp.mjs} +1 -1
  56. package/dist/{kv-cache-kkBB0vbI.cjs → kv-cache-CjgCOz3i.cjs} +1 -1
  57. package/dist/{kv-cache-7015lbrx.js → kv-cache-U7uz5nx4.js} +1 -1
  58. package/dist/{ld-CD2CsPxq.mjs → ld-CQFuiah9.mjs} +52 -9
  59. package/dist/ld-CcuZ4NZa.d.ts +250 -0
  60. package/dist/ld-Cel1RRZ5.d.cts +250 -0
  61. package/dist/{metrics-D3LHk6Z4.mjs → metrics-B-QxRSSR.mjs} +1 -1
  62. package/dist/{middleware-CCDXvXOG.mjs → middleware-BdlNa8BD.mjs} +1 -1
  63. package/dist/{middleware-CzoWvrdZ.mjs → middleware-CuVa0UuS.mjs} +459 -98
  64. package/dist/{middleware-N9eM16XE.js → middleware-P-AZEQMn.js} +1169 -141
  65. package/dist/mod-B2783_3v.d.cts +154 -0
  66. package/dist/mod-BHQE-7WQ.d.ts +154 -0
  67. package/dist/{mod-D0bxB8jS.d.ts → mod-DjZfJj9R.d.ts} +1 -1
  68. package/dist/{mod-B0dGgQsg.d.cts → mod-DvWOv2Ho.d.cts} +1 -1
  69. package/dist/mod.cjs +5 -4
  70. package/dist/mod.d.cts +6 -6
  71. package/dist/mod.d.ts +6 -6
  72. package/dist/mod.js +5 -5
  73. package/dist/{mq-Dm1Wmtm-.mjs → mq-eF96QxUT.mjs} +6 -0
  74. package/dist/nodeinfo/handler.test.mjs +2 -2
  75. package/dist/{owner-C5-FVX6t.mjs → owner-ccKkQOAf.mjs} +3 -3
  76. package/dist/{portable-collection-Cony3_LC.mjs → portable-collection-CndnpXfo.mjs} +2 -2
  77. package/dist/{portable-collection-CC2zNAje.js → portable-collection-DUlQ3s2M.js} +1 -1
  78. package/dist/{portable-collection-C-Z1SKDy.cjs → portable-collection-TC-kvkVI.cjs} +1 -1
  79. package/dist/{portable-key-DNc_flH-.mjs → portable-key-D5tFOoV-.mjs} +1 -1
  80. package/dist/{proof-Cryb1c70.mjs → proof-DPoF06nz.mjs} +98 -10
  81. package/dist/{send-AF25pWaJ.mjs → send-CF44O1Df.mjs} +3 -3
  82. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  83. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  84. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  85. package/dist/sig/compound-proof.test.mjs +2 -2
  86. package/dist/sig/http.test.mjs +2 -2
  87. package/dist/sig/key.test.mjs +1 -1
  88. package/dist/sig/ld.test.mjs +1 -1
  89. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  90. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  91. package/dist/sig/mod.cjs +2 -2
  92. package/dist/sig/mod.d.cts +2 -2
  93. package/dist/sig/mod.d.ts +2 -2
  94. package/dist/sig/mod.js +2 -2
  95. package/dist/sig/owner.test.mjs +1 -1
  96. package/dist/sig/portable-collection.test.mjs +1 -1
  97. package/dist/sig/portable-interop.test.mjs +3 -3
  98. package/dist/sig/portable-key.test.mjs +5 -5
  99. package/dist/sig/proof.test.mjs +44 -4
  100. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  101. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  102. package/dist/sig/verification.test.mjs +3 -3
  103. package/dist/{temporal-BsAeuUJj.mjs → temporal-XlZ4Wb94.mjs} +1 -1
  104. package/dist/testing/kv.test.mjs +1 -1
  105. package/dist/testing/mod.d.mts +262 -2
  106. package/dist/testing/mod.mjs +2 -2
  107. package/dist/{testing-t8xDeJZ5.mjs → testing-Dbr21vZk.mjs} +1 -1
  108. package/dist/utils/docloader.test.mjs +2 -2
  109. package/dist/utils/kv-cache.test.mjs +1 -1
  110. package/dist/utils/mod.cjs +1 -1
  111. package/dist/utils/mod.js +1 -1
  112. package/package.json +7 -7
  113. package/dist/inbox-CHvMaIFX.mjs +0 -188
  114. package/dist/mod-BRpER-B0.d.ts +0 -320
  115. package/dist/mod-xAfaLS4T.d.cts +0 -320
  116. package/dist/owner-BF0L9cMt.d.cts +0 -78
  117. package/dist/owner-BSzr5-I8.d.ts +0 -78
@@ -0,0 +1,312 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as getAuthenticationOrigin } from "./portable-key-id-Ca30-hOa.mjs";
5
+ import { o as serializeAuthentication, r as getEffectivePrincipals } from "./inbox-authentication-DflPYlLL.mjs";
6
+ import { n as version, t as name } from "./deno-Dpewt_jJ.mjs";
7
+ import { g as recordInboxActivity, n as getDurationMs, r as getFederationMetrics } from "./metrics-B-QxRSSR.mjs";
8
+ import { getLogger } from "@logtape/logtape";
9
+ import { getTypeId } from "@fedify/vocab";
10
+ import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
11
+ //#region src/federation/inbox.ts
12
+ /**
13
+ * Checks whether an activity's ID and all of its actor IDs have the same
14
+ * [FEP-fe34] origin. Activities without an ID have no origin to authenticate.
15
+ *
16
+ * Origins are compared by `getAuthenticationOrigin()`, so a portable activity
17
+ * and its portable actors, including compatible identifiers on any gateway,
18
+ * are compared by their DIDs. An ID that has no supported origin, such as an
19
+ * opaque URN, never matches.
20
+ *
21
+ * The caller is responsible for rejecting a missing primary actor ID before
22
+ * relying on this check.
23
+ *
24
+ * [FEP-fe34]: https://w3id.org/fep/fe34
25
+ * @internal
26
+ */
27
+ function actorsHaveSameOrigin(activity) {
28
+ if (activity.id == null) return true;
29
+ const origin = getAuthenticationOrigin(activity.id);
30
+ return origin != null && activity.actorIds.every((actor) => getAuthenticationOrigin(actor) === origin);
31
+ }
32
+ /**
33
+ * The headroom that the size check of a queued inbox message leaves for the
34
+ * fields that grow when Fedify retries the message, e.g., its attempt count.
35
+ */
36
+ const QUEUED_MESSAGE_HEADROOM = 64;
37
+ /**
38
+ * Computes the idempotency key of an incoming activity.
39
+ * @returns The key, or `null` if the activity is not deduplicated.
40
+ * @internal
41
+ */
42
+ async function getInboxIdempotencyKey(strategy, inboxContext, activity, origin, recipient, prefix) {
43
+ if (activity.id == null) return null;
44
+ strategy ??= "per-inbox";
45
+ let keyString;
46
+ if (typeof strategy === "function") keyString = await strategy(inboxContext(), activity);
47
+ else switch (strategy) {
48
+ case "global":
49
+ keyString = activity.id.href;
50
+ break;
51
+ case "per-origin":
52
+ keyString = `${origin}\n${activity.id.href}`;
53
+ break;
54
+ case "per-inbox":
55
+ keyString = `${origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
56
+ break;
57
+ default: keyString = `${origin}\n${activity.id.href}`;
58
+ }
59
+ return keyString == null ? null : [...prefix, keyString];
60
+ }
61
+ /**
62
+ * Checks whether an incoming activity has already been processed, by its
63
+ * idempotency key, or by the key that queue workers of older versions of
64
+ * Fedify recorded.
65
+ * @internal
66
+ */
67
+ async function isInboxActivityProcessed(kv, cacheKey, activity, origin, prefix) {
68
+ if (cacheKey == null) return false;
69
+ if (await kv.get(cacheKey) === true) return true;
70
+ if (activity.id == null) return false;
71
+ return await kv.get([
72
+ ...prefix,
73
+ origin,
74
+ activity.id.href
75
+ ]) === true;
76
+ }
77
+ /**
78
+ * The bytes added to the UTF-8 JSON length of a message for each of its JSON
79
+ * values, to bound the size of serialization formats other than JSON, such
80
+ * as the structured serialization of Deno KV, which spends more bytes than
81
+ * JSON on type tags and lengths of small values, e.g., empty arrays, and up
82
+ * to nine bytes on a fractional number that JSON writes in three.
83
+ */
84
+ const PER_VALUE_SERIALIZATION_OVERHEAD = 8;
85
+ /**
86
+ * Estimates, conservatively, how large a queued message is once a queue
87
+ * backend serializes it: the UTF-8 length of its JSON serialization, plus
88
+ * a fixed overhead for each JSON value in it, plus the bytes that encoding
89
+ * strings beyond Latin-1 as UTF-16 adds.
90
+ * @internal
91
+ */
92
+ function estimateQueuedMessageSize(message) {
93
+ const encoder = new TextEncoder();
94
+ let values = 0;
95
+ let stringExpansion = 0;
96
+ const countString = (value) => {
97
+ values++;
98
+ if (!/[\u0100-\uffff]/.test(value)) return;
99
+ const expansion = value.length * 2 - encoder.encode(value).byteLength;
100
+ if (expansion > 0) stringExpansion += expansion;
101
+ };
102
+ const count = (value) => {
103
+ if (typeof value === "string") {
104
+ countString(value);
105
+ return;
106
+ }
107
+ values++;
108
+ if (Array.isArray(value)) for (const item of value) count(item);
109
+ else if (value != null && typeof value === "object") for (const [key, item] of Object.entries(value)) {
110
+ if (item === void 0) continue;
111
+ countString(key);
112
+ count(item);
113
+ }
114
+ };
115
+ count(message);
116
+ return encoder.encode(JSON.stringify(message)).byteLength + stringExpansion + values * PER_VALUE_SERIALIZATION_OVERHEAD;
117
+ }
118
+ /**
119
+ * Checks whether an activity that declares no actor is authenticated: at
120
+ * least one principal must be authenticated for it, and its ID, if any, must
121
+ * have the same [FEP-fe34] origin as one of those principals.
122
+ *
123
+ * [FEP-fe34]: https://w3id.org/fep/fe34
124
+ * @returns `"ok"` if it is authenticated, or why it is not.
125
+ * @internal
126
+ */
127
+ function checkActorlessActivity(activity, authentication) {
128
+ const principals = getEffectivePrincipals(authentication);
129
+ if (principals.length < 1) return {
130
+ status: "noAuthenticatedPrincipal",
131
+ principals
132
+ };
133
+ if (activity.id == null) return {
134
+ status: "ok",
135
+ principals
136
+ };
137
+ const origin = getAuthenticationOrigin(activity.id);
138
+ if (origin == null || !principals.some((principal) => getAuthenticationOrigin(principal.id) === origin)) return {
139
+ status: "activityOriginMismatch",
140
+ principals
141
+ };
142
+ return {
143
+ status: "ok",
144
+ principals
145
+ };
146
+ }
147
+ /**
148
+ * Same as {@link checkActorlessActivity}, but returns a boolean.
149
+ * @internal
150
+ */
151
+ function isActorlessActivityAuthenticated(activity, authentication) {
152
+ return checkActorlessActivity(activity, authentication).status === "ok";
153
+ }
154
+ async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, authentication, verifiedView, replayable, actorless, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
155
+ const logger = getLogger([
156
+ "fedify",
157
+ "federation",
158
+ "inbox"
159
+ ]);
160
+ if (activity.actorId == null && !actorless) {
161
+ logger.error("Missing actor.", { activity: json });
162
+ span.setStatus({
163
+ code: SpanStatusCode.ERROR,
164
+ message: "Missing actor."
165
+ });
166
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
167
+ return "missingActor";
168
+ }
169
+ const contextMetadata = { authentication };
170
+ const cacheKey = await getInboxIdempotencyKey(idempotencyStrategy, () => inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href, contextMetadata), activity, ctx.origin, recipient, kvPrefixes.activityIdempotence);
171
+ if (await isInboxActivityProcessed(kv, cacheKey, activity, ctx.origin, kvPrefixes.activityIdempotence)) {
172
+ logger.debug("Activity {activityId} has already been processed.", {
173
+ activityId: activity.id?.href,
174
+ activity: json,
175
+ recipient
176
+ });
177
+ span.setStatus({
178
+ code: SpanStatusCode.UNSET,
179
+ message: `Activity ${activity.id?.href} has already been processed.`
180
+ });
181
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
182
+ return "alreadyProcessed";
183
+ }
184
+ if (activity.actorId != null) span.setAttribute("activitypub.actor.id", activity.actorId.href);
185
+ let message;
186
+ if (queue != null) {
187
+ const carrier = {};
188
+ propagation.inject(context.active(), carrier);
189
+ message = {
190
+ type: "inbox",
191
+ id: crypto.randomUUID(),
192
+ baseUrl: ctx.origin,
193
+ activity: originalJson ?? json,
194
+ ...normalizedActivity == null ? {} : { normalizedActivity },
195
+ ...ldSignatureVerified == null ? {} : { ldSignatureVerified },
196
+ ...verifiedView == null ? {} : { verifiedView },
197
+ ...authentication == null ? {} : { authentication: serializeAuthentication(authentication) },
198
+ identifier: recipient,
199
+ attempt: 0,
200
+ started: (/* @__PURE__ */ new Date()).toISOString(),
201
+ traceContext: carrier
202
+ };
203
+ const maxMessageSize = queue.maxMessageSize;
204
+ if (replayable === false) {
205
+ logger.warn("The incoming activity {activityId} uses a JSON-LD context that cannot be kept for the queue; processing it immediately instead.", {
206
+ activityId: activity.id?.href,
207
+ recipient
208
+ });
209
+ message = void 0;
210
+ } else if (maxMessageSize != null && estimateQueuedMessageSize(message) > maxMessageSize - QUEUED_MESSAGE_HEADROOM) {
211
+ logger.warn("The incoming activity {activityId} is too large to enqueue; processing it immediately instead.", {
212
+ activityId: activity.id?.href,
213
+ recipient,
214
+ maxMessageSize
215
+ });
216
+ message = void 0;
217
+ }
218
+ }
219
+ if (queue != null && message != null) {
220
+ try {
221
+ await queue.enqueue(message);
222
+ } catch (error) {
223
+ logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
224
+ error,
225
+ activityId: activity.id?.href,
226
+ activity: json,
227
+ recipient
228
+ });
229
+ span.setStatus({
230
+ code: SpanStatusCode.ERROR,
231
+ message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
232
+ });
233
+ throw error;
234
+ }
235
+ getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
236
+ role: "inbox",
237
+ queue,
238
+ activityType: getTypeId(activity).href
239
+ }, 0);
240
+ recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
241
+ logger.info("Activity {activityId} is enqueued.", {
242
+ activityId: activity.id?.href,
243
+ activity: json,
244
+ recipient
245
+ });
246
+ return "enqueued";
247
+ }
248
+ tracerProvider = tracerProvider ?? trace.getTracerProvider();
249
+ return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
250
+ const dispatched = inboxListeners?.dispatchWithClass(activity);
251
+ if (dispatched == null) {
252
+ logger.error("Unsupported activity type:\n{activity}", {
253
+ activity: json,
254
+ recipient
255
+ });
256
+ span.setStatus({
257
+ code: SpanStatusCode.UNSET,
258
+ message: `Unsupported activity type: ${getTypeId(activity).href}`
259
+ });
260
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
261
+ span.end();
262
+ return "unsupportedActivity";
263
+ }
264
+ const { class: cls, listener } = dispatched;
265
+ span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
266
+ try {
267
+ const activityType = getTypeId(activity).href;
268
+ const started = performance.now();
269
+ try {
270
+ const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
271
+ await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType, contextMetadata), activity);
272
+ } finally {
273
+ getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
274
+ }
275
+ } catch (error) {
276
+ try {
277
+ await inboxErrorHandler?.(ctx, error);
278
+ } catch (error) {
279
+ logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
280
+ error,
281
+ activityId: activity.id?.href,
282
+ activity: json,
283
+ recipient
284
+ });
285
+ }
286
+ logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
287
+ error,
288
+ activityId: activity.id?.href,
289
+ activity: json,
290
+ recipient
291
+ });
292
+ span.setStatus({
293
+ code: SpanStatusCode.ERROR,
294
+ message: String(error)
295
+ });
296
+ recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
297
+ span.end();
298
+ return "error";
299
+ }
300
+ recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
301
+ if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
302
+ logger.info("Activity {activityId} has been processed.", {
303
+ activityId: activity.id?.href,
304
+ activity: json,
305
+ recipient
306
+ });
307
+ span.end();
308
+ return "success";
309
+ });
310
+ }
311
+ //#endregion
312
+ export { isActorlessActivityAuthenticated as a, getInboxIdempotencyKey as i, checkActorlessActivity as n, isInboxActivityProcessed as o, estimateQueuedMessageSize as r, routeActivity as s, actorsHaveSameOrigin as t };
@@ -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-D3LHk6Z4.mjs";
4
+ import { p as recordDocumentCache } from "./metrics-B-QxRSSR.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,5 +1,5 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
- const require_http = require("./http-BfW3wyQ4.cjs");
2
+ const require_http = require("./http-CAl7Num5.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,6 +1,6 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { ct as recordDocumentCache, q as validateCryptoKey, t as doubleKnock } from "./http-DlNxxW_5.js";
3
+ import { X as validateCryptoKey, dt as recordDocumentCache, t as doubleKnock } from "./http-BBr-VLil.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, resolveDocumentLoaderTimeout, validatePublicUrl, withDocumentLoaderTimeout } from "@fedify/vocab-runtime";
6
6
  import { curry } from "es-toolkit";
@@ -3,8 +3,8 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { c as isPortableActorDocument, d as isPortableUri, l as isPortableId, m as parseKeyIdString, s as isCompatibleKeyId } from "./portable-key-id-Ca30-hOa.mjs";
5
5
  import { a as verificationObservation, i as triedKey, n as observeCheck, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
6
- import { n as version, t as name } from "./deno-eNpfj4oR.mjs";
7
- import { _ as recordKeyLookup, n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch, t as classifyFetchError } from "./metrics-D3LHk6Z4.mjs";
6
+ import { n as version, t as name } from "./deno-Dpewt_jJ.mjs";
7
+ import { _ as recordKeyLookup, n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch, t as classifyFetchError } from "./metrics-B-QxRSSR.mjs";
8
8
  import { n as attachKeyOwnerEvidence, t as MAX_PORTABLE_KEY_TTL } from "./key-owner-evidence-APB6NpSk.mjs";
9
9
  import { getLogger } from "@logtape/logtape";
10
10
  import { Activity, CryptographicKey, Multikey, Object as Object$1, getTypeId, isActor } from "@fedify/vocab";
@@ -200,6 +200,7 @@ async function fetchActorDocumentInternal(actorId, options, propagateContextFetc
200
200
  actorId: actorId.href,
201
201
  error
202
202
  });
203
+ if (propagateContextFetchErrors) throw new KeyContextFetchError(error);
203
204
  return null;
204
205
  }
205
206
  let object;
@@ -1146,8 +1147,26 @@ function createMemoizedDocumentLoader(documentLoader) {
1146
1147
  return cloneRemoteDocument(await remoteDocument);
1147
1148
  };
1148
1149
  }
1150
+ const effectiveContextLoaderPolicies = /* @__PURE__ */ new WeakMap();
1151
+ /**
1152
+ * Marks a context loader as the *effective* loader of a policy, i.e., one
1153
+ * that already applies what {@link wrapContextLoaderForJsonLd} (`"standard"`)
1154
+ * or {@link getNormalizationContextLoader} (`"normalization"`) would add.
1155
+ * Those functions return a marked loader as is instead of wrapping it again,
1156
+ * so that a caller can observe every response a parse actually uses,
1157
+ * including Fedify's own baseline contexts.
1158
+ * @param loader The effective context loader.
1159
+ * @param policy The policy the loader already applies.
1160
+ * @returns The given loader.
1161
+ * @internal
1162
+ */
1163
+ function markEffectiveContextLoader(loader, policy) {
1164
+ effectiveContextLoaderPolicies.set(loader, policy);
1165
+ return loader;
1166
+ }
1149
1167
  /** @internal */
1150
1168
  function wrapContextLoaderForJsonLd(contextLoader) {
1169
+ if (contextLoader != null && effectiveContextLoaderPolicies.has(contextLoader)) return contextLoader;
1151
1170
  const loader = contextLoader ?? builtInContextLoader;
1152
1171
  return async (url, options) => {
1153
1172
  try {
@@ -1161,6 +1180,7 @@ function wrapContextLoaderForJsonLd(contextLoader) {
1161
1180
  }
1162
1181
  /** @internal */
1163
1182
  function getNormalizationContextLoader(contextLoader) {
1183
+ if (contextLoader != null && effectiveContextLoaderPolicies.get(contextLoader) === "normalization") return contextLoader;
1164
1184
  const loader = wrapContextLoaderForJsonLd(contextLoader);
1165
1185
  return createMemoizedDocumentLoader(async (url, options) => {
1166
1186
  if (URL.canParse(url)) {
@@ -1613,10 +1633,21 @@ function getLdSignatureObject(jsonLd) {
1613
1633
  * @returns `true` if the document is authentic; `false` otherwise.
1614
1634
  */
1615
1635
  async function verifyJsonLd(jsonLd, options = {}) {
1616
- return await verifyJsonLdInternal(jsonLd, options, true);
1636
+ return (await verifyJsonLdInternal(jsonLd, options, true)).verified;
1617
1637
  }
1618
1638
  /** @internal */
1619
1639
  async function verifyCompactJsonLd(jsonLd, options = {}) {
1640
+ return (await verifyJsonLdInternal(jsonLd, options, false)).verified;
1641
+ }
1642
+ /**
1643
+ * Same as {@link verifyCompactJsonLd}, but also returns the key that verified
1644
+ * the signature, whose `owner` {@link fetchKey} has confirmed.
1645
+ * @param jsonLd The compacted JSON-LD document to verify.
1646
+ * @param options Options for verifying the JSON-LD document.
1647
+ * @returns The result.
1648
+ * @internal
1649
+ */
1650
+ async function verifyCompactJsonLdDetailed(jsonLd, options = {}) {
1620
1651
  return await verifyJsonLdInternal(jsonLd, options, false);
1621
1652
  }
1622
1653
  async function verifyJsonLdInternal(jsonLd, options, compact) {
@@ -1626,7 +1657,7 @@ async function verifyJsonLdInternal(jsonLd, options, compact) {
1626
1657
  }, "linkedData", (observation) => verifyJsonLdInternal(jsonLd, {
1627
1658
  ...options,
1628
1659
  [verificationObservation]: observation
1629
- }, compact), (value) => value);
1660
+ }, compact), (value) => value.verified);
1630
1661
  const observation = options[verificationObservation];
1631
1662
  return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("ld_signatures.verify", async (span) => {
1632
1663
  const start = performance.now();
@@ -1658,12 +1689,18 @@ async function verifyJsonLdInternal(jsonLd, options, compact) {
1658
1689
  const key = await verifySignature(compacted, verificationOptions);
1659
1690
  if (key == null) {
1660
1691
  if (observation?.attempt != null && !hasLdSignatureProperty(jsonLd)) observation.attempt.reason = { type: "noSignature" };
1661
- return false;
1692
+ return {
1693
+ verified: false,
1694
+ key: null
1695
+ };
1662
1696
  }
1663
1697
  if (key.ownerId == null) {
1664
1698
  if (observation?.attempt != null) observation.attempt.reason = { type: "missingOwner" };
1665
1699
  logger.debug("Key {keyId} has no owner.", { keyId: key.id?.href });
1666
- return false;
1700
+ return {
1701
+ verified: false,
1702
+ key: null
1703
+ };
1667
1704
  }
1668
1705
  attributions.delete(key.ownerId.href);
1669
1706
  if (attributions.size > 0) {
@@ -1672,10 +1709,16 @@ async function verifyJsonLdInternal(jsonLd, options, compact) {
1672
1709
  attributionIds: [...attributions].map((id) => new URL(id))
1673
1710
  };
1674
1711
  logger.debug("Some attributions are not authenticated by the Linked Data Signatures: {attributions}.", { attributions: [...attributions] });
1675
- return false;
1712
+ return {
1713
+ verified: false,
1714
+ key: null
1715
+ };
1676
1716
  }
1677
1717
  verified = true;
1678
- return true;
1718
+ return {
1719
+ verified: true,
1720
+ key
1721
+ };
1679
1722
  } catch (error) {
1680
1723
  threw = true;
1681
1724
  span.setStatus({
@@ -1706,4 +1749,4 @@ async function hashJsonLd(jsonLd, contextLoader) {
1706
1749
  return encodeHex(await crypto.subtle.digest("SHA-256", encoder.encode(canon)));
1707
1750
  }
1708
1751
  //#endregion
1709
- export { generateCryptoKeyPair as C, verifyKeyOwnership as E, fetchKeyDetailed as S, validateCryptoKey as T, wrapContextLoaderForJsonLd as _, compactJsonLd as a, fetchActorDocument as b, getNormalizationContextLoader as c, isClearlyMalformedContextReference as d, isInvalidUrlTypeError as f, verifySignature as g, verifyJsonLd as h, attachSignature as i, hasSignature as l, verifyCompactJsonLd as m, UnsafeJsonLdError as n, createSignature as o, signJsonLd as p, assertSafeJsonLd as r, detachSignature as s, InvalidContextReferenceError as t, hasSignatureLike as u, bypassKeyCacheReads as v, importJwk as w, fetchKey as x, exportJwk as y };
1752
+ export { fetchKey as C, validateCryptoKey as D, importJwk as E, verifyKeyOwnership as O, fetchActorDocument as S, generateCryptoKeyPair as T, verifyJsonLd as _, compactJsonLd as a, bypassKeyCacheReads as b, getNormalizationContextLoader as c, isClearlyMalformedContextReference as d, isInvalidUrlTypeError as f, verifyCompactJsonLdDetailed as g, verifyCompactJsonLd as h, attachSignature as i, hasSignature as l, signJsonLd as m, UnsafeJsonLdError as n, createSignature as o, markEffectiveContextLoader as p, assertSafeJsonLd as r, detachSignature as s, InvalidContextReferenceError as t, hasSignatureLike as u, verifySignature as v, fetchKeyDetailed as w, exportJwk as x, wrapContextLoaderForJsonLd as y };