@fedify/fedify 2.0.0-dev.1604 → 2.0.0-dev.1690
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/dist/actor-Be0ThtXy.cjs +42609 -0
- package/dist/{actor-BC3XtGTj.js → actor-ChbPLm6n.js} +7266 -2443
- package/dist/actor-D6K058Tb.d.cts +128 -0
- package/dist/{actor-CmRlosMl.js → actor-DuCeRiNh.js} +1 -1
- package/dist/{actor-C22bXuuC.d.ts → actor-T6RyhRgk.d.ts} +1 -1
- package/dist/{assert_rejects-7UF4R_Qs.js → assert_rejects-DiIiJbZn.js} +1 -1
- package/dist/{assert_throws-53_pKeP3.js → assert_throws-BOO88avQ.js} +1 -1
- package/dist/{authdocloader-CzpPQC01.js → authdocloader-Brax1A32.js} +3 -3
- package/dist/{authdocloader-D-DICGXT.js → authdocloader-CrxhFL8e.js} +6 -6
- package/dist/authdocloader-OSn_teLV.cjs +58 -0
- package/dist/{builder-D9zidAqR.js → builder-CYOcDUkj.js} +10 -4
- package/dist/chunk-DqRYRqnO.cjs +34 -0
- package/dist/client-CegPX0Rn.d.cts +222 -0
- package/dist/{client-Bsj0vQPr.js → client-CnOdwLLN.js} +1 -1
- package/dist/compat/mod.cjs +10 -0
- package/dist/compat/mod.d.cts +13 -0
- package/dist/compat/mod.d.ts +7 -7
- package/dist/compat/mod.js +5 -5
- package/dist/compat/transformers.test.js +18 -17
- package/dist/compat-DmDDELst.cjs +4 -0
- package/dist/compat-nxUqe4Z-.js +4 -0
- package/dist/{context-CDSZdQHD.d.ts → context-ByZprN0S.d.ts} +66 -6
- package/dist/context-C5BsZkDr.d.cts +2315 -0
- package/dist/docloader-CCqXeagZ.cjs +4861 -0
- package/dist/docloader-D-MrRyHl.d.cts +219 -0
- package/dist/{docloader-CkASOfZ5.js → docloader-XK3y2jn5.js} +189 -9
- package/dist/{esm-CXF1VoeR.js → esm-DnIzfEj0.js} +1 -1
- package/dist/federation/builder.test.js +10 -10
- package/dist/federation/collection.test.js +8 -8
- package/dist/federation/handler.test.js +26 -145
- package/dist/federation/idempotency.test.d.ts +3 -0
- package/dist/federation/idempotency.test.js +202 -0
- package/dist/federation/inbox.test.js +6 -6
- package/dist/federation/keycache.test.js +4 -4
- package/dist/federation/kv.test.js +9 -8
- package/dist/federation/middleware.test.js +232 -38
- package/dist/federation/mod.cjs +29 -0
- package/dist/federation/mod.d.cts +13 -0
- package/dist/federation/mod.d.ts +7 -7
- package/dist/federation/mod.js +15 -15
- package/dist/federation/mq.test.js +10 -10
- package/dist/federation/negotiation.test.d.ts +3 -0
- package/dist/federation/negotiation.test.js +28 -0
- package/dist/federation/retry.test.js +5 -5
- package/dist/federation/router.test.js +8 -8
- package/dist/federation/send.test.js +15 -15
- package/dist/{federation-CMX7WzeL.js → federation-D1U8YY9t.js} +3 -3
- package/dist/federation-H2_En3j5.cjs +244 -0
- package/dist/fixtures/media.example.com/avatars/test-avatar.jpg.json +6 -0
- package/dist/{http-CSYNybkH.js → http-BNOYnVsU.js} +2 -2
- package/dist/{http-B_pxY5of.js → http-BxbM8sEy.js} +6 -6
- package/dist/http-C7vbQwbz.cjs +826 -0
- package/dist/http-D-e6AFwR.d.cts +253 -0
- package/dist/{http-DqSNLFNY.d.ts → http-D6Uj2x2y.d.ts} +1 -1
- package/dist/{inbox-DoJlwIE4.js → inbox-BRru9pX3.js} +24 -7
- package/dist/{key-BOzqiGbl.js → key-1KXru8Ug.js} +2 -2
- package/dist/key-B3uag-rz.js +10 -0
- package/dist/key-BiBmb1Yy.cjs +10 -0
- package/dist/{key-DWWqeyaq.js → key-DK_nfU4I.js} +3 -3
- package/dist/key-Z6ceKnZC.cjs +290 -0
- package/dist/{key-Bif4eMj7.js → key-jyNTxCvK.js} +5 -5
- package/dist/{keycache--NYDYifd.js → keycache-CN61iGVj.js} +1 -1
- package/dist/{keys-MacD9GP6.js → keys-BPdFKgiy.js} +1 -1
- package/dist/kv-63Cil1MD.d.cts +81 -0
- package/dist/{ld-D9oTBLrs.js → ld-Dv8DNNAT.js} +2 -2
- package/dist/{lookup-Cz9VSYxw.js → lookup-BPviO8ij.js} +4 -4
- package/dist/lookup-hnMAAU5r.cjs +137 -0
- package/dist/{lookup-DuzrBbTj.js → lookup-pV0JOsuV.js} +21 -12
- package/dist/middleware-BmoOlgc1.cjs +4231 -0
- package/dist/{middleware-C0w2M0tb.js → middleware-Bz_A2jeJ.js} +87 -91
- package/dist/middleware-CI0-zw4U.js +26 -0
- package/dist/{middleware-CgNm3XOJ.js → middleware-DY9B2lL8.js} +57 -148
- package/dist/middleware-QNK-W-jE.cjs +17 -0
- package/dist/middleware-_vjt6FWU.js +17 -0
- package/dist/mod-8DMWKtQE.d.cts +80 -0
- package/dist/{mod-Drmz72EK.d.ts → mod-BhUKmBJD.d.ts} +2 -2
- package/dist/mod-C2tOeRkN.d.cts +1 -0
- package/dist/{mod-TFoH2Ql8.d.ts → mod-CerN_Sza.d.ts} +1 -1
- package/dist/mod-Cj1tHXBR.d.cts +102 -0
- package/dist/mod-CxkWO3Mg.d.cts +307 -0
- package/dist/{mod-RI3-KvUI.d.ts → mod-D6hQoxC5.d.ts} +2 -2
- package/dist/mod-Djzcw2ry.d.cts +266 -0
- package/dist/{mod-Cxt4Kpf6.d.ts → mod-DlU8ISoa.d.ts} +19 -1
- package/dist/mod-FZd39qVq.d.cts +1 -0
- package/dist/mod-jQ4OODsl.d.cts +113 -0
- package/dist/mod.cjs +150 -0
- package/dist/mod.d.cts +17 -0
- package/dist/mod.d.ts +10 -10
- package/dist/mod.js +20 -20
- package/dist/mq-B7R1Q-M5.d.cts +140 -0
- package/dist/negotiation-5NPJL6zp.js +71 -0
- package/dist/nodeinfo/client.test.js +10 -10
- package/dist/nodeinfo/handler.test.js +23 -22
- package/dist/nodeinfo/mod.cjs +11 -0
- package/dist/nodeinfo/mod.d.cts +5 -0
- package/dist/nodeinfo/mod.js +6 -6
- package/dist/nodeinfo/types.test.js +8 -8
- package/dist/nodeinfo-Co9lJrWl.cjs +4 -0
- package/dist/nodeinfo-DfycQ8Wf.js +4 -0
- package/dist/owner-BN_tO3cY.d.cts +67 -0
- package/dist/{owner-gB-pDKA-.js → owner-e3FYDhsk.js} +2 -2
- package/dist/{owner-CQPnQVtf.d.ts → owner-hd9lvQcP.d.ts} +2 -2
- package/dist/{proof-CScVkkIT.js → proof-6gFMwMNJ.js} +2 -2
- package/dist/proof-B-eqv0Ug.cjs +673 -0
- package/dist/{proof-VPikK_0V.js → proof-DfgvA3al.js} +6 -6
- package/dist/runtime/authdocloader.test.js +14 -14
- package/dist/runtime/docloader.test.js +9 -9
- package/dist/runtime/key.test.js +10 -10
- package/dist/runtime/langstr.test.js +8 -8
- package/dist/runtime/link.test.d.ts +3 -0
- package/dist/runtime/link.test.js +61 -0
- package/dist/runtime/mod.cjs +25 -0
- package/dist/runtime/mod.d.cts +6 -0
- package/dist/runtime/mod.d.ts +3 -3
- package/dist/runtime/mod.js +10 -10
- package/dist/runtime/multibase/multibase.test.js +8 -8
- package/dist/runtime/url.test.js +5 -5
- package/dist/runtime-C58AJWSv.cjs +4 -0
- package/dist/runtime-DPYEDf-o.js +4 -0
- package/dist/{send-flZ7H10-.js → send-Tl9NOnmO.js} +2 -2
- package/dist/sig/http.test.js +13 -13
- package/dist/sig/key.test.js +11 -11
- package/dist/sig/ld.test.js +10 -10
- package/dist/sig/mod.cjs +30 -0
- package/dist/sig/mod.d.cts +8 -0
- package/dist/sig/mod.d.ts +5 -5
- package/dist/sig/mod.js +10 -10
- package/dist/sig/owner.test.js +12 -12
- package/dist/sig/proof.test.js +12 -12
- package/dist/sig-ByHXzqUi.cjs +4 -0
- package/dist/sig-Cj3tk-ig.js +4 -0
- package/dist/testing/docloader.test.js +8 -8
- package/dist/testing/mod.d.ts +272 -0
- package/dist/testing/mod.js +3 -3
- package/dist/{testing-CdBSI4xF.js → testing-BWNCAbL-.js} +2 -2
- package/dist/{transformers-Dna8Fg7k.js → transformers-BFT6d7J5.js} +3 -3
- package/dist/transformers-CoBS-oFG.cjs +116 -0
- package/dist/{type-CAteiET0.js → type-C69ZBu7f.js} +7258 -2255
- package/dist/{types-Vo0qKhN7.js → types-DqxyTxOf.js} +4 -4
- package/dist/types-zqdWZh4O.cjs +315 -0
- package/dist/vocab/actor.test.js +10 -10
- package/dist/vocab/lookup.test.js +259 -9
- package/dist/vocab/mod.cjs +87 -0
- package/dist/vocab/mod.d.cts +6 -0
- package/dist/vocab/mod.d.ts +3 -3
- package/dist/vocab/mod.js +7 -7
- package/dist/vocab/type.test.js +3 -3
- package/dist/vocab/vocab.test.js +438 -14
- package/dist/vocab-B39-pFl9.cjs +291 -0
- package/dist/{vocab-SOE1ifCr.d.ts → vocab-BI0Ak5lL.d.ts} +290 -0
- package/dist/{vocab-89qErSc9.js → vocab-BWoeZsME.js} +26 -17
- package/dist/vocab-Dw1-yVGg.d.cts +14922 -0
- package/dist/webfinger/handler.test.js +23 -22
- package/dist/webfinger/lookup.test.js +9 -9
- package/dist/webfinger/mod.cjs +9 -0
- package/dist/webfinger/mod.d.cts +4 -0
- package/dist/webfinger/mod.js +6 -6
- package/dist/webfinger-BjOEdFPs.cjs +4 -0
- package/dist/webfinger-De_bU0iE.js +4 -0
- package/dist/x/cfworkers.cjs +100 -0
- package/dist/x/cfworkers.d.cts +59 -0
- package/dist/x/cfworkers.js +3 -3
- package/dist/x/cfworkers.test.js +8 -8
- package/dist/x/hono.cjs +61 -0
- package/dist/x/hono.d.cts +54 -0
- package/dist/x/hono.d.ts +6 -6
- package/dist/x/hono.js +3 -3
- package/dist/x/sveltekit.cjs +69 -0
- package/dist/x/sveltekit.d.cts +46 -0
- package/dist/x/sveltekit.d.ts +6 -6
- package/dist/x/sveltekit.js +3 -3
- package/package.json +68 -13
- package/dist/compat-Bb5myD13.js +0 -4
- package/dist/key-BteSu1lX.js +0 -10
- package/dist/middleware-CNfiKLIZ.js +0 -25
- package/dist/middleware-CnvU63kV.js +0 -17
- package/dist/nodeinfo-CyEbLjHs.js +0 -4
- package/dist/runtime-BSkOVUWM.js +0 -4
- package/dist/sig-BXJO--F9.js +0 -4
- package/dist/webfinger-C3GIyXIg.js +0 -4
- /package/dist/{assert_is_error-B035L3om.js → assert_is_error-BPGph1Jx.js} +0 -0
- /package/dist/{assert_not_equals-C80BG-_5.js → assert_not_equals-f3m3epl3.js} +0 -0
- /package/dist/{denokv-jZ0Z2h0M.js → denokv-Bv33Xxea.js} +0 -0
- /package/dist/{retry-CfF8Gn4d.js → retry-D4GJ670a.js} +0 -0
- /package/dist/{std__assert-DWivtrGR.js → std__assert-X-_kMxKM.js} +0 -0
@@ -1,8 +1,8 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
import { getUserAgent } from "./docloader-
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
3
|
+
import { URLPattern } from "urlpattern-polyfill";
|
4
|
+
|
5
|
+
import { getUserAgent } from "./docloader-XK3y2jn5.js";
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
7
7
|
|
8
8
|
//#region src/nodeinfo/client.ts
|
@@ -0,0 +1,315 @@
|
|
1
|
+
|
2
|
+
const { Temporal } = require("@js-temporal/polyfill");
|
3
|
+
const { URLPattern } = require("urlpattern-polyfill");
|
4
|
+
|
5
|
+
const require_chunk = require('./chunk-DqRYRqnO.cjs');
|
6
|
+
const require_docloader = require('./docloader-CCqXeagZ.cjs');
|
7
|
+
const __logtape_logtape = require_chunk.__toESM(require("@logtape/logtape"));
|
8
|
+
|
9
|
+
//#region src/nodeinfo/client.ts
|
10
|
+
const logger = (0, __logtape_logtape.getLogger)([
|
11
|
+
"fedify",
|
12
|
+
"nodeinfo",
|
13
|
+
"client"
|
14
|
+
]);
|
15
|
+
async function getNodeInfo(url, options = {}) {
|
16
|
+
try {
|
17
|
+
let nodeInfoUrl = url;
|
18
|
+
if (!options.direct) {
|
19
|
+
const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
|
20
|
+
const wellKnownResponse = await fetch(wellKnownUrl, { headers: {
|
21
|
+
Accept: "application/json",
|
22
|
+
"User-Agent": typeof options.userAgent === "string" ? options.userAgent : require_docloader.getUserAgent(options.userAgent)
|
23
|
+
} });
|
24
|
+
if (!wellKnownResponse.ok) {
|
25
|
+
logger.error("Failed to fetch {url}: {status} {statusText}", {
|
26
|
+
url: wellKnownUrl.href,
|
27
|
+
status: wellKnownResponse.status,
|
28
|
+
statusText: wellKnownResponse.statusText
|
29
|
+
});
|
30
|
+
return void 0;
|
31
|
+
}
|
32
|
+
const wellKnownRd = await wellKnownResponse.json();
|
33
|
+
const link = wellKnownRd?.links?.find((link$1) => link$1 != null && "rel" in link$1 && (link$1.rel === "http://nodeinfo.diaspora.software/ns/schema/2.0" || link$1.rel === "http://nodeinfo.diaspora.software/ns/schema/2.1") && "href" in link$1 && link$1.href != null);
|
34
|
+
if (link == null || link.href == null) {
|
35
|
+
logger.error("Failed to find a NodeInfo document link from {url}: {resourceDescriptor}", {
|
36
|
+
url: wellKnownUrl.href,
|
37
|
+
resourceDescriptor: wellKnownRd
|
38
|
+
});
|
39
|
+
return void 0;
|
40
|
+
}
|
41
|
+
nodeInfoUrl = link.href;
|
42
|
+
}
|
43
|
+
const response = await fetch(nodeInfoUrl, { headers: {
|
44
|
+
Accept: "application/json",
|
45
|
+
"User-Agent": typeof options.userAgent === "string" ? options.userAgent : require_docloader.getUserAgent(options.userAgent)
|
46
|
+
} });
|
47
|
+
if (!response.ok) {
|
48
|
+
logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
|
49
|
+
url: nodeInfoUrl.toString(),
|
50
|
+
status: response.status,
|
51
|
+
statusText: response.statusText
|
52
|
+
});
|
53
|
+
return void 0;
|
54
|
+
}
|
55
|
+
const data = await response.json();
|
56
|
+
if (options.parse === "none") return data;
|
57
|
+
return parseNodeInfo(data, { tryBestEffort: options.parse === "best-effort" }) ?? void 0;
|
58
|
+
} catch (error) {
|
59
|
+
logger.error("Failed to fetch NodeInfo document from {url}: {error}", {
|
60
|
+
url: url.toString(),
|
61
|
+
error
|
62
|
+
});
|
63
|
+
return void 0;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
/**
|
67
|
+
* Parses a NodeInfo document.
|
68
|
+
* @param data A JSON value that complies with the NodeInfo schema.
|
69
|
+
* @param options Options for parsing the NodeInfo document.
|
70
|
+
* @returns The parsed NodeInfo document if it is valid. Otherwise, `null`
|
71
|
+
* is returned.
|
72
|
+
* @since 1.2.0
|
73
|
+
*/
|
74
|
+
function parseNodeInfo(data, options = {}) {
|
75
|
+
if (typeof data !== "object" || data == null || !("software" in data)) return null;
|
76
|
+
const software = parseSoftware(data.software, options);
|
77
|
+
if (software == null) return null;
|
78
|
+
let protocols = [];
|
79
|
+
if ("protocols" in data && Array.isArray(data.protocols)) {
|
80
|
+
const ps = data.protocols.map(parseProtocol);
|
81
|
+
protocols = ps.filter((p) => p != null);
|
82
|
+
if (ps.length != protocols.length && !options.tryBestEffort) return null;
|
83
|
+
} else if (!options.tryBestEffort) return null;
|
84
|
+
let services;
|
85
|
+
if ("services" in data) {
|
86
|
+
if (typeof data.services === "object" && data.services != null) {
|
87
|
+
const ss = parseServices(data.services, options);
|
88
|
+
if (ss == null) {
|
89
|
+
if (!options.tryBestEffort) return null;
|
90
|
+
} else services = ss;
|
91
|
+
} else if (!options.tryBestEffort) return null;
|
92
|
+
}
|
93
|
+
let openRegistrations;
|
94
|
+
if ("openRegistrations" in data) {
|
95
|
+
if (typeof data.openRegistrations === "boolean") openRegistrations = data.openRegistrations;
|
96
|
+
else if (!options.tryBestEffort) return null;
|
97
|
+
}
|
98
|
+
let usage = {
|
99
|
+
users: {},
|
100
|
+
localPosts: 0,
|
101
|
+
localComments: 0
|
102
|
+
};
|
103
|
+
if ("usage" in data) {
|
104
|
+
const u = parseUsage(data.usage, options);
|
105
|
+
if (u == null) {
|
106
|
+
if (!options.tryBestEffort) return null;
|
107
|
+
} else usage = u;
|
108
|
+
}
|
109
|
+
let metadata;
|
110
|
+
if ("metadata" in data) {
|
111
|
+
if (typeof data.metadata === "object" && data.metadata != null) metadata = Object.fromEntries(Object.entries(data.metadata));
|
112
|
+
else if (!options.tryBestEffort) return null;
|
113
|
+
}
|
114
|
+
const result = {
|
115
|
+
software,
|
116
|
+
protocols,
|
117
|
+
usage
|
118
|
+
};
|
119
|
+
if (services != null) result.services = services;
|
120
|
+
if (openRegistrations != null) result.openRegistrations = openRegistrations;
|
121
|
+
if (metadata != null) result.metadata = metadata;
|
122
|
+
return result;
|
123
|
+
}
|
124
|
+
function parseSoftware(data, options = {}) {
|
125
|
+
if (typeof data !== "object" || data == null) {
|
126
|
+
if (!options.tryBestEffort) data = {};
|
127
|
+
return null;
|
128
|
+
}
|
129
|
+
let name;
|
130
|
+
if ("name" in data && typeof data.name === "string" && data.name.match(/^\s*[A-Za-z0-9-]+\s*$/)) {
|
131
|
+
if (!data.name.match(/^[a-z0-9-]+$/) && !options.tryBestEffort) return null;
|
132
|
+
name = data.name.trim().toLowerCase();
|
133
|
+
} else return null;
|
134
|
+
let version;
|
135
|
+
if ("version" in data) version = String(data.version);
|
136
|
+
else {
|
137
|
+
if (!options.tryBestEffort) return null;
|
138
|
+
version = "0.0.0";
|
139
|
+
}
|
140
|
+
let repository;
|
141
|
+
if ("repository" in data) {
|
142
|
+
if (typeof data.repository === "string") try {
|
143
|
+
repository = new URL(data.repository);
|
144
|
+
} catch {
|
145
|
+
if (!options.tryBestEffort) return null;
|
146
|
+
}
|
147
|
+
else if (!options.tryBestEffort) return null;
|
148
|
+
}
|
149
|
+
let homepage;
|
150
|
+
if ("homepage" in data) {
|
151
|
+
if (typeof data.homepage === "string") try {
|
152
|
+
homepage = new URL(data.homepage);
|
153
|
+
} catch {
|
154
|
+
if (!options.tryBestEffort) return null;
|
155
|
+
}
|
156
|
+
else if (!options.tryBestEffort) return null;
|
157
|
+
}
|
158
|
+
const result = {
|
159
|
+
name,
|
160
|
+
version
|
161
|
+
};
|
162
|
+
if (repository != null) result.repository = repository;
|
163
|
+
if (homepage != null) result.homepage = homepage;
|
164
|
+
return result;
|
165
|
+
}
|
166
|
+
function parseProtocol(data) {
|
167
|
+
if (data === "activitypub" || data === "buddycloud" || data === "dfrn" || data === "diaspora" || data === "libertree" || data === "ostatus" || data === "pumpio" || data === "tent" || data === "xmpp" || data === "zot") return data;
|
168
|
+
return null;
|
169
|
+
}
|
170
|
+
function parseServices(data, options = {}) {
|
171
|
+
if (!(typeof data === "object") || data == null) {
|
172
|
+
if (options.tryBestEffort) return {};
|
173
|
+
return null;
|
174
|
+
}
|
175
|
+
let inbound;
|
176
|
+
if ("inbound" in data && Array.isArray(data.inbound)) {
|
177
|
+
const is = data.inbound.map(parseInboundService);
|
178
|
+
inbound = is.filter((i) => i != null);
|
179
|
+
if (is.length > inbound.length && !options.tryBestEffort) return null;
|
180
|
+
}
|
181
|
+
let outbound;
|
182
|
+
if ("outbound" in data && Array.isArray(data.outbound)) {
|
183
|
+
const os = data.outbound.map(parseOutboundService);
|
184
|
+
outbound = os.filter((o) => o != null);
|
185
|
+
if (os.length > outbound.length && !options.tryBestEffort) return null;
|
186
|
+
}
|
187
|
+
const result = {};
|
188
|
+
if (inbound != null) result.inbound = inbound;
|
189
|
+
if (outbound != null) result.outbound = outbound;
|
190
|
+
return result;
|
191
|
+
}
|
192
|
+
function parseInboundService(data) {
|
193
|
+
if (data === "atom1.0" || data === "gnusocial" || data === "imap" || data === "pnut" || data === "pop3" || data === "pumpio" || data === "rss2.0" || data === "twitter") return data;
|
194
|
+
return null;
|
195
|
+
}
|
196
|
+
function parseOutboundService(data) {
|
197
|
+
if (data === "atom1.0" || data === "blogger" || data === "buddycloud" || data === "diaspora" || data === "dreamwidth" || data === "drupal" || data === "facebook" || data === "friendica" || data === "gnusocial" || data === "google" || data === "insanejournal" || data === "libertree" || data === "linkedin" || data === "livejournal" || data === "mediagoblin" || data === "myspace" || data === "pinterest" || data === "pnut" || data === "posterous" || data === "pumpio" || data === "redmatrix" || data === "rss2.0" || data === "smtp" || data === "tent" || data === "tumblr" || data === "twitter" || data === "wordpress" || data === "xmpp") return data;
|
198
|
+
return null;
|
199
|
+
}
|
200
|
+
function parseUsage(data, options = {}) {
|
201
|
+
if (typeof data !== "object" || data == null) return null;
|
202
|
+
const users = {};
|
203
|
+
if ("users" in data && typeof data.users === "object" && data.users != null) {
|
204
|
+
if ("total" in data.users) if (typeof data.users.total === "number") users.total = data.users.total;
|
205
|
+
else {
|
206
|
+
if (!options.tryBestEffort) return null;
|
207
|
+
if (typeof data.users.total === "string") {
|
208
|
+
const n = parseInt(data.users.total);
|
209
|
+
if (!isNaN(n)) users.total = n;
|
210
|
+
}
|
211
|
+
}
|
212
|
+
if ("activeHalfyear" in data.users) if (typeof data.users.activeHalfyear === "number") users.activeHalfyear = data.users.activeHalfyear;
|
213
|
+
else {
|
214
|
+
if (!options.tryBestEffort) return null;
|
215
|
+
if (typeof data.users.activeHalfyear === "string") {
|
216
|
+
const n = parseInt(data.users.activeHalfyear);
|
217
|
+
if (!isNaN(n)) users.activeHalfyear = n;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
if ("activeMonth" in data.users) if (typeof data.users.activeMonth === "number") users.activeMonth = data.users.activeMonth;
|
221
|
+
else {
|
222
|
+
if (!options.tryBestEffort) return null;
|
223
|
+
if (typeof data.users.activeMonth === "string") {
|
224
|
+
const n = parseInt(data.users.activeMonth);
|
225
|
+
if (!isNaN(n)) users.activeMonth = n;
|
226
|
+
}
|
227
|
+
}
|
228
|
+
} else if (!options.tryBestEffort) return null;
|
229
|
+
let localPosts = 0;
|
230
|
+
if ("localPosts" in data) if (typeof data.localPosts === "number") localPosts = data.localPosts;
|
231
|
+
else {
|
232
|
+
if (!options.tryBestEffort) return null;
|
233
|
+
if (typeof data.localPosts === "string") {
|
234
|
+
const n = parseInt(data.localPosts);
|
235
|
+
if (!isNaN(n)) localPosts = n;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
let localComments = 0;
|
239
|
+
if ("localComments" in data) if (typeof data.localComments === "number") localComments = data.localComments;
|
240
|
+
else {
|
241
|
+
if (!options.tryBestEffort) return null;
|
242
|
+
if (typeof data.localComments === "string") {
|
243
|
+
const n = parseInt(data.localComments);
|
244
|
+
if (!isNaN(n)) localComments = n;
|
245
|
+
}
|
246
|
+
}
|
247
|
+
return {
|
248
|
+
users,
|
249
|
+
localPosts,
|
250
|
+
localComments
|
251
|
+
};
|
252
|
+
}
|
253
|
+
|
254
|
+
//#endregion
|
255
|
+
//#region src/nodeinfo/types.ts
|
256
|
+
/**
|
257
|
+
* Converts a {@link NodeInfo} object to a JSON value.
|
258
|
+
* @param nodeInfo The {@link NodeInfo} object to convert.
|
259
|
+
* @returns The JSON value that complies with the NodeInfo schema.
|
260
|
+
* @throws {TypeError} If the {@link NodeInfo} object is invalid.
|
261
|
+
*/
|
262
|
+
function nodeInfoToJson(nodeInfo) {
|
263
|
+
if (!nodeInfo.software.name.match(/^[a-z0-9-]+$/)) throw new TypeError("Invalid software name.");
|
264
|
+
if (nodeInfo.protocols.length < 1) throw new TypeError("At least one protocol must be supported.");
|
265
|
+
if (nodeInfo.usage.users.total != null && (nodeInfo.usage.users.total < 0 || !Number.isInteger(nodeInfo.usage.users.total))) throw new TypeError("Invalid total users.");
|
266
|
+
if (nodeInfo.usage.users.activeHalfyear != null && (nodeInfo.usage.users.activeHalfyear < 0 || !Number.isInteger(nodeInfo.usage.users.activeHalfyear))) throw new TypeError("Invalid active halfyear users.");
|
267
|
+
if (nodeInfo.usage.users.activeMonth != null && (nodeInfo.usage.users.activeMonth < 0 || !Number.isInteger(nodeInfo.usage.users.activeMonth))) throw new TypeError("Invalid active month users.");
|
268
|
+
if (nodeInfo.usage.localPosts < 0 || !Number.isInteger(nodeInfo.usage.localPosts)) throw new TypeError("Invalid local posts.");
|
269
|
+
if (nodeInfo.usage.localComments < 0 || !Number.isInteger(nodeInfo.usage.localComments)) throw new TypeError("Invalid local comments.");
|
270
|
+
return {
|
271
|
+
"$schema": "http://nodeinfo.diaspora.software/ns/schema/2.1#",
|
272
|
+
version: "2.1",
|
273
|
+
software: {
|
274
|
+
name: nodeInfo.software.name,
|
275
|
+
version: nodeInfo.software.version,
|
276
|
+
repository: nodeInfo.software.repository?.href,
|
277
|
+
homepage: nodeInfo.software.homepage?.href
|
278
|
+
},
|
279
|
+
protocols: nodeInfo.protocols,
|
280
|
+
services: nodeInfo.services == null ? {
|
281
|
+
inbound: [],
|
282
|
+
outbound: []
|
283
|
+
} : {
|
284
|
+
inbound: nodeInfo.services.inbound ?? [],
|
285
|
+
outbound: nodeInfo.services.outbound ?? []
|
286
|
+
},
|
287
|
+
openRegistrations: nodeInfo.openRegistrations ?? false,
|
288
|
+
usage: {
|
289
|
+
users: nodeInfo.usage.users,
|
290
|
+
localPosts: nodeInfo.usage.localPosts,
|
291
|
+
localComments: nodeInfo.usage.localComments
|
292
|
+
},
|
293
|
+
metadata: nodeInfo.metadata ?? {}
|
294
|
+
};
|
295
|
+
}
|
296
|
+
|
297
|
+
//#endregion
|
298
|
+
Object.defineProperty(exports, 'getNodeInfo', {
|
299
|
+
enumerable: true,
|
300
|
+
get: function () {
|
301
|
+
return getNodeInfo;
|
302
|
+
}
|
303
|
+
});
|
304
|
+
Object.defineProperty(exports, 'nodeInfoToJson', {
|
305
|
+
enumerable: true,
|
306
|
+
get: function () {
|
307
|
+
return nodeInfoToJson;
|
308
|
+
}
|
309
|
+
});
|
310
|
+
Object.defineProperty(exports, 'parseNodeInfo', {
|
311
|
+
enumerable: true,
|
312
|
+
get: function () {
|
313
|
+
return parseNodeInfo;
|
314
|
+
}
|
315
|
+
});
|
package/dist/vocab/actor.test.js
CHANGED
@@ -3,19 +3,19 @@
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
4
4
|
globalThis.addEventListener = () => {};
|
5
5
|
|
6
|
-
import { Application, Group, Organization, Person, Service, __export } from "../type-
|
6
|
+
import { Application, Group, Organization, Person, Service, __export } from "../type-C69ZBu7f.js";
|
7
7
|
import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
8
8
|
import { assert } from "../assert-MZs1qjMx.js";
|
9
9
|
import "../assert_instance_of-DHz7EHNU.js";
|
10
|
-
import "../lookup-
|
11
|
-
import { getActorClassByTypeName, getActorHandle, getActorTypeName, isActor, normalizeActorHandle } from "../actor-
|
12
|
-
import { test } from "../testing-
|
13
|
-
import { assertStrictEquals } from "../std__assert-
|
14
|
-
import { assertFalse, assertRejects } from "../assert_rejects-
|
15
|
-
import "../assert_is_error-
|
16
|
-
import "../assert_not_equals-
|
17
|
-
import { assertThrows } from "../assert_throws-
|
18
|
-
import { esm_default } from "../esm-
|
10
|
+
import "../lookup-pV0JOsuV.js";
|
11
|
+
import { getActorClassByTypeName, getActorHandle, getActorTypeName, isActor, normalizeActorHandle } from "../actor-DuCeRiNh.js";
|
12
|
+
import { test } from "../testing-BWNCAbL-.js";
|
13
|
+
import { assertStrictEquals } from "../std__assert-X-_kMxKM.js";
|
14
|
+
import { assertFalse, assertRejects } from "../assert_rejects-DiIiJbZn.js";
|
15
|
+
import "../assert_is_error-BPGph1Jx.js";
|
16
|
+
import "../assert_not_equals-f3m3epl3.js";
|
17
|
+
import { assertThrows } from "../assert_throws-BOO88avQ.js";
|
18
|
+
import { esm_default } from "../esm-DnIzfEj0.js";
|
19
19
|
|
20
20
|
//#region ../../node_modules/.pnpm/fast-check@3.23.2/node_modules/fast-check/lib/esm/check/precondition/PreconditionFailure.js
|
21
21
|
var PreconditionFailure = class PreconditionFailure extends Error {
|
@@ -3,18 +3,18 @@
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
4
4
|
globalThis.addEventListener = () => {};
|
5
5
|
|
6
|
-
import { Collection, Note, Object as Object$1, Person } from "../type-
|
6
|
+
import { Collection, Note, Object as Object$1, Person } from "../type-C69ZBu7f.js";
|
7
7
|
import { assertEquals } from "../assert_equals-DSbWqCm3.js";
|
8
8
|
import "../assert-MZs1qjMx.js";
|
9
9
|
import { assertInstanceOf } from "../assert_instance_of-DHz7EHNU.js";
|
10
|
-
import { lookupObject, traverseCollection } from "../lookup-
|
11
|
-
import { mockDocumentLoader, test } from "../testing-
|
12
|
-
import "../std__assert-
|
13
|
-
import "../assert_rejects-
|
14
|
-
import "../assert_is_error-
|
15
|
-
import "../assert_not_equals-
|
16
|
-
import "../assert_throws-
|
17
|
-
import { esm_default } from "../esm-
|
10
|
+
import { lookupObject, traverseCollection } from "../lookup-pV0JOsuV.js";
|
11
|
+
import { mockDocumentLoader, test } from "../testing-BWNCAbL-.js";
|
12
|
+
import "../std__assert-X-_kMxKM.js";
|
13
|
+
import { assertRejects } from "../assert_rejects-DiIiJbZn.js";
|
14
|
+
import "../assert_is_error-BPGph1Jx.js";
|
15
|
+
import "../assert_not_equals-f3m3epl3.js";
|
16
|
+
import "../assert_throws-BOO88avQ.js";
|
17
|
+
import { esm_default } from "../esm-DnIzfEj0.js";
|
18
18
|
|
19
19
|
//#region src/vocab/lookup.test.ts
|
20
20
|
test("lookupObject()", {
|
@@ -202,5 +202,255 @@ test("traverseCollection()", {
|
|
202
202
|
new Note({ content: "This is a third simple note" })
|
203
203
|
]);
|
204
204
|
});
|
205
|
+
test("FEP-fe34: lookupObject() cross-origin security", {
|
206
|
+
sanitizeResources: false,
|
207
|
+
sanitizeOps: false
|
208
|
+
}, async (t) => {
|
209
|
+
await t.step("crossOrigin: ignore (default) - returns null for cross-origin objects", async () => {
|
210
|
+
const crossOriginDocumentLoader = async (url) => {
|
211
|
+
if (url === "https://example.com/note") return {
|
212
|
+
documentUrl: url,
|
213
|
+
contextUrl: null,
|
214
|
+
document: {
|
215
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
216
|
+
type: "Note",
|
217
|
+
id: "https://malicious.com/fake-note",
|
218
|
+
content: "This is a spoofed note from a different origin"
|
219
|
+
}
|
220
|
+
};
|
221
|
+
throw new Error(`Unexpected URL: ${url}`);
|
222
|
+
};
|
223
|
+
const result = await lookupObject("https://example.com/note", {
|
224
|
+
documentLoader: crossOriginDocumentLoader,
|
225
|
+
contextLoader: mockDocumentLoader
|
226
|
+
});
|
227
|
+
assertEquals(result, null);
|
228
|
+
});
|
229
|
+
await t.step("crossOrigin: throw - throws error for cross-origin objects", async () => {
|
230
|
+
const crossOriginDocumentLoader = async (url) => {
|
231
|
+
if (url === "https://example.com/note") return {
|
232
|
+
documentUrl: url,
|
233
|
+
contextUrl: null,
|
234
|
+
document: {
|
235
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
236
|
+
type: "Note",
|
237
|
+
id: "https://malicious.com/fake-note",
|
238
|
+
content: "This is a spoofed note from a different origin"
|
239
|
+
}
|
240
|
+
};
|
241
|
+
throw new Error(`Unexpected URL: ${url}`);
|
242
|
+
};
|
243
|
+
await assertRejects(() => lookupObject("https://example.com/note", {
|
244
|
+
documentLoader: crossOriginDocumentLoader,
|
245
|
+
contextLoader: mockDocumentLoader,
|
246
|
+
crossOrigin: "throw"
|
247
|
+
}), Error, "The object's @id (https://malicious.com/fake-note) has a different origin than the document URL (https://example.com/note)");
|
248
|
+
});
|
249
|
+
await t.step("crossOrigin: trust - allows cross-origin objects", async () => {
|
250
|
+
const crossOriginDocumentLoader = async (url) => {
|
251
|
+
if (url === "https://example.com/note") return {
|
252
|
+
documentUrl: url,
|
253
|
+
contextUrl: null,
|
254
|
+
document: {
|
255
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
256
|
+
type: "Note",
|
257
|
+
id: "https://malicious.com/fake-note",
|
258
|
+
content: "This is a spoofed note from a different origin"
|
259
|
+
}
|
260
|
+
};
|
261
|
+
throw new Error(`Unexpected URL: ${url}`);
|
262
|
+
};
|
263
|
+
const result = await lookupObject("https://example.com/note", {
|
264
|
+
documentLoader: crossOriginDocumentLoader,
|
265
|
+
contextLoader: mockDocumentLoader,
|
266
|
+
crossOrigin: "trust"
|
267
|
+
});
|
268
|
+
assertInstanceOf(result, Note);
|
269
|
+
assertEquals(result.id, new URL("https://malicious.com/fake-note"));
|
270
|
+
assertEquals(result.content, "This is a spoofed note from a different origin");
|
271
|
+
});
|
272
|
+
await t.step("same-origin objects are always trusted", async () => {
|
273
|
+
const sameOriginDocumentLoader = async (url) => {
|
274
|
+
if (url === "https://example.com/note") return {
|
275
|
+
documentUrl: url,
|
276
|
+
contextUrl: null,
|
277
|
+
document: {
|
278
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
279
|
+
type: "Note",
|
280
|
+
id: "https://example.com/note",
|
281
|
+
content: "This is a legitimate note from the same origin"
|
282
|
+
}
|
283
|
+
};
|
284
|
+
throw new Error(`Unexpected URL: ${url}`);
|
285
|
+
};
|
286
|
+
const result = await lookupObject("https://example.com/note", {
|
287
|
+
documentLoader: sameOriginDocumentLoader,
|
288
|
+
contextLoader: mockDocumentLoader
|
289
|
+
});
|
290
|
+
assertInstanceOf(result, Note);
|
291
|
+
assertEquals(result.id, new URL("https://example.com/note"));
|
292
|
+
assertEquals(result.content, "This is a legitimate note from the same origin");
|
293
|
+
});
|
294
|
+
await t.step("objects without @id are trusted", async () => {
|
295
|
+
const noIdDocumentLoader = async (url) => {
|
296
|
+
if (url === "https://example.com/note") return {
|
297
|
+
documentUrl: url,
|
298
|
+
contextUrl: null,
|
299
|
+
document: {
|
300
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
301
|
+
type: "Note",
|
302
|
+
content: "This is a note without an ID"
|
303
|
+
}
|
304
|
+
};
|
305
|
+
throw new Error(`Unexpected URL: ${url}`);
|
306
|
+
};
|
307
|
+
const result = await lookupObject("https://example.com/note", {
|
308
|
+
documentLoader: noIdDocumentLoader,
|
309
|
+
contextLoader: mockDocumentLoader
|
310
|
+
});
|
311
|
+
assertInstanceOf(result, Note);
|
312
|
+
assertEquals(result.id, null);
|
313
|
+
assertEquals(result.content, "This is a note without an ID");
|
314
|
+
});
|
315
|
+
await t.step("WebFinger lookup with cross-origin actor URL", async () => {
|
316
|
+
esm_default.spyGlobal();
|
317
|
+
esm_default.get("begin:https://example.com/.well-known/webfinger", {
|
318
|
+
subject: "acct:user@example.com",
|
319
|
+
links: [{
|
320
|
+
rel: "self",
|
321
|
+
href: "https://different-origin.com/actor",
|
322
|
+
type: "application/activity+json"
|
323
|
+
}]
|
324
|
+
});
|
325
|
+
const webfingerDocumentLoader = async (url) => {
|
326
|
+
if (url === "https://different-origin.com/actor") return {
|
327
|
+
documentUrl: url,
|
328
|
+
contextUrl: null,
|
329
|
+
document: {
|
330
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
331
|
+
type: "Person",
|
332
|
+
id: "https://malicious.com/fake-actor",
|
333
|
+
name: "Fake Actor"
|
334
|
+
}
|
335
|
+
};
|
336
|
+
throw new Error(`Unexpected URL: ${url}`);
|
337
|
+
};
|
338
|
+
const result1 = await lookupObject("@user@example.com", {
|
339
|
+
documentLoader: webfingerDocumentLoader,
|
340
|
+
contextLoader: mockDocumentLoader
|
341
|
+
});
|
342
|
+
assertEquals(result1, null);
|
343
|
+
await assertRejects(() => lookupObject("@user@example.com", {
|
344
|
+
documentLoader: webfingerDocumentLoader,
|
345
|
+
contextLoader: mockDocumentLoader,
|
346
|
+
crossOrigin: "throw"
|
347
|
+
}), Error, "The object's @id (https://malicious.com/fake-actor) has a different origin than the document URL (https://different-origin.com/actor)");
|
348
|
+
const result2 = await lookupObject("@user@example.com", {
|
349
|
+
documentLoader: webfingerDocumentLoader,
|
350
|
+
contextLoader: mockDocumentLoader,
|
351
|
+
crossOrigin: "trust"
|
352
|
+
});
|
353
|
+
assertInstanceOf(result2, Person);
|
354
|
+
assertEquals(result2.id, new URL("https://malicious.com/fake-actor"));
|
355
|
+
esm_default.removeRoutes();
|
356
|
+
esm_default.hardReset();
|
357
|
+
});
|
358
|
+
await t.step("subdomain same-origin check", async () => {
|
359
|
+
const subdomainDocumentLoader = async (url) => {
|
360
|
+
if (url === "https://api.example.com/note") return {
|
361
|
+
documentUrl: url,
|
362
|
+
contextUrl: null,
|
363
|
+
document: {
|
364
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
365
|
+
type: "Note",
|
366
|
+
id: "https://www.example.com/note",
|
367
|
+
content: "Cross-subdomain note"
|
368
|
+
}
|
369
|
+
};
|
370
|
+
throw new Error(`Unexpected URL: ${url}`);
|
371
|
+
};
|
372
|
+
const result = await lookupObject("https://api.example.com/note", {
|
373
|
+
documentLoader: subdomainDocumentLoader,
|
374
|
+
contextLoader: mockDocumentLoader
|
375
|
+
});
|
376
|
+
assertEquals(result, null);
|
377
|
+
});
|
378
|
+
await t.step("different port same-origin check", async () => {
|
379
|
+
const differentPortDocumentLoader = async (url) => {
|
380
|
+
if (url === "https://example.com:8080/note") return {
|
381
|
+
documentUrl: url,
|
382
|
+
contextUrl: null,
|
383
|
+
document: {
|
384
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
385
|
+
type: "Note",
|
386
|
+
id: "https://example.com:9090/note",
|
387
|
+
content: "Cross-port note"
|
388
|
+
}
|
389
|
+
};
|
390
|
+
throw new Error(`Unexpected URL: ${url}`);
|
391
|
+
};
|
392
|
+
const result = await lookupObject("https://example.com:8080/note", {
|
393
|
+
documentLoader: differentPortDocumentLoader,
|
394
|
+
contextLoader: mockDocumentLoader
|
395
|
+
});
|
396
|
+
assertEquals(result, null);
|
397
|
+
});
|
398
|
+
await t.step("protocol difference same-origin check", async () => {
|
399
|
+
const differentProtocolDocumentLoader = async (url) => {
|
400
|
+
if (url === "https://example.com/note") return {
|
401
|
+
documentUrl: url,
|
402
|
+
contextUrl: null,
|
403
|
+
document: {
|
404
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
405
|
+
type: "Note",
|
406
|
+
id: "http://example.com/note",
|
407
|
+
content: "Cross-protocol note"
|
408
|
+
}
|
409
|
+
};
|
410
|
+
throw new Error(`Unexpected URL: ${url}`);
|
411
|
+
};
|
412
|
+
const result = await lookupObject("https://example.com/note", {
|
413
|
+
documentLoader: differentProtocolDocumentLoader,
|
414
|
+
contextLoader: mockDocumentLoader
|
415
|
+
});
|
416
|
+
assertEquals(result, null);
|
417
|
+
});
|
418
|
+
await t.step("error handling with crossOrigin throw option", async () => {
|
419
|
+
const errorDocumentLoader = async (_url) => {
|
420
|
+
throw new Error("Network error");
|
421
|
+
};
|
422
|
+
const result = await lookupObject("https://example.com/note", {
|
423
|
+
documentLoader: errorDocumentLoader,
|
424
|
+
contextLoader: mockDocumentLoader,
|
425
|
+
crossOrigin: "throw"
|
426
|
+
});
|
427
|
+
assertEquals(result, null);
|
428
|
+
});
|
429
|
+
await t.step("malformed JSON handling with cross-origin policy", async () => {
|
430
|
+
const malformedJsonDocumentLoader = async (url) => {
|
431
|
+
if (url === "https://example.com/note") return {
|
432
|
+
documentUrl: url,
|
433
|
+
contextUrl: null,
|
434
|
+
document: "invalid json"
|
435
|
+
};
|
436
|
+
throw new Error(`Unexpected URL: ${url}`);
|
437
|
+
};
|
438
|
+
assertEquals(await lookupObject("https://example.com/note", {
|
439
|
+
documentLoader: malformedJsonDocumentLoader,
|
440
|
+
contextLoader: mockDocumentLoader,
|
441
|
+
crossOrigin: "ignore"
|
442
|
+
}), null);
|
443
|
+
assertEquals(await lookupObject("https://example.com/note", {
|
444
|
+
documentLoader: malformedJsonDocumentLoader,
|
445
|
+
contextLoader: mockDocumentLoader,
|
446
|
+
crossOrigin: "throw"
|
447
|
+
}), null);
|
448
|
+
assertEquals(await lookupObject("https://example.com/note", {
|
449
|
+
documentLoader: malformedJsonDocumentLoader,
|
450
|
+
contextLoader: mockDocumentLoader,
|
451
|
+
crossOrigin: "trust"
|
452
|
+
}), null);
|
453
|
+
});
|
454
|
+
});
|
205
455
|
|
206
456
|
//#endregion
|