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

Sign up to get free protection for your applications and to get access to all the features.
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) {