@fedify/fedify 1.3.0-dev.482 → 1.3.0-dev.485

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.
package/CHANGES.md CHANGED
@@ -8,6 +8,30 @@ Version 1.3.0
8
8
 
9
9
  To be released.
10
10
 
11
+ - Fedify now makes HTTP requests with the proper `User-Agent` header. [[#162]]
12
+
13
+ - Added `getUserAgent()` function.
14
+ - Added `GetUserAgentOptions` interface.
15
+ - Added `getDocumentLoader()` function.
16
+ - Added `GetDocumentLoaderOptions` interface.
17
+ - The type of `getAuthenticatedDocumentLoader()` function's second
18
+ parameter became `GetAuthenticatedDocumentLoaderOptions | undefined`
19
+ (was `boolean | undefined`).
20
+ - Added `GetAuthenticatedDocumentLoaderOptions` interface.
21
+ - Deprecated `fetchDocumentLoader()` function.
22
+ - Added `LookupObjectOptions.userAgent` option.
23
+ - Added the type of `getActorHandle()` function's second parameter became
24
+ `GetActorHandleOptions | undefined` (was `NormalizeActorHandleOptions |
25
+ undefined`).
26
+ - Added `GetActorHandleOptions` interface.
27
+ - Added the optional second parameter to `lookupWebFinger()` function.
28
+ - Added `LookupWebFingerOptions` interface.
29
+ - Added `GetNodeInfoOptions.userAgent` option.
30
+ - Added `-u`/--user-agent` option to `fedify lookup` subcommand.
31
+ - Added `-u`/--user-agent` option to `fedify node` subcommand.
32
+
33
+ [#162]: https://github.com/dahlia/fedify/issues/162
34
+
11
35
 
12
36
  Version 1.2.2
13
37
  -------------
package/esm/deno.js ADDED
@@ -0,0 +1,101 @@
1
+ export default {
2
+ "name": "@fedify/fedify",
3
+ "version": "1.3.0-dev.485+cfe509f3",
4
+ "license": "MIT",
5
+ "exports": {
6
+ ".": "./mod.ts",
7
+ "./federation": "./federation/mod.ts",
8
+ "./nodeinfo": "./nodeinfo/mod.ts",
9
+ "./runtime": "./runtime/mod.ts",
10
+ "./sig": "./sig/mod.ts",
11
+ "./vocab": "./vocab/mod.ts",
12
+ "./webfinger": "./webfinger/mod.ts",
13
+ "./x/denokv": "./x/denokv.ts",
14
+ "./x/fresh": "./x/fresh.ts",
15
+ "./x/hono": "./x/hono.ts"
16
+ },
17
+ "imports": {
18
+ "@cfworker/json-schema": "npm:@cfworker/json-schema@^2.0.1",
19
+ "@david/which-runtime": "jsr:@david/which-runtime@^0.2.0",
20
+ "@deno/dnt": "jsr:@deno/dnt@0.41.2",
21
+ "@fedify/fedify": "./mod.ts",
22
+ "@fedify/fedify/federation": "./federation/mod.ts",
23
+ "@fedify/fedify/nodeinfo": "./nodeinfo/mod.ts",
24
+ "@fedify/fedify/runtime": "./runtime/mod.ts",
25
+ "@fedify/fedify/sig": "./sig/mod.ts",
26
+ "@fedify/fedify/vocab": "./vocab/mod.ts",
27
+ "@fedify/fedify/webfinger": "./webfinger/mod.ts",
28
+ "@fedify/fedify/x/denokv": "./x/denokv.ts",
29
+ "@fedify/fedify/x/fresh": "./x/fresh.ts",
30
+ "@fedify/fedify/x/hono": "./x/hono.ts",
31
+ "@hongminhee/aitertools": "jsr:@hongminhee/aitertools@^0.6.0",
32
+ "@hugoalh/http-header-link": "jsr:@hugoalh/http-header-link@^1.0.2",
33
+ "@logtape/logtape": "jsr:@logtape/logtape@^0.7.1",
34
+ "@phensley/language-tag": "npm:@phensley/language-tag@^1.9.0",
35
+ "@std/assert": "jsr:@std/assert@^0.226.0",
36
+ "@std/async": "jsr:@std/async@^1.0.5",
37
+ "@std/bytes": "jsr:@std/bytes@^1.0.2",
38
+ "@std/collections": "jsr:@std/collections@^1.0.6",
39
+ "@std/encoding": "jsr:@std/encoding@^1.0.5",
40
+ "@std/fs": "jsr:@std/fs@^1.0.3",
41
+ "@std/http": "jsr:@std/http@^1.0.6",
42
+ "@std/path": "jsr:@std/path@^1.0.6",
43
+ "@std/semver": "jsr:@std/semver@^1.0.3",
44
+ "@std/testing": "jsr:@std/testing@^0.224.0",
45
+ "@std/text": "jsr:@std/text@^1.0.6",
46
+ "@std/url": "jsr:@std/url@^0.225.1",
47
+ "@std/yaml": "jsr:@std/yaml@^0.224.3",
48
+ "asn1js": "npm:asn1js@^3.0.5",
49
+ "fast-check": "npm:fast-check@^3.22.0",
50
+ "json-canon": "npm:json-canon@^1.0.1",
51
+ "jsonld": "npm:jsonld@^8.3.2",
52
+ "mock_fetch": "jsr:@hongminhee/deno-mock-fetch@^0.3.2",
53
+ "multibase": "npm:multibase@^4.0.6",
54
+ "multicodec": "npm:multicodec@^3.2.1",
55
+ "pkijs": "npm:pkijs@^3.2.4",
56
+ "uri-template-router": "npm:uri-template-router@^0.0.16",
57
+ "url-template": "npm:url-template@^3.1.1"
58
+ },
59
+ "include": [
60
+ "vocab/vocab.ts"
61
+ ],
62
+ "exclude": [
63
+ ".git/",
64
+ ".github/",
65
+ ".vscode/",
66
+ ".zed/",
67
+ "apidoc/",
68
+ "cli/",
69
+ "codegen/schema.yaml",
70
+ "docs/",
71
+ "examples/",
72
+ "logo.svg",
73
+ "npm/",
74
+ "vocab/*.yaml",
75
+ "!vocab/vocab.ts"
76
+ ],
77
+ "tasks": {
78
+ "cache": "deno task codegen && deno cache mod.ts",
79
+ "check": "deno task codegen && deno fmt --check && deno lint && deno check */*.ts",
80
+ "codegen": "deno run --allow-read --allow-write --check codegen/main.ts vocab/ ../runtime/ > vocab/vocab.ts && deno fmt vocab/vocab.ts && deno cache vocab/vocab.ts && deno check vocab/vocab.ts",
81
+ "test-without-codegen": "deno test --check --doc --allow-read --allow-write --allow-env --unstable-kv --trace-leaks",
82
+ "test": "deno task codegen && deno task test-without-codegen",
83
+ "coverage": "rm -rf coverage/ && deno task test --coverage && deno coverage --html coverage",
84
+ "bench": "deno task codegen && deno bench --allow-read --allow-write --allow-net --allow-env --allow-run --unstable-kv",
85
+ "apidoc": "deno task codegen && deno doc --html --name=Fedify --output=apidoc/ mod.ts",
86
+ "check-version": "deno run ../cli/scripts/check_version.ts",
87
+ "publish": "deno task codegen && deno publish",
88
+ "dnt-without-codegen": "deno run -A dnt.ts",
89
+ "dnt": "deno task codegen && deno task dnt-without-codegen",
90
+ "test-all": "deno task check && deno task test-without-codegen && deno task dnt-without-codegen && cd npm/ && bun run ./test_runner.js && cd ../",
91
+ "update": "deno run --allow-env --allow-read --allow-write=. --allow-run=git,deno --allow-net=jsr.io,registry.npmjs.org jsr:@molt/cli ./*.ts",
92
+ "update:commit": "deno task -q update --commit --pre-commit=fmt,lint",
93
+ "hooks:install": "deno run --allow-read=deno.json,.git/hooks/ --allow-write=.git/hooks/ jsr:@hongminhee/deno-task-hooks",
94
+ "hooks:pre-commit": "deno task check && deno task check-version"
95
+ },
96
+ "unstable": [
97
+ "kv",
98
+ "temporal"
99
+ ],
100
+ "lock": false
101
+ };
@@ -1,7 +1,7 @@
1
1
  import * as dntShim from "../_dnt.shims.js";
2
2
  import { getLogger, withContext } from "@logtape/logtape";
3
3
  import { handleNodeInfo, handleNodeInfoJrd } from "../nodeinfo/handler.js";
4
- import { fetchDocumentLoader, getAuthenticatedDocumentLoader, kvCache, } from "../runtime/docloader.js";
4
+ import { getAuthenticatedDocumentLoader, getDocumentLoader, kvCache, } from "../runtime/docloader.js";
5
5
  import { verifyRequest } from "../sig/http.js";
6
6
  import { exportJwk, importJwk, validateCryptoKey } from "../sig/key.js";
7
7
  import { hasSignature, signJsonLd } from "../sig/ld.js";
@@ -50,6 +50,7 @@ export class FederationImpl {
50
50
  documentLoader;
51
51
  contextLoader;
52
52
  authenticatedDocumentLoaderFactory;
53
+ userAgent;
53
54
  onOutboxError;
54
55
  signatureTimeWindow;
55
56
  skipSignatureVerification;
@@ -75,31 +76,34 @@ export class FederationImpl {
75
76
  this.router.add("/.well-known/nodeinfo", "nodeInfoJrd");
76
77
  this.objectCallbacks = {};
77
78
  this.objectTypeIds = {};
78
- if (options.allowPrivateAddress) {
79
+ if (options.allowPrivateAddress || options.userAgent != null) {
79
80
  if (options.documentLoader != null) {
80
- throw new TypeError("Cannot set documentLoader with allowPrivateAddress turned on.");
81
+ throw new TypeError("Cannot set documentLoader with allowPrivateAddress or " +
82
+ "userAgent options.");
81
83
  }
82
84
  else if (options.contextLoader != null) {
83
- throw new TypeError("Cannot set contextLoader with allowPrivateAddress turned on.");
85
+ throw new TypeError("Cannot set contextLoader with allowPrivateAddress or " +
86
+ "userAgent options.");
84
87
  }
85
88
  else if (options.authenticatedDocumentLoaderFactory != null) {
86
89
  throw new TypeError("Cannot set authenticatedDocumentLoaderFactory with " +
87
- "allowPrivateAddress turned on.");
90
+ "allowPrivateAddress or userAgent options.");
88
91
  }
89
92
  }
93
+ const { allowPrivateAddress, userAgent } = options;
90
94
  this.documentLoader = options.documentLoader ?? kvCache({
91
- loader: options.allowPrivateAddress
92
- ? (url) => fetchDocumentLoader(url, true)
93
- : fetchDocumentLoader,
95
+ loader: getDocumentLoader({ allowPrivateAddress, userAgent }),
94
96
  kv: options.kv,
95
97
  prefix: this.kvPrefixes.remoteDocument,
96
98
  });
97
99
  this.contextLoader = options.contextLoader ?? this.documentLoader;
98
100
  this.authenticatedDocumentLoaderFactory =
99
101
  options.authenticatedDocumentLoaderFactory ??
100
- (options.allowPrivateAddress
101
- ? (identity) => getAuthenticatedDocumentLoader(identity, true)
102
- : getAuthenticatedDocumentLoader);
102
+ ((identity) => getAuthenticatedDocumentLoader(identity, {
103
+ allowPrivateAddress,
104
+ userAgent,
105
+ }));
106
+ this.userAgent = userAgent;
103
107
  this.onOutboxError = options.onOutboxError;
104
108
  this.signatureTimeWindow = options.signatureTimeWindow ?? { hours: 1 };
105
109
  this.skipSignatureVerification = options.skipSignatureVerification ?? false;
@@ -1554,6 +1558,7 @@ export class ContextImpl {
1554
1558
  return lookupObject(identifier, {
1555
1559
  documentLoader: options.documentLoader ?? this.documentLoader,
1556
1560
  contextLoader: options.contextLoader ?? this.contextLoader,
1561
+ userAgent: options.userAgent ?? this.federation.userAgent,
1557
1562
  });
1558
1563
  }
1559
1564
  traverseCollection(collection, options = {}) {
@@ -1,12 +1,20 @@
1
1
  import { getLogger } from "@logtape/logtape";
2
2
  import { parse } from "../deps/jsr.io/@std/semver/1.0.3/mod.js";
3
+ import { getUserAgent, } from "../runtime/docloader.js";
3
4
  const logger = getLogger(["fedify", "nodeinfo", "client"]);
4
5
  export async function getNodeInfo(url, options = {}) {
5
6
  try {
6
7
  let nodeInfoUrl = url;
7
8
  if (!options.direct) {
8
9
  const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
9
- const wellKnownResponse = await fetch(wellKnownUrl);
10
+ const wellKnownResponse = await fetch(wellKnownUrl, {
11
+ headers: {
12
+ Accept: "application/json",
13
+ "User-Agent": typeof options.userAgent === "string"
14
+ ? options.userAgent
15
+ : getUserAgent(options.userAgent),
16
+ },
17
+ });
10
18
  if (!wellKnownResponse.ok) {
11
19
  logger.error("Failed to fetch {url}: {status} {statusText}", {
12
20
  url: wellKnownUrl.href,
@@ -28,7 +36,14 @@ export async function getNodeInfo(url, options = {}) {
28
36
  }
29
37
  nodeInfoUrl = link.href;
30
38
  }
31
- const response = await fetch(nodeInfoUrl);
39
+ const response = await fetch(nodeInfoUrl, {
40
+ headers: {
41
+ Accept: "application/json",
42
+ "User-Agent": typeof options.userAgent === "string"
43
+ ? options.userAgent
44
+ : getUserAgent(options.userAgent),
45
+ },
46
+ });
32
47
  if (!response.ok) {
33
48
  logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
34
49
  url: nodeInfoUrl.toString(),
@@ -1,6 +1,8 @@
1
1
  import * as dntShim from "../_dnt.shims.js";
2
2
  import { HTTPHeaderLink } from "@hugoalh/http-header-link";
3
3
  import { getLogger } from "@logtape/logtape";
4
+ import process from "node:process";
5
+ import metadata from "../deno.js";
4
6
  import { signRequest } from "../sig/http.js";
5
7
  import { validateCryptoKey } from "../sig/key.js";
6
8
  import preloadedContexts from "./contexts.js";
@@ -26,10 +28,13 @@ export class FetchError extends Error {
26
28
  this.url = typeof url === "string" ? new URL(url) : url;
27
29
  }
28
30
  }
29
- function createRequest(url) {
31
+ function createRequest(url, options = {}) {
30
32
  return new Request(url, {
31
33
  headers: {
32
34
  Accept: "application/activity+json, application/ld+json",
35
+ "User-Agent": typeof options.userAgent === "string"
36
+ ? options.userAgent
37
+ : getUserAgent(options.userAgent),
33
38
  },
34
39
  redirect: "manual",
35
40
  });
@@ -125,6 +130,65 @@ async function getRemoteDocument(url, response, fetch) {
125
130
  documentUrl,
126
131
  };
127
132
  }
133
+ /**
134
+ * Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
135
+ *
136
+ * The created loader preloads the below frequently used contexts by default
137
+ * (unless `options.ignorePreloadedContexts` is set to `true`):
138
+ *
139
+ * - <https://www.w3.org/ns/activitystreams>
140
+ * - <https://w3id.org/security/v1>
141
+ * - <https://w3id.org/security/data-integrity/v1>
142
+ * - <https://www.w3.org/ns/did/v1>
143
+ * - <https://w3id.org/security/multikey/v1>
144
+ * - <https://purl.archive.org/socialweb/webfinger>
145
+ * - <http://schema.org/>
146
+ * @param options Options for the document loader.
147
+ * @returns The document loader.
148
+ * @since 1.3.0
149
+ */
150
+ export function getDocumentLoader({ allowPrivateAddress, skipPreloadedContexts, userAgent } = {}) {
151
+ async function load(url) {
152
+ if (!skipPreloadedContexts && url in preloadedContexts) {
153
+ logger.debug("Using preloaded context: {url}.", { url });
154
+ return {
155
+ contextUrl: null,
156
+ document: preloadedContexts[url],
157
+ documentUrl: url,
158
+ };
159
+ }
160
+ if (!allowPrivateAddress) {
161
+ try {
162
+ await validatePublicUrl(url);
163
+ }
164
+ catch (error) {
165
+ if (error instanceof UrlError) {
166
+ logger.error("Disallowed private URL: {url}", { url, error });
167
+ }
168
+ throw error;
169
+ }
170
+ }
171
+ const request = createRequest(url, { userAgent });
172
+ logRequest(request);
173
+ const response = await fetch(request, {
174
+ // Since Bun has a bug that ignores the `Request.redirect` option,
175
+ // to work around it we specify `redirect: "manual"` here too:
176
+ // https://github.com/oven-sh/bun/issues/10754
177
+ redirect: "manual",
178
+ });
179
+ // Follow redirects manually to get the final URL:
180
+ if (response.status >= 300 && response.status < 400 &&
181
+ response.headers.has("Location")) {
182
+ return load(response.headers.get("Location"));
183
+ }
184
+ return getRemoteDocument(url, response, load);
185
+ }
186
+ return load;
187
+ }
188
+ const _fetchDocumentLoader = getDocumentLoader();
189
+ const _fetchDocumentLoader_allowPrivateAddress = getDocumentLoader({
190
+ allowPrivateAddress: true,
191
+ });
128
192
  /**
129
193
  * A JSON-LD document loader that utilizes the browser's `fetch` API.
130
194
  *
@@ -135,45 +199,20 @@ async function getRemoteDocument(url, response, fetch) {
135
199
  * - <https://w3id.org/security/data-integrity/v1>
136
200
  * - <https://www.w3.org/ns/did/v1>
137
201
  * - <https://w3id.org/security/multikey/v1>
202
+ * - <https://purl.archive.org/socialweb/webfinger>
203
+ * - <http://schema.org/>
138
204
  * @param url The URL of the document to load.
139
205
  * @param allowPrivateAddress Whether to allow fetching private network
140
206
  * addresses. Turned off by default.
141
207
  * @returns The remote document.
208
+ * @deprecated Use {@link getDocumentLoader} instead.
142
209
  */
143
- export async function fetchDocumentLoader(url, allowPrivateAddress = false) {
144
- if (url in preloadedContexts) {
145
- logger.debug("Using preloaded context: {url}.", { url });
146
- return {
147
- contextUrl: null,
148
- document: preloadedContexts[url],
149
- documentUrl: url,
150
- };
151
- }
152
- if (!allowPrivateAddress) {
153
- try {
154
- await validatePublicUrl(url);
155
- }
156
- catch (error) {
157
- if (error instanceof UrlError) {
158
- logger.error("Disallowed private URL: {url}", { url, error });
159
- }
160
- throw error;
161
- }
162
- }
163
- const request = createRequest(url);
164
- logRequest(request);
165
- const response = await fetch(request, {
166
- // Since Bun has a bug that ignores the `Request.redirect` option,
167
- // to work around it we specify `redirect: "manual"` here too:
168
- // https://github.com/oven-sh/bun/issues/10754
169
- redirect: "manual",
170
- });
171
- // Follow redirects manually to get the final URL:
172
- if (response.status >= 300 && response.status < 400 &&
173
- response.headers.has("Location")) {
174
- return fetchDocumentLoader(response.headers.get("Location"), allowPrivateAddress);
175
- }
176
- return getRemoteDocument(url, response, (url) => fetchDocumentLoader(url, allowPrivateAddress));
210
+ export function fetchDocumentLoader(url, allowPrivateAddress = false) {
211
+ logger.warn("fetchDocumentLoader() function is deprecated. " +
212
+ "Use getDocumentLoader() function instead.");
213
+ return (allowPrivateAddress
214
+ ? _fetchDocumentLoader_allowPrivateAddress
215
+ : _fetchDocumentLoader)(url);
177
216
  }
178
217
  /**
179
218
  * Gets an authenticated {@link DocumentLoader} for the given identity.
@@ -181,13 +220,12 @@ export async function fetchDocumentLoader(url, allowPrivateAddress = false) {
181
220
  * the fetched documents.
182
221
  * @param identity The identity to get the document loader for.
183
222
  * The actor's key pair.
184
- * @param allowPrivateAddress Whether to allow fetching private network
185
- * addresses. Turned off by default.
223
+ * @param options The options for the document loader.
186
224
  * @returns The authenticated document loader.
187
225
  * @throws {TypeError} If the key is invalid or unsupported.
188
226
  * @since 0.4.0
189
227
  */
190
- export function getAuthenticatedDocumentLoader(identity, allowPrivateAddress = false) {
228
+ export function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAgent } = {}) {
191
229
  validateCryptoKey(identity.privateKey);
192
230
  async function load(url) {
193
231
  if (!allowPrivateAddress) {
@@ -201,7 +239,7 @@ export function getAuthenticatedDocumentLoader(identity, allowPrivateAddress = f
201
239
  throw error;
202
240
  }
203
241
  }
204
- let request = createRequest(url);
242
+ let request = createRequest(url, { userAgent });
205
243
  request = await signRequest(request, identity.privateKey, identity.keyId);
206
244
  logRequest(request);
207
245
  const response = await fetch(request, {
@@ -268,3 +306,27 @@ export function kvCache({ loader, kv, prefix, rules }) {
268
306
  return cache;
269
307
  };
270
308
  }
309
+ /**
310
+ * Gets the user agent string for the given application and URL.
311
+ * @param options The options for making the user agent string.
312
+ * @returns The user agent string.
313
+ * @since 1.3.0
314
+ */
315
+ export function getUserAgent({ software, url } = {}) {
316
+ const fedify = `Fedify/${metadata.version}`;
317
+ const runtime = "Deno" in dntShim.dntGlobalThis
318
+ ? `Deno/${dntShim.Deno.version.deno}`
319
+ : "Bun" in dntShim.dntGlobalThis
320
+ // @ts-ignore: `Bun` is a global variable in Bun
321
+ ? `Bun/${Bun.version}`
322
+ : "process" in dntShim.dntGlobalThis
323
+ ? `Node.js/${process.version}`
324
+ : null;
325
+ const userAgent = software == null ? [fedify] : [software, fedify];
326
+ if (runtime != null)
327
+ userAgent.push(runtime);
328
+ if (url != null)
329
+ userAgent.push(`+${url.toString()}`);
330
+ const first = userAgent.shift();
331
+ return `${first} (${userAgent.join("; ")})`;
332
+ }
package/esm/sig/key.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as dntShim from "../_dnt.shims.js";
2
2
  import { getLogger } from "@logtape/logtape";
3
- import { fetchDocumentLoader, } from "../runtime/docloader.js";
3
+ import { getDocumentLoader, } from "../runtime/docloader.js";
4
4
  import { isActor } from "../vocab/actor.js";
5
5
  import { CryptographicKey, Object } from "../vocab/vocab.js";
6
6
  /**
@@ -123,7 +123,7 @@ cls, { documentLoader, contextLoader, keyCache } = {}) {
123
123
  logger.debug("Fetching key {keyId} to verify signature...", { keyId });
124
124
  let document;
125
125
  try {
126
- const remoteDocument = await (documentLoader ?? fetchDocumentLoader)(keyId);
126
+ const remoteDocument = await (documentLoader ?? getDocumentLoader())(keyId);
127
127
  document = remoteDocument.document;
128
128
  }
129
129
  catch (_) {
package/esm/sig/ld.js CHANGED
@@ -4,7 +4,7 @@ import { decodeBase64, encodeBase64 } from "../deps/jsr.io/@std/encoding/1.0.5/b
4
4
  import { encodeHex } from "../deps/jsr.io/@std/encoding/1.0.5/hex.js";
5
5
  // @ts-ignore TS7016
6
6
  import jsonld from "jsonld";
7
- import { fetchDocumentLoader, } from "../runtime/docloader.js";
7
+ import { getDocumentLoader, } from "../runtime/docloader.js";
8
8
  import { Activity, CryptographicKey, Object } from "../vocab/vocab.js";
9
9
  import { fetchKey, validateCryptoKey } from "./key.js";
10
10
  const logger = getLogger(["fedify", "sig", "ld"]);
@@ -200,7 +200,7 @@ export async function verifyJsonLd(jsonLd, options = {}) {
200
200
  async function hashJsonLd(jsonLd, contextLoader) {
201
201
  const canon = await jsonld.canonize(jsonLd, {
202
202
  format: "application/n-quads",
203
- documentLoader: contextLoader ?? fetchDocumentLoader,
203
+ documentLoader: contextLoader ?? getDocumentLoader(),
204
204
  });
205
205
  const encoder = new TextEncoder();
206
206
  const hash = await dntShim.crypto.subtle.digest("SHA-256", encoder.encode(canon));
package/esm/sig/owner.js CHANGED
@@ -1,4 +1,4 @@
1
- import { fetchDocumentLoader, } from "../runtime/docloader.js";
1
+ import { getDocumentLoader, } from "../runtime/docloader.js";
2
2
  import { isActor } from "../vocab/actor.js";
3
3
  import { CryptographicKey, Object as ASObject, } from "../vocab/vocab.js";
4
4
  export { exportJwk, generateCryptoKeyPair, importJwk } from "./key.js";
@@ -32,8 +32,8 @@ export async function doesActorOwnKey(activity, key, options) {
32
32
  * owner.
33
33
  */
34
34
  export async function getKeyOwner(keyId, options) {
35
- const documentLoader = options.documentLoader ?? fetchDocumentLoader;
36
- const contextLoader = options.contextLoader ?? fetchDocumentLoader;
35
+ const documentLoader = options.documentLoader ?? getDocumentLoader();
36
+ const contextLoader = options.contextLoader ?? getDocumentLoader();
37
37
  let object;
38
38
  if (keyId instanceof CryptographicKey) {
39
39
  object = keyId;
@@ -67,7 +67,7 @@ export function getActorClassByTypeName(typeName) {
67
67
  * ```
68
68
  *
69
69
  * @param actor The actor or actor URI to get the handle from.
70
- * @param options The options for normalizing the actor handle.
70
+ * @param options The extra options for getting the actor handle.
71
71
  * @returns The actor handle. It starts with `@` and is followed by the
72
72
  * username and domain, separated by `@` by default (it can be
73
73
  * customized with the options).
@@ -78,7 +78,9 @@ export function getActorClassByTypeName(typeName) {
78
78
  export async function getActorHandle(actor, options = {}) {
79
79
  const actorId = actor instanceof URL ? actor : actor.id;
80
80
  if (actorId != null) {
81
- const result = await lookupWebFinger(actorId);
81
+ const result = await lookupWebFinger(actorId, {
82
+ userAgent: options.userAgent,
83
+ });
82
84
  if (result != null) {
83
85
  const aliases = [...(result.aliases ?? [])];
84
86
  if (result.subject != null)
@@ -88,7 +90,7 @@ export async function getActorHandle(actor, options = {}) {
88
90
  if (match != null) {
89
91
  const hostname = new URL(`https://${match[2]}/`).hostname;
90
92
  if (hostname !== actorId.hostname &&
91
- !await verifyCrossOriginActorHandle(actorId.href, alias)) {
93
+ !await verifyCrossOriginActorHandle(actorId.href, alias, options.userAgent)) {
92
94
  continue;
93
95
  }
94
96
  return normalizeActorHandle(`@${match[1]}@${match[2]}`, options);
@@ -102,8 +104,8 @@ export async function getActorHandle(actor, options = {}) {
102
104
  }
103
105
  throw new TypeError("Actor does not have enough information to get the handle.");
104
106
  }
105
- async function verifyCrossOriginActorHandle(actorId, alias) {
106
- const response = await lookupWebFinger(alias);
107
+ async function verifyCrossOriginActorHandle(actorId, alias, userAgent) {
108
+ const response = await lookupWebFinger(alias, { userAgent });
107
109
  if (response == null)
108
110
  return false;
109
111
  for (const alias of response.aliases ?? []) {
@@ -1,7 +1,7 @@
1
1
  import * as dntShim from "../_dnt.shims.js";
2
2
  import { getLogger } from "@logtape/logtape";
3
3
  import { delay } from "../deps/jsr.io/@std/async/1.0.8/delay.js";
4
- import { fetchDocumentLoader, } from "../runtime/docloader.js";
4
+ import { getDocumentLoader, } from "../runtime/docloader.js";
5
5
  import { lookupWebFinger } from "../webfinger/lookup.js";
6
6
  import { Object } from "./vocab.js";
7
7
  const logger = getLogger(["fedify", "vocab", "lookup"]);
@@ -39,7 +39,8 @@ const handleRegexp = /^@?((?:[-A-Za-z0-9._~!$&'()*+,;=]|%[A-Fa-f0-9]{2})+)@([^@]
39
39
  * @since 0.2.0
40
40
  */
41
41
  export async function lookupObject(identifier, options = {}) {
42
- const documentLoader = options.documentLoader ?? fetchDocumentLoader;
42
+ const documentLoader = options.documentLoader ??
43
+ getDocumentLoader({ userAgent: options.userAgent });
43
44
  if (typeof identifier === "string") {
44
45
  const match = handleRegexp.exec(identifier);
45
46
  if (match)
@@ -57,7 +58,9 @@ export async function lookupObject(identifier, options = {}) {
57
58
  }
58
59
  }
59
60
  if (document == null) {
60
- const jrd = await lookupWebFinger(identifier);
61
+ const jrd = await lookupWebFinger(identifier, {
62
+ userAgent: options.userAgent,
63
+ });
61
64
  if (jrd?.links == null)
62
65
  return null;
63
66
  for (const l of jrd.links) {