@happy-ts/fetch-t 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +120 -0
- package/LICENSE +21 -674
- package/README.cn.md +138 -46
- package/README.md +136 -49
- package/dist/main.cjs +19 -16
- package/dist/main.cjs.map +1 -1
- package/dist/main.mjs +15 -14
- package/dist/main.mjs.map +1 -1
- package/dist/types.d.ts +432 -206
- package/package.json +39 -26
- package/dist/types.d.ts.map +0 -1
- package/docs/README.md +0 -39
- package/docs/classes/FetchError.md +0 -47
- package/docs/functions/fetchT.md +0 -423
- package/docs/interfaces/FetchInit.md +0 -23
- package/docs/interfaces/FetchProgress.md +0 -16
- package/docs/interfaces/FetchTask.md +0 -46
- package/docs/type-aliases/FetchResponse.md +0 -24
- package/docs/type-aliases/FetchResponseType.md +0 -17
- package/docs/variables/ABORT_ERROR.md +0 -17
- package/docs/variables/TIMEOUT_ERROR.md +0 -17
- package/src/fetch/constants.ts +0 -9
- package/src/fetch/defines.ts +0 -104
- package/src/fetch/fetch.ts +0 -292
- package/src/mod.ts +0 -3
package/README.cn.md
CHANGED
|
@@ -1,82 +1,174 @@
|
|
|
1
1
|
# fetchT
|
|
2
2
|
|
|
3
|
-
[](https://npmjs.org/package/@happy-ts/fetch-t)
|
|
4
|
+
[](https://npmjs.org/package/@happy-ts/fetch-t)
|
|
4
5
|
[](https://jsr.io/@happy-ts/fetch-t)
|
|
5
6
|
[](https://jsr.io/@happy-ts/fetch-t/score)
|
|
6
|
-
[](https://github.com/JiangJie/fetch-t/actions/workflows/test.yml)
|
|
7
8
|
[](https://codecov.io/gh/JiangJie/fetch-t)
|
|
9
|
+
[](https://github.com/JiangJie/fetch-t/blob/main/LICENSE)
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
[English](README.md)
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
类型安全的 Fetch API 封装,支持可中止请求、超时、进度追踪和 Rust 风格的 Result 错误处理。
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## 特性
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
- **可中止请求** - 随时通过 `FetchTask.abort()` 取消请求
|
|
18
|
+
- **类型安全响应** - 通过 `responseType` 参数指定返回类型
|
|
19
|
+
- **超时支持** - 指定毫秒数后自动中止请求
|
|
20
|
+
- **进度追踪** - 通过 `onProgress` 回调监控下载进度
|
|
21
|
+
- **数据流处理** - 通过 `onChunk` 回调访问原始数据块
|
|
22
|
+
- **Result 错误处理** - Rust 风格的 `Result` 类型实现显式错误处理
|
|
23
|
+
- **跨平台** - 支持 Deno、Node.js、Bun 和浏览器
|
|
20
24
|
|
|
21
25
|
## 安装
|
|
22
26
|
|
|
23
27
|
```sh
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
# npm
|
|
29
|
+
npm install @happy-ts/fetch-t
|
|
30
|
+
|
|
31
|
+
# yarn
|
|
27
32
|
yarn add @happy-ts/fetch-t
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#
|
|
33
|
+
|
|
34
|
+
# pnpm
|
|
35
|
+
pnpm add @happy-ts/fetch-t
|
|
36
|
+
|
|
37
|
+
# JSR (Deno)
|
|
33
38
|
deno add @happy-ts/fetch-t
|
|
34
|
-
|
|
39
|
+
|
|
40
|
+
# JSR (Bun)
|
|
35
41
|
bunx jsr add @happy-ts/fetch-t
|
|
36
42
|
```
|
|
37
43
|
|
|
38
|
-
##
|
|
44
|
+
## 快速开始
|
|
39
45
|
|
|
40
|
-
|
|
46
|
+
### 基础用法
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
```ts
|
|
49
|
+
import { fetchT } from '@happy-ts/fetch-t';
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
// GET JSON 数据
|
|
52
|
+
const result = await fetchT<{ id: number; title: string }>('https://api.example.com/data', {
|
|
53
|
+
responseType: 'json',
|
|
54
|
+
});
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
result.inspect(data => {
|
|
57
|
+
console.log(data.title);
|
|
58
|
+
}).inspectErr(err => {
|
|
59
|
+
console.error('请求失败:', err.message);
|
|
60
|
+
});
|
|
61
|
+
```
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
import { fetchT } from '@happy-ts/fetch-t';
|
|
63
|
+
### 可中止请求
|
|
51
64
|
|
|
52
|
-
|
|
65
|
+
```ts
|
|
66
|
+
const task = fetchT('https://api.example.com/large-file', {
|
|
53
67
|
abortable: true,
|
|
68
|
+
responseType: 'arraybuffer',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 5 秒后中止
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
task.abort('用户取消');
|
|
74
|
+
}, 5000);
|
|
75
|
+
|
|
76
|
+
const result = await task.response;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 超时控制
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const result = await fetchT('https://api.example.com/data', {
|
|
54
83
|
responseType: 'json',
|
|
55
|
-
timeout: 3000,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
84
|
+
timeout: 3000, // 3 秒后自动中止
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 进度追踪
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const result = await fetchT('https://api.example.com/large-file', {
|
|
92
|
+
responseType: 'blob',
|
|
93
|
+
onProgress(progressResult) {
|
|
60
94
|
progressResult.inspect(progress => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
console.error(err);
|
|
95
|
+
const percent = (progress.completedByteLength / progress.totalByteLength * 100).toFixed(1);
|
|
96
|
+
console.log(`下载进度: ${percent}%`);
|
|
64
97
|
});
|
|
65
98
|
},
|
|
66
99
|
});
|
|
100
|
+
```
|
|
67
101
|
|
|
68
|
-
|
|
69
|
-
fetchTask.abort('cancel');
|
|
70
|
-
});
|
|
102
|
+
### 错误处理
|
|
71
103
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
104
|
+
```ts
|
|
105
|
+
import { fetchT, ABORT_ERROR, TIMEOUT_ERROR } from '@happy-ts/fetch-t';
|
|
106
|
+
|
|
107
|
+
const result = await fetchT('https://api.example.com/data', {
|
|
108
|
+
responseType: 'json',
|
|
109
|
+
timeout: 3000,
|
|
77
110
|
});
|
|
111
|
+
|
|
112
|
+
if (result.isErr()) {
|
|
113
|
+
const err = result.unwrapErr();
|
|
114
|
+
if (err.name === TIMEOUT_ERROR) {
|
|
115
|
+
console.log('请求超时');
|
|
116
|
+
} else if (err.name === ABORT_ERROR) {
|
|
117
|
+
console.log('请求已中止');
|
|
118
|
+
} else {
|
|
119
|
+
console.log('请求失败:', err.message);
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
console.log('数据:', result.unwrap());
|
|
123
|
+
}
|
|
78
124
|
```
|
|
79
125
|
|
|
80
|
-
|
|
126
|
+
## API
|
|
127
|
+
|
|
128
|
+
### `fetchT(url, options?)`
|
|
129
|
+
|
|
130
|
+
| 参数 | 类型 | 描述 |
|
|
131
|
+
|------|------|------|
|
|
132
|
+
| `url` | `string \| URL` | 请求 URL |
|
|
133
|
+
| `options` | `FetchInit` | 扩展的 fetch 选项 |
|
|
134
|
+
|
|
135
|
+
### `FetchInit` 选项
|
|
136
|
+
|
|
137
|
+
继承标准 `RequestInit`,额外支持:
|
|
138
|
+
|
|
139
|
+
| 选项 | 类型 | 描述 |
|
|
140
|
+
|------|------|------|
|
|
141
|
+
| `abortable` | `boolean` | 如为 `true`,返回 `FetchTask` 而非 `FetchResponse` |
|
|
142
|
+
| `responseType` | `'text' \| 'arraybuffer' \| 'blob' \| 'json'` | 指定返回数据类型 |
|
|
143
|
+
| `timeout` | `number` | 指定毫秒数后自动中止 |
|
|
144
|
+
| `onProgress` | `(result: IOResult<FetchProgress>) => void` | 下载进度回调 |
|
|
145
|
+
| `onChunk` | `(chunk: Uint8Array) => void` | 原始数据块回调 |
|
|
146
|
+
|
|
147
|
+
### `FetchTask<T>`
|
|
148
|
+
|
|
149
|
+
当 `abortable: true` 时返回:
|
|
150
|
+
|
|
151
|
+
| 属性/方法 | 类型 | 描述 |
|
|
152
|
+
|-----------|------|------|
|
|
153
|
+
| `response` | `FetchResponse<T>` | 响应 Promise |
|
|
154
|
+
| `abort(reason?)` | `void` | 中止请求 |
|
|
155
|
+
| `aborted` | `boolean` | 请求是否已中止 |
|
|
156
|
+
|
|
157
|
+
### 常量
|
|
158
|
+
|
|
159
|
+
| 常量 | 描述 |
|
|
160
|
+
|------|------|
|
|
161
|
+
| `ABORT_ERROR` | 中止请求的错误名称 |
|
|
162
|
+
| `TIMEOUT_ERROR` | 超时请求的错误名称 |
|
|
163
|
+
|
|
164
|
+
## 示例
|
|
165
|
+
|
|
166
|
+
更多示例请参见 [examples](examples/) 目录。
|
|
167
|
+
|
|
168
|
+
## 文档
|
|
169
|
+
|
|
170
|
+
完整 API 文档请访问 [https://jiangjie.github.io/fetch-t/](https://jiangjie.github.io/fetch-t/)
|
|
171
|
+
|
|
172
|
+
## 许可证
|
|
81
173
|
|
|
82
|
-
|
|
174
|
+
[MIT](LICENSE)
|
package/README.md
CHANGED
|
@@ -4,84 +4,171 @@
|
|
|
4
4
|
[](https://npmjs.org/package/@happy-ts/fetch-t)
|
|
5
5
|
[](https://jsr.io/@happy-ts/fetch-t)
|
|
6
6
|
[](https://jsr.io/@happy-ts/fetch-t/score)
|
|
7
|
-
[](https://github.com/JiangJie/fetch-t/actions/workflows/test.yml)
|
|
8
8
|
[](https://codecov.io/gh/JiangJie/fetch-t)
|
|
9
|
+
[](https://github.com/JiangJie/fetch-t/blob/main/LICENSE)
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
[中文文档](README.cn.md)
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
Type-safe Fetch API wrapper with abortable requests, timeout support, progress tracking, and Rust-like Result error handling.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
## Features
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Support `progress`.
|
|
17
|
+
- **Abortable Requests** - Cancel requests anytime via `FetchTask.abort()`
|
|
18
|
+
- **Type-safe Responses** - Specify return type with `responseType` parameter
|
|
19
|
+
- **Timeout Support** - Auto-abort requests after specified milliseconds
|
|
20
|
+
- **Progress Tracking** - Monitor download progress with `onProgress` callback
|
|
21
|
+
- **Chunk Streaming** - Access raw data chunks via `onChunk` callback
|
|
22
|
+
- **Result Error Handling** - Rust-like `Result` type for explicit error handling
|
|
23
|
+
- **Cross-platform** - Works with Deno, Node.js, Bun, and browsers
|
|
25
24
|
|
|
26
25
|
## Installation
|
|
27
26
|
|
|
28
27
|
```sh
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
# npm
|
|
29
|
+
npm install @happy-ts/fetch-t
|
|
30
|
+
|
|
31
|
+
# yarn
|
|
32
32
|
yarn add @happy-ts/fetch-t
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
#
|
|
33
|
+
|
|
34
|
+
# pnpm
|
|
35
|
+
pnpm add @happy-ts/fetch-t
|
|
36
|
+
|
|
37
|
+
# JSR (Deno)
|
|
38
38
|
deno add @happy-ts/fetch-t
|
|
39
|
-
|
|
39
|
+
|
|
40
|
+
# JSR (Bun)
|
|
40
41
|
bunx jsr add @happy-ts/fetch-t
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
##
|
|
44
|
+
## Quick Start
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
### Basic Usage
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
```ts
|
|
49
|
+
import { fetchT } from '@happy-ts/fetch-t';
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
// GET JSON data
|
|
52
|
+
const result = await fetchT<{ id: number; title: string }>('https://api.example.com/data', {
|
|
53
|
+
responseType: 'json',
|
|
54
|
+
});
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
result.inspect(data => {
|
|
57
|
+
console.log(data.title);
|
|
58
|
+
}).inspectErr(err => {
|
|
59
|
+
console.error('Request failed:', err.message);
|
|
60
|
+
});
|
|
61
|
+
```
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
import { fetchT } from '@happy-ts/fetch-t';
|
|
63
|
+
### Abortable Request
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
```ts
|
|
66
|
+
const task = fetchT('https://api.example.com/large-file', {
|
|
58
67
|
abortable: true,
|
|
68
|
+
responseType: 'arraybuffer',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Abort after 5 seconds
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
task.abort('User cancelled');
|
|
74
|
+
}, 5000);
|
|
75
|
+
|
|
76
|
+
const result = await task.response;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### With Timeout
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const result = await fetchT('https://api.example.com/data', {
|
|
59
83
|
responseType: 'json',
|
|
60
|
-
timeout: 3000,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
timeout: 3000, // Auto-abort after 3 seconds
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Progress Tracking
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const result = await fetchT('https://api.example.com/large-file', {
|
|
92
|
+
responseType: 'blob',
|
|
93
|
+
onProgress(progressResult) {
|
|
65
94
|
progressResult.inspect(progress => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
console.error(err);
|
|
95
|
+
const percent = (progress.completedByteLength / progress.totalByteLength * 100).toFixed(1);
|
|
96
|
+
console.log(`Download: ${percent}%`);
|
|
69
97
|
});
|
|
70
98
|
},
|
|
71
99
|
});
|
|
100
|
+
```
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
fetchTask.abort('cancel');
|
|
75
|
-
});
|
|
102
|
+
### Error Handling
|
|
76
103
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
104
|
+
```ts
|
|
105
|
+
import { fetchT, ABORT_ERROR, TIMEOUT_ERROR } from '@happy-ts/fetch-t';
|
|
106
|
+
|
|
107
|
+
const result = await fetchT('https://api.example.com/data', {
|
|
108
|
+
responseType: 'json',
|
|
109
|
+
timeout: 3000,
|
|
82
110
|
});
|
|
111
|
+
|
|
112
|
+
if (result.isErr()) {
|
|
113
|
+
const err = result.unwrapErr();
|
|
114
|
+
if (err.name === TIMEOUT_ERROR) {
|
|
115
|
+
console.log('Request timed out');
|
|
116
|
+
} else if (err.name === ABORT_ERROR) {
|
|
117
|
+
console.log('Request was aborted');
|
|
118
|
+
} else {
|
|
119
|
+
console.log('Request failed:', err.message);
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
console.log('Data:', result.unwrap());
|
|
123
|
+
}
|
|
83
124
|
```
|
|
84
125
|
|
|
85
|
-
|
|
126
|
+
## API
|
|
127
|
+
|
|
128
|
+
### `fetchT(url, options?)`
|
|
129
|
+
|
|
130
|
+
| Parameter | Type | Description |
|
|
131
|
+
|-----------|------|-------------|
|
|
132
|
+
| `url` | `string \| URL` | Request URL |
|
|
133
|
+
| `options` | `FetchInit` | Extended fetch options |
|
|
134
|
+
|
|
135
|
+
### `FetchInit` Options
|
|
136
|
+
|
|
137
|
+
Extends standard `RequestInit` with:
|
|
138
|
+
|
|
139
|
+
| Option | Type | Description |
|
|
140
|
+
|--------|------|-------------|
|
|
141
|
+
| `abortable` | `boolean` | If `true`, returns `FetchTask` instead of `FetchResponse` |
|
|
142
|
+
| `responseType` | `'text' \| 'arraybuffer' \| 'blob' \| 'json'` | Specifies return data type |
|
|
143
|
+
| `timeout` | `number` | Auto-abort after milliseconds |
|
|
144
|
+
| `onProgress` | `(result: IOResult<FetchProgress>) => void` | Download progress callback |
|
|
145
|
+
| `onChunk` | `(chunk: Uint8Array) => void` | Raw data chunk callback |
|
|
146
|
+
|
|
147
|
+
### `FetchTask<T>`
|
|
148
|
+
|
|
149
|
+
Returned when `abortable: true`:
|
|
150
|
+
|
|
151
|
+
| Property/Method | Type | Description |
|
|
152
|
+
|-----------------|------|-------------|
|
|
153
|
+
| `response` | `FetchResponse<T>` | The response promise |
|
|
154
|
+
| `abort(reason?)` | `void` | Abort the request |
|
|
155
|
+
| `aborted` | `boolean` | Whether request was aborted |
|
|
156
|
+
|
|
157
|
+
### Constants
|
|
158
|
+
|
|
159
|
+
| Constant | Description |
|
|
160
|
+
|----------|-------------|
|
|
161
|
+
| `ABORT_ERROR` | Error name for aborted requests |
|
|
162
|
+
| `TIMEOUT_ERROR` | Error name for timed out requests |
|
|
163
|
+
|
|
164
|
+
## Examples
|
|
165
|
+
|
|
166
|
+
For more examples, see the [examples](examples/) directory.
|
|
167
|
+
|
|
168
|
+
## Documentation
|
|
169
|
+
|
|
170
|
+
Full API documentation is available at [https://jiangjie.github.io/fetch-t/](https://jiangjie.github.io/fetch-t/)
|
|
171
|
+
|
|
172
|
+
## License
|
|
86
173
|
|
|
87
|
-
|
|
174
|
+
[MIT](LICENSE)
|
package/dist/main.cjs
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
|
+
|
|
5
|
+
const happyRusty = require('happy-rusty');
|
|
6
|
+
const invariant = require('tiny-invariant');
|
|
5
7
|
|
|
6
8
|
const ABORT_ERROR = "AbortError";
|
|
7
9
|
const TIMEOUT_ERROR = "TimeoutError";
|
|
8
10
|
|
|
9
11
|
class FetchError extends Error {
|
|
10
12
|
/**
|
|
11
|
-
* The name
|
|
13
|
+
* The error name, always `'FetchError'`.
|
|
12
14
|
*/
|
|
13
15
|
name = "FetchError";
|
|
14
16
|
/**
|
|
15
|
-
* The status code of the response.
|
|
17
|
+
* The HTTP status code of the response (e.g., 404, 500).
|
|
16
18
|
*/
|
|
17
19
|
status = 0;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new FetchError instance.
|
|
22
|
+
*
|
|
23
|
+
* @param message - The status text from the HTTP response (e.g., "Not Found").
|
|
24
|
+
* @param status - The HTTP status code (e.g., 404).
|
|
25
|
+
*/
|
|
18
26
|
constructor(message, status) {
|
|
19
27
|
super(message);
|
|
20
28
|
this.status = status;
|
|
@@ -26,7 +34,7 @@ function fetchT(url, init) {
|
|
|
26
34
|
invariant(url instanceof URL, () => `Url must be a string or URL object but received ${url}.`);
|
|
27
35
|
}
|
|
28
36
|
const {
|
|
29
|
-
// default not
|
|
37
|
+
// default not abortable
|
|
30
38
|
abortable = false,
|
|
31
39
|
responseType,
|
|
32
40
|
timeout,
|
|
@@ -59,7 +67,7 @@ function fetchT(url, init) {
|
|
|
59
67
|
let totalByteLength = null;
|
|
60
68
|
let completedByteLength = 0;
|
|
61
69
|
if (shouldNotifyProgress) {
|
|
62
|
-
const contentLength = res.headers.get("content-length")
|
|
70
|
+
const contentLength = res.headers.get("content-length");
|
|
63
71
|
if (contentLength == null) {
|
|
64
72
|
onProgress(happyRusty.Err(new Error("No content-length in response headers.")));
|
|
65
73
|
} else {
|
|
@@ -116,16 +124,12 @@ function fetchT(url, init) {
|
|
|
116
124
|
});
|
|
117
125
|
if (shouldWaitTimeout) {
|
|
118
126
|
const timer = setTimeout(() => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
controller.abort(error);
|
|
123
|
-
}
|
|
127
|
+
const error = new Error();
|
|
128
|
+
error.name = TIMEOUT_ERROR;
|
|
129
|
+
controller.abort(error);
|
|
124
130
|
}, timeout);
|
|
125
131
|
cancelTimer = () => {
|
|
126
|
-
|
|
127
|
-
clearTimeout(timer);
|
|
128
|
-
}
|
|
132
|
+
clearTimeout(timer);
|
|
129
133
|
cancelTimer = null;
|
|
130
134
|
};
|
|
131
135
|
}
|
|
@@ -143,9 +147,8 @@ function fetchT(url, init) {
|
|
|
143
147
|
return response;
|
|
144
148
|
}
|
|
145
149
|
};
|
|
146
|
-
} else {
|
|
147
|
-
return response;
|
|
148
150
|
}
|
|
151
|
+
return response;
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
exports.ABORT_ERROR = ABORT_ERROR;
|
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, 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, E = any> = AsyncResult<T, E>;\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, Error>;\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, Error>;\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, Error>;\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, Error>;\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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"}
|
|
1
|
+
{"version":3,"file":"main.cjs","sources":["../src/fetch/constants.ts","../src/fetch/defines.ts","../src/fetch/fetch.ts"],"sourcesContent":["/**\n * Error name for aborted fetch requests.\n *\n * This matches the standard `AbortError` name used by the Fetch API when a request\n * is cancelled via `AbortController.abort()`.\n *\n * @example\n * ```typescript\n * import { fetchT, ABORT_ERROR } from '@happy-ts/fetch-t';\n *\n * const task = fetchT('https://api.example.com/data', { abortable: true });\n * task.abort();\n *\n * const result = await task.response;\n * result.inspectErr((err) => {\n * if (err.name === ABORT_ERROR) {\n * console.log('Request was aborted');\n * }\n * });\n * ```\n */\nexport const ABORT_ERROR = 'AbortError' as const;\n\n/**\n * Error name for timed out fetch requests.\n *\n * This is set on the `Error.name` property when a request exceeds the specified\n * `timeout` duration and is automatically aborted.\n *\n * @example\n * ```typescript\n * import { fetchT, TIMEOUT_ERROR } from '@happy-ts/fetch-t';\n *\n * const result = await fetchT('https://api.example.com/slow-endpoint', {\n * timeout: 5000, // 5 seconds\n * });\n *\n * result.inspectErr((err) => {\n * if (err.name === TIMEOUT_ERROR) {\n * console.log('Request timed out after 5 seconds');\n * }\n * });\n * ```\n */\nexport const TIMEOUT_ERROR = 'TimeoutError' as const;\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { AsyncResult, IOResult } from 'happy-rusty';\n\n/**\n * Represents the response of a fetch operation as an async Result type.\n *\n * This is an alias for `AsyncResult<T, E>` from the `happy-rusty` library,\n * providing Rust-like error handling without throwing exceptions.\n *\n * @typeParam T - The type of the data expected in a successful response.\n * @typeParam E - The type of the error (defaults to `any`). Typically `Error` or `FetchError`.\n *\n * @example\n * ```typescript\n * import { fetchT, type FetchResponse } from '@happy-ts/fetch-t';\n *\n * // FetchResponse is a Promise that resolves to Result<T, E>\n * const response: FetchResponse<string> = fetchT('https://api.example.com', {\n * responseType: 'text',\n * });\n *\n * const result = await response;\n * result\n * .inspect((text) => console.log('Success:', text))\n * .inspectErr((err) => console.error('Error:', err));\n * ```\n */\nexport type FetchResponse<T, E = any> = AsyncResult<T, E>;\n\n/**\n * Represents an abortable fetch operation with control methods.\n *\n * Returned when `abortable: true` is set in the fetch options. Provides\n * the ability to cancel the request and check its abort status.\n *\n * @typeParam T - The type of the data expected in the response.\n *\n * @example\n * ```typescript\n * import { fetchT, type FetchTask } from '@happy-ts/fetch-t';\n *\n * interface User {\n * id: number;\n * name: string;\n * }\n *\n * const task: FetchTask<User> = fetchT<User>('https://api.example.com/user/1', {\n * abortable: true,\n * responseType: 'json',\n * });\n *\n * // Check if aborted\n * console.log('Is aborted:', task.aborted); // false\n *\n * // Abort with optional reason\n * task.abort('User navigated away');\n *\n * // Access the response (will be an error after abort)\n * const result = await task.response;\n * result.inspectErr((err) => console.log('Aborted:', err.message));\n * ```\n */\nexport interface FetchTask<T> {\n /**\n * Aborts the fetch task, optionally with a reason.\n *\n * Once aborted, the `response` promise will resolve to an `Err` containing\n * an `AbortError`. The abort reason can be any value and will be passed\n * to the underlying `AbortController.abort()`.\n *\n * @param reason - An optional value indicating why the task was aborted.\n * This can be an Error, string, or any other value.\n */\n abort(reason?: any): void;\n\n /**\n * Indicates whether the fetch task has been aborted.\n *\n * Returns `true` if `abort()` was called or if the request timed out.\n */\n readonly aborted: boolean;\n\n /**\n * The response promise of the fetch task.\n *\n * Resolves to `Ok<T>` on success, or `Err<Error>` on failure (including abort).\n */\n readonly response: FetchResponse<T>;\n}\n\n/**\n * Specifies the expected response type for automatic parsing.\n *\n * - `'text'` - Parse response as string via `Response.text()`\n * - `'json'` - Parse response as JSON via `Response.json()`\n * - `'arraybuffer'` - Parse response as ArrayBuffer via `Response.arrayBuffer()`\n * - `'blob'` - Parse response as Blob via `Response.blob()`\n *\n * If not specified, the raw `Response` object is returned.\n *\n * @example\n * ```typescript\n * import { fetchT, type FetchResponseType } from '@happy-ts/fetch-t';\n *\n * const responseType: FetchResponseType = 'json';\n *\n * const result = await fetchT('https://api.example.com/data', { responseType });\n * ```\n */\nexport type FetchResponseType = 'text' | 'arraybuffer' | 'blob' | 'json';\n\n/**\n * Represents the download progress of a fetch operation.\n *\n * Passed to the `onProgress` callback when tracking download progress.\n * Note: Progress tracking requires the server to send a `Content-Length` header.\n *\n * @example\n * ```typescript\n * import { fetchT, type FetchProgress } from '@happy-ts/fetch-t';\n *\n * await fetchT('https://example.com/file.zip', {\n * responseType: 'blob',\n * onProgress: (result) => {\n * result.inspect((progress: FetchProgress) => {\n * const percent = (progress.completedByteLength / progress.totalByteLength) * 100;\n * console.log(`Downloaded: ${percent.toFixed(1)}%`);\n * });\n * },\n * });\n * ```\n */\nexport interface FetchProgress {\n /**\n * The total number of bytes to be received (from Content-Length header).\n */\n totalByteLength: number;\n\n /**\n * The number of bytes received so far.\n */\n completedByteLength: number;\n}\n\n/**\n * Extended fetch options that add additional capabilities to the standard `RequestInit`.\n *\n * @example\n * ```typescript\n * import { fetchT, type FetchInit } from '@happy-ts/fetch-t';\n *\n * const options: FetchInit = {\n * // Standard RequestInit options\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ key: 'value' }),\n *\n * // Extended options\n * abortable: true, // Return FetchTask for manual abort control\n * responseType: 'json', // Auto-parse response as JSON\n * timeout: 10000, // Abort after 10 seconds\n * onProgress: (result) => { // Track download progress\n * result.inspect(({ completedByteLength, totalByteLength }) => {\n * console.log(`${completedByteLength}/${totalByteLength}`);\n * });\n * },\n * onChunk: (chunk) => { // Receive raw data chunks\n * console.log('Received chunk:', chunk.byteLength, 'bytes');\n * },\n * };\n *\n * const task = fetchT('https://api.example.com/upload', options);\n * ```\n */\nexport interface FetchInit extends RequestInit {\n /**\n * When `true`, returns a `FetchTask` instead of `FetchResponse`.\n *\n * The `FetchTask` provides `abort()` method and `aborted` status.\n *\n * @default false\n */\n abortable?: boolean;\n\n /**\n * Specifies how the response body should be parsed.\n *\n * - `'text'` - Returns `string`\n * - `'json'` - Returns parsed JSON (type `T`)\n * - `'arraybuffer'` - Returns `ArrayBuffer`\n * - `'blob'` - Returns `Blob`\n * - `undefined` - Returns raw `Response` object\n */\n responseType?: FetchResponseType;\n\n /**\n * Maximum time in milliseconds to wait for the request to complete.\n *\n * If exceeded, the request is automatically aborted with a `TimeoutError`.\n * Must be a positive number.\n */\n timeout?: number;\n\n /**\n * Callback invoked during download to report progress.\n *\n * Receives an `IOResult<FetchProgress>`:\n * - `Ok(FetchProgress)` - Progress update with byte counts\n * - `Err(Error)` - If `Content-Length` header is missing (called once)\n *\n * @param progressResult - The progress result, either success with progress data or error.\n */\n onProgress?: (progressResult: IOResult<FetchProgress>) => void;\n\n /**\n * Callback invoked when a chunk of data is received.\n *\n * Useful for streaming or processing data as it arrives.\n * Each chunk is a `Uint8Array` containing the raw bytes.\n *\n * @param chunk - The raw data chunk received from the response stream.\n */\n onChunk?: (chunk: Uint8Array) => void;\n}\n\n/**\n * Custom error class for HTTP error responses (non-2xx status codes).\n *\n * Thrown when `Response.ok` is `false`. Contains the HTTP status code\n * for programmatic error handling.\n *\n * @example\n * ```typescript\n * import { fetchT, FetchError } from '@happy-ts/fetch-t';\n *\n * const result = await fetchT('https://api.example.com/not-found', {\n * responseType: 'json',\n * });\n *\n * result.inspectErr((err) => {\n * if (err instanceof FetchError) {\n * console.log('HTTP Status:', err.status); // e.g., 404\n * console.log('Status Text:', err.message); // e.g., \"Not Found\"\n *\n * // Handle specific status codes\n * switch (err.status) {\n * case 401:\n * console.log('Unauthorized - please login');\n * break;\n * case 404:\n * console.log('Resource not found');\n * break;\n * case 500:\n * console.log('Server error');\n * break;\n * }\n * }\n * });\n * ```\n */\nexport class FetchError extends Error {\n /**\n * The error name, always `'FetchError'`.\n */\n override name = 'FetchError';\n\n /**\n * The HTTP status code of the response (e.g., 404, 500).\n */\n status = 0;\n\n /**\n * Creates a new FetchError instance.\n *\n * @param message - The status text from the HTTP response (e.g., \"Not Found\").\n * @param status - The HTTP status code (e.g., 404).\n */\n constructor(message: string, status: number) {\n super(message);\n this.status = status;\n }\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 an abortable `FetchTask`.\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, must include `abortable: true` and `responseType: 'text'`.\n * @returns A `FetchTask` representing the abortable 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 an abortable `FetchTask`.\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, must include `abortable: true` and `responseType: 'arraybuffer'`.\n * @returns A `FetchTask` representing the abortable 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 an abortable `FetchTask`.\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, must include `abortable: true` and `responseType: 'blob'`.\n * @returns A `FetchTask` representing the abortable 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 an abortable `FetchTask`.\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, must include `abortable: true` and `responseType: 'json'`.\n * @returns A `FetchTask` representing the abortable operation with a response parsed as type `T`.\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.\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, must include `responseType: '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, Error>;\n\n/**\n * Fetches a resource from the network as an ArrayBuffer.\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, must include `responseType: '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, Error>;\n\n/**\n * Fetches a resource from the network as a Blob.\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, must include `responseType: '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, Error>;\n\n/**\n * Fetches a resource from the network and parses it as JSON.\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, must include `responseType: 'json'`.\n * @returns A `FetchResponse` representing the operation with a response parsed as type `T`.\n */\nexport function fetchT<T>(url: string | URL, init: FetchInit & {\n responseType: 'json';\n}): FetchResponse<T, Error>;\n\n/**\n * Fetches a resource from the network and returns an abortable `FetchTask` 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, must include `abortable: true`.\n * @returns A `FetchTask` representing the abortable operation with a `Response` object.\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` with a generic `Response` object.\n *\n * @param url - The resource to fetch. Can be a URL object or a string representing a URL.\n * @param init - Optional additional options for the fetch operation.\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 * Enhanced fetch function that wraps the native Fetch API with additional capabilities.\n *\n * Features:\n * - **Abortable requests**: Set `abortable: true` to get a `FetchTask` with `abort()` method.\n * - **Type-safe responses**: Use `responseType` to automatically parse responses as text, JSON, ArrayBuffer, or Blob.\n * - **Timeout support**: Set `timeout` in milliseconds to auto-abort long-running requests.\n * - **Progress tracking**: Use `onProgress` callback to track download progress (requires Content-Length header).\n * - **Chunk streaming**: Use `onChunk` callback to receive raw data chunks as they arrive.\n * - **Result type error handling**: Returns `Result<T, Error>` instead of throwing exceptions.\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, extending standard `RequestInit` with custom properties.\n * @returns A `FetchTask<T>` if `abortable: true`, otherwise a `FetchResponse<T>` (which is `AsyncResult<T, Error>`).\n * @throws {Error} If `url` is not a string or URL object.\n * @throws {Error} If `timeout` is specified but is not a positive number.\n *\n * @example\n * // Basic GET request - returns Response object wrapped in Result\n * const result = await fetchT('https://api.example.com/data');\n * result\n * .inspect((res) => console.log('Status:', res.status))\n * .inspectErr((err) => console.error('Error:', err));\n *\n * @example\n * // GET JSON with type safety\n * interface User {\n * id: number;\n * name: string;\n * }\n * const result = await fetchT<User>('https://api.example.com/user/1', {\n * responseType: 'json',\n * });\n * result.inspect((user) => console.log(user.name));\n *\n * @example\n * // POST request with JSON body\n * const result = await fetchT<User>('https://api.example.com/users', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ name: 'John' }),\n * responseType: 'json',\n * });\n *\n * @example\n * // Abortable request with timeout\n * const task = fetchT('https://api.example.com/data', {\n * abortable: true,\n * timeout: 5000, // 5 seconds\n * });\n *\n * // Cancel the request if needed\n * task.abort('User cancelled');\n *\n * // Check if aborted\n * console.log('Aborted:', task.aborted);\n *\n * // Wait for response\n * const result = await task.response;\n *\n * @example\n * // Track download progress\n * const result = await fetchT('https://example.com/large-file.zip', {\n * responseType: 'blob',\n * onProgress: (progressResult) => {\n * progressResult\n * .inspect(({ completedByteLength, totalByteLength }) => {\n * const percent = ((completedByteLength / totalByteLength) * 100).toFixed(1);\n * console.log(`Progress: ${percent}%`);\n * })\n * .inspectErr((err) => console.warn('Progress unavailable:', err.message));\n * },\n * });\n *\n * @example\n * // Stream data chunks\n * const chunks: Uint8Array[] = [];\n * const result = await fetchT('https://example.com/stream', {\n * onChunk: (chunk) => chunks.push(chunk),\n * });\n */\nexport function fetchT<T>(url: string | URL, init?: FetchInit): FetchTask<T> | FetchResponse<T> {\n // Fast path: most URLs are passed as strings\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 abortable\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 // Content-Length may not be present in response headers\n let totalByteLength: number | null = null;\n let completedByteLength = 0;\n\n if (shouldNotifyProgress) {\n // Headers.get() is case-insensitive per spec\n const contentLength = 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 // Continue reading the stream\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 const error = new Error();\n error.name = TIMEOUT_ERROR;\n controller.abort(error);\n }, timeout);\n\n cancelTimer = (): void => {\n clearTimeout(timer);\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 }\n\n return response;\n}"],"names":["Err","Ok"],"mappings":";;;;;;;AAqBO,MAAM,WAAA,GAAc;AAuBpB,MAAM,aAAA,GAAgB;;ACwNtB,MAAM,mBAAmB,KAAA,CAAM;AAAA;AAAA;AAAA;AAAA,EAIzB,IAAA,GAAO,YAAA;AAAA;AAAA;AAAA;AAAA,EAKhB,MAAA,GAAS,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT,WAAA,CAAY,SAAiB,MAAA,EAAgB;AACzC,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAClB;AACJ;;AChFO,SAAS,MAAA,CAAU,KAAmB,IAAA,EAAmD;AAE5F,EAAA,IAAI,OAAO,QAAQ,QAAA,EAAU;AACzB,IAAA,SAAA,CAAU,GAAA,YAAe,GAAA,EAAK,MAAM,CAAA,gDAAA,EAAoD,GAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACnG;AAEA,EAAA,MAAM;AAAA;AAAA,IAEF,SAAA,GAAY,KAAA;AAAA,IACZ,YAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAG;AAAA,GACP,GAAI,QAAQ,EAAC;AAEb,EAAA,MAAM,oBAAoB,OAAA,IAAW,IAAA;AACrC,EAAA,IAAI,WAAA;AAEJ,EAAA,IAAI,iBAAA,EAAmB;AACnB,IAAA,SAAA,CAAU,OAAO,YAAY,QAAA,IAAY,OAAA,GAAU,GAAG,MAAM,CAAA,qDAAA,EAAyD,OAAQ,CAAA,CAAA,CAAG,CAAA;AAAA,EACpI;AAEA,EAAA,IAAI,UAAA;AAEJ,EAAA,IAAI,aAAa,iBAAA,EAAmB;AAChC,IAAA,UAAA,GAAa,IAAI,eAAA,EAAgB;AACjC,IAAA,IAAA,CAAK,SAAS,UAAA,CAAW,MAAA;AAAA,EAC7B;AAEA,EAAA,MAAM,WAA6B,KAAA,CAAM,GAAA,EAAK,IAAI,CAAA,CAAE,IAAA,CAAK,OAAO,GAAA,KAA0B;AACtF,IAAA,WAAA,IAAc;AAEd,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACT,MAAA,MAAM,GAAA,CAAI,MAAM,MAAA,EAAO;AACvB,MAAA,OAAOA,eAAI,IAAI,UAAA,CAAW,IAAI,UAAA,EAAY,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,IACzD;AAEA,IAAA,IAAI,IAAI,IAAA,EAAM;AAEV,MAAA,MAAM,oBAAA,GAAuB,OAAO,UAAA,KAAe,UAAA;AACnD,MAAA,MAAM,iBAAA,GAAoB,OAAO,OAAA,KAAY,UAAA;AAE7C,MAAA,IAAK,wBAAwB,iBAAA,EAAoB;AAE7C,QAAA,MAAM,CAAC,OAAA,EAAS,OAAO,CAAA,GAAI,GAAA,CAAI,KAAK,GAAA,EAAI;AAExC,QAAA,MAAM,MAAA,GAAS,QAAQ,SAAA,EAAU;AAEjC,QAAA,IAAI,eAAA,GAAiC,IAAA;AACrC,QAAA,IAAI,mBAAA,GAAsB,CAAA;AAE1B,QAAA,IAAI,oBAAA,EAAsB;AAEtB,UAAA,MAAM,aAAA,GAAgB,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA;AACtD,UAAA,IAAI,iBAAiB,IAAA,EAAM;AAEvB,YAAA,UAAA,CAAWA,cAAA,CAAI,IAAI,KAAA,CAAM,wCAAwC,CAAC,CAAC,CAAA;AAAA,UACvE,CAAA,MAAO;AACH,YAAA,eAAA,GAAkB,QAAA,CAAS,eAAe,EAAE,CAAA;AAAA,UAChD;AAAA,QACJ;AAEA,QAAA,MAAA,CAAO,IAAA,GAAO,IAAA,CAAK,SAAS,OAAO,EAAE,IAAA,EAAM,OAAM,EAAG;AAChD,UAAA,IAAI,IAAA,EAAM;AACN,YAAA;AAAA,UACJ;AAGA,UAAA,IAAI,iBAAA,EAAmB;AACnB,YAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,UACjB;AAGA,UAAA,IAAI,oBAAA,IAAwB,mBAAmB,IAAA,EAAM;AACjD,YAAA,mBAAA,IAAuB,KAAA,CAAM,UAAA;AAC7B,YAAA,UAAA,CAAWC,aAAA,CAAG;AAAA,cACV,eAAA;AAAA,cACA;AAAA,aACH,CAAC,CAAA;AAAA,UACN;AAGA,UAAA,MAAA,CAAO,IAAA,EAAK,CAAE,IAAA,CAAK,MAAM,CAAA;AAAA,QAC7B,CAAC,CAAA;AAGD,QAAA,GAAA,GAAM,IAAI,SAAS,OAAA,EAAS;AAAA,UACxB,SAAS,GAAA,CAAI,OAAA;AAAA,UACb,QAAQ,GAAA,CAAI,MAAA;AAAA,UACZ,YAAY,GAAA,CAAI;AAAA,SACnB,CAAA;AAAA,MACL;AAAA,IACJ;AAEA,IAAA,QAAQ,YAAA;AAAc,MAClB,KAAK,aAAA,EAAe;AAChB,QAAA,OAAOA,aAAA,CAAG,MAAM,GAAA,CAAI,WAAA,EAAkB,CAAA;AAAA,MAC1C;AAAA,MACA,KAAK,MAAA,EAAQ;AACT,QAAA,OAAOA,aAAA,CAAG,MAAM,GAAA,CAAI,IAAA,EAAW,CAAA;AAAA,MACnC;AAAA,MACA,KAAK,MAAA,EAAQ;AACT,QAAA,IAAI;AACA,UAAA,OAAOA,aAAA,CAAG,MAAM,GAAA,CAAI,IAAA,EAAW,CAAA;AAAA,QACnC,CAAA,CAAA,MAAQ;AACJ,UAAA,OAAOD,cAAA,CAAI,IAAI,KAAA,CAAM,qDAAqD,CAAC,CAAA;AAAA,QAC/E;AAAA,MACJ;AAAA,MACA,KAAK,MAAA,EAAQ;AACT,QAAA,OAAOC,aAAA,CAAG,MAAM,GAAA,CAAI,IAAA,EAAW,CAAA;AAAA,MACnC;AAAA,MACA,SAAS;AAEL,QAAA,OAAOA,cAAG,GAAQ,CAAA;AAAA,MACtB;AAAA;AACJ,EACJ,CAAC,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,IAAA,WAAA,IAAc;AAEd,IAAA,OAAOD,eAAI,GAAG,CAAA;AAAA,EAClB,CAAC,CAAA;AAED,EAAA,IAAI,iBAAA,EAAmB;AACnB,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC3B,MAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,EAAM;AACxB,MAAA,KAAA,CAAM,IAAA,GAAO,aAAA;AACb,MAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AAAA,IAC1B,GAAG,OAAO,CAAA;AAEV,IAAA,WAAA,GAAc,MAAY;AACtB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,WAAA,GAAc,IAAA;AAAA,IAClB,CAAA;AAAA,EACJ;AAEA,EAAA,IAAI,SAAA,EAAW;AACX,IAAA,OAAO;AAAA;AAAA,MAEH,MAAM,MAAA,EAAoB;AACtB,QAAA,WAAA,IAAc;AACd,QAAA,UAAA,CAAW,MAAM,MAAM,CAAA;AAAA,MAC3B,CAAA;AAAA,MAEA,IAAI,OAAA,GAAmB;AACnB,QAAA,OAAO,WAAW,MAAA,CAAO,OAAA;AAAA,MAC7B,CAAA;AAAA,MAEA,IAAI,QAAA,GAA6B;AAC7B,QAAA,OAAO,QAAA;AAAA,MACX;AAAA,KACJ;AAAA,EACJ;AAEA,EAAA,OAAO,QAAA;AACX;;;;;;;"}
|