@fedify/vocab-runtime 2.1.0 → 2.1.2
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/deno.json +1 -1
- package/dist/{chunk-CUT6urMc.cjs → chunk-CKQMccvm.cjs} +7 -9
- package/dist/jsonld.cjs +4 -5
- package/dist/jsonld.d.cts +3 -4
- package/dist/jsonld.d.ts +2 -2
- package/dist/jsonld.js +1 -3
- package/dist/mod.cjs +107 -180
- package/dist/mod.d.cts +1 -2
- package/dist/mod.d.ts +1 -2
- package/dist/mod.js +83 -159
- package/dist/tests/{chunk-DWy1uDak.cjs → chunk-Do9eywBl.cjs} +13 -17
- package/dist/tests/decimal.test.cjs +12 -21
- package/dist/tests/{decimal.test.js → decimal.test.mjs} +10 -18
- package/dist/tests/{docloader-D3nu2LmR.cjs → docloader-0Yz9aPvU.cjs} +78 -91
- package/dist/tests/{docloader-U31begIa.js → docloader-DiLx08rm.mjs} +62 -75
- package/dist/tests/docloader.test.cjs +98 -88
- package/dist/tests/{docloader.test.js → docloader.test.mjs} +98 -94
- package/dist/tests/internal/multicodec.test.cjs +5 -7
- package/dist/tests/internal/{multicodec.test.js → multicodec.test.mjs} +3 -4
- package/dist/tests/{key-ByCmSI2y.js → key-BeTHFQJK.mjs} +14 -25
- package/dist/tests/{key-CCPn6TEY.cjs → key-DTTIntwb.cjs} +60 -71
- package/dist/tests/key.test.cjs +23 -45
- package/dist/tests/{key.test.js → key.test.mjs} +21 -42
- package/dist/tests/{langstr-EPh86hXK.cjs → langstr-CbAxaeEZ.cjs} +6 -8
- package/dist/tests/{langstr-BsVE3s9u.js → langstr-Di5AvKpB.mjs} +1 -2
- package/dist/tests/langstr.test.cjs +7 -8
- package/dist/tests/{langstr.test.js → langstr.test.mjs} +3 -4
- package/dist/tests/{link-DYNFAdNu.cjs → link-FguCydMA.cjs} +6 -8
- package/dist/tests/{link-C3q2TC2G.js → link-NUUWCdnK.mjs} +1 -2
- package/dist/tests/link.test.cjs +5 -7
- package/dist/tests/{link.test.js → link.test.mjs} +3 -4
- package/dist/tests/multibase/multibase.test.cjs +10 -17
- package/dist/tests/multibase/{multibase.test.js → multibase.test.mjs} +10 -18
- package/dist/tests/{multibase-B4g8pz6F.js → multibase-BgU9XRf7.mjs} +5 -12
- package/dist/tests/{multibase-o_ovPHYJ.cjs → multibase-F7LtMMsK.cjs} +43 -49
- package/dist/tests/{multicodec--6hQ74zI.cjs → multicodec-CxGVGa91.cjs} +16 -18
- package/dist/tests/{multicodec-Dq3IiOV4.js → multicodec-CyFp54fI.mjs} +1 -2
- package/dist/tests/{request-DyrEDYQ-.cjs → request-B4BOehn0.cjs} +38 -72
- package/dist/tests/{request-BH_NlxCL.js → request-Nob25QBF.mjs} +4 -45
- package/dist/tests/request.test.cjs +23 -24
- package/dist/tests/request.test.mjs +42 -0
- package/dist/tests/{url-CWEP9Zs9.js → url-BQ_kgmCk.mjs} +3 -7
- package/dist/tests/{url-DIjOdK8Q.cjs → url-pFuSds44.cjs} +31 -35
- package/dist/tests/url.test.cjs +5 -7
- package/dist/tests/{url.test.js → url.test.mjs} +3 -4
- package/package.json +3 -3
- package/src/docloader.test.ts +67 -0
- package/src/docloader.ts +43 -11
- package/dist/tests/request.test.js +0 -43
- /package/dist/tests/{decimal.test.d.ts → decimal.test.d.mts} +0 -0
- /package/dist/tests/{docloader.test.d.ts → docloader.test.d.mts} +0 -0
- /package/dist/tests/internal/{multicodec.test.d.ts → multicodec.test.d.mts} +0 -0
- /package/dist/tests/{key.test.d.ts → key.test.d.mts} +0 -0
- /package/dist/tests/{langstr.test.d.ts → langstr.test.d.mts} +0 -0
- /package/dist/tests/{link.test.d.ts → link.test.d.mts} +0 -0
- /package/dist/tests/multibase/{multibase.test.d.ts → multibase.test.d.mts} +0 -0
- /package/dist/tests/{request.test.d.ts → request.test.d.mts} +0 -0
- /package/dist/tests/{url.test.d.ts → url.test.d.mts} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { o as version, r as getUserAgent } from "./request-Nob25QBF.mjs";
|
|
2
|
+
import { deepStrictEqual } from "node:assert";
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
//#region src/request.test.ts
|
|
6
|
+
test("getUserAgent()", () => {
|
|
7
|
+
if ("Deno" in globalThis) {
|
|
8
|
+
deepStrictEqual(getUserAgent(), `Fedify/${version} (Deno/${Deno.version.deno})`);
|
|
9
|
+
deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${version}; Deno/${Deno.version.deno})`);
|
|
10
|
+
deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${version} (Deno/${Deno.version.deno}; +https://example.com/)`);
|
|
11
|
+
deepStrictEqual(getUserAgent({
|
|
12
|
+
software: "MyApp/1.0.0",
|
|
13
|
+
url: new URL("https://example.com/")
|
|
14
|
+
}), `MyApp/1.0.0 (Fedify/${version}; Deno/${Deno.version.deno}; +https://example.com/)`);
|
|
15
|
+
} else if ("Bun" in globalThis) {
|
|
16
|
+
deepStrictEqual(getUserAgent(), `Fedify/${version} (Bun/${Bun.version})`);
|
|
17
|
+
deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${version}; Bun/${Bun.version})`);
|
|
18
|
+
deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${version} (Bun/${Bun.version}; +https://example.com/)`);
|
|
19
|
+
deepStrictEqual(getUserAgent({
|
|
20
|
+
software: "MyApp/1.0.0",
|
|
21
|
+
url: new URL("https://example.com/")
|
|
22
|
+
}), `MyApp/1.0.0 (Fedify/${version}; Bun/${Bun.version}; +https://example.com/)`);
|
|
23
|
+
} else if (navigator.userAgent === "Cloudflare-Workers") {
|
|
24
|
+
deepStrictEqual(getUserAgent(), `Fedify/${version} (Cloudflare-Workers)`);
|
|
25
|
+
deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${version}; Cloudflare-Workers)`);
|
|
26
|
+
deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${version} (Cloudflare-Workers; +https://example.com/)`);
|
|
27
|
+
deepStrictEqual(getUserAgent({
|
|
28
|
+
software: "MyApp/1.0.0",
|
|
29
|
+
url: new URL("https://example.com/")
|
|
30
|
+
}), `MyApp/1.0.0 (Fedify/${version}; Cloudflare-Workers; +https://example.com/)`);
|
|
31
|
+
} else {
|
|
32
|
+
deepStrictEqual(getUserAgent(), `Fedify/${version} (Node.js/${process.versions.node})`);
|
|
33
|
+
deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${version}; Node.js/${process.versions.node})`);
|
|
34
|
+
deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${version} (Node.js/${process.versions.node}; +https://example.com/)`);
|
|
35
|
+
deepStrictEqual(getUserAgent({
|
|
36
|
+
software: "MyApp/1.0.0",
|
|
37
|
+
url: new URL("https://example.com/")
|
|
38
|
+
}), `MyApp/1.0.0 (Fedify/${version}; Node.js/${process.versions.node}; +https://example.com/)`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
42
|
+
export {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { lookup } from "node:dns/promises";
|
|
2
2
|
import { isIP } from "node:net";
|
|
3
|
-
|
|
4
3
|
//#region src/url.ts
|
|
5
4
|
var UrlError = class extends Error {
|
|
6
5
|
constructor(message) {
|
|
@@ -18,8 +17,7 @@ async function validatePublicUrl(url) {
|
|
|
18
17
|
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.substring(1, hostname.length - 2);
|
|
19
18
|
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
20
19
|
if ("Deno" in globalThis && !isIP(hostname)) {
|
|
21
|
-
|
|
22
|
-
if (netPermission.state !== "granted") return;
|
|
20
|
+
if ((await Deno.permissions.query({ name: "net" })).state !== "granted") return;
|
|
23
21
|
}
|
|
24
22
|
if ("Bun" in globalThis) {
|
|
25
23
|
if (hostname === "example.com" || hostname.endsWith(".example.com")) return;
|
|
@@ -55,9 +53,7 @@ function expandIPv6Address(address) {
|
|
|
55
53
|
if (address.startsWith("::")) address = "0000" + address;
|
|
56
54
|
if (address.endsWith("::")) address = address + "0000";
|
|
57
55
|
address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
|
|
58
|
-
|
|
59
|
-
return parts.map((part) => part.padStart(4, "0")).join(":");
|
|
56
|
+
return address.split(":").map((part) => part.padStart(4, "0")).join(":");
|
|
60
57
|
}
|
|
61
|
-
|
|
62
58
|
//#endregion
|
|
63
|
-
export {
|
|
59
|
+
export { validatePublicUrl as a, isValidPublicIPv6Address as i, expandIPv6Address as n, isValidPublicIPv4Address as r, UrlError as t };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
require("./chunk-Do9eywBl.cjs");
|
|
2
|
+
let node_dns_promises = require("node:dns/promises");
|
|
3
|
+
let node_net = require("node:net");
|
|
5
4
|
//#region src/url.ts
|
|
6
5
|
var UrlError = class extends Error {
|
|
7
6
|
constructor(message) {
|
|
@@ -19,8 +18,7 @@ async function validatePublicUrl(url) {
|
|
|
19
18
|
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.substring(1, hostname.length - 2);
|
|
20
19
|
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
21
20
|
if ("Deno" in globalThis && !(0, node_net.isIP)(hostname)) {
|
|
22
|
-
|
|
23
|
-
if (netPermission.state !== "granted") return;
|
|
21
|
+
if ((await Deno.permissions.query({ name: "net" })).state !== "granted") return;
|
|
24
22
|
}
|
|
25
23
|
if ("Bun" in globalThis) {
|
|
26
24
|
if (hostname === "example.com" || hostname.endsWith(".example.com")) return;
|
|
@@ -56,38 +54,36 @@ function expandIPv6Address(address) {
|
|
|
56
54
|
if (address.startsWith("::")) address = "0000" + address;
|
|
57
55
|
if (address.endsWith("::")) address = address + "0000";
|
|
58
56
|
address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
|
|
59
|
-
|
|
60
|
-
return parts.map((part) => part.padStart(4, "0")).join(":");
|
|
57
|
+
return address.split(":").map((part) => part.padStart(4, "0")).join(":");
|
|
61
58
|
}
|
|
62
|
-
|
|
63
59
|
//#endregion
|
|
64
|
-
Object.defineProperty(exports,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
Object.defineProperty(exports, "UrlError", {
|
|
61
|
+
enumerable: true,
|
|
62
|
+
get: function() {
|
|
63
|
+
return UrlError;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
Object.defineProperty(exports, "expandIPv6Address", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
get: function() {
|
|
69
|
+
return expandIPv6Address;
|
|
70
|
+
}
|
|
69
71
|
});
|
|
70
|
-
Object.defineProperty(exports,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
Object.defineProperty(exports, "isValidPublicIPv4Address", {
|
|
73
|
+
enumerable: true,
|
|
74
|
+
get: function() {
|
|
75
|
+
return isValidPublicIPv4Address;
|
|
76
|
+
}
|
|
75
77
|
});
|
|
76
|
-
Object.defineProperty(exports,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
Object.defineProperty(exports, "isValidPublicIPv6Address", {
|
|
79
|
+
enumerable: true,
|
|
80
|
+
get: function() {
|
|
81
|
+
return isValidPublicIPv6Address;
|
|
82
|
+
}
|
|
81
83
|
});
|
|
82
|
-
Object.defineProperty(exports,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
Object.defineProperty(exports, "validatePublicUrl", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
get: function() {
|
|
87
|
+
return validatePublicUrl;
|
|
88
|
+
}
|
|
87
89
|
});
|
|
88
|
-
Object.defineProperty(exports, 'validatePublicUrl', {
|
|
89
|
-
enumerable: true,
|
|
90
|
-
get: function () {
|
|
91
|
-
return validatePublicUrl;
|
|
92
|
-
}
|
|
93
|
-
});
|
package/dist/tests/url.test.cjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
const require_url = require(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
require("./chunk-Do9eywBl.cjs");
|
|
2
|
+
const require_url = require("./url-pFuSds44.cjs");
|
|
3
|
+
let node_assert = require("node:assert");
|
|
4
|
+
let node_test = require("node:test");
|
|
6
5
|
//#region src/url.test.ts
|
|
7
6
|
(0, node_test.test)("validatePublicUrl()", async () => {
|
|
8
7
|
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("ftp://localhost"), require_url.UrlError);
|
|
@@ -33,5 +32,4 @@ const node_test = require_chunk.__toESM(require("node:test"));
|
|
|
33
32
|
(0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("2001:db8::"), "2001:0db8:0000:0000:0000:0000:0000:0000");
|
|
34
33
|
(0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("2001:db8::1"), "2001:0db8:0000:0000:0000:0000:0000:0001");
|
|
35
34
|
});
|
|
36
|
-
|
|
37
|
-
//#endregion
|
|
35
|
+
//#endregion
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as validatePublicUrl, i as isValidPublicIPv6Address, n as expandIPv6Address, r as isValidPublicIPv4Address, t as UrlError } from "./url-BQ_kgmCk.mjs";
|
|
2
2
|
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
3
3
|
import { test } from "node:test";
|
|
4
|
-
|
|
5
4
|
//#region src/url.test.ts
|
|
6
5
|
test("validatePublicUrl()", async () => {
|
|
7
6
|
await rejects(() => validatePublicUrl("ftp://localhost"), UrlError);
|
|
@@ -32,5 +31,5 @@ test("expandIPv6Address()", () => {
|
|
|
32
31
|
deepStrictEqual(expandIPv6Address("2001:db8::"), "2001:0db8:0000:0000:0000:0000:0000:0000");
|
|
33
32
|
deepStrictEqual(expandIPv6Address("2001:db8::1"), "2001:0db8:0000:0000:0000:0000:0000:0001");
|
|
34
33
|
});
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
//#endregion
|
|
35
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/vocab-runtime",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"homepage": "https://fedify.dev/",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/node": "^24.2.1",
|
|
63
63
|
"fetch-mock": "^12.5.4",
|
|
64
|
-
"tsdown": "^0.
|
|
65
|
-
"typescript": "^5.9.
|
|
64
|
+
"tsdown": "^0.21.6",
|
|
65
|
+
"typescript": "^5.9.2"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@logtape/logtape": "^2.0.5",
|
package/src/docloader.test.ts
CHANGED
|
@@ -369,6 +369,73 @@ test("getDocumentLoader()", async (t) => {
|
|
|
369
369
|
);
|
|
370
370
|
});
|
|
371
371
|
|
|
372
|
+
let redirectAttempts = 0;
|
|
373
|
+
fetchMock.get("begin:https://example.com/too-many-redirects/", (cl) => {
|
|
374
|
+
redirectAttempts++;
|
|
375
|
+
const index = Number(cl.url.split("/").at(-1));
|
|
376
|
+
return {
|
|
377
|
+
status: 302,
|
|
378
|
+
headers: {
|
|
379
|
+
Location: `https://example.com/too-many-redirects/${index + 1}`,
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
await t.test("too many redirects", async () => {
|
|
385
|
+
redirectAttempts = 0;
|
|
386
|
+
await rejects(
|
|
387
|
+
() => fetchDocumentLoader("https://example.com/too-many-redirects/0"),
|
|
388
|
+
FetchError,
|
|
389
|
+
"Too many redirections",
|
|
390
|
+
);
|
|
391
|
+
deepStrictEqual(redirectAttempts, 21);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
let loopAttempts = 0;
|
|
395
|
+
fetchMock.get("https://example.com/redirect-loop-a", () => {
|
|
396
|
+
loopAttempts++;
|
|
397
|
+
return {
|
|
398
|
+
status: 302,
|
|
399
|
+
headers: { Location: "https://example.com/redirect-loop-b" },
|
|
400
|
+
};
|
|
401
|
+
});
|
|
402
|
+
fetchMock.get("https://example.com/redirect-loop-b", () => {
|
|
403
|
+
loopAttempts++;
|
|
404
|
+
return {
|
|
405
|
+
status: 302,
|
|
406
|
+
headers: { Location: "https://example.com/redirect-loop-a" },
|
|
407
|
+
};
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
await t.test("redirect loop", async () => {
|
|
411
|
+
loopAttempts = 0;
|
|
412
|
+
await rejects(
|
|
413
|
+
() => fetchDocumentLoader("https://example.com/redirect-loop-a"),
|
|
414
|
+
FetchError,
|
|
415
|
+
"Redirect loop detected",
|
|
416
|
+
);
|
|
417
|
+
deepStrictEqual(loopAttempts, 2);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
let relativeLoopAttempts = 0;
|
|
421
|
+
fetchMock.get("https://example.com/redirect-loop-relative", () => {
|
|
422
|
+
relativeLoopAttempts++;
|
|
423
|
+
return {
|
|
424
|
+
status: 302,
|
|
425
|
+
headers: { Location: "/redirect-loop-relative" },
|
|
426
|
+
};
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
await t.test("redirect loop with relative location", async () => {
|
|
430
|
+
relativeLoopAttempts = 0;
|
|
431
|
+
await rejects(
|
|
432
|
+
() => fetchDocumentLoader("https://example.com/redirect-loop-relative"),
|
|
433
|
+
FetchError,
|
|
434
|
+
"Redirect loop detected",
|
|
435
|
+
);
|
|
436
|
+
deepStrictEqual(relativeLoopAttempts, 1);
|
|
437
|
+
});
|
|
438
|
+
|
|
372
439
|
// Regression test for ReDoS vulnerability (CVE-2025-68475)
|
|
373
440
|
// Malicious HTML payload: <a a="b" a="b" ... (unclosed tag)
|
|
374
441
|
// With the vulnerable regex, this causes catastrophic backtracking
|
package/src/docloader.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { UrlError, validatePublicUrl } from "./url.ts";
|
|
13
13
|
|
|
14
14
|
const logger = getLogger(["fedify", "runtime", "docloader"]);
|
|
15
|
+
const DEFAULT_MAX_REDIRECTION = 20;
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* A remote JSON-LD document and its context fetched by
|
|
@@ -293,38 +294,45 @@ export function getDocumentLoader(
|
|
|
293
294
|
async function load(
|
|
294
295
|
url: string,
|
|
295
296
|
options?: DocumentLoaderOptions,
|
|
297
|
+
redirected = 0,
|
|
298
|
+
visited = new Set<string>(),
|
|
296
299
|
): Promise<RemoteDocument> {
|
|
297
300
|
options?.signal?.throwIfAborted();
|
|
298
|
-
|
|
299
|
-
|
|
301
|
+
const currentUrl = new URL(url).href;
|
|
302
|
+
if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
|
|
303
|
+
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|
|
300
304
|
return {
|
|
301
305
|
contextUrl: null,
|
|
302
|
-
document: preloadedContexts[
|
|
303
|
-
documentUrl:
|
|
306
|
+
document: preloadedContexts[currentUrl],
|
|
307
|
+
documentUrl: currentUrl,
|
|
304
308
|
};
|
|
305
309
|
}
|
|
306
310
|
if (!allowPrivateAddress) {
|
|
307
311
|
try {
|
|
308
|
-
await validatePublicUrl(
|
|
312
|
+
await validatePublicUrl(currentUrl);
|
|
309
313
|
} catch (error) {
|
|
310
314
|
if (error instanceof UrlError) {
|
|
311
|
-
logger.error("Disallowed private URL: {url}", {
|
|
315
|
+
logger.error("Disallowed private URL: {url}", {
|
|
316
|
+
url: currentUrl,
|
|
317
|
+
error,
|
|
318
|
+
});
|
|
312
319
|
}
|
|
313
320
|
throw error;
|
|
314
321
|
}
|
|
315
322
|
}
|
|
323
|
+
visited.add(currentUrl);
|
|
316
324
|
|
|
317
325
|
return await tracer.startActiveSpan(
|
|
318
326
|
"activitypub.fetch_document",
|
|
319
327
|
{
|
|
320
328
|
kind: SpanKind.CLIENT,
|
|
321
329
|
attributes: {
|
|
322
|
-
"url.full":
|
|
330
|
+
"url.full": currentUrl,
|
|
323
331
|
},
|
|
324
332
|
},
|
|
325
333
|
async (span) => {
|
|
326
334
|
try {
|
|
327
|
-
const request = createActivityPubRequest(
|
|
335
|
+
const request = createActivityPubRequest(currentUrl, { userAgent });
|
|
328
336
|
logRequest(logger, request);
|
|
329
337
|
const response = await fetch(request, {
|
|
330
338
|
// Since Bun has a bug that ignores the `Request.redirect` option,
|
|
@@ -340,12 +348,36 @@ export function getDocumentLoader(
|
|
|
340
348
|
response.status >= 300 && response.status < 400 &&
|
|
341
349
|
response.headers.has("Location")
|
|
342
350
|
) {
|
|
343
|
-
|
|
351
|
+
if (redirected >= DEFAULT_MAX_REDIRECTION) {
|
|
352
|
+
logger.error(
|
|
353
|
+
"Too many redirections ({redirections}) while fetching document.",
|
|
354
|
+
{ redirections: redirected + 1, url: currentUrl },
|
|
355
|
+
);
|
|
356
|
+
throw new FetchError(
|
|
357
|
+
currentUrl,
|
|
358
|
+
`Too many redirections (${redirected + 1})`,
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
const redirectUrl = new URL(
|
|
362
|
+
response.headers.get("Location")!,
|
|
363
|
+
response.url === "" ? currentUrl : response.url,
|
|
364
|
+
).href;
|
|
344
365
|
span.setAttribute("http.redirect.url", redirectUrl);
|
|
345
|
-
|
|
366
|
+
if (visited.has(redirectUrl)) {
|
|
367
|
+
logger.error(
|
|
368
|
+
"Detected a redirect loop while fetching document: {url} -> " +
|
|
369
|
+
"{redirectUrl}",
|
|
370
|
+
{ url: currentUrl, redirectUrl },
|
|
371
|
+
);
|
|
372
|
+
throw new FetchError(
|
|
373
|
+
currentUrl,
|
|
374
|
+
`Redirect loop detected: ${redirectUrl}`,
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
return await load(redirectUrl, options, redirected + 1, visited);
|
|
346
378
|
}
|
|
347
379
|
|
|
348
|
-
const result = await getRemoteDocument(
|
|
380
|
+
const result = await getRemoteDocument(currentUrl, response, load);
|
|
349
381
|
span.setAttribute("docloader.document_url", result.documentUrl);
|
|
350
382
|
if (result.contextUrl != null) {
|
|
351
383
|
span.setAttribute("docloader.context_url", result.contextUrl);
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { deno_default, getUserAgent } from "./request-BH_NlxCL.js";
|
|
2
|
-
import { deepStrictEqual } from "node:assert";
|
|
3
|
-
import { test } from "node:test";
|
|
4
|
-
import process from "node:process";
|
|
5
|
-
|
|
6
|
-
//#region src/request.test.ts
|
|
7
|
-
test("getUserAgent()", () => {
|
|
8
|
-
if ("Deno" in globalThis) {
|
|
9
|
-
deepStrictEqual(getUserAgent(), `Fedify/${deno_default.version} (Deno/${Deno.version.deno})`);
|
|
10
|
-
deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Deno/${Deno.version.deno})`);
|
|
11
|
-
deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${deno_default.version} (Deno/${Deno.version.deno}; +https://example.com/)`);
|
|
12
|
-
deepStrictEqual(getUserAgent({
|
|
13
|
-
software: "MyApp/1.0.0",
|
|
14
|
-
url: new URL("https://example.com/")
|
|
15
|
-
}), `MyApp/1.0.0 (Fedify/${deno_default.version}; Deno/${Deno.version.deno}; +https://example.com/)`);
|
|
16
|
-
} else if ("Bun" in globalThis) {
|
|
17
|
-
deepStrictEqual(getUserAgent(), `Fedify/${deno_default.version} (Bun/${Bun.version})`);
|
|
18
|
-
deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Bun/${Bun.version})`);
|
|
19
|
-
deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${deno_default.version} (Bun/${Bun.version}; +https://example.com/)`);
|
|
20
|
-
deepStrictEqual(getUserAgent({
|
|
21
|
-
software: "MyApp/1.0.0",
|
|
22
|
-
url: new URL("https://example.com/")
|
|
23
|
-
}), `MyApp/1.0.0 (Fedify/${deno_default.version}; Bun/${Bun.version}; +https://example.com/)`);
|
|
24
|
-
} else if (navigator.userAgent === "Cloudflare-Workers") {
|
|
25
|
-
deepStrictEqual(getUserAgent(), `Fedify/${deno_default.version} (Cloudflare-Workers)`);
|
|
26
|
-
deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Cloudflare-Workers)`);
|
|
27
|
-
deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${deno_default.version} (Cloudflare-Workers; +https://example.com/)`);
|
|
28
|
-
deepStrictEqual(getUserAgent({
|
|
29
|
-
software: "MyApp/1.0.0",
|
|
30
|
-
url: new URL("https://example.com/")
|
|
31
|
-
}), `MyApp/1.0.0 (Fedify/${deno_default.version}; Cloudflare-Workers; +https://example.com/)`);
|
|
32
|
-
} else {
|
|
33
|
-
deepStrictEqual(getUserAgent(), `Fedify/${deno_default.version} (Node.js/${process.versions.node})`);
|
|
34
|
-
deepStrictEqual(getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${deno_default.version}; Node.js/${process.versions.node})`);
|
|
35
|
-
deepStrictEqual(getUserAgent({ url: "https://example.com/" }), `Fedify/${deno_default.version} (Node.js/${process.versions.node}; +https://example.com/)`);
|
|
36
|
-
deepStrictEqual(getUserAgent({
|
|
37
|
-
software: "MyApp/1.0.0",
|
|
38
|
-
url: new URL("https://example.com/")
|
|
39
|
-
}), `MyApp/1.0.0 (Fedify/${deno_default.version}; Node.js/${process.versions.node}; +https://example.com/)`);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
//#endregion
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|