@fedify/vocab-runtime 2.0.0-dev.1875 → 2.0.0-dev.196
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/LICENSE +1 -1
- package/README.md +2 -1
- package/deno.json +7 -1
- package/dist/chunk-DWy1uDak.cjs +39 -0
- package/dist/docloader.test.cjs +5851 -0
- package/dist/docloader.test.d.cts +1 -0
- package/dist/docloader.test.d.ts +1 -0
- package/dist/docloader.test.js +5877 -0
- package/dist/key.test.cjs +272 -0
- package/dist/key.test.d.cts +1 -0
- package/dist/key.test.d.ts +1 -0
- package/dist/key.test.js +271 -0
- package/dist/langstr.test.cjs +51 -0
- package/dist/langstr.test.d.cts +1 -0
- package/dist/langstr.test.d.ts +1 -0
- package/dist/langstr.test.js +50 -0
- package/dist/link-CdFPEo9O.cjs +189 -0
- package/dist/link-Ck2yj4dH.js +183 -0
- package/dist/link.test.cjs +56 -0
- package/dist/link.test.d.cts +1 -0
- package/dist/link.test.d.ts +1 -0
- package/dist/link.test.js +55 -0
- package/dist/mod.cjs +102 -63
- package/dist/mod.js +102 -63
- package/dist/multibase/multibase.test.cjs +346 -0
- package/dist/multibase/multibase.test.d.cts +1 -0
- package/dist/multibase/multibase.test.d.ts +1 -0
- package/dist/multibase/multibase.test.js +345 -0
- package/dist/multibase-BFbBiaPE.cjs +347 -0
- package/dist/multibase-DStmqni9.js +311 -0
- package/dist/request-BPQb2VYj.cjs +138 -0
- package/dist/request-SuYiIZUu.js +108 -0
- package/dist/request.test.cjs +44 -0
- package/dist/request.test.d.cts +1 -0
- package/dist/request.test.d.ts +1 -0
- package/dist/request.test.js +43 -0
- package/dist/url-C5Vs9nYh.cjs +93 -0
- package/dist/url-fW_DHbih.js +63 -0
- package/dist/url.test.cjs +37 -0
- package/dist/url.test.d.cts +1 -0
- package/dist/url.test.d.ts +1 -0
- package/dist/url.test.js +36 -0
- package/package.json +4 -3
- package/src/docloader.test.ts +29 -1
- package/src/docloader.ts +101 -45
- package/tsdown.config.ts +18 -7
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-DWy1uDak.cjs');
|
|
2
|
+
const node_process = require_chunk.__toESM(require("node:process"));
|
|
3
|
+
|
|
4
|
+
//#region deno.json
|
|
5
|
+
var name = "@fedify/vocab-runtime";
|
|
6
|
+
var version = "2.0.0-dev.196+c3cfc0a9";
|
|
7
|
+
var license = "MIT";
|
|
8
|
+
var exports$1 = { ".": "./src/mod.ts" };
|
|
9
|
+
var description = "Runtime library for @fedify/vocab";
|
|
10
|
+
var author = {
|
|
11
|
+
"name": "Hong Minhee",
|
|
12
|
+
"email": "hong@minhee.org",
|
|
13
|
+
"url": "https://hongminhee.org/"
|
|
14
|
+
};
|
|
15
|
+
var imports = {
|
|
16
|
+
"@multiformats/base-x": "npm:@multiformats/base-x@^4.0.1",
|
|
17
|
+
"asn1js": "npm:asn1js@^3.0.6",
|
|
18
|
+
"byte-encodings": "npm:byte-encodings@^1.0.11",
|
|
19
|
+
"fetch-mock": "npm:fetch-mock@^12.5.4",
|
|
20
|
+
"multicodec": "npm:multicodec@^3.2.1",
|
|
21
|
+
"pkijs": "npm:pkijs@^3.2.5"
|
|
22
|
+
};
|
|
23
|
+
var exclude = ["dist", "node_modules"];
|
|
24
|
+
var publish = { "exclude": ["**/*.test.ts"] };
|
|
25
|
+
var tasks = {
|
|
26
|
+
"check": "deno fmt --check && deno lint && deno check src/*.ts",
|
|
27
|
+
"test": "deno test"
|
|
28
|
+
};
|
|
29
|
+
var deno_default = {
|
|
30
|
+
name,
|
|
31
|
+
version,
|
|
32
|
+
license,
|
|
33
|
+
exports: exports$1,
|
|
34
|
+
description,
|
|
35
|
+
author,
|
|
36
|
+
imports,
|
|
37
|
+
exclude,
|
|
38
|
+
publish,
|
|
39
|
+
tasks
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/request.ts
|
|
44
|
+
/**
|
|
45
|
+
* Error thrown when fetching a JSON-LD document failed.
|
|
46
|
+
*/
|
|
47
|
+
var FetchError = class extends Error {
|
|
48
|
+
/**
|
|
49
|
+
* The URL that failed to fetch.
|
|
50
|
+
*/
|
|
51
|
+
url;
|
|
52
|
+
/**
|
|
53
|
+
* Constructs a new `FetchError`.
|
|
54
|
+
*
|
|
55
|
+
* @param url The URL that failed to fetch.
|
|
56
|
+
* @param message Error message.
|
|
57
|
+
*/
|
|
58
|
+
constructor(url, message) {
|
|
59
|
+
super(message == null ? url.toString() : `${url}: ${message}`);
|
|
60
|
+
this.name = "FetchError";
|
|
61
|
+
this.url = typeof url === "string" ? new URL(url) : url;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Creates a request for the given URL.
|
|
66
|
+
* @param url The URL to create the request for.
|
|
67
|
+
* @param options The options for the request.
|
|
68
|
+
* @returns The created request.
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
function createActivityPubRequest(url, options = {}) {
|
|
72
|
+
return new Request(url, {
|
|
73
|
+
headers: {
|
|
74
|
+
Accept: "application/activity+json, application/ld+json",
|
|
75
|
+
"User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
|
|
76
|
+
},
|
|
77
|
+
redirect: "manual"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Gets the user agent string for the given application and URL.
|
|
82
|
+
* @param options The options for making the user agent string.
|
|
83
|
+
* @returns The user agent string.
|
|
84
|
+
* @since 1.3.0
|
|
85
|
+
*/
|
|
86
|
+
function getUserAgent({ software, url } = {}) {
|
|
87
|
+
const fedify = `Fedify/${deno_default.version}`;
|
|
88
|
+
const runtime = globalThis.Deno?.version?.deno != null ? `Deno/${Deno.version.deno}` : globalThis.process?.versions?.bun != null ? `Bun/${node_process.default.versions.bun}` : "navigator" in globalThis && navigator.userAgent === "Cloudflare-Workers" ? navigator.userAgent : globalThis.process?.versions?.node != null ? `Node.js/${node_process.default.versions.node}` : null;
|
|
89
|
+
const userAgent = software == null ? [fedify] : [software, fedify];
|
|
90
|
+
if (runtime != null) userAgent.push(runtime);
|
|
91
|
+
if (url != null) userAgent.push(`+${url.toString()}`);
|
|
92
|
+
const first = userAgent.shift();
|
|
93
|
+
return `${first} (${userAgent.join("; ")})`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Logs the request.
|
|
97
|
+
* @param request The request to log.
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
function logRequest(logger, request) {
|
|
101
|
+
logger.debug("Fetching document: {method} {url} {headers}", {
|
|
102
|
+
method: request.method,
|
|
103
|
+
url: request.url,
|
|
104
|
+
headers: Object.fromEntries(request.headers.entries())
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
Object.defineProperty(exports, 'FetchError', {
|
|
110
|
+
enumerable: true,
|
|
111
|
+
get: function () {
|
|
112
|
+
return FetchError;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
Object.defineProperty(exports, 'createActivityPubRequest', {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
get: function () {
|
|
118
|
+
return createActivityPubRequest;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
Object.defineProperty(exports, 'deno_default', {
|
|
122
|
+
enumerable: true,
|
|
123
|
+
get: function () {
|
|
124
|
+
return deno_default;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
Object.defineProperty(exports, 'getUserAgent', {
|
|
128
|
+
enumerable: true,
|
|
129
|
+
get: function () {
|
|
130
|
+
return getUserAgent;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
Object.defineProperty(exports, 'logRequest', {
|
|
134
|
+
enumerable: true,
|
|
135
|
+
get: function () {
|
|
136
|
+
return logRequest;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
|
|
3
|
+
//#region deno.json
|
|
4
|
+
var name = "@fedify/vocab-runtime";
|
|
5
|
+
var version = "2.0.0-dev.196+c3cfc0a9";
|
|
6
|
+
var license = "MIT";
|
|
7
|
+
var exports = { ".": "./src/mod.ts" };
|
|
8
|
+
var description = "Runtime library for @fedify/vocab";
|
|
9
|
+
var author = {
|
|
10
|
+
"name": "Hong Minhee",
|
|
11
|
+
"email": "hong@minhee.org",
|
|
12
|
+
"url": "https://hongminhee.org/"
|
|
13
|
+
};
|
|
14
|
+
var imports = {
|
|
15
|
+
"@multiformats/base-x": "npm:@multiformats/base-x@^4.0.1",
|
|
16
|
+
"asn1js": "npm:asn1js@^3.0.6",
|
|
17
|
+
"byte-encodings": "npm:byte-encodings@^1.0.11",
|
|
18
|
+
"fetch-mock": "npm:fetch-mock@^12.5.4",
|
|
19
|
+
"multicodec": "npm:multicodec@^3.2.1",
|
|
20
|
+
"pkijs": "npm:pkijs@^3.2.5"
|
|
21
|
+
};
|
|
22
|
+
var exclude = ["dist", "node_modules"];
|
|
23
|
+
var publish = { "exclude": ["**/*.test.ts"] };
|
|
24
|
+
var tasks = {
|
|
25
|
+
"check": "deno fmt --check && deno lint && deno check src/*.ts",
|
|
26
|
+
"test": "deno test"
|
|
27
|
+
};
|
|
28
|
+
var deno_default = {
|
|
29
|
+
name,
|
|
30
|
+
version,
|
|
31
|
+
license,
|
|
32
|
+
exports,
|
|
33
|
+
description,
|
|
34
|
+
author,
|
|
35
|
+
imports,
|
|
36
|
+
exclude,
|
|
37
|
+
publish,
|
|
38
|
+
tasks
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/request.ts
|
|
43
|
+
/**
|
|
44
|
+
* Error thrown when fetching a JSON-LD document failed.
|
|
45
|
+
*/
|
|
46
|
+
var FetchError = class extends Error {
|
|
47
|
+
/**
|
|
48
|
+
* The URL that failed to fetch.
|
|
49
|
+
*/
|
|
50
|
+
url;
|
|
51
|
+
/**
|
|
52
|
+
* Constructs a new `FetchError`.
|
|
53
|
+
*
|
|
54
|
+
* @param url The URL that failed to fetch.
|
|
55
|
+
* @param message Error message.
|
|
56
|
+
*/
|
|
57
|
+
constructor(url, message) {
|
|
58
|
+
super(message == null ? url.toString() : `${url}: ${message}`);
|
|
59
|
+
this.name = "FetchError";
|
|
60
|
+
this.url = typeof url === "string" ? new URL(url) : url;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Creates a request for the given URL.
|
|
65
|
+
* @param url The URL to create the request for.
|
|
66
|
+
* @param options The options for the request.
|
|
67
|
+
* @returns The created request.
|
|
68
|
+
* @internal
|
|
69
|
+
*/
|
|
70
|
+
function createActivityPubRequest(url, options = {}) {
|
|
71
|
+
return new Request(url, {
|
|
72
|
+
headers: {
|
|
73
|
+
Accept: "application/activity+json, application/ld+json",
|
|
74
|
+
"User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
|
|
75
|
+
},
|
|
76
|
+
redirect: "manual"
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Gets the user agent string for the given application and URL.
|
|
81
|
+
* @param options The options for making the user agent string.
|
|
82
|
+
* @returns The user agent string.
|
|
83
|
+
* @since 1.3.0
|
|
84
|
+
*/
|
|
85
|
+
function getUserAgent({ software, url } = {}) {
|
|
86
|
+
const fedify = `Fedify/${deno_default.version}`;
|
|
87
|
+
const runtime = globalThis.Deno?.version?.deno != null ? `Deno/${Deno.version.deno}` : globalThis.process?.versions?.bun != null ? `Bun/${process.versions.bun}` : "navigator" in globalThis && navigator.userAgent === "Cloudflare-Workers" ? navigator.userAgent : globalThis.process?.versions?.node != null ? `Node.js/${process.versions.node}` : null;
|
|
88
|
+
const userAgent = software == null ? [fedify] : [software, fedify];
|
|
89
|
+
if (runtime != null) userAgent.push(runtime);
|
|
90
|
+
if (url != null) userAgent.push(`+${url.toString()}`);
|
|
91
|
+
const first = userAgent.shift();
|
|
92
|
+
return `${first} (${userAgent.join("; ")})`;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Logs the request.
|
|
96
|
+
* @param request The request to log.
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
function logRequest(logger, request) {
|
|
100
|
+
logger.debug("Fetching document: {method} {url} {headers}", {
|
|
101
|
+
method: request.method,
|
|
102
|
+
url: request.url,
|
|
103
|
+
headers: Object.fromEntries(request.headers.entries())
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
export { FetchError, createActivityPubRequest, deno_default, getUserAgent, logRequest };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-DWy1uDak.cjs');
|
|
2
|
+
const require_request = require('./request-BPQb2VYj.cjs');
|
|
3
|
+
const node_assert = require_chunk.__toESM(require("node:assert"));
|
|
4
|
+
const node_test = require_chunk.__toESM(require("node:test"));
|
|
5
|
+
const node_process = require_chunk.__toESM(require("node:process"));
|
|
6
|
+
|
|
7
|
+
//#region src/request.test.ts
|
|
8
|
+
(0, node_test.test)("getUserAgent()", () => {
|
|
9
|
+
if ("Deno" in globalThis) {
|
|
10
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent(), `Fedify/${require_request.deno_default.version} (Deno/${Deno.version.deno})`);
|
|
11
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Deno/${Deno.version.deno})`);
|
|
12
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({ url: "https://example.com/" }), `Fedify/${require_request.deno_default.version} (Deno/${Deno.version.deno}; +https://example.com/)`);
|
|
13
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({
|
|
14
|
+
software: "MyApp/1.0.0",
|
|
15
|
+
url: new URL("https://example.com/")
|
|
16
|
+
}), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Deno/${Deno.version.deno}; +https://example.com/)`);
|
|
17
|
+
} else if ("Bun" in globalThis) {
|
|
18
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent(), `Fedify/${require_request.deno_default.version} (Bun/${Bun.version})`);
|
|
19
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Bun/${Bun.version})`);
|
|
20
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({ url: "https://example.com/" }), `Fedify/${require_request.deno_default.version} (Bun/${Bun.version}; +https://example.com/)`);
|
|
21
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({
|
|
22
|
+
software: "MyApp/1.0.0",
|
|
23
|
+
url: new URL("https://example.com/")
|
|
24
|
+
}), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Bun/${Bun.version}; +https://example.com/)`);
|
|
25
|
+
} else if (navigator.userAgent === "Cloudflare-Workers") {
|
|
26
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent(), `Fedify/${require_request.deno_default.version} (Cloudflare-Workers)`);
|
|
27
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Cloudflare-Workers)`);
|
|
28
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({ url: "https://example.com/" }), `Fedify/${require_request.deno_default.version} (Cloudflare-Workers; +https://example.com/)`);
|
|
29
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({
|
|
30
|
+
software: "MyApp/1.0.0",
|
|
31
|
+
url: new URL("https://example.com/")
|
|
32
|
+
}), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Cloudflare-Workers; +https://example.com/)`);
|
|
33
|
+
} else {
|
|
34
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent(), `Fedify/${require_request.deno_default.version} (Node.js/${node_process.default.versions.node})`);
|
|
35
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({ software: "MyApp/1.0.0" }), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Node.js/${node_process.default.versions.node})`);
|
|
36
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({ url: "https://example.com/" }), `Fedify/${require_request.deno_default.version} (Node.js/${node_process.default.versions.node}; +https://example.com/)`);
|
|
37
|
+
(0, node_assert.deepStrictEqual)(require_request.getUserAgent({
|
|
38
|
+
software: "MyApp/1.0.0",
|
|
39
|
+
url: new URL("https://example.com/")
|
|
40
|
+
}), `MyApp/1.0.0 (Fedify/${require_request.deno_default.version}; Node.js/${node_process.default.versions.node}; +https://example.com/)`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { deno_default, getUserAgent } from "./request-SuYiIZUu.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
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-DWy1uDak.cjs');
|
|
2
|
+
const node_dns_promises = require_chunk.__toESM(require("node:dns/promises"));
|
|
3
|
+
const node_net = require_chunk.__toESM(require("node:net"));
|
|
4
|
+
|
|
5
|
+
//#region src/url.ts
|
|
6
|
+
var UrlError = class extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "UrlError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Validates a URL to prevent SSRF attacks.
|
|
14
|
+
*/
|
|
15
|
+
async function validatePublicUrl(url) {
|
|
16
|
+
const parsed = new URL(url);
|
|
17
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
|
|
18
|
+
let hostname = parsed.hostname;
|
|
19
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.substring(1, hostname.length - 2);
|
|
20
|
+
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
21
|
+
if ("Deno" in globalThis && !(0, node_net.isIP)(hostname)) {
|
|
22
|
+
const netPermission = await Deno.permissions.query({ name: "net" });
|
|
23
|
+
if (netPermission.state !== "granted") return;
|
|
24
|
+
}
|
|
25
|
+
if ("Bun" in globalThis) {
|
|
26
|
+
if (hostname === "example.com" || hostname.endsWith(".example.com")) return;
|
|
27
|
+
else if (hostname === "fedify-test.internal") throw new UrlError("Invalid or private address: fedify-test.internal");
|
|
28
|
+
}
|
|
29
|
+
let addresses;
|
|
30
|
+
try {
|
|
31
|
+
addresses = await (0, node_dns_promises.lookup)(hostname, { all: true });
|
|
32
|
+
} catch {
|
|
33
|
+
addresses = [];
|
|
34
|
+
}
|
|
35
|
+
for (const { address, family } of addresses) if (family === 4 && !isValidPublicIPv4Address(address) || family === 6 && !isValidPublicIPv6Address(address) || family < 4 || family === 5 || family > 6) throw new UrlError(`Invalid or private address: ${address}`);
|
|
36
|
+
}
|
|
37
|
+
function isValidPublicIPv4Address(address) {
|
|
38
|
+
const parts = address.split(".");
|
|
39
|
+
const first = parseInt(parts[0]);
|
|
40
|
+
if (first === 0 || first === 10 || first === 127) return false;
|
|
41
|
+
const second = parseInt(parts[1]);
|
|
42
|
+
if (first === 169 && second === 254) return false;
|
|
43
|
+
if (first === 172 && second >= 16 && second <= 31) return false;
|
|
44
|
+
if (first === 192 && second === 168) return false;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function isValidPublicIPv6Address(address) {
|
|
48
|
+
address = expandIPv6Address(address);
|
|
49
|
+
if (address.at(4) !== ":") return false;
|
|
50
|
+
const firstWord = parseInt(address.substring(0, 4), 16);
|
|
51
|
+
return !(firstWord >= 64512 && firstWord <= 65023 || firstWord >= 65152 && firstWord <= 65215 || firstWord === 0 || firstWord >= 65280);
|
|
52
|
+
}
|
|
53
|
+
function expandIPv6Address(address) {
|
|
54
|
+
address = address.toLowerCase();
|
|
55
|
+
if (address === "::") return "0000:0000:0000:0000:0000:0000:0000:0000";
|
|
56
|
+
if (address.startsWith("::")) address = "0000" + address;
|
|
57
|
+
if (address.endsWith("::")) address = address + "0000";
|
|
58
|
+
address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
|
|
59
|
+
const parts = address.split(":");
|
|
60
|
+
return parts.map((part) => part.padStart(4, "0")).join(":");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
Object.defineProperty(exports, 'UrlError', {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
get: function () {
|
|
67
|
+
return UrlError;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(exports, 'expandIPv6Address', {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
get: function () {
|
|
73
|
+
return expandIPv6Address;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(exports, 'isValidPublicIPv4Address', {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
get: function () {
|
|
79
|
+
return isValidPublicIPv4Address;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
Object.defineProperty(exports, 'isValidPublicIPv6Address', {
|
|
83
|
+
enumerable: true,
|
|
84
|
+
get: function () {
|
|
85
|
+
return isValidPublicIPv6Address;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
Object.defineProperty(exports, 'validatePublicUrl', {
|
|
89
|
+
enumerable: true,
|
|
90
|
+
get: function () {
|
|
91
|
+
return validatePublicUrl;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { lookup } from "node:dns/promises";
|
|
2
|
+
import { isIP } from "node:net";
|
|
3
|
+
|
|
4
|
+
//#region src/url.ts
|
|
5
|
+
var UrlError = class extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "UrlError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Validates a URL to prevent SSRF attacks.
|
|
13
|
+
*/
|
|
14
|
+
async function validatePublicUrl(url) {
|
|
15
|
+
const parsed = new URL(url);
|
|
16
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
|
|
17
|
+
let hostname = parsed.hostname;
|
|
18
|
+
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.substring(1, hostname.length - 2);
|
|
19
|
+
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
20
|
+
if ("Deno" in globalThis && !isIP(hostname)) {
|
|
21
|
+
const netPermission = await Deno.permissions.query({ name: "net" });
|
|
22
|
+
if (netPermission.state !== "granted") return;
|
|
23
|
+
}
|
|
24
|
+
if ("Bun" in globalThis) {
|
|
25
|
+
if (hostname === "example.com" || hostname.endsWith(".example.com")) return;
|
|
26
|
+
else if (hostname === "fedify-test.internal") throw new UrlError("Invalid or private address: fedify-test.internal");
|
|
27
|
+
}
|
|
28
|
+
let addresses;
|
|
29
|
+
try {
|
|
30
|
+
addresses = await lookup(hostname, { all: true });
|
|
31
|
+
} catch {
|
|
32
|
+
addresses = [];
|
|
33
|
+
}
|
|
34
|
+
for (const { address, family } of addresses) if (family === 4 && !isValidPublicIPv4Address(address) || family === 6 && !isValidPublicIPv6Address(address) || family < 4 || family === 5 || family > 6) throw new UrlError(`Invalid or private address: ${address}`);
|
|
35
|
+
}
|
|
36
|
+
function isValidPublicIPv4Address(address) {
|
|
37
|
+
const parts = address.split(".");
|
|
38
|
+
const first = parseInt(parts[0]);
|
|
39
|
+
if (first === 0 || first === 10 || first === 127) return false;
|
|
40
|
+
const second = parseInt(parts[1]);
|
|
41
|
+
if (first === 169 && second === 254) return false;
|
|
42
|
+
if (first === 172 && second >= 16 && second <= 31) return false;
|
|
43
|
+
if (first === 192 && second === 168) return false;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
function isValidPublicIPv6Address(address) {
|
|
47
|
+
address = expandIPv6Address(address);
|
|
48
|
+
if (address.at(4) !== ":") return false;
|
|
49
|
+
const firstWord = parseInt(address.substring(0, 4), 16);
|
|
50
|
+
return !(firstWord >= 64512 && firstWord <= 65023 || firstWord >= 65152 && firstWord <= 65215 || firstWord === 0 || firstWord >= 65280);
|
|
51
|
+
}
|
|
52
|
+
function expandIPv6Address(address) {
|
|
53
|
+
address = address.toLowerCase();
|
|
54
|
+
if (address === "::") return "0000:0000:0000:0000:0000:0000:0000:0000";
|
|
55
|
+
if (address.startsWith("::")) address = "0000" + address;
|
|
56
|
+
if (address.endsWith("::")) address = address + "0000";
|
|
57
|
+
address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
|
|
58
|
+
const parts = address.split(":");
|
|
59
|
+
return parts.map((part) => part.padStart(4, "0")).join(":");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
export { UrlError, expandIPv6Address, isValidPublicIPv4Address, isValidPublicIPv6Address, validatePublicUrl };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const require_chunk = require('./chunk-DWy1uDak.cjs');
|
|
2
|
+
const require_url = require('./url-C5Vs9nYh.cjs');
|
|
3
|
+
const node_assert = require_chunk.__toESM(require("node:assert"));
|
|
4
|
+
const node_test = require_chunk.__toESM(require("node:test"));
|
|
5
|
+
|
|
6
|
+
//#region src/url.test.ts
|
|
7
|
+
(0, node_test.test)("validatePublicUrl()", async () => {
|
|
8
|
+
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("ftp://localhost"), require_url.UrlError);
|
|
9
|
+
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="), require_url.UrlError);
|
|
10
|
+
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://localhost"), require_url.UrlError);
|
|
11
|
+
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://127.0.0.1"), require_url.UrlError);
|
|
12
|
+
await (0, node_assert.rejects)(() => require_url.validatePublicUrl("https://[::1]"), require_url.UrlError);
|
|
13
|
+
});
|
|
14
|
+
(0, node_test.test)("isValidPublicIPv4Address()", () => {
|
|
15
|
+
(0, node_assert.ok)(require_url.isValidPublicIPv4Address("8.8.8.8"));
|
|
16
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv4Address("192.168.1.1"));
|
|
17
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv4Address("127.0.0.1"));
|
|
18
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv4Address("10.0.0.1"));
|
|
19
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv4Address("127.16.0.1"));
|
|
20
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv4Address("169.254.0.1"));
|
|
21
|
+
});
|
|
22
|
+
(0, node_test.test)("isValidPublicIPv6Address()", () => {
|
|
23
|
+
(0, node_assert.ok)(require_url.isValidPublicIPv6Address("2001:db8::1"));
|
|
24
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("::1"));
|
|
25
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("fc00::1"));
|
|
26
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("fe80::1"));
|
|
27
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("ff00::1"));
|
|
28
|
+
(0, node_assert.ok)(!require_url.isValidPublicIPv6Address("::"));
|
|
29
|
+
});
|
|
30
|
+
(0, node_test.test)("expandIPv6Address()", () => {
|
|
31
|
+
(0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("::"), "0000:0000:0000:0000:0000:0000:0000:0000");
|
|
32
|
+
(0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("::1"), "0000:0000:0000:0000:0000:0000:0000:0001");
|
|
33
|
+
(0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("2001:db8::"), "2001:0db8:0000:0000:0000:0000:0000:0000");
|
|
34
|
+
(0, node_assert.deepStrictEqual)(require_url.expandIPv6Address("2001:db8::1"), "2001:0db8:0000:0000:0000:0000:0000:0001");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/url.test.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { UrlError, expandIPv6Address, isValidPublicIPv4Address, isValidPublicIPv6Address, validatePublicUrl } from "./url-fW_DHbih.js";
|
|
2
|
+
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
|
|
5
|
+
//#region src/url.test.ts
|
|
6
|
+
test("validatePublicUrl()", async () => {
|
|
7
|
+
await rejects(() => validatePublicUrl("ftp://localhost"), UrlError);
|
|
8
|
+
await rejects(() => validatePublicUrl("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=="), UrlError);
|
|
9
|
+
await rejects(() => validatePublicUrl("https://localhost"), UrlError);
|
|
10
|
+
await rejects(() => validatePublicUrl("https://127.0.0.1"), UrlError);
|
|
11
|
+
await rejects(() => validatePublicUrl("https://[::1]"), UrlError);
|
|
12
|
+
});
|
|
13
|
+
test("isValidPublicIPv4Address()", () => {
|
|
14
|
+
ok(isValidPublicIPv4Address("8.8.8.8"));
|
|
15
|
+
ok(!isValidPublicIPv4Address("192.168.1.1"));
|
|
16
|
+
ok(!isValidPublicIPv4Address("127.0.0.1"));
|
|
17
|
+
ok(!isValidPublicIPv4Address("10.0.0.1"));
|
|
18
|
+
ok(!isValidPublicIPv4Address("127.16.0.1"));
|
|
19
|
+
ok(!isValidPublicIPv4Address("169.254.0.1"));
|
|
20
|
+
});
|
|
21
|
+
test("isValidPublicIPv6Address()", () => {
|
|
22
|
+
ok(isValidPublicIPv6Address("2001:db8::1"));
|
|
23
|
+
ok(!isValidPublicIPv6Address("::1"));
|
|
24
|
+
ok(!isValidPublicIPv6Address("fc00::1"));
|
|
25
|
+
ok(!isValidPublicIPv6Address("fe80::1"));
|
|
26
|
+
ok(!isValidPublicIPv6Address("ff00::1"));
|
|
27
|
+
ok(!isValidPublicIPv6Address("::"));
|
|
28
|
+
});
|
|
29
|
+
test("expandIPv6Address()", () => {
|
|
30
|
+
deepStrictEqual(expandIPv6Address("::"), "0000:0000:0000:0000:0000:0000:0000:0000");
|
|
31
|
+
deepStrictEqual(expandIPv6Address("::1"), "0000:0000:0000:0000:0000:0000:0000:0001");
|
|
32
|
+
deepStrictEqual(expandIPv6Address("2001:db8::"), "2001:0db8:0000:0000:0000:0000:0000:0000");
|
|
33
|
+
deepStrictEqual(expandIPv6Address("2001:db8::1"), "2001:0db8:0000:0000:0000:0000:0000:0001");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/vocab-runtime",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.196+c3cfc0a9",
|
|
4
4
|
"homepage": "https://fedify.dev/",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -55,12 +55,13 @@
|
|
|
55
55
|
"typescript": "^5.9.3"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@logtape/logtape": "^
|
|
58
|
+
"@logtape/logtape": "^2.0.0",
|
|
59
59
|
"@multiformats/base-x": "^4.0.1",
|
|
60
|
+
"@opentelemetry/api": "^1.9.0",
|
|
60
61
|
"asn1js": "^3.0.6",
|
|
61
62
|
"byte-encodings": "^1.0.11",
|
|
62
63
|
"multicodec": "^3.2.1",
|
|
63
|
-
"pkijs": "^3.
|
|
64
|
+
"pkijs": "^3.3.3"
|
|
64
65
|
},
|
|
65
66
|
"scripts": {
|
|
66
67
|
"build": "tsdown",
|
package/src/docloader.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fetchMock from "fetch-mock";
|
|
2
|
-
import { deepStrictEqual, rejects } from "node:assert";
|
|
2
|
+
import { deepStrictEqual, ok, rejects } from "node:assert";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
import preloadedContexts from "./contexts.ts";
|
|
5
5
|
import { getDocumentLoader } from "./docloader.ts";
|
|
@@ -361,5 +361,33 @@ test("getDocumentLoader()", async (t) => {
|
|
|
361
361
|
);
|
|
362
362
|
});
|
|
363
363
|
|
|
364
|
+
// Regression test for ReDoS vulnerability (CVE-2025-68475)
|
|
365
|
+
// Malicious HTML payload: <a a="b" a="b" ... (unclosed tag)
|
|
366
|
+
// With the vulnerable regex, this causes catastrophic backtracking
|
|
367
|
+
const maliciousPayload = "<a" + ' a="b"'.repeat(30) + " ";
|
|
368
|
+
|
|
369
|
+
fetchMock.get("https://example.com/redos", {
|
|
370
|
+
body: maliciousPayload,
|
|
371
|
+
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
await t.test("ReDoS resistance (CVE-2025-68475)", async () => {
|
|
375
|
+
const start = performance.now();
|
|
376
|
+
// The malicious HTML will fail JSON parsing, but the important thing is
|
|
377
|
+
// that it should complete quickly (not hang due to ReDoS)
|
|
378
|
+
await rejects(
|
|
379
|
+
() => fetchDocumentLoader("https://example.com/redos"),
|
|
380
|
+
SyntaxError,
|
|
381
|
+
);
|
|
382
|
+
const elapsed = performance.now() - start;
|
|
383
|
+
|
|
384
|
+
// Should complete in under 1 second. With the vulnerable regex,
|
|
385
|
+
// this would take 14+ seconds for 30 repetitions.
|
|
386
|
+
ok(
|
|
387
|
+
elapsed < 1000,
|
|
388
|
+
`Potential ReDoS vulnerability detected: ${elapsed}ms (expected < 1000ms)`,
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
|
|
364
392
|
fetchMock.hardReset();
|
|
365
393
|
});
|