@fedify/fedify 2.5.0-dev.2597 → 2.5.0-dev.2614

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 (123) hide show
  1. package/dist/{builder-Da_V_LDj.mjs → builder-B2H8GsyI.mjs} +82 -3
  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-BqqP38Sf.mjs → compound-proof-BavXW-ow.mjs} +2 -2
  6. package/dist/{context-ByflxsY9.d.cts → context-C1NqcIct.d.cts} +1061 -443
  7. package/dist/{context-uQUY5fQT.d.ts → context-xrqq4Hrq.d.ts} +1064 -446
  8. package/dist/{deno-4cGKg59R.mjs → deno-CRXki9-m.mjs} +1 -1
  9. package/dist/{docloader-BiHD6NSO.mjs → docloader-Ca6i6ddp.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 +4 -4
  14. package/dist/federation/hashlink-media.test.mjs +2 -2
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/inbox-actorless.test.mjs +5 -5
  17. package/dist/federation/inbox-report.test.mjs +220 -5
  18. package/dist/federation/inbox-view.test.mjs +1 -1
  19. package/dist/federation/inbox.test.mjs +1 -1
  20. package/dist/federation/metrics.test.mjs +1 -1
  21. package/dist/federation/middleware.test.mjs +7 -7
  22. package/dist/federation/mod.cjs +1 -1
  23. package/dist/federation/mod.d.cts +3 -3
  24. package/dist/federation/mod.d.ts +4 -4
  25. package/dist/federation/mod.js +1 -1
  26. package/dist/federation/mq.test.mjs +1 -1
  27. package/dist/federation/outbound-body.test.d.mts +2 -0
  28. package/dist/federation/outbound-body.test.mjs +149 -0
  29. package/dist/federation/outbound-capture.test.d.mts +2 -0
  30. package/dist/federation/outbound-capture.test.mjs +394 -0
  31. package/dist/federation/outbound-dispatch.test.d.mts +2 -0
  32. package/dist/federation/outbound-dispatch.test.mjs +368 -0
  33. package/dist/federation/outbound-document.test.d.mts +2 -0
  34. package/dist/federation/outbound-document.test.mjs +171 -0
  35. package/dist/federation/outbound-provenance.test.d.mts +2 -0
  36. package/dist/federation/outbound-provenance.test.mjs +543 -0
  37. package/dist/federation/outbound-report.test.d.mts +2 -0
  38. package/dist/federation/outbound-report.test.mjs +5475 -0
  39. package/dist/federation/outbound-workers.test.d.mts +2 -0
  40. package/dist/federation/outbound-workers.test.mjs +64 -0
  41. package/dist/federation/outbound-workflow.test.d.mts +2 -0
  42. package/dist/federation/outbound-workflow.test.mjs +154 -0
  43. package/dist/federation/outgoing-proof.test.mjs +4 -4
  44. package/dist/federation/portable-actor.test.mjs +3 -3
  45. package/dist/federation/portable-chain.test.mjs +3 -3
  46. package/dist/federation/portable-collection.test.mjs +4 -4
  47. package/dist/federation/portable-delivery.test.mjs +106 -7
  48. package/dist/federation/portable-gateway-keys.test.mjs +5 -5
  49. package/dist/federation/portable-inbox.test.mjs +430 -5
  50. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  51. package/dist/federation/portable-route-activity.test.mjs +2 -2
  52. package/dist/federation/portable.test.mjs +3 -3
  53. package/dist/federation/send.test.mjs +44 -3
  54. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  55. package/dist/federation/tasks/tasks.test.mjs +2 -2
  56. package/dist/federation/temporal.test.mjs +1 -1
  57. package/dist/federation/webfinger.test.mjs +1 -1
  58. package/dist/{federation-BKJZEC0O.cjs → federation-DRl81KbJ.cjs} +2358 -405
  59. package/dist/{http-DQP4rw46.js → http-C2H4DT6t.js} +62 -32
  60. package/dist/{http-KU7_TLD9.cjs → http-DHSIMipE.cjs} +62 -32
  61. package/dist/{http-D5n4S1oL.mjs → http-DqOXkYbX.mjs} +64 -34
  62. package/dist/{inbox-CElAglrC.mjs → inbox-C4iW1g0F.mjs} +90 -3
  63. package/dist/{inbox-view-BTUuIIf6.mjs → inbox-view-Bu_aSwFJ.mjs} +1 -1
  64. package/dist/{key-DbLNT-vA.d.ts → key-dwwAg5bA.d.ts} +1 -1
  65. package/dist/{kv-cache-CFBwLUZr.js → kv-cache-B-x_OrBo.js} +1 -1
  66. package/dist/{kv-cache-DrQn-ugb.cjs → kv-cache-CHR_BVyM.cjs} +1 -1
  67. package/dist/{kv-cache-BV55jAE6.mjs → kv-cache-DJsxu9nI.mjs} +1 -1
  68. package/dist/{ld-CmAQYDWE.mjs → ld-C5JOBBFj.mjs} +2 -2
  69. package/dist/{ld-Inagww4R.d.ts → ld-o4LnhCYY.d.ts} +2 -2
  70. package/dist/{metrics-BvV60TwE.mjs → metrics-CTteRnEr.mjs} +1 -1
  71. package/dist/{metrics-DoRtI0qU.d.ts → metrics-DVKYY6K1.d.ts} +1 -1
  72. package/dist/{middleware-Di4y_M3y.js → middleware-B-tMVSUA.js} +2303 -351
  73. package/dist/{middleware-D02eC0j7.mjs → middleware-BkRqMGPU.mjs} +1573 -325
  74. package/dist/{middleware-iAJcDIM2.mjs → middleware-CPigKLnt.mjs} +1 -1
  75. package/dist/{mod-B7j30IQf.d.ts → mod-CQC9xlLZ.d.ts} +3 -3
  76. package/dist/{mod-B4kBw1-8.d.ts → mod-D22ZsiKi.d.ts} +2 -2
  77. package/dist/{mod-BN0TTfyQ.d.ts → mod-DO0epJGH.d.ts} +1 -1
  78. package/dist/{mod-DF2gbN_d.d.cts → mod-TwiQgEf3.d.cts} +1 -1
  79. package/dist/mod.cjs +4 -4
  80. package/dist/mod.d.cts +4 -4
  81. package/dist/mod.d.ts +8 -8
  82. package/dist/mod.js +4 -4
  83. package/dist/nodeinfo/handler.test.mjs +1 -1
  84. package/dist/outbound-body-CckPA5gc.mjs +83 -0
  85. package/dist/outbound-capture-DVgnRzlf.mjs +184 -0
  86. package/dist/outbound-document-BMgwLXTY.mjs +179 -0
  87. package/dist/{owner-C1-csjTo.mjs → owner-CKlRIkeF.mjs} +3 -3
  88. package/dist/{portable-collection-C0MNEEED.mjs → portable-collection-B7RBHVGl.mjs} +2 -2
  89. package/dist/{portable-collection-DkTLVfoM.js → portable-collection-CByyPjLc.js} +2 -2
  90. package/dist/{portable-collection-w6Qn-vJ3.cjs → portable-collection-CmLjeKsP.cjs} +2 -2
  91. package/dist/{portable-key-jtaxB7TD.mjs → portable-key-Clg0IH8C.mjs} +1 -1
  92. package/dist/{proof-CzWCRhnC.mjs → proof-tAb5_Dq4.mjs} +3 -3
  93. package/dist/{send-C9-dwpEH.mjs → send-DzfFREML.mjs} +153 -40
  94. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  95. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  96. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  97. package/dist/sig/compound-proof.test.mjs +2 -2
  98. package/dist/sig/http.test.mjs +38 -2
  99. package/dist/sig/key.test.mjs +1 -1
  100. package/dist/sig/ld.test.mjs +1 -1
  101. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  102. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  103. package/dist/sig/mod.cjs +2 -2
  104. package/dist/sig/mod.d.ts +3 -3
  105. package/dist/sig/mod.js +2 -2
  106. package/dist/sig/owner.test.mjs +1 -1
  107. package/dist/sig/portable-collection.test.mjs +1 -1
  108. package/dist/sig/portable-interop.test.mjs +3 -3
  109. package/dist/sig/portable-key.test.mjs +5 -5
  110. package/dist/sig/proof.test.mjs +3 -3
  111. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  112. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  113. package/dist/sig/verification.test.mjs +3 -3
  114. package/dist/{temporal-lDCl8Zxn.mjs → temporal-D9Nt8Fm4.mjs} +1 -1
  115. package/dist/testing/kv.test.mjs +1 -1
  116. package/dist/testing/mod.d.mts +1030 -412
  117. package/dist/utils/docloader.test.mjs +2 -2
  118. package/dist/utils/kv-cache.test.mjs +1 -1
  119. package/dist/utils/mod.cjs +1 -1
  120. package/dist/utils/mod.d.ts +1 -1
  121. package/dist/utils/mod.js +1 -1
  122. package/package.json +6 -6
  123. package/skills/fedify/SKILL.md +60 -1
@@ -0,0 +1,179 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { preloadedContexts } from "@fedify/vocab-runtime";
5
+ //#region src/federation/outbound-document.ts
6
+ const asContext = "https://www.w3.org/ns/activitystreams";
7
+ const asNamespace = `${asContext}#`;
8
+ const blind = /* @__PURE__ */ new Set([
9
+ "bto",
10
+ "bcc",
11
+ "as:bto",
12
+ "as:bcc",
13
+ `${asNamespace}bto`,
14
+ `${asNamespace}bcc`
15
+ ]);
16
+ const contextCandidates = [
17
+ asContext,
18
+ "https://w3id.org/security/v1",
19
+ "https://w3id.org/identity/v1",
20
+ "https://gotosocial.org/ns",
21
+ "https://w3id.org/security/data-integrity/v1",
22
+ "https://w3id.org/security/data-integrity/v2"
23
+ ];
24
+ function safeContext(context) {
25
+ if (Array.isArray(context)) return context.every(safeContext);
26
+ if (context == null || typeof context !== "object") return false;
27
+ for (const [name, definition] of Object.entries(context)) {
28
+ if (name === "@import") return false;
29
+ const mapping = typeof definition === "string" ? definition : definition != null && typeof definition === "object" ? definition["@id"] : void 0;
30
+ if (name === "as" && mapping !== asNamespace) return false;
31
+ if (name !== "as" && typeof mapping === "string" && mapping.startsWith(asNamespace) && (mapping.endsWith("#") || mapping.endsWith("/") || typeof definition === "object" && definition["@prefix"] === true)) return false;
32
+ if (typeof mapping === "string" && blind.has(mapping) && name !== "bto" && name !== "bcc") return false;
33
+ if ((name === "bto" || name === "bcc") && mapping !== `as:${name}` && mapping !== `${asNamespace}${name}`) return false;
34
+ if (name === "@vocab" && typeof mapping === "string" && mapping.startsWith(asNamespace)) return false;
35
+ if (definition != null && typeof definition === "object" && "@context" in definition && !safeContext(definition["@context"])) return false;
36
+ }
37
+ return true;
38
+ }
39
+ const contexts = new Set(contextCandidates.filter((url) => {
40
+ const document = preloadedContexts[url];
41
+ return document != null && typeof document === "object" && safeContext(document["@context"]);
42
+ }));
43
+ const generatedTerms = {
44
+ toot: "http://joinmastodon.org/ns#",
45
+ misskey: "https://misskey-hub.net/ns#",
46
+ fedibird: "http://fedibird.com/ns#",
47
+ sensitive: "as:sensitive",
48
+ votersCount: {
49
+ "@id": "toot:votersCount",
50
+ "@type": "http://www.w3.org/2001/XMLSchema#nonNegativeInteger"
51
+ },
52
+ Emoji: "toot:Emoji",
53
+ Hashtag: "as:Hashtag",
54
+ ChatMessage: "http://litepub.social/ns#ChatMessage",
55
+ quote: {
56
+ "@id": "https://w3id.org/fep/044f#quote",
57
+ "@type": "@id"
58
+ },
59
+ quoteUrl: "as:quoteUrl",
60
+ _misskey_quote: "misskey:_misskey_quote",
61
+ quoteUri: "fedibird:quoteUri",
62
+ QuoteAuthorization: "https://w3id.org/fep/044f#QuoteAuthorization",
63
+ QuoteRequest: "https://w3id.org/fep/044f#QuoteRequest",
64
+ quoteAuthorization: {
65
+ "@id": "https://w3id.org/fep/044f#quoteAuthorization",
66
+ "@type": "@id"
67
+ },
68
+ emojiReactions: {
69
+ "@id": "fedibird:emojiReactions",
70
+ "@type": "@id"
71
+ }
72
+ };
73
+ function sameDefinition(value, expected) {
74
+ if (value === expected) return true;
75
+ if (value == null || expected == null || typeof value !== "object" || typeof expected !== "object" || Array.isArray(value)) return false;
76
+ const actual = value;
77
+ const reference = expected;
78
+ return Object.keys(actual).length === Object.keys(reference).length && Object.keys(actual).every((name) => Object.hasOwn(reference, name) && sameDefinition(actual[name], reference[name]));
79
+ }
80
+ function generatedContext(value) {
81
+ if (value == null || typeof value !== "object" || Array.isArray(value)) return false;
82
+ return Object.entries(value).every(([name, definition]) => Object.hasOwn(generatedTerms, name) && sameDefinition(definition, generatedTerms[name]));
83
+ }
84
+ const keywords = /* @__PURE__ */ new Set([
85
+ "@id",
86
+ "@type",
87
+ "@value",
88
+ "@language",
89
+ "@direction",
90
+ "@index",
91
+ "@list",
92
+ "@set",
93
+ "@graph",
94
+ "@reverse",
95
+ "@included",
96
+ "@nest"
97
+ ]);
98
+ /** Validate the explicit opt-in before replacing observer registration. */
99
+ function snapshotSenderPayloadOption(value) {
100
+ if (value === void 0) return false;
101
+ if (typeof value !== "boolean") throw new TypeError("Outbound senderPayload must be a boolean.");
102
+ return value;
103
+ }
104
+ /** Project existing plain JSON only; no getters, JSON-LD processing or serializers. */
105
+ function projectSenderPayload(input) {
106
+ try {
107
+ let compact = false;
108
+ let redacted = false;
109
+ const seen = /* @__PURE__ */ new WeakSet();
110
+ const copy = (value, depth, contextData = false) => {
111
+ if (depth >= 64) throw new TypeError("Unsafe document depth.");
112
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
113
+ if (typeof value === "number" && Number.isFinite(value)) return value;
114
+ if (typeof value !== "object" || value == null || seen.has(value)) throw new TypeError("Not independent JSON data.");
115
+ const array = Array.isArray(value);
116
+ const prototype = Object.getPrototypeOf(value);
117
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null) throw new TypeError("Unsupported document object.");
118
+ seen.add(value);
119
+ try {
120
+ const descriptors = Object.getOwnPropertyDescriptors(value);
121
+ if (Object.hasOwn(descriptors, "toJSON")) throw new TypeError("Custom JSON conversion is not projected.");
122
+ const names = Reflect.ownKeys(descriptors).filter((name) => descriptors[name].enumerable);
123
+ if (names.some((name) => typeof name !== "string" || !("value" in descriptors[name]))) throw new TypeError("Not plain JSON properties.");
124
+ let contextValue;
125
+ if (!contextData && !array && descriptors["@context"]?.enumerable) {
126
+ const context = copy(descriptors["@context"].value, depth + 1, true);
127
+ contextValue = context;
128
+ const urls = typeof context === "string" ? [context] : context;
129
+ if (!Array.isArray(urls) || urls.length === 0 || (depth === 0 ? !urls.includes(asContext) : !compact) || !urls.every((url) => typeof url === "string" ? contexts.has(url) : depth === 0 && generatedContext(url))) throw new TypeError("Unsupported document context.");
130
+ compact = true;
131
+ }
132
+ if (array) {
133
+ const length = descriptors.length.value;
134
+ if (names.length !== length || names.some((name, index) => name !== String(index))) throw new TypeError("Not a JSON array.");
135
+ return Object.freeze(names.map((name) => copy(descriptors[name].value, depth + 1, contextData)));
136
+ }
137
+ const result = Object.create(null);
138
+ for (const name of names) {
139
+ if (contextData) {
140
+ result[name] = copy(descriptors[name].value, depth + 1, true);
141
+ continue;
142
+ }
143
+ if (name === "@context") {
144
+ if (contextValue === void 0 || !compact) throw new TypeError("Scoped context not supported.");
145
+ result[name] = contextValue;
146
+ continue;
147
+ }
148
+ if (!compact && !keywords.has(name) && !/^[A-Za-z][A-Za-z0-9+.-]*:/.test(name)) throw new TypeError("Unresolved document property.");
149
+ const next = copy(descriptors[name].value, depth + 1);
150
+ if (blind.has(name)) {
151
+ redacted = true;
152
+ continue;
153
+ }
154
+ result[name] = next;
155
+ }
156
+ return Object.freeze(result);
157
+ } finally {
158
+ seen.delete(value);
159
+ }
160
+ };
161
+ if (input == null || typeof input !== "object") throw new TypeError("Not an outgoing document.");
162
+ const value = copy(input, 0);
163
+ return Object.freeze({
164
+ stage: "sender-payload",
165
+ type: "available",
166
+ representation: "privacy-projection",
167
+ redacted,
168
+ value
169
+ });
170
+ } catch {
171
+ return Object.freeze({
172
+ stage: "sender-payload",
173
+ type: "unavailable",
174
+ reason: "privacy-filter-unavailable"
175
+ });
176
+ }
177
+ }
178
+ //#endregion
179
+ export { snapshotSenderPayloadOption as n, projectSenderPayload as t };
@@ -2,10 +2,10 @@ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { d as isPortableUri, l as isPortableId, n as getCanonicalPortableId, p as isSamePublicKey$1, s as isCompatibleKeyId } from "./portable-key-id-Ca30-hOa.mjs";
5
- import { n as version, t as name } from "./deno-4cGKg59R.mjs";
5
+ import { n as version, t as name } from "./deno-CRXki9-m.mjs";
6
6
  import { i as parseKeyOwnerEvidence, r as getVerifiedKeyOwnerEvidence } from "./key-owner-evidence-APB6NpSk.mjs";
7
- import { O as verifyKeyOwnership, S as fetchActorDocument, x as exportJwk } from "./ld-CmAQYDWE.mjs";
8
- import { n as resolvePortableActorKey, t as fetchPortableGatewayKey } from "./portable-key-jtaxB7TD.mjs";
7
+ import { O as verifyKeyOwnership, S as fetchActorDocument, x as exportJwk } from "./ld-C5JOBBFj.mjs";
8
+ import { n as resolvePortableActorKey, t as fetchPortableGatewayKey } from "./portable-key-Clg0IH8C.mjs";
9
9
  import { getLogger } from "@logtape/logtape";
10
10
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
11
11
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -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-4cGKg59R.mjs";
5
- import { d as verifyPortableObjectProof, p as verifyPortableObjectProofWithRoot } from "./proof-CzWCRhnC.mjs";
4
+ import { n as version, t as name } from "./deno-CRXki9-m.mjs";
5
+ import { d as verifyPortableObjectProof, p as verifyPortableObjectProofWithRoot } from "./proof-tAb5_Dq4.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { CollectionPage, Object as Object$1, isActor } from "@fedify/vocab";
8
8
  import { trace } from "@opentelemetry/api";
@@ -1,10 +1,10 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { $ as getVerifiedKeyOwnerEvidence, C as verifyPortableObjectProofWithRoot, Dt as getCanonicalPortableId, Ft as isPortableUri, G as fetchActorDocument, Lt as isSamePublicKey$1, Mt as isCompatibleKeyId, Nt as isPortableId, Rt as name, W as exportJwk, Z as verifyKeyOwnership, et as parseKeyOwnerEvidence, l as fetchPortableGatewayKey, u as resolvePortableActorKey, x as verifyPortableObjectProof, zt as version } from "./http-DQP4rw46.js";
3
+ import { $ as getVerifiedKeyOwnerEvidence, C as verifyPortableObjectProofWithRoot, Dt as getCanonicalPortableId, Ft as isPortableUri, G as fetchActorDocument, Lt as isSamePublicKey$1, Mt as isCompatibleKeyId, Nt as isPortableId, Rt as name, W as exportJwk, Z as verifyKeyOwnership, et as parseKeyOwnerEvidence, l as fetchPortableGatewayKey, u as resolvePortableActorKey, x as verifyPortableObjectProof, zt as version } from "./http-C2H4DT6t.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
+ import { canonicalizePortableUri, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, parseIri } from "@fedify/vocab-runtime";
5
6
  import { CollectionPage, CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
6
7
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
- import { canonicalizePortableUri, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, parseIri } from "@fedify/vocab-runtime";
8
8
  import { dereferencePortableIri, getPortableGatewayCandidates } from "@fedify/vocab-runtime/internal/portable-dereference";
9
9
  //#region src/sig/owner.ts
10
10
  const logger$1 = getLogger([
@@ -1,9 +1,9 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
- const require_http = require("./http-KU7_TLD9.cjs");
2
+ const require_http = require("./http-DHSIMipE.cjs");
3
3
  let _logtape_logtape = require("@logtape/logtape");
4
+ let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
4
5
  let _fedify_vocab = require("@fedify/vocab");
5
6
  let _opentelemetry_api = require("@opentelemetry/api");
6
- let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
7
7
  let _fedify_vocab_runtime_internal_portable_dereference = require("@fedify/vocab-runtime/internal/portable-dereference");
8
8
  //#region src/sig/owner.ts
9
9
  const logger$1 = (0, _logtape_logtape.getLogger)([
@@ -2,7 +2,7 @@ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { a as getRawCanonicalPortableId, c as isPortableActorDocument, d as isPortableUri, n as getCanonicalPortableId, p as isSamePublicKey } from "./portable-key-id-Ca30-hOa.mjs";
5
- import { p as verifyPortableObjectProofWithRoot } from "./proof-CzWCRhnC.mjs";
5
+ import { p as verifyPortableObjectProofWithRoot } from "./proof-tAb5_Dq4.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { CryptographicKey, Multikey, Object as Object$1 } from "@fedify/vocab";
8
8
  import { FetchError, formatIri, getDocumentLoader, isGatewayUrl, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
@@ -3,9 +3,9 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { i as getPortableDid, l as isPortableId } 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-4cGKg59R.mjs";
7
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BvV60TwE.mjs";
8
- import { C as fetchKey, D as validateCryptoKey, b as bypassKeyCacheReads, c as getNormalizationContextLoader } from "./ld-CmAQYDWE.mjs";
6
+ import { n as version, t as name } from "./deno-CRXki9-m.mjs";
7
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-CTteRnEr.mjs";
8
+ import { C as fetchKey, D as validateCryptoKey, b as bypassKeyCacheReads, c as getNormalizationContextLoader } from "./ld-C5JOBBFj.mjs";
9
9
  import { n as preloadedOnlyDocumentLoader } from "./public-audience--klBIOk1.mjs";
10
10
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-4IJqPYos.mjs";
11
11
  import { getLogger } from "@logtape/logtape";
@@ -2,11 +2,13 @@ import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { d as isPortableUri } from "./portable-key-id-Ca30-hOa.mjs";
5
- import { n as version, t as name } from "./deno-4cGKg59R.mjs";
6
- import { n as getDurationMs, r as getFederationMetrics } from "./metrics-BvV60TwE.mjs";
7
- import { r as doubleKnockScoped } from "./http-D5n4S1oL.mjs";
5
+ import { n as version, t as name } from "./deno-CRXki9-m.mjs";
6
+ import { n as getDurationMs, r as getFederationMetrics } from "./metrics-CTteRnEr.mjs";
7
+ import { r as doubleKnockScoped } from "./http-DqOXkYbX.mjs";
8
+ import { t as ResponseBodyPump } from "./outbound-body-CckPA5gc.mjs";
9
+ import { r as takeCapturedDiagnostic } from "./outbound-capture-DVgnRzlf.mjs";
8
10
  import { getLogger, withContext } from "@logtape/logtape";
9
- import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
11
+ import { SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
10
12
  import { FetchError, UrlError, canonicalizePortableUri, getFe34Origin, isGatewayUrl, toCompatibleEf61Id } from "@fedify/vocab-runtime";
11
13
  import { getPortableGatewayCandidates } from "@fedify/vocab-runtime/internal/portable-dereference";
12
14
  import { openDirectFetch, openPublicFetch } from "@fedify/vocab-runtime/internal";
@@ -193,11 +195,15 @@ function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
193
195
  * @throws {Error} If the activity fails to send.
194
196
  */
195
197
  function sendActivity(options, deps = {}) {
198
+ options.attemptStarted?.(options.inbox.href);
199
+ const attempt = options.observation?.startAttempt(options.inbox.href);
200
+ const parentSpanContext = trace.getSpanContext(context.active());
196
201
  const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
197
202
  return tracerProvider.getTracer(name, version).startActiveSpan("activitypub.send_activity", {
198
203
  kind: SpanKind.CLIENT,
199
204
  attributes: { "activitypub.shared_inbox": options.sharedInbox ?? false }
200
205
  }, async (span) => {
206
+ attempt?.setSpan(span, parentSpanContext);
201
207
  if (options.activityId != null) span.setAttribute("activitypub.activity.id", options.activityId);
202
208
  if (options.activityType != null) span.setAttribute("activitypub.activity.type", options.activityType);
203
209
  const spanContext = span.spanContext();
@@ -208,12 +214,14 @@ function sendActivity(options, deps = {}) {
208
214
  }, () => sendActivityInternal({
209
215
  ...options,
210
216
  tracerProvider
211
- }, span, deps));
217
+ }, span, deps, attempt));
218
+ await attempt?.succeeded();
212
219
  } catch (e) {
213
220
  span.setStatus({
214
221
  code: SpanStatusCode.ERROR,
215
222
  message: String(e)
216
223
  });
224
+ await attempt?.failed(e);
217
225
  throw e;
218
226
  } finally {
219
227
  span.end();
@@ -253,6 +261,7 @@ async function sendActivityThroughGateways(parameters, inboxes) {
253
261
  throw lastError;
254
262
  }
255
263
  const MAX_ERROR_RESPONSE_BODY_BYTES = 1024;
264
+ const ERROR_RESPONSE_BODY_TIMEOUT_MS = 1e3;
256
265
  function getActivityActorId(activity) {
257
266
  if (!isRecord(activity)) return void 0;
258
267
  return getIdValue(activity.actor);
@@ -273,16 +282,25 @@ function isRecord(value) {
273
282
  return typeof value === "object" && value != null;
274
283
  }
275
284
  async function readLimitedResponseBody(response, maxBytes) {
285
+ const captured = takeCapturedDiagnostic(response);
286
+ if (captured !== void 0) return captured;
276
287
  if (response.body == null) return "";
277
- const reader = response.body.getReader();
288
+ const pump = new ResponseBodyPump(response.body, ERROR_RESPONSE_BODY_TIMEOUT_MS);
278
289
  const decoder = new TextDecoder();
279
290
  const chunks = [];
280
291
  let totalBytes = 0;
281
292
  let truncated = false;
293
+ let timedOut = false;
282
294
  try {
283
295
  while (true) {
284
- const { done, value } = await reader.read();
296
+ const result = await pump.read();
297
+ if (result == null) {
298
+ timedOut = true;
299
+ break;
300
+ }
301
+ const { done, value } = result;
285
302
  if (done) break;
303
+ if (value.length === 0) continue;
286
304
  if (totalBytes + value.length > maxBytes) {
287
305
  const remaining = maxBytes - totalBytes;
288
306
  if (remaining > 0) chunks.push(decoder.decode(value.slice(0, remaining), { stream: true }));
@@ -293,14 +311,14 @@ async function readLimitedResponseBody(response, maxBytes) {
293
311
  totalBytes += value.length;
294
312
  }
295
313
  } finally {
296
- if (truncated) reader.cancel().catch(() => {});
297
- reader.releaseLock();
314
+ pump.close();
298
315
  }
299
316
  let result = chunks.join("");
300
317
  if (truncated) result += "… (truncated)";
318
+ else if (timedOut) result += "… (timed out)";
301
319
  return result;
302
320
  }
303
- async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span, deps) {
321
+ async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, allowPrivateAddress, headers, specDeterminer, meterProvider, tracerProvider }, span, deps, attempt) {
304
322
  const logger = getLogger([
305
323
  "fedify",
306
324
  "federation",
@@ -316,6 +334,11 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
316
334
  session = await openUnvalidatedHop(url);
317
335
  } catch (error) {
318
336
  if (error instanceof UrlError && error.reason === "dns") {
337
+ attempt?.failure({
338
+ kind: "transport",
339
+ phase: "validation",
340
+ error
341
+ });
319
342
  logger.error("DNS resolution failed for URL: {url}", {
320
343
  url,
321
344
  error
@@ -324,13 +347,47 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
324
347
  failure.cause = error;
325
348
  throw failure;
326
349
  }
327
- if (error instanceof UrlError) logger.error("Disallowed private URL: {url}", {
328
- url,
329
- error
330
- });
350
+ if (error instanceof UrlError) {
351
+ if (error.reason === "disallowed") attempt?.failure({
352
+ kind: "policy",
353
+ phase: "validation",
354
+ error
355
+ });
356
+ logger.error("Disallowed private URL: {url}", {
357
+ url,
358
+ error
359
+ });
360
+ }
331
361
  throw error;
332
362
  }
333
- return session;
363
+ if (attempt == null) return session;
364
+ return {
365
+ url: session.url,
366
+ async fetch(input, init) {
367
+ try {
368
+ return await session.fetch(input, init);
369
+ } catch (error) {
370
+ attempt.failure({
371
+ kind: "transport",
372
+ phase: "request",
373
+ error
374
+ });
375
+ throw error;
376
+ }
377
+ },
378
+ async close() {
379
+ try {
380
+ await session.close();
381
+ } catch (error) {
382
+ attempt.failure({
383
+ kind: "local",
384
+ phase: "cleanup",
385
+ error
386
+ });
387
+ throw error;
388
+ }
389
+ }
390
+ };
334
391
  }
335
392
  let initialSession;
336
393
  try {
@@ -348,14 +405,45 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
348
405
  return openHop(url);
349
406
  };
350
407
  try {
351
- headers = new Headers(headers);
352
- headers.set("Content-Type", "application/activity+json");
353
- const request = new Request(inbox, {
354
- method: "POST",
355
- headers,
356
- body: JSON.stringify(activity),
357
- redirect: "manual"
358
- });
408
+ let request;
409
+ let body;
410
+ try {
411
+ headers = new Headers(headers);
412
+ headers.set("Content-Type", "application/activity+json");
413
+ } catch (error) {
414
+ attempt?.failure({
415
+ kind: "local",
416
+ phase: "request-preparation",
417
+ error
418
+ });
419
+ throw error;
420
+ }
421
+ try {
422
+ attempt?.snapshotSenderPayload(activity);
423
+ body = JSON.stringify(activity);
424
+ } catch (error) {
425
+ attempt?.failure({
426
+ kind: "local",
427
+ phase: "serialization",
428
+ error
429
+ });
430
+ throw error;
431
+ }
432
+ try {
433
+ request = new Request(inbox, {
434
+ method: "POST",
435
+ headers,
436
+ body,
437
+ redirect: "manual"
438
+ });
439
+ } catch (error) {
440
+ attempt?.failure({
441
+ kind: "local",
442
+ phase: "request-preparation",
443
+ error
444
+ });
445
+ throw error;
446
+ }
359
447
  let rsaKey = null;
360
448
  for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
361
449
  rsaKey = key;
@@ -372,13 +460,25 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
372
460
  privateKey: pair.privateKey
373
461
  }))
374
462
  });
463
+ const consume = (response) => {
464
+ return consumeSendActivityResponse(response, inbox);
465
+ };
375
466
  let outcome;
376
467
  try {
377
- outcome = rsaKey == null ? await fetchWithValidatedRedirects(request, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox)) : await doubleKnockScoped(request, rsaKey, {
468
+ outcome = rsaKey == null ? await fetchWithValidatedRedirects(request, openDeliveryHop, consume, attempt) : await doubleKnockScoped(request, rsaKey, {
378
469
  tracerProvider,
379
470
  specDeterminer
380
- }, openDeliveryHop, (response) => consumeSendActivityResponse(response, inbox));
471
+ }, openDeliveryHop, consume, attempt == null ? void 0 : {
472
+ fetch: (request, submit) => attempt.fetch(request, submit),
473
+ response: (response, disposition) => attempt.response(response, disposition),
474
+ error: (phase, error) => attempt.failure({
475
+ kind: "local",
476
+ phase,
477
+ error
478
+ })
479
+ });
381
480
  } catch (error) {
481
+ attempt?.unknown(error);
382
482
  const failure = error instanceof FetchError || error instanceof UrlError ? error : createFetchError(inbox.href, error);
383
483
  logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
384
484
  activityId,
@@ -398,7 +498,13 @@ async function sendActivityInternal({ activity, activityId, activityType, keys,
398
498
  statusText,
399
499
  error
400
500
  });
401
- throw new SendActivityError(inbox, status, `Failed to send activity ${activityId} to ${inbox.href} (${status} ${statusText}):\n${error}`, error, responseHeaders);
501
+ const failure = new SendActivityError(inbox, status, `Failed to send activity ${activityId} to ${inbox.href} (${status} ${statusText}):\n${error}`, error, responseHeaders);
502
+ attempt?.failure({
503
+ kind: "http",
504
+ phase: "response",
505
+ error: failure
506
+ });
507
+ throw failure;
402
508
  }
403
509
  deliverySuccess = true;
404
510
  const eventAttributes = {
@@ -442,7 +548,7 @@ function cancelResponseBody(response, url) {
442
548
  /**
443
549
  * Reads what is needed from the final response of a delivery while its
444
550
  * session is open: the body of a successful response is cancelled, and at
445
- * most 1 KiB of an error response body is read.
551
+ * most 1 KiB of an error response body is read within its diagnostic budget.
446
552
  */
447
553
  async function consumeSendActivityResponse(response, inbox) {
448
554
  if (response.ok) {
@@ -472,9 +578,10 @@ async function consumeSendActivityResponse(response, inbox) {
472
578
  * @param request The request to send.
473
579
  * @param openHop The function to open a session for each hop.
474
580
  * @param consume The function to consume the final response.
581
+ * @param attempt The in-process attempt observation, if registered.
475
582
  * @returns The value returned by `consume`.
476
583
  */
477
- async function fetchWithValidatedRedirects(request, openHop, consume) {
584
+ async function fetchWithValidatedRedirects(request, openHop, consume, attempt) {
478
585
  const logger = getLogger([
479
586
  "fedify",
480
587
  "federation",
@@ -485,7 +592,7 @@ async function fetchWithValidatedRedirects(request, openHop, consume) {
485
592
  const session = await openHop(request.url);
486
593
  let response;
487
594
  try {
488
- response = await session.fetch(request, { redirect: "manual" });
595
+ response = await (attempt == null ? session.fetch(request, { redirect: "manual" }) : attempt.fetch(request, () => session.fetch(request, { redirect: "manual" })));
489
596
  } catch (error) {
490
597
  await closeAfterError(session, logger);
491
598
  throw error;
@@ -497,17 +604,21 @@ async function fetchWithValidatedRedirects(request, openHop, consume) {
497
604
  303,
498
605
  307,
499
606
  308
500
- ].includes(response.status) || location == null) try {
501
- return await consume(response);
502
- } catch (error) {
503
- logger.debug("Failed to consume the response from {url}; closing the session: {error}", {
504
- url: request.url,
505
- error
506
- });
507
- throw error;
508
- } finally {
509
- await session.close();
607
+ ].includes(response.status) || location == null) {
608
+ attempt?.response(response, "final");
609
+ try {
610
+ return await consume(response);
611
+ } catch (error) {
612
+ logger.debug("Failed to consume the response from {url}; closing the session: {error}", {
613
+ url: request.url,
614
+ error
615
+ });
616
+ throw error;
617
+ } finally {
618
+ await session.close();
619
+ }
510
620
  }
621
+ attempt?.response(response, "intermediate");
511
622
  cancelResponseBody(response, request.url);
512
623
  await session.close();
513
624
  if (redirects >= 20) throw new TypeError("Too many redirects");
@@ -568,7 +679,9 @@ var SendActivityError = class extends Error {
568
679
  * The response body from the inbox, if any. Note that this may be
569
680
  * truncated to a maximum of 1 KiB to prevent excessive memory consumption
570
681
  * when remote servers return large error pages (e.g., Cloudflare error pages).
571
- * If truncated, the string will end with `"… (truncated)"`.
682
+ * If truncated, the string will end with `"… (truncated)"`. Diagnostic reading
683
+ * has a separate one-second budget; a deadline retains any partial preview and
684
+ * appends `"… (timed out)"`. This is independent of application body capture.
572
685
  */
573
686
  responseBody;
574
687
  /**
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
- import { a as inspectCompoundPortableObjectApplicability, n as discoverCompoundProofDocuments } from "../compound-proof-BqqP38Sf.mjs";
6
+ import { a as inspectCompoundPortableObjectApplicability, n as discoverCompoundProofDocuments } from "../compound-proof-BavXW-ow.mjs";
7
7
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/sig/compound-proof-discovery.test.ts
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
- import { a as inspectCompoundPortableObjectApplicability, i as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "../compound-proof-BqqP38Sf.mjs";
6
+ import { a as inspectCompoundPortableObjectApplicability, i as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "../compound-proof-BavXW-ow.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  //#region src/sig/compound-proof-producer.test.ts
9
9
  const portableId = "ap+ef61://did:key:z6MkAlice/objects/1";
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
6
  import { t as assert } from "../assert-D1Eafmd_.mjs";
7
- import { c as verifyMapLocalProof, f as verifyPortableObjectProofPolicy } from "../proof-CzWCRhnC.mjs";
7
+ import { c as verifyMapLocalProof, f as verifyPortableObjectProofPolicy } from "../proof-tAb5_Dq4.mjs";
8
8
  import { n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
9
- import { a as inspectCompoundPortableObjectApplicability, o as verifyCompoundPortableObjectProofs, s as verifyCompoundProofDocuments, t as containsCompoundPortableObject } from "../compound-proof-BqqP38Sf.mjs";
9
+ import { a as inspectCompoundPortableObjectApplicability, o as verifyCompoundPortableObjectProofs, s as verifyCompoundProofDocuments, t as containsCompoundPortableObject } from "../compound-proof-BavXW-ow.mjs";
10
10
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
11
11
  import { t as map_local_context_conflict_default } from "../map-local-context-conflict-DOYEEJ3u.mjs";
12
12
  import { CryptographicKey, Multikey, Person } from "@fedify/vocab";
@@ -6,9 +6,9 @@ import "../std__assert-DvMDRPes.mjs";
6
6
  import { t as assert } from "../assert-D1Eafmd_.mjs";
7
7
  import { n as preloadedOnlyDocumentLoader } from "../public-audience--klBIOk1.mjs";
8
8
  import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
9
- import { h as verifyProof, s as signObject } from "../proof-CzWCRhnC.mjs";
9
+ import { h as verifyProof, s as signObject } from "../proof-tAb5_Dq4.mjs";
10
10
  import { n as ed25519PrivateKey, r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
11
- import { s as verifyCompoundProofDocuments } from "../compound-proof-BqqP38Sf.mjs";
11
+ import { s as verifyCompoundProofDocuments } from "../compound-proof-BavXW-ow.mjs";
12
12
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
13
13
  import { t as map_local_context_conflict_default } from "../map-local-context-conflict-DOYEEJ3u.mjs";
14
14
  import { Create, DataIntegrityProof, Note, Question } from "@fedify/vocab";