@fedify/fedify 2.4.0-dev.2256 → 2.4.0-dev.2260

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 (105) hide show
  1. package/dist/{builder-DvSQqk2l.mjs → builder-BK8HfSUm.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-5bg_j_ye.mjs → compound-proof-D--HeIbV.mjs} +41 -4
  6. package/dist/{context-CtI307_A.d.ts → context-7NyiqIcg.d.ts} +111 -2
  7. package/dist/{context-B6X-t8gI.d.cts → context-tsYPJ2Ew.d.cts} +111 -2
  8. package/dist/{deno-AmcRMq5z.mjs → deno-DVP7DGJC.mjs} +1 -1
  9. package/dist/{docloader-CIP_SG39.mjs → docloader-Dv1JJl37.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 +76 -5
  14. package/dist/federation/hashlink-media.test.mjs +2 -2
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/inbox-report.test.d.mts +2 -0
  17. package/dist/federation/inbox-report.test.mjs +475 -0
  18. package/dist/federation/metrics.test.mjs +1 -1
  19. package/dist/federation/middleware.test.mjs +7 -7
  20. package/dist/federation/mod.cjs +1 -1
  21. package/dist/federation/mod.d.cts +4 -3
  22. package/dist/federation/mod.d.ts +4 -3
  23. package/dist/federation/mod.js +1 -1
  24. package/dist/federation/outgoing-proof.test.mjs +54 -10
  25. package/dist/federation/portable-actor.test.mjs +3 -3
  26. package/dist/federation/portable-chain.test.mjs +3 -3
  27. package/dist/federation/portable-collection.test.mjs +66 -4
  28. package/dist/federation/portable-delivery.test.mjs +2 -2
  29. package/dist/federation/portable-gateway-keys.test.mjs +4 -4
  30. package/dist/federation/portable-inbox.test.mjs +67 -6
  31. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  32. package/dist/federation/portable-route-activity.test.mjs +2 -2
  33. package/dist/federation/portable.test.mjs +3 -3
  34. package/dist/federation/send.test.mjs +3 -3
  35. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  36. package/dist/federation/tasks/tasks.test.mjs +2 -2
  37. package/dist/federation/temporal.test.mjs +1 -1
  38. package/dist/federation/webfinger.test.mjs +1 -1
  39. package/dist/{federation-BDzIgqOd.cjs → federation-Bo5z395X.cjs} +552 -122
  40. package/dist/{http-1fZ83jj7.cjs → http-BIhWdDf5.cjs} +447 -110
  41. package/dist/{http-BGb3QioZ.js → http-DRMu1C2T.js} +430 -111
  42. package/dist/{http-DkqWu-sm.mjs → http-DvpuyDJB.mjs} +141 -98
  43. package/dist/{key-CEsu7oaD.mjs → key-B7N8mNoY.mjs} +30 -3
  44. package/dist/{http-BrwKINQn.d.cts → key-C1nv7_jD.d.ts} +638 -229
  45. package/dist/{http-DGx0KOws.d.ts → key-DYxuyd-s.d.cts} +638 -229
  46. package/dist/{kv-cache-CJS4mImS.mjs → kv-cache-D0kA0rml.mjs} +1 -1
  47. package/dist/{kv-cache-CGhEgt98.js → kv-cache-DZLpqhW9.js} +1 -1
  48. package/dist/{kv-cache-BEWuZSB4.cjs → kv-cache-mF8Zmjix.cjs} +1 -1
  49. package/dist/{ld-Cf4CPWN_.mjs → ld-BDMNyM2M.mjs} +40 -5
  50. package/dist/{metrics-CocsZP0E.mjs → metrics-BbBj-VZN.mjs} +2 -1
  51. package/dist/{middleware-Cne_jMZo.mjs → middleware-CE1X7-UM.mjs} +1 -1
  52. package/dist/{middleware-Dxn0E9ES.mjs → middleware-DAA7wDdS.mjs} +520 -131
  53. package/dist/{middleware-BfYke3YQ.js → middleware-DfUWPAa-.js} +552 -122
  54. package/dist/{mod-C7_2j7VT.d.cts → mod-BojP2Bw3.d.cts} +1 -1
  55. package/dist/{mod-BlLwewYe.d.ts → mod-CavhAgLw.d.cts} +5 -255
  56. package/dist/{mod-VHYh1lTK.d.cts → mod-CtpF2BU5.d.ts} +5 -255
  57. package/dist/{mod-B8MI1mpi.d.ts → mod-DEZXwV6J.d.ts} +1 -1
  58. package/dist/{mod-DFL6IxYQ.d.cts → mod-Dw-nPxQh.d.cts} +1 -1
  59. package/dist/{mod-BhTaDR5H.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
  60. package/dist/mod.cjs +4 -4
  61. package/dist/mod.d.cts +7 -7
  62. package/dist/mod.d.ts +7 -7
  63. package/dist/mod.js +4 -4
  64. package/dist/nodeinfo/handler.test.mjs +1 -1
  65. package/dist/{owner-DSJ7nFgy.mjs → owner-CzUycuhd.mjs} +3 -3
  66. package/dist/{portable-collection-BuBV6xTo.js → portable-collection-COcJxZg_.js} +1 -1
  67. package/dist/{portable-collection-qskqXJrH.mjs → portable-collection-CTcNJiAa.mjs} +2 -2
  68. package/dist/{portable-collection-r-mHg1PV.cjs → portable-collection-cPMffcHJ.cjs} +1 -1
  69. package/dist/{portable-key-woGH_1nn.mjs → portable-key-B4QN3OzR.mjs} +1 -1
  70. package/dist/{proof-8WiH_-Gq.mjs → proof-nce4pf5S.mjs} +133 -16
  71. package/dist/{send-JKzZBgPU.mjs → send-4Idpe2z0.mjs} +3 -3
  72. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  73. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  74. package/dist/sig/compound-proof-verification.test.mjs +19 -2
  75. package/dist/sig/compound-proof.test.mjs +2 -2
  76. package/dist/sig/http.test.mjs +2 -2
  77. package/dist/sig/key.test.mjs +1 -1
  78. package/dist/sig/ld.test.mjs +2 -2
  79. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  80. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  81. package/dist/sig/mod.cjs +2 -2
  82. package/dist/sig/mod.d.cts +2 -2
  83. package/dist/sig/mod.d.ts +2 -2
  84. package/dist/sig/mod.js +2 -2
  85. package/dist/sig/owner.test.mjs +1 -1
  86. package/dist/sig/portable-collection.test.mjs +1 -1
  87. package/dist/sig/portable-interop.test.mjs +3 -3
  88. package/dist/sig/portable-key.test.mjs +6 -6
  89. package/dist/sig/proof.test.mjs +76 -3
  90. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  91. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  92. package/dist/sig/verification.test.d.mts +2 -0
  93. package/dist/sig/verification.test.mjs +310 -0
  94. package/dist/{temporal-CT3fRaMH.mjs → temporal-BbjmPHYn.mjs} +1 -1
  95. package/dist/testing/kv.test.mjs +1 -1
  96. package/dist/testing/mod.d.mts +315 -28
  97. package/dist/utils/docloader.test.mjs +2 -2
  98. package/dist/utils/kv-cache.test.mjs +1 -1
  99. package/dist/utils/mod.cjs +1 -1
  100. package/dist/utils/mod.d.cts +1 -1
  101. package/dist/utils/mod.d.ts +1 -1
  102. package/dist/utils/mod.js +1 -1
  103. package/dist/verification-C3954hQh.mjs +5 -0
  104. package/dist/verification-Dpfcw8ml.mjs +105 -0
  105. package/package.json +7 -7
@@ -2,7 +2,7 @@ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { i as getPortableDid, l as isPortableId, n as getCanonicalPortableId } from "./portable-key-id-Ca30-hOa.mjs";
5
- import { n as version, t as name } from "./deno-AmcRMq5z.mjs";
5
+ import { n as version, t as name } from "./deno-DVP7DGJC.mjs";
6
6
  import { t as ActivityListenerSet } from "./activity-listener-C-BSbKI7.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { Router, RouterError, assertPath } from "@fedify/uri-template";
@@ -55,6 +55,7 @@ var FederationBuilderImpl = class {
55
55
  featuredTagsCallbacks;
56
56
  inboxListeners;
57
57
  outboxListeners;
58
+ inboxRequestFinishedHandler;
58
59
  inboxErrorHandler;
59
60
  outboxListenerErrorHandler;
60
61
  outboxAuthorizePredicate;
@@ -90,7 +91,7 @@ var FederationBuilderImpl = class {
90
91
  * specified.
91
92
  */
92
93
  async build(options) {
93
- const { FederationImpl } = await import("./middleware-Cne_jMZo.mjs");
94
+ const { FederationImpl } = await import("./middleware-CE1X7-UM.mjs");
94
95
  const f = new FederationImpl(options);
95
96
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
96
97
  f.router = this.router.clone();
@@ -116,6 +117,7 @@ var FederationBuilderImpl = class {
116
117
  f.featuredTagsCallbacks = this.featuredTagsCallbacks == null ? void 0 : { ...this.featuredTagsCallbacks };
117
118
  f.inboxListeners = this.inboxListeners?.clone();
118
119
  f.outboxListeners = this.outboxListeners?.clone();
120
+ f.inboxRequestFinishedHandler = this.inboxRequestFinishedHandler;
119
121
  f.inboxErrorHandler = this.inboxErrorHandler;
120
122
  f.outboxListenerErrorHandler = this.outboxListenerErrorHandler;
121
123
  f.outboxAuthorizePredicate = this.outboxAuthorizePredicate;
@@ -596,6 +598,10 @@ var FederationBuilderImpl = class {
596
598
  this.inboxErrorHandler = handler;
597
599
  return setters;
598
600
  },
601
+ onRequestFinished: (handler) => {
602
+ this.inboxRequestFinishedHandler = handler;
603
+ return setters;
604
+ },
599
605
  onUnverifiedActivity: (handler) => {
600
606
  this.unverifiedActivityHandler = handler;
601
607
  return setters;
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { en as ActivityTransformer, n as Context } from "../context-B6X-t8gI.cjs";
2
+ import { en as ActivityTransformer, n as Context } from "../context-tsYPJ2Ew.cjs";
3
3
  import { Activity } from "@fedify/vocab";
4
4
 
5
5
  //#region src/compat/transformers.d.ts
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { en as ActivityTransformer, n as Context } from "../context-CtI307_A.js";
2
+ import { en as ActivityTransformer, n as Context } from "../context-7NyiqIcg.js";
3
3
  import { Activity } from "@fedify/vocab";
4
4
 
5
5
  //#region src/compat/transformers.d.ts
@@ -5,7 +5,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
7
7
  import { t as assert } from "../assert-D1Eafmd_.mjs";
8
- import { O as actorDehydrator, k as autoIdAssigner, n as FederationImpl } from "../middleware-Dxn0E9ES.mjs";
8
+ import { O as actorDehydrator, k as autoIdAssigner, n as FederationImpl } from "../middleware-DAA7wDdS.mjs";
9
9
  import { Follow, Person } from "@fedify/vocab";
10
10
  import { test } from "@fedify/fixture";
11
11
  //#region src/compat/transformers.test.ts
@@ -2,9 +2,10 @@ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { a as getRawCanonicalPortableId, l as isPortableId, n as getCanonicalPortableId } from "./portable-key-id-Ca30-hOa.mjs";
5
+ import { a as verificationObservation, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
5
6
  import { n as preloadedOnlyDocumentLoader } from "./public-audience--klBIOk1.mjs";
6
- import { c as getNormalizationContextLoader } from "./ld-Cf4CPWN_.mjs";
7
- import { l as verifyPortableObjectProofPolicy, n as classifyFep2277CoreType, o as verifyMapLocalProof } from "./proof-8WiH_-Gq.mjs";
7
+ import { c as getNormalizationContextLoader } from "./ld-BDMNyM2M.mjs";
8
+ import { l as verifyPortableObjectProofPolicy, n as classifyFep2277CoreType, o as verifyMapLocalProof } from "./proof-nce4pf5S.mjs";
8
9
  import { parseIri, preloadedContexts } from "@fedify/vocab-runtime";
9
10
  import jsonld from "@fedify/vocab-runtime/jsonld";
10
11
  //#region src/sig/compound-proof.ts
@@ -441,6 +442,26 @@ function findUnsupportedCompoundProofShape(json) {
441
442
  return findInJsonMaps(json, (map, path) => Object.hasOwn(map, "proof") && !isJsonMap(map.proof) ? childPath(path, "proof") : void 0) ?? null;
442
443
  }
443
444
  /**
445
+ * Finds the first embedded map that carries a direct `proof` but not its own
446
+ * `@context`. The map-local compound-proof profile verifies each proof-bearing
447
+ * map as a self-contained document, so the inbox rejects such a map with
448
+ * `missingContext` whatever its proof. An object embedded with its captured
449
+ * signed representation, e.g., by {@link signObject}, always keeps its own
450
+ * `@context`; one rebuilt under its parent's context, e.g., an object parsed
451
+ * from a received document, never does, and its proof no longer covers what
452
+ * it has become anyway.
453
+ *
454
+ * The input must be a finite, already-materialized JSON tree; see
455
+ * {@link containsCompoundPortableObject}.
456
+ *
457
+ * @returns The RFC 6901 JSON Pointer to the map, or `null` if every embedded
458
+ * proof-bearing map has its own `@context`.
459
+ * @internal
460
+ */
461
+ function findEmbeddedProofWithoutContext(json) {
462
+ return findInJsonMaps(json, (map, path) => path !== "" && Object.hasOwn(map, "proof") && !Object.hasOwn(map, "@context") ? path : void 0) ?? null;
463
+ }
464
+ /**
444
465
  * Discovers direct literal proof-bearing maps in an immutable JSON snapshot.
445
466
  *
446
467
  * Discovery is bounded and atomic. Unsupported proof shapes, non-JSON input,
@@ -517,7 +538,23 @@ async function verifyDiscoveredProofDocuments(discovered, options) {
517
538
  verified: false,
518
539
  reason: { type: "missingContext" }
519
540
  });
520
- const key = await verifyMapLocalProof(document.securedDocument, options);
541
+ const inherited = options[verificationObservation];
542
+ let subjectId = null;
543
+ if (inherited != null && document.id != null) try {
544
+ subjectId = parseIri(document.id);
545
+ } catch {}
546
+ const key = await observeAttempt({ [verificationObservation]: inherited == null ? void 0 : {
547
+ ...inherited,
548
+ attempt: void 0,
549
+ check: void 0,
550
+ subject: {
551
+ id: subjectId,
552
+ pointer: document.path
553
+ }
554
+ } }, "objectIntegrity", (observation) => verifyMapLocalProof(document.securedDocument, {
555
+ ...options,
556
+ [verificationObservation]: inherited == null ? void 0 : observation
557
+ }), (key) => key != null);
521
558
  return key == null ? Object.freeze({
522
559
  ...metadata,
523
560
  verified: false,
@@ -949,4 +986,4 @@ async function checkServedPortableMap({ map, contexts }, options) {
949
986
  return UNSECURED_CORE_TYPES.has(type) ? null : "missingProof";
950
987
  }
951
988
  //#endregion
952
- export { verifyCompoundPortableObjectProofs as a, inspectCompoundPortableObjectApplicability as i, discoverCompoundProofDocuments as n, verifyCompoundProofDocuments as o, findUnsupportedCompoundProofShape as r, verifyServedPortableObjects as s, containsCompoundPortableObject as t };
989
+ export { inspectCompoundPortableObjectApplicability as a, verifyServedPortableObjects as c, findUnsupportedCompoundProofShape as i, discoverCompoundProofDocuments as n, verifyCompoundPortableObjectProofs as o, findEmbeddedProofWithoutContext as r, verifyCompoundProofDocuments as s, containsCompoundPortableObject as t };
@@ -1,6 +1,6 @@
1
1
  /// <reference lib="esnext.temporal" />
2
2
  import { o as JsonValue, s as NodeInfo, t as GetNodeInfoOptions } from "./client-D-XP_Ccb.js";
3
- import { o as VerifyRequestFailureReason, t as HttpMessageSignaturesSpec } from "./http-DGx0KOws.js";
3
+ import { _ as InboxVerificationKey, d as InboxProofPolicyFailureReason, h as InboxVerificationAttempt, w as VerifyRequestFailureReason, y as HttpMessageSignaturesSpec } from "./key-C1nv7_jD.js";
4
4
  import { n as GetKeyOwnerOptions } from "./owner-BSzr5-I8.js";
5
5
  import { n as KvStore, t as KvKey } from "./kv-B2mzK5tl.js";
6
6
  import { Router } from "@fedify/uri-template";
@@ -11,6 +11,105 @@ import { Link as Link$1, LookupWebFingerOptions, ResourceDescriptor } from "@fed
11
11
  import { MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
12
12
  import { StandardSchemaV1 } from "@standard-schema/spec";
13
13
 
14
+ //#region src/federation/inbox-report.d.ts
15
+ /**
16
+ * A JSON value retained from the incoming request.
17
+ * @since 2.4.0
18
+ */
19
+ type InboxJsonValue = null | boolean | number | string | readonly InboxJsonValue[] | {
20
+ readonly [key: string]: InboxJsonValue;
21
+ };
22
+ /**
23
+ * The final inbox authentication decision.
24
+ * @since 2.4.0
25
+ */
26
+ type InboxAuthentication = {
27
+ readonly status: "verified"; /** Direct references to the attempts that authenticated this delivery. */
28
+ readonly attempts: readonly Extract<InboxVerificationAttempt, {
29
+ status: "verified";
30
+ }>[];
31
+ } | {
32
+ readonly status: "rejected";
33
+ readonly reason: {
34
+ readonly type: "verificationFailed";
35
+ } | {
36
+ readonly type: "actorKeyMismatch";
37
+ readonly key: InboxVerificationKey;
38
+ readonly actorIds: readonly URL[];
39
+ } | {
40
+ readonly type: "proofPolicy";
41
+ readonly policy: "portableActor" | "portableActivity" | "compound";
42
+ readonly detail?: InboxProofPolicyFailureReason;
43
+ } | {
44
+ readonly type: "invalidNonce";
45
+ };
46
+ } | {
47
+ readonly status: "skipped";
48
+ } | {
49
+ readonly status: "notDetermined";
50
+ };
51
+ /**
52
+ * How an inbox request finished.
53
+ * @since 2.4.0
54
+ */
55
+ type InboxRequestOutcome = {
56
+ readonly type: "response";
57
+ readonly status: number;
58
+ readonly disposition: "processed" | "enqueued" | "duplicate" | "unhandled" | "customResponse";
59
+ } | {
60
+ readonly type: "response";
61
+ readonly status: number;
62
+ readonly disposition: "rejected";
63
+ readonly reason: "recipientNotFound" | "invalidJson" | "invalidJsonLd" | "invalidActivity" | "bodyTooLarge" | "missingActor" | "authentication";
64
+ } | {
65
+ readonly type: "response";
66
+ readonly status: number;
67
+ readonly disposition: "failed";
68
+ readonly reason: "bodyUnavailable" | "enqueueError" | "listenerError";
69
+ readonly error?: unknown;
70
+ } | {
71
+ readonly type: "exception";
72
+ readonly stage: "prepare" | "parse" | "verify" | "policy" | "dispatch" | "respond"; /** The original thrown value, including null/undefined. */
73
+ readonly error: unknown;
74
+ };
75
+ /**
76
+ * Observations of a single inbox delivery request.
77
+ * @since 2.4.0
78
+ */
79
+ interface InboxRequestReport {
80
+ /** The delivery route, including a portable gateway route. */
81
+ readonly inbox: {
82
+ readonly kind: "personal" | "shared" | "portable";
83
+ readonly recipient: string | null;
84
+ };
85
+ /** Unavailable differs from a successfully parsed JSON null. */
86
+ readonly payload: {
87
+ readonly status: "unavailable";
88
+ } | {
89
+ readonly status: "parsed";
90
+ readonly value: InboxJsonValue;
91
+ };
92
+ /** The Activity obtained by the normal processing flow, if any. */
93
+ readonly activity: Activity | null;
94
+ /** Logical evaluations, including unsuccessful fallback paths. */
95
+ readonly attempts: readonly InboxVerificationAttempt[];
96
+ /** Cryptographic success alone does not imply this decision is verified. */
97
+ readonly authentication: InboxAuthentication;
98
+ /** Response status snapshot or the original exception. */
99
+ readonly outcome: InboxRequestOutcome;
100
+ }
101
+ /**
102
+ * Observes an inbox delivery after processing, before fetch resolves/rethrows.
103
+ * The callback is awaited. Its errors are logged and swallowed, preserving
104
+ * the original response/exception. The last registered callback replaces the
105
+ * previous one. It excludes inbox collection GETs, unmatched routes,
106
+ * programmatic routing, and queue workers. This is not an atomic persistence
107
+ * guarantee. Keys/owner/controller declarations remain untrusted unless the
108
+ * final authentication decision verifies them.
109
+ * @since 2.4.0
110
+ */
111
+ type InboxRequestFinishedHandler<TContextData> = (context: RequestContext<TContextData>, report: InboxRequestReport) => void | Promise<void>;
112
+ //#endregion
14
113
  //#region src/compat/types.d.ts
15
114
  /**
16
115
  * A function that transforms an activity object.
@@ -2790,6 +2889,16 @@ interface InboxListenerSetters<TContextData> {
2790
2889
  */
2791
2890
  onError(handler: InboxErrorHandler<TContextData>): InboxListenerSetters<TContextData>;
2792
2891
  /**
2892
+ * Observes each configured inbox delivery once, before fetch finishes.
2893
+ * The callback is awaited and its errors do not alter delivery processing.
2894
+ * Calling this again replaces the previous callback. Built federations
2895
+ * retain the callback registered at build time.
2896
+ * @param handler The request completion observer.
2897
+ * @returns This setter for chaining.
2898
+ * @since 2.4.0
2899
+ */
2900
+ onRequestFinished(handler: InboxRequestFinishedHandler<TContextData>): InboxListenerSetters<TContextData>;
2901
+ /**
2793
2902
  * Registers a callback for incoming activities whose HTTP signatures could
2794
2903
  * not be verified.
2795
2904
  *
@@ -4716,4 +4825,4 @@ interface ActorKeyPair extends CryptoKeyPair {
4716
4825
  readonly multikey: Multikey;
4717
4826
  }
4718
4827
  //#endregion
4719
- export { respondWithObject as $, digest as $t, InboxChallengePolicy as A, CustomCollectionDispatcher as At, createFederation as B, OutboxListener as Bt, FederationBenchmarkOptions as C, ActorKeyPairsDispatcher as Ct, FederationStartQueueOptions as D, CollectionDispatcher as Dt, FederationOptions as E, CollectionCursor as Et, PortableInboxForwardingOptions as F, MediaUploaderCallback as Ft, TaskEnqueueOptions as G, SharedInboxKeyDispatcher as Gt, createFederationBuilder as H, OutboxPermanentFailureHandler as Ht, Rfc6570Expression as I, NodeInfoDispatcher as It, CreateExponentialBackoffPolicyOptions as J, WebFingerLinksDispatcher as Jt, TaskHandler as K, UnverifiedActivityHandler as Kt, FederationKvPrefixes as L, ObjectAuthorizePredicate as Lt, MediaUploaderSetters as M, HashlinkMediaRequest as Mt, ObjectCallbackSetters as N, InboxErrorHandler as Nt, IdempotencyKeyCallback as O, CustomCollectionCounter as Ot, OutboxListenerSetters as P, InboxListener as Pt, RespondWithObjectOptions as Q, buildCollectionSynchronizationHeader as Qt, FederationOrigin as R, ObjectDispatcher as Rt, Federation as S, ActorHandleMapper as St, FederationFetchOptions as T, CollectionCounter as Tt, TaskDefinition as U, PortableActorIdMapper as Ut, Message as V, OutboxListenerErrorHandler as Vt, TaskDefinitionOptions as W, PortableCollectionOwnerMapper as Wt, RetryPolicy as X, SenderKeyPair as Xt, RetryContext as Y, SendActivityError as Yt, createExponentialBackoffPolicy as Z, PageItems as Zt, ActorCallbackSetters as _, NormalizedCircuitBreakerOptions as _t, GetObjectOptions as a, MessageQueueEnqueueOptions as at, CustomCollectionCallbackSetters as b, ActorAliasMapper as bt, IsSignedByAudienceOptions as c, CircuitBreaker as ct, ParseUriResult as d, CircuitBreakerCreateOptions as dt, ActivityTransformer as en, respondWithObjectIfAcceptable as et, PortableRequest as f, CircuitBreakerFailurePolicy as ft, SendActivityOptionsForCollection as g, CircuitBreakerStateChange as gt, SendActivityOptions as h, CircuitBreakerState as ht, GetActorOptions as i, MessageQueueDepth as it, InboxListenerSetters as j, HashlinkMediaDispatcher as jt, IdempotencyStrategy as k, CustomCollectionCursor as kt, OutboxContext as l, CircuitBreakerActivityDrop as lt, RouteActivityOptions as m, CircuitBreakerOptions as mt, Context as n, InProcessMessageQueueOptions as nt, GetSignedKeyOptions as o, MessageQueueListenOptions as ot, RequestContext as p, CircuitBreakerKvState as pt, TaskRegistry as q, UnverifiedActivityReason as qt, ForwardActivityOptions as r, MessageQueue as rt, InboxContext as s, ParallelMessageQueue as st, ActorKeyPair as t, InProcessMessageQueue as tt, ParseUriOptions as u, CircuitBreakerBeforeSendDecision as ut, CollectionCallbackSetters as v, normalizeCircuitBreakerOptions as vt, FederationBuilder as w, AuthorizePredicate as wt, Federatable as x, ActorDispatcher as xt, ConstructorWithTypeId as y, parseCircuitBreakerKvState as yt, FederationQueueOptions as z, OutboxErrorHandler as zt };
4828
+ export { respondWithObject as $, digest as $t, InboxChallengePolicy as A, CustomCollectionDispatcher as At, createFederation as B, OutboxListener as Bt, FederationBenchmarkOptions as C, ActorKeyPairsDispatcher as Ct, FederationStartQueueOptions as D, CollectionDispatcher as Dt, FederationOptions as E, CollectionCursor as Et, PortableInboxForwardingOptions as F, MediaUploaderCallback as Ft, TaskEnqueueOptions as G, SharedInboxKeyDispatcher as Gt, createFederationBuilder as H, OutboxPermanentFailureHandler as Ht, Rfc6570Expression as I, NodeInfoDispatcher as It, CreateExponentialBackoffPolicyOptions as J, WebFingerLinksDispatcher as Jt, TaskHandler as K, UnverifiedActivityHandler as Kt, FederationKvPrefixes as L, ObjectAuthorizePredicate as Lt, MediaUploaderSetters as M, HashlinkMediaRequest as Mt, ObjectCallbackSetters as N, InboxErrorHandler as Nt, IdempotencyKeyCallback as O, CustomCollectionCounter as Ot, OutboxListenerSetters as P, InboxListener as Pt, RespondWithObjectOptions as Q, buildCollectionSynchronizationHeader as Qt, FederationOrigin as R, ObjectDispatcher as Rt, Federation as S, ActorHandleMapper as St, FederationFetchOptions as T, CollectionCounter as Tt, TaskDefinition as U, PortableActorIdMapper as Ut, Message as V, OutboxListenerErrorHandler as Vt, TaskDefinitionOptions as W, PortableCollectionOwnerMapper as Wt, RetryPolicy as X, SenderKeyPair as Xt, RetryContext as Y, SendActivityError as Yt, createExponentialBackoffPolicy as Z, PageItems as Zt, ActorCallbackSetters as _, NormalizedCircuitBreakerOptions as _t, GetObjectOptions as a, InboxRequestReport as an, MessageQueueEnqueueOptions as at, CustomCollectionCallbackSetters as b, ActorAliasMapper as bt, IsSignedByAudienceOptions as c, CircuitBreaker as ct, ParseUriResult as d, CircuitBreakerCreateOptions as dt, ActivityTransformer as en, respondWithObjectIfAcceptable as et, PortableRequest as f, CircuitBreakerFailurePolicy as ft, SendActivityOptionsForCollection as g, CircuitBreakerStateChange as gt, SendActivityOptions as h, CircuitBreakerState as ht, GetActorOptions as i, InboxRequestOutcome as in, MessageQueueDepth as it, InboxListenerSetters as j, HashlinkMediaDispatcher as jt, IdempotencyStrategy as k, CustomCollectionCursor as kt, OutboxContext as l, CircuitBreakerActivityDrop as lt, RouteActivityOptions as m, CircuitBreakerOptions as mt, Context as n, InboxJsonValue as nn, InProcessMessageQueueOptions as nt, GetSignedKeyOptions as o, MessageQueueListenOptions as ot, RequestContext as p, CircuitBreakerKvState as pt, TaskRegistry as q, UnverifiedActivityReason as qt, ForwardActivityOptions as r, InboxRequestFinishedHandler as rn, MessageQueue as rt, InboxContext as s, ParallelMessageQueue as st, ActorKeyPair as t, InboxAuthentication as tn, InProcessMessageQueue as tt, ParseUriOptions as u, CircuitBreakerBeforeSendDecision as ut, CollectionCallbackSetters as v, normalizeCircuitBreakerOptions as vt, FederationBuilder as w, AuthorizePredicate as wt, Federatable as x, ActorDispatcher as xt, ConstructorWithTypeId as y, parseCircuitBreakerKvState as yt, FederationQueueOptions as z, OutboxErrorHandler as zt };
@@ -1,6 +1,6 @@
1
1
  /// <reference lib="esnext.temporal" />
2
2
  import { o as JsonValue, s as NodeInfo, t as GetNodeInfoOptions } from "./client-D-XP_Ccb.cjs";
3
- import { o as VerifyRequestFailureReason, t as HttpMessageSignaturesSpec } from "./http-BrwKINQn.cjs";
3
+ import { _ as InboxVerificationKey, d as InboxProofPolicyFailureReason, h as InboxVerificationAttempt, w as VerifyRequestFailureReason, y as HttpMessageSignaturesSpec } from "./key-DYxuyd-s.cjs";
4
4
  import { n as GetKeyOwnerOptions } from "./owner-BF0L9cMt.cjs";
5
5
  import { n as KvStore, t as KvKey } from "./kv-B2mzK5tl.cjs";
6
6
  import { Activity, Actor, Collection, CryptographicKey, Hashtag, Link, LookupObjectOptions, Multikey, Object as Object$1, Recipient, Tombstone, TraverseCollectionOptions } from "@fedify/vocab";
@@ -9,6 +9,105 @@ import { Link as Link$1, LookupWebFingerOptions, ResourceDescriptor } from "@fed
9
9
  import { MeterProvider, Span, Tracer, TracerProvider } from "@opentelemetry/api";
10
10
  import { StandardSchemaV1 } from "@standard-schema/spec";
11
11
 
12
+ //#region src/federation/inbox-report.d.ts
13
+ /**
14
+ * A JSON value retained from the incoming request.
15
+ * @since 2.4.0
16
+ */
17
+ type InboxJsonValue = null | boolean | number | string | readonly InboxJsonValue[] | {
18
+ readonly [key: string]: InboxJsonValue;
19
+ };
20
+ /**
21
+ * The final inbox authentication decision.
22
+ * @since 2.4.0
23
+ */
24
+ type InboxAuthentication = {
25
+ readonly status: "verified"; /** Direct references to the attempts that authenticated this delivery. */
26
+ readonly attempts: readonly Extract<InboxVerificationAttempt, {
27
+ status: "verified";
28
+ }>[];
29
+ } | {
30
+ readonly status: "rejected";
31
+ readonly reason: {
32
+ readonly type: "verificationFailed";
33
+ } | {
34
+ readonly type: "actorKeyMismatch";
35
+ readonly key: InboxVerificationKey;
36
+ readonly actorIds: readonly URL[];
37
+ } | {
38
+ readonly type: "proofPolicy";
39
+ readonly policy: "portableActor" | "portableActivity" | "compound";
40
+ readonly detail?: InboxProofPolicyFailureReason;
41
+ } | {
42
+ readonly type: "invalidNonce";
43
+ };
44
+ } | {
45
+ readonly status: "skipped";
46
+ } | {
47
+ readonly status: "notDetermined";
48
+ };
49
+ /**
50
+ * How an inbox request finished.
51
+ * @since 2.4.0
52
+ */
53
+ type InboxRequestOutcome = {
54
+ readonly type: "response";
55
+ readonly status: number;
56
+ readonly disposition: "processed" | "enqueued" | "duplicate" | "unhandled" | "customResponse";
57
+ } | {
58
+ readonly type: "response";
59
+ readonly status: number;
60
+ readonly disposition: "rejected";
61
+ readonly reason: "recipientNotFound" | "invalidJson" | "invalidJsonLd" | "invalidActivity" | "bodyTooLarge" | "missingActor" | "authentication";
62
+ } | {
63
+ readonly type: "response";
64
+ readonly status: number;
65
+ readonly disposition: "failed";
66
+ readonly reason: "bodyUnavailable" | "enqueueError" | "listenerError";
67
+ readonly error?: unknown;
68
+ } | {
69
+ readonly type: "exception";
70
+ readonly stage: "prepare" | "parse" | "verify" | "policy" | "dispatch" | "respond"; /** The original thrown value, including null/undefined. */
71
+ readonly error: unknown;
72
+ };
73
+ /**
74
+ * Observations of a single inbox delivery request.
75
+ * @since 2.4.0
76
+ */
77
+ interface InboxRequestReport {
78
+ /** The delivery route, including a portable gateway route. */
79
+ readonly inbox: {
80
+ readonly kind: "personal" | "shared" | "portable";
81
+ readonly recipient: string | null;
82
+ };
83
+ /** Unavailable differs from a successfully parsed JSON null. */
84
+ readonly payload: {
85
+ readonly status: "unavailable";
86
+ } | {
87
+ readonly status: "parsed";
88
+ readonly value: InboxJsonValue;
89
+ };
90
+ /** The Activity obtained by the normal processing flow, if any. */
91
+ readonly activity: Activity | null;
92
+ /** Logical evaluations, including unsuccessful fallback paths. */
93
+ readonly attempts: readonly InboxVerificationAttempt[];
94
+ /** Cryptographic success alone does not imply this decision is verified. */
95
+ readonly authentication: InboxAuthentication;
96
+ /** Response status snapshot or the original exception. */
97
+ readonly outcome: InboxRequestOutcome;
98
+ }
99
+ /**
100
+ * Observes an inbox delivery after processing, before fetch resolves/rethrows.
101
+ * The callback is awaited. Its errors are logged and swallowed, preserving
102
+ * the original response/exception. The last registered callback replaces the
103
+ * previous one. It excludes inbox collection GETs, unmatched routes,
104
+ * programmatic routing, and queue workers. This is not an atomic persistence
105
+ * guarantee. Keys/owner/controller declarations remain untrusted unless the
106
+ * final authentication decision verifies them.
107
+ * @since 2.4.0
108
+ */
109
+ type InboxRequestFinishedHandler<TContextData> = (context: RequestContext<TContextData>, report: InboxRequestReport) => void | Promise<void>;
110
+ //#endregion
12
111
  //#region src/compat/types.d.ts
13
112
  /**
14
113
  * A function that transforms an activity object.
@@ -2788,6 +2887,16 @@ interface InboxListenerSetters<TContextData> {
2788
2887
  */
2789
2888
  onError(handler: InboxErrorHandler<TContextData>): InboxListenerSetters<TContextData>;
2790
2889
  /**
2890
+ * Observes each configured inbox delivery once, before fetch finishes.
2891
+ * The callback is awaited and its errors do not alter delivery processing.
2892
+ * Calling this again replaces the previous callback. Built federations
2893
+ * retain the callback registered at build time.
2894
+ * @param handler The request completion observer.
2895
+ * @returns This setter for chaining.
2896
+ * @since 2.4.0
2897
+ */
2898
+ onRequestFinished(handler: InboxRequestFinishedHandler<TContextData>): InboxListenerSetters<TContextData>;
2899
+ /**
2791
2900
  * Registers a callback for incoming activities whose HTTP signatures could
2792
2901
  * not be verified.
2793
2902
  *
@@ -4714,4 +4823,4 @@ interface ActorKeyPair extends CryptoKeyPair {
4714
4823
  readonly multikey: Multikey;
4715
4824
  }
4716
4825
  //#endregion
4717
- export { respondWithObject as $, digest as $t, InboxChallengePolicy as A, CustomCollectionDispatcher as At, createFederation as B, OutboxListener as Bt, FederationBenchmarkOptions as C, ActorKeyPairsDispatcher as Ct, FederationStartQueueOptions as D, CollectionDispatcher as Dt, FederationOptions as E, CollectionCursor as Et, PortableInboxForwardingOptions as F, MediaUploaderCallback as Ft, TaskEnqueueOptions as G, SharedInboxKeyDispatcher as Gt, createFederationBuilder as H, OutboxPermanentFailureHandler as Ht, Rfc6570Expression as I, NodeInfoDispatcher as It, CreateExponentialBackoffPolicyOptions as J, WebFingerLinksDispatcher as Jt, TaskHandler as K, UnverifiedActivityHandler as Kt, FederationKvPrefixes as L, ObjectAuthorizePredicate as Lt, MediaUploaderSetters as M, HashlinkMediaRequest as Mt, ObjectCallbackSetters as N, InboxErrorHandler as Nt, IdempotencyKeyCallback as O, CustomCollectionCounter as Ot, OutboxListenerSetters as P, InboxListener as Pt, RespondWithObjectOptions as Q, buildCollectionSynchronizationHeader as Qt, FederationOrigin as R, ObjectDispatcher as Rt, Federation as S, ActorHandleMapper as St, FederationFetchOptions as T, CollectionCounter as Tt, TaskDefinition as U, PortableActorIdMapper as Ut, Message as V, OutboxListenerErrorHandler as Vt, TaskDefinitionOptions as W, PortableCollectionOwnerMapper as Wt, RetryPolicy as X, SenderKeyPair as Xt, RetryContext as Y, SendActivityError as Yt, createExponentialBackoffPolicy as Z, PageItems as Zt, ActorCallbackSetters as _, NormalizedCircuitBreakerOptions as _t, GetObjectOptions as a, MessageQueueEnqueueOptions as at, CustomCollectionCallbackSetters as b, ActorAliasMapper as bt, IsSignedByAudienceOptions as c, CircuitBreaker as ct, ParseUriResult as d, CircuitBreakerCreateOptions as dt, ActivityTransformer as en, respondWithObjectIfAcceptable as et, PortableRequest as f, CircuitBreakerFailurePolicy as ft, SendActivityOptionsForCollection as g, CircuitBreakerStateChange as gt, SendActivityOptions as h, CircuitBreakerState as ht, GetActorOptions as i, MessageQueueDepth as it, InboxListenerSetters as j, HashlinkMediaDispatcher as jt, IdempotencyStrategy as k, CustomCollectionCursor as kt, OutboxContext as l, CircuitBreakerActivityDrop as lt, RouteActivityOptions as m, CircuitBreakerOptions as mt, Context as n, InProcessMessageQueueOptions as nt, GetSignedKeyOptions as o, MessageQueueListenOptions as ot, RequestContext as p, CircuitBreakerKvState as pt, TaskRegistry as q, UnverifiedActivityReason as qt, ForwardActivityOptions as r, MessageQueue as rt, InboxContext as s, ParallelMessageQueue as st, ActorKeyPair as t, InProcessMessageQueue as tt, ParseUriOptions as u, CircuitBreakerBeforeSendDecision as ut, CollectionCallbackSetters as v, normalizeCircuitBreakerOptions as vt, FederationBuilder as w, AuthorizePredicate as wt, Federatable as x, ActorDispatcher as xt, ConstructorWithTypeId as y, parseCircuitBreakerKvState as yt, FederationQueueOptions as z, OutboxErrorHandler as zt };
4826
+ export { respondWithObject as $, digest as $t, InboxChallengePolicy as A, CustomCollectionDispatcher as At, createFederation as B, OutboxListener as Bt, FederationBenchmarkOptions as C, ActorKeyPairsDispatcher as Ct, FederationStartQueueOptions as D, CollectionDispatcher as Dt, FederationOptions as E, CollectionCursor as Et, PortableInboxForwardingOptions as F, MediaUploaderCallback as Ft, TaskEnqueueOptions as G, SharedInboxKeyDispatcher as Gt, createFederationBuilder as H, OutboxPermanentFailureHandler as Ht, Rfc6570Expression as I, NodeInfoDispatcher as It, CreateExponentialBackoffPolicyOptions as J, WebFingerLinksDispatcher as Jt, TaskHandler as K, UnverifiedActivityHandler as Kt, FederationKvPrefixes as L, ObjectAuthorizePredicate as Lt, MediaUploaderSetters as M, HashlinkMediaRequest as Mt, ObjectCallbackSetters as N, InboxErrorHandler as Nt, IdempotencyKeyCallback as O, CustomCollectionCounter as Ot, OutboxListenerSetters as P, InboxListener as Pt, RespondWithObjectOptions as Q, buildCollectionSynchronizationHeader as Qt, FederationOrigin as R, ObjectDispatcher as Rt, Federation as S, ActorHandleMapper as St, FederationFetchOptions as T, CollectionCounter as Tt, TaskDefinition as U, PortableActorIdMapper as Ut, Message as V, OutboxListenerErrorHandler as Vt, TaskDefinitionOptions as W, PortableCollectionOwnerMapper as Wt, RetryPolicy as X, SenderKeyPair as Xt, RetryContext as Y, SendActivityError as Yt, createExponentialBackoffPolicy as Z, PageItems as Zt, ActorCallbackSetters as _, NormalizedCircuitBreakerOptions as _t, GetObjectOptions as a, InboxRequestReport as an, MessageQueueEnqueueOptions as at, CustomCollectionCallbackSetters as b, ActorAliasMapper as bt, IsSignedByAudienceOptions as c, CircuitBreaker as ct, ParseUriResult as d, CircuitBreakerCreateOptions as dt, ActivityTransformer as en, respondWithObjectIfAcceptable as et, PortableRequest as f, CircuitBreakerFailurePolicy as ft, SendActivityOptionsForCollection as g, CircuitBreakerStateChange as gt, SendActivityOptions as h, CircuitBreakerState as ht, GetActorOptions as i, InboxRequestOutcome as in, MessageQueueDepth as it, InboxListenerSetters as j, HashlinkMediaDispatcher as jt, IdempotencyStrategy as k, CustomCollectionCursor as kt, OutboxContext as l, CircuitBreakerActivityDrop as lt, RouteActivityOptions as m, CircuitBreakerOptions as mt, Context as n, InboxJsonValue as nn, InProcessMessageQueueOptions as nt, GetSignedKeyOptions as o, MessageQueueListenOptions as ot, RequestContext as p, CircuitBreakerKvState as pt, TaskRegistry as q, UnverifiedActivityReason as qt, ForwardActivityOptions as r, InboxRequestFinishedHandler as rn, MessageQueue as rt, InboxContext as s, ParallelMessageQueue as st, ActorKeyPair as t, InboxAuthentication as tn, InProcessMessageQueue as tt, ParseUriOptions as u, CircuitBreakerBeforeSendDecision as ut, CollectionCallbackSetters as v, normalizeCircuitBreakerOptions as vt, FederationBuilder as w, AuthorizePredicate as wt, Federatable as x, ActorDispatcher as xt, ConstructorWithTypeId as y, parseCircuitBreakerKvState as yt, FederationQueueOptions as z, OutboxErrorHandler as zt };
@@ -3,6 +3,6 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  //#region deno.json
5
5
  var name = "@fedify/fedify";
6
- var version = "2.4.0-dev.2256+683c50ba";
6
+ var version = "2.4.0-dev.2260+42356e7d";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,8 +1,8 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { c as validateCryptoKey } from "./key-CEsu7oaD.mjs";
5
- import { n as doubleKnock } from "./http-DkqWu-sm.mjs";
4
+ import { c as validateCryptoKey } from "./key-B7N8mNoY.mjs";
5
+ import { n as doubleKnock } from "./http-DvpuyDJB.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, resolveDocumentLoaderTimeout, validatePublicUrl, withDocumentLoaderTimeout } from "@fedify/vocab-runtime";
8
8
  import { curry } from "es-toolkit";
@@ -7,7 +7,7 @@ import { t as assertExists } from "../assert_exists-cffSpJ7X.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
9
  import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
10
- import { r as createFederationBuilder } from "../builder-DvSQqk2l.mjs";
10
+ import { r as createFederationBuilder } from "../builder-BK8HfSUm.mjs";
11
11
  import { DisallowedOperatorError, DisallowedVarSpecModifierError, DuplicateRouteVariableError, RouteTemplateOptionsNotMatchedError, RouterError } from "@fedify/uri-template";
12
12
  import { Activity, Note, Person } from "@fedify/vocab";
13
13
  import { test } from "@fedify/fixture";
@@ -6,11 +6,11 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
8
  import { t as assert } from "../assert-D1Eafmd_.mjs";
9
- import { n as exportJwk } from "../key-CEsu7oaD.mjs";
9
+ import { n as exportJwk } from "../key-B7N8mNoY.mjs";
10
10
  import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
11
- import { a as signObject, d as verifyProof } from "../proof-8WiH_-Gq.mjs";
11
+ import { a as signObject, d as verifyProof } from "../proof-nce4pf5S.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
13
- import { n as FederationImpl } from "../middleware-Dxn0E9ES.mjs";
13
+ import { n as FederationImpl } from "../middleware-DAA7wDdS.mjs";
14
14
  import { Create, DataIntegrityProof, Note } from "@fedify/vocab";
15
15
  import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
16
16
  import { mockDocumentLoader, test } from "@fedify/fixture";
@@ -5,10 +5,10 @@ import { r as createRequestContext, t as createInboxContext } from "../context-8
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
- import { a as compactJsonLd, m as verifyCompactJsonLd, p as signJsonLd } from "../ld-Cf4CPWN_.mjs";
9
- import { l as signRequest } from "../http-DkqWu-sm.mjs";
8
+ import { a as compactJsonLd, m as verifyCompactJsonLd, p as signJsonLd } from "../ld-BDMNyM2M.mjs";
9
+ import { l as signRequest } from "../http-DvpuyDJB.mjs";
10
10
  import { a as rsaPrivateKey3, c as rsaPublicKey3, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
11
- import { f as handleInbox, o as createFederation } from "../middleware-Dxn0E9ES.mjs";
11
+ import { f as handleInbox, o as createFederation } from "../middleware-DAA7wDdS.mjs";
12
12
  import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
13
13
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
14
14
  import { Create, CryptographicKey, Multikey, Person, Update } from "@fedify/vocab";
@@ -10,12 +10,12 @@ import "../std__assert-DvMDRPes.mjs";
10
10
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
11
11
  import { t as assert } from "../assert-D1Eafmd_.mjs";
12
12
  import { r as parseAcceptSignature } from "../accept-CPVqki78.mjs";
13
- import { o as generateCryptoKeyPair } from "../key-CEsu7oaD.mjs";
14
- import { a as compactJsonLd, p as signJsonLd } from "../ld-Cf4CPWN_.mjs";
15
- import { a as signObject } from "../proof-8WiH_-Gq.mjs";
16
- import { l as signRequest } from "../http-DkqWu-sm.mjs";
13
+ import { o as generateCryptoKeyPair } from "../key-B7N8mNoY.mjs";
14
+ import { a as compactJsonLd, p as signJsonLd } from "../ld-BDMNyM2M.mjs";
15
+ import { a as signObject } from "../proof-nce4pf5S.mjs";
16
+ import { l as signRequest } from "../http-DvpuyDJB.mjs";
17
17
  import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
18
- import { d as handleCustomCollection, f as handleInbox, g as respondWithObjectIfAcceptable, h as respondWithObject, l as handleActor, m as handleOutbox, o as createFederation, p as handleObject, u as handleCollection } from "../middleware-Dxn0E9ES.mjs";
18
+ import { d as handleCustomCollection, f as handleInbox, g as respondWithObjectIfAcceptable, h as respondWithObject, l as handleActor, m as handleOutbox, o as createFederation, p as handleObject, u as handleCollection } from "../middleware-DAA7wDdS.mjs";
19
19
  import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
20
20
  import { Activity, Create, CryptographicKey, Multikey, Note, Person, Tombstone } from "@fedify/vocab";
21
21
  import { FetchError, parseIri } from "@fedify/vocab-runtime";
@@ -1242,6 +1242,67 @@ test("handleCollection() records not_found collection metrics", async () => {
1242
1242
  assertEquals(durations[0].attributes["activitypub.collection.result"], "not_found");
1243
1243
  assertEquals(recorder.getMeasurements("activitypub.collection.page.items").length, 0);
1244
1244
  });
1245
+ test("handleInbox() reports dispatch context errors before a failing error hook", async () => {
1246
+ const kv = new MemoryKvStore();
1247
+ const federation = createFederation({ kv });
1248
+ const request = await signRequest(new Request("https://example.com/inbox", {
1249
+ method: "POST",
1250
+ body: JSON.stringify(await new Create({ actor: rsaPublicKey3.ownerId }).toJsonLd({ contextLoader: mockDocumentLoader }))
1251
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1252
+ const context = createRequestContext({
1253
+ federation,
1254
+ request,
1255
+ url: new URL(request.url),
1256
+ data: void 0,
1257
+ documentLoader: mockDocumentLoader,
1258
+ contextLoader: mockDocumentLoader
1259
+ });
1260
+ const error = /* @__PURE__ */ new Error("Cannot create dispatch context");
1261
+ const reports = [];
1262
+ const listeners = new ActivityListenerSet();
1263
+ let listenerCalled = false;
1264
+ listeners.add(Create, () => {
1265
+ listenerCalled = true;
1266
+ });
1267
+ let errorHookCalled = false;
1268
+ assertEquals((await handleInbox(request, {
1269
+ context,
1270
+ recipient: null,
1271
+ kv,
1272
+ kvPrefixes: {
1273
+ activityIdempotence: ["activity"],
1274
+ publicKey: ["key"],
1275
+ acceptSignatureNonce: ["nonce"]
1276
+ },
1277
+ actorDispatcher: () => new Person({}),
1278
+ inboxListeners: listeners,
1279
+ inboxContextFactory: () => {
1280
+ throw error;
1281
+ },
1282
+ inboxErrorHandler: (_ctx, value) => {
1283
+ assertEquals(value, error);
1284
+ errorHookCalled = true;
1285
+ throw new Error("Error hook failed");
1286
+ },
1287
+ inboxRequestFinishedHandler: (_ctx, report) => {
1288
+ reports.push(report);
1289
+ },
1290
+ onNotFound: () => new Response(null, { status: 404 }),
1291
+ signatureTimeWindow: false,
1292
+ skipSignatureVerification: false
1293
+ })).status, 500);
1294
+ assertEquals(listenerCalled, false);
1295
+ assertEquals(errorHookCalled, true);
1296
+ assertEquals(reports.length, 1);
1297
+ assertEquals(reports[0].authentication.status, "verified");
1298
+ assertEquals(reports[0].outcome, {
1299
+ type: "response",
1300
+ status: 500,
1301
+ disposition: "failed",
1302
+ reason: "listenerError",
1303
+ error
1304
+ });
1305
+ });
1245
1306
  test("handleInbox()", async () => {
1246
1307
  const activity = new Create({
1247
1308
  id: new URL("https://example.com/activities/1"),
@@ -4426,7 +4487,11 @@ test("handleInbox() nonce replay prevention", async () => {
4426
4487
  if (identifier !== "someone") return null;
4427
4488
  return new Person({ name: "Someone" });
4428
4489
  };
4490
+ let report;
4429
4491
  const response = await handleInbox(signedRequest, {
4492
+ inboxRequestFinishedHandler: (_ctx, value) => {
4493
+ report = value;
4494
+ },
4430
4495
  recipient: "someone",
4431
4496
  context,
4432
4497
  inboxContextFactory(_activity) {
@@ -4454,6 +4519,12 @@ test("handleInbox() nonce replay prevention", async () => {
4454
4519
  }
4455
4520
  });
4456
4521
  assertEquals(response.status, 401);
4522
+ assert(report != null);
4523
+ assertEquals(report.authentication, {
4524
+ status: "rejected",
4525
+ reason: { type: "invalidNonce" }
4526
+ });
4527
+ assertEquals(report.attempts.at(-1)?.status, "verified");
4457
4528
  const acceptSig = response.headers.get("Accept-Signature");
4458
4529
  assert(acceptSig != null, "Must emit fresh Accept-Signature challenge");
4459
4530
  const parsed = parseAcceptSignature(acceptSig);
@@ -10,8 +10,8 @@ import "../std__assert-DvMDRPes.mjs";
10
10
  import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
11
11
  import { t as assert } from "../assert-D1Eafmd_.mjs";
12
12
  import { r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
13
- import { o as createFederation, x as parseHashlinkGatewayRequest } from "../middleware-Dxn0E9ES.mjs";
14
- import { r as createFederationBuilder } from "../builder-DvSQqk2l.mjs";
13
+ import { o as createFederation, x as parseHashlinkGatewayRequest } from "../middleware-DAA7wDdS.mjs";
14
+ import { r as createFederationBuilder } from "../builder-BK8HfSUm.mjs";
15
15
  import { n as RouterError } from "../router-u5O5KCQr.mjs";
16
16
  import { Note } from "@fedify/vocab";
17
17
  import { computeDigestMultibase, encodeMultibase, exportDidKey } from "@fedify/vocab-runtime";
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import "../std__assert-DvMDRPes.mjs";
7
- import { a as signObject } from "../proof-8WiH_-Gq.mjs";
7
+ import { a as signObject } from "../proof-nce4pf5S.mjs";
8
8
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
9
- import { o as createFederation } from "../middleware-Dxn0E9ES.mjs";
9
+ import { o as createFederation } from "../middleware-DAA7wDdS.mjs";
10
10
  import { Create, Follow, Person } from "@fedify/vocab";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  //#region src/federation/idempotency.test.ts
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";