@fedify/vocab-runtime 2.4.0-pr.936.41 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +4 -2
- package/dist/{tests/docloader-Ck8bcBir.mjs → contexts-BK1CqBR5.cjs} +164 -275
- package/dist/{tests/docloader-S_gQZ2tt.cjs → contexts-DPuJ4UYL.js} +160 -294
- package/dist/{docloader-C_dir7Xb.d.ts → docloader-CYwqh5Df.d.cts} +66 -2
- package/dist/{docloader-D2DTRiyA.d.cts → docloader-CYwqh5Df.d.ts} +66 -2
- package/dist/internal/jsonld-cache.cjs +70 -1
- package/dist/internal/jsonld-cache.d.cts +35 -2
- package/dist/internal/jsonld-cache.d.ts +35 -2
- package/dist/internal/jsonld-cache.js +67 -2
- package/dist/internal/portable-dereference.cjs +721 -0
- package/dist/internal/portable-dereference.d.cts +349 -0
- package/dist/internal/portable-dereference.d.ts +349 -0
- package/dist/internal/portable-dereference.js +702 -0
- package/dist/internal/signed-representation.cjs +378 -0
- package/dist/internal/signed-representation.d.cts +144 -0
- package/dist/internal/signed-representation.d.ts +144 -0
- package/dist/internal/signed-representation.js +371 -0
- package/dist/jsonld.cjs +2 -2
- package/dist/mod.cjs +711 -4527
- package/dist/mod.d.cts +400 -8
- package/dist/mod.d.ts +400 -9
- package/dist/mod.js +692 -4522
- package/dist/portable-DqtfLy_1.d.cts +160 -0
- package/dist/portable-DtWsu2yU.d.ts +160 -0
- package/dist/tests/body-CYiu9CO-.mjs +130 -0
- package/dist/tests/body-CkEwROsn.cjs +153 -0
- package/dist/tests/body.test.cjs +89 -0
- package/dist/tests/body.test.d.cts +1 -0
- package/dist/tests/body.test.d.mts +1 -0
- package/dist/tests/body.test.mjs +90 -0
- package/dist/tests/contexts-CIKsin4e.mjs +4512 -0
- package/dist/tests/contexts-DizzBjz4.cjs +4523 -0
- package/dist/tests/decimal.test.cjs +8 -7
- package/dist/tests/decimal.test.mjs +8 -6
- package/dist/tests/digest-3FeH2Y-Q.cjs +176 -0
- package/dist/tests/digest-COC7xDiQ.mjs +141 -0
- package/dist/tests/digest.test.cjs +102 -0
- package/dist/tests/digest.test.d.cts +1 -0
- package/dist/tests/digest.test.d.mts +1 -0
- package/dist/tests/digest.test.mjs +103 -0
- package/dist/tests/docloader-C3YFl6y4.cjs +396 -0
- package/dist/tests/docloader-CvmS4sVq.mjs +373 -0
- package/dist/tests/docloader.test.cjs +692 -39
- package/dist/tests/docloader.test.mjs +686 -34
- package/dist/tests/internal/multicodec.test.cjs +2 -3
- package/dist/tests/internal/multicodec.test.mjs +2 -2
- package/dist/tests/internal/portable-dereference.test.cjs +172 -0
- package/dist/tests/internal/portable-dereference.test.d.cts +1 -0
- package/dist/tests/internal/portable-dereference.test.d.mts +1 -0
- package/dist/tests/internal/portable-dereference.test.mjs +173 -0
- package/dist/tests/jsonld-cache-BPQmOZWD.mjs +342 -0
- package/dist/tests/jsonld-cache-C07AyNOY.cjs +397 -0
- package/dist/tests/jsonld-cache.test.cjs +101 -298
- package/dist/tests/jsonld-cache.test.mjs +94 -289
- package/dist/tests/{key-_wXwomh_.cjs → key-C-AYkdJJ.cjs} +11 -4
- package/dist/tests/{key-CDGDH_vC.mjs → key-C2Db_TAJ.mjs} +11 -3
- package/dist/tests/key.test.cjs +6 -5
- package/dist/tests/key.test.mjs +6 -4
- package/dist/tests/langstr.test.cjs +4 -4
- package/dist/tests/langstr.test.mjs +2 -2
- package/dist/tests/link.test.cjs +2 -3
- package/dist/tests/link.test.mjs +2 -2
- package/dist/tests/multibase/multibase.test.cjs +8 -9
- package/dist/tests/multibase/multibase.test.mjs +6 -6
- package/dist/tests/{multibase-Bz_UUDtL.cjs → multibase-B5Mea7Ip.cjs} +19 -2
- package/dist/tests/{multibase-B4bvakyA.mjs → multibase-BPnF_L4e.mjs} +12 -1
- package/dist/tests/portable-dereference-BNXtgg5U.cjs +278 -0
- package/dist/tests/portable-dereference-DRE5bz-l.mjs +249 -0
- package/dist/tests/portable-media-BLKllU56.cjs +153 -0
- package/dist/tests/portable-media-DRSGBORB.mjs +148 -0
- package/dist/tests/portable-media.test.cjs +222 -0
- package/dist/tests/portable-media.test.d.cts +1 -0
- package/dist/tests/portable-media.test.d.mts +1 -0
- package/dist/tests/portable-media.test.mjs +223 -0
- package/dist/tests/portable-workers.test.cjs +36 -0
- package/dist/tests/portable-workers.test.d.cts +2 -0
- package/dist/tests/portable-workers.test.d.mts +2 -0
- package/dist/tests/portable-workers.test.mjs +35 -0
- package/dist/tests/{request-uk51rkhO.cjs → request-8vPWtV1-.cjs} +10 -4
- package/dist/tests/{request-C8CaGwtt.mjs → request-DRaOaTqD.mjs} +8 -2
- package/dist/tests/request.test.cjs +7 -4
- package/dist/tests/request.test.mjs +5 -2
- package/dist/tests/signed-representation.test.cjs +588 -0
- package/dist/tests/signed-representation.test.d.cts +1 -0
- package/dist/tests/signed-representation.test.d.mts +1 -0
- package/dist/tests/signed-representation.test.mjs +589 -0
- package/dist/tests/temporal.test.cjs +1 -2
- package/dist/tests/temporal.test.mjs +1 -1
- package/dist/tests/url-BNakuZ8k.cjs +998 -0
- package/dist/tests/url-DMxmp7ZG.mjs +859 -0
- package/dist/tests/url.test.cjs +489 -6
- package/dist/tests/url.test.mjs +489 -5
- package/dist/url-DrGTR8yv.cjs +993 -0
- package/dist/url-Dzyp-NsC.js +860 -0
- package/package.json +27 -4
- package/scripts/test-bun.mjs +17 -0
- package/src/body.test.ts +125 -0
- package/src/body.ts +152 -0
- package/src/contexts/cid-v1.json +114 -0
- package/src/contexts/fep-22cd.json +21 -0
- package/src/contexts/miscellany.json +17 -0
- package/src/contexts.ts +39 -1
- package/src/digest.test.ts +220 -0
- package/src/digest.ts +229 -0
- package/src/docloader.test.ts +918 -27
- package/src/docloader.ts +320 -105
- package/src/internal/jsonld-cache.ts +97 -1
- package/src/internal/portable-dereference.test.ts +254 -0
- package/src/internal/portable-dereference.ts +1000 -0
- package/src/internal/signed-representation.ts +565 -0
- package/src/jsonld-cache.test.ts +89 -0
- package/src/key.test.ts +9 -0
- package/src/key.ts +11 -1
- package/src/mod.ts +28 -0
- package/src/multibase/multibase.test.ts +5 -5
- package/src/portable-media.test.ts +293 -0
- package/src/portable-media.ts +264 -0
- package/src/portable-workers.test.ts +81 -0
- package/src/portable.ts +180 -0
- package/src/preprocessor.ts +7 -0
- package/src/request.test.ts +10 -1
- package/src/request.ts +9 -1
- package/src/signed-representation.test.ts +338 -0
- package/src/url.test.ts +823 -3
- package/src/url.ts +700 -23
- package/tsdown.config.ts +2 -0
- package/dist/tests/url-CsOV_B_P.cjs +0 -482
- package/dist/tests/url-Du7RQQgP.mjs +0 -392
- package/dist/url-DD4F0ULf.cjs +0 -483
- package/dist/url-DGVbSVVi.js +0 -393
- /package/dist/{chunk-M78iaK0I.cjs → rolldown-runtime-B7lfambq.cjs} +0 -0
- /package/dist/tests/{langstr-CbAxaeEZ.cjs → langstr-C4Fl80ae.cjs} +0 -0
- /package/dist/tests/{langstr-Di5AvKpB.mjs → langstr-CQ26J_L7.mjs} +0 -0
- /package/dist/tests/{link-NUUWCdnK.mjs → link-Cevmc87v.mjs} +0 -0
- /package/dist/tests/{link-FguCydMA.cjs → link-DlKm8bEr.cjs} +0 -0
- /package/dist/tests/{multicodec-CxGVGa91.cjs → multicodec-CLRPeW4N.cjs} +0 -0
- /package/dist/tests/{multicodec-CyFp54fI.mjs → multicodec-CRIj_05H.mjs} +0 -0
- /package/dist/tests/{chunk-C2EiDwsr.cjs → rolldown-runtime-emK7D4bc.cjs} +0 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
const require_request = require("./request-8vPWtV1-.cjs");
|
|
2
|
+
const require_body = require("./body-CkEwROsn.cjs");
|
|
3
|
+
const require_contexts = require("./contexts-DizzBjz4.cjs");
|
|
4
|
+
const require_link = require("./link-DlKm8bEr.cjs");
|
|
5
|
+
const require_url = require("./url-BNakuZ8k.cjs");
|
|
6
|
+
let _logtape_logtape = require("@logtape/logtape");
|
|
7
|
+
let _opentelemetry_api = require("@opentelemetry/api");
|
|
8
|
+
//#region src/docloader.ts
|
|
9
|
+
const logger = (0, _logtape_logtape.getLogger)([
|
|
10
|
+
"fedify",
|
|
11
|
+
"runtime",
|
|
12
|
+
"docloader"
|
|
13
|
+
]);
|
|
14
|
+
const DEFAULT_MAX_REDIRECTION = 20;
|
|
15
|
+
const MAX_HTML_SIZE = 1024 * 1024;
|
|
16
|
+
const MAX_ERROR_BODY_SIZE = 1024 * 1024;
|
|
17
|
+
const DEFAULT_TIMEOUT = 1e4;
|
|
18
|
+
const MAX_TIMEOUT = 2147483647;
|
|
19
|
+
function createResponseMetadata(response) {
|
|
20
|
+
return new Response(null, {
|
|
21
|
+
headers: response.headers,
|
|
22
|
+
status: response.status,
|
|
23
|
+
statusText: response.statusText
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const NULL_BODY_STATUSES = /* @__PURE__ */ new Set([
|
|
27
|
+
204,
|
|
28
|
+
205,
|
|
29
|
+
304
|
|
30
|
+
]);
|
|
31
|
+
/**
|
|
32
|
+
* Reads the body of an error response while the document loader is still
|
|
33
|
+
* running, so that its timeout and `AbortSignal` also bound the read, and
|
|
34
|
+
* nothing reading {@link FetchError.response} later waits on the network.
|
|
35
|
+
* The body is kept byte for byte, unless it is too large or cannot be read;
|
|
36
|
+
* then only the status and headers are kept. A response whose status
|
|
37
|
+
* the `Response` constructor does not accept (e.g., 999) is kept as a clone
|
|
38
|
+
* whose body has been read in full; if its body is too large or cannot be
|
|
39
|
+
* read, no response is kept at all.
|
|
40
|
+
*/
|
|
41
|
+
async function bufferErrorResponse(response, url, signal) {
|
|
42
|
+
if (response.status < 200 || response.status > 599) {
|
|
43
|
+
const clone = response.clone();
|
|
44
|
+
try {
|
|
45
|
+
await require_body.readBoundedBytes(response, MAX_ERROR_BODY_SIZE, url);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
await clone.body?.cancel().catch(() => {});
|
|
48
|
+
if (signal?.aborted) throw error;
|
|
49
|
+
logger.debug("Failed to read the error response body from {url}: {error}", {
|
|
50
|
+
url,
|
|
51
|
+
error
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
return clone;
|
|
56
|
+
}
|
|
57
|
+
if (response.body == null || NULL_BODY_STATUSES.has(response.status)) return createResponseMetadata(response);
|
|
58
|
+
let body;
|
|
59
|
+
try {
|
|
60
|
+
body = await require_body.readBoundedBytes(response, MAX_ERROR_BODY_SIZE, url);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (signal?.aborted) throw error;
|
|
63
|
+
logger.debug("Failed to read the error response body from {url}: {error}", {
|
|
64
|
+
url,
|
|
65
|
+
error
|
|
66
|
+
});
|
|
67
|
+
return createResponseMetadata(response);
|
|
68
|
+
}
|
|
69
|
+
return new Response(body, {
|
|
70
|
+
headers: response.headers,
|
|
71
|
+
status: response.status,
|
|
72
|
+
statusText: response.statusText
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Gets a {@link RemoteDocument} from the given response.
|
|
77
|
+
* @param url The URL of the document to load.
|
|
78
|
+
* @param response The response to get the document from.
|
|
79
|
+
* @param fetch The function to fetch the document.
|
|
80
|
+
* @param options The options for loading the document.
|
|
81
|
+
* @returns The loaded remote document.
|
|
82
|
+
* @throws {FetchError} If the response is not OK.
|
|
83
|
+
* @internal
|
|
84
|
+
*/
|
|
85
|
+
async function getRemoteDocument(url, response, fetch, options) {
|
|
86
|
+
const documentUrl = response.url === "" ? url : response.url;
|
|
87
|
+
const docUrl = new URL(documentUrl);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
if (options?.suppressError) logger.warn("Failed to fetch document: {status} {url} {headers}", {
|
|
90
|
+
status: response.status,
|
|
91
|
+
url: documentUrl,
|
|
92
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
93
|
+
});
|
|
94
|
+
else logger.error("Failed to fetch document: {status} {url} {headers}", {
|
|
95
|
+
status: response.status,
|
|
96
|
+
url: documentUrl,
|
|
97
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
98
|
+
});
|
|
99
|
+
throw new require_request.FetchError(documentUrl, `HTTP ${response.status}: ${documentUrl}`, await bufferErrorResponse(response, documentUrl, options?.signal));
|
|
100
|
+
}
|
|
101
|
+
const contentType = response.headers.get("Content-Type");
|
|
102
|
+
const jsonLd = contentType == null || contentType === "application/activity+json" || contentType.startsWith("application/activity+json;") || contentType === "application/ld+json" || contentType.startsWith("application/ld+json;");
|
|
103
|
+
const linkHeader = response.headers.get("Link");
|
|
104
|
+
let contextUrl = null;
|
|
105
|
+
if (linkHeader != null) {
|
|
106
|
+
let link;
|
|
107
|
+
try {
|
|
108
|
+
link = new require_link.HttpHeaderLink(linkHeader);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
if (e instanceof SyntaxError) link = new require_link.HttpHeaderLink();
|
|
111
|
+
else throw e;
|
|
112
|
+
}
|
|
113
|
+
if (jsonLd) {
|
|
114
|
+
const entries = link.getByRel("http://www.w3.org/ns/json-ld#context");
|
|
115
|
+
for (const [uri, params] of entries) if ("type" in params && params.type === "application/ld+json") {
|
|
116
|
+
contextUrl = uri;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
const entries = link.getByRel("alternate");
|
|
121
|
+
for (const [uri, params] of entries) {
|
|
122
|
+
const altUri = new URL(uri, docUrl);
|
|
123
|
+
if ("type" in params && (params.type === "application/activity+json" || params.type === "application/ld+json" || params.type.startsWith("application/ld+json;")) && altUri.href !== docUrl.href) {
|
|
124
|
+
logger.debug("Found alternate document: {alternateUrl} from {url}", {
|
|
125
|
+
alternateUrl: altUri.href,
|
|
126
|
+
url: documentUrl
|
|
127
|
+
});
|
|
128
|
+
return await fetch(altUri.href, options);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
let document;
|
|
134
|
+
if (!jsonLd && (contentType === "text/html" || contentType?.startsWith("text/html;") || contentType === "application/xhtml+xml" || contentType?.startsWith("application/xhtml+xml;"))) {
|
|
135
|
+
const errorResponse = createResponseMetadata(response);
|
|
136
|
+
let html;
|
|
137
|
+
try {
|
|
138
|
+
html = await require_body.readBoundedText(response, MAX_HTML_SIZE, documentUrl);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (!(error instanceof require_body.BodyTooLargeError)) throw error;
|
|
141
|
+
logger.warn("HTML response too large, skipping alternate link discovery: {url}", {
|
|
142
|
+
url: documentUrl,
|
|
143
|
+
maxBytes: MAX_HTML_SIZE
|
|
144
|
+
});
|
|
145
|
+
throw new require_request.FetchError(documentUrl, `HTML document is too large to scan for an ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
146
|
+
}
|
|
147
|
+
const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
|
|
148
|
+
const attrPattern = /([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
149
|
+
let tagMatch;
|
|
150
|
+
while ((tagMatch = tagPattern.exec(html)) !== null) {
|
|
151
|
+
const tagContent = tagMatch[2];
|
|
152
|
+
let attrMatch;
|
|
153
|
+
const attribs = {};
|
|
154
|
+
attrPattern.lastIndex = 0;
|
|
155
|
+
while ((attrMatch = attrPattern.exec(tagContent)) !== null) {
|
|
156
|
+
const key = attrMatch[1].toLowerCase();
|
|
157
|
+
attribs[key] = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
|
|
158
|
+
}
|
|
159
|
+
if (attribs.rel === "alternate" && "type" in attribs && (attribs.type === "application/activity+json" || attribs.type === "application/ld+json" || attribs.type.startsWith("application/ld+json;")) && "href" in attribs && new URL(attribs.href, docUrl).href !== docUrl.href) {
|
|
160
|
+
logger.debug("Found alternate document: {alternateUrl} from {url}", {
|
|
161
|
+
alternateUrl: attribs.href,
|
|
162
|
+
url: documentUrl
|
|
163
|
+
});
|
|
164
|
+
return await fetch(new URL(attribs.href, docUrl).href, options);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
document = JSON.parse(html);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
171
|
+
throw new require_request.FetchError(documentUrl, `HTML document has no ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
172
|
+
}
|
|
173
|
+
} else document = JSON.parse(await require_body.readBoundedText(response, require_body.MAX_BODY_SIZE, documentUrl));
|
|
174
|
+
logger.debug("Fetched document: {status} {url} {headers}", {
|
|
175
|
+
status: response.status,
|
|
176
|
+
url: documentUrl,
|
|
177
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
178
|
+
});
|
|
179
|
+
return {
|
|
180
|
+
contextUrl,
|
|
181
|
+
document,
|
|
182
|
+
documentUrl
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Resolves {@link DocumentLoaderFactoryOptions.timeout} into milliseconds.
|
|
187
|
+
* @param timeout The timeout option. `undefined` means the default timeout,
|
|
188
|
+
* and `null` means no timeout.
|
|
189
|
+
* @returns The timeout in milliseconds, or `null` if it is turned off.
|
|
190
|
+
* @throws {RangeError} If the timeout is invalid.
|
|
191
|
+
* @internal
|
|
192
|
+
*/
|
|
193
|
+
function resolveDocumentLoaderTimeout(timeout) {
|
|
194
|
+
if (timeout === void 0) return DEFAULT_TIMEOUT;
|
|
195
|
+
if (timeout === null) return null;
|
|
196
|
+
if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0) throw new RangeError(`The document loader timeout must be a positive finite number of milliseconds, but got ${String(timeout)}.`);
|
|
197
|
+
const ms = Math.ceil(timeout);
|
|
198
|
+
if (ms > MAX_TIMEOUT) throw new RangeError(`The document loader timeout must not be greater than ${MAX_TIMEOUT} milliseconds, but got ${timeout}.`);
|
|
199
|
+
return ms;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Bounds each call of the given document loader by the given timeout.
|
|
203
|
+
* The timeout is combined with the caller's `signal`, and the combined
|
|
204
|
+
* signal is passed to the loader. The call settles no later than the
|
|
205
|
+
* timeout even if the loader is stuck in a step that cannot be aborted,
|
|
206
|
+
* e.g., a DNS lookup.
|
|
207
|
+
*
|
|
208
|
+
* A timed-out call throws a {@link FetchError} without a response, whose
|
|
209
|
+
* `cause` is a `DOMException` named `"TimeoutError"`. If the caller's
|
|
210
|
+
* signal is aborted, its reason is thrown instead.
|
|
211
|
+
* @param loader The document loader to bound.
|
|
212
|
+
* @param timeout The timeout in milliseconds, or `null` for no timeout.
|
|
213
|
+
* It is assumed to have been resolved by
|
|
214
|
+
* {@link resolveDocumentLoaderTimeout}.
|
|
215
|
+
* @returns The bounded document loader.
|
|
216
|
+
* @internal
|
|
217
|
+
*/
|
|
218
|
+
function withDocumentLoaderTimeout(loader, timeout) {
|
|
219
|
+
if (timeout == null) return loader;
|
|
220
|
+
return async (url, options) => {
|
|
221
|
+
const callerSignal = options?.signal;
|
|
222
|
+
callerSignal?.throwIfAborted();
|
|
223
|
+
const controller = new AbortController();
|
|
224
|
+
const timeoutReason = new DOMException(`The document loader timed out after ${timeout} ms.`, "TimeoutError");
|
|
225
|
+
let timedOut = false;
|
|
226
|
+
const timer = setTimeout(() => {
|
|
227
|
+
timedOut = true;
|
|
228
|
+
controller.abort(timeoutReason);
|
|
229
|
+
}, timeout);
|
|
230
|
+
const onCallerAbort = () => controller.abort(callerSignal?.reason);
|
|
231
|
+
callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
232
|
+
let onAbort;
|
|
233
|
+
const aborted = new Promise((_, reject) => {
|
|
234
|
+
onAbort = () => reject(controller.signal.reason);
|
|
235
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
236
|
+
});
|
|
237
|
+
const loading = loader(url, {
|
|
238
|
+
...options,
|
|
239
|
+
signal: controller.signal
|
|
240
|
+
});
|
|
241
|
+
loading.catch(() => {});
|
|
242
|
+
try {
|
|
243
|
+
return await Promise.race([loading, aborted]);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
if (callerSignal?.aborted) throw callerSignal.reason;
|
|
246
|
+
if (!timedOut) throw error;
|
|
247
|
+
logger[options?.suppressError ? "warn" : "error"]("Timed out after {timeout} ms while fetching document: {url}", {
|
|
248
|
+
timeout,
|
|
249
|
+
url
|
|
250
|
+
});
|
|
251
|
+
const fetchError = new require_request.FetchError(url, `Timed out after ${timeout} ms`);
|
|
252
|
+
fetchError.cause = timeoutReason;
|
|
253
|
+
throw fetchError;
|
|
254
|
+
} finally {
|
|
255
|
+
clearTimeout(timer);
|
|
256
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
257
|
+
if (onAbort != null) controller.signal.removeEventListener("abort", onAbort);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
|
|
263
|
+
* At most 20 HTTP redirects and alternate document links are followed in total
|
|
264
|
+
* per call. Revisiting a URL within that chain throws a {@link FetchError}.
|
|
265
|
+
* Each call times out after 10 seconds by default; see
|
|
266
|
+
* {@link DocumentLoaderFactoryOptions.timeout}.
|
|
267
|
+
*
|
|
268
|
+
* The created loader preloads the below frequently used contexts by default
|
|
269
|
+
* (unless `options.skipPreloadedContexts` is set to `true`):
|
|
270
|
+
*
|
|
271
|
+
* - <https://www.w3.org/ns/activitystreams>
|
|
272
|
+
* - <https://w3id.org/security/v1>
|
|
273
|
+
* - <https://w3id.org/security/data-integrity/v1>
|
|
274
|
+
* - <https://w3id.org/security/data-integrity/v2>
|
|
275
|
+
* - <https://www.w3.org/ns/did/v1>
|
|
276
|
+
* - <https://www.w3.org/ns/cid/v1>
|
|
277
|
+
* - <https://w3id.org/security/multikey/v1>
|
|
278
|
+
* - <https://w3id.org/fep/ef61>
|
|
279
|
+
* - <https://w3id.org/fep/7aa9>
|
|
280
|
+
* - <https://purl.archive.org/socialweb/webfinger>
|
|
281
|
+
* - <http://schema.org/>
|
|
282
|
+
* @param options Options for the document loader.
|
|
283
|
+
* @returns The document loader.
|
|
284
|
+
* @since 1.3.0
|
|
285
|
+
*/
|
|
286
|
+
function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, timeout, userAgent } = {}) {
|
|
287
|
+
const resolvedTimeout = resolveDocumentLoaderTimeout(timeout);
|
|
288
|
+
const tracer = _opentelemetry_api.trace.getTracerProvider().getTracer(require_request.name, require_request.version);
|
|
289
|
+
const maximumRedirection = maxRedirection ?? DEFAULT_MAX_REDIRECTION;
|
|
290
|
+
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
291
|
+
options?.signal?.throwIfAborted();
|
|
292
|
+
const currentUrl = new URL(url).href;
|
|
293
|
+
if (!skipPreloadedContexts && currentUrl in require_contexts.preloadedContexts) {
|
|
294
|
+
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|
|
295
|
+
return {
|
|
296
|
+
contextUrl: null,
|
|
297
|
+
document: require_contexts.preloadedContexts[currentUrl],
|
|
298
|
+
documentUrl: currentUrl
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (!allowPrivateAddress) {
|
|
302
|
+
try {
|
|
303
|
+
await require_url.validatePublicUrl(currentUrl);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (error instanceof require_url.UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
|
|
306
|
+
url: currentUrl,
|
|
307
|
+
error
|
|
308
|
+
});
|
|
309
|
+
else logger[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
|
|
310
|
+
url: currentUrl,
|
|
311
|
+
error
|
|
312
|
+
});
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
options?.signal?.throwIfAborted();
|
|
316
|
+
}
|
|
317
|
+
visited.add(currentUrl);
|
|
318
|
+
return await tracer.startActiveSpan("activitypub.fetch_document", {
|
|
319
|
+
kind: _opentelemetry_api.SpanKind.CLIENT,
|
|
320
|
+
attributes: { "url.full": currentUrl }
|
|
321
|
+
}, async (span) => {
|
|
322
|
+
try {
|
|
323
|
+
const request = require_request.createActivityPubRequest(currentUrl, { userAgent });
|
|
324
|
+
require_request.logRequest(logger, request);
|
|
325
|
+
const response = await fetch(request, {
|
|
326
|
+
redirect: "manual",
|
|
327
|
+
signal: options?.signal
|
|
328
|
+
});
|
|
329
|
+
span.setAttribute("http.response.status_code", response.status);
|
|
330
|
+
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
331
|
+
if (redirected >= maximumRedirection) {
|
|
332
|
+
logger[options?.suppressError ? "warn" : "error"]("Too many redirections ({redirections}) while fetching document.", {
|
|
333
|
+
redirections: redirected + 1,
|
|
334
|
+
url: currentUrl
|
|
335
|
+
});
|
|
336
|
+
throw new require_request.FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
|
|
337
|
+
}
|
|
338
|
+
const redirectUrl = new URL(response.headers.get("Location"), response.url === "" ? currentUrl : response.url).href;
|
|
339
|
+
span.setAttribute("http.redirect.url", redirectUrl);
|
|
340
|
+
if (visited.has(redirectUrl)) {
|
|
341
|
+
logger[options?.suppressError ? "warn" : "error"]("Detected a redirect loop while fetching document: {url} -> {redirectUrl}", {
|
|
342
|
+
url: currentUrl,
|
|
343
|
+
redirectUrl
|
|
344
|
+
});
|
|
345
|
+
throw new require_request.FetchError(currentUrl, `Redirect loop detected: ${redirectUrl}`);
|
|
346
|
+
}
|
|
347
|
+
return await load(redirectUrl, options, redirected + 1, visited);
|
|
348
|
+
}
|
|
349
|
+
const result = await getRemoteDocument(currentUrl, response, async (alternateUrl) => {
|
|
350
|
+
options?.signal?.throwIfAborted();
|
|
351
|
+
if (redirected >= DEFAULT_MAX_REDIRECTION) throw new require_request.FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
|
|
352
|
+
if (visited.has(alternateUrl)) throw new require_request.FetchError(currentUrl, `Redirect loop detected: ${alternateUrl}`);
|
|
353
|
+
return await load(alternateUrl, options, redirected + 1, visited);
|
|
354
|
+
}, options);
|
|
355
|
+
span.setAttribute("docloader.document_url", result.documentUrl);
|
|
356
|
+
if (result.contextUrl != null) span.setAttribute("docloader.context_url", result.contextUrl);
|
|
357
|
+
return result;
|
|
358
|
+
} catch (error) {
|
|
359
|
+
span.recordException(error);
|
|
360
|
+
span.setStatus({
|
|
361
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
362
|
+
message: String(error)
|
|
363
|
+
});
|
|
364
|
+
throw error;
|
|
365
|
+
} finally {
|
|
366
|
+
span.end();
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
return withDocumentLoaderTimeout((url, options) => load(url, options), resolvedTimeout);
|
|
371
|
+
}
|
|
372
|
+
//#endregion
|
|
373
|
+
Object.defineProperty(exports, "getDocumentLoader", {
|
|
374
|
+
enumerable: true,
|
|
375
|
+
get: function() {
|
|
376
|
+
return getDocumentLoader;
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
Object.defineProperty(exports, "getRemoteDocument", {
|
|
380
|
+
enumerable: true,
|
|
381
|
+
get: function() {
|
|
382
|
+
return getRemoteDocument;
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
Object.defineProperty(exports, "resolveDocumentLoaderTimeout", {
|
|
386
|
+
enumerable: true,
|
|
387
|
+
get: function() {
|
|
388
|
+
return resolveDocumentLoaderTimeout;
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
Object.defineProperty(exports, "withDocumentLoaderTimeout", {
|
|
392
|
+
enumerable: true,
|
|
393
|
+
get: function() {
|
|
394
|
+
return withDocumentLoaderTimeout;
|
|
395
|
+
}
|
|
396
|
+
});
|