@ooneex/fetcher 0.0.1

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 Ooneex
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,573 @@
1
+ # @ooneex/fetcher
2
+
3
+ A powerful and flexible TypeScript/JavaScript HTTP client library built on top of the native Fetch API. This package provides a comprehensive set of methods for making HTTP requests with automatic JSON handling, header management, file uploads, and response status detection.
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
+ ![Deno](https://img.shields.io/badge/Deno-Compatible-blue?style=flat-square&logo=deno)
8
+ ![Node.js](https://img.shields.io/badge/Node.js-Compatible-green?style=flat-square&logo=node.js)
9
+ ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)
10
+ ![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
11
+
12
+ ## Features
13
+
14
+ ✅ **Native Fetch Based** - Built on top of the modern Fetch API
15
+
16
+ ✅ **Type-Safe** - Full TypeScript support with proper type definitions
17
+
18
+ ✅ **HTTP Methods** - Support for GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
19
+
20
+ ✅ **File Upload** - Built-in file and blob upload functionality
21
+
22
+ ✅ **Header Management** - Comprehensive header manipulation methods
23
+
24
+ ✅ **Authentication** - Bearer token and basic authentication support
25
+
26
+ ✅ **Request Aborting** - Built-in AbortController support
27
+
28
+ ✅ **Status Detection** - Automatic HTTP status code classification
29
+
30
+ ✅ **JSON Handling** - Automatic JSON serialization and deserialization
31
+
32
+ ✅ **Error Handling** - Comprehensive error handling and response parsing
33
+
34
+ ✅ **Cross-Platform** - Works in Browser, Node.js, Bun, and Deno
35
+
36
+ ✅ **Zero Dependencies** - Minimal external dependencies
37
+
38
+ ## Installation
39
+
40
+ ### Bun
41
+ ```bash
42
+ bun add @ooneex/fetcher
43
+ ```
44
+
45
+ ### pnpm
46
+ ```bash
47
+ pnpm add @ooneex/fetcher
48
+ ```
49
+
50
+ ### Yarn
51
+ ```bash
52
+ yarn add @ooneex/fetcher
53
+ ```
54
+
55
+ ### npm
56
+ ```bash
57
+ npm install @ooneex/fetcher
58
+ ```
59
+
60
+ ## Usage
61
+
62
+ ### Basic Usage
63
+
64
+ ```typescript
65
+ import { Fetcher } from '@ooneex/fetcher';
66
+
67
+ const api = new Fetcher('https://api.example.com');
68
+
69
+ // GET request
70
+ const response = await api.get('/users');
71
+ console.log(response.data); // parsed JSON data
72
+ console.log(response.isSuccessful); // true for 2xx status codes
73
+
74
+ // POST request with JSON data
75
+ const newUser = { name: 'John Doe', email: 'john@example.com' };
76
+ const createResponse = await api.post('/users', newUser);
77
+ ```
78
+
79
+ ### Authentication
80
+
81
+ ```typescript
82
+ import { Fetcher } from '@ooneex/fetcher';
83
+
84
+ const api = new Fetcher('https://api.example.com');
85
+
86
+ // Bearer token authentication
87
+ api.setBearerToken('your-jwt-token');
88
+
89
+ // Basic authentication
90
+ api.setBasicToken('base64-encoded-credentials');
91
+
92
+ // Make authenticated requests
93
+ const response = await api.get('/protected-resource');
94
+
95
+ // Clear authentication
96
+ api.clearBearerToken();
97
+ api.clearBasicToken();
98
+ ```
99
+
100
+ ### Header Management
101
+
102
+ ```typescript
103
+ import { Fetcher } from '@ooneex/fetcher';
104
+
105
+ const api = new Fetcher('https://api.example.com');
106
+
107
+ // Set content type
108
+ api.setContentType('application/json');
109
+
110
+ // Set language
111
+ api.setLang('en-US');
112
+
113
+ // Chain header methods
114
+ api.setBearerToken('token')
115
+ .setContentType('application/json')
116
+ .setLang('fr-FR');
117
+
118
+ // Access header object directly
119
+ api.header.set('X-Custom-Header', 'custom-value');
120
+ ```
121
+
122
+ ### File Upload
123
+
124
+ ```typescript
125
+ import { Fetcher } from '@ooneex/fetcher';
126
+
127
+ const api = new Fetcher('https://api.example.com');
128
+
129
+ // Upload a file
130
+ const fileInput = document.querySelector('input[type="file"]');
131
+ const file = fileInput.files[0];
132
+
133
+ const uploadResponse = await api.upload('/upload', file, 'document');
134
+
135
+ // Upload a blob
136
+ const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
137
+ const blobResponse = await api.upload('/upload-text', blob, 'textFile');
138
+ ```
139
+
140
+ ### Request Aborting
141
+
142
+ ```typescript
143
+ import { Fetcher } from '@ooneex/fetcher';
144
+
145
+ const api = new Fetcher('https://api.example.com');
146
+
147
+ // Start a request
148
+ const requestPromise = api.get('/slow-endpoint');
149
+
150
+ // Abort the request
151
+ api.abort();
152
+
153
+ try {
154
+ const response = await requestPromise;
155
+ } catch (error) {
156
+ console.log('Request was aborted');
157
+ }
158
+ ```
159
+
160
+ ### Advanced Usage
161
+
162
+ ```typescript
163
+ import { Fetcher } from '@ooneex/fetcher';
164
+
165
+ const api = new Fetcher('https://api.example.com');
166
+
167
+ // Configure headers and authentication
168
+ api.setBearerToken('jwt-token')
169
+ .setContentType('application/json')
170
+ .setLang('en-US');
171
+
172
+ // Make various HTTP requests
173
+ const users = await api.get('/users');
174
+ const user = await api.post('/users', { name: 'Jane Doe' });
175
+ const updated = await api.put('/users/1', { name: 'Jane Smith' });
176
+ const patched = await api.patch('/users/1', { email: 'jane@example.com' });
177
+ await api.delete('/users/1');
178
+
179
+ // Check response status
180
+ if (user.isSuccessful) {
181
+ console.log('User created:', user.data);
182
+ } else if (user.isClientError) {
183
+ console.log('Client error:', user.message);
184
+ } else if (user.isServerError) {
185
+ console.log('Server error:', user.message);
186
+ }
187
+
188
+ // Clone fetcher for different configurations
189
+ const authenticatedApi = api.clone().setBearerToken('different-token');
190
+ ```
191
+
192
+ ## API Reference
193
+
194
+ ### `Fetcher` Class
195
+
196
+ The main class for making HTTP requests.
197
+
198
+ #### Constructor
199
+
200
+ ##### `new Fetcher(baseURL: string)`
201
+ Creates a new Fetcher instance with the specified base URL.
202
+
203
+ **Parameters:**
204
+ - `baseURL` - The base URL for all requests
205
+
206
+ **Example:**
207
+ ```typescript
208
+ const api = new Fetcher('https://api.example.com');
209
+ ```
210
+
211
+ #### Authentication Methods
212
+
213
+ ##### `setBearerToken(token: string): Fetcher`
214
+ Sets the Authorization header with a Bearer token.
215
+
216
+ **Parameters:**
217
+ - `token` - The Bearer token
218
+
219
+ **Returns:** The Fetcher instance for method chaining
220
+
221
+ **Example:**
222
+ ```typescript
223
+ api.setBearerToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
224
+ ```
225
+
226
+ ##### `setBasicToken(token: string): Fetcher`
227
+ Sets the Authorization header with Basic authentication.
228
+
229
+ **Parameters:**
230
+ - `token` - The base64-encoded credentials
231
+
232
+ **Returns:** The Fetcher instance for method chaining
233
+
234
+ **Example:**
235
+ ```typescript
236
+ api.setBasicToken(btoa('username:password'));
237
+ ```
238
+
239
+ ##### `clearBearerToken(): Fetcher`
240
+ Removes the Authorization header.
241
+
242
+ **Returns:** The Fetcher instance for method chaining
243
+
244
+ ##### `clearBasicToken(): Fetcher`
245
+ Removes the Authorization header.
246
+
247
+ **Returns:** The Fetcher instance for method chaining
248
+
249
+ #### Header Management Methods
250
+
251
+ ##### `setContentType(contentType: MimeType): Fetcher`
252
+ Sets the Content-Type header.
253
+
254
+ **Parameters:**
255
+ - `contentType` - The MIME type for the content
256
+
257
+ **Returns:** The Fetcher instance for method chaining
258
+
259
+ **Example:**
260
+ ```typescript
261
+ api.setContentType('application/json');
262
+ api.setContentType('multipart/form-data');
263
+ ```
264
+
265
+ ##### `setLang(lang: string): Fetcher`
266
+ Sets the Accept-Language header.
267
+
268
+ **Parameters:**
269
+ - `lang` - The language code
270
+
271
+ **Returns:** The Fetcher instance for method chaining
272
+
273
+ **Example:**
274
+ ```typescript
275
+ api.setLang('en-US');
276
+ api.setLang('fr-FR');
277
+ ```
278
+
279
+ #### Request Control Methods
280
+
281
+ ##### `abort(): Fetcher`
282
+ Aborts the current request and creates a new AbortController.
283
+
284
+ **Returns:** The Fetcher instance for method chaining
285
+
286
+ **Example:**
287
+ ```typescript
288
+ api.abort(); // Cancels any ongoing requests
289
+ ```
290
+
291
+ ##### `clone(): Fetcher`
292
+ Creates a new Fetcher instance with the same base URL.
293
+
294
+ **Returns:** A new Fetcher instance
295
+
296
+ **Example:**
297
+ ```typescript
298
+ const newApi = api.clone();
299
+ ```
300
+
301
+ #### HTTP Method Shortcuts
302
+
303
+ ##### `get<T = unknown>(path: string): Promise<FetcherResponseType<T>>`
304
+ Performs a GET request.
305
+
306
+ **Parameters:**
307
+ - `path` - The endpoint path
308
+
309
+ **Returns:** Promise resolving to a FetcherResponseType
310
+
311
+ **Example:**
312
+ ```typescript
313
+ const users = await api.get<User[]>('/users');
314
+ ```
315
+
316
+ ##### `post<T = unknown>(path: string, data?: unknown): Promise<FetcherResponseType<T>>`
317
+ Performs a POST request.
318
+
319
+ **Parameters:**
320
+ - `path` - The endpoint path
321
+ - `data` - Optional request body data
322
+
323
+ **Returns:** Promise resolving to a FetcherResponseType
324
+
325
+ **Example:**
326
+ ```typescript
327
+ const newUser = await api.post<User>('/users', { name: 'John' });
328
+ ```
329
+
330
+ ##### `put<T = unknown>(path: string, data?: unknown): Promise<FetcherResponseType<T>>`
331
+ Performs a PUT request.
332
+
333
+ **Parameters:**
334
+ - `path` - The endpoint path
335
+ - `data` - Optional request body data
336
+
337
+ **Returns:** Promise resolving to a FetcherResponseType
338
+
339
+ **Example:**
340
+ ```typescript
341
+ const updatedUser = await api.put<User>('/users/1', { name: 'Jane' });
342
+ ```
343
+
344
+ ##### `patch<T = unknown>(path: string, data?: unknown): Promise<FetcherResponseType<T>>`
345
+ Performs a PATCH request.
346
+
347
+ **Parameters:**
348
+ - `path` - The endpoint path
349
+ - `data` - Optional request body data
350
+
351
+ **Returns:** Promise resolving to a FetcherResponseType
352
+
353
+ **Example:**
354
+ ```typescript
355
+ const patchedUser = await api.patch<User>('/users/1', { email: 'new@email.com' });
356
+ ```
357
+
358
+ ##### `delete<T = unknown>(path: string): Promise<FetcherResponseType<T>>`
359
+ Performs a DELETE request.
360
+
361
+ **Parameters:**
362
+ - `path` - The endpoint path
363
+
364
+ **Returns:** Promise resolving to a FetcherResponseType
365
+
366
+ **Example:**
367
+ ```typescript
368
+ await api.delete('/users/1');
369
+ ```
370
+
371
+ ##### `head<T = unknown>(path: string): Promise<FetcherResponseType<T>>`
372
+ Performs a HEAD request.
373
+
374
+ **Parameters:**
375
+ - `path` - The endpoint path
376
+
377
+ **Returns:** Promise resolving to a FetcherResponseType
378
+
379
+ **Example:**
380
+ ```typescript
381
+ const headers = await api.head('/users/1');
382
+ ```
383
+
384
+ ##### `options<T = unknown>(path: string): Promise<FetcherResponseType<T>>`
385
+ Performs an OPTIONS request.
386
+
387
+ **Parameters:**
388
+ - `path` - The endpoint path
389
+
390
+ **Returns:** Promise resolving to a FetcherResponseType
391
+
392
+ **Example:**
393
+ ```typescript
394
+ const options = await api.options('/users');
395
+ ```
396
+
397
+ ##### `request<T = unknown>(method: HttpMethodType, path: string, data?: unknown): Promise<FetcherResponseType<T>>`
398
+ Performs a custom HTTP request.
399
+
400
+ **Parameters:**
401
+ - `method` - The HTTP method
402
+ - `path` - The endpoint path
403
+ - `data` - Optional request body data
404
+
405
+ **Returns:** Promise resolving to a FetcherResponseType
406
+
407
+ **Example:**
408
+ ```typescript
409
+ const response = await api.request('PATCH', '/users/1', { status: 'active' });
410
+ ```
411
+
412
+ ##### `upload<T = unknown>(path: string, file: File | Blob, name?: string): Promise<FetcherResponseType<T>>`
413
+ Uploads a file or blob.
414
+
415
+ **Parameters:**
416
+ - `path` - The endpoint path
417
+ - `file` - The file or blob to upload
418
+ - `name` - Optional field name (defaults to "file")
419
+
420
+ **Returns:** Promise resolving to a FetcherResponseType
421
+
422
+ **Example:**
423
+ ```typescript
424
+ const fileInput = document.querySelector('input[type="file"]');
425
+ const file = fileInput.files[0];
426
+ const response = await api.upload('/upload', file, 'document');
427
+ ```
428
+
429
+ #### Properties
430
+
431
+ ##### `header: Header`
432
+ Read-only access to the Header instance for direct header manipulation.
433
+
434
+ **Example:**
435
+ ```typescript
436
+ api.header.set('X-API-Key', 'your-api-key');
437
+ const contentType = api.header.get('Content-Type');
438
+ ```
439
+
440
+ ### Types
441
+
442
+ #### `FetcherResponseType<T>`
443
+ The response object returned by all HTTP methods.
444
+
445
+ **Properties:**
446
+ - `data: T | null` - The parsed response data
447
+ - `message: string | null` - Error message if parsing failed
448
+ - `header: ReadonlyHeader` - Response headers
449
+ - `isInformational: boolean` - True for 1xx status codes
450
+ - `isSuccessful: boolean` - True for 2xx status codes
451
+ - `isRedirect: boolean` - True for 3xx status codes
452
+ - `isClientError: boolean` - True for 4xx status codes
453
+ - `isServerError: boolean` - True for 5xx status codes
454
+ - `isError: boolean` - True for 4xx or 5xx status codes
455
+
456
+ **Example:**
457
+ ```typescript
458
+ const response = await api.get<User[]>('/users');
459
+
460
+ if (response.isSuccessful) {
461
+ console.log('Users:', response.data);
462
+ } else if (response.isClientError) {
463
+ console.error('Client error:', response.message);
464
+ } else if (response.isServerError) {
465
+ console.error('Server error:', response.message);
466
+ }
467
+ ```
468
+
469
+ #### `IFetcher`
470
+ Interface defining the Fetcher contract.
471
+
472
+ **Example:**
473
+ ```typescript
474
+ import { IFetcher } from '@ooneex/fetcher';
475
+
476
+ class CustomFetcher implements IFetcher {
477
+ // Implement all required methods
478
+ }
479
+ ```
480
+
481
+ ## Error Handling
482
+
483
+ The fetcher automatically handles various error scenarios:
484
+
485
+ ```typescript
486
+ import { Fetcher } from '@ooneex/fetcher';
487
+
488
+ const api = new Fetcher('https://api.example.com');
489
+
490
+ try {
491
+ const response = await api.get('/users');
492
+
493
+ if (response.isSuccessful) {
494
+ // Handle successful response
495
+ console.log('Data:', response.data);
496
+ } else if (response.isClientError) {
497
+ // Handle 4xx errors
498
+ console.error('Client error:', response.message);
499
+ } else if (response.isServerError) {
500
+ // Handle 5xx errors
501
+ console.error('Server error:', response.message);
502
+ }
503
+ } catch (error) {
504
+ // Handle network errors or other exceptions
505
+ console.error('Request failed:', error);
506
+ }
507
+ ```
508
+
509
+ ## Advanced Features
510
+
511
+ ### URL Building
512
+ The fetcher automatically handles URL building:
513
+
514
+ ```typescript
515
+ const api = new Fetcher('https://api.example.com');
516
+
517
+ // These all work correctly
518
+ await api.get('/users'); // -> https://api.example.com/users
519
+ await api.get('users'); // -> https://api.example.com/users
520
+ await api.get('https://other.com/api'); // -> https://other.com/api (absolute URL)
521
+
522
+ // Base URL with trailing slash
523
+ const api2 = new Fetcher('https://api.example.com/');
524
+ await api2.get('/users'); // -> https://api.example.com/users
525
+ ```
526
+
527
+ ### Content Type Handling
528
+ The fetcher automatically sets appropriate Content-Type headers:
529
+
530
+ ```typescript
531
+ const api = new Fetcher('https://api.example.com');
532
+
533
+ // Automatic JSON content type for objects
534
+ await api.post('/users', { name: 'John' }); // Content-Type: application/json
535
+
536
+ // FormData handling
537
+ const formData = new FormData();
538
+ formData.append('name', 'John');
539
+ await api.post('/users', formData); // Content-Type: multipart/form-data
540
+
541
+ // String data
542
+ await api.post('/users', 'raw string data');
543
+
544
+ // Blob/ArrayBuffer data
545
+ const blob = new Blob(['data'], { type: 'text/plain' });
546
+ await api.post('/upload', blob);
547
+ ```
548
+
549
+ ## License
550
+
551
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
552
+
553
+ ## Contributing
554
+
555
+ 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.
556
+
557
+ ### Development Setup
558
+
559
+ 1. Clone the repository
560
+ 2. Install dependencies: `bun install`
561
+ 3. Run tests: `bun run test`
562
+ 4. Build the project: `bun run build`
563
+
564
+ ### Guidelines
565
+
566
+ - Write tests for new features
567
+ - Follow the existing code style
568
+ - Update documentation for API changes
569
+ - Ensure all tests pass before submitting PR
570
+
571
+ ---
572
+
573
+ Made with ❤️ by the Ooneex team
@@ -0,0 +1,54 @@
1
+ import { Header as Header2 } from "@ooneex/http-header";
2
+ import { MimeType as MimeType2 } from "@ooneex/http-mimes";
3
+ import { ResponseDataType as ResponseDataType2 } from "@ooneex/http-response";
4
+ import { HttpMethodType as HttpMethodType2 } from "@ooneex/types";
5
+ import { Header } from "@ooneex/http-header";
6
+ import { MimeType } from "@ooneex/http-mimes";
7
+ import { ResponseDataType } from "@ooneex/http-response";
8
+ import { HttpMethodType } from "@ooneex/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);
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,3 @@
1
+ import{Header as c}from"@ooneex/http-header";class T{baseURL;abortController;header=new c;constructor(e){this.baseURL=e;this.abortController=new AbortController}setBearerToken(e){return this.header.setBearerToken(e),this}setBasicToken(e){return this.header.setBasicAuth(e),this}clearBearerToken(){return this.header.remove("Authorization"),this}clearBasicToken(){return this.header.remove("Authorization"),this}setContentType(e){return this.header.contentType(e),this}setLang(e){return this.header.setLang(e),this}abort(){return this.abortController.abort(),this.abortController=new AbortController,this}clone(){return new T(this.baseURL)}async get(e){return this.request("GET",e)}async post(e,n){return this.request("POST",e,n)}async put(e,n){return this.request("PUT",e,n)}async patch(e,n){return this.request("PATCH",e,n)}async delete(e){return this.request("DELETE",e)}async head(e){return this.request("HEAD",e)}async options(e){return this.request("OPTIONS",e)}async request(e,n,r){let s=this.buildURL(n),o=this.buildRequestOptions(e,r),i=await fetch(s,o);try{return await i.json()}catch(t){throw Error(t instanceof Error?t.message:"Failed to parse JSON response")}}async upload(e,n,r="file"){let s=new FormData;s.append(r,n);let o=this.header.get("Content-Type");this.header.remove("Content-Type");let i=await this.request("POST",e,s);if(o)this.header.set("Content-Type",o);return i}buildURL(e){if(e.startsWith("http://")||e.startsWith("https://"))return e;let n=e.startsWith("/")?e:`/${e}`;return`${this.baseURL.endsWith("/")?this.baseURL.slice(0,-1):this.baseURL}${n}`}buildRequestOptions(e,n){let r=this.header.native,s;if(n!==void 0&&e!=="GET"&&e!=="HEAD"&&e!=="OPTIONS"){if(n instanceof FormData)s=n,this.header.clearContentType();else if(typeof n==="string")s=n;else if(n instanceof Blob||n instanceof ArrayBuffer)s=n;else if(s=JSON.stringify(n),!this.header.has("Content-Type"))this.header.setJson()}return{method:e,headers:r,...s!==void 0&&{body:s},signal:this.abortController.signal}}}export{T as Fetcher};
2
+
3
+ //# debugId=8AF8D142203AB99D64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/Fetcher.ts"],
4
+ "sourcesContent": [
5
+ "import { Header } from \"@ooneex/http-header\";\nimport type { MimeType } from \"@ooneex/http-mimes\";\nimport type { ResponseDataType } from \"@ooneex/http-response\";\nimport type { HttpMethodType } from \"@ooneex/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 return new Fetcher(this.baseURL);\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 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": "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",
8
+ "debugId": "8AF8D142203AB99D64756E2164756E21",
9
+ "names": []
10
+ }
Binary file
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@ooneex/fetcher",
3
+ "description": "",
4
+ "version": "0.0.1",
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
+ "publish:prod": "bun publish --tolerate-republish --access public",
29
+ "publish:pack": "bun pm pack --destination ./dist",
30
+ "publish:dry": "bun publish --dry-run"
31
+ },
32
+ "dependencies": {
33
+ "@ooneex/http-header": "0.0.1"
34
+ },
35
+ "devDependencies": {
36
+ "@ooneex/http-response": "0.0.1",
37
+ "@ooneex/http-mimes": "0.0.1",
38
+ "@ooneex/types": "0.0.1"
39
+ }
40
+ }