@fedify/vocab-runtime 2.0.31-dev.2308 → 2.0.31-dev.2310
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 +2 -2
- package/dist/mod.cjs +1 -1
- package/dist/mod.js +1 -1
- package/dist/tests/{body-Bx6-vXo_.mjs → body-DhJNq-PQ.mjs} +1 -1
- package/dist/tests/{body-DGmcZ1Kz.cjs → body-N4Kc5pwz.cjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/docloader.test.cjs +41 -44
- package/dist/tests/docloader.test.mjs +32 -35
- package/dist/tests/multibase/multibase.test.cjs +7 -7
- package/dist/tests/multibase/multibase.test.mjs +5 -5
- package/dist/tests/{request-Ban3HXuI.cjs → request-CFKB-Yqd.cjs} +1 -1
- package/dist/tests/{request-BV7yaWdl.mjs → request-DDAEJJqw.mjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/package.json +6 -3
- package/scripts/test-bun.mjs +18 -0
- package/src/docloader.test.ts +75 -68
- package/src/multibase/multibase.test.ts +5 -5
package/deno.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/vocab-runtime",
|
|
3
|
-
"version": "2.0.31-dev.
|
|
3
|
+
"version": "2.0.31-dev.2310+e604de57",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/mod.ts",
|
|
@@ -34,6 +34,6 @@
|
|
|
34
34
|
},
|
|
35
35
|
"tasks": {
|
|
36
36
|
"check": "deno fmt --check && deno lint && deno check src/*.ts",
|
|
37
|
-
"test": "deno test"
|
|
37
|
+
"test": "deno test --allow-net --allow-env=LOG"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/dist/mod.cjs
CHANGED
|
@@ -4504,7 +4504,7 @@ const preloadedContexts = {
|
|
|
4504
4504
|
//#endregion
|
|
4505
4505
|
//#region deno.json
|
|
4506
4506
|
var name = "@fedify/vocab-runtime";
|
|
4507
|
-
var version = "2.0.31-dev.
|
|
4507
|
+
var version = "2.0.31-dev.2310+e604de57";
|
|
4508
4508
|
//#endregion
|
|
4509
4509
|
//#region src/request.ts
|
|
4510
4510
|
/**
|
package/dist/mod.js
CHANGED
|
@@ -4499,7 +4499,7 @@ const preloadedContexts = {
|
|
|
4499
4499
|
//#endregion
|
|
4500
4500
|
//#region deno.json
|
|
4501
4501
|
var name = "@fedify/vocab-runtime";
|
|
4502
|
-
var version = "2.0.31-dev.
|
|
4502
|
+
var version = "2.0.31-dev.2310+e604de57";
|
|
4503
4503
|
//#endregion
|
|
4504
4504
|
//#region src/request.ts
|
|
4505
4505
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-CFKB-Yqd.cjs");
|
|
3
3
|
let _logtape_logtape = require("@logtape/logtape");
|
|
4
4
|
//#region src/body.ts
|
|
5
5
|
/** Decoded JSON body limit: 16 MiB. @internal */
|
package/dist/tests/body.test.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_body = require("./body-
|
|
2
|
+
const require_body = require("./body-N4Kc5pwz.cjs");
|
|
3
3
|
let node_assert_strict = require("node:assert/strict");
|
|
4
4
|
let node_test = require("node:test");
|
|
5
5
|
let node_zlib = require("node:zlib");
|
package/dist/tests/body.test.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as readBoundedText, t as BodyTooLargeError } from "./body-
|
|
1
|
+
import { r as readBoundedText, t as BodyTooLargeError } from "./body-DhJNq-PQ.mjs";
|
|
2
2
|
import { deepStrictEqual, ok, rejects } from "node:assert/strict";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
3
|
-
const require_body = require("./body-
|
|
2
|
+
const require_request = require("./request-CFKB-Yqd.cjs");
|
|
3
|
+
const require_body = require("./body-N4Kc5pwz.cjs");
|
|
4
4
|
const require_link = require("./link-FguCydMA.cjs");
|
|
5
5
|
const require_url = require("./url-Qk1svKZU.cjs");
|
|
6
|
-
let node_test = require("node:test");
|
|
7
6
|
let node_zlib = require("node:zlib");
|
|
8
7
|
let _logtape_logtape = require("@logtape/logtape");
|
|
9
8
|
let node_assert = require("node:assert");
|
|
10
9
|
let node_dns_promises = require("node:dns/promises");
|
|
11
10
|
node_dns_promises = require_chunk.__toESM(node_dns_promises);
|
|
11
|
+
let _fedify_fixture = require("@fedify/fixture");
|
|
12
12
|
let node_http = require("node:http");
|
|
13
13
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
14
14
|
let jsonld_dist_jsonld_esm_js = require("jsonld/dist/jsonld.esm.js");
|
|
@@ -5906,7 +5906,7 @@ function getDocumentLoader({ allowPrivateAddress, skipPreloadedContexts, userAge
|
|
|
5906
5906
|
var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
5907
5907
|
//#endregion
|
|
5908
5908
|
//#region src/docloader.test.ts
|
|
5909
|
-
(0,
|
|
5909
|
+
(0, _fedify_fixture.test)("new FetchError()", () => {
|
|
5910
5910
|
const e = new require_request.FetchError("https://example.com/", "An error message.");
|
|
5911
5911
|
(0, node_assert.deepStrictEqual)(e.name, "FetchError");
|
|
5912
5912
|
(0, node_assert.deepStrictEqual)(e.url, new URL("https://example.com/"));
|
|
@@ -5915,7 +5915,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
5915
5915
|
(0, node_assert.deepStrictEqual)(e2.url, new URL("https://example.org/"));
|
|
5916
5916
|
(0, node_assert.deepStrictEqual)(e2.message, "https://example.org/");
|
|
5917
5917
|
});
|
|
5918
|
-
(0,
|
|
5918
|
+
(0, _fedify_fixture.test)("getDocumentLoader()", async (t) => {
|
|
5919
5919
|
const fetchDocumentLoader = getDocumentLoader();
|
|
5920
5920
|
esm_default.spyGlobal();
|
|
5921
5921
|
esm_default.get("https://example.com/object", { body: {
|
|
@@ -5924,7 +5924,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
5924
5924
|
name: "Fetched object",
|
|
5925
5925
|
type: "Object"
|
|
5926
5926
|
} });
|
|
5927
|
-
await t.
|
|
5927
|
+
await t.step("ok", async () => {
|
|
5928
5928
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/object"), {
|
|
5929
5929
|
contextUrl: null,
|
|
5930
5930
|
documentUrl: "https://example.com/object",
|
|
@@ -5967,7 +5967,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
5967
5967
|
Link: "<https://example.com/object>; rel=\"alternate\"; type=\"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"\""
|
|
5968
5968
|
}
|
|
5969
5969
|
});
|
|
5970
|
-
await t.
|
|
5970
|
+
await t.step("Link header", async () => {
|
|
5971
5971
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/link-ctx"), {
|
|
5972
5972
|
contextUrl: "https://www.w3.org/ns/activitystreams",
|
|
5973
5973
|
documentUrl: "https://example.com/link-ctx",
|
|
@@ -5988,7 +5988,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
5988
5988
|
}
|
|
5989
5989
|
});
|
|
5990
5990
|
});
|
|
5991
|
-
await t.
|
|
5991
|
+
await t.step("Link header relative url", async () => {
|
|
5992
5992
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/link-ctx"), {
|
|
5993
5993
|
contextUrl: "https://www.w3.org/ns/activitystreams",
|
|
5994
5994
|
documentUrl: "https://example.com/link-ctx",
|
|
@@ -6009,7 +6009,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6009
6009
|
}
|
|
6010
6010
|
});
|
|
6011
6011
|
});
|
|
6012
|
-
await t.
|
|
6012
|
+
await t.step("wrong Link header syntax", async () => {
|
|
6013
6013
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/obj-w-wrong-link"), {
|
|
6014
6014
|
contextUrl: null,
|
|
6015
6015
|
documentUrl: "https://example.com/obj-w-wrong-link",
|
|
@@ -6033,7 +6033,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6033
6033
|
</html>`,
|
|
6034
6034
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
6035
6035
|
});
|
|
6036
|
-
await t.
|
|
6036
|
+
await t.step("HTML <link>", async () => {
|
|
6037
6037
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/html-link"), {
|
|
6038
6038
|
contextUrl: null,
|
|
6039
6039
|
documentUrl: "https://example.com/object",
|
|
@@ -6057,7 +6057,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6057
6057
|
</html>`,
|
|
6058
6058
|
headers: { "Content-Type": "application/xhtml+xml; charset=utf-8" }
|
|
6059
6059
|
});
|
|
6060
|
-
await t.
|
|
6060
|
+
await t.step("XHTML <link>", async () => {
|
|
6061
6061
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/xhtml-link"), {
|
|
6062
6062
|
contextUrl: null,
|
|
6063
6063
|
documentUrl: "https://example.com/object",
|
|
@@ -6083,7 +6083,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6083
6083
|
</html>`,
|
|
6084
6084
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
6085
6085
|
});
|
|
6086
|
-
await t.
|
|
6086
|
+
await t.step("HTML <a>", async () => {
|
|
6087
6087
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/html-a"), {
|
|
6088
6088
|
contextUrl: null,
|
|
6089
6089
|
documentUrl: "https://example.com/object",
|
|
@@ -6104,7 +6104,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6104
6104
|
},
|
|
6105
6105
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
6106
6106
|
});
|
|
6107
|
-
await t.
|
|
6107
|
+
await t.step("Wrong Content-Type", async () => {
|
|
6108
6108
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader("https://example.com/wrong-content-type"), {
|
|
6109
6109
|
contextUrl: null,
|
|
6110
6110
|
documentUrl: "https://example.com/wrong-content-type",
|
|
@@ -6117,10 +6117,10 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6117
6117
|
});
|
|
6118
6118
|
});
|
|
6119
6119
|
esm_default.get("https://example.com/404", { status: 404 });
|
|
6120
|
-
await t.
|
|
6120
|
+
await t.step("not ok", async () => {
|
|
6121
6121
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/404"), require_request.FetchError, "HTTP 404: https://example.com/404");
|
|
6122
6122
|
});
|
|
6123
|
-
await t.
|
|
6123
|
+
await t.step("preloaded contexts", async () => {
|
|
6124
6124
|
for (const [url, document] of Object.entries(preloadedContexts)) (0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
|
|
6125
6125
|
contextUrl: null,
|
|
6126
6126
|
documentUrl: url,
|
|
@@ -6128,7 +6128,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6128
6128
|
});
|
|
6129
6129
|
});
|
|
6130
6130
|
esm_default.get("https://www.w3.org/ns/cid/v1", { status: 503 });
|
|
6131
|
-
await t.
|
|
6131
|
+
await t.step("preloaded CID v1 context", async () => {
|
|
6132
6132
|
const url = "https://www.w3.org/ns/cid/v1";
|
|
6133
6133
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
|
|
6134
6134
|
contextUrl: null,
|
|
@@ -6136,7 +6136,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6136
6136
|
document: cid_v1_default
|
|
6137
6137
|
});
|
|
6138
6138
|
});
|
|
6139
|
-
await t.
|
|
6139
|
+
await t.step("preloaded FEP-ef61 context", async () => {
|
|
6140
6140
|
const url = "https://w3id.org/fep/ef61";
|
|
6141
6141
|
(0, node_assert.ok)(url in preloadedContexts);
|
|
6142
6142
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
|
|
@@ -6153,7 +6153,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6153
6153
|
});
|
|
6154
6154
|
});
|
|
6155
6155
|
esm_default.get("https://w3id.org/fep/7aa9", { status: 502 });
|
|
6156
|
-
await t.
|
|
6156
|
+
await t.step("preloaded FEP-7aa9 context", async () => {
|
|
6157
6157
|
const url = "https://w3id.org/fep/7aa9";
|
|
6158
6158
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
|
|
6159
6159
|
contextUrl: null,
|
|
@@ -6184,7 +6184,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6184
6184
|
(0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
|
|
6185
6185
|
});
|
|
6186
6186
|
esm_default.get("https://w3id.org/fep/6757", { status: 502 });
|
|
6187
|
-
await t.
|
|
6187
|
+
await t.step("preloaded FEP-6757 context", async () => {
|
|
6188
6188
|
const url = "https://w3id.org/fep/6757";
|
|
6189
6189
|
(0, node_assert.deepStrictEqual)(await fetchDocumentLoader(url), {
|
|
6190
6190
|
contextUrl: null,
|
|
@@ -6213,7 +6213,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6213
6213
|
(0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
|
|
6214
6214
|
});
|
|
6215
6215
|
esm_default.get("https://w3id.org/fep/22cd", { status: 502 });
|
|
6216
|
-
await t.
|
|
6216
|
+
await t.step("preloaded FEP-22cd context", async () => {
|
|
6217
6217
|
const url = "https://w3id.org/fep/22cd";
|
|
6218
6218
|
const articleId = "https://example.com/articles/1";
|
|
6219
6219
|
const translatorId = "https://example.com/users/alice";
|
|
@@ -6291,7 +6291,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6291
6291
|
(0, node_assert.deepStrictEqual)(esm_default.callHistory.calls(url).length, 0);
|
|
6292
6292
|
(0, node_assert.deepStrictEqual)(esm_default.callHistory.calls("https://www.w3.org/ns/activitystreams").length, 0);
|
|
6293
6293
|
});
|
|
6294
|
-
await t.
|
|
6294
|
+
await t.step("deny non-HTTP/HTTPS", async () => {
|
|
6295
6295
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("ftp://localhost"), require_url.UrlError);
|
|
6296
6296
|
});
|
|
6297
6297
|
esm_default.get("https://example.com/localhost-redirect", {
|
|
@@ -6316,13 +6316,13 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6316
6316
|
name: "Fetched object",
|
|
6317
6317
|
type: "Object"
|
|
6318
6318
|
} });
|
|
6319
|
-
await t.
|
|
6319
|
+
await t.step("allowPrivateAddress: false", async () => {
|
|
6320
6320
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://localhost/object"), require_url.UrlError);
|
|
6321
6321
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/localhost-redirect"), require_url.UrlError);
|
|
6322
6322
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/localhost-link"), require_url.UrlError);
|
|
6323
6323
|
});
|
|
6324
6324
|
const fetchDocumentLoader2 = getDocumentLoader({ allowPrivateAddress: true });
|
|
6325
|
-
await t.
|
|
6325
|
+
await t.step("allowPrivateAddress: true", async () => {
|
|
6326
6326
|
const expected = {
|
|
6327
6327
|
contextUrl: null,
|
|
6328
6328
|
documentUrl: "https://localhost/object",
|
|
@@ -6345,7 +6345,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6345
6345
|
headers: { Location: `https://example.com/too-many-redirects/${Number(cl.url.split("/").at(-1)) + 1}` }
|
|
6346
6346
|
};
|
|
6347
6347
|
});
|
|
6348
|
-
await t.
|
|
6348
|
+
await t.step("too many redirects", async () => {
|
|
6349
6349
|
redirectAttempts = 0;
|
|
6350
6350
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/too-many-redirects/0"), require_request.FetchError, "Too many redirections");
|
|
6351
6351
|
(0, node_assert.deepStrictEqual)(redirectAttempts, 21);
|
|
@@ -6365,7 +6365,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6365
6365
|
headers: { Location: "https://example.com/redirect-loop-a" }
|
|
6366
6366
|
};
|
|
6367
6367
|
});
|
|
6368
|
-
await t.
|
|
6368
|
+
await t.step("redirect loop", async () => {
|
|
6369
6369
|
loopAttempts = 0;
|
|
6370
6370
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/redirect-loop-a"), require_request.FetchError, "Redirect loop detected");
|
|
6371
6371
|
(0, node_assert.deepStrictEqual)(loopAttempts, 2);
|
|
@@ -6378,7 +6378,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6378
6378
|
headers: { Location: "/redirect-loop-relative" }
|
|
6379
6379
|
};
|
|
6380
6380
|
});
|
|
6381
|
-
await t.
|
|
6381
|
+
await t.step("redirect loop with relative location", async () => {
|
|
6382
6382
|
relativeLoopAttempts = 0;
|
|
6383
6383
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/redirect-loop-relative"), require_request.FetchError, "Redirect loop detected");
|
|
6384
6384
|
(0, node_assert.deepStrictEqual)(relativeLoopAttempts, 1);
|
|
@@ -6388,7 +6388,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6388
6388
|
body: maliciousPayload,
|
|
6389
6389
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
6390
6390
|
});
|
|
6391
|
-
await t.
|
|
6391
|
+
await t.step("ReDoS resistance (CVE-2025-68475)", async () => {
|
|
6392
6392
|
const start = performance.now();
|
|
6393
6393
|
await (0, node_assert.rejects)(() => fetchDocumentLoader("https://example.com/redos"), SyntaxError);
|
|
6394
6394
|
const elapsed = performance.now() - start;
|
|
@@ -6396,7 +6396,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6396
6396
|
});
|
|
6397
6397
|
esm_default.hardReset();
|
|
6398
6398
|
});
|
|
6399
|
-
(0,
|
|
6399
|
+
(0, _fedify_fixture.test)("getDocumentLoader() bounds JSON, HTML, and alternate documents", async () => {
|
|
6400
6400
|
const url = "https://example.com/bounded";
|
|
6401
6401
|
esm_default.mockGlobal();
|
|
6402
6402
|
let oversized = true;
|
|
@@ -6426,7 +6426,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6426
6426
|
esm_default.hardReset();
|
|
6427
6427
|
}
|
|
6428
6428
|
});
|
|
6429
|
-
(0,
|
|
6429
|
+
(0, _fedify_fixture.test)("getDocumentLoader() rejects oversized gzip responses from fetch", async () => {
|
|
6430
6430
|
const document = { name: "a".repeat(16 * 1024 * 1024) };
|
|
6431
6431
|
const compressed = (0, node_zlib.gzipSync)(JSON.stringify(document));
|
|
6432
6432
|
(0, node_assert.ok)(compressed.byteLength < 64 * 1024);
|
|
@@ -6456,7 +6456,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6456
6456
|
});
|
|
6457
6457
|
}
|
|
6458
6458
|
});
|
|
6459
|
-
(0,
|
|
6459
|
+
(0, _fedify_fixture.test)("getRemoteDocument() bounds JSON by default", async () => {
|
|
6460
6460
|
let canceled = false;
|
|
6461
6461
|
let pulls = 0;
|
|
6462
6462
|
await (0, node_assert.rejects)(getRemoteDocument("https://example.com/document", new Response(new ReadableStream({
|
|
@@ -6473,17 +6473,14 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6473
6473
|
(0, node_assert.deepStrictEqual)(canceled, true);
|
|
6474
6474
|
(0, node_assert.deepStrictEqual)(pulls, 257);
|
|
6475
6475
|
});
|
|
6476
|
-
(0,
|
|
6477
|
-
if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted")
|
|
6478
|
-
t.skip("requires the net permission");
|
|
6479
|
-
return;
|
|
6480
|
-
}
|
|
6476
|
+
(0, _fedify_fixture.test)("getDocumentLoader() logs DNS failures as such", async (t) => {
|
|
6477
|
+
if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") throw new Error("This DNS test requires Deno network permission (--allow-net).");
|
|
6481
6478
|
const loader = getDocumentLoader();
|
|
6482
6479
|
for (const result of [
|
|
6483
6480
|
"throws",
|
|
6484
6481
|
"empty",
|
|
6485
6482
|
"private"
|
|
6486
|
-
]) await t.
|
|
6483
|
+
]) await t.step(result, async () => {
|
|
6487
6484
|
const originalLookup = node_dns_promises.default.lookup;
|
|
6488
6485
|
node_dns_promises.default.lookup = (() => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [{
|
|
6489
6486
|
address: "127.0.0.1",
|
|
@@ -6531,14 +6528,14 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6531
6528
|
}
|
|
6532
6529
|
});
|
|
6533
6530
|
});
|
|
6534
|
-
(0,
|
|
6531
|
+
(0, _fedify_fixture.test)("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
6535
6532
|
const base = "https://example.com/alternate-chain/";
|
|
6536
6533
|
const loader = getDocumentLoader({ allowPrivateAddress: true });
|
|
6537
6534
|
for (const mode of [
|
|
6538
6535
|
"header",
|
|
6539
6536
|
"html",
|
|
6540
6537
|
"mixed"
|
|
6541
|
-
]) for (const hops of [20, 21]) await t.
|
|
6538
|
+
]) for (const hops of [20, 21]) await t.step(`${mode}: ${hops} hops`, async () => {
|
|
6542
6539
|
esm_default.mockGlobal();
|
|
6543
6540
|
let requests = 0;
|
|
6544
6541
|
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
@@ -6567,7 +6564,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6567
6564
|
"alternate-redirect",
|
|
6568
6565
|
"redirect-alternate",
|
|
6569
6566
|
"redirect-intermediate"
|
|
6570
|
-
]) await t.
|
|
6567
|
+
]) await t.step(`${mode}: cycle`, async () => {
|
|
6571
6568
|
esm_default.mockGlobal();
|
|
6572
6569
|
let requests = 0;
|
|
6573
6570
|
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
@@ -6589,7 +6586,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6589
6586
|
esm_default.hardReset();
|
|
6590
6587
|
}
|
|
6591
6588
|
});
|
|
6592
|
-
await t.
|
|
6589
|
+
await t.step("alternate followed by 20 redirects shares the limit", async () => {
|
|
6593
6590
|
esm_default.mockGlobal();
|
|
6594
6591
|
let requests = 0;
|
|
6595
6592
|
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
@@ -6609,7 +6606,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6609
6606
|
esm_default.hardReset();
|
|
6610
6607
|
}
|
|
6611
6608
|
});
|
|
6612
|
-
await t.
|
|
6609
|
+
await t.step("relative alternate after redirect and isolated calls", async () => {
|
|
6613
6610
|
esm_default.mockGlobal();
|
|
6614
6611
|
esm_default.get(`${base}start`, Response.redirect(`${base}html`, 302));
|
|
6615
6612
|
esm_default.get(`${base}html`, alternate("./document", true));
|
|
@@ -6633,10 +6630,10 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6633
6630
|
} });
|
|
6634
6631
|
}
|
|
6635
6632
|
});
|
|
6636
|
-
(0,
|
|
6633
|
+
(0, _fedify_fixture.test)("getDocumentLoader() preserves cancellation across alternates", async (t) => {
|
|
6637
6634
|
const base = "https://example.com/alternate-abort/";
|
|
6638
6635
|
const loader = getDocumentLoader({ allowPrivateAddress: true });
|
|
6639
|
-
for (const html of [false, true]) await t.
|
|
6636
|
+
for (const html of [false, true]) await t.step(html ? "HTML" : "Link header", async () => {
|
|
6640
6637
|
esm_default.mockGlobal();
|
|
6641
6638
|
const controller = new AbortController();
|
|
6642
6639
|
let requests = 0;
|
|
@@ -6659,7 +6656,7 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
|
|
|
6659
6656
|
}
|
|
6660
6657
|
});
|
|
6661
6658
|
});
|
|
6662
|
-
(0,
|
|
6659
|
+
(0, _fedify_fixture.test)("getDocumentLoader() rejects cancellation before fetching", async () => {
|
|
6663
6660
|
esm_default.mockGlobal();
|
|
6664
6661
|
let requests = 0;
|
|
6665
6662
|
const url = "https://example.com/pre-aborted-alternate";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-
|
|
2
|
-
import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-
|
|
1
|
+
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-DDAEJJqw.mjs";
|
|
2
|
+
import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-DhJNq-PQ.mjs";
|
|
3
3
|
import { t as HttpHeaderLink } from "./link-NUUWCdnK.mjs";
|
|
4
4
|
import { o as validatePublicUrl, t as UrlError } from "./url-DzlMS01z.mjs";
|
|
5
|
-
import { test } from "node:test";
|
|
6
5
|
import { gzipSync } from "node:zlib";
|
|
7
6
|
import { configure, getLogger, reset } from "@logtape/logtape";
|
|
8
7
|
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
9
8
|
import dns from "node:dns/promises";
|
|
9
|
+
import { test } from "@fedify/fixture";
|
|
10
10
|
import { createServer } from "node:http";
|
|
11
11
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
12
12
|
import jsonld from "jsonld/dist/jsonld.esm.js";
|
|
@@ -5941,7 +5941,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
5941
5941
|
name: "Fetched object",
|
|
5942
5942
|
type: "Object"
|
|
5943
5943
|
} });
|
|
5944
|
-
await t.
|
|
5944
|
+
await t.step("ok", async () => {
|
|
5945
5945
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/object"), {
|
|
5946
5946
|
contextUrl: null,
|
|
5947
5947
|
documentUrl: "https://example.com/object",
|
|
@@ -5984,7 +5984,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
5984
5984
|
Link: "<https://example.com/object>; rel=\"alternate\"; type=\"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"\""
|
|
5985
5985
|
}
|
|
5986
5986
|
});
|
|
5987
|
-
await t.
|
|
5987
|
+
await t.step("Link header", async () => {
|
|
5988
5988
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/link-ctx"), {
|
|
5989
5989
|
contextUrl: "https://www.w3.org/ns/activitystreams",
|
|
5990
5990
|
documentUrl: "https://example.com/link-ctx",
|
|
@@ -6005,7 +6005,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6005
6005
|
}
|
|
6006
6006
|
});
|
|
6007
6007
|
});
|
|
6008
|
-
await t.
|
|
6008
|
+
await t.step("Link header relative url", async () => {
|
|
6009
6009
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/link-ctx"), {
|
|
6010
6010
|
contextUrl: "https://www.w3.org/ns/activitystreams",
|
|
6011
6011
|
documentUrl: "https://example.com/link-ctx",
|
|
@@ -6026,7 +6026,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6026
6026
|
}
|
|
6027
6027
|
});
|
|
6028
6028
|
});
|
|
6029
|
-
await t.
|
|
6029
|
+
await t.step("wrong Link header syntax", async () => {
|
|
6030
6030
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/obj-w-wrong-link"), {
|
|
6031
6031
|
contextUrl: null,
|
|
6032
6032
|
documentUrl: "https://example.com/obj-w-wrong-link",
|
|
@@ -6050,7 +6050,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6050
6050
|
</html>`,
|
|
6051
6051
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
6052
6052
|
});
|
|
6053
|
-
await t.
|
|
6053
|
+
await t.step("HTML <link>", async () => {
|
|
6054
6054
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/html-link"), {
|
|
6055
6055
|
contextUrl: null,
|
|
6056
6056
|
documentUrl: "https://example.com/object",
|
|
@@ -6074,7 +6074,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6074
6074
|
</html>`,
|
|
6075
6075
|
headers: { "Content-Type": "application/xhtml+xml; charset=utf-8" }
|
|
6076
6076
|
});
|
|
6077
|
-
await t.
|
|
6077
|
+
await t.step("XHTML <link>", async () => {
|
|
6078
6078
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/xhtml-link"), {
|
|
6079
6079
|
contextUrl: null,
|
|
6080
6080
|
documentUrl: "https://example.com/object",
|
|
@@ -6100,7 +6100,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6100
6100
|
</html>`,
|
|
6101
6101
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
6102
6102
|
});
|
|
6103
|
-
await t.
|
|
6103
|
+
await t.step("HTML <a>", async () => {
|
|
6104
6104
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/html-a"), {
|
|
6105
6105
|
contextUrl: null,
|
|
6106
6106
|
documentUrl: "https://example.com/object",
|
|
@@ -6121,7 +6121,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6121
6121
|
},
|
|
6122
6122
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
6123
6123
|
});
|
|
6124
|
-
await t.
|
|
6124
|
+
await t.step("Wrong Content-Type", async () => {
|
|
6125
6125
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/wrong-content-type"), {
|
|
6126
6126
|
contextUrl: null,
|
|
6127
6127
|
documentUrl: "https://example.com/wrong-content-type",
|
|
@@ -6134,10 +6134,10 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6134
6134
|
});
|
|
6135
6135
|
});
|
|
6136
6136
|
esm_default.get("https://example.com/404", { status: 404 });
|
|
6137
|
-
await t.
|
|
6137
|
+
await t.step("not ok", async () => {
|
|
6138
6138
|
await rejects(() => fetchDocumentLoader("https://example.com/404"), FetchError, "HTTP 404: https://example.com/404");
|
|
6139
6139
|
});
|
|
6140
|
-
await t.
|
|
6140
|
+
await t.step("preloaded contexts", async () => {
|
|
6141
6141
|
for (const [url, document] of Object.entries(preloadedContexts)) deepStrictEqual(await fetchDocumentLoader(url), {
|
|
6142
6142
|
contextUrl: null,
|
|
6143
6143
|
documentUrl: url,
|
|
@@ -6145,7 +6145,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6145
6145
|
});
|
|
6146
6146
|
});
|
|
6147
6147
|
esm_default.get("https://www.w3.org/ns/cid/v1", { status: 503 });
|
|
6148
|
-
await t.
|
|
6148
|
+
await t.step("preloaded CID v1 context", async () => {
|
|
6149
6149
|
const url = "https://www.w3.org/ns/cid/v1";
|
|
6150
6150
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
6151
6151
|
contextUrl: null,
|
|
@@ -6153,7 +6153,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6153
6153
|
document: cid_v1_default
|
|
6154
6154
|
});
|
|
6155
6155
|
});
|
|
6156
|
-
await t.
|
|
6156
|
+
await t.step("preloaded FEP-ef61 context", async () => {
|
|
6157
6157
|
const url = "https://w3id.org/fep/ef61";
|
|
6158
6158
|
ok(url in preloadedContexts);
|
|
6159
6159
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
@@ -6170,7 +6170,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6170
6170
|
});
|
|
6171
6171
|
});
|
|
6172
6172
|
esm_default.get("https://w3id.org/fep/7aa9", { status: 502 });
|
|
6173
|
-
await t.
|
|
6173
|
+
await t.step("preloaded FEP-7aa9 context", async () => {
|
|
6174
6174
|
const url = "https://w3id.org/fep/7aa9";
|
|
6175
6175
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
6176
6176
|
contextUrl: null,
|
|
@@ -6201,7 +6201,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6201
6201
|
deepStrictEqual(esm_default.callHistory.calls(url).length, 0);
|
|
6202
6202
|
});
|
|
6203
6203
|
esm_default.get("https://w3id.org/fep/6757", { status: 502 });
|
|
6204
|
-
await t.
|
|
6204
|
+
await t.step("preloaded FEP-6757 context", async () => {
|
|
6205
6205
|
const url = "https://w3id.org/fep/6757";
|
|
6206
6206
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
6207
6207
|
contextUrl: null,
|
|
@@ -6230,7 +6230,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6230
6230
|
deepStrictEqual(esm_default.callHistory.calls(url).length, 0);
|
|
6231
6231
|
});
|
|
6232
6232
|
esm_default.get("https://w3id.org/fep/22cd", { status: 502 });
|
|
6233
|
-
await t.
|
|
6233
|
+
await t.step("preloaded FEP-22cd context", async () => {
|
|
6234
6234
|
const url = "https://w3id.org/fep/22cd";
|
|
6235
6235
|
const articleId = "https://example.com/articles/1";
|
|
6236
6236
|
const translatorId = "https://example.com/users/alice";
|
|
@@ -6308,7 +6308,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6308
6308
|
deepStrictEqual(esm_default.callHistory.calls(url).length, 0);
|
|
6309
6309
|
deepStrictEqual(esm_default.callHistory.calls("https://www.w3.org/ns/activitystreams").length, 0);
|
|
6310
6310
|
});
|
|
6311
|
-
await t.
|
|
6311
|
+
await t.step("deny non-HTTP/HTTPS", async () => {
|
|
6312
6312
|
await rejects(() => fetchDocumentLoader("ftp://localhost"), UrlError);
|
|
6313
6313
|
});
|
|
6314
6314
|
esm_default.get("https://example.com/localhost-redirect", {
|
|
@@ -6333,13 +6333,13 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6333
6333
|
name: "Fetched object",
|
|
6334
6334
|
type: "Object"
|
|
6335
6335
|
} });
|
|
6336
|
-
await t.
|
|
6336
|
+
await t.step("allowPrivateAddress: false", async () => {
|
|
6337
6337
|
await rejects(() => fetchDocumentLoader("https://localhost/object"), UrlError);
|
|
6338
6338
|
await rejects(() => fetchDocumentLoader("https://example.com/localhost-redirect"), UrlError);
|
|
6339
6339
|
await rejects(() => fetchDocumentLoader("https://example.com/localhost-link"), UrlError);
|
|
6340
6340
|
});
|
|
6341
6341
|
const fetchDocumentLoader2 = getDocumentLoader({ allowPrivateAddress: true });
|
|
6342
|
-
await t.
|
|
6342
|
+
await t.step("allowPrivateAddress: true", async () => {
|
|
6343
6343
|
const expected = {
|
|
6344
6344
|
contextUrl: null,
|
|
6345
6345
|
documentUrl: "https://localhost/object",
|
|
@@ -6362,7 +6362,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6362
6362
|
headers: { Location: `https://example.com/too-many-redirects/${Number(cl.url.split("/").at(-1)) + 1}` }
|
|
6363
6363
|
};
|
|
6364
6364
|
});
|
|
6365
|
-
await t.
|
|
6365
|
+
await t.step("too many redirects", async () => {
|
|
6366
6366
|
redirectAttempts = 0;
|
|
6367
6367
|
await rejects(() => fetchDocumentLoader("https://example.com/too-many-redirects/0"), FetchError, "Too many redirections");
|
|
6368
6368
|
deepStrictEqual(redirectAttempts, 21);
|
|
@@ -6382,7 +6382,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6382
6382
|
headers: { Location: "https://example.com/redirect-loop-a" }
|
|
6383
6383
|
};
|
|
6384
6384
|
});
|
|
6385
|
-
await t.
|
|
6385
|
+
await t.step("redirect loop", async () => {
|
|
6386
6386
|
loopAttempts = 0;
|
|
6387
6387
|
await rejects(() => fetchDocumentLoader("https://example.com/redirect-loop-a"), FetchError, "Redirect loop detected");
|
|
6388
6388
|
deepStrictEqual(loopAttempts, 2);
|
|
@@ -6395,7 +6395,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6395
6395
|
headers: { Location: "/redirect-loop-relative" }
|
|
6396
6396
|
};
|
|
6397
6397
|
});
|
|
6398
|
-
await t.
|
|
6398
|
+
await t.step("redirect loop with relative location", async () => {
|
|
6399
6399
|
relativeLoopAttempts = 0;
|
|
6400
6400
|
await rejects(() => fetchDocumentLoader("https://example.com/redirect-loop-relative"), FetchError, "Redirect loop detected");
|
|
6401
6401
|
deepStrictEqual(relativeLoopAttempts, 1);
|
|
@@ -6405,7 +6405,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
6405
6405
|
body: maliciousPayload,
|
|
6406
6406
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
6407
6407
|
});
|
|
6408
|
-
await t.
|
|
6408
|
+
await t.step("ReDoS resistance (CVE-2025-68475)", async () => {
|
|
6409
6409
|
const start = performance.now();
|
|
6410
6410
|
await rejects(() => fetchDocumentLoader("https://example.com/redos"), SyntaxError);
|
|
6411
6411
|
const elapsed = performance.now() - start;
|
|
@@ -6491,16 +6491,13 @@ test("getRemoteDocument() bounds JSON by default", async () => {
|
|
|
6491
6491
|
deepStrictEqual(pulls, 257);
|
|
6492
6492
|
});
|
|
6493
6493
|
test("getDocumentLoader() logs DNS failures as such", async (t) => {
|
|
6494
|
-
if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted")
|
|
6495
|
-
t.skip("requires the net permission");
|
|
6496
|
-
return;
|
|
6497
|
-
}
|
|
6494
|
+
if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") throw new Error("This DNS test requires Deno network permission (--allow-net).");
|
|
6498
6495
|
const loader = getDocumentLoader();
|
|
6499
6496
|
for (const result of [
|
|
6500
6497
|
"throws",
|
|
6501
6498
|
"empty",
|
|
6502
6499
|
"private"
|
|
6503
|
-
]) await t.
|
|
6500
|
+
]) await t.step(result, async () => {
|
|
6504
6501
|
const originalLookup = dns.lookup;
|
|
6505
6502
|
dns.lookup = (() => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [{
|
|
6506
6503
|
address: "127.0.0.1",
|
|
@@ -6555,7 +6552,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
|
6555
6552
|
"header",
|
|
6556
6553
|
"html",
|
|
6557
6554
|
"mixed"
|
|
6558
|
-
]) for (const hops of [20, 21]) await t.
|
|
6555
|
+
]) for (const hops of [20, 21]) await t.step(`${mode}: ${hops} hops`, async () => {
|
|
6559
6556
|
esm_default.mockGlobal();
|
|
6560
6557
|
let requests = 0;
|
|
6561
6558
|
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
@@ -6584,7 +6581,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
|
6584
6581
|
"alternate-redirect",
|
|
6585
6582
|
"redirect-alternate",
|
|
6586
6583
|
"redirect-intermediate"
|
|
6587
|
-
]) await t.
|
|
6584
|
+
]) await t.step(`${mode}: cycle`, async () => {
|
|
6588
6585
|
esm_default.mockGlobal();
|
|
6589
6586
|
let requests = 0;
|
|
6590
6587
|
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
@@ -6606,7 +6603,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
|
6606
6603
|
esm_default.hardReset();
|
|
6607
6604
|
}
|
|
6608
6605
|
});
|
|
6609
|
-
await t.
|
|
6606
|
+
await t.step("alternate followed by 20 redirects shares the limit", async () => {
|
|
6610
6607
|
esm_default.mockGlobal();
|
|
6611
6608
|
let requests = 0;
|
|
6612
6609
|
esm_default.get(`begin:${base}`, ({ url }) => {
|
|
@@ -6626,7 +6623,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
|
6626
6623
|
esm_default.hardReset();
|
|
6627
6624
|
}
|
|
6628
6625
|
});
|
|
6629
|
-
await t.
|
|
6626
|
+
await t.step("relative alternate after redirect and isolated calls", async () => {
|
|
6630
6627
|
esm_default.mockGlobal();
|
|
6631
6628
|
esm_default.get(`${base}start`, Response.redirect(`${base}html`, 302));
|
|
6632
6629
|
esm_default.get(`${base}html`, alternate("./document", true));
|
|
@@ -6653,7 +6650,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
|
6653
6650
|
test("getDocumentLoader() preserves cancellation across alternates", async (t) => {
|
|
6654
6651
|
const base = "https://example.com/alternate-abort/";
|
|
6655
6652
|
const loader = getDocumentLoader({ allowPrivateAddress: true });
|
|
6656
|
-
for (const html of [false, true]) await t.
|
|
6653
|
+
for (const html of [false, true]) await t.step(html ? "HTML" : "Link header", async () => {
|
|
6657
6654
|
esm_default.mockGlobal();
|
|
6658
6655
|
const controller = new AbortController();
|
|
6659
6656
|
let requests = 0;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
require("../chunk-Do9eywBl.cjs");
|
|
2
2
|
const require_multibase = require("../multibase-CnmModw0.cjs");
|
|
3
|
-
let node_test = require("node:test");
|
|
4
3
|
let node_assert = require("node:assert");
|
|
4
|
+
let _fedify_fixture = require("@fedify/fixture");
|
|
5
5
|
//#region src/multibase/multibase.test.ts
|
|
6
|
-
(0,
|
|
6
|
+
(0, _fedify_fixture.test)("multibase.encode and decode", async (t) => {
|
|
7
7
|
const testCases = [
|
|
8
8
|
[
|
|
9
9
|
"base16",
|
|
@@ -316,22 +316,22 @@ let node_assert = require("node:assert");
|
|
|
316
316
|
"Uw7fDr8O_8J-lsMO3w6_Dv_CfmI7wn6W28J-krw=="
|
|
317
317
|
]
|
|
318
318
|
];
|
|
319
|
-
for (const [name, input, expectedOutput] of testCases) await t.
|
|
319
|
+
for (const [name, input, expectedOutput] of testCases) await t.step(`Encoding/Decoding ${name} with ${input}`, () => {
|
|
320
320
|
(0, node_assert.deepStrictEqual)(require_multibase.decodeText(require_multibase.encodeMultibase(name, require_multibase.encodeText(input))), expectedOutput, `Encoding ${name} failed`);
|
|
321
321
|
(0, node_assert.deepStrictEqual)(require_multibase.decodeMultibase(expectedOutput), require_multibase.encodeText(input), `Decoding ${name} failed`);
|
|
322
322
|
(0, node_assert.deepStrictEqual)(require_multibase.decodeMultibase(require_multibase.encodeText(expectedOutput)), require_multibase.encodeText(input), `Decoding buffer of ${name} failed`);
|
|
323
323
|
});
|
|
324
|
-
await t.
|
|
324
|
+
await t.step("should allow base32pad full alphabet", () => {
|
|
325
325
|
const decoded = require_multibase.decodeMultibase("ctimaq4ygg2iegci7");
|
|
326
326
|
(0, node_assert.deepStrictEqual)(require_multibase.decodeMultibase(require_multibase.encodeMultibase("c", decoded)), decoded);
|
|
327
327
|
});
|
|
328
328
|
});
|
|
329
|
-
(0,
|
|
330
|
-
await t.
|
|
329
|
+
(0, _fedify_fixture.test)("constants", async (t) => {
|
|
330
|
+
await t.step("constants indexed by name", () => {
|
|
331
331
|
const names$1 = require_multibase.names;
|
|
332
332
|
(0, node_assert.deepStrictEqual)(Object.keys(names$1).length, 23);
|
|
333
333
|
});
|
|
334
|
-
await t.
|
|
334
|
+
await t.step("constants indexed by code", () => {
|
|
335
335
|
const codes$1 = require_multibase.codes;
|
|
336
336
|
(0, node_assert.deepStrictEqual)(Object.keys(codes$1).length, 23);
|
|
337
337
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as decodeText, i as names, n as encodeMultibase, o as encodeText, r as codes, t as decodeMultibase } from "../multibase-DLBufb99.mjs";
|
|
2
|
-
import { test } from "node:test";
|
|
3
2
|
import { deepStrictEqual } from "node:assert";
|
|
3
|
+
import { test } from "@fedify/fixture";
|
|
4
4
|
//#region src/multibase/multibase.test.ts
|
|
5
5
|
test("multibase.encode and decode", async (t) => {
|
|
6
6
|
const testCases = [
|
|
@@ -315,21 +315,21 @@ test("multibase.encode and decode", async (t) => {
|
|
|
315
315
|
"Uw7fDr8O_8J-lsMO3w6_Dv_CfmI7wn6W28J-krw=="
|
|
316
316
|
]
|
|
317
317
|
];
|
|
318
|
-
for (const [name, input, expectedOutput] of testCases) await t.
|
|
318
|
+
for (const [name, input, expectedOutput] of testCases) await t.step(`Encoding/Decoding ${name} with ${input}`, () => {
|
|
319
319
|
deepStrictEqual(decodeText(encodeMultibase(name, encodeText(input))), expectedOutput, `Encoding ${name} failed`);
|
|
320
320
|
deepStrictEqual(decodeMultibase(expectedOutput), encodeText(input), `Decoding ${name} failed`);
|
|
321
321
|
deepStrictEqual(decodeMultibase(encodeText(expectedOutput)), encodeText(input), `Decoding buffer of ${name} failed`);
|
|
322
322
|
});
|
|
323
|
-
await t.
|
|
323
|
+
await t.step("should allow base32pad full alphabet", () => {
|
|
324
324
|
const decoded = decodeMultibase("ctimaq4ygg2iegci7");
|
|
325
325
|
deepStrictEqual(decodeMultibase(encodeMultibase("c", decoded)), decoded);
|
|
326
326
|
});
|
|
327
327
|
});
|
|
328
328
|
test("constants", async (t) => {
|
|
329
|
-
await t.
|
|
329
|
+
await t.step("constants indexed by name", () => {
|
|
330
330
|
deepStrictEqual(Object.keys(names).length, 23);
|
|
331
331
|
});
|
|
332
|
-
await t.
|
|
332
|
+
await t.step("constants indexed by code", () => {
|
|
333
333
|
deepStrictEqual(Object.keys(codes).length, 23);
|
|
334
334
|
});
|
|
335
335
|
});
|
|
@@ -3,7 +3,7 @@ let node_process = require("node:process");
|
|
|
3
3
|
node_process = require_chunk.__toESM(node_process);
|
|
4
4
|
//#region deno.json
|
|
5
5
|
var name = "@fedify/vocab-runtime";
|
|
6
|
-
var version = "2.0.31-dev.
|
|
6
|
+
var version = "2.0.31-dev.2310+e604de57";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/request.ts
|
|
9
9
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Do9eywBl.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-CFKB-Yqd.cjs");
|
|
3
3
|
let node_test = require("node:test");
|
|
4
4
|
let node_process = require("node:process");
|
|
5
5
|
node_process = require_chunk.__toESM(node_process);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as version, r as getUserAgent } from "./request-
|
|
1
|
+
import { o as version, r as getUserAgent } from "./request-DDAEJJqw.mjs";
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { deepStrictEqual } from "node:assert";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/vocab-runtime",
|
|
3
|
-
"version": "2.0.31-dev.
|
|
3
|
+
"version": "2.0.31-dev.2310+e604de57",
|
|
4
4
|
"homepage": "https://fedify.dev/",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"@types/node": "^24.2.1",
|
|
73
73
|
"fetch-mock": "^12.5.4",
|
|
74
74
|
"tsdown": "^0.21.6",
|
|
75
|
-
"typescript": "^6.0.0"
|
|
75
|
+
"typescript": "^6.0.0",
|
|
76
|
+
"@fedify/fixture": "2.0.0"
|
|
76
77
|
},
|
|
77
78
|
"dependencies": {
|
|
78
79
|
"@js-temporal/polyfill": "^0.5.1",
|
|
@@ -89,6 +90,8 @@
|
|
|
89
90
|
"build": "pnpm --filter @fedify/vocab-runtime... run build:self",
|
|
90
91
|
"prepublish": "pnpm build",
|
|
91
92
|
"pretest": "pnpm build",
|
|
92
|
-
"test": "cd dist/tests && node --test"
|
|
93
|
+
"test": "cd dist/tests && node --test",
|
|
94
|
+
"pretest:bun": "pnpm build",
|
|
95
|
+
"test:bun": "node scripts/test-bun.mjs"
|
|
93
96
|
}
|
|
94
97
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { access, glob } from "node:fs/promises";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
|
|
5
|
+
const sources = await Array.fromAsync(glob("src/**/*.test.ts"));
|
|
6
|
+
if (sources.length === 0) throw new Error("No vocab-runtime tests found.");
|
|
7
|
+
|
|
8
|
+
const tests = sources.map((source) =>
|
|
9
|
+
source.replaceAll("\\", "/").replace(/^src\//, "dist/tests/")
|
|
10
|
+
.replace(/\.ts$/, ".mjs")
|
|
11
|
+
).sort();
|
|
12
|
+
for (const test of tests) await access(test);
|
|
13
|
+
|
|
14
|
+
const result = spawnSync("bun", ["test", "--timeout", "60000", ...tests], {
|
|
15
|
+
stdio: "inherit",
|
|
16
|
+
});
|
|
17
|
+
if (result.error) throw result.error;
|
|
18
|
+
process.exitCode = result.status ?? 1;
|
package/src/docloader.test.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { configure, type LogRecord, reset } from "@logtape/logtape";
|
|
|
2
2
|
import fetchMock from "fetch-mock";
|
|
3
3
|
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
4
4
|
import dns from "node:dns/promises";
|
|
5
|
-
import { test } from "
|
|
5
|
+
import { test } from "@fedify/fixture";
|
|
6
6
|
import { createServer } from "node:http";
|
|
7
7
|
import { gzipSync } from "node:zlib";
|
|
8
8
|
import preloadedContexts from "./contexts.ts";
|
|
@@ -37,7 +37,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
37
37
|
},
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
await t.
|
|
40
|
+
await t.step("ok", async () => {
|
|
41
41
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/object"), {
|
|
42
42
|
contextUrl: null,
|
|
43
43
|
documentUrl: "https://example.com/object",
|
|
@@ -94,7 +94,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
94
94
|
},
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
await t.
|
|
97
|
+
await t.step("Link header", async () => {
|
|
98
98
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/link-ctx"), {
|
|
99
99
|
contextUrl: "https://www.w3.org/ns/activitystreams",
|
|
100
100
|
documentUrl: "https://example.com/link-ctx",
|
|
@@ -117,7 +117,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
117
117
|
});
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
await t.
|
|
120
|
+
await t.step("Link header relative url", async () => {
|
|
121
121
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/link-ctx"), {
|
|
122
122
|
contextUrl: "https://www.w3.org/ns/activitystreams",
|
|
123
123
|
documentUrl: "https://example.com/link-ctx",
|
|
@@ -143,7 +143,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
143
143
|
);
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
-
await t.
|
|
146
|
+
await t.step("wrong Link header syntax", async () => {
|
|
147
147
|
deepStrictEqual(
|
|
148
148
|
await fetchDocumentLoader("https://example.com/obj-w-wrong-link"),
|
|
149
149
|
{
|
|
@@ -172,7 +172,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
172
172
|
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
-
await t.
|
|
175
|
+
await t.step("HTML <link>", async () => {
|
|
176
176
|
deepStrictEqual(
|
|
177
177
|
await fetchDocumentLoader("https://example.com/html-link"),
|
|
178
178
|
{
|
|
@@ -201,7 +201,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
201
201
|
headers: { "Content-Type": "application/xhtml+xml; charset=utf-8" },
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
-
await t.
|
|
204
|
+
await t.step("XHTML <link>", async () => {
|
|
205
205
|
deepStrictEqual(
|
|
206
206
|
await fetchDocumentLoader("https://example.com/xhtml-link"),
|
|
207
207
|
{
|
|
@@ -232,7 +232,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
232
232
|
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
233
233
|
});
|
|
234
234
|
|
|
235
|
-
await t.
|
|
235
|
+
await t.step("HTML <a>", async () => {
|
|
236
236
|
deepStrictEqual(await fetchDocumentLoader("https://example.com/html-a"), {
|
|
237
237
|
contextUrl: null,
|
|
238
238
|
documentUrl: "https://example.com/object",
|
|
@@ -255,7 +255,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
255
255
|
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
-
await t.
|
|
258
|
+
await t.step("Wrong Content-Type", async () => {
|
|
259
259
|
deepStrictEqual(
|
|
260
260
|
await fetchDocumentLoader("https://example.com/wrong-content-type"),
|
|
261
261
|
{
|
|
@@ -273,7 +273,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
273
273
|
|
|
274
274
|
fetchMock.get("https://example.com/404", { status: 404 });
|
|
275
275
|
|
|
276
|
-
await t.
|
|
276
|
+
await t.step("not ok", async () => {
|
|
277
277
|
await rejects(
|
|
278
278
|
() => fetchDocumentLoader("https://example.com/404"),
|
|
279
279
|
FetchError,
|
|
@@ -281,7 +281,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
281
281
|
);
|
|
282
282
|
});
|
|
283
283
|
|
|
284
|
-
await t.
|
|
284
|
+
await t.step("preloaded contexts", async () => {
|
|
285
285
|
for (const [url, document] of Object.entries(preloadedContexts)) {
|
|
286
286
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
287
287
|
contextUrl: null,
|
|
@@ -297,7 +297,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
297
297
|
// See: https://www.w3.org/TR/cid-1.0/#json-ld-context
|
|
298
298
|
// https://github.com/fedify-dev/fedify/issues/932
|
|
299
299
|
fetchMock.get("https://www.w3.org/ns/cid/v1", { status: 503 });
|
|
300
|
-
await t.
|
|
300
|
+
await t.step("preloaded CID v1 context", async () => {
|
|
301
301
|
const url = "https://www.w3.org/ns/cid/v1";
|
|
302
302
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
303
303
|
contextUrl: null,
|
|
@@ -311,7 +311,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
311
311
|
// document referencing it fails before application handlers run. It has to
|
|
312
312
|
// be resolved from the built-in copy rather than over the network.
|
|
313
313
|
// See: https://github.com/fedify-dev/fedify/issues/982
|
|
314
|
-
await t.
|
|
314
|
+
await t.step("preloaded FEP-ef61 context", async () => {
|
|
315
315
|
const url = "https://w3id.org/fep/ef61";
|
|
316
316
|
ok(url in preloadedContexts);
|
|
317
317
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
@@ -334,7 +334,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
334
334
|
// A Codeberg Pages outage must not prevent loading the FEP-7aa9 context.
|
|
335
335
|
// See: https://github.com/fedify-dev/fedify/issues/1078
|
|
336
336
|
fetchMock.get("https://w3id.org/fep/7aa9", { status: 502 });
|
|
337
|
-
await t.
|
|
337
|
+
await t.step("preloaded FEP-7aa9 context", async () => {
|
|
338
338
|
const url = "https://w3id.org/fep/7aa9";
|
|
339
339
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
340
340
|
contextUrl: null,
|
|
@@ -371,7 +371,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
371
371
|
// A Codeberg Pages outage must not prevent loading or expanding FEP-6757.
|
|
372
372
|
// See: https://github.com/fedify-dev/fedify/issues/1211
|
|
373
373
|
fetchMock.get("https://w3id.org/fep/6757", { status: 502 });
|
|
374
|
-
await t.
|
|
374
|
+
await t.step("preloaded FEP-6757 context", async () => {
|
|
375
375
|
const url = "https://w3id.org/fep/6757";
|
|
376
376
|
deepStrictEqual(await fetchDocumentLoader(url), {
|
|
377
377
|
contextUrl: null,
|
|
@@ -412,7 +412,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
412
412
|
// A Codeberg Pages outage must not prevent expanding translation metadata.
|
|
413
413
|
// See: https://github.com/fedify-dev/fedify/issues/1214
|
|
414
414
|
fetchMock.get("https://w3id.org/fep/22cd", { status: 502 });
|
|
415
|
-
await t.
|
|
415
|
+
await t.step("preloaded FEP-22cd context", async () => {
|
|
416
416
|
const url = "https://w3id.org/fep/22cd";
|
|
417
417
|
const articleId = "https://example.com/articles/1";
|
|
418
418
|
const translatorId = "https://example.com/users/alice";
|
|
@@ -491,7 +491,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
491
491
|
);
|
|
492
492
|
});
|
|
493
493
|
|
|
494
|
-
await t.
|
|
494
|
+
await t.step("deny non-HTTP/HTTPS", async () => {
|
|
495
495
|
await rejects(
|
|
496
496
|
() => fetchDocumentLoader("ftp://localhost"),
|
|
497
497
|
UrlError,
|
|
@@ -525,7 +525,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
525
525
|
},
|
|
526
526
|
});
|
|
527
527
|
|
|
528
|
-
await t.
|
|
528
|
+
await t.step("allowPrivateAddress: false", async () => {
|
|
529
529
|
await rejects(
|
|
530
530
|
() => fetchDocumentLoader("https://localhost/object"),
|
|
531
531
|
UrlError,
|
|
@@ -542,7 +542,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
542
542
|
|
|
543
543
|
const fetchDocumentLoader2 = getDocumentLoader({ allowPrivateAddress: true });
|
|
544
544
|
|
|
545
|
-
await t.
|
|
545
|
+
await t.step("allowPrivateAddress: true", async () => {
|
|
546
546
|
const expected = {
|
|
547
547
|
contextUrl: null,
|
|
548
548
|
documentUrl: "https://localhost/object",
|
|
@@ -579,7 +579,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
579
579
|
};
|
|
580
580
|
});
|
|
581
581
|
|
|
582
|
-
await t.
|
|
582
|
+
await t.step("too many redirects", async () => {
|
|
583
583
|
redirectAttempts = 0;
|
|
584
584
|
await rejects(
|
|
585
585
|
() => fetchDocumentLoader("https://example.com/too-many-redirects/0"),
|
|
@@ -605,7 +605,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
605
605
|
};
|
|
606
606
|
});
|
|
607
607
|
|
|
608
|
-
await t.
|
|
608
|
+
await t.step("redirect loop", async () => {
|
|
609
609
|
loopAttempts = 0;
|
|
610
610
|
await rejects(
|
|
611
611
|
() => fetchDocumentLoader("https://example.com/redirect-loop-a"),
|
|
@@ -624,7 +624,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
624
624
|
};
|
|
625
625
|
});
|
|
626
626
|
|
|
627
|
-
await t.
|
|
627
|
+
await t.step("redirect loop with relative location", async () => {
|
|
628
628
|
relativeLoopAttempts = 0;
|
|
629
629
|
await rejects(
|
|
630
630
|
() => fetchDocumentLoader("https://example.com/redirect-loop-relative"),
|
|
@@ -644,7 +644,7 @@ test("getDocumentLoader()", async (t) => {
|
|
|
644
644
|
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
645
645
|
});
|
|
646
646
|
|
|
647
|
-
await t.
|
|
647
|
+
await t.step("ReDoS resistance (CVE-2025-68475)", async () => {
|
|
648
648
|
const start = performance.now();
|
|
649
649
|
// The malicious HTML will fail JSON parsing, but the important thing is
|
|
650
650
|
// that it should complete quickly (not hang due to ReDoS)
|
|
@@ -793,12 +793,13 @@ test("getDocumentLoader() logs DNS failures as such", async (t) => {
|
|
|
793
793
|
"Deno" in globalThis &&
|
|
794
794
|
(await Deno.permissions.query({ name: "net" })).state !== "granted"
|
|
795
795
|
) {
|
|
796
|
-
|
|
797
|
-
|
|
796
|
+
throw new Error(
|
|
797
|
+
"This DNS test requires Deno network permission (--allow-net).",
|
|
798
|
+
);
|
|
798
799
|
}
|
|
799
800
|
const loader = getDocumentLoader();
|
|
800
801
|
for (const result of ["throws", "empty", "private"] as const) {
|
|
801
|
-
await t.
|
|
802
|
+
await t.step(result, async () => {
|
|
802
803
|
// Stubbing works only because url.ts uses the default node:dns/promises
|
|
803
804
|
// import; see the FIXME there.
|
|
804
805
|
const originalLookup = dns.lookup;
|
|
@@ -854,7 +855,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
|
854
855
|
const loader = getDocumentLoader({ allowPrivateAddress: true });
|
|
855
856
|
for (const mode of ["header", "html", "mixed"] as const) {
|
|
856
857
|
for (const hops of [20, 21]) {
|
|
857
|
-
await t.
|
|
858
|
+
await t.step(`${mode}: ${hops} hops`, async () => {
|
|
858
859
|
fetchMock.mockGlobal();
|
|
859
860
|
let requests = 0;
|
|
860
861
|
fetchMock.get(`begin:${base}`, ({ url }) => {
|
|
@@ -899,7 +900,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
|
899
900
|
"redirect-intermediate",
|
|
900
901
|
]
|
|
901
902
|
) {
|
|
902
|
-
await t.
|
|
903
|
+
await t.step(`${mode}: cycle`, async () => {
|
|
903
904
|
fetchMock.mockGlobal();
|
|
904
905
|
let requests = 0;
|
|
905
906
|
fetchMock.get(`begin:${base}`, ({ url }) => {
|
|
@@ -933,49 +934,55 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
|
|
|
933
934
|
});
|
|
934
935
|
}
|
|
935
936
|
|
|
936
|
-
await t.
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
requests
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
await rejects(loader(`${base}0`), (error: unknown) => {
|
|
949
|
-
ok(error instanceof FetchError);
|
|
950
|
-
ok(error.message.includes("Too many redirections (21)"));
|
|
951
|
-
return true;
|
|
937
|
+
await t.step(
|
|
938
|
+
"alternate followed by 20 redirects shares the limit",
|
|
939
|
+
async () => {
|
|
940
|
+
fetchMock.mockGlobal();
|
|
941
|
+
let requests = 0;
|
|
942
|
+
fetchMock.get(`begin:${base}`, ({ url }) => {
|
|
943
|
+
requests++;
|
|
944
|
+
const index = Number(new URL(url).pathname.split("/").at(-1));
|
|
945
|
+
if (index === 21) return Response.json({ done: true });
|
|
946
|
+
return index === 0
|
|
947
|
+
? alternate(`${base}1`, false)
|
|
948
|
+
: Response.redirect(`${base}${index + 1}`, 302);
|
|
952
949
|
});
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
950
|
+
try {
|
|
951
|
+
await rejects(loader(`${base}0`), (error: unknown) => {
|
|
952
|
+
ok(error instanceof FetchError);
|
|
953
|
+
ok(error.message.includes("Too many redirections (21)"));
|
|
954
|
+
return true;
|
|
955
|
+
});
|
|
956
|
+
deepStrictEqual(requests, 21);
|
|
957
|
+
} finally {
|
|
958
|
+
fetchMock.hardReset();
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
);
|
|
958
962
|
|
|
959
|
-
await t.
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
963
|
+
await t.step(
|
|
964
|
+
"relative alternate after redirect and isolated calls",
|
|
965
|
+
async () => {
|
|
966
|
+
fetchMock.mockGlobal();
|
|
967
|
+
fetchMock.get(`${base}start`, Response.redirect(`${base}html`, 302));
|
|
968
|
+
fetchMock.get(`${base}html`, alternate("./document", true));
|
|
969
|
+
fetchMock.get(`${base}document`, Response.json({ done: true }));
|
|
970
|
+
try {
|
|
971
|
+
for (let i = 0; i < 2; i++) {
|
|
972
|
+
const results = await Promise.all([
|
|
973
|
+
loader(`${base}start`),
|
|
974
|
+
loader(`${base}start`),
|
|
975
|
+
]);
|
|
976
|
+
for (const result of results) {
|
|
977
|
+
deepStrictEqual(result.document, { done: true });
|
|
978
|
+
deepStrictEqual(result.documentUrl, `${base}document`);
|
|
979
|
+
}
|
|
973
980
|
}
|
|
981
|
+
} finally {
|
|
982
|
+
fetchMock.hardReset();
|
|
974
983
|
}
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
}
|
|
978
|
-
});
|
|
984
|
+
},
|
|
985
|
+
);
|
|
979
986
|
|
|
980
987
|
function alternate(next: string, html: boolean): Response {
|
|
981
988
|
return html
|
|
@@ -998,7 +1005,7 @@ test("getDocumentLoader() preserves cancellation across alternates", async (t) =
|
|
|
998
1005
|
const base = "https://example.com/alternate-abort/";
|
|
999
1006
|
const loader = getDocumentLoader({ allowPrivateAddress: true });
|
|
1000
1007
|
for (const html of [false, true]) {
|
|
1001
|
-
await t.
|
|
1008
|
+
await t.step(html ? "HTML" : "Link header", async () => {
|
|
1002
1009
|
fetchMock.mockGlobal();
|
|
1003
1010
|
const controller = new AbortController();
|
|
1004
1011
|
let requests = 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { deepStrictEqual } from "node:assert";
|
|
2
|
-
import { test } from "
|
|
2
|
+
import { test } from "@fedify/fixture";
|
|
3
3
|
import * as constants from "./constants.ts";
|
|
4
4
|
import { decodeMultibase, encodeMultibase } from "./mod.ts";
|
|
5
5
|
import type { BaseName } from "./types.d.ts";
|
|
@@ -76,7 +76,7 @@ test("multibase.encode and decode", async (t) => {
|
|
|
76
76
|
];
|
|
77
77
|
|
|
78
78
|
for (const [name, input, expectedOutput] of testCases) {
|
|
79
|
-
await t.
|
|
79
|
+
await t.step(`Encoding/Decoding ${name} with ${input}`, () => {
|
|
80
80
|
const encoded = encodeMultibase(name, encodeText(input));
|
|
81
81
|
deepStrictEqual(
|
|
82
82
|
decodeText(encoded),
|
|
@@ -96,7 +96,7 @@ test("multibase.encode and decode", async (t) => {
|
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
await t.
|
|
99
|
+
await t.step("should allow base32pad full alphabet", () => {
|
|
100
100
|
const encodedStr = "ctimaq4ygg2iegci7";
|
|
101
101
|
const decoded = decodeMultibase(encodedStr);
|
|
102
102
|
const encoded = encodeMultibase("c", decoded);
|
|
@@ -105,12 +105,12 @@ test("multibase.encode and decode", async (t) => {
|
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
test("constants", async (t) => {
|
|
108
|
-
await t.
|
|
108
|
+
await t.step("constants indexed by name", () => {
|
|
109
109
|
const names = constants.names;
|
|
110
110
|
deepStrictEqual(Object.keys(names).length, 23);
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
await t.
|
|
113
|
+
await t.step("constants indexed by code", () => {
|
|
114
114
|
const codes = constants.codes;
|
|
115
115
|
deepStrictEqual(Object.keys(codes).length, 23);
|
|
116
116
|
});
|