@talosjs/fetcher 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Talos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,476 @@
1
+ # @talosjs/fetcher
2
+
3
+ Lightweight HTTP client with typed headers, response parsing, and configurable request/response handling for external API integration. This package provides a fluent API for HTTP operations with built-in support for authentication tokens, content types, request cancellation, and file uploads.
4
+
5
+ ![Browser](https://img.shields.io/badge/Browser-Compatible-green?style=flat-square&logo=googlechrome)
6
+ ![Bun](https://img.shields.io/badge/Bun-Compatible-orange?style=flat-square&logo=bun)
7
+ ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)
8
+ ![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
9
+
10
+ ## Features
11
+
12
+ ✅ **Simple API** - Intuitive methods for GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS
13
+
14
+ ✅ **Typed Responses** - Generic response types with `ResponseDataType<T>` for full TypeScript support
15
+
16
+ ✅ **Authentication** - Built-in support for Bearer and Basic authentication tokens
17
+
18
+ ✅ **File Uploads** - Easy file upload with automatic FormData handling
19
+
20
+ ✅ **Request Cancellation** - Abort in-flight requests with AbortController integration
21
+
22
+ ✅ **Header Management** - Typed header manipulation via `@talosjs/http-header`
23
+
24
+ ✅ **Base URL Support** - Configure base URL for cleaner API calls
25
+
26
+ ✅ **Cloneable** - Create independent copies of configured fetcher instances
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ bun add @talosjs/fetcher
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### Basic GET Request
37
+
38
+ ```typescript
39
+ import { Fetcher } from '@talosjs/fetcher';
40
+
41
+ const fetcher = new Fetcher('https://api.example.com');
42
+
43
+ const response = await fetcher.get<{ users: User[] }>('/users');
44
+
45
+ if (response.success) {
46
+ console.log(response.data.users);
47
+ } else {
48
+ console.error(response.message);
49
+ }
50
+ ```
51
+
52
+ ### POST Request with Data
53
+
54
+ ```typescript
55
+ import { Fetcher } from '@talosjs/fetcher';
56
+
57
+ const fetcher = new Fetcher('https://api.example.com');
58
+
59
+ const response = await fetcher.post<{ user: User }>('/users', {
60
+ name: 'John Doe',
61
+ email: 'john@example.com'
62
+ });
63
+
64
+ if (response.success) {
65
+ console.log('Created user:', response.data.user);
66
+ }
67
+ ```
68
+
69
+ ### With Authentication
70
+
71
+ ```typescript
72
+ import { Fetcher } from '@talosjs/fetcher';
73
+
74
+ const fetcher = new Fetcher('https://api.example.com');
75
+
76
+ // Set Bearer token
77
+ fetcher.setBearerToken('your-jwt-token');
78
+
79
+ const response = await fetcher.get<{ profile: Profile }>('/me');
80
+
81
+ // Clear token when done
82
+ fetcher.clearBearerToken();
83
+ ```
84
+
85
+ ### File Upload
86
+
87
+ ```typescript
88
+ import { Fetcher } from '@talosjs/fetcher';
89
+
90
+ const fetcher = new Fetcher('https://api.example.com');
91
+
92
+ const file = document.querySelector('input[type="file"]').files[0];
93
+
94
+ const response = await fetcher.upload<{ url: string }>(
95
+ '/upload',
96
+ file,
97
+ 'avatar' // form field name
98
+ );
99
+
100
+ if (response.success) {
101
+ console.log('Uploaded to:', response.data.url);
102
+ }
103
+ ```
104
+
105
+ ### Request Cancellation
106
+
107
+ ```typescript
108
+ import { Fetcher } from '@talosjs/fetcher';
109
+
110
+ const fetcher = new Fetcher('https://api.example.com');
111
+
112
+ // Start a request
113
+ const promise = fetcher.get('/slow-endpoint');
114
+
115
+ // Cancel it
116
+ fetcher.abort();
117
+
118
+ // Handle the cancellation
119
+ try {
120
+ await promise;
121
+ } catch (error) {
122
+ if (error.name === 'AbortError') {
123
+ console.log('Request was cancelled');
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## API Reference
129
+
130
+ ### Classes
131
+
132
+ #### `Fetcher`
133
+
134
+ HTTP client class for making fetch requests.
135
+
136
+ **Constructor:**
137
+ ```typescript
138
+ new Fetcher(baseURL?: string)
139
+ ```
140
+
141
+ **Parameters:**
142
+ - `baseURL` - Optional base URL to prepend to all request paths
143
+
144
+ **Properties:**
145
+
146
+ ##### `header: Header`
147
+
148
+ Access to the underlying Header instance for advanced header manipulation.
149
+
150
+ **Methods:**
151
+
152
+ ##### `get<T>(path: string): Promise<ResponseDataType<T>>`
153
+
154
+ Performs a GET request.
155
+
156
+ **Parameters:**
157
+ - `path` - Request path (appended to base URL if set)
158
+
159
+ **Returns:** Promise resolving to typed response data
160
+
161
+ **Example:**
162
+ ```typescript
163
+ const response = await fetcher.get<{ items: Item[] }>('/items');
164
+ ```
165
+
166
+ ##### `post<T>(path: string, data?: unknown): Promise<ResponseDataType<T>>`
167
+
168
+ Performs a POST request with optional body data.
169
+
170
+ **Parameters:**
171
+ - `path` - Request path
172
+ - `data` - Optional request body (automatically JSON-stringified for objects)
173
+
174
+ **Returns:** Promise resolving to typed response data
175
+
176
+ **Example:**
177
+ ```typescript
178
+ const response = await fetcher.post<{ id: string }>('/items', { name: 'New Item' });
179
+ ```
180
+
181
+ ##### `put<T>(path: string, data?: unknown): Promise<ResponseDataType<T>>`
182
+
183
+ Performs a PUT request with optional body data.
184
+
185
+ ##### `patch<T>(path: string, data?: unknown): Promise<ResponseDataType<T>>`
186
+
187
+ Performs a PATCH request with optional body data.
188
+
189
+ ##### `delete<T>(path: string): Promise<ResponseDataType<T>>`
190
+
191
+ Performs a DELETE request.
192
+
193
+ ##### `head<T>(path: string): Promise<ResponseDataType<T>>`
194
+
195
+ Performs a HEAD request.
196
+
197
+ ##### `options<T>(path: string): Promise<ResponseDataType<T>>`
198
+
199
+ Performs an OPTIONS request.
200
+
201
+ ##### `request<T>(method: HttpMethodType, path: string, data?: unknown): Promise<ResponseDataType<T>>`
202
+
203
+ Performs a custom HTTP request.
204
+
205
+ **Parameters:**
206
+ - `method` - HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)
207
+ - `path` - Request path
208
+ - `data` - Optional request body
209
+
210
+ **Example:**
211
+ ```typescript
212
+ const response = await fetcher.request<{ result: string }>('POST', '/custom', { foo: 'bar' });
213
+ ```
214
+
215
+ ##### `upload<T>(path: string, file: File | Blob, name?: string): Promise<ResponseDataType<T>>`
216
+
217
+ Uploads a file using multipart/form-data.
218
+
219
+ **Parameters:**
220
+ - `path` - Upload endpoint path
221
+ - `file` - File or Blob to upload
222
+ - `name` - Form field name (default: 'file')
223
+
224
+ **Returns:** Promise resolving to typed response data
225
+
226
+ **Example:**
227
+ ```typescript
228
+ const response = await fetcher.upload<{ url: string }>('/upload', file, 'document');
229
+ ```
230
+
231
+ ##### `setBearerToken(token: string): this`
232
+
233
+ Sets the Authorization header with a Bearer token.
234
+
235
+ **Parameters:**
236
+ - `token` - JWT or other bearer token
237
+
238
+ **Returns:** The fetcher instance for chaining
239
+
240
+ ##### `setBasicToken(token: string): this`
241
+
242
+ Sets the Authorization header with Basic authentication.
243
+
244
+ **Parameters:**
245
+ - `token` - Base64-encoded credentials
246
+
247
+ **Returns:** The fetcher instance for chaining
248
+
249
+ ##### `clearBearerToken(): this`
250
+
251
+ Removes the Authorization header.
252
+
253
+ **Returns:** The fetcher instance for chaining
254
+
255
+ ##### `clearBasicToken(): this`
256
+
257
+ Removes the Authorization header.
258
+
259
+ **Returns:** The fetcher instance for chaining
260
+
261
+ ##### `setContentType(contentType: MimeType): this`
262
+
263
+ Sets the Content-Type header.
264
+
265
+ **Parameters:**
266
+ - `contentType` - MIME type string
267
+
268
+ **Returns:** The fetcher instance for chaining
269
+
270
+ ##### `setLang(lang: string): this`
271
+
272
+ Sets the Accept-Language or custom language header.
273
+
274
+ **Parameters:**
275
+ - `lang` - Language code (e.g., 'en', 'fr')
276
+
277
+ **Returns:** The fetcher instance for chaining
278
+
279
+ ##### `abort(): this`
280
+
281
+ Cancels any in-flight requests and resets the AbortController.
282
+
283
+ **Returns:** The fetcher instance for chaining
284
+
285
+ ##### `clone(): Fetcher`
286
+
287
+ Creates a new Fetcher instance with the same base URL.
288
+
289
+ **Returns:** New Fetcher instance
290
+
291
+ ### Interfaces
292
+
293
+ #### `IFetcher`
294
+
295
+ ```typescript
296
+ interface IFetcher {
297
+ readonly header: Header;
298
+
299
+ setBearerToken(token: string): IFetcher;
300
+ setBasicToken(token: string): IFetcher;
301
+ clearBearerToken(): IFetcher;
302
+ clearBasicToken(): IFetcher;
303
+ setContentType(contentType: MimeType): IFetcher;
304
+ setLang(lang: string): IFetcher;
305
+ abort(): IFetcher;
306
+ clone(): IFetcher;
307
+
308
+ get<T>(path: string): Promise<ResponseDataType<T>>;
309
+ post<T>(path: string, data?: unknown): Promise<ResponseDataType<T>>;
310
+ put<T>(path: string, data?: unknown): Promise<ResponseDataType<T>>;
311
+ patch<T>(path: string, data?: unknown): Promise<ResponseDataType<T>>;
312
+ delete<T>(path: string): Promise<ResponseDataType<T>>;
313
+ head<T>(path: string): Promise<ResponseDataType<T>>;
314
+ options<T>(path: string): Promise<ResponseDataType<T>>;
315
+ request<T>(method: HttpMethodType, path: string, data?: unknown): Promise<ResponseDataType<T>>;
316
+ upload<T>(path: string, file: File | Blob, name?: string): Promise<ResponseDataType<T>>;
317
+ }
318
+ ```
319
+
320
+ ## Advanced Usage
321
+
322
+ ### Creating API Client Classes
323
+
324
+ ```typescript
325
+ import { Fetcher } from '@talosjs/fetcher';
326
+
327
+ class UserApi {
328
+ private fetcher: Fetcher;
329
+
330
+ constructor(baseUrl: string, token?: string) {
331
+ this.fetcher = new Fetcher(baseUrl);
332
+ if (token) {
333
+ this.fetcher.setBearerToken(token);
334
+ }
335
+ }
336
+
337
+ public async getUsers() {
338
+ return this.fetcher.get<{ users: User[] }>('/users');
339
+ }
340
+
341
+ public async createUser(data: CreateUserDto) {
342
+ return this.fetcher.post<{ user: User }>('/users', data);
343
+ }
344
+
345
+ public async updateUser(id: string, data: UpdateUserDto) {
346
+ return this.fetcher.patch<{ user: User }>(`/users/${id}`, data);
347
+ }
348
+
349
+ public async deleteUser(id: string) {
350
+ return this.fetcher.delete<{ success: boolean }>(`/users/${id}`);
351
+ }
352
+ }
353
+ ```
354
+
355
+ ### Request Interceptors with Header Manipulation
356
+
357
+ ```typescript
358
+ import { Fetcher } from '@talosjs/fetcher';
359
+
360
+ const fetcher = new Fetcher('https://api.example.com');
361
+
362
+ // Add custom headers
363
+ fetcher.header.set('X-Request-ID', generateRequestId());
364
+ fetcher.header.set('X-Client-Version', '1.0.0');
365
+
366
+ const response = await fetcher.get('/endpoint');
367
+ ```
368
+
369
+ ### Error Handling
370
+
371
+ ```typescript
372
+ import { Fetcher } from '@talosjs/fetcher';
373
+
374
+ const fetcher = new Fetcher('https://api.example.com');
375
+
376
+ const response = await fetcher.get<{ data: string }>('/resource');
377
+
378
+ if (response.success) {
379
+ // Handle successful response
380
+ console.log(response.data);
381
+ } else if (response.isClientError) {
382
+ // Handle 4xx errors
383
+ console.error('Client error:', response.message);
384
+ } else if (response.isServerError) {
385
+ // Handle 5xx errors
386
+ console.error('Server error:', response.message);
387
+ } else if (response.isNotFound) {
388
+ // Handle 404
389
+ console.error('Resource not found');
390
+ } else if (response.isUnauthorized) {
391
+ // Handle 401
392
+ console.error('Unauthorized - please login');
393
+ }
394
+ ```
395
+
396
+ ### Chaining Configuration
397
+
398
+ ```typescript
399
+ import { Fetcher } from '@talosjs/fetcher';
400
+
401
+ const fetcher = new Fetcher('https://api.example.com')
402
+ .setBearerToken('your-token')
403
+ .setLang('en')
404
+ .setContentType('application/json');
405
+
406
+ const response = await fetcher.post('/data', { key: 'value' });
407
+ ```
408
+
409
+ ### Cloning for Different Contexts
410
+
411
+ ```typescript
412
+ import { Fetcher } from '@talosjs/fetcher';
413
+
414
+ const baseFetcher = new Fetcher('https://api.example.com');
415
+
416
+ // Create authenticated clone
417
+ const authFetcher = baseFetcher.clone();
418
+ authFetcher.setBearerToken('user-token');
419
+
420
+ // Create admin clone
421
+ const adminFetcher = baseFetcher.clone();
422
+ adminFetcher.setBearerToken('admin-token');
423
+
424
+ // Use independently
425
+ await authFetcher.get('/user/profile');
426
+ await adminFetcher.get('/admin/dashboard');
427
+ ```
428
+
429
+ ### Uploading Multiple Files
430
+
431
+ ```typescript
432
+ import { Fetcher } from '@talosjs/fetcher';
433
+
434
+ const fetcher = new Fetcher('https://api.example.com');
435
+
436
+ async function uploadFiles(files: File[]) {
437
+ const results = [];
438
+
439
+ for (const file of files) {
440
+ const response = await fetcher.upload<{ url: string }>(
441
+ '/upload',
442
+ file,
443
+ 'file'
444
+ );
445
+ results.push(response);
446
+ }
447
+
448
+ return results;
449
+ }
450
+ ```
451
+
452
+ ## License
453
+
454
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
455
+
456
+ ## Contributing
457
+
458
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
459
+
460
+ ### Development Setup
461
+
462
+ 1. Clone the repository
463
+ 2. Install dependencies: `bun install`
464
+ 3. Run tests: `bun run test`
465
+ 4. Build the project: `bun run build`
466
+
467
+ ### Guidelines
468
+
469
+ - Write tests for new features
470
+ - Follow the existing code style
471
+ - Update documentation for API changes
472
+ - Ensure all tests pass before submitting PR
473
+
474
+ ---
475
+
476
+ Made with ❤️ by the Talos team
@@ -0,0 +1,54 @@
1
+ import { Header as Header2 } from "@talosjs/http-header";
2
+ import { MimeType as MimeType2 } from "@talosjs/http-mimes";
3
+ import { ResponseDataType as ResponseDataType2 } from "@talosjs/http-response";
4
+ import { HttpMethodType as HttpMethodType2 } from "@talosjs/types";
5
+ import { Header } from "@talosjs/http-header";
6
+ import { MimeType } from "@talosjs/http-mimes";
7
+ import { ResponseDataType } from "@talosjs/http-response";
8
+ import { HttpMethodType } from "@talosjs/types";
9
+ interface IFetcher {
10
+ readonly header: Header;
11
+ setBearerToken(token: string): IFetcher;
12
+ setBasicToken(token: string): IFetcher;
13
+ clearBearerToken(): IFetcher;
14
+ clearBasicToken(): IFetcher;
15
+ setContentType(contentType: MimeType): IFetcher;
16
+ setLang(lang: string): IFetcher;
17
+ abort(): IFetcher;
18
+ clone(): IFetcher;
19
+ get<T extends Record<string, unknown> = Record<string, unknown>>(path: string): Promise<ResponseDataType<T>>;
20
+ post<T extends Record<string, unknown> = Record<string, unknown>>(path: string, data?: unknown): Promise<ResponseDataType<T>>;
21
+ put<T extends Record<string, unknown> = Record<string, unknown>>(path: string, data?: unknown): Promise<ResponseDataType<T>>;
22
+ patch<T extends Record<string, unknown> = Record<string, unknown>>(path: string, data?: unknown): Promise<ResponseDataType<T>>;
23
+ delete<T extends Record<string, unknown> = Record<string, unknown>>(path: string): Promise<ResponseDataType<T>>;
24
+ head<T extends Record<string, unknown> = Record<string, unknown>>(path: string): Promise<ResponseDataType<T>>;
25
+ options<T extends Record<string, unknown> = Record<string, unknown>>(path: string): Promise<ResponseDataType<T>>;
26
+ request<T extends Record<string, unknown> = Record<string, unknown>>(method: HttpMethodType, path: string, data?: unknown): Promise<ResponseDataType<T>>;
27
+ upload<T extends Record<string, unknown> = Record<string, unknown>>(path: string, file: File | Blob, name?: string): Promise<ResponseDataType<T>>;
28
+ }
29
+ declare class Fetcher implements IFetcher {
30
+ private baseURL?;
31
+ private abortController;
32
+ readonly header: Header2;
33
+ constructor(baseURL?: string | undefined);
34
+ setBearerToken(token: string): this;
35
+ setBasicToken(token: string): this;
36
+ clearBearerToken(): this;
37
+ clearBasicToken(): this;
38
+ setContentType(contentType: MimeType2): this;
39
+ setLang(lang: string): this;
40
+ abort(): this;
41
+ clone(): Fetcher;
42
+ get<T extends Record<string, unknown> = Record<string, unknown>>(path: string): Promise<ResponseDataType2<T>>;
43
+ post<T extends Record<string, unknown> = Record<string, unknown>>(path: string, data?: unknown): Promise<ResponseDataType2<T>>;
44
+ put<T extends Record<string, unknown> = Record<string, unknown>>(path: string, data?: unknown): Promise<ResponseDataType2<T>>;
45
+ patch<T extends Record<string, unknown> = Record<string, unknown>>(path: string, data?: unknown): Promise<ResponseDataType2<T>>;
46
+ delete<T extends Record<string, unknown> = Record<string, unknown>>(path: string): Promise<ResponseDataType2<T>>;
47
+ head<T extends Record<string, unknown> = Record<string, unknown>>(path: string): Promise<ResponseDataType2<T>>;
48
+ options<T extends Record<string, unknown> = Record<string, unknown>>(path: string): Promise<ResponseDataType2<T>>;
49
+ request<T extends Record<string, unknown> = Record<string, unknown>>(method: HttpMethodType2, path: string, data?: unknown): Promise<ResponseDataType2<T>>;
50
+ upload<T extends Record<string, unknown> = Record<string, unknown>>(path: string, file: File | Blob, name?: string): Promise<ResponseDataType2<T>>;
51
+ private buildURL;
52
+ private buildRequestOptions;
53
+ }
54
+ export { IFetcher, Fetcher };
package/dist/index.js ADDED
@@ -0,0 +1,129 @@
1
+ // src/Fetcher.ts
2
+ import { Header } from "@talosjs/http-header";
3
+
4
+ class Fetcher {
5
+ baseURL;
6
+ abortController;
7
+ header = new Header;
8
+ constructor(baseURL) {
9
+ this.baseURL = baseURL;
10
+ this.abortController = new AbortController;
11
+ }
12
+ setBearerToken(token) {
13
+ this.header.setBearerToken(token);
14
+ return this;
15
+ }
16
+ setBasicToken(token) {
17
+ this.header.setBasicAuth(token);
18
+ return this;
19
+ }
20
+ clearBearerToken() {
21
+ this.header.remove("Authorization");
22
+ return this;
23
+ }
24
+ clearBasicToken() {
25
+ this.header.remove("Authorization");
26
+ return this;
27
+ }
28
+ setContentType(contentType) {
29
+ this.header.contentType(contentType);
30
+ return this;
31
+ }
32
+ setLang(lang) {
33
+ this.header.setLang(lang);
34
+ return this;
35
+ }
36
+ abort() {
37
+ this.abortController.abort();
38
+ this.abortController = new AbortController;
39
+ return this;
40
+ }
41
+ clone() {
42
+ const cloned = new Fetcher(this.baseURL);
43
+ return cloned;
44
+ }
45
+ async get(path) {
46
+ return this.request("GET", path);
47
+ }
48
+ async post(path, data) {
49
+ return this.request("POST", path, data);
50
+ }
51
+ async put(path, data) {
52
+ return this.request("PUT", path, data);
53
+ }
54
+ async patch(path, data) {
55
+ return this.request("PATCH", path, data);
56
+ }
57
+ async delete(path) {
58
+ return this.request("DELETE", path);
59
+ }
60
+ async head(path) {
61
+ return this.request("HEAD", path);
62
+ }
63
+ async options(path) {
64
+ return this.request("OPTIONS", path);
65
+ }
66
+ async request(method, path, data) {
67
+ const fullURL = this.buildURL(path);
68
+ const requestOptions = this.buildRequestOptions(method, data);
69
+ const response = await fetch(fullURL, requestOptions);
70
+ try {
71
+ return await response.json();
72
+ } catch (error) {
73
+ throw new Error(error instanceof Error ? error.message : "Failed to parse JSON response");
74
+ }
75
+ }
76
+ async upload(path, file, name = "file") {
77
+ const formData = new FormData;
78
+ formData.append(name, file);
79
+ const originalContentType = this.header.get("Content-Type");
80
+ this.header.remove("Content-Type");
81
+ const result = await this.request("POST", path, formData);
82
+ if (originalContentType) {
83
+ this.header.set("Content-Type", originalContentType);
84
+ }
85
+ return result;
86
+ }
87
+ buildURL(url) {
88
+ if (url.startsWith("http://") || url.startsWith("https://")) {
89
+ return url;
90
+ }
91
+ if (!this.baseURL) {
92
+ return url;
93
+ }
94
+ const path = url.startsWith("/") ? url : `/${url}`;
95
+ const baseURL = this.baseURL.endsWith("/") ? this.baseURL.slice(0, -1) : this.baseURL;
96
+ return `${baseURL}${path}`;
97
+ }
98
+ buildRequestOptions(method, data) {
99
+ const headers = this.header.native;
100
+ let body;
101
+ if (data !== undefined && method !== "GET" && method !== "HEAD" && method !== "OPTIONS") {
102
+ if (data instanceof FormData) {
103
+ body = data;
104
+ this.header.clearContentType();
105
+ } else if (typeof data === "string") {
106
+ body = data;
107
+ } else if (data instanceof Blob || data instanceof ArrayBuffer) {
108
+ body = data;
109
+ } else {
110
+ body = JSON.stringify(data);
111
+ if (!this.header.has("Content-Type")) {
112
+ this.header.setJson();
113
+ }
114
+ }
115
+ }
116
+ const requestOptions = {
117
+ method,
118
+ headers,
119
+ ...body !== undefined && { body },
120
+ signal: this.abortController.signal
121
+ };
122
+ return requestOptions;
123
+ }
124
+ }
125
+ export {
126
+ Fetcher
127
+ };
128
+
129
+ //# debugId=6BEC85E0C8278A0F64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/Fetcher.ts"],
4
+ "sourcesContent": [
5
+ "import { Header } from \"@talosjs/http-header\";\nimport type { MimeType } from \"@talosjs/http-mimes\";\nimport type { ResponseDataType } from \"@talosjs/http-response\";\nimport type { HttpMethodType } from \"@talosjs/types\";\nimport type { IFetcher } from \"./types\";\n\nexport class Fetcher implements IFetcher {\n private abortController: AbortController;\n public readonly header: Header = new Header();\n\n constructor(private baseURL?: string) {\n this.abortController = new AbortController();\n }\n\n public setBearerToken(token: string): this {\n this.header.setBearerToken(token);\n\n return this;\n }\n\n public setBasicToken(token: string): this {\n this.header.setBasicAuth(token);\n\n return this;\n }\n\n public clearBearerToken(): this {\n this.header.remove(\"Authorization\");\n\n return this;\n }\n\n public clearBasicToken(): this {\n this.header.remove(\"Authorization\");\n\n return this;\n }\n\n public setContentType(contentType: MimeType): this {\n this.header.contentType(contentType);\n\n return this;\n }\n\n public setLang(lang: string): this {\n this.header.setLang(lang);\n\n return this;\n }\n\n public abort(): this {\n this.abortController.abort();\n this.abortController = new AbortController();\n\n return this;\n }\n\n public clone(): Fetcher {\n const cloned = new Fetcher(this.baseURL);\n return cloned;\n }\n\n public async get<T extends Record<string, unknown> = Record<string, unknown>>(\n path: string,\n ): Promise<ResponseDataType<T>> {\n return this.request<T>(\"GET\", path);\n }\n\n public async post<T extends Record<string, unknown> = Record<string, unknown>>(\n path: string,\n data?: unknown,\n ): Promise<ResponseDataType<T>> {\n return this.request<T>(\"POST\", path, data);\n }\n\n public async put<T extends Record<string, unknown> = Record<string, unknown>>(\n path: string,\n data?: unknown,\n ): Promise<ResponseDataType<T>> {\n return this.request<T>(\"PUT\", path, data);\n }\n\n public async patch<T extends Record<string, unknown> = Record<string, unknown>>(\n path: string,\n data?: unknown,\n ): Promise<ResponseDataType<T>> {\n return this.request<T>(\"PATCH\", path, data);\n }\n\n public async delete<T extends Record<string, unknown> = Record<string, unknown>>(\n path: string,\n ): Promise<ResponseDataType<T>> {\n return this.request<T>(\"DELETE\", path);\n }\n\n public async head<T extends Record<string, unknown> = Record<string, unknown>>(\n path: string,\n ): Promise<ResponseDataType<T>> {\n return this.request<T>(\"HEAD\", path);\n }\n\n public async options<T extends Record<string, unknown> = Record<string, unknown>>(\n path: string,\n ): Promise<ResponseDataType<T>> {\n return this.request<T>(\"OPTIONS\", path);\n }\n\n public async request<T extends Record<string, unknown> = Record<string, unknown>>(\n method: HttpMethodType,\n path: string,\n data?: unknown,\n ): Promise<ResponseDataType<T>> {\n const fullURL = this.buildURL(path);\n const requestOptions = this.buildRequestOptions(method, data);\n const response = await fetch(fullURL, requestOptions);\n\n try {\n return await response.json();\n } catch (error) {\n throw new Error(error instanceof Error ? error.message : \"Failed to parse JSON response\");\n }\n }\n\n public async upload<T extends Record<string, unknown> = Record<string, unknown>>(\n path: string,\n file: File | Blob,\n name = \"file\",\n ): Promise<ResponseDataType<T>> {\n const formData = new FormData();\n formData.append(name, file);\n\n // Clear any existing Content-Type to let browser set multipart/form-data boundary\n const originalContentType = this.header.get(\"Content-Type\");\n this.header.remove(\"Content-Type\");\n\n const result = await this.request<T>(\"POST\", path, formData);\n\n // Restore original Content-Type if it existed\n if (originalContentType) {\n this.header.set(\"Content-Type\", originalContentType);\n }\n\n return result;\n }\n\n private buildURL(url: string): string {\n if (url.startsWith(\"http://\") || url.startsWith(\"https://\")) {\n return url;\n }\n\n if (!this.baseURL) {\n return url;\n }\n\n const path = url.startsWith(\"/\") ? url : `/${url}`;\n const baseURL = this.baseURL.endsWith(\"/\") ? this.baseURL.slice(0, -1) : this.baseURL;\n\n return `${baseURL}${path}`;\n }\n\n private buildRequestOptions(method: string, data?: unknown): RequestInit {\n const headers = this.header.native;\n\n let body: BodyInit | undefined;\n\n if (data !== undefined && method !== \"GET\" && method !== \"HEAD\" && method !== \"OPTIONS\") {\n if (data instanceof FormData) {\n body = data;\n this.header.clearContentType();\n } else if (typeof data === \"string\") {\n body = data;\n } else if (data instanceof Blob || data instanceof ArrayBuffer) {\n body = data;\n } else {\n body = JSON.stringify(data);\n // Set Content-Type to application/json if not already set\n if (!this.header.has(\"Content-Type\")) {\n this.header.setJson();\n }\n }\n }\n\n const requestOptions: RequestInit = {\n method,\n headers,\n ...(body !== undefined && { body }),\n signal: this.abortController.signal,\n };\n\n return requestOptions;\n }\n}\n"
6
+ ],
7
+ "mappings": ";AAAA;AAAA;AAMO,MAAM,QAA4B;AAAA,EAInB;AAAA,EAHZ;AAAA,EACQ,SAAiB,IAAI;AAAA,EAErC,WAAW,CAAS,SAAkB;AAAA,IAAlB;AAAA,IAClB,KAAK,kBAAkB,IAAI;AAAA;AAAA,EAGtB,cAAc,CAAC,OAAqB;AAAA,IACzC,KAAK,OAAO,eAAe,KAAK;AAAA,IAEhC,OAAO;AAAA;AAAA,EAGF,aAAa,CAAC,OAAqB;AAAA,IACxC,KAAK,OAAO,aAAa,KAAK;AAAA,IAE9B,OAAO;AAAA;AAAA,EAGF,gBAAgB,GAAS;AAAA,IAC9B,KAAK,OAAO,OAAO,eAAe;AAAA,IAElC,OAAO;AAAA;AAAA,EAGF,eAAe,GAAS;AAAA,IAC7B,KAAK,OAAO,OAAO,eAAe;AAAA,IAElC,OAAO;AAAA;AAAA,EAGF,cAAc,CAAC,aAA6B;AAAA,IACjD,KAAK,OAAO,YAAY,WAAW;AAAA,IAEnC,OAAO;AAAA;AAAA,EAGF,OAAO,CAAC,MAAoB;AAAA,IACjC,KAAK,OAAO,QAAQ,IAAI;AAAA,IAExB,OAAO;AAAA;AAAA,EAGF,KAAK,GAAS;AAAA,IACnB,KAAK,gBAAgB,MAAM;AAAA,IAC3B,KAAK,kBAAkB,IAAI;AAAA,IAE3B,OAAO;AAAA;AAAA,EAGF,KAAK,GAAY;AAAA,IACtB,MAAM,SAAS,IAAI,QAAQ,KAAK,OAAO;AAAA,IACvC,OAAO;AAAA;AAAA,OAGI,IAAgE,CAC3E,MAC8B;AAAA,IAC9B,OAAO,KAAK,QAAW,OAAO,IAAI;AAAA;AAAA,OAGvB,KAAiE,CAC5E,MACA,MAC8B;AAAA,IAC9B,OAAO,KAAK,QAAW,QAAQ,MAAM,IAAI;AAAA;AAAA,OAG9B,IAAgE,CAC3E,MACA,MAC8B;AAAA,IAC9B,OAAO,KAAK,QAAW,OAAO,MAAM,IAAI;AAAA;AAAA,OAG7B,MAAkE,CAC7E,MACA,MAC8B;AAAA,IAC9B,OAAO,KAAK,QAAW,SAAS,MAAM,IAAI;AAAA;AAAA,OAG/B,OAAmE,CAC9E,MAC8B;AAAA,IAC9B,OAAO,KAAK,QAAW,UAAU,IAAI;AAAA;AAAA,OAG1B,KAAiE,CAC5E,MAC8B;AAAA,IAC9B,OAAO,KAAK,QAAW,QAAQ,IAAI;AAAA;AAAA,OAGxB,QAAoE,CAC/E,MAC8B;AAAA,IAC9B,OAAO,KAAK,QAAW,WAAW,IAAI;AAAA;AAAA,OAG3B,QAAoE,CAC/E,QACA,MACA,MAC8B;AAAA,IAC9B,MAAM,UAAU,KAAK,SAAS,IAAI;AAAA,IAClC,MAAM,iBAAiB,KAAK,oBAAoB,QAAQ,IAAI;AAAA,IAC5D,MAAM,WAAW,MAAM,MAAM,SAAS,cAAc;AAAA,IAEpD,IAAI;AAAA,MACF,OAAO,MAAM,SAAS,KAAK;AAAA,MAC3B,OAAO,OAAO;AAAA,MACd,MAAM,IAAI,MAAM,iBAAiB,QAAQ,MAAM,UAAU,+BAA+B;AAAA;AAAA;AAAA,OAI/E,OAAmE,CAC9E,MACA,MACA,OAAO,QACuB;AAAA,IAC9B,MAAM,WAAW,IAAI;AAAA,IACrB,SAAS,OAAO,MAAM,IAAI;AAAA,IAG1B,MAAM,sBAAsB,KAAK,OAAO,IAAI,cAAc;AAAA,IAC1D,KAAK,OAAO,OAAO,cAAc;AAAA,IAEjC,MAAM,SAAS,MAAM,KAAK,QAAW,QAAQ,MAAM,QAAQ;AAAA,IAG3D,IAAI,qBAAqB;AAAA,MACvB,KAAK,OAAO,IAAI,gBAAgB,mBAAmB;AAAA,IACrD;AAAA,IAEA,OAAO;AAAA;AAAA,EAGD,QAAQ,CAAC,KAAqB;AAAA,IACpC,IAAI,IAAI,WAAW,SAAS,KAAK,IAAI,WAAW,UAAU,GAAG;AAAA,MAC3D,OAAO;AAAA,IACT;AAAA,IAEA,IAAI,CAAC,KAAK,SAAS;AAAA,MACjB,OAAO;AAAA,IACT;AAAA,IAEA,MAAM,OAAO,IAAI,WAAW,GAAG,IAAI,MAAM,IAAI;AAAA,IAC7C,MAAM,UAAU,KAAK,QAAQ,SAAS,GAAG,IAAI,KAAK,QAAQ,MAAM,GAAG,EAAE,IAAI,KAAK;AAAA,IAE9E,OAAO,GAAG,UAAU;AAAA;AAAA,EAGd,mBAAmB,CAAC,QAAgB,MAA6B;AAAA,IACvE,MAAM,UAAU,KAAK,OAAO;AAAA,IAE5B,IAAI;AAAA,IAEJ,IAAI,SAAS,aAAa,WAAW,SAAS,WAAW,UAAU,WAAW,WAAW;AAAA,MACvF,IAAI,gBAAgB,UAAU;AAAA,QAC5B,OAAO;AAAA,QACP,KAAK,OAAO,iBAAiB;AAAA,MAC/B,EAAO,SAAI,OAAO,SAAS,UAAU;AAAA,QACnC,OAAO;AAAA,MACT,EAAO,SAAI,gBAAgB,QAAQ,gBAAgB,aAAa;AAAA,QAC9D,OAAO;AAAA,MACT,EAAO;AAAA,QACL,OAAO,KAAK,UAAU,IAAI;AAAA,QAE1B,IAAI,CAAC,KAAK,OAAO,IAAI,cAAc,GAAG;AAAA,UACpC,KAAK,OAAO,QAAQ;AAAA,QACtB;AAAA;AAAA,IAEJ;AAAA,IAEA,MAAM,iBAA8B;AAAA,MAClC;AAAA,MACA;AAAA,SACI,SAAS,aAAa,EAAE,KAAK;AAAA,MACjC,QAAQ,KAAK,gBAAgB;AAAA,IAC/B;AAAA,IAEA,OAAO;AAAA;AAEX;",
8
+ "debugId": "6BEC85E0C8278A0F64756E2164756E21",
9
+ "names": []
10
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@talosjs/fetcher",
3
+ "description": "Lightweight HTTP client with typed headers, response parsing, and configurable request/response handling for external API integration",
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "files": [
7
+ "dist",
8
+ "LICENSE",
9
+ "README.md",
10
+ "package.json"
11
+ ],
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "license": "MIT",
24
+ "scripts": {
25
+ "test": "bun test tests",
26
+ "build": "bunup",
27
+ "lint": "tsgo --noEmit && bunx biome lint",
28
+ "npm:publish": "bun publish --tolerate-republish --force --production --access public"
29
+ },
30
+ "dependencies": {
31
+ "@talosjs/http-header": "^1.2.12"
32
+ },
33
+ "devDependencies": {
34
+ "@talosjs/http-response": "^1.4.1",
35
+ "@talosjs/http-mimes": "^1.1.11",
36
+ "@talosjs/types": "^1.3.7"
37
+ },
38
+ "keywords": [
39
+ "api-client",
40
+ "bun",
41
+ "fetch",
42
+ "fetcher",
43
+ "http-client",
44
+ "talos",
45
+ "typescript"
46
+ ]
47
+ }