@happy-ts/fetch-t 1.2.1 → 1.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.cn.md +24 -27
- package/README.md +24 -33
- package/dist/main.cjs +41 -0
- package/dist/main.cjs.map +1 -1
- package/dist/main.mjs +41 -0
- package/dist/main.mjs.map +1 -1
- package/dist/types.d.ts +25 -2
- package/docs/README.md +1 -0
- package/docs/classes/FetchError.md +3 -3
- package/docs/functions/fetchT.md +10 -10
- package/docs/interfaces/FetchInit.md +5 -3
- package/docs/interfaces/FetchProgress.md +16 -0
- package/docs/interfaces/FetchTask.md +3 -3
- package/docs/type-aliases/FetchResponse.md +1 -1
- package/docs/type-aliases/FetchResponseType.md +1 -1
- package/docs/variables/ABORT_ERROR.md +1 -1
- package/docs/variables/TIMEOUT_ERROR.md +1 -1
- package/package.json +2 -2
- package/src/fetch/defines.ts +28 -1
- package/src/fetch/fetch.ts +61 -0
package/README.cn.md
CHANGED
|
@@ -16,35 +16,22 @@ fetchT 的返回数据是一个明确的类型,可以是 `string` `ArrayBuffer
|
|
|
16
16
|
|
|
17
17
|
支持超时自动取消请求。
|
|
18
18
|
|
|
19
|
+
支持进度回调。
|
|
20
|
+
|
|
19
21
|
## 安装
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
```sh
|
|
24
|
+
# via pnpm
|
|
23
25
|
pnpm add @happy-ts/fetch-t
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
yarn
|
|
27
|
-
```
|
|
26
|
+
# or via yarn
|
|
28
27
|
yarn add @happy-ts/fetch-t
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
npm
|
|
32
|
-
```
|
|
28
|
+
# or just from npm
|
|
33
29
|
npm install --save @happy-ts/fetch-t
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
通过 JSR
|
|
37
|
-
```
|
|
30
|
+
# via JSR
|
|
38
31
|
jsr add @happy-ts/fetch-t
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
通过 deno
|
|
42
|
-
```
|
|
32
|
+
# for deno
|
|
43
33
|
deno add @happy-ts/fetch-t
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
通过 bun
|
|
47
|
-
```
|
|
34
|
+
# for bun
|
|
48
35
|
bunx jsr add @happy-ts/fetch-t
|
|
49
36
|
```
|
|
50
37
|
|
|
@@ -66,6 +53,16 @@ const fetchTask = fetchT('https://example.com', {
|
|
|
66
53
|
abortable: true,
|
|
67
54
|
responseType: 'json',
|
|
68
55
|
timeout: 3000,
|
|
56
|
+
onChunk(chunk): void {
|
|
57
|
+
console.assert(chunk instanceof Uint8Array);
|
|
58
|
+
},
|
|
59
|
+
onProgress(progressResult): void {
|
|
60
|
+
progressResult.inspect(progress => {
|
|
61
|
+
console.log(`${ progress.completedByteLength }/${ progress.totalByteLength }`);
|
|
62
|
+
}).inspectErr(err => {
|
|
63
|
+
console.error(err);
|
|
64
|
+
});
|
|
65
|
+
},
|
|
69
66
|
});
|
|
70
67
|
|
|
71
68
|
somethingHappenAsync(() => {
|
|
@@ -73,11 +70,11 @@ somethingHappenAsync(() => {
|
|
|
73
70
|
});
|
|
74
71
|
|
|
75
72
|
const res = await fetchTask.response;
|
|
76
|
-
|
|
77
|
-
console.
|
|
78
|
-
}
|
|
79
|
-
console.
|
|
80
|
-
}
|
|
73
|
+
res.inspect(data => {
|
|
74
|
+
console.log(data);
|
|
75
|
+
}).inspectErr(err => {
|
|
76
|
+
console.assert(err === 'cancel');
|
|
77
|
+
});
|
|
81
78
|
```
|
|
82
79
|
|
|
83
80
|
更多示例可参见测试用例 <a href="tests/fetch.test.ts">fetch.test.ts</a>。
|
package/README.md
CHANGED
|
@@ -21,41 +21,22 @@ The return data of fetchT is of a specific type, which can be either `string`, `
|
|
|
21
21
|
|
|
22
22
|
Support `timeout`.
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Support `progress`.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
## Installation
|
|
27
27
|
|
|
28
|
-
```
|
|
28
|
+
```sh
|
|
29
|
+
# via pnpm
|
|
29
30
|
pnpm add @happy-ts/fetch-t
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
or via yarn
|
|
33
|
-
|
|
34
|
-
```
|
|
31
|
+
# or via yarn
|
|
35
32
|
yarn add @happy-ts/fetch-t
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
or just from npm
|
|
39
|
-
|
|
40
|
-
```
|
|
33
|
+
# or just from npm
|
|
41
34
|
npm install --save @happy-ts/fetch-t
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
via JSR
|
|
45
|
-
|
|
46
|
-
```
|
|
35
|
+
# via JSR
|
|
47
36
|
jsr add @happy-ts/fetch-t
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
for deno
|
|
51
|
-
|
|
52
|
-
```
|
|
37
|
+
# for deno
|
|
53
38
|
deno add @happy-ts/fetch-t
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
for bun
|
|
57
|
-
|
|
58
|
-
```
|
|
39
|
+
# for bun
|
|
59
40
|
bunx jsr add @happy-ts/fetch-t
|
|
60
41
|
```
|
|
61
42
|
|
|
@@ -77,6 +58,16 @@ const fetchTask = fetchT('https://example.com', {
|
|
|
77
58
|
abortable: true,
|
|
78
59
|
responseType: 'json',
|
|
79
60
|
timeout: 3000,
|
|
61
|
+
onChunk(chunk): void {
|
|
62
|
+
console.assert(chunk instanceof Uint8Array);
|
|
63
|
+
},
|
|
64
|
+
onProgress(progressResult): void {
|
|
65
|
+
progressResult.inspect(progress => {
|
|
66
|
+
console.log(`${ progress.completedByteLength }/${ progress.totalByteLength }`);
|
|
67
|
+
}).inspectErr(err => {
|
|
68
|
+
console.error(err);
|
|
69
|
+
});
|
|
70
|
+
},
|
|
80
71
|
});
|
|
81
72
|
|
|
82
73
|
somethingHappenAsync(() => {
|
|
@@ -84,11 +75,11 @@ somethingHappenAsync(() => {
|
|
|
84
75
|
});
|
|
85
76
|
|
|
86
77
|
const res = await fetchTask.response;
|
|
87
|
-
|
|
88
|
-
console.
|
|
89
|
-
}
|
|
90
|
-
console.
|
|
91
|
-
}
|
|
78
|
+
res.inspect(data => {
|
|
79
|
+
console.log(data);
|
|
80
|
+
}).inspectErr(err => {
|
|
81
|
+
console.assert(err === 'cancel');
|
|
82
|
+
});
|
|
92
83
|
```
|
|
93
84
|
|
|
94
85
|
For more examples, please refer to test case <a href="tests/fetch.test.ts">fetch.test.ts</a>.
|
package/dist/main.cjs
CHANGED
|
@@ -30,6 +30,8 @@ function fetchT(url, init) {
|
|
|
30
30
|
abortable = false,
|
|
31
31
|
responseType,
|
|
32
32
|
timeout,
|
|
33
|
+
onProgress,
|
|
34
|
+
onChunk,
|
|
33
35
|
...rest
|
|
34
36
|
} = init ?? {};
|
|
35
37
|
const shouldWaitTimeout = timeout != null;
|
|
@@ -48,6 +50,45 @@ function fetchT(url, init) {
|
|
|
48
50
|
await res.body?.cancel();
|
|
49
51
|
return happyRusty.Err(new FetchError(res.statusText, res.status));
|
|
50
52
|
}
|
|
53
|
+
if (res.body) {
|
|
54
|
+
const shouldNotifyProgress = typeof onProgress === "function";
|
|
55
|
+
const shouldNotifyChunk = typeof onChunk === "function";
|
|
56
|
+
if (shouldNotifyProgress || shouldNotifyChunk) {
|
|
57
|
+
const [stream1, stream2] = res.body.tee();
|
|
58
|
+
const reader = stream1.getReader();
|
|
59
|
+
let totalByteLength = null;
|
|
60
|
+
let completedByteLength = 0;
|
|
61
|
+
if (shouldNotifyProgress) {
|
|
62
|
+
const contentLength = res.headers.get("content-length") ?? res.headers.get("Content-Length");
|
|
63
|
+
if (contentLength == null) {
|
|
64
|
+
onProgress(happyRusty.Err(new Error("No content-length in response headers.")));
|
|
65
|
+
} else {
|
|
66
|
+
totalByteLength = parseInt(contentLength, 10);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
reader.read().then(function notify({ done, value }) {
|
|
70
|
+
if (done) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (shouldNotifyChunk) {
|
|
74
|
+
onChunk(value);
|
|
75
|
+
}
|
|
76
|
+
if (shouldNotifyProgress && totalByteLength != null) {
|
|
77
|
+
completedByteLength += value.byteLength;
|
|
78
|
+
onProgress(happyRusty.Ok({
|
|
79
|
+
totalByteLength,
|
|
80
|
+
completedByteLength
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
reader.read().then(notify);
|
|
84
|
+
});
|
|
85
|
+
res = new Response(stream2, {
|
|
86
|
+
headers: res.headers,
|
|
87
|
+
status: res.status,
|
|
88
|
+
statusText: res.statusText
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
51
92
|
switch (responseType) {
|
|
52
93
|
case "arraybuffer": {
|
|
53
94
|
return happyRusty.Ok(await res.arrayBuffer());
|
package/dist/main.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.cjs","sources":["../src/fetch/constants.ts","../src/fetch/defines.ts","../src/fetch/fetch.ts"],"sourcesContent":["/**\n * Name of abort error;\n */\nexport const ABORT_ERROR = 'AbortError' as const;\n\n/**\n * Name of timeout error;\n */\nexport const TIMEOUT_ERROR = 'TimeoutError' as const;","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { AsyncResult } from 'happy-rusty';\n\n/**\n * Represents the response of a fetch operation, encapsulating the result data or any error that occurred.\n *\n * @typeParam T - The type of the data expected in the response.\n */\nexport type FetchResponse<T> = AsyncResult<T, any>;\n\n/**\n * Defines the structure and behavior of a fetch task, including the ability to abort the task and check its status.\n *\n * @typeParam T - The type of the data expected in the response.\n */\nexport interface FetchTask<T> {\n /**\n * Aborts the fetch task, optionally with a reason for the abortion.\n *\n * @param reason - An optional parameter to indicate why the task was aborted.\n */\n abort(reason?: any): void;\n\n /**\n * Indicates whether the fetch task has been aborted.\n */\n readonly aborted: boolean;\n\n /**\n * The response of the fetch task, represented as an `AsyncResult`.\n */\n readonly response: FetchResponse<T>;\n}\n\n/**\n * Specifies the expected response type of the fetch request.\n */\nexport type FetchResponseType = 'text' | 'arraybuffer' | 'blob' | 'json';\n\n/**\n * Extends the standard `RequestInit` interface from the Fetch API to include additional custom options.\n */\nexport interface FetchInit extends RequestInit {\n /**\n * Indicates whether the fetch request should be abortable.\n */\n abortable?: boolean;\n\n /**\n * Specifies the expected response type of the fetch request.\n */\n responseType?: FetchResponseType;\n\n /**\n * Specifies the maximum time in milliseconds to wait for the fetch request to complete.\n */\n timeout?: number;\n}\n\n/**\n * Represents an error that occurred during a fetch operation when the response is not ok.\n */\nexport class FetchError extends Error {\n /**\n * The name of the error.\n */\n name = 'FetchError';\n /**\n * The status code of the response.\n */\n status = 0;\n\n constructor(message: string, status: number) {\n super(message);\n this.status = status;\n }\n}","import { Err, Ok } from 'happy-rusty';\nimport invariant from 'tiny-invariant';\nimport { TIMEOUT_ERROR } from './constants.ts';\nimport { FetchError, type FetchInit, type FetchResponse, type FetchTask } from './defines.ts';\n\n/**\n * Fetches a resource from the network as a text string and returns a `FetchTask` representing the operation.\n *\n * @typeParam T - The expected type of the response data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a `string` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'text';\n}): FetchTask<string>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer and returns a `FetchTask` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with an `ArrayBuffer` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'arraybuffer';\n}): FetchTask<ArrayBuffer>;\n\n/**\n * Fetches a resource from the network as a Blob and returns a `FetchTask` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a `Blob` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'blob';\n}): FetchTask<Blob>;\n\n/**\n * Fetches a resource from the network and parses it as JSON, returning a `FetchTask` representing the operation.\n *\n * @typeParam T - The expected type of the parsed JSON data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a response parsed as JSON.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'json';\n}): FetchTask<T>;\n\n/**\n * Fetches a resource from the network as a text string and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'text'.\n * @returns A `FetchResponse` representing the operation with a `string` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'text';\n}): FetchResponse<string>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'arraybuffer'.\n * @returns A `FetchResponse` representing the operation with an `ArrayBuffer` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'arraybuffer';\n}): FetchResponse<ArrayBuffer>;\n\n/**\n * Fetches a resource from the network as a Blob and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'blob'.\n * @returns A `FetchResponse` representing the operation with a `Blob` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'blob';\n}): FetchResponse<Blob>;\n\n/**\n * Fetches a resource from the network and parses it as JSON, returning a `FetchResponse` representing the operation.\n *\n * @typeParam T - The expected type of the parsed JSON data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'json'.\n * @returns A `FetchResponse` representing the operation with a response parsed as JSON.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n responseType: 'json';\n}): FetchResponse<T>;\n\n/**\n * Fetches a resource from the network and returns a `FetchTask` representing the operation with a generic `Response`.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, indicating that the operation should be abortable.\n * @returns A `FetchTask` representing the operation with a generic `Response`.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n}): FetchTask<Response>;\n\n/**\n * Fetches a resource from the network and returns a `FetchResponse` or `FetchTask` based on the provided options.\n *\n * @typeParam T - The expected type of the response data when not using a specific `responseType`.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchResponse` representing the operation with a `Response` object.\n */\nexport function fetchT(url: string | URL, init?: FetchInit): FetchResponse<Response>;\n\n/**\n * Fetches a resource from the network and returns either a `FetchTask` or `FetchResponse` based on the provided options.\n *\n * @typeParam T - The expected type of the response data when not using a specific `responseType`.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` or `FetchResponse` depending on the provided options in `init`.\n */\nexport function fetchT<T>(url: string | URL, init?: FetchInit): FetchTask<T> | FetchResponse<T> {\n // most cases\n if (typeof url !== 'string') {\n invariant(url instanceof URL, () => `Url must be a string or URL object but received ${ url }.`);\n }\n\n const {\n // default not abort able\n abortable = false,\n responseType,\n timeout,\n ...rest\n } = init ?? {};\n\n const shouldWaitTimeout = timeout != null;\n let cancelTimer: (() => void) | null;\n\n if (shouldWaitTimeout) {\n invariant(typeof timeout === 'number' && timeout > 0, () => `Timeout must be a number greater than 0 but received ${ timeout }.`);\n }\n\n let controller: AbortController;\n\n if (abortable || shouldWaitTimeout) {\n controller = new AbortController();\n rest.signal = controller.signal;\n }\n\n const response: FetchResponse<T> = fetch(url, rest).then(async (res): FetchResponse<T> => {\n cancelTimer?.();\n\n if (!res.ok) {\n await res.body?.cancel();\n return Err(new FetchError(res.statusText, res.status));\n }\n\n switch (responseType) {\n case 'arraybuffer': {\n return Ok(await res.arrayBuffer() as T);\n }\n case 'blob': {\n return Ok(await res.blob() as T);\n }\n case 'json': {\n try {\n return Ok(await res.json() as T);\n } catch {\n return Err(new Error('Response is invalid json while responseType is json'));\n }\n }\n case 'text': {\n return Ok(await res.text() as T);\n }\n default: {\n // default return the Response object\n return Ok(res as T);\n }\n }\n }).catch((err) => {\n cancelTimer?.();\n\n return Err(err);\n });\n\n if (shouldWaitTimeout) {\n const timer = setTimeout(() => {\n if (!controller.signal.aborted) {\n const error = new Error();\n error.name = TIMEOUT_ERROR;\n controller.abort(error);\n }\n }, timeout);\n\n cancelTimer = (): void => {\n if (timer) {\n clearTimeout(timer);\n }\n\n cancelTimer = null;\n };\n }\n\n if (abortable) {\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n abort(reason?: any): void {\n cancelTimer?.();\n controller.abort(reason);\n },\n\n get aborted(): boolean {\n return controller.signal.aborted;\n },\n\n get response(): FetchResponse<T> {\n return response;\n },\n };\n } else {\n return response;\n }\n}"],"names":["Err","Ok"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"main.cjs","sources":["../src/fetch/constants.ts","../src/fetch/defines.ts","../src/fetch/fetch.ts"],"sourcesContent":["/**\n * Name of abort error;\n */\nexport const ABORT_ERROR = 'AbortError' as const;\n\n/**\n * Name of timeout error;\n */\nexport const TIMEOUT_ERROR = 'TimeoutError' as const;","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { AsyncResult, IOResult } from 'happy-rusty';\n\n/**\n * Represents the response of a fetch operation, encapsulating the result data or any error that occurred.\n *\n * @typeParam T - The type of the data expected in the response.\n */\nexport type FetchResponse<T> = AsyncResult<T, any>;\n\n/**\n * Defines the structure and behavior of a fetch task, including the ability to abort the task and check its status.\n *\n * @typeParam T - The type of the data expected in the response.\n */\nexport interface FetchTask<T> {\n /**\n * Aborts the fetch task, optionally with a reason for the abortion.\n *\n * @param reason - An optional parameter to indicate why the task was aborted.\n */\n abort(reason?: any): void;\n\n /**\n * Indicates whether the fetch task has been aborted.\n */\n readonly aborted: boolean;\n\n /**\n * The response of the fetch task, represented as an `AsyncResult`.\n */\n readonly response: FetchResponse<T>;\n}\n\n/**\n * Specifies the expected response type of the fetch request.\n */\nexport type FetchResponseType = 'text' | 'arraybuffer' | 'blob' | 'json';\n\n/**\n * Represents the progress of a fetch operation.\n */\nexport interface FetchProgress {\n /**\n * The total number of bytes to be received.\n */\n totalByteLength: number;\n\n /**\n * The number of bytes received so far.\n */\n completedByteLength: number;\n}\n\n/**\n * Extends the standard `RequestInit` interface from the Fetch API to include additional custom options.\n */\nexport interface FetchInit extends RequestInit {\n /**\n * Indicates whether the fetch request should be abortable.\n */\n abortable?: boolean;\n\n /**\n * Specifies the expected response type of the fetch request.\n */\n responseType?: FetchResponseType;\n\n /**\n * Specifies the maximum time in milliseconds to wait for the fetch request to complete.\n */\n timeout?: number;\n\n /**\n * Specifies a function to be called when the fetch request makes progress.\n * @param progressResult - The progress of the fetch request.\n */\n onProgress?: (progressResult: IOResult<FetchProgress>) => void;\n\n /**\n * Specifies a function to be called when the fetch request receives a chunk of data.\n * @param chunk - The chunk of data received.\n */\n onChunk?: (chunk: Uint8Array) => void;\n}\n\n/**\n * Represents an error that occurred during a fetch operation when the response is not ok.\n */\nexport class FetchError extends Error {\n /**\n * The name of the error.\n */\n name = 'FetchError';\n /**\n * The status code of the response.\n */\n status = 0;\n\n constructor(message: string, status: number) {\n super(message);\n this.status = status;\n }\n}","import { Err, Ok } from 'happy-rusty';\nimport invariant from 'tiny-invariant';\nimport { TIMEOUT_ERROR } from './constants.ts';\nimport { FetchError, type FetchInit, type FetchResponse, type FetchTask } from './defines.ts';\n\n/**\n * Fetches a resource from the network as a text string and returns a `FetchTask` representing the operation.\n *\n * @typeParam T - The expected type of the response data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a `string` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'text';\n}): FetchTask<string>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer and returns a `FetchTask` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with an `ArrayBuffer` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'arraybuffer';\n}): FetchTask<ArrayBuffer>;\n\n/**\n * Fetches a resource from the network as a Blob and returns a `FetchTask` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a `Blob` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'blob';\n}): FetchTask<Blob>;\n\n/**\n * Fetches a resource from the network and parses it as JSON, returning a `FetchTask` representing the operation.\n *\n * @typeParam T - The expected type of the parsed JSON data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a response parsed as JSON.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'json';\n}): FetchTask<T>;\n\n/**\n * Fetches a resource from the network as a text string and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'text'.\n * @returns A `FetchResponse` representing the operation with a `string` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'text';\n}): FetchResponse<string>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'arraybuffer'.\n * @returns A `FetchResponse` representing the operation with an `ArrayBuffer` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'arraybuffer';\n}): FetchResponse<ArrayBuffer>;\n\n/**\n * Fetches a resource from the network as a Blob and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'blob'.\n * @returns A `FetchResponse` representing the operation with a `Blob` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'blob';\n}): FetchResponse<Blob>;\n\n/**\n * Fetches a resource from the network and parses it as JSON, returning a `FetchResponse` representing the operation.\n *\n * @typeParam T - The expected type of the parsed JSON data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'json'.\n * @returns A `FetchResponse` representing the operation with a response parsed as JSON.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n responseType: 'json';\n}): FetchResponse<T>;\n\n/**\n * Fetches a resource from the network and returns a `FetchTask` representing the operation with a generic `Response`.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, indicating that the operation should be abortable.\n * @returns A `FetchTask` representing the operation with a generic `Response`.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n}): FetchTask<Response>;\n\n/**\n * Fetches a resource from the network and returns a `FetchResponse` or `FetchTask` based on the provided options.\n *\n * @typeParam T - The expected type of the response data when not using a specific `responseType`.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchResponse` representing the operation with a `Response` object.\n */\nexport function fetchT(url: string | URL, init?: FetchInit): FetchResponse<Response>;\n\n/**\n * Fetches a resource from the network and returns either a `FetchTask` or `FetchResponse` based on the provided options.\n *\n * @typeParam T - The expected type of the response data when not using a specific `responseType`.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` or `FetchResponse` depending on the provided options in `init`.\n */\nexport function fetchT<T>(url: string | URL, init?: FetchInit): FetchTask<T> | FetchResponse<T> {\n // most cases\n if (typeof url !== 'string') {\n invariant(url instanceof URL, () => `Url must be a string or URL object but received ${ url }.`);\n }\n\n const {\n // default not abort able\n abortable = false,\n responseType,\n timeout,\n onProgress,\n onChunk,\n ...rest\n } = init ?? {};\n\n const shouldWaitTimeout = timeout != null;\n let cancelTimer: (() => void) | null;\n\n if (shouldWaitTimeout) {\n invariant(typeof timeout === 'number' && timeout > 0, () => `Timeout must be a number greater than 0 but received ${ timeout }.`);\n }\n\n let controller: AbortController;\n\n if (abortable || shouldWaitTimeout) {\n controller = new AbortController();\n rest.signal = controller.signal;\n }\n\n const response: FetchResponse<T> = fetch(url, rest).then(async (res): FetchResponse<T> => {\n cancelTimer?.();\n\n if (!res.ok) {\n await res.body?.cancel();\n return Err(new FetchError(res.statusText, res.status));\n }\n\n if (res.body) {\n // should notify progress or data chunk?\n const shouldNotifyProgress = typeof onProgress === 'function';\n const shouldNotifyChunk = typeof onChunk === 'function';\n\n if ((shouldNotifyProgress || shouldNotifyChunk)) {\n // tee the original stream to two streams, one for notify progress, another for response\n const [stream1, stream2] = res.body.tee();\n\n const reader = stream1.getReader();\n // may has no content-length\n let totalByteLength: number | null = null;\n let completedByteLength = 0;\n\n if (shouldNotifyProgress) {\n // try to get content-length\n // compatible with http/2\n const contentLength = res.headers.get('content-length') ?? res.headers.get('Content-Length');\n if (contentLength == null) {\n // response headers has no content-length\n onProgress(Err(new Error('No content-length in response headers.')));\n } else {\n totalByteLength = parseInt(contentLength, 10);\n }\n }\n\n reader.read().then(function notify({ done, value }) {\n if (done) {\n return;\n }\n\n // notify chunk\n if (shouldNotifyChunk) {\n onChunk(value);\n }\n\n // notify progress\n if (shouldNotifyProgress && totalByteLength != null) {\n completedByteLength += value.byteLength;\n onProgress(Ok({\n totalByteLength,\n completedByteLength,\n }));\n }\n\n\n // continue to read\n reader.read().then(notify);\n });\n\n // replace the original response with the new one\n res = new Response(stream2, {\n headers: res.headers,\n status: res.status,\n statusText: res.statusText,\n });\n }\n }\n\n switch (responseType) {\n case 'arraybuffer': {\n return Ok(await res.arrayBuffer() as T);\n }\n case 'blob': {\n return Ok(await res.blob() as T);\n }\n case 'json': {\n try {\n return Ok(await res.json() as T);\n } catch {\n return Err(new Error('Response is invalid json while responseType is json'));\n }\n }\n case 'text': {\n return Ok(await res.text() as T);\n }\n default: {\n // default return the Response object\n return Ok(res as T);\n }\n }\n }).catch((err) => {\n cancelTimer?.();\n\n return Err(err);\n });\n\n if (shouldWaitTimeout) {\n const timer = setTimeout(() => {\n if (!controller.signal.aborted) {\n const error = new Error();\n error.name = TIMEOUT_ERROR;\n controller.abort(error);\n }\n }, timeout);\n\n cancelTimer = (): void => {\n if (timer) {\n clearTimeout(timer);\n }\n\n cancelTimer = null;\n };\n }\n\n if (abortable) {\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n abort(reason?: any): void {\n cancelTimer?.();\n controller.abort(reason);\n },\n\n get aborted(): boolean {\n return controller.signal.aborted;\n },\n\n get response(): FetchResponse<T> {\n return response;\n },\n };\n } else {\n return response;\n }\n}"],"names":["Err","Ok"],"mappings":"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"}
|
package/dist/main.mjs
CHANGED
|
@@ -28,6 +28,8 @@ function fetchT(url, init) {
|
|
|
28
28
|
abortable = false,
|
|
29
29
|
responseType,
|
|
30
30
|
timeout,
|
|
31
|
+
onProgress,
|
|
32
|
+
onChunk,
|
|
31
33
|
...rest
|
|
32
34
|
} = init ?? {};
|
|
33
35
|
const shouldWaitTimeout = timeout != null;
|
|
@@ -46,6 +48,45 @@ function fetchT(url, init) {
|
|
|
46
48
|
await res.body?.cancel();
|
|
47
49
|
return Err(new FetchError(res.statusText, res.status));
|
|
48
50
|
}
|
|
51
|
+
if (res.body) {
|
|
52
|
+
const shouldNotifyProgress = typeof onProgress === "function";
|
|
53
|
+
const shouldNotifyChunk = typeof onChunk === "function";
|
|
54
|
+
if (shouldNotifyProgress || shouldNotifyChunk) {
|
|
55
|
+
const [stream1, stream2] = res.body.tee();
|
|
56
|
+
const reader = stream1.getReader();
|
|
57
|
+
let totalByteLength = null;
|
|
58
|
+
let completedByteLength = 0;
|
|
59
|
+
if (shouldNotifyProgress) {
|
|
60
|
+
const contentLength = res.headers.get("content-length") ?? res.headers.get("Content-Length");
|
|
61
|
+
if (contentLength == null) {
|
|
62
|
+
onProgress(Err(new Error("No content-length in response headers.")));
|
|
63
|
+
} else {
|
|
64
|
+
totalByteLength = parseInt(contentLength, 10);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
reader.read().then(function notify({ done, value }) {
|
|
68
|
+
if (done) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (shouldNotifyChunk) {
|
|
72
|
+
onChunk(value);
|
|
73
|
+
}
|
|
74
|
+
if (shouldNotifyProgress && totalByteLength != null) {
|
|
75
|
+
completedByteLength += value.byteLength;
|
|
76
|
+
onProgress(Ok({
|
|
77
|
+
totalByteLength,
|
|
78
|
+
completedByteLength
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
reader.read().then(notify);
|
|
82
|
+
});
|
|
83
|
+
res = new Response(stream2, {
|
|
84
|
+
headers: res.headers,
|
|
85
|
+
status: res.status,
|
|
86
|
+
statusText: res.statusText
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
49
90
|
switch (responseType) {
|
|
50
91
|
case "arraybuffer": {
|
|
51
92
|
return Ok(await res.arrayBuffer());
|
package/dist/main.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.mjs","sources":["../src/fetch/constants.ts","../src/fetch/defines.ts","../src/fetch/fetch.ts"],"sourcesContent":["/**\n * Name of abort error;\n */\nexport const ABORT_ERROR = 'AbortError' as const;\n\n/**\n * Name of timeout error;\n */\nexport const TIMEOUT_ERROR = 'TimeoutError' as const;","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { AsyncResult } from 'happy-rusty';\n\n/**\n * Represents the response of a fetch operation, encapsulating the result data or any error that occurred.\n *\n * @typeParam T - The type of the data expected in the response.\n */\nexport type FetchResponse<T> = AsyncResult<T, any>;\n\n/**\n * Defines the structure and behavior of a fetch task, including the ability to abort the task and check its status.\n *\n * @typeParam T - The type of the data expected in the response.\n */\nexport interface FetchTask<T> {\n /**\n * Aborts the fetch task, optionally with a reason for the abortion.\n *\n * @param reason - An optional parameter to indicate why the task was aborted.\n */\n abort(reason?: any): void;\n\n /**\n * Indicates whether the fetch task has been aborted.\n */\n readonly aborted: boolean;\n\n /**\n * The response of the fetch task, represented as an `AsyncResult`.\n */\n readonly response: FetchResponse<T>;\n}\n\n/**\n * Specifies the expected response type of the fetch request.\n */\nexport type FetchResponseType = 'text' | 'arraybuffer' | 'blob' | 'json';\n\n/**\n * Extends the standard `RequestInit` interface from the Fetch API to include additional custom options.\n */\nexport interface FetchInit extends RequestInit {\n /**\n * Indicates whether the fetch request should be abortable.\n */\n abortable?: boolean;\n\n /**\n * Specifies the expected response type of the fetch request.\n */\n responseType?: FetchResponseType;\n\n /**\n * Specifies the maximum time in milliseconds to wait for the fetch request to complete.\n */\n timeout?: number;\n}\n\n/**\n * Represents an error that occurred during a fetch operation when the response is not ok.\n */\nexport class FetchError extends Error {\n /**\n * The name of the error.\n */\n name = 'FetchError';\n /**\n * The status code of the response.\n */\n status = 0;\n\n constructor(message: string, status: number) {\n super(message);\n this.status = status;\n }\n}","import { Err, Ok } from 'happy-rusty';\nimport invariant from 'tiny-invariant';\nimport { TIMEOUT_ERROR } from './constants.ts';\nimport { FetchError, type FetchInit, type FetchResponse, type FetchTask } from './defines.ts';\n\n/**\n * Fetches a resource from the network as a text string and returns a `FetchTask` representing the operation.\n *\n * @typeParam T - The expected type of the response data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a `string` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'text';\n}): FetchTask<string>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer and returns a `FetchTask` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with an `ArrayBuffer` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'arraybuffer';\n}): FetchTask<ArrayBuffer>;\n\n/**\n * Fetches a resource from the network as a Blob and returns a `FetchTask` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a `Blob` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'blob';\n}): FetchTask<Blob>;\n\n/**\n * Fetches a resource from the network and parses it as JSON, returning a `FetchTask` representing the operation.\n *\n * @typeParam T - The expected type of the parsed JSON data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a response parsed as JSON.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'json';\n}): FetchTask<T>;\n\n/**\n * Fetches a resource from the network as a text string and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'text'.\n * @returns A `FetchResponse` representing the operation with a `string` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'text';\n}): FetchResponse<string>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'arraybuffer'.\n * @returns A `FetchResponse` representing the operation with an `ArrayBuffer` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'arraybuffer';\n}): FetchResponse<ArrayBuffer>;\n\n/**\n * Fetches a resource from the network as a Blob and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'blob'.\n * @returns A `FetchResponse` representing the operation with a `Blob` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'blob';\n}): FetchResponse<Blob>;\n\n/**\n * Fetches a resource from the network and parses it as JSON, returning a `FetchResponse` representing the operation.\n *\n * @typeParam T - The expected type of the parsed JSON data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'json'.\n * @returns A `FetchResponse` representing the operation with a response parsed as JSON.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n responseType: 'json';\n}): FetchResponse<T>;\n\n/**\n * Fetches a resource from the network and returns a `FetchTask` representing the operation with a generic `Response`.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, indicating that the operation should be abortable.\n * @returns A `FetchTask` representing the operation with a generic `Response`.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n}): FetchTask<Response>;\n\n/**\n * Fetches a resource from the network and returns a `FetchResponse` or `FetchTask` based on the provided options.\n *\n * @typeParam T - The expected type of the response data when not using a specific `responseType`.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchResponse` representing the operation with a `Response` object.\n */\nexport function fetchT(url: string | URL, init?: FetchInit): FetchResponse<Response>;\n\n/**\n * Fetches a resource from the network and returns either a `FetchTask` or `FetchResponse` based on the provided options.\n *\n * @typeParam T - The expected type of the response data when not using a specific `responseType`.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` or `FetchResponse` depending on the provided options in `init`.\n */\nexport function fetchT<T>(url: string | URL, init?: FetchInit): FetchTask<T> | FetchResponse<T> {\n // most cases\n if (typeof url !== 'string') {\n invariant(url instanceof URL, () => `Url must be a string or URL object but received ${ url }.`);\n }\n\n const {\n // default not abort able\n abortable = false,\n responseType,\n timeout,\n ...rest\n } = init ?? {};\n\n const shouldWaitTimeout = timeout != null;\n let cancelTimer: (() => void) | null;\n\n if (shouldWaitTimeout) {\n invariant(typeof timeout === 'number' && timeout > 0, () => `Timeout must be a number greater than 0 but received ${ timeout }.`);\n }\n\n let controller: AbortController;\n\n if (abortable || shouldWaitTimeout) {\n controller = new AbortController();\n rest.signal = controller.signal;\n }\n\n const response: FetchResponse<T> = fetch(url, rest).then(async (res): FetchResponse<T> => {\n cancelTimer?.();\n\n if (!res.ok) {\n await res.body?.cancel();\n return Err(new FetchError(res.statusText, res.status));\n }\n\n switch (responseType) {\n case 'arraybuffer': {\n return Ok(await res.arrayBuffer() as T);\n }\n case 'blob': {\n return Ok(await res.blob() as T);\n }\n case 'json': {\n try {\n return Ok(await res.json() as T);\n } catch {\n return Err(new Error('Response is invalid json while responseType is json'));\n }\n }\n case 'text': {\n return Ok(await res.text() as T);\n }\n default: {\n // default return the Response object\n return Ok(res as T);\n }\n }\n }).catch((err) => {\n cancelTimer?.();\n\n return Err(err);\n });\n\n if (shouldWaitTimeout) {\n const timer = setTimeout(() => {\n if (!controller.signal.aborted) {\n const error = new Error();\n error.name = TIMEOUT_ERROR;\n controller.abort(error);\n }\n }, timeout);\n\n cancelTimer = (): void => {\n if (timer) {\n clearTimeout(timer);\n }\n\n cancelTimer = null;\n };\n }\n\n if (abortable) {\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n abort(reason?: any): void {\n cancelTimer?.();\n controller.abort(reason);\n },\n\n get aborted(): boolean {\n return controller.signal.aborted;\n },\n\n get response(): FetchResponse<T> {\n return response;\n },\n };\n } else {\n return response;\n }\n}"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"main.mjs","sources":["../src/fetch/constants.ts","../src/fetch/defines.ts","../src/fetch/fetch.ts"],"sourcesContent":["/**\n * Name of abort error;\n */\nexport const ABORT_ERROR = 'AbortError' as const;\n\n/**\n * Name of timeout error;\n */\nexport const TIMEOUT_ERROR = 'TimeoutError' as const;","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { AsyncResult, IOResult } from 'happy-rusty';\n\n/**\n * Represents the response of a fetch operation, encapsulating the result data or any error that occurred.\n *\n * @typeParam T - The type of the data expected in the response.\n */\nexport type FetchResponse<T> = AsyncResult<T, any>;\n\n/**\n * Defines the structure and behavior of a fetch task, including the ability to abort the task and check its status.\n *\n * @typeParam T - The type of the data expected in the response.\n */\nexport interface FetchTask<T> {\n /**\n * Aborts the fetch task, optionally with a reason for the abortion.\n *\n * @param reason - An optional parameter to indicate why the task was aborted.\n */\n abort(reason?: any): void;\n\n /**\n * Indicates whether the fetch task has been aborted.\n */\n readonly aborted: boolean;\n\n /**\n * The response of the fetch task, represented as an `AsyncResult`.\n */\n readonly response: FetchResponse<T>;\n}\n\n/**\n * Specifies the expected response type of the fetch request.\n */\nexport type FetchResponseType = 'text' | 'arraybuffer' | 'blob' | 'json';\n\n/**\n * Represents the progress of a fetch operation.\n */\nexport interface FetchProgress {\n /**\n * The total number of bytes to be received.\n */\n totalByteLength: number;\n\n /**\n * The number of bytes received so far.\n */\n completedByteLength: number;\n}\n\n/**\n * Extends the standard `RequestInit` interface from the Fetch API to include additional custom options.\n */\nexport interface FetchInit extends RequestInit {\n /**\n * Indicates whether the fetch request should be abortable.\n */\n abortable?: boolean;\n\n /**\n * Specifies the expected response type of the fetch request.\n */\n responseType?: FetchResponseType;\n\n /**\n * Specifies the maximum time in milliseconds to wait for the fetch request to complete.\n */\n timeout?: number;\n\n /**\n * Specifies a function to be called when the fetch request makes progress.\n * @param progressResult - The progress of the fetch request.\n */\n onProgress?: (progressResult: IOResult<FetchProgress>) => void;\n\n /**\n * Specifies a function to be called when the fetch request receives a chunk of data.\n * @param chunk - The chunk of data received.\n */\n onChunk?: (chunk: Uint8Array) => void;\n}\n\n/**\n * Represents an error that occurred during a fetch operation when the response is not ok.\n */\nexport class FetchError extends Error {\n /**\n * The name of the error.\n */\n name = 'FetchError';\n /**\n * The status code of the response.\n */\n status = 0;\n\n constructor(message: string, status: number) {\n super(message);\n this.status = status;\n }\n}","import { Err, Ok } from 'happy-rusty';\nimport invariant from 'tiny-invariant';\nimport { TIMEOUT_ERROR } from './constants.ts';\nimport { FetchError, type FetchInit, type FetchResponse, type FetchTask } from './defines.ts';\n\n/**\n * Fetches a resource from the network as a text string and returns a `FetchTask` representing the operation.\n *\n * @typeParam T - The expected type of the response data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a `string` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'text';\n}): FetchTask<string>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer and returns a `FetchTask` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with an `ArrayBuffer` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'arraybuffer';\n}): FetchTask<ArrayBuffer>;\n\n/**\n * Fetches a resource from the network as a Blob and returns a `FetchTask` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a `Blob` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'blob';\n}): FetchTask<Blob>;\n\n/**\n * Fetches a resource from the network and parses it as JSON, returning a `FetchTask` representing the operation.\n *\n * @typeParam T - The expected type of the parsed JSON data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` representing the operation with a response parsed as JSON.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n abortable: true;\n responseType: 'json';\n}): FetchTask<T>;\n\n/**\n * Fetches a resource from the network as a text string and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'text'.\n * @returns A `FetchResponse` representing the operation with a `string` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'text';\n}): FetchResponse<string>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'arraybuffer'.\n * @returns A `FetchResponse` representing the operation with an `ArrayBuffer` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'arraybuffer';\n}): FetchResponse<ArrayBuffer>;\n\n/**\n * Fetches a resource from the network as a Blob and returns a `FetchResponse` representing the operation.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'blob'.\n * @returns A `FetchResponse` representing the operation with a `Blob` response.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n responseType: 'blob';\n}): FetchResponse<Blob>;\n\n/**\n * Fetches a resource from the network and parses it as JSON, returning a `FetchResponse` representing the operation.\n *\n * @typeParam T - The expected type of the parsed JSON data.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, specifying the response type as 'json'.\n * @returns A `FetchResponse` representing the operation with a response parsed as JSON.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n responseType: 'json';\n}): FetchResponse<T>;\n\n/**\n * Fetches a resource from the network and returns a `FetchTask` representing the operation with a generic `Response`.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, indicating that the operation should be abortable.\n * @returns A `FetchTask` representing the operation with a generic `Response`.\n */\nexport function fetchT(url: string | URL, init: FetchInit & {\n abortable: true;\n}): FetchTask<Response>;\n\n/**\n * Fetches a resource from the network and returns a `FetchResponse` or `FetchTask` based on the provided options.\n *\n * @typeParam T - The expected type of the response data when not using a specific `responseType`.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchResponse` representing the operation with a `Response` object.\n */\nexport function fetchT(url: string | URL, init?: FetchInit): FetchResponse<Response>;\n\n/**\n * Fetches a resource from the network and returns either a `FetchTask` or `FetchResponse` based on the provided options.\n *\n * @typeParam T - The expected type of the response data when not using a specific `responseType`.\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Additional options for the fetch operation, including custom `FetchInit` properties.\n * @returns A `FetchTask` or `FetchResponse` depending on the provided options in `init`.\n */\nexport function fetchT<T>(url: string | URL, init?: FetchInit): FetchTask<T> | FetchResponse<T> {\n // most cases\n if (typeof url !== 'string') {\n invariant(url instanceof URL, () => `Url must be a string or URL object but received ${ url }.`);\n }\n\n const {\n // default not abort able\n abortable = false,\n responseType,\n timeout,\n onProgress,\n onChunk,\n ...rest\n } = init ?? {};\n\n const shouldWaitTimeout = timeout != null;\n let cancelTimer: (() => void) | null;\n\n if (shouldWaitTimeout) {\n invariant(typeof timeout === 'number' && timeout > 0, () => `Timeout must be a number greater than 0 but received ${ timeout }.`);\n }\n\n let controller: AbortController;\n\n if (abortable || shouldWaitTimeout) {\n controller = new AbortController();\n rest.signal = controller.signal;\n }\n\n const response: FetchResponse<T> = fetch(url, rest).then(async (res): FetchResponse<T> => {\n cancelTimer?.();\n\n if (!res.ok) {\n await res.body?.cancel();\n return Err(new FetchError(res.statusText, res.status));\n }\n\n if (res.body) {\n // should notify progress or data chunk?\n const shouldNotifyProgress = typeof onProgress === 'function';\n const shouldNotifyChunk = typeof onChunk === 'function';\n\n if ((shouldNotifyProgress || shouldNotifyChunk)) {\n // tee the original stream to two streams, one for notify progress, another for response\n const [stream1, stream2] = res.body.tee();\n\n const reader = stream1.getReader();\n // may has no content-length\n let totalByteLength: number | null = null;\n let completedByteLength = 0;\n\n if (shouldNotifyProgress) {\n // try to get content-length\n // compatible with http/2\n const contentLength = res.headers.get('content-length') ?? res.headers.get('Content-Length');\n if (contentLength == null) {\n // response headers has no content-length\n onProgress(Err(new Error('No content-length in response headers.')));\n } else {\n totalByteLength = parseInt(contentLength, 10);\n }\n }\n\n reader.read().then(function notify({ done, value }) {\n if (done) {\n return;\n }\n\n // notify chunk\n if (shouldNotifyChunk) {\n onChunk(value);\n }\n\n // notify progress\n if (shouldNotifyProgress && totalByteLength != null) {\n completedByteLength += value.byteLength;\n onProgress(Ok({\n totalByteLength,\n completedByteLength,\n }));\n }\n\n\n // continue to read\n reader.read().then(notify);\n });\n\n // replace the original response with the new one\n res = new Response(stream2, {\n headers: res.headers,\n status: res.status,\n statusText: res.statusText,\n });\n }\n }\n\n switch (responseType) {\n case 'arraybuffer': {\n return Ok(await res.arrayBuffer() as T);\n }\n case 'blob': {\n return Ok(await res.blob() as T);\n }\n case 'json': {\n try {\n return Ok(await res.json() as T);\n } catch {\n return Err(new Error('Response is invalid json while responseType is json'));\n }\n }\n case 'text': {\n return Ok(await res.text() as T);\n }\n default: {\n // default return the Response object\n return Ok(res as T);\n }\n }\n }).catch((err) => {\n cancelTimer?.();\n\n return Err(err);\n });\n\n if (shouldWaitTimeout) {\n const timer = setTimeout(() => {\n if (!controller.signal.aborted) {\n const error = new Error();\n error.name = TIMEOUT_ERROR;\n controller.abort(error);\n }\n }, timeout);\n\n cancelTimer = (): void => {\n if (timer) {\n clearTimeout(timer);\n }\n\n cancelTimer = null;\n };\n }\n\n if (abortable) {\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n abort(reason?: any): void {\n cancelTimer?.();\n controller.abort(reason);\n },\n\n get aborted(): boolean {\n return controller.signal.aborted;\n },\n\n get response(): FetchResponse<T> {\n return response;\n },\n };\n } else {\n return response;\n }\n}"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsyncResult } from 'happy-rusty';
|
|
1
|
+
import { AsyncResult, IOResult } from 'happy-rusty';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Name of abort error;
|
|
@@ -40,6 +40,19 @@ interface FetchTask<T> {
|
|
|
40
40
|
* Specifies the expected response type of the fetch request.
|
|
41
41
|
*/
|
|
42
42
|
type FetchResponseType = 'text' | 'arraybuffer' | 'blob' | 'json';
|
|
43
|
+
/**
|
|
44
|
+
* Represents the progress of a fetch operation.
|
|
45
|
+
*/
|
|
46
|
+
interface FetchProgress {
|
|
47
|
+
/**
|
|
48
|
+
* The total number of bytes to be received.
|
|
49
|
+
*/
|
|
50
|
+
totalByteLength: number;
|
|
51
|
+
/**
|
|
52
|
+
* The number of bytes received so far.
|
|
53
|
+
*/
|
|
54
|
+
completedByteLength: number;
|
|
55
|
+
}
|
|
43
56
|
/**
|
|
44
57
|
* Extends the standard `RequestInit` interface from the Fetch API to include additional custom options.
|
|
45
58
|
*/
|
|
@@ -56,6 +69,16 @@ interface FetchInit extends RequestInit {
|
|
|
56
69
|
* Specifies the maximum time in milliseconds to wait for the fetch request to complete.
|
|
57
70
|
*/
|
|
58
71
|
timeout?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Specifies a function to be called when the fetch request makes progress.
|
|
74
|
+
* @param progressResult - The progress of the fetch request.
|
|
75
|
+
*/
|
|
76
|
+
onProgress?: (progressResult: IOResult<FetchProgress>) => void;
|
|
77
|
+
/**
|
|
78
|
+
* Specifies a function to be called when the fetch request receives a chunk of data.
|
|
79
|
+
* @param chunk - The chunk of data received.
|
|
80
|
+
*/
|
|
81
|
+
onChunk?: (chunk: Uint8Array) => void;
|
|
59
82
|
}
|
|
60
83
|
/**
|
|
61
84
|
* Represents an error that occurred during a fetch operation when the response is not ok.
|
|
@@ -179,5 +202,5 @@ declare function fetchT(url: string | URL, init: FetchInit & {
|
|
|
179
202
|
*/
|
|
180
203
|
declare function fetchT(url: string | URL, init?: FetchInit): FetchResponse<Response>;
|
|
181
204
|
|
|
182
|
-
export { ABORT_ERROR, FetchError, type FetchInit, type FetchResponse, type FetchResponseType, type FetchTask, TIMEOUT_ERROR, fetchT };
|
|
205
|
+
export { ABORT_ERROR, FetchError, type FetchInit, type FetchProgress, type FetchResponse, type FetchResponseType, type FetchTask, TIMEOUT_ERROR, fetchT };
|
|
183
206
|
//# sourceMappingURL=types.d.ts.map
|
package/docs/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
| Interface | Description |
|
|
16
16
|
| ------ | ------ |
|
|
17
17
|
| [FetchInit](interfaces/FetchInit.md) | Extends the standard `RequestInit` interface from the Fetch API to include additional custom options. |
|
|
18
|
+
| [FetchProgress](interfaces/FetchProgress.md) | Represents the progress of a fetch operation. |
|
|
18
19
|
| [FetchTask](interfaces/FetchTask.md) | Defines the structure and behavior of a fetch task, including the ability to abort the task and check its status. |
|
|
19
20
|
|
|
20
21
|
## Type Aliases
|
|
@@ -37,11 +37,11 @@ new FetchError(message, status): FetchError
|
|
|
37
37
|
|
|
38
38
|
#### Defined in
|
|
39
39
|
|
|
40
|
-
[defines.ts:
|
|
40
|
+
[defines.ts:100](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L100)
|
|
41
41
|
|
|
42
42
|
## Properties
|
|
43
43
|
|
|
44
44
|
| Property | Type | Default value | Description | Overrides | Defined in |
|
|
45
45
|
| ------ | ------ | ------ | ------ | ------ | ------ |
|
|
46
|
-
| `name` | `string` | `'FetchError'` | The name of the error. | `Error.name` | [defines.ts:
|
|
47
|
-
| `status` | `number` | `0` | The status code of the response. | - | [defines.ts:
|
|
46
|
+
| `name` | `string` | `'FetchError'` | The name of the error. | `Error.name` | [defines.ts:94](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L94) |
|
|
47
|
+
| `status` | `number` | `0` | The status code of the response. | - | [defines.ts:98](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L98) |
|
package/docs/functions/fetchT.md
CHANGED
|
@@ -57,7 +57,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
57
57
|
|
|
58
58
|
### Defined in
|
|
59
59
|
|
|
60
|
-
[fetch.ts:14](https://github.com/JiangJie/fetch-t/blob/
|
|
60
|
+
[fetch.ts:14](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L14)
|
|
61
61
|
|
|
62
62
|
## fetchT(url, init)
|
|
63
63
|
|
|
@@ -96,7 +96,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
96
96
|
|
|
97
97
|
### Defined in
|
|
98
98
|
|
|
99
|
-
[fetch.ts:26](https://github.com/JiangJie/fetch-t/blob/
|
|
99
|
+
[fetch.ts:26](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L26)
|
|
100
100
|
|
|
101
101
|
## fetchT(url, init)
|
|
102
102
|
|
|
@@ -135,7 +135,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
135
135
|
|
|
136
136
|
### Defined in
|
|
137
137
|
|
|
138
|
-
[fetch.ts:38](https://github.com/JiangJie/fetch-t/blob/
|
|
138
|
+
[fetch.ts:38](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L38)
|
|
139
139
|
|
|
140
140
|
## fetchT(url, init)
|
|
141
141
|
|
|
@@ -180,7 +180,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
180
180
|
|
|
181
181
|
### Defined in
|
|
182
182
|
|
|
183
|
-
[fetch.ts:51](https://github.com/JiangJie/fetch-t/blob/
|
|
183
|
+
[fetch.ts:51](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L51)
|
|
184
184
|
|
|
185
185
|
## fetchT(url, init)
|
|
186
186
|
|
|
@@ -219,7 +219,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
219
219
|
|
|
220
220
|
### Defined in
|
|
221
221
|
|
|
222
|
-
[fetch.ts:63](https://github.com/JiangJie/fetch-t/blob/
|
|
222
|
+
[fetch.ts:63](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L63)
|
|
223
223
|
|
|
224
224
|
## fetchT(url, init)
|
|
225
225
|
|
|
@@ -258,7 +258,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
258
258
|
|
|
259
259
|
### Defined in
|
|
260
260
|
|
|
261
|
-
[fetch.ts:74](https://github.com/JiangJie/fetch-t/blob/
|
|
261
|
+
[fetch.ts:74](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L74)
|
|
262
262
|
|
|
263
263
|
## fetchT(url, init)
|
|
264
264
|
|
|
@@ -297,7 +297,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
297
297
|
|
|
298
298
|
### Defined in
|
|
299
299
|
|
|
300
|
-
[fetch.ts:85](https://github.com/JiangJie/fetch-t/blob/
|
|
300
|
+
[fetch.ts:85](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L85)
|
|
301
301
|
|
|
302
302
|
## fetchT(url, init)
|
|
303
303
|
|
|
@@ -342,7 +342,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
342
342
|
|
|
343
343
|
### Defined in
|
|
344
344
|
|
|
345
|
-
[fetch.ts:97](https://github.com/JiangJie/fetch-t/blob/
|
|
345
|
+
[fetch.ts:97](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L97)
|
|
346
346
|
|
|
347
347
|
## fetchT(url, init)
|
|
348
348
|
|
|
@@ -381,7 +381,7 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
381
381
|
|
|
382
382
|
### Defined in
|
|
383
383
|
|
|
384
|
-
[fetch.ts:108](https://github.com/JiangJie/fetch-t/blob/
|
|
384
|
+
[fetch.ts:108](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L108)
|
|
385
385
|
|
|
386
386
|
## fetchT(url, init)
|
|
387
387
|
|
|
@@ -420,4 +420,4 @@ Additional options for the fetch operation, including custom `FetchInit` propert
|
|
|
420
420
|
|
|
421
421
|
### Defined in
|
|
422
422
|
|
|
423
|
-
[fetch.ts:120](https://github.com/JiangJie/fetch-t/blob/
|
|
423
|
+
[fetch.ts:120](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/fetch.ts#L120)
|
|
@@ -16,6 +16,8 @@ Extends the standard `RequestInit` interface from the Fetch API to include addit
|
|
|
16
16
|
|
|
17
17
|
| Property | Type | Description | Defined in |
|
|
18
18
|
| ------ | ------ | ------ | ------ |
|
|
19
|
-
| `abortable?` | `boolean` | Indicates whether the fetch request should be abortable. | [defines.ts:
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
19
|
+
| `abortable?` | `boolean` | Indicates whether the fetch request should be abortable. | [defines.ts:62](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L62) |
|
|
20
|
+
| `onChunk?` | (`chunk`: `Uint8Array`) => `void` | Specifies a function to be called when the fetch request receives a chunk of data. | [defines.ts:84](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L84) |
|
|
21
|
+
| `onProgress?` | (`progressResult`: `IOResult`\<[`FetchProgress`](FetchProgress.md)\>) => `void` | Specifies a function to be called when the fetch request makes progress. | [defines.ts:78](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L78) |
|
|
22
|
+
| `responseType?` | [`FetchResponseType`](../type-aliases/FetchResponseType.md) | Specifies the expected response type of the fetch request. | [defines.ts:67](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L67) |
|
|
23
|
+
| `timeout?` | `number` | Specifies the maximum time in milliseconds to wait for the fetch request to complete. | [defines.ts:72](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L72) |
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[**@happy-ts/fetch-t**](../README.md) • **Docs**
|
|
2
|
+
|
|
3
|
+
***
|
|
4
|
+
|
|
5
|
+
[@happy-ts/fetch-t](../README.md) / FetchProgress
|
|
6
|
+
|
|
7
|
+
# Interface: FetchProgress
|
|
8
|
+
|
|
9
|
+
Represents the progress of a fetch operation.
|
|
10
|
+
|
|
11
|
+
## Properties
|
|
12
|
+
|
|
13
|
+
| Property | Type | Description | Defined in |
|
|
14
|
+
| ------ | ------ | ------ | ------ |
|
|
15
|
+
| `completedByteLength` | `number` | The number of bytes received so far. | [defines.ts:52](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L52) |
|
|
16
|
+
| `totalByteLength` | `number` | The total number of bytes to be received. | [defines.ts:47](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L47) |
|
|
@@ -18,8 +18,8 @@ Defines the structure and behavior of a fetch task, including the ability to abo
|
|
|
18
18
|
|
|
19
19
|
| Property | Modifier | Type | Description | Defined in |
|
|
20
20
|
| ------ | ------ | ------ | ------ | ------ |
|
|
21
|
-
| `aborted` | `readonly` | `boolean` | Indicates whether the fetch task has been aborted. | [defines.ts:27](https://github.com/JiangJie/fetch-t/blob/
|
|
22
|
-
| `response` | `readonly` | [`FetchResponse`](../type-aliases/FetchResponse.md)\<`T`\> | The response of the fetch task, represented as an `AsyncResult`. | [defines.ts:32](https://github.com/JiangJie/fetch-t/blob/
|
|
21
|
+
| `aborted` | `readonly` | `boolean` | Indicates whether the fetch task has been aborted. | [defines.ts:27](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L27) |
|
|
22
|
+
| `response` | `readonly` | [`FetchResponse`](../type-aliases/FetchResponse.md)\<`T`\> | The response of the fetch task, represented as an `AsyncResult`. | [defines.ts:32](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L32) |
|
|
23
23
|
|
|
24
24
|
## Methods
|
|
25
25
|
|
|
@@ -43,4 +43,4 @@ Aborts the fetch task, optionally with a reason for the abortion.
|
|
|
43
43
|
|
|
44
44
|
#### Defined in
|
|
45
45
|
|
|
46
|
-
[defines.ts:22](https://github.com/JiangJie/fetch-t/blob/
|
|
46
|
+
[defines.ts:22](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L22)
|
|
@@ -20,4 +20,4 @@ Represents the response of a fetch operation, encapsulating the result data or a
|
|
|
20
20
|
|
|
21
21
|
## Defined in
|
|
22
22
|
|
|
23
|
-
[defines.ts:9](https://github.com/JiangJie/fetch-t/blob/
|
|
23
|
+
[defines.ts:9](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L9)
|
|
@@ -14,4 +14,4 @@ Specifies the expected response type of the fetch request.
|
|
|
14
14
|
|
|
15
15
|
## Defined in
|
|
16
16
|
|
|
17
|
-
[defines.ts:38](https://github.com/JiangJie/fetch-t/blob/
|
|
17
|
+
[defines.ts:38](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/defines.ts#L38)
|
|
@@ -14,4 +14,4 @@ Name of abort error;
|
|
|
14
14
|
|
|
15
15
|
## Defined in
|
|
16
16
|
|
|
17
|
-
[constants.ts:4](https://github.com/JiangJie/fetch-t/blob/
|
|
17
|
+
[constants.ts:4](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/constants.ts#L4)
|
|
@@ -14,4 +14,4 @@ Name of timeout error;
|
|
|
14
14
|
|
|
15
15
|
## Defined in
|
|
16
16
|
|
|
17
|
-
[constants.ts:9](https://github.com/JiangJie/fetch-t/blob/
|
|
17
|
+
[constants.ts:9](https://github.com/JiangJie/fetch-t/blob/2e206031a806329279bb68d7ae74aa44f812eb58/src/fetch/constants.ts#L9)
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Abortable fetch wrapper with the ability to specify the return type.",
|
|
4
4
|
"author": "jiang115jie@gmail.com",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.3.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"source": "src/mod.ts",
|
|
9
9
|
"main": "dist/main.cjs",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"typedoc": "^0.26.5",
|
|
53
53
|
"typedoc-plugin-markdown": "^4.2.3",
|
|
54
54
|
"typescript": "^5.5.4",
|
|
55
|
-
"typescript-eslint": "^8.0.
|
|
55
|
+
"typescript-eslint": "^8.0.1"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"happy-rusty": "^1.4.0",
|
package/src/fetch/defines.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import type { AsyncResult } from 'happy-rusty';
|
|
2
|
+
import type { AsyncResult, IOResult } from 'happy-rusty';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Represents the response of a fetch operation, encapsulating the result data or any error that occurred.
|
|
@@ -37,6 +37,21 @@ export interface FetchTask<T> {
|
|
|
37
37
|
*/
|
|
38
38
|
export type FetchResponseType = 'text' | 'arraybuffer' | 'blob' | 'json';
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Represents the progress of a fetch operation.
|
|
42
|
+
*/
|
|
43
|
+
export interface FetchProgress {
|
|
44
|
+
/**
|
|
45
|
+
* The total number of bytes to be received.
|
|
46
|
+
*/
|
|
47
|
+
totalByteLength: number;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The number of bytes received so far.
|
|
51
|
+
*/
|
|
52
|
+
completedByteLength: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
/**
|
|
41
56
|
* Extends the standard `RequestInit` interface from the Fetch API to include additional custom options.
|
|
42
57
|
*/
|
|
@@ -55,6 +70,18 @@ export interface FetchInit extends RequestInit {
|
|
|
55
70
|
* Specifies the maximum time in milliseconds to wait for the fetch request to complete.
|
|
56
71
|
*/
|
|
57
72
|
timeout?: number;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Specifies a function to be called when the fetch request makes progress.
|
|
76
|
+
* @param progressResult - The progress of the fetch request.
|
|
77
|
+
*/
|
|
78
|
+
onProgress?: (progressResult: IOResult<FetchProgress>) => void;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Specifies a function to be called when the fetch request receives a chunk of data.
|
|
82
|
+
* @param chunk - The chunk of data received.
|
|
83
|
+
*/
|
|
84
|
+
onChunk?: (chunk: Uint8Array) => void;
|
|
58
85
|
}
|
|
59
86
|
|
|
60
87
|
/**
|
package/src/fetch/fetch.ts
CHANGED
|
@@ -138,6 +138,8 @@ export function fetchT<T>(url: string | URL, init?: FetchInit): FetchTask<T> | F
|
|
|
138
138
|
abortable = false,
|
|
139
139
|
responseType,
|
|
140
140
|
timeout,
|
|
141
|
+
onProgress,
|
|
142
|
+
onChunk,
|
|
141
143
|
...rest
|
|
142
144
|
} = init ?? {};
|
|
143
145
|
|
|
@@ -163,6 +165,65 @@ export function fetchT<T>(url: string | URL, init?: FetchInit): FetchTask<T> | F
|
|
|
163
165
|
return Err(new FetchError(res.statusText, res.status));
|
|
164
166
|
}
|
|
165
167
|
|
|
168
|
+
if (res.body) {
|
|
169
|
+
// should notify progress or data chunk?
|
|
170
|
+
const shouldNotifyProgress = typeof onProgress === 'function';
|
|
171
|
+
const shouldNotifyChunk = typeof onChunk === 'function';
|
|
172
|
+
|
|
173
|
+
if ((shouldNotifyProgress || shouldNotifyChunk)) {
|
|
174
|
+
// tee the original stream to two streams, one for notify progress, another for response
|
|
175
|
+
const [stream1, stream2] = res.body.tee();
|
|
176
|
+
|
|
177
|
+
const reader = stream1.getReader();
|
|
178
|
+
// may has no content-length
|
|
179
|
+
let totalByteLength: number | null = null;
|
|
180
|
+
let completedByteLength = 0;
|
|
181
|
+
|
|
182
|
+
if (shouldNotifyProgress) {
|
|
183
|
+
// try to get content-length
|
|
184
|
+
// compatible with http/2
|
|
185
|
+
const contentLength = res.headers.get('content-length') ?? res.headers.get('Content-Length');
|
|
186
|
+
if (contentLength == null) {
|
|
187
|
+
// response headers has no content-length
|
|
188
|
+
onProgress(Err(new Error('No content-length in response headers.')));
|
|
189
|
+
} else {
|
|
190
|
+
totalByteLength = parseInt(contentLength, 10);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
reader.read().then(function notify({ done, value }) {
|
|
195
|
+
if (done) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// notify chunk
|
|
200
|
+
if (shouldNotifyChunk) {
|
|
201
|
+
onChunk(value);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// notify progress
|
|
205
|
+
if (shouldNotifyProgress && totalByteLength != null) {
|
|
206
|
+
completedByteLength += value.byteLength;
|
|
207
|
+
onProgress(Ok({
|
|
208
|
+
totalByteLength,
|
|
209
|
+
completedByteLength,
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
// continue to read
|
|
215
|
+
reader.read().then(notify);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// replace the original response with the new one
|
|
219
|
+
res = new Response(stream2, {
|
|
220
|
+
headers: res.headers,
|
|
221
|
+
status: res.status,
|
|
222
|
+
statusText: res.statusText,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
166
227
|
switch (responseType) {
|
|
167
228
|
case 'arraybuffer': {
|
|
168
229
|
return Ok(await res.arrayBuffer() as T);
|