@fedify/vocab-runtime 2.4.0-dev.2164 → 2.4.0-dev.2190
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 +1 -1
- package/dist/{docloader-ClQraSWr.d.cts → docloader-C63enuyr.d.cts} +53 -1
- package/dist/{docloader-ClQraSWr.d.ts → docloader-C63enuyr.d.ts} +53 -1
- package/dist/internal/jsonld-cache.d.cts +1 -1
- package/dist/internal/jsonld-cache.d.ts +1 -1
- package/dist/internal/portable-dereference.d.cts +2 -2
- package/dist/internal/portable-dereference.d.ts +2 -2
- package/dist/mod.cjs +205 -16
- package/dist/mod.d.cts +3 -3
- package/dist/mod.d.ts +3 -3
- package/dist/mod.js +204 -17
- package/dist/{portable-ilkFPcXa.d.cts → portable-Cux3IA20.d.cts} +1 -1
- package/dist/{portable-DRtvOxTb.d.ts → portable-DBA1FS_L.d.ts} +1 -1
- package/dist/tests/{body-CHcG33yp.cjs → body-CmZdqiw3.cjs} +60 -1
- package/dist/tests/{body-DJHT_hfs.mjs → body-DMNbCBhz.mjs} +55 -2
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/decimal.test.cjs +1 -1
- package/dist/tests/decimal.test.mjs +1 -1
- package/dist/tests/{docloader-BHHtcRTx.mjs → docloader-6iodlx7G.mjs} +152 -18
- package/dist/tests/{docloader-D_hTp-cz.cjs → docloader-B0omnjRS.cjs} +163 -17
- package/dist/tests/docloader.test.cjs +265 -2
- package/dist/tests/docloader.test.mjs +266 -3
- package/dist/tests/{request-CmTmbJyw.cjs → request-BU0mCgOL.cjs} +1 -1
- package/dist/tests/{request-GqYdtV1u.mjs → request-CGmX5n4v.mjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/package.json +1 -1
- package/src/body.ts +57 -0
- package/src/docloader.test.ts +341 -3
- package/src/docloader.ts +205 -5
- package/src/mod.ts +2 -0
package/deno.json
CHANGED
|
@@ -146,6 +146,28 @@ interface DocumentLoaderFactoryOptions {
|
|
|
146
146
|
* @since 2.2.0
|
|
147
147
|
*/
|
|
148
148
|
maxRedirection?: number;
|
|
149
|
+
/**
|
|
150
|
+
* The timeout in milliseconds for each call of the created document
|
|
151
|
+
* loader. The timeout is shared by all the steps of a call, including
|
|
152
|
+
* URL validation, every HTTP redirect and alternate document link it
|
|
153
|
+
* follows, retries, and reading the response body; it does not restart
|
|
154
|
+
* for each of them. It does not interrupt synchronous work such as
|
|
155
|
+
* parsing a document that has already been received.
|
|
156
|
+
*
|
|
157
|
+
* When a call times out, the loader throws a {@link FetchError} without
|
|
158
|
+
* a {@link FetchError.response}, whose `cause` is a `DOMException` named
|
|
159
|
+
* `"TimeoutError"`. An `AbortSignal` passed through
|
|
160
|
+
* {@link DocumentLoaderOptions.signal} still cancels a call; in that case
|
|
161
|
+
* the loader throws the signal's reason as before.
|
|
162
|
+
*
|
|
163
|
+
* Fractional values are rounded up. Set it to `null` to turn off the
|
|
164
|
+
* timeout.
|
|
165
|
+
* @default `10000` (10 seconds)
|
|
166
|
+
* @throws {RangeError} If the value is not a positive finite number or is
|
|
167
|
+
* greater than 2,147,483,647 (about 24.8 days).
|
|
168
|
+
* @since 2.4.0
|
|
169
|
+
*/
|
|
170
|
+
timeout?: number | null;
|
|
149
171
|
}
|
|
150
172
|
/**
|
|
151
173
|
* A factory function that creates an authenticated {@link DocumentLoader} for
|
|
@@ -173,6 +195,33 @@ type AuthenticatedDocumentLoaderFactory = (identity: {
|
|
|
173
195
|
*/
|
|
174
196
|
declare function getRemoteDocument(url: string, response: Response, fetch: (url: string, options?: DocumentLoaderOptions) => Promise<RemoteDocument>, options?: DocumentLoaderOptions): Promise<RemoteDocument>;
|
|
175
197
|
/**
|
|
198
|
+
* Resolves {@link DocumentLoaderFactoryOptions.timeout} into milliseconds.
|
|
199
|
+
* @param timeout The timeout option. `undefined` means the default timeout,
|
|
200
|
+
* and `null` means no timeout.
|
|
201
|
+
* @returns The timeout in milliseconds, or `null` if it is turned off.
|
|
202
|
+
* @throws {RangeError} If the timeout is invalid.
|
|
203
|
+
* @internal
|
|
204
|
+
*/
|
|
205
|
+
declare function resolveDocumentLoaderTimeout(timeout: number | null | undefined): number | null;
|
|
206
|
+
/**
|
|
207
|
+
* Bounds each call of the given document loader by the given timeout.
|
|
208
|
+
* The timeout is combined with the caller's `signal`, and the combined
|
|
209
|
+
* signal is passed to the loader. The call settles no later than the
|
|
210
|
+
* timeout even if the loader is stuck in a step that cannot be aborted,
|
|
211
|
+
* e.g., a DNS lookup.
|
|
212
|
+
*
|
|
213
|
+
* A timed-out call throws a {@link FetchError} without a response, whose
|
|
214
|
+
* `cause` is a `DOMException` named `"TimeoutError"`. If the caller's
|
|
215
|
+
* signal is aborted, its reason is thrown instead.
|
|
216
|
+
* @param loader The document loader to bound.
|
|
217
|
+
* @param timeout The timeout in milliseconds, or `null` for no timeout.
|
|
218
|
+
* It is assumed to have been resolved by
|
|
219
|
+
* {@link resolveDocumentLoaderTimeout}.
|
|
220
|
+
* @returns The bounded document loader.
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
declare function withDocumentLoaderTimeout(loader: DocumentLoader, timeout: number | null): DocumentLoader;
|
|
224
|
+
/**
|
|
176
225
|
* Options for {@link getDocumentLoader}.
|
|
177
226
|
* @since 1.3.0
|
|
178
227
|
*/
|
|
@@ -186,6 +235,8 @@ interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions {
|
|
|
186
235
|
* Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
|
|
187
236
|
* At most 20 HTTP redirects and alternate document links are followed in total
|
|
188
237
|
* per call. Revisiting a URL within that chain throws a {@link FetchError}.
|
|
238
|
+
* Each call times out after 10 seconds by default; see
|
|
239
|
+
* {@link DocumentLoaderFactoryOptions.timeout}.
|
|
189
240
|
*
|
|
190
241
|
* The created loader preloads the below frequently used contexts by default
|
|
191
242
|
* (unless `options.skipPreloadedContexts` is set to `true`):
|
|
@@ -209,7 +260,8 @@ declare function getDocumentLoader({
|
|
|
209
260
|
allowPrivateAddress,
|
|
210
261
|
maxRedirection,
|
|
211
262
|
skipPreloadedContexts,
|
|
263
|
+
timeout,
|
|
212
264
|
userAgent
|
|
213
265
|
}?: GetDocumentLoaderOptions): DocumentLoader;
|
|
214
266
|
//#endregion
|
|
215
|
-
export { DocumentLoaderOptions as a, getDocumentLoader as c,
|
|
267
|
+
export { logRequest as _, DocumentLoaderOptions as a, getDocumentLoader as c, withDocumentLoaderTimeout as d, CreateRequestOptions as f, getUserAgent as g, createActivityPubRequest as h, DocumentLoaderFactoryOptions as i, getRemoteDocument as l, GetUserAgentOptions as m, DocumentLoader as n, GetDocumentLoaderOptions as o, FetchError as p, DocumentLoaderFactory as r, RemoteDocument as s, AuthenticatedDocumentLoaderFactory as t, resolveDocumentLoaderTimeout as u };
|
|
@@ -146,6 +146,28 @@ interface DocumentLoaderFactoryOptions {
|
|
|
146
146
|
* @since 2.2.0
|
|
147
147
|
*/
|
|
148
148
|
maxRedirection?: number;
|
|
149
|
+
/**
|
|
150
|
+
* The timeout in milliseconds for each call of the created document
|
|
151
|
+
* loader. The timeout is shared by all the steps of a call, including
|
|
152
|
+
* URL validation, every HTTP redirect and alternate document link it
|
|
153
|
+
* follows, retries, and reading the response body; it does not restart
|
|
154
|
+
* for each of them. It does not interrupt synchronous work such as
|
|
155
|
+
* parsing a document that has already been received.
|
|
156
|
+
*
|
|
157
|
+
* When a call times out, the loader throws a {@link FetchError} without
|
|
158
|
+
* a {@link FetchError.response}, whose `cause` is a `DOMException` named
|
|
159
|
+
* `"TimeoutError"`. An `AbortSignal` passed through
|
|
160
|
+
* {@link DocumentLoaderOptions.signal} still cancels a call; in that case
|
|
161
|
+
* the loader throws the signal's reason as before.
|
|
162
|
+
*
|
|
163
|
+
* Fractional values are rounded up. Set it to `null` to turn off the
|
|
164
|
+
* timeout.
|
|
165
|
+
* @default `10000` (10 seconds)
|
|
166
|
+
* @throws {RangeError} If the value is not a positive finite number or is
|
|
167
|
+
* greater than 2,147,483,647 (about 24.8 days).
|
|
168
|
+
* @since 2.4.0
|
|
169
|
+
*/
|
|
170
|
+
timeout?: number | null;
|
|
149
171
|
}
|
|
150
172
|
/**
|
|
151
173
|
* A factory function that creates an authenticated {@link DocumentLoader} for
|
|
@@ -173,6 +195,33 @@ type AuthenticatedDocumentLoaderFactory = (identity: {
|
|
|
173
195
|
*/
|
|
174
196
|
declare function getRemoteDocument(url: string, response: Response, fetch: (url: string, options?: DocumentLoaderOptions) => Promise<RemoteDocument>, options?: DocumentLoaderOptions): Promise<RemoteDocument>;
|
|
175
197
|
/**
|
|
198
|
+
* Resolves {@link DocumentLoaderFactoryOptions.timeout} into milliseconds.
|
|
199
|
+
* @param timeout The timeout option. `undefined` means the default timeout,
|
|
200
|
+
* and `null` means no timeout.
|
|
201
|
+
* @returns The timeout in milliseconds, or `null` if it is turned off.
|
|
202
|
+
* @throws {RangeError} If the timeout is invalid.
|
|
203
|
+
* @internal
|
|
204
|
+
*/
|
|
205
|
+
declare function resolveDocumentLoaderTimeout(timeout: number | null | undefined): number | null;
|
|
206
|
+
/**
|
|
207
|
+
* Bounds each call of the given document loader by the given timeout.
|
|
208
|
+
* The timeout is combined with the caller's `signal`, and the combined
|
|
209
|
+
* signal is passed to the loader. The call settles no later than the
|
|
210
|
+
* timeout even if the loader is stuck in a step that cannot be aborted,
|
|
211
|
+
* e.g., a DNS lookup.
|
|
212
|
+
*
|
|
213
|
+
* A timed-out call throws a {@link FetchError} without a response, whose
|
|
214
|
+
* `cause` is a `DOMException` named `"TimeoutError"`. If the caller's
|
|
215
|
+
* signal is aborted, its reason is thrown instead.
|
|
216
|
+
* @param loader The document loader to bound.
|
|
217
|
+
* @param timeout The timeout in milliseconds, or `null` for no timeout.
|
|
218
|
+
* It is assumed to have been resolved by
|
|
219
|
+
* {@link resolveDocumentLoaderTimeout}.
|
|
220
|
+
* @returns The bounded document loader.
|
|
221
|
+
* @internal
|
|
222
|
+
*/
|
|
223
|
+
declare function withDocumentLoaderTimeout(loader: DocumentLoader, timeout: number | null): DocumentLoader;
|
|
224
|
+
/**
|
|
176
225
|
* Options for {@link getDocumentLoader}.
|
|
177
226
|
* @since 1.3.0
|
|
178
227
|
*/
|
|
@@ -186,6 +235,8 @@ interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions {
|
|
|
186
235
|
* Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
|
|
187
236
|
* At most 20 HTTP redirects and alternate document links are followed in total
|
|
188
237
|
* per call. Revisiting a URL within that chain throws a {@link FetchError}.
|
|
238
|
+
* Each call times out after 10 seconds by default; see
|
|
239
|
+
* {@link DocumentLoaderFactoryOptions.timeout}.
|
|
189
240
|
*
|
|
190
241
|
* The created loader preloads the below frequently used contexts by default
|
|
191
242
|
* (unless `options.skipPreloadedContexts` is set to `true`):
|
|
@@ -209,7 +260,8 @@ declare function getDocumentLoader({
|
|
|
209
260
|
allowPrivateAddress,
|
|
210
261
|
maxRedirection,
|
|
211
262
|
skipPreloadedContexts,
|
|
263
|
+
timeout,
|
|
212
264
|
userAgent
|
|
213
265
|
}?: GetDocumentLoaderOptions): DocumentLoader;
|
|
214
266
|
//#endregion
|
|
215
|
-
export { DocumentLoaderOptions as a, getDocumentLoader as c,
|
|
267
|
+
export { logRequest as _, DocumentLoaderOptions as a, getDocumentLoader as c, withDocumentLoaderTimeout as d, CreateRequestOptions as f, getUserAgent as g, createActivityPubRequest as h, DocumentLoaderFactoryOptions as i, getRemoteDocument as l, GetUserAgentOptions as m, DocumentLoader as n, GetDocumentLoaderOptions as o, FetchError as p, DocumentLoaderFactory as r, RemoteDocument as s, AuthenticatedDocumentLoaderFactory as t, resolveDocumentLoaderTimeout as u };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { n as DocumentLoader, s as RemoteDocument } from "../docloader-
|
|
3
|
-
import { r as PortableObjectVerifier } from "../portable-
|
|
2
|
+
import { n as DocumentLoader, s as RemoteDocument } from "../docloader-C63enuyr.cjs";
|
|
3
|
+
import { r as PortableObjectVerifier } from "../portable-Cux3IA20.cjs";
|
|
4
4
|
import { Span, TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/internal/portable-dereference.d.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { n as DocumentLoader, s as RemoteDocument } from "../docloader-
|
|
3
|
-
import { r as PortableObjectVerifier } from "../portable-
|
|
2
|
+
import { n as DocumentLoader, s as RemoteDocument } from "../docloader-C63enuyr.js";
|
|
3
|
+
import { r as PortableObjectVerifier } from "../portable-DBA1FS_L.js";
|
|
4
4
|
import { Span, TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/internal/portable-dereference.d.ts
|
package/dist/mod.cjs
CHANGED
|
@@ -16,7 +16,7 @@ let _multiformats_base_x = require("@multiformats/base-x");
|
|
|
16
16
|
_multiformats_base_x = require_rolldown_runtime.__toESM(_multiformats_base_x, 1);
|
|
17
17
|
//#region deno.json
|
|
18
18
|
var name = "@fedify/vocab-runtime";
|
|
19
|
-
var version = "2.4.0-dev.
|
|
19
|
+
var version = "2.4.0-dev.2190+be3a8be0";
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/request.ts
|
|
22
22
|
/**
|
|
@@ -167,6 +167,59 @@ async function readBoundedText(message, maxBytes, url) {
|
|
|
167
167
|
reader?.releaseLock();
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Reads raw bytes while limiting bytes received from the body stream.
|
|
172
|
+
* Unlike {@link readBoundedText}, the bytes are returned as they were
|
|
173
|
+
* received, without decoding.
|
|
174
|
+
* @param message The response or request to read.
|
|
175
|
+
* @param maxBytes The maximum number of decoded bytes to read.
|
|
176
|
+
* @param url The URL to include in errors and logs.
|
|
177
|
+
* @returns The body bytes.
|
|
178
|
+
* @throws {BodyTooLargeError} If the body exceeds the limit.
|
|
179
|
+
* @internal
|
|
180
|
+
*/
|
|
181
|
+
async function readBoundedBytes(message, maxBytes, url) {
|
|
182
|
+
validateBodySizeLimit(maxBytes);
|
|
183
|
+
const reader = message.body?.getReader();
|
|
184
|
+
const tooLarge = () => {
|
|
185
|
+
(0, _logtape_logtape.getLogger)([
|
|
186
|
+
"fedify",
|
|
187
|
+
"runtime",
|
|
188
|
+
"body"
|
|
189
|
+
]).warn("Body from {url} exceeds the limit of {maxBytes} bytes.", {
|
|
190
|
+
url: url.toString(),
|
|
191
|
+
maxBytes
|
|
192
|
+
});
|
|
193
|
+
throw new BodyTooLargeError(url, maxBytes);
|
|
194
|
+
};
|
|
195
|
+
try {
|
|
196
|
+
const length = message.headers.get("Content-Length");
|
|
197
|
+
const encoding = message.headers.get("Content-Encoding");
|
|
198
|
+
if ((encoding == null || encoding.toLowerCase() === "identity") && length != null && /^\d+$/.test(length) && Number(length) > maxBytes) tooLarge();
|
|
199
|
+
if (reader == null) return /* @__PURE__ */ new Uint8Array(0);
|
|
200
|
+
const chunks = [];
|
|
201
|
+
let size = 0;
|
|
202
|
+
while (true) {
|
|
203
|
+
const { done, value } = await reader.read();
|
|
204
|
+
if (done) break;
|
|
205
|
+
size += value.byteLength;
|
|
206
|
+
if (size > maxBytes) tooLarge();
|
|
207
|
+
chunks.push(value);
|
|
208
|
+
}
|
|
209
|
+
const bytes = new Uint8Array(size);
|
|
210
|
+
let offset = 0;
|
|
211
|
+
for (const chunk of chunks) {
|
|
212
|
+
bytes.set(chunk, offset);
|
|
213
|
+
offset += chunk.byteLength;
|
|
214
|
+
}
|
|
215
|
+
return bytes;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (reader != null) reader.cancel(error).catch(() => {});
|
|
218
|
+
throw error;
|
|
219
|
+
} finally {
|
|
220
|
+
reader?.releaseLock();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
170
223
|
//#endregion
|
|
171
224
|
//#region src/link.ts
|
|
172
225
|
const parametersNeedLowerCase = ["rel", "type"];
|
|
@@ -357,6 +410,9 @@ const logger = (0, _logtape_logtape.getLogger)([
|
|
|
357
410
|
]);
|
|
358
411
|
const DEFAULT_MAX_REDIRECTION = 20;
|
|
359
412
|
const MAX_HTML_SIZE = 1024 * 1024;
|
|
413
|
+
const MAX_ERROR_BODY_SIZE = 1024 * 1024;
|
|
414
|
+
const DEFAULT_TIMEOUT = 1e4;
|
|
415
|
+
const MAX_TIMEOUT = 2147483647;
|
|
360
416
|
function createResponseMetadata(response) {
|
|
361
417
|
return new Response(null, {
|
|
362
418
|
headers: response.headers,
|
|
@@ -364,6 +420,55 @@ function createResponseMetadata(response) {
|
|
|
364
420
|
statusText: response.statusText
|
|
365
421
|
});
|
|
366
422
|
}
|
|
423
|
+
const NULL_BODY_STATUSES = /* @__PURE__ */ new Set([
|
|
424
|
+
204,
|
|
425
|
+
205,
|
|
426
|
+
304
|
|
427
|
+
]);
|
|
428
|
+
/**
|
|
429
|
+
* Reads the body of an error response while the document loader is still
|
|
430
|
+
* running, so that its timeout and `AbortSignal` also bound the read, and
|
|
431
|
+
* nothing reading {@link FetchError.response} later waits on the network.
|
|
432
|
+
* The body is kept byte for byte, unless it is too large or cannot be read;
|
|
433
|
+
* then only the status and headers are kept. A response whose status
|
|
434
|
+
* the `Response` constructor does not accept (e.g., 999) is kept as a clone
|
|
435
|
+
* whose body has been read in full; if its body is too large or cannot be
|
|
436
|
+
* read, no response is kept at all.
|
|
437
|
+
*/
|
|
438
|
+
async function bufferErrorResponse(response, url, signal) {
|
|
439
|
+
if (response.status < 200 || response.status > 599) {
|
|
440
|
+
const clone = response.clone();
|
|
441
|
+
try {
|
|
442
|
+
await readBoundedBytes(response, MAX_ERROR_BODY_SIZE, url);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
await clone.body?.cancel().catch(() => {});
|
|
445
|
+
if (signal?.aborted) throw error;
|
|
446
|
+
logger.debug("Failed to read the error response body from {url}: {error}", {
|
|
447
|
+
url,
|
|
448
|
+
error
|
|
449
|
+
});
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
return clone;
|
|
453
|
+
}
|
|
454
|
+
if (response.body == null || NULL_BODY_STATUSES.has(response.status)) return createResponseMetadata(response);
|
|
455
|
+
let body;
|
|
456
|
+
try {
|
|
457
|
+
body = await readBoundedBytes(response, MAX_ERROR_BODY_SIZE, url);
|
|
458
|
+
} catch (error) {
|
|
459
|
+
if (signal?.aborted) throw error;
|
|
460
|
+
logger.debug("Failed to read the error response body from {url}: {error}", {
|
|
461
|
+
url,
|
|
462
|
+
error
|
|
463
|
+
});
|
|
464
|
+
return createResponseMetadata(response);
|
|
465
|
+
}
|
|
466
|
+
return new Response(body, {
|
|
467
|
+
headers: response.headers,
|
|
468
|
+
status: response.status,
|
|
469
|
+
statusText: response.statusText
|
|
470
|
+
});
|
|
471
|
+
}
|
|
367
472
|
/**
|
|
368
473
|
* Gets a {@link RemoteDocument} from the given response.
|
|
369
474
|
* @param url The URL of the document to load.
|
|
@@ -388,7 +493,7 @@ async function getRemoteDocument(url, response, fetch, options) {
|
|
|
388
493
|
url: documentUrl,
|
|
389
494
|
headers: Object.fromEntries(response.headers.entries())
|
|
390
495
|
});
|
|
391
|
-
throw new FetchError(documentUrl, `HTTP ${response.status}: ${documentUrl}`, response
|
|
496
|
+
throw new FetchError(documentUrl, `HTTP ${response.status}: ${documentUrl}`, await bufferErrorResponse(response, documentUrl, options?.signal));
|
|
392
497
|
}
|
|
393
498
|
const contentType = response.headers.get("Content-Type");
|
|
394
499
|
const jsonLd = contentType == null || contentType === "application/activity+json" || contentType.startsWith("application/activity+json;") || contentType === "application/ld+json" || contentType.startsWith("application/ld+json;");
|
|
@@ -475,9 +580,87 @@ async function getRemoteDocument(url, response, fetch, options) {
|
|
|
475
580
|
};
|
|
476
581
|
}
|
|
477
582
|
/**
|
|
583
|
+
* Resolves {@link DocumentLoaderFactoryOptions.timeout} into milliseconds.
|
|
584
|
+
* @param timeout The timeout option. `undefined` means the default timeout,
|
|
585
|
+
* and `null` means no timeout.
|
|
586
|
+
* @returns The timeout in milliseconds, or `null` if it is turned off.
|
|
587
|
+
* @throws {RangeError} If the timeout is invalid.
|
|
588
|
+
* @internal
|
|
589
|
+
*/
|
|
590
|
+
function resolveDocumentLoaderTimeout(timeout) {
|
|
591
|
+
if (timeout === void 0) return DEFAULT_TIMEOUT;
|
|
592
|
+
if (timeout === null) return null;
|
|
593
|
+
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)}.`);
|
|
594
|
+
const ms = Math.ceil(timeout);
|
|
595
|
+
if (ms > MAX_TIMEOUT) throw new RangeError(`The document loader timeout must not be greater than ${MAX_TIMEOUT} milliseconds, but got ${timeout}.`);
|
|
596
|
+
return ms;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Bounds each call of the given document loader by the given timeout.
|
|
600
|
+
* The timeout is combined with the caller's `signal`, and the combined
|
|
601
|
+
* signal is passed to the loader. The call settles no later than the
|
|
602
|
+
* timeout even if the loader is stuck in a step that cannot be aborted,
|
|
603
|
+
* e.g., a DNS lookup.
|
|
604
|
+
*
|
|
605
|
+
* A timed-out call throws a {@link FetchError} without a response, whose
|
|
606
|
+
* `cause` is a `DOMException` named `"TimeoutError"`. If the caller's
|
|
607
|
+
* signal is aborted, its reason is thrown instead.
|
|
608
|
+
* @param loader The document loader to bound.
|
|
609
|
+
* @param timeout The timeout in milliseconds, or `null` for no timeout.
|
|
610
|
+
* It is assumed to have been resolved by
|
|
611
|
+
* {@link resolveDocumentLoaderTimeout}.
|
|
612
|
+
* @returns The bounded document loader.
|
|
613
|
+
* @internal
|
|
614
|
+
*/
|
|
615
|
+
function withDocumentLoaderTimeout(loader, timeout) {
|
|
616
|
+
if (timeout == null) return loader;
|
|
617
|
+
return async (url, options) => {
|
|
618
|
+
const callerSignal = options?.signal;
|
|
619
|
+
callerSignal?.throwIfAborted();
|
|
620
|
+
const controller = new AbortController();
|
|
621
|
+
const timeoutReason = new DOMException(`The document loader timed out after ${timeout} ms.`, "TimeoutError");
|
|
622
|
+
let timedOut = false;
|
|
623
|
+
const timer = setTimeout(() => {
|
|
624
|
+
timedOut = true;
|
|
625
|
+
controller.abort(timeoutReason);
|
|
626
|
+
}, timeout);
|
|
627
|
+
const onCallerAbort = () => controller.abort(callerSignal?.reason);
|
|
628
|
+
callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
629
|
+
let onAbort;
|
|
630
|
+
const aborted = new Promise((_, reject) => {
|
|
631
|
+
onAbort = () => reject(controller.signal.reason);
|
|
632
|
+
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
633
|
+
});
|
|
634
|
+
const loading = loader(url, {
|
|
635
|
+
...options,
|
|
636
|
+
signal: controller.signal
|
|
637
|
+
});
|
|
638
|
+
loading.catch(() => {});
|
|
639
|
+
try {
|
|
640
|
+
return await Promise.race([loading, aborted]);
|
|
641
|
+
} catch (error) {
|
|
642
|
+
if (callerSignal?.aborted) throw callerSignal.reason;
|
|
643
|
+
if (!timedOut) throw error;
|
|
644
|
+
logger[options?.suppressError ? "warn" : "error"]("Timed out after {timeout} ms while fetching document: {url}", {
|
|
645
|
+
timeout,
|
|
646
|
+
url
|
|
647
|
+
});
|
|
648
|
+
const fetchError = new FetchError(url, `Timed out after ${timeout} ms`);
|
|
649
|
+
fetchError.cause = timeoutReason;
|
|
650
|
+
throw fetchError;
|
|
651
|
+
} finally {
|
|
652
|
+
clearTimeout(timer);
|
|
653
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
654
|
+
if (onAbort != null) controller.signal.removeEventListener("abort", onAbort);
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
478
659
|
* Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
|
|
479
660
|
* At most 20 HTTP redirects and alternate document links are followed in total
|
|
480
661
|
* per call. Revisiting a URL within that chain throws a {@link FetchError}.
|
|
662
|
+
* Each call times out after 10 seconds by default; see
|
|
663
|
+
* {@link DocumentLoaderFactoryOptions.timeout}.
|
|
481
664
|
*
|
|
482
665
|
* The created loader preloads the below frequently used contexts by default
|
|
483
666
|
* (unless `options.skipPreloadedContexts` is set to `true`):
|
|
@@ -497,7 +680,8 @@ async function getRemoteDocument(url, response, fetch, options) {
|
|
|
497
680
|
* @returns The document loader.
|
|
498
681
|
* @since 1.3.0
|
|
499
682
|
*/
|
|
500
|
-
function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, userAgent } = {}) {
|
|
683
|
+
function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, timeout, userAgent } = {}) {
|
|
684
|
+
const resolvedTimeout = resolveDocumentLoaderTimeout(timeout);
|
|
501
685
|
const tracer = _opentelemetry_api.trace.getTracerProvider().getTracer(name, version);
|
|
502
686
|
const maximumRedirection = maxRedirection ?? DEFAULT_MAX_REDIRECTION;
|
|
503
687
|
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
@@ -511,18 +695,21 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
|
|
|
511
695
|
documentUrl: currentUrl
|
|
512
696
|
};
|
|
513
697
|
}
|
|
514
|
-
if (!allowPrivateAddress)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
url
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
698
|
+
if (!allowPrivateAddress) {
|
|
699
|
+
try {
|
|
700
|
+
await require_url.validatePublicUrl(currentUrl);
|
|
701
|
+
} catch (error) {
|
|
702
|
+
if (error instanceof require_url.UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
|
|
703
|
+
url: currentUrl,
|
|
704
|
+
error
|
|
705
|
+
});
|
|
706
|
+
else logger[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
|
|
707
|
+
url: currentUrl,
|
|
708
|
+
error
|
|
709
|
+
});
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
options?.signal?.throwIfAborted();
|
|
526
713
|
}
|
|
527
714
|
visited.add(currentUrl);
|
|
528
715
|
return await tracer.startActiveSpan("activitypub.fetch_document", {
|
|
@@ -577,7 +764,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
|
|
|
577
764
|
}
|
|
578
765
|
});
|
|
579
766
|
}
|
|
580
|
-
return (url, options) => load(url, options);
|
|
767
|
+
return withDocumentLoaderTimeout((url, options) => load(url, options), resolvedTimeout);
|
|
581
768
|
}
|
|
582
769
|
//#endregion
|
|
583
770
|
//#region src/internal/multicodec.ts
|
|
@@ -1436,7 +1623,9 @@ exports.parseHashlink = parseHashlink;
|
|
|
1436
1623
|
exports.parseIri = require_url.parseIri;
|
|
1437
1624
|
exports.parseJsonLdId = require_url.parseJsonLdId;
|
|
1438
1625
|
exports.preloadedContexts = require_contexts.preloadedContexts;
|
|
1626
|
+
exports.resolveDocumentLoaderTimeout = resolveDocumentLoaderTimeout;
|
|
1439
1627
|
exports.toCompatibleEf61Id = require_url.toCompatibleEf61Id;
|
|
1440
1628
|
exports.validatePublicUrl = require_url.validatePublicUrl;
|
|
1441
1629
|
exports.verifyDigestMultibase = verifyDigestMultibase;
|
|
1442
1630
|
exports.verifyHashlink = verifyHashlink;
|
|
1631
|
+
exports.withDocumentLoaderTimeout = withDocumentLoaderTimeout;
|
package/dist/mod.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { a as DocumentLoaderOptions, c as getDocumentLoader, d as
|
|
3
|
-
import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-
|
|
2
|
+
import { _ as logRequest, a as DocumentLoaderOptions, c as getDocumentLoader, d as withDocumentLoaderTimeout, f as CreateRequestOptions, g as getUserAgent, h as createActivityPubRequest, i as DocumentLoaderFactoryOptions, l as getRemoteDocument, m as GetUserAgentOptions, n as DocumentLoader, o as GetDocumentLoaderOptions, p as FetchError, r as DocumentLoaderFactory, s as RemoteDocument, t as AuthenticatedDocumentLoaderFactory, u as resolveDocumentLoaderTimeout } from "./docloader-C63enuyr.cjs";
|
|
3
|
+
import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-Cux3IA20.cjs";
|
|
4
4
|
import { TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/contexts.d.ts
|
|
@@ -567,4 +567,4 @@ declare function isValidPublicIPv4Address(address: string): boolean;
|
|
|
567
567
|
declare function isValidPublicIPv6Address(address: string): boolean;
|
|
568
568
|
declare function expandIPv6Address(address: string): string;
|
|
569
569
|
//#endregion
|
|
570
|
-
export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type ParsedDigestMultibase, type ParsedHashlink, type PortableObjectReferrer, type PortableObjectVerification, type PortableObjectVerifier, type PortableObjectVerifierOptions, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, computeDigestMultibase, createActivityPubRequest, createHashlink, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isGatewayUrl, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseDigestMultibase, parseGatewayUrl, parseHashlink, parseIri, parseJsonLdId, preloadedContexts, toCompatibleEf61Id, validatePublicUrl, verifyDigestMultibase, verifyHashlink };
|
|
570
|
+
export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type ParsedDigestMultibase, type ParsedHashlink, type PortableObjectReferrer, type PortableObjectVerification, type PortableObjectVerifier, type PortableObjectVerifierOptions, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, computeDigestMultibase, createActivityPubRequest, createHashlink, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isGatewayUrl, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseDigestMultibase, parseGatewayUrl, parseHashlink, parseIri, parseJsonLdId, preloadedContexts, resolveDocumentLoaderTimeout, toCompatibleEf61Id, validatePublicUrl, verifyDigestMultibase, verifyHashlink, withDocumentLoaderTimeout };
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import { a as DocumentLoaderOptions, c as getDocumentLoader, d as
|
|
3
|
-
import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-
|
|
2
|
+
import { _ as logRequest, a as DocumentLoaderOptions, c as getDocumentLoader, d as withDocumentLoaderTimeout, f as CreateRequestOptions, g as getUserAgent, h as createActivityPubRequest, i as DocumentLoaderFactoryOptions, l as getRemoteDocument, m as GetUserAgentOptions, n as DocumentLoader, o as GetDocumentLoaderOptions, p as FetchError, r as DocumentLoaderFactory, s as RemoteDocument, t as AuthenticatedDocumentLoaderFactory, u as resolveDocumentLoaderTimeout } from "./docloader-C63enuyr.js";
|
|
3
|
+
import { i as PortableObjectVerifierOptions, n as PortableObjectVerification, r as PortableObjectVerifier, t as PortableObjectReferrer } from "./portable-DBA1FS_L.js";
|
|
4
4
|
import { TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
//#region src/contexts.d.ts
|
|
6
6
|
declare const preloadedContexts: Record<string, unknown>;
|
|
@@ -566,4 +566,4 @@ declare function isValidPublicIPv4Address(address: string): boolean;
|
|
|
566
566
|
declare function isValidPublicIPv6Address(address: string): boolean;
|
|
567
567
|
declare function expandIPv6Address(address: string): string;
|
|
568
568
|
//#endregion
|
|
569
|
-
export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type ParsedDigestMultibase, type ParsedHashlink, type PortableObjectReferrer, type PortableObjectVerification, type PortableObjectVerifier, type PortableObjectVerifierOptions, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, computeDigestMultibase, createActivityPubRequest, createHashlink, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isGatewayUrl, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseDigestMultibase, parseGatewayUrl, parseHashlink, parseIri, parseJsonLdId, preloadedContexts, toCompatibleEf61Id, validatePublicUrl, verifyDigestMultibase, verifyHashlink };
|
|
569
|
+
export { type AuthenticatedDocumentLoaderFactory, type CreateRequestOptions, type Decimal, type DidKeyVerificationMethod, type DocumentLoader, type DocumentLoaderFactory, type DocumentLoaderFactoryOptions, type DocumentLoaderOptions, FetchError, type GetDocumentLoaderOptions, type GetUserAgentOptions, type Json, LanguageString, type ParsedDigestMultibase, type ParsedHashlink, type PortableObjectReferrer, type PortableObjectVerification, type PortableObjectVerifier, type PortableObjectVerifierOptions, type PropertyPreprocessor, type PropertyPreprocessorContext, type RemoteDocument, UrlError, arePortableUrisEqual, canParseDecimal, canonicalizePortableUri, computeDigestMultibase, createActivityPubRequest, createHashlink, decodeMultibase, encodeMultibase, encodingFromBaseData, expandIPv6Address, exportDidKey, exportMultibaseKey, exportSpki, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, getRemoteDocument, getUserAgent, haveSameFe34Origin, haveSameIriOrigin, importDidKey, importMultibaseKey, importPem, importPkcs1, importSpki, isDecimal, isGatewayUrl, isValidPublicIPv4Address, isValidPublicIPv6Address, logRequest, parseDecimal, parseDidKeyVerificationMethod, parseDigestMultibase, parseGatewayUrl, parseHashlink, parseIri, parseJsonLdId, preloadedContexts, resolveDocumentLoaderTimeout, toCompatibleEf61Id, validatePublicUrl, verifyDigestMultibase, verifyHashlink, withDocumentLoaderTimeout };
|