@fedify/fedify 2.3.0-dev.994 → 2.3.0-pr.809.36

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 (138) hide show
  1. package/README.md +16 -7
  2. package/dist/{assert-DikXweDx.mjs → assert-OguE97r2.mjs} +1 -1
  3. package/dist/{assert_instance_of-C4Ri6VuN.mjs → assert_instance_of-DBC5X09g.mjs} +1 -1
  4. package/dist/{assert_not_equals--wG9hV7u.mjs → assert_not_equals-DkVK8oqV.mjs} +1 -1
  5. package/dist/{assert_rejects-B-qJtC9Z.mjs → assert_rejects-DN60FHPX.mjs} +28 -3
  6. package/dist/{assert_strict_equals-Dmjbg-bA.mjs → assert_strict_equals-XEgZAlrj.mjs} +1 -1
  7. package/dist/{assert_throws-4NwKEy2q.mjs → assert_throws-BOkhLGYc.mjs} +1 -1
  8. package/dist/{builder-yAlpiIqP.mjs → builder-DSZ7FZmg.mjs} +96 -42
  9. package/dist/{chunk-nlSIicah.js → chunk-CRNNMoPX.js} +2 -2
  10. package/dist/circuit-breaker-CSWsyoef.mjs +337 -0
  11. package/dist/{client-z-8dc-e1.d.cts → client-CAM_bQXx.d.cts} +1 -0
  12. package/dist/{client-AtlibPOU.d.ts → client-CSddvgWN.d.ts} +1 -2
  13. package/dist/compat/mod.d.cts +2 -1
  14. package/dist/compat/mod.d.ts +2 -3
  15. package/dist/compat/mod.js +3 -3
  16. package/dist/compat/outgoing-jsonld.test.mjs +4 -4
  17. package/dist/compat/public-audience.test.mjs +4 -4
  18. package/dist/compat/transformers.test.mjs +5 -5
  19. package/dist/{context-BzH2-ajs.d.ts → context-BBVLF7lx.d.cts} +342 -269
  20. package/dist/{context-DJGagtNd.d.cts → context-BU6jSQdo.d.ts} +344 -266
  21. package/dist/{context-Dk_tacqz.mjs → context-DVoTs_wM.mjs} +64 -5
  22. package/dist/{deno-XBVlKnOX.mjs → deno-DCdM7d93.mjs} +1 -1
  23. package/dist/{docloader-DPp-X6gk.mjs → docloader-B7mVwA5_.mjs} +4 -4
  24. package/dist/{esm-DVILvP5e.mjs → esm-vrlUxr60.mjs} +4 -7
  25. package/dist/federation/builder.test.mjs +163 -11
  26. package/dist/federation/circuit-breaker.test.d.mts +2 -0
  27. package/dist/federation/circuit-breaker.test.mjs +446 -0
  28. package/dist/federation/collection.test.mjs +3 -3
  29. package/dist/federation/handler.test.mjs +1770 -29
  30. package/dist/federation/idempotency.test.mjs +5 -5
  31. package/dist/federation/inbox.test.mjs +4 -4
  32. package/dist/federation/keycache.test.mjs +5 -5
  33. package/dist/federation/kv.test.mjs +2 -2
  34. package/dist/federation/metrics.test.d.mts +2 -0
  35. package/dist/federation/metrics.test.mjs +634 -0
  36. package/dist/federation/middleware.test.mjs +4036 -146
  37. package/dist/federation/mod.cjs +195 -14
  38. package/dist/federation/mod.d.cts +6 -4
  39. package/dist/federation/mod.d.ts +6 -6
  40. package/dist/federation/mod.js +192 -14
  41. package/dist/federation/mq.test.mjs +176 -15
  42. package/dist/federation/negotiation.test.mjs +4 -4
  43. package/dist/federation/retry.test.mjs +3 -3
  44. package/dist/federation/router.test.mjs +190 -9
  45. package/dist/federation/send.test.mjs +153 -15
  46. package/dist/federation/temporal.test.d.mts +2 -0
  47. package/dist/federation/temporal.test.mjs +71 -0
  48. package/dist/federation/webfinger.test.mjs +150 -5
  49. package/dist/{http-CSlLA54v.mjs → http-BufbkLuW.mjs} +146 -47
  50. package/dist/{http-B4rrzxtg.js → http-Dh18nwJg.js} +994 -64
  51. package/dist/{http-aQzN9Ayi.d.ts → http-VyDTd4G3.d.cts} +15 -3
  52. package/dist/{http-BrGqJDXL.cjs → http-gOiudB3g.cjs} +1099 -61
  53. package/dist/{http-CrGuipxe.d.cts → http-lf8Hsd91.d.ts} +15 -1
  54. package/dist/{key-Bzx--sHD.mjs → key-DZdKLJXT.mjs} +43 -18
  55. package/dist/{kv-CbLNp3zQ.d.cts → kv-D6hNiMTK.d.ts} +1 -0
  56. package/dist/{kv-cache-DI-Sk7-I.cjs → kv-cache-BuK_nlpY.cjs} +20 -3
  57. package/dist/{kv-cache-U__xU4qR.mjs → kv-cache-C7SQnkGI.mjs} +21 -3
  58. package/dist/{kv-cache-EMIqoIuB.js → kv-cache-DNDSb6Po.js} +20 -3
  59. package/dist/{kv-GFYnFoOl.d.ts → kv-gJ8LYbxX.d.cts} +1 -3
  60. package/dist/ld-0Lsznacf.mjs +626 -0
  61. package/dist/metrics-BHeWd24f.mjs +815 -0
  62. package/dist/{middleware-zjZ6AFCW.mjs → middleware-BNANvDh2.mjs} +1 -1
  63. package/dist/{middleware-nidH7VZH.js → middleware-BgzRkDcm.js} +2260 -556
  64. package/dist/{middleware-An4DjES4.cjs → middleware-D-iES1nQ.cjs} +2318 -623
  65. package/dist/{middleware-K1g6bEkV.mjs → middleware-EV6J_eAc.mjs} +1639 -373
  66. package/dist/{mod-CR8soWa9.d.ts → mod-B0hW12_O.d.cts} +26 -4
  67. package/dist/mod-C0F6kvgS.d.cts +182 -0
  68. package/dist/{mod-Cr3f-ACa.d.cts → mod-COIAjwRS.d.ts} +26 -2
  69. package/dist/{mod-CMEbIaNh.d.cts → mod-DFvNJcNb.d.ts} +56 -4
  70. package/dist/mod-vPYVoa5n.d.ts +182 -0
  71. package/dist/{mod-CLgIXe9w.d.ts → mod-yvIXFAEi.d.cts} +56 -6
  72. package/dist/mod.cjs +9 -6
  73. package/dist/mod.d.cts +12 -10
  74. package/dist/mod.d.ts +12 -12
  75. package/dist/mod.js +11 -11
  76. package/dist/mq-D-nlpY04.d.ts +208 -0
  77. package/dist/mq-D8uSFzxe.d.cts +208 -0
  78. package/dist/{negotiation-SQvQgUqe.mjs → negotiation-DDstyBvc.mjs} +29 -0
  79. package/dist/nodeinfo/client.test.mjs +4 -4
  80. package/dist/nodeinfo/handler.test.mjs +4 -4
  81. package/dist/nodeinfo/mod.d.cts +2 -1
  82. package/dist/nodeinfo/mod.d.ts +2 -3
  83. package/dist/nodeinfo/mod.js +3 -3
  84. package/dist/nodeinfo/types.test.mjs +3 -3
  85. package/dist/otel/exporter.test.mjs +28 -25
  86. package/dist/otel/mod.cjs +6 -5
  87. package/dist/otel/mod.d.cts +5 -3
  88. package/dist/otel/mod.d.ts +5 -5
  89. package/dist/otel/mod.js +8 -7
  90. package/dist/{outgoing-jsonld-CNmZLixq.mjs → outgoing-jsonld-L_DbOaFe.mjs} +2 -2
  91. package/dist/{owner-C-tqfvxn.mjs → owner-Cl-1cAR8.mjs} +2 -2
  92. package/dist/{owner-CptqhsOy.d.cts → owner-CnngXDNJ.d.ts} +2 -1
  93. package/dist/{owner-74ARJ5TL.d.ts → owner-DEvZuyOE.d.cts} +2 -3
  94. package/dist/{proof-Bbbwf8_x.js → proof-CyXndf6-.js} +378 -15
  95. package/dist/{proof-BlmRPzrK.mjs → proof-Dnz8jtiQ.mjs} +26 -8
  96. package/dist/{proof-BW89QMVB.cjs → proof-tz35unOj.cjs} +432 -15
  97. package/dist/runtime/mod.d.cts +1 -0
  98. package/dist/runtime/mod.d.ts +1 -2
  99. package/dist/runtime/mod.js +3 -3
  100. package/dist/{send-05pJLcb6.mjs → send-DIbBYN5P.mjs} +72 -26
  101. package/dist/sig/http.test.mjs +356 -33
  102. package/dist/sig/key.test.mjs +103 -6
  103. package/dist/sig/ld.test.mjs +696 -7
  104. package/dist/sig/mod.cjs +2 -2
  105. package/dist/sig/mod.d.cts +4 -3
  106. package/dist/sig/mod.d.ts +4 -5
  107. package/dist/sig/mod.js +4 -4
  108. package/dist/sig/owner.test.mjs +6 -6
  109. package/dist/sig/proof.test.mjs +169 -8
  110. package/dist/{std__assert-CRDpx_HF.mjs → std__assert-BBjXFNOb.mjs} +5 -30
  111. package/dist/temporal-BEwob1Vg.mjs +95 -0
  112. package/dist/testing/mod.d.mts +110 -10
  113. package/dist/testing/mod.mjs +1 -2
  114. package/dist/{transformers-ve6e2xcg.js → transformers-BGMIq1cs.js} +2 -2
  115. package/dist/{types-hvL8ElAs.js → types-CAY3OdLq.js} +2 -2
  116. package/dist/utils/docloader.test.mjs +6 -6
  117. package/dist/utils/kv-cache.test.mjs +67 -2
  118. package/dist/utils/mod.cjs +1 -1
  119. package/dist/utils/mod.d.cts +2 -1
  120. package/dist/utils/mod.d.ts +2 -3
  121. package/dist/utils/mod.js +3 -3
  122. package/dist/vocab/cjs.test.mjs +1 -1
  123. package/dist/vocab/mod.d.cts +1 -0
  124. package/dist/vocab/mod.d.ts +1 -2
  125. package/dist/vocab/mod.js +2 -2
  126. package/package.json +16 -16
  127. package/dist/ld-DR78beiv.mjs +0 -279
  128. package/dist/middleware-BFBaR0mF.cjs +0 -4
  129. package/dist/mod-2d12ffz3.d.ts +0 -64
  130. package/dist/mod-D35TRn09.d.cts +0 -62
  131. package/dist/router-CrMLXoOr.mjs +0 -114
  132. package/dist/{activity-listener-ell7W1s9.mjs → activity-listener-tztVvlNb.mjs} +0 -0
  133. package/dist/{assert_equals-Ew3jOFa3.mjs → assert_equals-C-ZRDbaf.mjs} +0 -0
  134. package/dist/{client-D_1QpnWt.mjs → client-ByXmQhYD.mjs} +1 -1
  135. package/dist/{collection-D-HqUuA2.mjs → collection-Cc3DVAhE.mjs} +0 -0
  136. package/dist/{keycache-EGATflN-.mjs → keycache-BeU0LCII.mjs} +0 -0
  137. package/dist/{public-audience-DYFHzm_c.mjs → public-audience-Cvbr2Gzt.mjs} +1 -1
  138. /package/dist/{retry-bMXBL97A.mjs → retry-CXg_MBI-.mjs} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
- import "urlpattern-polyfill";
3
- import { _ as version, d as validateCryptoKey, g as name, s as fetchKey } from "./http-B4rrzxtg.js";
2
+ import { URLPattern } from "urlpattern-polyfill";
3
+ import { F as version, P as name, _ as measureSignatureKeyFetch, d as validateCryptoKey, f as getDurationMs, p as getFederationMetrics, s as fetchKey } from "./http-Dh18nwJg.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { Activity, CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1, PUBLIC_COLLECTION, getTypeId, isActor } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -15,6 +15,290 @@ const logger$3 = getLogger([
15
15
  "sig",
16
16
  "ld"
17
17
  ]);
18
+ const localContext = [
19
+ "https://w3id.org/identity/v1",
20
+ "https://www.w3.org/ns/activitystreams",
21
+ "https://w3id.org/security/v1",
22
+ "https://w3id.org/security/data-integrity/v1"
23
+ ];
24
+ const localContextUrls = new Set(localContext);
25
+ const builtInContextLoader = getDocumentLoader();
26
+ const disallowedJsonLdKeywords = new Set([
27
+ "@graph",
28
+ "@included",
29
+ "@reverse"
30
+ ]);
31
+ /** @internal */
32
+ var UnsafeJsonLdError = class extends TypeError {
33
+ keyword;
34
+ constructor(keyword) {
35
+ super(`Unsupported JSON-LD keyword: ${keyword}.`);
36
+ this.keyword = keyword;
37
+ this.name = "UnsafeJsonLdError";
38
+ }
39
+ };
40
+ /** @internal */
41
+ var InvalidContextReferenceError = class extends TypeError {
42
+ reference;
43
+ constructor(reference) {
44
+ super(`Invalid JSON-LD context reference: ${reference}.`);
45
+ this.reference = reference;
46
+ this.name = "InvalidContextReferenceError";
47
+ }
48
+ };
49
+ function createLoadingRemoteContextFailedError(reference, cause) {
50
+ const message = cause instanceof Error ? cause.message : String(cause);
51
+ const error = /* @__PURE__ */ new Error(`Dereferencing a URL did not result in a valid JSON-LD context: ${reference}. ${message}`);
52
+ error.name = "jsonld.InvalidUrl";
53
+ error.details = {
54
+ code: "loading remote context failed",
55
+ url: reference
56
+ };
57
+ error.cause = cause;
58
+ return error;
59
+ }
60
+ /** @internal */
61
+ function isClearlyMalformedContextReference(reference) {
62
+ for (const char of reference) {
63
+ const code = char.charCodeAt(0);
64
+ if (code <= 32 || code === 127) return true;
65
+ }
66
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(reference) && !URL.canParse(reference)) return true;
67
+ for (let i = 0; i < reference.length; i++) {
68
+ if (reference[i] !== "%") continue;
69
+ if (i + 2 >= reference.length || !/[0-9A-Fa-f]/.test(reference[i + 1]) || !/[0-9A-Fa-f]/.test(reference[i + 2])) return true;
70
+ i += 2;
71
+ }
72
+ if (reference.startsWith("./") || reference.startsWith("../") || reference.startsWith("/") || reference.startsWith("//")) {
73
+ for (const char of reference) if ("[]<>\"\\^`{|}".includes(char)) return true;
74
+ }
75
+ return false;
76
+ }
77
+ function cloneRemoteDocument(remoteDocument) {
78
+ return structuredClone(remoteDocument);
79
+ }
80
+ function createMemoizedDocumentLoader(documentLoader) {
81
+ const cache = /* @__PURE__ */ new Map();
82
+ return async (url, options) => {
83
+ const cacheKey = URL.canParse(url) ? new URL(url).href : url;
84
+ let remoteDocument = cache.get(cacheKey);
85
+ if (remoteDocument == null) {
86
+ remoteDocument = Promise.resolve(documentLoader(url, options)).then(cloneRemoteDocument);
87
+ remoteDocument.catch(() => {
88
+ if (cache.get(cacheKey) === remoteDocument) cache.delete(cacheKey);
89
+ });
90
+ cache.set(cacheKey, remoteDocument);
91
+ }
92
+ return cloneRemoteDocument(await remoteDocument);
93
+ };
94
+ }
95
+ /** @internal */
96
+ function wrapContextLoaderForJsonLd(contextLoader) {
97
+ const loader = contextLoader ?? builtInContextLoader;
98
+ return async (url, options) => {
99
+ try {
100
+ return await loader(url, options);
101
+ } catch (error) {
102
+ if (!isInvalidUrlTypeError(error)) throw error;
103
+ if (isClearlyMalformedContextReference(url)) throw new InvalidContextReferenceError(url);
104
+ throw createLoadingRemoteContextFailedError(url, error);
105
+ }
106
+ };
107
+ }
108
+ /** @internal */
109
+ function getNormalizationContextLoader(contextLoader) {
110
+ const loader = wrapContextLoaderForJsonLd(contextLoader);
111
+ return createMemoizedDocumentLoader(async (url, options) => {
112
+ if (URL.canParse(url)) {
113
+ const normalizedUrl = new URL(url).href;
114
+ if (localContextUrls.has(normalizedUrl)) return await builtInContextLoader(normalizedUrl, options);
115
+ }
116
+ return await loader(url, options);
117
+ });
118
+ }
119
+ /** @internal */
120
+ async function compactJsonLd(jsonLd, contextLoader) {
121
+ const hasLds = typeof jsonLd === "object" && jsonLd != null && "signature" in jsonLd;
122
+ const signature = hasLds ? jsonLd.signature : void 0;
123
+ const normalizationContextLoader = getNormalizationContextLoader(contextLoader);
124
+ const document = hasLds ? detachSignature(jsonLd) : jsonLd;
125
+ await assertNoGraphBeforeCompaction(document, normalizationContextLoader);
126
+ const compacted = await jsonld.compact(document, localContext, { documentLoader: normalizationContextLoader });
127
+ if (hasLds && typeof compacted === "object" && compacted != null) compacted.signature = signature;
128
+ assertSafeJsonLd(compacted);
129
+ return compacted;
130
+ }
131
+ function createInvalidRemoteContextError(reference) {
132
+ const error = /* @__PURE__ */ new Error(`Dereferencing a URL did not result in a JSON object. The response was valid JSON, but it was not a JSON object. URL: "${reference}".`);
133
+ error.name = "jsonld.InvalidUrl";
134
+ error.details = {
135
+ code: "invalid remote context",
136
+ url: reference
137
+ };
138
+ return error;
139
+ }
140
+ function getRemoteContext(remoteDocument, reference) {
141
+ const { contextUrl, documentUrl } = remoteDocument;
142
+ let { document } = remoteDocument;
143
+ if (typeof document === "string") document = JSON.parse(document);
144
+ if (typeof document !== "object" || document == null || Array.isArray(document)) throw createInvalidRemoteContextError(reference);
145
+ let context = "@context" in document ? document["@context"] : {};
146
+ if (contextUrl != null) context = Array.isArray(context) ? [...context, contextUrl] : [context, contextUrl];
147
+ return {
148
+ context,
149
+ baseUrl: documentUrl ?? reference
150
+ };
151
+ }
152
+ function createGraphAliasContextState() {
153
+ return {
154
+ graphTerms: /* @__PURE__ */ new Set(),
155
+ jsonTerms: /* @__PURE__ */ new Set(),
156
+ propertyContexts: /* @__PURE__ */ new Map(),
157
+ termTargets: /* @__PURE__ */ new Map()
158
+ };
159
+ }
160
+ function cloneGraphAliasContextState(state) {
161
+ return {
162
+ graphTerms: new Set(state.graphTerms),
163
+ jsonTerms: new Set(state.jsonTerms),
164
+ propertyContexts: new Map(state.propertyContexts),
165
+ termTargets: new Map(state.termTargets)
166
+ };
167
+ }
168
+ function resolveContextTarget(target, state) {
169
+ if (target === "@graph") return target;
170
+ const mapped = state.termTargets.get(target);
171
+ if (mapped == null) return target;
172
+ return mapped;
173
+ }
174
+ function getDirectContextTarget(definition) {
175
+ if (definition === null) return null;
176
+ if (typeof definition === "string") return definition;
177
+ if (typeof definition === "object" && definition != null && "@id" in definition) {
178
+ const id = definition["@id"];
179
+ if (id === null) return null;
180
+ if (typeof id === "string") return id;
181
+ }
182
+ }
183
+ function isJsonTypedDefinition(definition) {
184
+ return typeof definition === "object" && definition != null && "@type" in definition && definition["@type"] === "@json";
185
+ }
186
+ function resolveLocalContextTarget(target, state, localTargets, seen = /* @__PURE__ */ new Set()) {
187
+ if (target === "@graph") return target;
188
+ if (seen.has(target)) return target;
189
+ seen.add(target);
190
+ if (localTargets.has(target)) {
191
+ const localTarget = localTargets.get(target);
192
+ return localTarget == null ? target : resolveLocalContextTarget(localTarget, state, localTargets, seen);
193
+ }
194
+ return resolveContextTarget(target, state);
195
+ }
196
+ function refreshGraphAliases(state) {
197
+ state.graphTerms.clear();
198
+ for (const [term, target] of state.termTargets) if (target === "@graph") state.graphTerms.add(term);
199
+ }
200
+ function normalizeContextReference(reference, baseUrl) {
201
+ if (baseUrl != null) return new URL(reference, baseUrl).href;
202
+ return URL.canParse(reference) ? new URL(reference).href : reference;
203
+ }
204
+ /** @internal */
205
+ function isInvalidUrlTypeError(error) {
206
+ const code = error.code;
207
+ return error instanceof TypeError && (code === "ERR_INVALID_URL" || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
208
+ }
209
+ async function applyGraphAliasContext(state, context, documentLoader, remoteContextCache, baseUrl = null, processingContexts = /* @__PURE__ */ new Set()) {
210
+ if (context === null) return createGraphAliasContextState();
211
+ let nextState = cloneGraphAliasContextState(state);
212
+ if (Array.isArray(context)) {
213
+ for (const item of context) nextState = await applyGraphAliasContext(nextState, item, documentLoader, remoteContextCache, baseUrl, processingContexts);
214
+ return nextState;
215
+ }
216
+ if (typeof context === "string") {
217
+ const reference = normalizeContextReference(context, baseUrl);
218
+ const cacheKey = `${baseUrl ?? ""}\n${reference}`;
219
+ if (processingContexts.has(cacheKey)) return nextState;
220
+ processingContexts.add(cacheKey);
221
+ try {
222
+ let remoteContext = remoteContextCache.get(cacheKey);
223
+ if (remoteContext == null) {
224
+ remoteContext = (async () => {
225
+ try {
226
+ return getRemoteContext(await documentLoader(reference), reference);
227
+ } catch (error) {
228
+ if (reference === context && isInvalidUrlTypeError(error) && isClearlyMalformedContextReference(context)) throw new InvalidContextReferenceError(context);
229
+ throw error;
230
+ }
231
+ })();
232
+ remoteContextCache.set(cacheKey, remoteContext);
233
+ }
234
+ const loadedRemoteContext = await remoteContext;
235
+ return await applyGraphAliasContext(nextState, loadedRemoteContext.context, documentLoader, remoteContextCache, loadedRemoteContext.baseUrl, processingContexts);
236
+ } finally {
237
+ processingContexts.delete(cacheKey);
238
+ }
239
+ }
240
+ if (typeof context === "object" && context != null) {
241
+ if ("@import" in context && typeof context["@import"] === "string") nextState = await applyGraphAliasContext(nextState, context["@import"], documentLoader, remoteContextCache, baseUrl, processingContexts);
242
+ const localTargets = /* @__PURE__ */ new Map();
243
+ for (const [term, definition] of globalThis.Object.entries(context)) {
244
+ if (term.startsWith("@")) continue;
245
+ const target = getDirectContextTarget(definition);
246
+ if (target == null) localTargets.set(term, null);
247
+ else if (typeof target === "string") localTargets.set(term, target);
248
+ else localTargets.delete(term);
249
+ }
250
+ for (const [term, definition] of globalThis.Object.entries(context)) {
251
+ if (term.startsWith("@")) continue;
252
+ if (localTargets.has(term)) {
253
+ const directTarget = localTargets.get(term);
254
+ if (directTarget == null) nextState.termTargets.set(term, null);
255
+ else nextState.termTargets.set(term, resolveLocalContextTarget(directTarget, nextState, localTargets));
256
+ } else nextState.termTargets.delete(term);
257
+ if (typeof definition === "object" && definition != null && "@context" in definition) nextState.propertyContexts.set(term, {
258
+ context: definition["@context"],
259
+ baseUrl
260
+ });
261
+ else nextState.propertyContexts.delete(term);
262
+ if (isJsonTypedDefinition(definition)) nextState.jsonTerms.add(term);
263
+ else nextState.jsonTerms.delete(term);
264
+ }
265
+ refreshGraphAliases(nextState);
266
+ }
267
+ return nextState;
268
+ }
269
+ async function assertNoGraphBeforeCompaction(jsonLd, documentLoader, inheritedState = createGraphAliasContextState(), propertyContext, remoteContextCache = /* @__PURE__ */ new Map()) {
270
+ if (Array.isArray(jsonLd)) {
271
+ for (const item of jsonLd) await assertNoGraphBeforeCompaction(item, documentLoader, inheritedState, propertyContext, remoteContextCache);
272
+ return;
273
+ }
274
+ if (typeof jsonLd !== "object" || jsonLd == null) return;
275
+ const jsonLiteralWrapper = isJsonLiteralWrapper(jsonLd);
276
+ let state = inheritedState;
277
+ if (propertyContext !== void 0) state = await applyGraphAliasContext(state, propertyContext.context, documentLoader, remoteContextCache, propertyContext.baseUrl);
278
+ if ("@context" in jsonLd) state = await applyGraphAliasContext(state, jsonLd["@context"], documentLoader, remoteContextCache);
279
+ for (const [key, value] of globalThis.Object.entries(jsonLd)) {
280
+ if (key === "@context") continue;
281
+ if (jsonLiteralWrapper && key === "@value") continue;
282
+ if (key === "@graph" || state.graphTerms.has(key)) throw new UnsafeJsonLdError("@graph");
283
+ if (state.jsonTerms.has(key)) continue;
284
+ await assertNoGraphBeforeCompaction(value, documentLoader, state, state.propertyContexts.get(key), remoteContextCache);
285
+ }
286
+ }
287
+ function isJsonLiteralWrapper(value) {
288
+ return "@value" in value && (value["@type"] === "@json" || value.type === "@json");
289
+ }
290
+ /** @internal */
291
+ function assertSafeJsonLd(jsonLd) {
292
+ if (Array.isArray(jsonLd)) for (const item of jsonLd) assertSafeJsonLd(item);
293
+ else if (typeof jsonLd === "object" && jsonLd != null) {
294
+ const jsonLiteralWrapper = isJsonLiteralWrapper(jsonLd);
295
+ for (const [key, value] of globalThis.Object.entries(jsonLd)) {
296
+ if (disallowedJsonLdKeywords.has(key)) throw new UnsafeJsonLdError(key);
297
+ if (jsonLiteralWrapper && key === "@value") continue;
298
+ assertSafeJsonLd(value);
299
+ }
300
+ }
301
+ }
18
302
  /**
19
303
  * Attaches a LD signature to the given JSON-LD document.
20
304
  * @param jsonLd The JSON-LD document to attach the signature to. It is not
@@ -145,6 +429,17 @@ function detachSignature(jsonLd) {
145
429
  }
146
430
  /**
147
431
  * Verifies Linked Data Signatures of the given JSON-LD document.
432
+ *
433
+ * This is a low-level utility that only checks the cryptographic signature
434
+ * and (optionally) the cached key. It does not run the JSON-LD parsing,
435
+ * attribution, and owner checks that a complete inbound LD verification
436
+ * needs. For incoming activities, prefer {@link verifyJsonLd}, which is
437
+ * the public verification entry point and the one that emits the
438
+ * `activitypub.signature.verification.duration` metric for the LD path.
439
+ * `verifySignature` itself only emits
440
+ * `activitypub.signature.key_fetch.duration`, since the rest of the work
441
+ * that the verification-duration metric is meant to cover happens in
442
+ * `verifyJsonLd`.
148
443
  * @param jsonLd The JSON-LD document to verify.
149
444
  * @param options Options for verifying the signature.
150
445
  * @returns The public key that signed the document or `null` if the signature
@@ -164,7 +459,7 @@ async function verifySignature(jsonLd, options = {}) {
164
459
  });
165
460
  return null;
166
461
  }
167
- const { key, cached } = await fetchKey(new URL(sig.creator), CryptographicKey, options);
462
+ const { key, cached } = await measureSignatureKeyFetch(options.meterProvider, "linked_data", () => fetchKey(new URL(sig.creator), CryptographicKey, options));
168
463
  if (key == null) return null;
169
464
  const sigOpts = {
170
465
  ...sig,
@@ -204,13 +499,13 @@ async function verifySignature(jsonLd, options = {}) {
204
499
  keyId: sig.creator,
205
500
  ...sig
206
501
  });
207
- const { key } = await fetchKey(new URL(sig.creator), CryptographicKey, {
502
+ const { key } = await measureSignatureKeyFetch(options.meterProvider, "linked_data", () => fetchKey(new URL(sig.creator), CryptographicKey, {
208
503
  ...options,
209
504
  keyCache: {
210
505
  get: () => Promise.resolve(void 0),
211
506
  set: async (keyId, key) => await options.keyCache?.set(keyId, key)
212
507
  }
213
- });
508
+ }));
214
509
  if (key == null) return null;
215
510
  return await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature.slice(), messageBytes) ? key : null;
216
511
  }
@@ -222,6 +517,33 @@ async function verifySignature(jsonLd, options = {}) {
222
517
  return null;
223
518
  }
224
519
  /**
520
+ * Known Linked Data Signature `type` values, used to keep
521
+ * `ld_signatures.type` on a bounded set of spec-defined string values.
522
+ * Fedify only signs and verifies `RsaSignature2017`; other values come in
523
+ * only from external documents and are dropped from the metric attribute to
524
+ * avoid attacker-controlled cardinality.
525
+ */
526
+ const LD_KNOWN_SIGNATURE_TYPES = new Set(["RsaSignature2017"]);
527
+ /**
528
+ * Reports only whether a `signature` key is present on the document, with
529
+ * no shape check on its value. This is intentionally looser than
530
+ * {@link hasSignature} (which validates a full `RsaSignature2017` shape)
531
+ * and {@link hasSignatureLike} (which structurally accepts several known
532
+ * suites): `verifyJsonLd` needs to tell a document with a malformed or
533
+ * unsupported signature payload (classified as `rejected`) apart from a
534
+ * truly unsigned document (classified as `missing`), and only this
535
+ * presence-only check captures both cases.
536
+ */
537
+ function hasLdSignatureProperty(jsonLd) {
538
+ return typeof jsonLd === "object" && jsonLd != null && "signature" in jsonLd;
539
+ }
540
+ function getLdSignatureObject(jsonLd) {
541
+ if (!hasLdSignatureProperty(jsonLd)) return void 0;
542
+ const { signature } = jsonLd;
543
+ if (typeof signature !== "object" || signature == null || Array.isArray(signature)) return;
544
+ return signature;
545
+ }
546
+ /**
225
547
  * Verify the authenticity of the given JSON-LD document using Linked Data
226
548
  * Signatures. If the document is signed, this function verifies the signature
227
549
  * and checks if the document is attributed to the owner of the public key.
@@ -231,19 +553,39 @@ async function verifySignature(jsonLd, options = {}) {
231
553
  * @returns `true` if the document is authentic; `false` otherwise.
232
554
  */
233
555
  async function verifyJsonLd(jsonLd, options = {}) {
556
+ return await verifyJsonLdInternal(jsonLd, options, true);
557
+ }
558
+ /** @internal */
559
+ async function verifyCompactJsonLd(jsonLd, options = {}) {
560
+ return await verifyJsonLdInternal(jsonLd, options, false);
561
+ }
562
+ async function verifyJsonLdInternal(jsonLd, options, compact) {
234
563
  return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("ld_signatures.verify", async (span) => {
564
+ const start = performance.now();
565
+ let verified = false;
566
+ let threw = false;
567
+ let signatureType;
235
568
  try {
236
- const object = await Object$1.fromJsonLd(jsonLd, options);
569
+ const verificationOptions = hasSignature(jsonLd) ? {
570
+ ...options,
571
+ contextLoader: getNormalizationContextLoader(options.contextLoader)
572
+ } : options;
573
+ const compacted = compact ? hasSignature(jsonLd) ? await compactJsonLd(jsonLd, options.contextLoader) : jsonLd : jsonLd;
574
+ const object = await Object$1.fromJsonLd(compacted, verificationOptions);
237
575
  if (object.id != null) span.setAttribute("activitypub.object.id", object.id.href);
238
576
  span.setAttribute("activitypub.object.type", getTypeId(object).href);
239
- if (typeof jsonLd === "object" && jsonLd != null && "signature" in jsonLd && typeof jsonLd.signature === "object" && jsonLd.signature != null) {
240
- if ("creator" in jsonLd.signature && typeof jsonLd.signature.creator === "string") span.setAttribute("ld_signatures.key_id", jsonLd.signature.creator);
241
- if ("signatureValue" in jsonLd.signature && typeof jsonLd.signature.signatureValue === "string") span.setAttribute("ld_signatures.signature", jsonLd.signature.signatureValue);
242
- if ("type" in jsonLd.signature && typeof jsonLd.signature.type === "string") span.setAttribute("ld_signatures.type", jsonLd.signature.type);
577
+ const sig = getLdSignatureObject(jsonLd);
578
+ if (sig != null) {
579
+ if (typeof sig.creator === "string") span.setAttribute("ld_signatures.key_id", sig.creator);
580
+ if (typeof sig.signatureValue === "string") span.setAttribute("ld_signatures.signature", sig.signatureValue);
581
+ if (typeof sig.type === "string") {
582
+ span.setAttribute("ld_signatures.type", sig.type);
583
+ if (LD_KNOWN_SIGNATURE_TYPES.has(sig.type)) signatureType = sig.type;
584
+ }
243
585
  }
244
586
  const attributions = new Set(object.attributionIds.map((uri) => uri.href));
245
587
  if (object instanceof Activity) for (const uri of object.actorIds) attributions.add(uri.href);
246
- const key = await verifySignature(jsonLd, options);
588
+ const key = await verifySignature(compacted, verificationOptions);
247
589
  if (key == null) return false;
248
590
  if (key.ownerId == null) {
249
591
  logger$3.debug("Key {keyId} has no owner.", { keyId: key.id?.href });
@@ -254,14 +596,18 @@ async function verifyJsonLd(jsonLd, options = {}) {
254
596
  logger$3.debug("Some attributions are not authenticated by the Linked Data Signatures: {attributions}.", { attributions: [...attributions] });
255
597
  return false;
256
598
  }
599
+ verified = true;
257
600
  return true;
258
601
  } catch (error) {
602
+ threw = true;
259
603
  span.setStatus({
260
604
  code: SpanStatusCode.ERROR,
261
605
  message: String(error)
262
606
  });
263
607
  throw error;
264
608
  } finally {
609
+ const classified = threw ? "error" : verified ? "verified" : hasLdSignatureProperty(jsonLd) ? "rejected" : "missing";
610
+ getFederationMetrics(options.meterProvider).recordSignatureVerificationDuration(getDurationMs(start), "linked_data", classified, { ldType: signatureType });
265
611
  span.end();
266
612
  }
267
613
  });
@@ -766,6 +1112,15 @@ async function normalizeOutgoingActivityJsonLd(jsonLd, contextLoader) {
766
1112
  }
767
1113
  //#endregion
768
1114
  //#region src/sig/proof.ts
1115
+ /**
1116
+ * Known Object Integrity Proof `cryptosuite` values, used to keep
1117
+ * `object_integrity_proofs.cryptosuite` on a bounded set of spec-defined
1118
+ * string values. Fedify currently signs and verifies only
1119
+ * `eddsa-jcs-2022`; other values come in only from external proofs and are
1120
+ * dropped from the metric attribute to avoid attacker-controlled
1121
+ * cardinality.
1122
+ */
1123
+ const OIP_KNOWN_CRYPTOSUITES = new Set(["eddsa-jcs-2022"]);
769
1124
  const logger = getLogger([
770
1125
  "fedify",
771
1126
  "sig",
@@ -892,6 +1247,10 @@ async function signObject(object, privateKey, keyId, options = {}) {
892
1247
  */
893
1248
  async function verifyProof(jsonLd, proof, options = {}) {
894
1249
  return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("object_integrity_proofs.verify", async (span) => {
1250
+ const start = performance.now();
1251
+ let verified = false;
1252
+ let threw = false;
1253
+ const cryptosuite = proof.cryptosuite != null && OIP_KNOWN_CRYPTOSUITES.has(proof.cryptosuite) ? proof.cryptosuite : void 0;
895
1254
  if (span.isRecording()) {
896
1255
  if (proof.cryptosuite != null) span.setAttribute("object_integrity_proofs.cryptosuite", proof.cryptosuite);
897
1256
  if (proof.verificationMethodId != null) span.setAttribute("object_integrity_proofs.key_id", proof.verificationMethodId.href);
@@ -900,21 +1259,25 @@ async function verifyProof(jsonLd, proof, options = {}) {
900
1259
  try {
901
1260
  const key = await verifyProofInternal(jsonLd, proof, options);
902
1261
  if (key == null) span.setStatus({ code: SpanStatusCode.ERROR });
1262
+ else verified = true;
903
1263
  return key;
904
1264
  } catch (error) {
1265
+ threw = true;
905
1266
  span.setStatus({
906
1267
  code: SpanStatusCode.ERROR,
907
1268
  message: String(error)
908
1269
  });
909
1270
  throw error;
910
1271
  } finally {
1272
+ const classified = threw ? "error" : verified ? "verified" : "rejected";
1273
+ getFederationMetrics(options.meterProvider).recordSignatureVerificationDuration(getDurationMs(start), "object_integrity", classified, { cryptosuite });
911
1274
  span.end();
912
1275
  }
913
1276
  });
914
1277
  }
915
1278
  async function verifyProofInternal(jsonLd, proof, options) {
916
1279
  if (typeof jsonLd !== "object" || jsonLd == null || Array.isArray(jsonLd) || proof.cryptosuite !== "eddsa-jcs-2022" || proof.verificationMethodId == null || proof.proofPurpose !== "assertionMethod" || proof.proofValue == null || proof.created == null) return null;
917
- const publicKeyPromise = fetchKey(proof.verificationMethodId, Multikey, options);
1280
+ const publicKeyPromise = measureSignatureKeyFetch(options.meterProvider, "object_integrity", () => fetchKey(proof.verificationMethodId, Multikey, options));
918
1281
  const proofConfig = {
919
1282
  "@context": jsonLd["@context"],
920
1283
  type: "DataIntegrityProof",
@@ -954,7 +1317,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
954
1317
  proof,
955
1318
  keyId: proof.verificationMethodId.href
956
1319
  });
957
- return await verifyProof(jsonLd, proof, {
1320
+ return await verifyProofInternal(jsonLd, proof, {
958
1321
  ...options,
959
1322
  keyCache: {
960
1323
  get: () => Promise.resolve(void 0),
@@ -985,7 +1348,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
985
1348
  keyId: proof.verificationMethodId.href,
986
1349
  proof
987
1350
  });
988
- return await verifyProof(jsonLd, proof, {
1351
+ return await verifyProofInternal(jsonLd, proof, {
989
1352
  ...options,
990
1353
  keyCache: {
991
1354
  get: () => Promise.resolve(void 0),
@@ -1038,4 +1401,4 @@ async function verifyObject(cls, jsonLd, options = {}) {
1038
1401
  return object;
1039
1402
  }
1040
1403
  //#endregion
1041
- export { verifyProof as a, getKeyOwner as c, detachSignature as d, hasSignatureLike as f, verifySignature as h, verifyObject as i, attachSignature as l, verifyJsonLd as m, hasProofLike as n, normalizeOutgoingActivityJsonLd as o, signJsonLd as p, signObject as r, doesActorOwnKey as s, createProof as t, createSignature as u };
1404
+ export { verifySignature as C, verifyJsonLd as S, hasSignatureLike as _, verifyProof as a, signJsonLd as b, getKeyOwner as c, attachSignature as d, compactJsonLd as f, hasSignature as g, getNormalizationContextLoader as h, verifyObject as i, InvalidContextReferenceError as l, detachSignature as m, hasProofLike as n, normalizeOutgoingActivityJsonLd as o, createSignature as p, signObject as r, doesActorOwnKey as s, createProof as t, assertSafeJsonLd as u, isClearlyMalformedContextReference as v, wrapContextLoaderForJsonLd as w, verifyCompactJsonLd as x, isInvalidUrlTypeError as y };
@@ -1,16 +1,26 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-XBVlKnOX.mjs";
5
- import { n as fetchKey, o as validateCryptoKey } from "./key-Bzx--sHD.mjs";
6
- import { n as preloadedOnlyDocumentLoader } from "./public-audience-DYFHzm_c.mjs";
7
- import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-CNmZLixq.mjs";
4
+ import { n as version, t as name } from "./deno-DCdM7d93.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BHeWd24f.mjs";
6
+ import { n as fetchKey, o as validateCryptoKey } from "./key-DZdKLJXT.mjs";
7
+ import { n as preloadedOnlyDocumentLoader } from "./public-audience-Cvbr2Gzt.mjs";
8
+ import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-L_DbOaFe.mjs";
9
+ import { getLogger } from "@logtape/logtape";
8
10
  import { Activity, DataIntegrityProof, Multikey, getTypeId } from "@fedify/vocab";
9
11
  import { SpanStatusCode, trace } from "@opentelemetry/api";
10
- import { getLogger } from "@logtape/logtape";
11
12
  import { encodeHex } from "byte-encodings/hex";
12
13
  import serialize from "json-canon";
13
14
  //#region src/sig/proof.ts
15
+ /**
16
+ * Known Object Integrity Proof `cryptosuite` values, used to keep
17
+ * `object_integrity_proofs.cryptosuite` on a bounded set of spec-defined
18
+ * string values. Fedify currently signs and verifies only
19
+ * `eddsa-jcs-2022`; other values come in only from external proofs and are
20
+ * dropped from the metric attribute to avoid attacker-controlled
21
+ * cardinality.
22
+ */
23
+ const OIP_KNOWN_CRYPTOSUITES = new Set(["eddsa-jcs-2022"]);
14
24
  const logger = getLogger([
15
25
  "fedify",
16
26
  "sig",
@@ -137,6 +147,10 @@ async function signObject(object, privateKey, keyId, options = {}) {
137
147
  */
138
148
  async function verifyProof(jsonLd, proof, options = {}) {
139
149
  return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("object_integrity_proofs.verify", async (span) => {
150
+ const start = performance.now();
151
+ let verified = false;
152
+ let threw = false;
153
+ const cryptosuite = proof.cryptosuite != null && OIP_KNOWN_CRYPTOSUITES.has(proof.cryptosuite) ? proof.cryptosuite : void 0;
140
154
  if (span.isRecording()) {
141
155
  if (proof.cryptosuite != null) span.setAttribute("object_integrity_proofs.cryptosuite", proof.cryptosuite);
142
156
  if (proof.verificationMethodId != null) span.setAttribute("object_integrity_proofs.key_id", proof.verificationMethodId.href);
@@ -145,21 +159,25 @@ async function verifyProof(jsonLd, proof, options = {}) {
145
159
  try {
146
160
  const key = await verifyProofInternal(jsonLd, proof, options);
147
161
  if (key == null) span.setStatus({ code: SpanStatusCode.ERROR });
162
+ else verified = true;
148
163
  return key;
149
164
  } catch (error) {
165
+ threw = true;
150
166
  span.setStatus({
151
167
  code: SpanStatusCode.ERROR,
152
168
  message: String(error)
153
169
  });
154
170
  throw error;
155
171
  } finally {
172
+ const classified = threw ? "error" : verified ? "verified" : "rejected";
173
+ getFederationMetrics(options.meterProvider).recordSignatureVerificationDuration(getDurationMs(start), "object_integrity", classified, { cryptosuite });
156
174
  span.end();
157
175
  }
158
176
  });
159
177
  }
160
178
  async function verifyProofInternal(jsonLd, proof, options) {
161
179
  if (typeof jsonLd !== "object" || jsonLd == null || Array.isArray(jsonLd) || proof.cryptosuite !== "eddsa-jcs-2022" || proof.verificationMethodId == null || proof.proofPurpose !== "assertionMethod" || proof.proofValue == null || proof.created == null) return null;
162
- const publicKeyPromise = fetchKey(proof.verificationMethodId, Multikey, options);
180
+ const publicKeyPromise = measureSignatureKeyFetch(options.meterProvider, "object_integrity", () => fetchKey(proof.verificationMethodId, Multikey, options));
163
181
  const proofConfig = {
164
182
  "@context": jsonLd["@context"],
165
183
  type: "DataIntegrityProof",
@@ -199,7 +217,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
199
217
  proof,
200
218
  keyId: proof.verificationMethodId.href
201
219
  });
202
- return await verifyProof(jsonLd, proof, {
220
+ return await verifyProofInternal(jsonLd, proof, {
203
221
  ...options,
204
222
  keyCache: {
205
223
  get: () => Promise.resolve(void 0),
@@ -230,7 +248,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
230
248
  keyId: proof.verificationMethodId.href,
231
249
  proof
232
250
  });
233
- return await verifyProof(jsonLd, proof, {
251
+ return await verifyProofInternal(jsonLd, proof, {
234
252
  ...options,
235
253
  keyCache: {
236
254
  get: () => Promise.resolve(void 0),