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

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 (141) hide show
  1. package/README.md +3 -0
  2. package/dist/{activity-listener-C-BSbKI7.mjs → activity-listener-CELFAodc.mjs} +8 -4
  3. package/dist/{builder-Da8b9O6v.mjs → builder-DphK9v7f.mjs} +5 -5
  4. package/dist/{client-D-XP_Ccb.d.ts → client-3InDZbba.d.cts} +0 -1
  5. package/dist/{client-D-XP_Ccb.d.cts → client-3InDZbba.d.ts} +0 -1
  6. package/dist/{client-CEnNz8O7.mjs → client-5SIFzW3q.mjs} +113 -64
  7. package/dist/compat/mod.d.cts +1 -1
  8. package/dist/compat/mod.d.ts +1 -1
  9. package/dist/compat/transformers.test.mjs +1 -1
  10. package/dist/{compound-proof-D1djO5fa.mjs → compound-proof-cYtvlTgF.mjs} +2 -2
  11. package/dist/{context-8Tu9kea0.mjs → context-COHd1Rsh.mjs} +2 -0
  12. package/dist/{context-BdB54V-A.d.ts → context-DQJE9YfH.d.ts} +332 -6
  13. package/dist/{context-CQ9H8vNW.d.cts → context-DSuL3YWx.d.cts} +332 -6
  14. package/dist/{deno-eNpfj4oR.mjs → deno-iOAA7iRN.mjs} +1 -1
  15. package/dist/docloader-DCZkiTeF.mjs +136 -0
  16. package/dist/{exporter-DThFfe7P.mjs → exporter-DVG68ozH.mjs} +36 -30
  17. package/dist/federation/builder.test.mjs +1 -1
  18. package/dist/federation/compound-fanout.test.mjs +6 -3
  19. package/dist/federation/handler-compound-proof.test.mjs +5 -5
  20. package/dist/federation/handler.test.mjs +22 -7
  21. package/dist/federation/hashlink-media.test.mjs +2 -2
  22. package/dist/federation/idempotency.test.mjs +2 -2
  23. package/dist/federation/inbox-actorless.test.d.mts +2 -0
  24. package/dist/federation/inbox-actorless.test.mjs +487 -0
  25. package/dist/federation/inbox-authentication.test.d.mts +2 -0
  26. package/dist/federation/inbox-authentication.test.mjs +116 -0
  27. package/dist/federation/inbox-report.test.mjs +5 -5
  28. package/dist/federation/inbox-view.test.d.mts +2 -0
  29. package/dist/federation/inbox-view.test.mjs +369 -0
  30. package/dist/federation/inbox.test.mjs +22 -12
  31. package/dist/federation/metrics.test.mjs +1 -1
  32. package/dist/federation/middleware.test.mjs +470 -35
  33. package/dist/federation/mod.cjs +2 -1
  34. package/dist/federation/mod.d.cts +4 -4
  35. package/dist/federation/mod.d.ts +4 -4
  36. package/dist/federation/mod.js +8 -2
  37. package/dist/federation/mq.test.mjs +36 -1
  38. package/dist/federation/outgoing-proof.test.mjs +9 -6
  39. package/dist/federation/portable-actor.test.mjs +3 -3
  40. package/dist/federation/portable-chain.test.mjs +3 -3
  41. package/dist/federation/portable-collection.test.mjs +4 -4
  42. package/dist/federation/portable-delivery.test.mjs +2 -2
  43. package/dist/federation/portable-gateway-keys.test.mjs +13 -7
  44. package/dist/federation/portable-inbox.test.mjs +4 -4
  45. package/dist/federation/portable-parse-uri.test.mjs +1 -1
  46. package/dist/federation/portable-route-activity.test.mjs +2 -2
  47. package/dist/federation/portable.test.mjs +3 -3
  48. package/dist/federation/send.test.mjs +502 -93
  49. package/dist/federation/tasks/enqueue.test.mjs +3 -3
  50. package/dist/federation/tasks/tasks.test.mjs +4 -4
  51. package/dist/federation/temporal.test.mjs +1 -1
  52. package/dist/federation/webfinger.test.mjs +2 -2
  53. package/dist/{federation-BVH7drJA.cjs → federation-D0AZw_aw.cjs} +1415 -236
  54. package/dist/{http-BUJ5SFh6.mjs → http-BoJCMnjY.mjs} +204 -106
  55. package/dist/{http-DlNxxW_5.js → http-ChXUU_3c.js} +319 -123
  56. package/dist/{http-BfW3wyQ4.cjs → http-kv1LZw-r.cjs} +339 -125
  57. package/dist/inbox-BjihTKLo.mjs +312 -0
  58. package/dist/inbox-authentication-DflPYlLL.mjs +171 -0
  59. package/dist/inbox-view-D-skypg-.mjs +385 -0
  60. package/dist/{key-nYGY4QB9.d.cts → key-DbLNT-vA.d.ts} +1 -2
  61. package/dist/{key-DhsBxxZN.d.ts → key-DlHT4R6k.d.cts} +1 -2
  62. package/dist/{kv-cache-D_m3A241.mjs → kv-cache-Cmsyke4d.mjs} +1 -1
  63. package/dist/{kv-cache-7015lbrx.js → kv-cache-DQcte7ES.js} +73 -28
  64. package/dist/{kv-cache-kkBB0vbI.cjs → kv-cache-Ddh5sMxH.cjs} +72 -27
  65. package/dist/ld-CWVpo_NM.d.cts +250 -0
  66. package/dist/{ld-CD2CsPxq.mjs → ld-CdvcOEXQ.mjs} +52 -9
  67. package/dist/ld-Inagww4R.d.ts +250 -0
  68. package/dist/{metrics-D3LHk6Z4.mjs → metrics-CpxvUrBz.mjs} +1 -1
  69. package/dist/{middleware-N9eM16XE.js → middleware-BHb5toqA.js} +1413 -247
  70. package/dist/{middleware-CCDXvXOG.mjs → middleware-BKs2DLUF.mjs} +1 -1
  71. package/dist/{middleware-CzoWvrdZ.mjs → middleware-CoBLrTKX.mjs} +533 -132
  72. package/dist/{mod-B0dGgQsg.d.cts → mod-6TQ4UQZb.d.cts} +1 -1
  73. package/dist/mod-B4kBw1-8.d.ts +154 -0
  74. package/dist/{mod-DkhfZC1u.d.ts → mod-B7j30IQf.d.ts} +2 -9
  75. package/dist/mod-C1nT74IG.d.cts +154 -0
  76. package/dist/{mod-D0bxB8jS.d.ts → mod-CIeBgsOC.d.ts} +1 -1
  77. package/dist/{mod-B5-wdzmJ.d.cts → mod-YvA4Rk8J.d.cts} +2 -9
  78. package/dist/mod.cjs +6 -5
  79. package/dist/mod.d.cts +9 -9
  80. package/dist/mod.d.ts +9 -9
  81. package/dist/mod.js +6 -6
  82. package/dist/{mq-Dm1Wmtm-.mjs → mq-eF96QxUT.mjs} +6 -0
  83. package/dist/nodeinfo/client.test.mjs +337 -19
  84. package/dist/nodeinfo/handler.test.mjs +2 -2
  85. package/dist/nodeinfo/mod.cjs +1 -1
  86. package/dist/nodeinfo/mod.d.cts +1 -1
  87. package/dist/nodeinfo/mod.d.ts +1 -1
  88. package/dist/nodeinfo/mod.js +1 -1
  89. package/dist/otel/exporter.test.mjs +257 -1
  90. package/dist/otel/mod.cjs +36 -30
  91. package/dist/otel/mod.d.cts +13 -0
  92. package/dist/otel/mod.d.ts +13 -0
  93. package/dist/otel/mod.js +36 -30
  94. package/dist/{owner-C5-FVX6t.mjs → owner-DTnVpUKA.mjs} +3 -3
  95. package/dist/{portable-collection-Cony3_LC.mjs → portable-collection-CJeYTL61.mjs} +2 -2
  96. package/dist/{portable-collection-C-Z1SKDy.cjs → portable-collection-DciwBIia.cjs} +1 -1
  97. package/dist/{portable-collection-CC2zNAje.js → portable-collection-TmtpzD4E.js} +1 -1
  98. package/dist/{portable-key-DNc_flH-.mjs → portable-key-DcYok4ki.mjs} +1 -1
  99. package/dist/{proof-Cryb1c70.mjs → proof-GH7Poos_.mjs} +98 -10
  100. package/dist/{send-AF25pWaJ.mjs → send-BgGJh6pv.mjs} +174 -76
  101. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  102. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  103. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  104. package/dist/sig/compound-proof.test.mjs +2 -2
  105. package/dist/sig/http.test.mjs +553 -2
  106. package/dist/sig/key.test.mjs +1 -1
  107. package/dist/sig/ld.test.mjs +1 -1
  108. package/dist/sig/mitra-receive-interop.test.mjs +4 -4
  109. package/dist/sig/mitra-send-interop.test.mjs +3 -3
  110. package/dist/sig/mod.cjs +2 -2
  111. package/dist/sig/mod.d.cts +3 -3
  112. package/dist/sig/mod.d.ts +3 -3
  113. package/dist/sig/mod.js +2 -2
  114. package/dist/sig/owner.test.mjs +1 -1
  115. package/dist/sig/portable-collection.test.mjs +1 -1
  116. package/dist/sig/portable-interop.test.mjs +3 -3
  117. package/dist/sig/portable-key.test.mjs +5 -5
  118. package/dist/sig/proof.test.mjs +45 -5
  119. package/dist/sig/tootik-request-interop.test.mjs +4 -4
  120. package/dist/sig/tootik-send-interop.test.mjs +3 -3
  121. package/dist/sig/verification.test.mjs +3 -3
  122. package/dist/{temporal-BsAeuUJj.mjs → temporal-gJINzcL4.mjs} +1 -1
  123. package/dist/testing/kv.test.mjs +1 -1
  124. package/dist/testing/mod.d.mts +288 -2
  125. package/dist/testing/mod.mjs +2 -2
  126. package/dist/{testing-t8xDeJZ5.mjs → testing-Dbr21vZk.mjs} +1 -1
  127. package/dist/{types-_S2_MeLF.js → types-DUjZb4i6.js} +112 -63
  128. package/dist/{types-poIao40f.cjs → types-hKCCjii0.cjs} +111 -62
  129. package/dist/utils/docloader.test.mjs +344 -36
  130. package/dist/utils/kv-cache.test.mjs +1 -1
  131. package/dist/utils/mod.cjs +1 -1
  132. package/dist/utils/mod.d.cts +1 -1
  133. package/dist/utils/mod.d.ts +1 -1
  134. package/dist/utils/mod.js +1 -1
  135. package/package.json +7 -7
  136. package/dist/docloader-B-0nqOB1.mjs +0 -91
  137. package/dist/inbox-CHvMaIFX.mjs +0 -188
  138. package/dist/mod-BRpER-B0.d.ts +0 -320
  139. package/dist/mod-xAfaLS4T.d.cts +0 -320
  140. package/dist/owner-BF0L9cMt.d.cts +0 -78
  141. package/dist/owner-BSzr5-I8.d.ts +0 -78
@@ -1,8 +1,9 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
- const require_http = require("./http-BfW3wyQ4.cjs");
2
+ const require_http = require("./http-kv1LZw-r.cjs");
3
3
  let _logtape_logtape = require("@logtape/logtape");
4
4
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
5
5
  let es_toolkit = require("es-toolkit");
6
+ let _fedify_vocab_runtime_internal = require("@fedify/vocab-runtime/internal");
6
7
  //#region src/utils/docloader.ts
7
8
  const logger$1 = (0, _logtape_logtape.getLogger)([
8
9
  "fedify",
@@ -26,13 +27,35 @@ const DEFAULT_MAX_REDIRECTION = 20;
26
27
  * @throws {RangeError} If the `timeout` option is invalid.
27
28
  * @since 0.4.0
28
29
  */
29
- function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, timeout, tracerProvider } = {}) {
30
+ function getAuthenticatedDocumentLoader(identity, options = {}) {
31
+ return createAuthenticatedDocumentLoader(identity, options);
32
+ }
33
+ /**
34
+ * The placeholder that the alternate document callback of
35
+ * `getRemoteDocument()` returns, so that the alternate document is loaded
36
+ * after the current hop is closed.
37
+ */
38
+ const ALTERNATE_PLACEHOLDER = Object.freeze({
39
+ contextUrl: null,
40
+ document: null,
41
+ documentUrl: "about:blank"
42
+ });
43
+ /**
44
+ * The implementation of {@link getAuthenticatedDocumentLoader}. This is not
45
+ * exported from any entry point.
46
+ * @param identity The identity to get the document loader for.
47
+ * @param options The options for the document loader.
48
+ * @param deps The dependencies, for tests.
49
+ * @returns The authenticated document loader.
50
+ * @throws {TypeError} If the key is invalid or unsupported.
51
+ */
52
+ function createAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, timeout, tracerProvider } = {}, deps = {}) {
30
53
  require_http.validateCryptoKey(identity.privateKey);
31
54
  const resolvedTimeout = (0, _fedify_vocab_runtime.resolveDocumentLoaderTimeout)(timeout);
32
55
  async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
33
56
  options?.signal?.throwIfAborted();
34
57
  let currentUrl = new URL(url).href;
35
- await validateUrl(currentUrl, options);
58
+ validateUrl(currentUrl);
36
59
  visited.add(currentUrl);
37
60
  const originalRequest = (0, _fedify_vocab_runtime.createActivityPubRequest)(currentUrl, { userAgent });
38
61
  function follow(nextUrl) {
@@ -41,46 +64,68 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
41
64
  if (visited.has(nextUrl)) throw new _fedify_vocab_runtime.FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
42
65
  redirected++;
43
66
  }
44
- const response = await require_http.doubleKnock(originalRequest, identity, {
67
+ const result = await require_http.doubleKnockScoped(originalRequest, identity, {
45
68
  maxRedirection,
46
69
  specDeterminer,
47
70
  log: (0, es_toolkit.curry)(_fedify_vocab_runtime.logRequest)(logger$1),
48
71
  tracerProvider,
49
72
  signal: options?.signal,
50
- validateRedirect: async (nextUrl) => {
73
+ validateRedirect: (nextUrl) => {
51
74
  follow(nextUrl);
52
- await validateUrl(nextUrl, options);
75
+ validateUrl(nextUrl);
53
76
  visited.add(nextUrl);
54
77
  currentUrl = nextUrl;
55
78
  }
79
+ }, (hopUrl) => openHop(hopUrl, options), async (response) => {
80
+ options?.signal?.throwIfAborted();
81
+ let alternateUrl;
82
+ const document = await (0, _fedify_vocab_runtime.getRemoteDocument)(currentUrl, response, (url) => {
83
+ alternateUrl = url;
84
+ return Promise.resolve(ALTERNATE_PLACEHOLDER);
85
+ }, options);
86
+ if (alternateUrl == null) return { document };
87
+ if (!response.bodyUsed && response.body?.locked !== true) response.body?.cancel().catch((error) => {
88
+ logger$1.debug("Failed to cancel the response body from {url}: {error}", {
89
+ url: currentUrl,
90
+ error
91
+ });
92
+ });
93
+ return { alternateUrl };
56
94
  });
57
95
  options?.signal?.throwIfAborted();
58
- return (0, _fedify_vocab_runtime.getRemoteDocument)(currentUrl, response, (alternateUrl) => {
59
- follow(alternateUrl);
60
- return load(alternateUrl, options, redirected, visited);
61
- }, options);
96
+ if ("document" in result) return result.document;
97
+ follow(result.alternateUrl);
98
+ return load(result.alternateUrl, options, redirected, visited);
99
+ }
100
+ function openUnvalidatedHop(url, signal) {
101
+ if (deps.openHop != null) return deps.openHop(url);
102
+ return allowPrivateAddress ? (0, _fedify_vocab_runtime_internal.openDirectFetch)(url) : (0, _fedify_vocab_runtime_internal.openPublicFetch)(url, { signal });
62
103
  }
63
- async function validateUrl(url, options) {
104
+ async function openHop(url, options) {
105
+ try {
106
+ const session = await openUnvalidatedHop(url, options?.signal);
107
+ if (options?.signal?.aborted) {
108
+ await session.close();
109
+ options.signal.throwIfAborted();
110
+ }
111
+ return session;
112
+ } catch (error) {
113
+ if (error instanceof _fedify_vocab_runtime.UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
114
+ url,
115
+ error
116
+ });
117
+ else logger$1[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
118
+ url,
119
+ error
120
+ });
121
+ throw error;
122
+ }
123
+ }
124
+ function validateUrl(url) {
64
125
  const parsedUrl = new URL(url);
65
126
  const protocol = parsedUrl.protocol;
66
127
  if (protocol !== "http:" && protocol !== "https:") throw new _fedify_vocab_runtime.UrlError(`Unsupported protocol: ${protocol}`);
67
128
  if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new _fedify_vocab_runtime.UrlError("URLs with credentials are not allowed");
68
- if (!allowPrivateAddress) {
69
- try {
70
- await (0, _fedify_vocab_runtime.validatePublicUrl)(url);
71
- } catch (error) {
72
- if (error instanceof _fedify_vocab_runtime.UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
73
- url,
74
- error
75
- });
76
- else logger$1[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
77
- url,
78
- error
79
- });
80
- throw error;
81
- }
82
- options?.signal?.throwIfAborted();
83
- }
84
129
  }
85
130
  return (0, _fedify_vocab_runtime.withDocumentLoaderTimeout)((url, options) => load(url, options), resolvedTimeout);
86
131
  }
@@ -0,0 +1,250 @@
1
+ /// <reference lib="esnext.temporal" />
2
+ import { a as KeyCache, v as VerificationObservationOptions } from "./key-DlHT4R6k.cjs";
3
+ import { Activity, Actor, CryptographicKey } from "@fedify/vocab";
4
+ import { DocumentLoader } from "@fedify/vocab-runtime";
5
+ import { MeterProvider, TracerProvider } from "@opentelemetry/api";
6
+
7
+ //#region src/sig/owner.d.ts
8
+ /**
9
+ * Options for {@link doesActorOwnKey}.
10
+ * @since 0.8.0
11
+ */
12
+ interface DoesActorOwnKeyOptions {
13
+ /**
14
+ * The document loader to use for fetching the actor.
15
+ */
16
+ documentLoader?: DocumentLoader;
17
+ /**
18
+ * The context loader to use for JSON-LD context retrieval.
19
+ */
20
+ contextLoader?: DocumentLoader;
21
+ /**
22
+ * The OpenTelemetry tracer provider to use for tracing. If omitted,
23
+ * the global tracer provider is used.
24
+ * @since 1.3.0
25
+ */
26
+ tracerProvider?: TracerProvider;
27
+ }
28
+ /**
29
+ * Checks if the actor of the given activity owns the specified key.
30
+ *
31
+ * Ownership is never taken from what the activity or the key say about
32
+ * themselves, as both are written by whoever sent the activity. It is
33
+ * established from a document that the owner itself serves: either the key's
34
+ * claimed owner links back to the key, or the actor the activity claims lists
35
+ * the key as its own.
36
+ *
37
+ * @param activity The activity to check.
38
+ * @param key The public key to check.
39
+ * @param options Options for checking the key ownership.
40
+ * @returns Whether the actor is the owner of the key.
41
+ */
42
+ declare function doesActorOwnKey(activity: Activity, key: CryptographicKey, options: DoesActorOwnKeyOptions): Promise<boolean>;
43
+ /**
44
+ * Options for {@link getKeyOwner}.
45
+ * @since 0.8.0
46
+ */
47
+ interface GetKeyOwnerOptions {
48
+ /**
49
+ * The document loader to use for fetching the key and its owner.
50
+ */
51
+ documentLoader?: DocumentLoader;
52
+ /**
53
+ * The context loader to use for JSON-LD context retrieval.
54
+ */
55
+ contextLoader?: DocumentLoader;
56
+ /**
57
+ * The OpenTelemetry tracer provider to use for tracing. If omitted,
58
+ * the global tracer provider is used.
59
+ * @since 1.3.0
60
+ */
61
+ tracerProvider?: TracerProvider;
62
+ }
63
+ /**
64
+ * Gets the actor that owns the specified key. Returns `null` if the key has no
65
+ * known owner.
66
+ *
67
+ * The owner is only returned when the key and the owner link to each other:
68
+ * a document that merely claims to own a key is not enough, as the claim and
69
+ * the key can come from the same host.
70
+ *
71
+ * @param keyId The ID of the key to check, or the key itself.
72
+ * @param options Options for getting the key owner.
73
+ * @returns The actor that owns the key, or `null` if the key has no known
74
+ * owner.
75
+ * @since 0.7.0
76
+ */
77
+ declare function getKeyOwner(keyId: URL | CryptographicKey, options: GetKeyOwnerOptions): Promise<Actor | null>;
78
+ //#endregion
79
+ //#region src/sig/ld.d.ts
80
+ /**
81
+ * How a context loader marked by {@link markEffectiveContextLoader} already
82
+ * resolves contexts.
83
+ * @internal
84
+ */
85
+ type EffectiveContextLoaderPolicy = "standard" | "normalization";
86
+ /**
87
+ * A signature of a JSON-LD document.
88
+ * @since 1.0.0
89
+ */
90
+ interface Signature {
91
+ "@context"?: "https://w3id.org/identity/v1";
92
+ type: "RsaSignature2017";
93
+ id?: string;
94
+ creator: string;
95
+ created: string;
96
+ signatureValue: string;
97
+ }
98
+ /**
99
+ * Attaches a LD signature to the given JSON-LD document.
100
+ * @param jsonLd The JSON-LD document to attach the signature to. It is not
101
+ * modified.
102
+ * @param signature The signature to attach.
103
+ * @returns The JSON-LD document with the attached signature.
104
+ * @throws {TypeError} If the input document is not a valid JSON-LD document.
105
+ * @since 1.0.0
106
+ */
107
+ declare function attachSignature(jsonLd: unknown, signature: Signature): {
108
+ signature: Signature;
109
+ };
110
+ /**
111
+ * Options for creating Linked Data Signatures.
112
+ * @since 1.0.0
113
+ */
114
+ interface CreateSignatureOptions {
115
+ /**
116
+ * The context loader for loading remote JSON-LD contexts.
117
+ */
118
+ contextLoader?: DocumentLoader;
119
+ /**
120
+ * The time when the signature was created. If not specified, the current
121
+ * time will be used.
122
+ */
123
+ created?: Temporal.Instant;
124
+ }
125
+ /**
126
+ * Creates a LD signature for the given JSON-LD document.
127
+ * @param jsonLd The JSON-LD document to sign.
128
+ * @param privateKey The private key to sign the document.
129
+ * @param keyId The ID of the public key that corresponds to the private key.
130
+ * @param options Additional options for creating the signature.
131
+ * See also {@link CreateSignatureOptions}.
132
+ * @return The created signature.
133
+ * @throws {TypeError} If the private key is invalid or unsupported.
134
+ * @since 1.0.0
135
+ */
136
+ declare function createSignature(jsonLd: unknown, privateKey: CryptoKey, keyId: URL, {
137
+ contextLoader,
138
+ created
139
+ }?: CreateSignatureOptions): Promise<Signature>;
140
+ /**
141
+ * Options for signing JSON-LD documents.
142
+ * @since 1.0.0
143
+ */
144
+ interface SignJsonLdOptions extends CreateSignatureOptions {
145
+ /**
146
+ * The OpenTelemetry tracer provider for tracing the signing process.
147
+ * If omitted, the global tracer provider is used.
148
+ * @since 1.3.0
149
+ */
150
+ tracerProvider?: TracerProvider;
151
+ }
152
+ /**
153
+ * Signs the given JSON-LD document with the private key and returns the signed
154
+ * JSON-LD document.
155
+ * @param jsonLd The JSON-LD document to sign.
156
+ * @param privateKey The private key to sign the document.
157
+ * @param keyId The key ID to use in the signature. It will be used by the
158
+ * verifier to fetch the corresponding public key.
159
+ * @param options Additional options for signing the document.
160
+ * See also {@link SignJsonLdOptions}.
161
+ * @returns The signed JSON-LD document.
162
+ * @throws {TypeError} If the private key is invalid or unsupported.
163
+ * @since 1.0.0
164
+ */
165
+ declare function signJsonLd(jsonLd: unknown, privateKey: CryptoKey, keyId: URL, options: SignJsonLdOptions): Promise<{
166
+ signature: Signature;
167
+ }>;
168
+ /**
169
+ * Checks if the given JSON-LD document has a Linked Data Signature-like
170
+ * object, without restricting it to a single suite-specific shape.
171
+ * @param jsonLd The JSON-LD document to check.
172
+ * @returns `true` if the document has a signature-like object; `false`
173
+ * otherwise.
174
+ * @since 2.2.0
175
+ */
176
+ declare function hasSignatureLike(jsonLd: unknown): boolean;
177
+ /**
178
+ * Detaches Linked Data Signatures from the given JSON-LD document.
179
+ * @param jsonLd The JSON-LD document to modify.
180
+ * @returns The modified JSON-LD document. If the input document does not
181
+ * contain a signature, the original document is returned.
182
+ * @since 1.0.0
183
+ */
184
+ declare function detachSignature(jsonLd: unknown): unknown;
185
+ /**
186
+ * Options for verifying Linked Data Signatures.
187
+ * @since 1.0.0
188
+ */
189
+ interface VerifySignatureOptions extends VerificationObservationOptions {
190
+ /**
191
+ * The document loader to use for fetching the public key.
192
+ */
193
+ documentLoader?: DocumentLoader;
194
+ /**
195
+ * The context loader to use for JSON-LD context retrieval.
196
+ */
197
+ contextLoader?: DocumentLoader;
198
+ /**
199
+ * The key cache to use for caching public keys.
200
+ */
201
+ keyCache?: KeyCache;
202
+ /**
203
+ * The OpenTelemetry tracer provider for tracing the verification process.
204
+ * If omitted, the global tracer provider is used.
205
+ * @since 1.3.0
206
+ */
207
+ tracerProvider?: TracerProvider;
208
+ /**
209
+ * The OpenTelemetry meter provider. If omitted, the global meter provider
210
+ * is used.
211
+ * @since 2.3.0
212
+ */
213
+ meterProvider?: MeterProvider;
214
+ }
215
+ /**
216
+ * Verifies Linked Data Signatures of the given JSON-LD document.
217
+ *
218
+ * This is a low-level utility that only checks the cryptographic signature
219
+ * and (optionally) the cached key. It does not run the JSON-LD parsing,
220
+ * attribution, and owner checks that a complete inbound LD verification
221
+ * needs. For incoming activities, prefer {@link verifyJsonLd}, which is
222
+ * the public verification entry point and the one that emits the
223
+ * `activitypub.signature.verification.duration` metric for the LD path.
224
+ * `verifySignature` itself only emits
225
+ * `activitypub.signature.key_fetch.duration`, since the rest of the work
226
+ * that the verification-duration metric is meant to cover happens in
227
+ * `verifyJsonLd`.
228
+ * @param jsonLd The JSON-LD document to verify.
229
+ * @param options Options for verifying the signature.
230
+ * @returns The public key that signed the document or `null` if the signature
231
+ * is invalid or the key is not found.
232
+ * @since 1.0.0
233
+ */
234
+ declare function verifySignature(jsonLd: unknown, options?: VerifySignatureOptions): Promise<CryptographicKey | null>;
235
+ /**
236
+ * Options for verifying JSON-LD documents.
237
+ */
238
+ interface VerifyJsonLdOptions extends VerifySignatureOptions {}
239
+ /**
240
+ * Verify the authenticity of the given JSON-LD document using Linked Data
241
+ * Signatures. If the document is signed, this function verifies the signature
242
+ * and checks if the document is attributed to the owner of the public key.
243
+ * If the document is not signed, this function returns `false`.
244
+ * @param jsonLd The JSON-LD document to verify.
245
+ * @param options Options for verifying the document.
246
+ * @returns `true` if the document is authentic; `false` otherwise.
247
+ */
248
+ declare function verifyJsonLd(jsonLd: unknown, options?: VerifyJsonLdOptions): Promise<boolean>;
249
+ //#endregion
250
+ export { VerifySignatureOptions as a, detachSignature as c, verifyJsonLd as d, verifySignature as f, getKeyOwner as g, doesActorOwnKey as h, VerifyJsonLdOptions as i, hasSignatureLike as l, GetKeyOwnerOptions as m, EffectiveContextLoaderPolicy as n, attachSignature as o, DoesActorOwnKeyOptions as p, SignJsonLdOptions as r, createSignature as s, CreateSignatureOptions as t, signJsonLd as u };
@@ -3,8 +3,8 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { c as isPortableActorDocument, d as isPortableUri, l as isPortableId, m as parseKeyIdString, s as isCompatibleKeyId } from "./portable-key-id-Ca30-hOa.mjs";
5
5
  import { a as verificationObservation, i as triedKey, n as observeCheck, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
6
- import { n as version, t as name } from "./deno-eNpfj4oR.mjs";
7
- import { _ as recordKeyLookup, n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch, t as classifyFetchError } from "./metrics-D3LHk6Z4.mjs";
6
+ import { n as version, t as name } from "./deno-iOAA7iRN.mjs";
7
+ import { _ as recordKeyLookup, n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch, t as classifyFetchError } from "./metrics-CpxvUrBz.mjs";
8
8
  import { n as attachKeyOwnerEvidence, t as MAX_PORTABLE_KEY_TTL } from "./key-owner-evidence-APB6NpSk.mjs";
9
9
  import { getLogger } from "@logtape/logtape";
10
10
  import { Activity, CryptographicKey, Multikey, Object as Object$1, getTypeId, isActor } from "@fedify/vocab";
@@ -200,6 +200,7 @@ async function fetchActorDocumentInternal(actorId, options, propagateContextFetc
200
200
  actorId: actorId.href,
201
201
  error
202
202
  });
203
+ if (propagateContextFetchErrors) throw new KeyContextFetchError(error);
203
204
  return null;
204
205
  }
205
206
  let object;
@@ -1146,8 +1147,26 @@ function createMemoizedDocumentLoader(documentLoader) {
1146
1147
  return cloneRemoteDocument(await remoteDocument);
1147
1148
  };
1148
1149
  }
1150
+ const effectiveContextLoaderPolicies = /* @__PURE__ */ new WeakMap();
1151
+ /**
1152
+ * Marks a context loader as the *effective* loader of a policy, i.e., one
1153
+ * that already applies what {@link wrapContextLoaderForJsonLd} (`"standard"`)
1154
+ * or {@link getNormalizationContextLoader} (`"normalization"`) would add.
1155
+ * Those functions return a marked loader as is instead of wrapping it again,
1156
+ * so that a caller can observe every response a parse actually uses,
1157
+ * including Fedify's own baseline contexts.
1158
+ * @param loader The effective context loader.
1159
+ * @param policy The policy the loader already applies.
1160
+ * @returns The given loader.
1161
+ * @internal
1162
+ */
1163
+ function markEffectiveContextLoader(loader, policy) {
1164
+ effectiveContextLoaderPolicies.set(loader, policy);
1165
+ return loader;
1166
+ }
1149
1167
  /** @internal */
1150
1168
  function wrapContextLoaderForJsonLd(contextLoader) {
1169
+ if (contextLoader != null && effectiveContextLoaderPolicies.has(contextLoader)) return contextLoader;
1151
1170
  const loader = contextLoader ?? builtInContextLoader;
1152
1171
  return async (url, options) => {
1153
1172
  try {
@@ -1161,6 +1180,7 @@ function wrapContextLoaderForJsonLd(contextLoader) {
1161
1180
  }
1162
1181
  /** @internal */
1163
1182
  function getNormalizationContextLoader(contextLoader) {
1183
+ if (contextLoader != null && effectiveContextLoaderPolicies.get(contextLoader) === "normalization") return contextLoader;
1164
1184
  const loader = wrapContextLoaderForJsonLd(contextLoader);
1165
1185
  return createMemoizedDocumentLoader(async (url, options) => {
1166
1186
  if (URL.canParse(url)) {
@@ -1613,10 +1633,21 @@ function getLdSignatureObject(jsonLd) {
1613
1633
  * @returns `true` if the document is authentic; `false` otherwise.
1614
1634
  */
1615
1635
  async function verifyJsonLd(jsonLd, options = {}) {
1616
- return await verifyJsonLdInternal(jsonLd, options, true);
1636
+ return (await verifyJsonLdInternal(jsonLd, options, true)).verified;
1617
1637
  }
1618
1638
  /** @internal */
1619
1639
  async function verifyCompactJsonLd(jsonLd, options = {}) {
1640
+ return (await verifyJsonLdInternal(jsonLd, options, false)).verified;
1641
+ }
1642
+ /**
1643
+ * Same as {@link verifyCompactJsonLd}, but also returns the key that verified
1644
+ * the signature, whose `owner` {@link fetchKey} has confirmed.
1645
+ * @param jsonLd The compacted JSON-LD document to verify.
1646
+ * @param options Options for verifying the JSON-LD document.
1647
+ * @returns The result.
1648
+ * @internal
1649
+ */
1650
+ async function verifyCompactJsonLdDetailed(jsonLd, options = {}) {
1620
1651
  return await verifyJsonLdInternal(jsonLd, options, false);
1621
1652
  }
1622
1653
  async function verifyJsonLdInternal(jsonLd, options, compact) {
@@ -1626,7 +1657,7 @@ async function verifyJsonLdInternal(jsonLd, options, compact) {
1626
1657
  }, "linkedData", (observation) => verifyJsonLdInternal(jsonLd, {
1627
1658
  ...options,
1628
1659
  [verificationObservation]: observation
1629
- }, compact), (value) => value);
1660
+ }, compact), (value) => value.verified);
1630
1661
  const observation = options[verificationObservation];
1631
1662
  return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("ld_signatures.verify", async (span) => {
1632
1663
  const start = performance.now();
@@ -1658,12 +1689,18 @@ async function verifyJsonLdInternal(jsonLd, options, compact) {
1658
1689
  const key = await verifySignature(compacted, verificationOptions);
1659
1690
  if (key == null) {
1660
1691
  if (observation?.attempt != null && !hasLdSignatureProperty(jsonLd)) observation.attempt.reason = { type: "noSignature" };
1661
- return false;
1692
+ return {
1693
+ verified: false,
1694
+ key: null
1695
+ };
1662
1696
  }
1663
1697
  if (key.ownerId == null) {
1664
1698
  if (observation?.attempt != null) observation.attempt.reason = { type: "missingOwner" };
1665
1699
  logger.debug("Key {keyId} has no owner.", { keyId: key.id?.href });
1666
- return false;
1700
+ return {
1701
+ verified: false,
1702
+ key: null
1703
+ };
1667
1704
  }
1668
1705
  attributions.delete(key.ownerId.href);
1669
1706
  if (attributions.size > 0) {
@@ -1672,10 +1709,16 @@ async function verifyJsonLdInternal(jsonLd, options, compact) {
1672
1709
  attributionIds: [...attributions].map((id) => new URL(id))
1673
1710
  };
1674
1711
  logger.debug("Some attributions are not authenticated by the Linked Data Signatures: {attributions}.", { attributions: [...attributions] });
1675
- return false;
1712
+ return {
1713
+ verified: false,
1714
+ key: null
1715
+ };
1676
1716
  }
1677
1717
  verified = true;
1678
- return true;
1718
+ return {
1719
+ verified: true,
1720
+ key
1721
+ };
1679
1722
  } catch (error) {
1680
1723
  threw = true;
1681
1724
  span.setStatus({
@@ -1706,4 +1749,4 @@ async function hashJsonLd(jsonLd, contextLoader) {
1706
1749
  return encodeHex(await crypto.subtle.digest("SHA-256", encoder.encode(canon)));
1707
1750
  }
1708
1751
  //#endregion
1709
- export { generateCryptoKeyPair as C, verifyKeyOwnership as E, fetchKeyDetailed as S, validateCryptoKey as T, wrapContextLoaderForJsonLd as _, compactJsonLd as a, fetchActorDocument as b, getNormalizationContextLoader as c, isClearlyMalformedContextReference as d, isInvalidUrlTypeError as f, verifySignature as g, verifyJsonLd as h, attachSignature as i, hasSignature as l, verifyCompactJsonLd as m, UnsafeJsonLdError as n, createSignature as o, signJsonLd as p, assertSafeJsonLd as r, detachSignature as s, InvalidContextReferenceError as t, hasSignatureLike as u, bypassKeyCacheReads as v, importJwk as w, fetchKey as x, exportJwk as y };
1752
+ export { fetchKey as C, validateCryptoKey as D, importJwk as E, verifyKeyOwnership as O, fetchActorDocument as S, generateCryptoKeyPair as T, verifyJsonLd as _, compactJsonLd as a, bypassKeyCacheReads as b, getNormalizationContextLoader as c, isClearlyMalformedContextReference as d, isInvalidUrlTypeError as f, verifyCompactJsonLdDetailed as g, verifyCompactJsonLd as h, attachSignature as i, hasSignature as l, signJsonLd as m, UnsafeJsonLdError as n, createSignature as o, markEffectiveContextLoader as p, assertSafeJsonLd as r, detachSignature as s, InvalidContextReferenceError as t, hasSignatureLike as u, verifySignature as v, fetchKeyDetailed as w, exportJwk as x, wrapContextLoaderForJsonLd as y };