@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.
Files changed (185) hide show
  1. package/dist/actor-Be0ThtXy.cjs +42609 -0
  2. package/dist/{actor-BC3XtGTj.js → actor-ChbPLm6n.js} +7266 -2443
  3. package/dist/actor-D6K058Tb.d.cts +128 -0
  4. package/dist/{actor-CmRlosMl.js → actor-DuCeRiNh.js} +1 -1
  5. package/dist/{actor-C22bXuuC.d.ts → actor-T6RyhRgk.d.ts} +1 -1
  6. package/dist/{assert_rejects-7UF4R_Qs.js → assert_rejects-DiIiJbZn.js} +1 -1
  7. package/dist/{assert_throws-53_pKeP3.js → assert_throws-BOO88avQ.js} +1 -1
  8. package/dist/{authdocloader-CzpPQC01.js → authdocloader-Brax1A32.js} +3 -3
  9. package/dist/{authdocloader-D-DICGXT.js → authdocloader-CrxhFL8e.js} +6 -6
  10. package/dist/authdocloader-OSn_teLV.cjs +58 -0
  11. package/dist/{builder-D9zidAqR.js → builder-CYOcDUkj.js} +10 -4
  12. package/dist/chunk-DqRYRqnO.cjs +34 -0
  13. package/dist/client-CegPX0Rn.d.cts +222 -0
  14. package/dist/{client-Bsj0vQPr.js → client-CnOdwLLN.js} +1 -1
  15. package/dist/compat/mod.cjs +10 -0
  16. package/dist/compat/mod.d.cts +13 -0
  17. package/dist/compat/mod.d.ts +7 -7
  18. package/dist/compat/mod.js +5 -5
  19. package/dist/compat/transformers.test.js +18 -17
  20. package/dist/compat-DmDDELst.cjs +4 -0
  21. package/dist/compat-nxUqe4Z-.js +4 -0
  22. package/dist/{context-CDSZdQHD.d.ts → context-ByZprN0S.d.ts} +66 -6
  23. package/dist/context-C5BsZkDr.d.cts +2315 -0
  24. package/dist/docloader-CCqXeagZ.cjs +4861 -0
  25. package/dist/docloader-D-MrRyHl.d.cts +219 -0
  26. package/dist/{docloader-CkASOfZ5.js → docloader-XK3y2jn5.js} +189 -9
  27. package/dist/{esm-CXF1VoeR.js → esm-DnIzfEj0.js} +1 -1
  28. package/dist/federation/builder.test.js +10 -10
  29. package/dist/federation/collection.test.js +8 -8
  30. package/dist/federation/handler.test.js +26 -145
  31. package/dist/federation/idempotency.test.d.ts +3 -0
  32. package/dist/federation/idempotency.test.js +202 -0
  33. package/dist/federation/inbox.test.js +6 -6
  34. package/dist/federation/keycache.test.js +4 -4
  35. package/dist/federation/kv.test.js +9 -8
  36. package/dist/federation/middleware.test.js +232 -38
  37. package/dist/federation/mod.cjs +29 -0
  38. package/dist/federation/mod.d.cts +13 -0
  39. package/dist/federation/mod.d.ts +7 -7
  40. package/dist/federation/mod.js +15 -15
  41. package/dist/federation/mq.test.js +10 -10
  42. package/dist/federation/negotiation.test.d.ts +3 -0
  43. package/dist/federation/negotiation.test.js +28 -0
  44. package/dist/federation/retry.test.js +5 -5
  45. package/dist/federation/router.test.js +8 -8
  46. package/dist/federation/send.test.js +15 -15
  47. package/dist/{federation-CMX7WzeL.js → federation-D1U8YY9t.js} +3 -3
  48. package/dist/federation-H2_En3j5.cjs +244 -0
  49. package/dist/fixtures/media.example.com/avatars/test-avatar.jpg.json +6 -0
  50. package/dist/{http-CSYNybkH.js → http-BNOYnVsU.js} +2 -2
  51. package/dist/{http-B_pxY5of.js → http-BxbM8sEy.js} +6 -6
  52. package/dist/http-C7vbQwbz.cjs +826 -0
  53. package/dist/http-D-e6AFwR.d.cts +253 -0
  54. package/dist/{http-DqSNLFNY.d.ts → http-D6Uj2x2y.d.ts} +1 -1
  55. package/dist/{inbox-DoJlwIE4.js → inbox-BRru9pX3.js} +24 -7
  56. package/dist/{key-BOzqiGbl.js → key-1KXru8Ug.js} +2 -2
  57. package/dist/key-B3uag-rz.js +10 -0
  58. package/dist/key-BiBmb1Yy.cjs +10 -0
  59. package/dist/{key-DWWqeyaq.js → key-DK_nfU4I.js} +3 -3
  60. package/dist/key-Z6ceKnZC.cjs +290 -0
  61. package/dist/{key-Bif4eMj7.js → key-jyNTxCvK.js} +5 -5
  62. package/dist/{keycache--NYDYifd.js → keycache-CN61iGVj.js} +1 -1
  63. package/dist/{keys-MacD9GP6.js → keys-BPdFKgiy.js} +1 -1
  64. package/dist/kv-63Cil1MD.d.cts +81 -0
  65. package/dist/{ld-D9oTBLrs.js → ld-Dv8DNNAT.js} +2 -2
  66. package/dist/{lookup-Cz9VSYxw.js → lookup-BPviO8ij.js} +4 -4
  67. package/dist/lookup-hnMAAU5r.cjs +137 -0
  68. package/dist/{lookup-DuzrBbTj.js → lookup-pV0JOsuV.js} +21 -12
  69. package/dist/middleware-BmoOlgc1.cjs +4231 -0
  70. package/dist/{middleware-C0w2M0tb.js → middleware-Bz_A2jeJ.js} +87 -91
  71. package/dist/middleware-CI0-zw4U.js +26 -0
  72. package/dist/{middleware-CgNm3XOJ.js → middleware-DY9B2lL8.js} +57 -148
  73. package/dist/middleware-QNK-W-jE.cjs +17 -0
  74. package/dist/middleware-_vjt6FWU.js +17 -0
  75. package/dist/mod-8DMWKtQE.d.cts +80 -0
  76. package/dist/{mod-Drmz72EK.d.ts → mod-BhUKmBJD.d.ts} +2 -2
  77. package/dist/mod-C2tOeRkN.d.cts +1 -0
  78. package/dist/{mod-TFoH2Ql8.d.ts → mod-CerN_Sza.d.ts} +1 -1
  79. package/dist/mod-Cj1tHXBR.d.cts +102 -0
  80. package/dist/mod-CxkWO3Mg.d.cts +307 -0
  81. package/dist/{mod-RI3-KvUI.d.ts → mod-D6hQoxC5.d.ts} +2 -2
  82. package/dist/mod-Djzcw2ry.d.cts +266 -0
  83. package/dist/{mod-Cxt4Kpf6.d.ts → mod-DlU8ISoa.d.ts} +19 -1
  84. package/dist/mod-FZd39qVq.d.cts +1 -0
  85. package/dist/mod-jQ4OODsl.d.cts +113 -0
  86. package/dist/mod.cjs +150 -0
  87. package/dist/mod.d.cts +17 -0
  88. package/dist/mod.d.ts +10 -10
  89. package/dist/mod.js +20 -20
  90. package/dist/mq-B7R1Q-M5.d.cts +140 -0
  91. package/dist/negotiation-5NPJL6zp.js +71 -0
  92. package/dist/nodeinfo/client.test.js +10 -10
  93. package/dist/nodeinfo/handler.test.js +23 -22
  94. package/dist/nodeinfo/mod.cjs +11 -0
  95. package/dist/nodeinfo/mod.d.cts +5 -0
  96. package/dist/nodeinfo/mod.js +6 -6
  97. package/dist/nodeinfo/types.test.js +8 -8
  98. package/dist/nodeinfo-Co9lJrWl.cjs +4 -0
  99. package/dist/nodeinfo-DfycQ8Wf.js +4 -0
  100. package/dist/owner-BN_tO3cY.d.cts +67 -0
  101. package/dist/{owner-gB-pDKA-.js → owner-e3FYDhsk.js} +2 -2
  102. package/dist/{owner-CQPnQVtf.d.ts → owner-hd9lvQcP.d.ts} +2 -2
  103. package/dist/{proof-CScVkkIT.js → proof-6gFMwMNJ.js} +2 -2
  104. package/dist/proof-B-eqv0Ug.cjs +673 -0
  105. package/dist/{proof-VPikK_0V.js → proof-DfgvA3al.js} +6 -6
  106. package/dist/runtime/authdocloader.test.js +14 -14
  107. package/dist/runtime/docloader.test.js +9 -9
  108. package/dist/runtime/key.test.js +10 -10
  109. package/dist/runtime/langstr.test.js +8 -8
  110. package/dist/runtime/link.test.d.ts +3 -0
  111. package/dist/runtime/link.test.js +61 -0
  112. package/dist/runtime/mod.cjs +25 -0
  113. package/dist/runtime/mod.d.cts +6 -0
  114. package/dist/runtime/mod.d.ts +3 -3
  115. package/dist/runtime/mod.js +10 -10
  116. package/dist/runtime/multibase/multibase.test.js +8 -8
  117. package/dist/runtime/url.test.js +5 -5
  118. package/dist/runtime-C58AJWSv.cjs +4 -0
  119. package/dist/runtime-DPYEDf-o.js +4 -0
  120. package/dist/{send-flZ7H10-.js → send-Tl9NOnmO.js} +2 -2
  121. package/dist/sig/http.test.js +13 -13
  122. package/dist/sig/key.test.js +11 -11
  123. package/dist/sig/ld.test.js +10 -10
  124. package/dist/sig/mod.cjs +30 -0
  125. package/dist/sig/mod.d.cts +8 -0
  126. package/dist/sig/mod.d.ts +5 -5
  127. package/dist/sig/mod.js +10 -10
  128. package/dist/sig/owner.test.js +12 -12
  129. package/dist/sig/proof.test.js +12 -12
  130. package/dist/sig-ByHXzqUi.cjs +4 -0
  131. package/dist/sig-Cj3tk-ig.js +4 -0
  132. package/dist/testing/docloader.test.js +8 -8
  133. package/dist/testing/mod.d.ts +272 -0
  134. package/dist/testing/mod.js +3 -3
  135. package/dist/{testing-CdBSI4xF.js → testing-BWNCAbL-.js} +2 -2
  136. package/dist/{transformers-Dna8Fg7k.js → transformers-BFT6d7J5.js} +3 -3
  137. package/dist/transformers-CoBS-oFG.cjs +116 -0
  138. package/dist/{type-CAteiET0.js → type-C69ZBu7f.js} +7258 -2255
  139. package/dist/{types-Vo0qKhN7.js → types-DqxyTxOf.js} +4 -4
  140. package/dist/types-zqdWZh4O.cjs +315 -0
  141. package/dist/vocab/actor.test.js +10 -10
  142. package/dist/vocab/lookup.test.js +259 -9
  143. package/dist/vocab/mod.cjs +87 -0
  144. package/dist/vocab/mod.d.cts +6 -0
  145. package/dist/vocab/mod.d.ts +3 -3
  146. package/dist/vocab/mod.js +7 -7
  147. package/dist/vocab/type.test.js +3 -3
  148. package/dist/vocab/vocab.test.js +438 -14
  149. package/dist/vocab-B39-pFl9.cjs +291 -0
  150. package/dist/{vocab-SOE1ifCr.d.ts → vocab-BI0Ak5lL.d.ts} +290 -0
  151. package/dist/{vocab-89qErSc9.js → vocab-BWoeZsME.js} +26 -17
  152. package/dist/vocab-Dw1-yVGg.d.cts +14922 -0
  153. package/dist/webfinger/handler.test.js +23 -22
  154. package/dist/webfinger/lookup.test.js +9 -9
  155. package/dist/webfinger/mod.cjs +9 -0
  156. package/dist/webfinger/mod.d.cts +4 -0
  157. package/dist/webfinger/mod.js +6 -6
  158. package/dist/webfinger-BjOEdFPs.cjs +4 -0
  159. package/dist/webfinger-De_bU0iE.js +4 -0
  160. package/dist/x/cfworkers.cjs +100 -0
  161. package/dist/x/cfworkers.d.cts +59 -0
  162. package/dist/x/cfworkers.js +3 -3
  163. package/dist/x/cfworkers.test.js +8 -8
  164. package/dist/x/hono.cjs +61 -0
  165. package/dist/x/hono.d.cts +54 -0
  166. package/dist/x/hono.d.ts +6 -6
  167. package/dist/x/hono.js +3 -3
  168. package/dist/x/sveltekit.cjs +69 -0
  169. package/dist/x/sveltekit.d.cts +46 -0
  170. package/dist/x/sveltekit.d.ts +6 -6
  171. package/dist/x/sveltekit.js +3 -3
  172. package/package.json +68 -13
  173. package/dist/compat-Bb5myD13.js +0 -4
  174. package/dist/key-BteSu1lX.js +0 -10
  175. package/dist/middleware-CNfiKLIZ.js +0 -25
  176. package/dist/middleware-CnvU63kV.js +0 -17
  177. package/dist/nodeinfo-CyEbLjHs.js +0 -4
  178. package/dist/runtime-BSkOVUWM.js +0 -4
  179. package/dist/sig-BXJO--F9.js +0 -4
  180. package/dist/webfinger-C3GIyXIg.js +0 -4
  181. /package/dist/{assert_is_error-B035L3om.js → assert_is_error-BPGph1Jx.js} +0 -0
  182. /package/dist/{assert_not_equals-C80BG-_5.js → assert_not_equals-f3m3epl3.js} +0 -0
  183. /package/dist/{denokv-jZ0Z2h0M.js → denokv-Bv33Xxea.js} +0 -0
  184. /package/dist/{retry-CfF8Gn4d.js → retry-D4GJ670a.js} +0 -0
  185. /package/dist/{std__assert-DWivtrGR.js → std__assert-X-_kMxKM.js} +0 -0
@@ -1,8 +1,8 @@
1
1
 
2
- import { Temporal } from "@js-temporal/polyfill";
3
- import { URLPattern } from "urlpattern-polyfill";
4
-
5
- import { getUserAgent } from "./docloader-CkASOfZ5.js";
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
+ });
@@ -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-CAteiET0.js";
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-DuzrBbTj.js";
11
- import { getActorClassByTypeName, getActorHandle, getActorTypeName, isActor, normalizeActorHandle } from "../actor-CmRlosMl.js";
12
- import { test } from "../testing-CdBSI4xF.js";
13
- import { assertStrictEquals } from "../std__assert-DWivtrGR.js";
14
- import { assertFalse, assertRejects } from "../assert_rejects-7UF4R_Qs.js";
15
- import "../assert_is_error-B035L3om.js";
16
- import "../assert_not_equals-C80BG-_5.js";
17
- import { assertThrows } from "../assert_throws-53_pKeP3.js";
18
- import { esm_default } from "../esm-CXF1VoeR.js";
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-CAteiET0.js";
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-DuzrBbTj.js";
11
- import { mockDocumentLoader, test } from "../testing-CdBSI4xF.js";
12
- import "../std__assert-DWivtrGR.js";
13
- import "../assert_rejects-7UF4R_Qs.js";
14
- import "../assert_is_error-B035L3om.js";
15
- import "../assert_not_equals-C80BG-_5.js";
16
- import "../assert_throws-53_pKeP3.js";
17
- import { esm_default } from "../esm-CXF1VoeR.js";
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