@stll/fetch 0.1.1 → 0.3.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/README.md +15 -10
- package/dist/body-reader.d.ts +20 -0
- package/dist/body-reader.js +77 -0
- package/dist/{index-nwAjbSQV.d.ts → index-pEynxKdA.d.ts} +2 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -79
- package/dist/publisher.d.ts +1 -3
- package/dist/publisher.js +2 -15
- package/dist/race-abort-DhS8oTc3.js +15 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -6,26 +6,31 @@
|
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { fetchWithTimeout } from "@stll/fetch";
|
|
9
|
+
import { readBodyWithIdleDeadline } from "@stll/fetch/body-reader";
|
|
9
10
|
|
|
10
11
|
// Abort when no response headers arrive within 5 seconds.
|
|
11
12
|
const response = await fetchWithTimeout(url, {
|
|
12
13
|
timeout: { type: "headers", ms: 5_000 },
|
|
13
14
|
});
|
|
14
15
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
timeout: { type: "idle", ms: 10_000 },
|
|
16
|
+
// Bound headers separately, then observe chunks in the caller's own read.
|
|
17
|
+
const download = await fetchWithTimeout(url, {
|
|
18
|
+
timeout: { type: "headers", ms: 10_000 },
|
|
19
19
|
signal: controller.signal, // optional; either signal aborts the request
|
|
20
20
|
});
|
|
21
|
+
const bytes = await readBodyWithIdleDeadline(download, {
|
|
22
|
+
idleMs: 10_000,
|
|
23
|
+
signal: controller.signal,
|
|
24
|
+
});
|
|
21
25
|
```
|
|
22
26
|
|
|
23
|
-
The timeout is required by the type. `headers` bounds the wait for the
|
|
24
|
-
response; `
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
The timeout is required by the type. `headers` bounds only the wait for the
|
|
28
|
+
response; `total` bounds the request and its body consumption through the
|
|
29
|
+
transport's own signal. Both policies return the transport's response
|
|
30
|
+
unchanged. `readBodyWithIdleDeadline` consumes the caller's own body reader,
|
|
31
|
+
restarts its deadline after every chunk, and cancels a stalled body with a
|
|
32
|
+
`TimeoutError` `DOMException`. The caller's `signal` and a `Request`'s own
|
|
33
|
+
signal still abort the request.
|
|
29
34
|
`timeoutMs: number` remains the legacy numeric form of the total policy.
|
|
30
35
|
|
|
31
36
|
`createFetchWithTimeout(fetcher)` returns the same function over another
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const BodyByteLimitError_base: import("better-result").TaggedErrorClass<"BodyByteLimitError">;
|
|
2
|
+
export declare class BodyByteLimitError extends BodyByteLimitError_base<{
|
|
3
|
+
message: string;
|
|
4
|
+
limit: number;
|
|
5
|
+
}> {}
|
|
6
|
+
type BodyIdleDeadlineOptions = {
|
|
7
|
+
idleMs: number;
|
|
8
|
+
maxBytes?: number | undefined;
|
|
9
|
+
signal?: AbortSignal | undefined;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Yields response chunks while bounding the wait for each successive chunk.
|
|
13
|
+
* @yields Each byte chunk from the transport body.
|
|
14
|
+
*/
|
|
15
|
+
export declare function iterateBodyWithIdleDeadline(response: Response, { idleMs, maxBytes, signal }: BodyIdleDeadlineOptions): AsyncGenerator<Uint8Array, void, void>;
|
|
16
|
+
/** Reads a response body while bounding the wait for each successive chunk. */
|
|
17
|
+
export declare const readBodyWithIdleDeadline: (response: Response, options: BodyIdleDeadlineOptions) => Promise<Uint8Array>;
|
|
18
|
+
export declare const readBodyTextWithIdleDeadline: (response: Response, options: BodyIdleDeadlineOptions) => Promise<string>;
|
|
19
|
+
export declare const readBodyJsonWithIdleDeadline: (response: Response, options: BodyIdleDeadlineOptions) => Promise<unknown>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { t as raceAbort } from "./race-abort-DhS8oTc3.js";
|
|
2
|
+
import { TaggedError } from "better-result";
|
|
3
|
+
// src/body-reader.ts
|
|
4
|
+
var BodyByteLimitError = class extends TaggedError("BodyByteLimitError") {};
|
|
5
|
+
const invalidIdleMs = (idleMs) => {
|
|
6
|
+
if (!Number.isFinite(idleMs) || idleMs < 0 || idleMs > 2147483647) return new DOMException("Invalid body idle deadline duration", "NotSupportedError");
|
|
7
|
+
};
|
|
8
|
+
const responseReader = (body) => body.getReader();
|
|
9
|
+
/**
|
|
10
|
+
* Yields response chunks while bounding the wait for each successive chunk.
|
|
11
|
+
* @yields Each byte chunk from the transport body.
|
|
12
|
+
*/
|
|
13
|
+
async function* iterateBodyWithIdleDeadline(response, { idleMs, maxBytes, signal }) {
|
|
14
|
+
const invalidDuration = invalidIdleMs(idleMs);
|
|
15
|
+
if (invalidDuration !== void 0) await Promise.reject(invalidDuration);
|
|
16
|
+
if (signal?.aborted === true) {
|
|
17
|
+
response.body?.cancel(signal.reason).catch((cancellationError) => {});
|
|
18
|
+
signal.throwIfAborted();
|
|
19
|
+
}
|
|
20
|
+
if (maxBytes !== void 0 && (!Number.isSafeInteger(maxBytes) || maxBytes < 0)) await Promise.reject(new DOMException("Invalid response body byte limit", "NotSupportedError"));
|
|
21
|
+
const body = response.body;
|
|
22
|
+
if (body === null) return;
|
|
23
|
+
const reader = responseReader(body);
|
|
24
|
+
let exitReason;
|
|
25
|
+
let byteLength = 0;
|
|
26
|
+
try {
|
|
27
|
+
for (;;) {
|
|
28
|
+
if (signal?.aborted === true) exitReason = signal.reason;
|
|
29
|
+
signal?.throwIfAborted();
|
|
30
|
+
const deadline = new AbortController();
|
|
31
|
+
const timeoutError = new DOMException("Response body stalled", "TimeoutError");
|
|
32
|
+
const timer = setTimeout(() => deadline.abort(timeoutError), idleMs);
|
|
33
|
+
const combined = signal === void 0 ? deadline.signal : AbortSignal.any([signal, deadline.signal]);
|
|
34
|
+
let next;
|
|
35
|
+
try {
|
|
36
|
+
next = await raceAbort(reader.read(), combined);
|
|
37
|
+
} finally {
|
|
38
|
+
exitReason ??= combined.reason;
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
}
|
|
41
|
+
if (next.done) return;
|
|
42
|
+
byteLength += next.value.byteLength;
|
|
43
|
+
if (maxBytes !== void 0 && byteLength > maxBytes) {
|
|
44
|
+
const limitError = new BodyByteLimitError({
|
|
45
|
+
message: `Response body exceeds the ${maxBytes}-byte limit`,
|
|
46
|
+
limit: maxBytes
|
|
47
|
+
});
|
|
48
|
+
exitReason = limitError;
|
|
49
|
+
await Promise.reject(limitError);
|
|
50
|
+
}
|
|
51
|
+
yield next.value;
|
|
52
|
+
}
|
|
53
|
+
} finally {
|
|
54
|
+
reader.cancel(exitReason).then(() => reader.releaseLock()).catch((cancellationError) => {
|
|
55
|
+
reader.releaseLock();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Reads a response body while bounding the wait for each successive chunk. */
|
|
60
|
+
const readBodyWithIdleDeadline = async (response, options) => {
|
|
61
|
+
const chunks = [];
|
|
62
|
+
let byteLength = 0;
|
|
63
|
+
for await (const chunk of iterateBodyWithIdleDeadline(response, options)) {
|
|
64
|
+
chunks.push(chunk);
|
|
65
|
+
byteLength += chunk.byteLength;
|
|
66
|
+
}
|
|
67
|
+
const bytes = new Uint8Array(byteLength);
|
|
68
|
+
let offset = 0;
|
|
69
|
+
for (const chunk of chunks) {
|
|
70
|
+
bytes.set(chunk, offset);
|
|
71
|
+
offset += chunk.byteLength;
|
|
72
|
+
}
|
|
73
|
+
return bytes;
|
|
74
|
+
};
|
|
75
|
+
const readBodyTextWithIdleDeadline = async (response, options) => new TextDecoder().decode(await readBodyWithIdleDeadline(response, options));
|
|
76
|
+
const readBodyJsonWithIdleDeadline = async (response, options) => JSON.parse(await readBodyTextWithIdleDeadline(response, options));
|
|
77
|
+
export { BodyByteLimitError, iterateBodyWithIdleDeadline, readBodyJsonWithIdleDeadline, readBodyTextWithIdleDeadline, readBodyWithIdleDeadline };
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
//#region src/connection-failure.d.ts
|
|
2
1
|
declare const isConnectionFailure: (cause: unknown) => boolean;
|
|
3
|
-
//#endregion
|
|
4
|
-
//#region src/index.d.ts
|
|
5
2
|
type FetchTimeout = {
|
|
6
3
|
type: "total";
|
|
7
4
|
ms: number;
|
|
8
5
|
} | {
|
|
9
6
|
type: "headers";
|
|
10
7
|
ms: number;
|
|
11
|
-
} | {
|
|
12
|
-
type: "idle";
|
|
13
|
-
ms: number;
|
|
14
8
|
};
|
|
15
9
|
type TransportRequestInit = RequestInit & {
|
|
16
|
-
|
|
10
|
+
/** `false` turns off the runtime's own idle limit; the signal owns the deadline. */
|
|
11
|
+
timeout?: FetchTimeout | false;
|
|
17
12
|
timeoutMs?: number;
|
|
18
13
|
};
|
|
19
14
|
type Fetcher = (input: string | URL | Request, init?: TransportRequestInit) => Promise<Response>;
|
|
@@ -31,5 +26,4 @@ type FetchWithTimeoutInit = Omit<RequestInit, "signal"> & {
|
|
|
31
26
|
declare const createFetchWithTimeout: (fetcher: Fetcher) => (input: string | URL | Request, init: FetchWithTimeoutInit) => Promise<Response>;
|
|
32
27
|
/** Uses the current global fetch implementation at request time. */
|
|
33
28
|
declare const fetchWithTimeout: (input: string | URL | Request, init: FetchWithTimeoutInit) => Promise<Response>;
|
|
34
|
-
//#endregion
|
|
35
29
|
export { createFetchWithTimeout as a, TransportRequestInit as i, FetchWithTimeoutInit as n, fetchWithTimeout as o, Fetcher as r, isConnectionFailure as s, FetchTimeout as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createFetchWithTimeout, i as TransportRequestInit, n as FetchWithTimeoutInit, o as fetchWithTimeout, r as Fetcher, s as isConnectionFailure, t as FetchTimeout } from "./index-
|
|
1
|
+
import { a as createFetchWithTimeout, i as TransportRequestInit, n as FetchWithTimeoutInit, o as fetchWithTimeout, r as Fetcher, s as isConnectionFailure, t as FetchTimeout } from "./index-pEynxKdA.js";
|
|
2
2
|
export { FetchTimeout, FetchWithTimeoutInit, Fetcher, TransportRequestInit, createFetchWithTimeout, fetchWithTimeout, isConnectionFailure };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/connection-failure.ts
|
|
2
2
|
const CONNECTION_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
3
3
|
"ConnectionRefused",
|
|
4
4
|
"FailedToOpenSocket",
|
|
@@ -48,8 +48,7 @@ const isConnectionFailure = (cause) => {
|
|
|
48
48
|
if (codes.length > 0) return codes.some((code) => CONNECTION_ERROR_CODES.has(code) || code.startsWith("ERR_SSL_") || code.startsWith("ERR_TLS_"));
|
|
49
49
|
return messages.some((message) => /^(?:Unable to connect|Failed to fetch|fetch failed|NetworkError|Load failed|TLS handshake failed|DNS lookup failed)/iu.test(message));
|
|
50
50
|
};
|
|
51
|
-
|
|
52
|
-
//#region src/index.ts
|
|
51
|
+
// src/index.ts
|
|
53
52
|
const executeFetchWithTimeout = async (fetcher, input, { timeout, timeoutMs, signal, ...init }) => {
|
|
54
53
|
const callerSignals = [signal, input instanceof Request ? input.signal : void 0].filter((candidate) => candidate !== void 0);
|
|
55
54
|
const activeFetcher = fetcher ?? globalThis.fetch;
|
|
@@ -58,10 +57,9 @@ const executeFetchWithTimeout = async (fetcher, input, { timeout, timeoutMs, sig
|
|
|
58
57
|
if (timeout === void 0 || timeout.type === "total") return await activeFetcher(input, {
|
|
59
58
|
...init,
|
|
60
59
|
signal: AbortSignal.any([...callerSignals, AbortSignal.timeout(duration)]),
|
|
61
|
-
|
|
60
|
+
timeout: false,
|
|
62
61
|
timeoutMs: duration
|
|
63
62
|
});
|
|
64
|
-
const policy = timeout;
|
|
65
63
|
const controller = new AbortController();
|
|
66
64
|
const combined = AbortSignal.any([...callerSignals, controller.signal]);
|
|
67
65
|
const expire = () => controller.abort(new DOMException("Fetch timed out", "TimeoutError"));
|
|
@@ -70,92 +68,21 @@ const executeFetchWithTimeout = async (fetcher, input, { timeout, timeoutMs, sig
|
|
|
70
68
|
clearTimeout(timer);
|
|
71
69
|
timer = void 0;
|
|
72
70
|
};
|
|
73
|
-
|
|
74
|
-
clear();
|
|
75
|
-
timer = setTimeout(expire, duration);
|
|
76
|
-
};
|
|
77
|
-
reset();
|
|
71
|
+
timer = setTimeout(expire, duration);
|
|
78
72
|
const response = await Promise.resolve().then(async () => await activeFetcher(input, {
|
|
79
73
|
...init,
|
|
80
74
|
signal: combined,
|
|
81
|
-
timeout
|
|
75
|
+
timeout,
|
|
82
76
|
timeoutMs: duration
|
|
83
77
|
})).finally(clear);
|
|
84
78
|
if (combined.aborted) {
|
|
85
79
|
await Promise.allSettled([response.body?.cancel(combined.reason)]);
|
|
86
80
|
throw combined.reason;
|
|
87
81
|
}
|
|
88
|
-
|
|
89
|
-
const reader = response.body.getReader();
|
|
90
|
-
let stopped = false;
|
|
91
|
-
let cancellation;
|
|
92
|
-
let abort;
|
|
93
|
-
const finish = () => {
|
|
94
|
-
stopped = true;
|
|
95
|
-
clear();
|
|
96
|
-
combined.removeEventListener("abort", abort);
|
|
97
|
-
};
|
|
98
|
-
const body = new ReadableStream({
|
|
99
|
-
start(stream) {
|
|
100
|
-
abort = () => {
|
|
101
|
-
if (stopped) return;
|
|
102
|
-
finish();
|
|
103
|
-
stream.error(combined.reason);
|
|
104
|
-
cancellation = reader.cancel(combined.reason).then(() => reader.releaseLock(), () => reader.releaseLock());
|
|
105
|
-
};
|
|
106
|
-
combined.addEventListener("abort", abort, { once: true });
|
|
107
|
-
if (combined.aborted) {
|
|
108
|
-
abort();
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
async pull(stream) {
|
|
113
|
-
if (stopped) {
|
|
114
|
-
await cancellation;
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
reset();
|
|
118
|
-
await reader.read().finally(clear).then(({ done, value }) => {
|
|
119
|
-
if (stopped) return;
|
|
120
|
-
if (done) {
|
|
121
|
-
finish();
|
|
122
|
-
reader.releaseLock();
|
|
123
|
-
stream.close();
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
stream.enqueue(value);
|
|
127
|
-
}, (error) => {
|
|
128
|
-
if (stopped) return;
|
|
129
|
-
finish();
|
|
130
|
-
reader.releaseLock();
|
|
131
|
-
stream.error(error);
|
|
132
|
-
});
|
|
133
|
-
await cancellation;
|
|
134
|
-
},
|
|
135
|
-
async cancel(reason) {
|
|
136
|
-
if (stopped) {
|
|
137
|
-
await cancellation;
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
finish();
|
|
141
|
-
await reader.cancel(reason).finally(() => reader.releaseLock());
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
const wrapped = new Response(body, {
|
|
145
|
-
status: response.status,
|
|
146
|
-
statusText: response.statusText,
|
|
147
|
-
headers: response.headers
|
|
148
|
-
});
|
|
149
|
-
Object.defineProperties(wrapped, {
|
|
150
|
-
url: { value: response.url },
|
|
151
|
-
redirected: { value: response.redirected },
|
|
152
|
-
type: { value: response.type }
|
|
153
|
-
});
|
|
154
|
-
return wrapped;
|
|
82
|
+
return response;
|
|
155
83
|
};
|
|
156
84
|
/** Creates a wrapper with the supplied transport and an explicit timeout policy. */
|
|
157
85
|
const createFetchWithTimeout = (fetcher) => async (input, init) => await executeFetchWithTimeout(fetcher, input, init);
|
|
158
86
|
/** Uses the current global fetch implementation at request time. */
|
|
159
87
|
const fetchWithTimeout = async (input, init) => await executeFetchWithTimeout(void 0, input, init);
|
|
160
|
-
//#endregion
|
|
161
88
|
export { createFetchWithTimeout, fetchWithTimeout, isConnectionFailure };
|
package/dist/publisher.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Result } from "better-result";
|
|
2
|
-
import { i as TransportRequestInit } from "./index-
|
|
3
|
-
//#region src/publisher.d.ts
|
|
2
|
+
import { i as TransportRequestInit } from "./index-pEynxKdA.js";
|
|
4
3
|
export type PublisherPolicy = {
|
|
5
4
|
type: "obey";
|
|
6
5
|
} | {
|
|
@@ -121,5 +120,4 @@ type ReadBoundedBodyOptions = {
|
|
|
121
120
|
};
|
|
122
121
|
export declare const readBoundedBody: (response: Response, { maxBytes, signal }: ReadBoundedBodyOptions) => Promise<Result<Uint8Array, PublisherBodyLimitError>>;
|
|
123
122
|
export declare const createPolitePublisherFetch: <Target extends PublisherTarget>({ validateTarget, fetcher, clock, robotsTtlMs, robotsRetryMs, robotsLoadTimeoutMs, defaultMinimumIntervalMs, onTelemetry }: PublisherFetcherOptions<Target>) => (input: string | URL, options?: PublisherFetchOptions) => Promise<Result<PublisherFetchResult, PublisherPolicyError>>;
|
|
124
|
-
//#endregion
|
|
125
123
|
export {};
|
package/dist/publisher.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { t as raceAbort } from "./race-abort-DhS8oTc3.js";
|
|
1
2
|
import { Result, TaggedError } from "better-result";
|
|
2
|
-
|
|
3
|
+
// src/publisher.ts
|
|
3
4
|
const ROBOTS_MAX_BYTES = 524288;
|
|
4
5
|
const ROBOTS_TTL_MS = 36e5;
|
|
5
6
|
const ROBOTS_RETRY_MS = 6e4;
|
|
@@ -193,19 +194,6 @@ const evaluatePublisherRobots = (groups, productToken, path) => {
|
|
|
193
194
|
crawlDelayMs: Math.max(0, ...selected.map(({ crawlDelayMs }) => crawlDelayMs ?? 0))
|
|
194
195
|
};
|
|
195
196
|
};
|
|
196
|
-
const abortReason = (signal) => signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError");
|
|
197
|
-
const raceAbort = async (work, signal) => {
|
|
198
|
-
if (!signal) return await work;
|
|
199
|
-
const { promise: aborted, reject } = Promise.withResolvers();
|
|
200
|
-
const onAbort = () => reject(abortReason(signal));
|
|
201
|
-
if (signal.aborted) onAbort();
|
|
202
|
-
else signal.addEventListener("abort", onAbort, { once: true });
|
|
203
|
-
try {
|
|
204
|
-
return await Promise.race([work, aborted]);
|
|
205
|
-
} finally {
|
|
206
|
-
signal.removeEventListener("abort", onAbort);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
197
|
var PublisherBodyLimitError = class extends TaggedError("publisher_body_limit") {};
|
|
210
198
|
const readBoundedBody = async (response, { maxBytes, signal }) => {
|
|
211
199
|
if (!response.body) return Result.ok(/* @__PURE__ */ new Uint8Array());
|
|
@@ -666,5 +654,4 @@ const createPolitePublisherFetch = ({ validateTarget, fetcher, clock = {
|
|
|
666
654
|
};
|
|
667
655
|
return async (input, options = {}) => await walkPublisherRedirects(input, options, bindPublisherRuntimeToAbort(runtime, options.init?.signal ?? void 0));
|
|
668
656
|
};
|
|
669
|
-
//#endregion
|
|
670
657
|
export { AgentExcludedError, CrawlDelayUnsupportedError, PublisherBodyLimitError, PublisherBodyNotReplayableError, PublisherBypassUnauditedError, PublisherRedirectLimitError, PublisherRequestAbortedError, PublisherTargetRefusedError, RobotsDisallowedError, RobotsUnavailableError, createPolitePublisherFetch, evaluatePublisherRobots, readBoundedBody };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/race-abort.ts
|
|
2
|
+
const abortReason = (signal) => signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
3
|
+
const raceAbort = async (work, signal) => {
|
|
4
|
+
if (signal === void 0) return await work;
|
|
5
|
+
const { promise: aborted, reject } = Promise.withResolvers();
|
|
6
|
+
const onAbort = () => reject(abortReason(signal));
|
|
7
|
+
if (signal.aborted) onAbort();
|
|
8
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
9
|
+
try {
|
|
10
|
+
return await Promise.race([work, aborted]);
|
|
11
|
+
} finally {
|
|
12
|
+
signal.removeEventListener("abort", onAbort);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export { raceAbort as t };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stll/fetch",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Fetch with
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Fetch with mandatory total or header timeouts, idle-bounded body reads, connection-failure classification, and a robots-aware polite publisher fetch",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"abort",
|
|
7
7
|
"fetch",
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"./publisher": {
|
|
34
34
|
"types": "./dist/publisher.d.ts",
|
|
35
35
|
"import": "./dist/publisher.js"
|
|
36
|
+
},
|
|
37
|
+
"./body-reader": {
|
|
38
|
+
"types": "./dist/body-reader.d.ts",
|
|
39
|
+
"import": "./dist/body-reader.js"
|
|
36
40
|
}
|
|
37
41
|
},
|
|
38
42
|
"publishConfig": {
|
|
@@ -57,7 +61,7 @@
|
|
|
57
61
|
"@stll/typescript-config": "0.0.0",
|
|
58
62
|
"bun-types": "1.4.4",
|
|
59
63
|
"fast-check": "^4.10.2",
|
|
60
|
-
"tsdown": "0.23.
|
|
64
|
+
"tsdown": "0.23.4"
|
|
61
65
|
},
|
|
62
66
|
"main": "./dist/index.js",
|
|
63
67
|
"types": "./dist/index.d.ts"
|