@vielzeug/fetchit 1.0.4 → 1.1.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/README.md ADDED
@@ -0,0 +1,511 @@
1
+ # @vielzeug/fetchit
2
+
3
+ Modern, type-safe HTTP client with intelligent caching, request deduplication, and query management for TypeScript.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Type-Safe** - Full TypeScript support with automatic type inference
8
+ - ✅ **Zero Dependencies** - Only requires `@vielzeug/toolkit` for retry logic
9
+ - ✅ **Lightweight** - ~3 KB gzipped
10
+ - ✅ **Smart Caching** - TanStack Query-inspired caching with stale-while-revalidate
11
+ - ✅ **Request Deduplication** - Prevents duplicate in-flight requests
12
+ - ✅ **Async Validation** - Built-in retry logic with exponential backoff
13
+ - ✅ **Abort Support** - Cancel requests with AbortController
14
+ - ✅ **Framework Agnostic** - Works anywhere JavaScript runs
15
+ - ✅ **Stable Keys** - Property order doesn't matter for cache matching
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ # pnpm
21
+ pnpm add @vielzeug/fetchit
22
+
23
+ # npm
24
+ npm install @vielzeug/fetchit
25
+
26
+ # yarn
27
+ yarn add @vielzeug/fetchit
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ### Simple HTTP Client
33
+
34
+ ```typescript
35
+ import { createHttpClient } from '@vielzeug/fetchit';
36
+
37
+ const http = createHttpClient({
38
+ baseUrl: 'https://api.example.com',
39
+ timeout: 5000,
40
+ headers: { 'Authorization': 'Bearer token' }
41
+ });
42
+
43
+ // Make requests
44
+ const user = await http.get('/users/1');
45
+ const created = await http.post('/users', {
46
+ body: { name: 'Alice', email: 'alice@example.com' }
47
+ });
48
+
49
+ // Update headers dynamically
50
+ http.setHeaders({ 'Authorization': 'Bearer new-token' });
51
+ ```
52
+
53
+ ### Query Client with Caching
54
+
55
+ ```typescript
56
+ import { createQueryClient, createHttpClient } from '@vielzeug/fetchit';
57
+
58
+ const http = createHttpClient({ baseUrl: 'https://api.example.com' });
59
+ const queryClient = createQueryClient({
60
+ cache: {
61
+ staleTime: 5000, // 5 seconds
62
+ gcTime: 300000 // 5 minutes
63
+ }
64
+ });
65
+
66
+ // Fetch with caching
67
+ const user = await queryClient.fetch({
68
+ queryKey: ['users', 1],
69
+ queryFn: () => http.get('/users/1'),
70
+ staleTime: 5000,
71
+ retry: 3
72
+ });
73
+
74
+ // Same request reuses cache
75
+ const sameUser = await queryClient.fetch({
76
+ queryKey: ['users', 1],
77
+ queryFn: () => http.get('/users/1')
78
+ }); // ✅ Returns cached data instantly
79
+ ```
80
+
81
+ ### Using Both Together
82
+
83
+ ```typescript
84
+ import { createHttpClient, createQueryClient } from '@vielzeug/fetchit';
85
+
86
+ // Create HTTP client for requests
87
+ const http = createHttpClient({
88
+ baseUrl: 'https://api.example.com',
89
+ headers: { 'Authorization': 'Bearer token' }
90
+ });
91
+
92
+ // Create query client for caching
93
+ const queryClient = createQueryClient({
94
+ cache: { staleTime: 5000 }
95
+ });
96
+
97
+ // Use HTTP client for simple requests
98
+ await http.post('/analytics', { body: { event: 'click' } });
99
+
100
+ // Use query client for cached data fetching
101
+ await queryClient.fetch({
102
+ queryKey: ['users'],
103
+ queryFn: () => http.get('/users')
104
+ });
105
+
106
+ // Mutations with cache invalidation
107
+ await queryClient.mutate({
108
+ mutationFn: (data) => http.post('/users', { body: data }),
109
+ onSuccess: () => queryClient.invalidate(['users'])
110
+ }, { name: 'Charlie' });
111
+ ```
112
+
113
+ ## API Reference
114
+
115
+ ### HTTP Client
116
+
117
+ #### `createHttpClient(options)`
118
+
119
+ Creates a simple HTTP client for making requests without caching overhead.
120
+
121
+ **Options:**
122
+ - `baseUrl?: string` - Base URL for all requests
123
+ - `headers?: Record<string, string>` - Default headers
124
+ - `timeout?: number` - Request timeout in milliseconds (default: 30000)
125
+ - `dedupe?: boolean` - Enable request deduplication (default: true)
126
+ - `logger?: (level, msg, meta) => void` - Custom logger function
127
+
128
+ **Methods:**
129
+ - `get(url, config?)` - GET request
130
+ - `post(url, config?)` - POST request
131
+ - `put(url, config?)` - PUT request
132
+ - `patch(url, config?)` - PATCH request
133
+ - `delete(url, config?)` - DELETE request
134
+ - `request(method, url, config?)` - Custom method
135
+ - `setHeaders(headers)` - Update default headers
136
+ - `getHeaders()` - Get current headers
137
+
138
+ **Example:**
139
+ ```typescript
140
+ const http = createHttpClient({
141
+ baseUrl: 'https://api.example.com',
142
+ timeout: 5000,
143
+ headers: { 'Authorization': 'Bearer token' }
144
+ });
145
+
146
+ // GET request
147
+ const users = await http.get<User[]>('/users', {
148
+ params: { page: 1, limit: 10 }
149
+ });
150
+
151
+ // POST with body
152
+ const created = await http.post<User>('/users', {
153
+ body: { name: 'Alice', email: 'alice@example.com' }
154
+ });
155
+
156
+ // Custom headers per request
157
+ await http.get('/protected', {
158
+ headers: { 'X-Custom-Header': 'value' }
159
+ });
160
+ ```
161
+
162
+ ---
163
+
164
+ ### Query Client
165
+
166
+ #### `createQueryClient(options)`
167
+
168
+ Creates a query client with intelligent caching and state management.
169
+
170
+ **Options:**
171
+ - `staleTime?: number` - Time in ms before data is considered stale (default: 0)
172
+ - `gcTime?: number` - Time in ms before unused cache is garbage collected (default: 300000)
173
+ - `cache?: { staleTime?, gcTime? }` - Nested cache configuration
174
+ - `refetch?: { onFocus?, onReconnect? }` - Auto-refetch configuration
175
+
176
+ **Methods:**
177
+ - `fetch(options)` - Fetch data with caching
178
+ - `prefetch(options)` - Prefetch data (swallows errors)
179
+ - `mutate(options, variables)` - Execute mutations
180
+ - `invalidate(queryKey)` - Invalidate cached queries
181
+ - `setData(queryKey, data)` - Manually set cache data
182
+ - `getData(queryKey)` - Get cached data
183
+ - `getState(queryKey)` - Get query state
184
+ - `subscribe(queryKey, listener)` - Subscribe to query changes
185
+ - `unsubscribe(queryKey, listener)` - Unsubscribe from changes
186
+ - `clearCache()` - Clear all cached data
187
+ - `getCacheSize()` - Get number of cached entries
188
+
189
+ **Example:**
190
+ ```typescript
191
+ const queryClient = createQueryClient({
192
+ cache: { staleTime: 5000, gcTime: 300000 }
193
+ });
194
+
195
+ // Fetch with caching
196
+ const user = await queryClient.fetch({
197
+ queryKey: ['users', 1],
198
+ queryFn: () => fetch('/users/1').then(r => r.json()),
199
+ staleTime: 5000,
200
+ retry: 3,
201
+ onSuccess: (data) => console.log('Loaded:', data),
202
+ onError: (err) => console.error('Failed:', err)
203
+ });
204
+
205
+ // Subscribe to changes
206
+ const unsubscribe = queryClient.subscribe(['users', 1], (state) => {
207
+ console.log('State:', state.status, state.data);
208
+ });
209
+
210
+ // Manually update cache
211
+ queryClient.setData(['users', 1], (old) => ({
212
+ ...old,
213
+ name: 'Updated Name'
214
+ }));
215
+
216
+ // Invalidate cache
217
+ queryClient.invalidate(['users']); // Invalidates all user queries
218
+ ```
219
+
220
+ ---
221
+
222
+ ## Advanced Features
223
+
224
+ ### Request Deduplication
225
+
226
+ Automatically prevents duplicate in-flight requests.
227
+
228
+ ```typescript
229
+ const http = createHttpClient({ dedupe: true });
230
+
231
+ // These run concurrently but only make ONE request
232
+ const [user1, user2, user3] = await Promise.all([
233
+ http.get('/users/1'),
234
+ http.get('/users/1'),
235
+ http.get('/users/1')
236
+ ]);
237
+
238
+ // All three get the same response
239
+ console.log(user1 === user2 && user2 === user3); // true
240
+ ```
241
+
242
+ ### Retry Logic
243
+
244
+ Built-in retry with exponential backoff.
245
+
246
+ ```typescript
247
+ await queryClient.fetch({
248
+ queryKey: ['users'],
249
+ queryFn: () => fetchUsers(),
250
+ retry: 3, // Retry 3 times (4 attempts total)
251
+ retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000)
252
+ });
253
+ ```
254
+
255
+ ### Abort Requests
256
+
257
+ Cancel requests with AbortController.
258
+
259
+ ```typescript
260
+ const controller = new AbortController();
261
+
262
+ const promise = http.get('/slow-endpoint', {
263
+ signal: controller.signal
264
+ });
265
+
266
+ // Cancel after 1 second
267
+ setTimeout(() => controller.abort(), 1000);
268
+
269
+ try {
270
+ await promise;
271
+ } catch (err) {
272
+ console.log('Request aborted');
273
+ }
274
+ ```
275
+
276
+ ### Cache Invalidation
277
+
278
+ Smart cache invalidation with prefix matching.
279
+
280
+ ```typescript
281
+ // Cache some data
282
+ await queryClient.fetch({
283
+ queryKey: ['users', 1],
284
+ queryFn: () => fetchUser(1)
285
+ });
286
+
287
+ await queryClient.fetch({
288
+ queryKey: ['users', 2],
289
+ queryFn: () => fetchUser(2)
290
+ });
291
+
292
+ // Invalidate all user queries
293
+ queryClient.invalidate(['users']);
294
+
295
+ // Or invalidate specific user
296
+ queryClient.invalidate(['users', 1]);
297
+ ```
298
+
299
+ ### Stable Query Keys
300
+
301
+ Property order doesn't matter for cache matching.
302
+
303
+ ```typescript
304
+ // These are treated as the same query
305
+ const key1 = ['users', { page: 1, filter: 'active' }];
306
+ const key2 = ['users', { filter: 'active', page: 1 }];
307
+
308
+ // Both use the same cache entry
309
+ await queryClient.fetch({ queryKey: key1, queryFn: fetchUsers });
310
+ await queryClient.fetch({ queryKey: key2, queryFn: fetchUsers }); // Uses cache
311
+ ```
312
+
313
+ ### Mutations
314
+
315
+ Execute mutations with optimistic updates and cache invalidation.
316
+
317
+ ```typescript
318
+ await queryClient.mutate({
319
+ mutationFn: async (data) => {
320
+ return await http.post('/users', { body: data });
321
+ },
322
+ onSuccess: (newUser, variables) => {
323
+ // Update cache optimistically
324
+ queryClient.setData(['users'], (old = []) => [...old, newUser]);
325
+ },
326
+ onError: (error, variables) => {
327
+ console.error('Mutation failed:', error);
328
+ },
329
+ onSettled: (data, error, variables) => {
330
+ // Refetch to ensure consistency
331
+ queryClient.invalidate(['users']);
332
+ }
333
+ }, { name: 'Alice', email: 'alice@example.com' });
334
+ ```
335
+
336
+ ### Subscriptions
337
+
338
+ Subscribe to query state changes.
339
+
340
+ ```typescript
341
+ const unsubscribe = queryClient.subscribe(['users', 1], (state) => {
342
+ console.log('Status:', state.status);
343
+ console.log('Data:', state.data);
344
+ console.log('Error:', state.error);
345
+ console.log('Loading:', state.isLoading);
346
+ console.log('Success:', state.isSuccess);
347
+ });
348
+
349
+ // Later, unsubscribe
350
+ unsubscribe();
351
+ ```
352
+
353
+ ---
354
+
355
+ ## TypeScript Support
356
+
357
+ Full TypeScript support with automatic type inference.
358
+
359
+ ```typescript
360
+ import { createHttpClient, type Infer } from '@vielzeug/fetchit';
361
+
362
+ interface User {
363
+ id: number;
364
+ name: string;
365
+ email: string;
366
+ }
367
+
368
+ const http = createHttpClient({ baseUrl: 'https://api.example.com' });
369
+
370
+ // Type inference
371
+ const user = await http.get<User>('/users/1');
372
+ console.log(user.name); // ✅ Type-safe
373
+
374
+ // Mutation types
375
+ await queryClient.mutate<User, { name: string; email: string }>({
376
+ mutationFn: async (vars) => {
377
+ return await http.post<User>('/users', { body: vars });
378
+ },
379
+ onSuccess: (data) => {
380
+ console.log(data.id); // ✅ Type-safe
381
+ }
382
+ }, { name: 'Alice', email: 'alice@example.com' });
383
+ ```
384
+
385
+ ---
386
+
387
+ ## Error Handling
388
+
389
+ Custom error class with detailed information.
390
+
391
+ ```typescript
392
+ import { HttpError } from '@vielzeug/fetchit';
393
+
394
+ try {
395
+ await http.get('/not-found');
396
+ } catch (err) {
397
+ if (err instanceof HttpError) {
398
+ console.log('URL:', err.url); // '/not-found'
399
+ console.log('Method:', err.method); // 'GET'
400
+ console.log('Status:', err.status); // 404
401
+ console.log('Original:', err.original); // Original error
402
+ }
403
+ }
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Comparison with Alternatives
409
+
410
+ | Feature | fetchit | TanStack Query | SWR | axios |
411
+ |---------|---------|----------------|-----|-------|
412
+ | Bundle Size | **~3 KB** | ~15 KB | ~5 KB | ~13 KB |
413
+ | Dependencies | 1 (@vielzeug/toolkit) | 0 | 0 | Many |
414
+ | TypeScript | Native | Native | Good | Good |
415
+ | Caching | ✅ | ✅ | ✅ | ❌ |
416
+ | Request Deduplication | ✅ | ✅ | ✅ | ❌ |
417
+ | Stable Keys | ✅ | ❌ | ❌ | N/A |
418
+ | Retry Logic | ✅ | ✅ | ✅ | ✅ |
419
+ | Framework Agnostic | ✅ | ✅ | ❌ (React) | ✅ |
420
+ | Separate HTTP Client | ✅ | ❌ | ❌ | ✅ |
421
+ | Query Management | ✅ | ✅ | ✅ | ❌ |
422
+
423
+ ---
424
+
425
+ ## Best Practices
426
+
427
+ ### Use HTTP Client for Simple Requests
428
+
429
+ When you don't need caching, use the HTTP client:
430
+
431
+ ```typescript
432
+ const http = createHttpClient({ baseUrl: 'https://api.example.com' });
433
+
434
+ // Simple one-off requests
435
+ await http.post('/analytics/event', { body: { event: 'click' } });
436
+ ```
437
+
438
+ ### Use Query Client for Data Fetching
439
+
440
+ When you need caching and state management:
441
+
442
+ ```typescript
443
+ const queryClient = createQueryClient({ cache: { staleTime: 5000 } });
444
+ const http = createHttpClient({ baseUrl: 'https://api.example.com' });
445
+
446
+ // Fetch and cache user data
447
+ await queryClient.fetch({
448
+ queryKey: ['users', userId],
449
+ queryFn: () => http.get(`/users/${userId}`)
450
+ });
451
+ ```
452
+
453
+ ### Combine Both for Full-Featured Apps
454
+
455
+ Use HTTP client and query client together:
456
+
457
+ ```typescript
458
+ const http = createHttpClient({
459
+ baseUrl: 'https://api.example.com',
460
+ headers: { 'Authorization': 'Bearer token' }
461
+ });
462
+
463
+ const queryClient = createQueryClient({
464
+ cache: { staleTime: 5000 }
465
+ });
466
+
467
+ // HTTP client for simple requests
468
+ await http.post('/events', { body: event });
469
+
470
+ // Query client for cached data
471
+ await queryClient.fetch({
472
+ queryKey: ['users'],
473
+ queryFn: () => http.get('/users')
474
+ });
475
+
476
+ // Mutations with cache invalidation
477
+ await queryClient.mutate({
478
+ mutationFn: (data) => http.post('/users', { body: data }),
479
+ onSuccess: () => queryClient.invalidate(['users'])
480
+ }, userData);
481
+ ```
482
+
483
+ ### Optimize Cache Settings
484
+
485
+ ```typescript
486
+ const queryClient = createQueryClient({
487
+ cache: {
488
+ staleTime: 5000, // 5 seconds - how long data is fresh
489
+ gcTime: 300000 // 5 minutes - how long to keep unused data
490
+ }
491
+ });
492
+ ```
493
+
494
+ ---
495
+
496
+ ## License
497
+
498
+ MIT
499
+
500
+ ## Contributing
501
+
502
+ Contributions are welcome! Please read the [Contributing Guide](https://github.com/helmuthdu/vielzeug/blob/main/CONTRIBUTING.md).
503
+
504
+ ## Credits
505
+
506
+ Inspired by [TanStack Query](https://tanstack.com/query) and [SWR](https://swr.vercel.app/).
507
+
508
+
509
+
510
+
511
+
package/dist/fetchit.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./logit/dist/logit.cjs");class E extends Error{url;method;status;originalError;constructor(e,r,n,o,a){super(e),this.name="HttpError",this.url=r,this.method=n,this.status=o,this.originalError=a}}const C=5e3,R=12e4,S={ABORTED:499,BAD_REQUEST:400,CONFLICT:409,FORBIDDEN:403,NOT_ALLOWED:405,NOT_FOUND:404,PRE_CONDITION:412,TIMEOUT:408,UNAUTHORIZED:401},u={ERROR:"ERROR",PENDING:"PENDING",SUCCESS:"SUCCESS"},i=new Map;function T(t,e){if(e.id)return e.id;const{method:r="GET",body:n,headers:o}=e;return JSON.stringify({body:n,headers:o,method:r,url:t})}function f(t,e,r,n,o){const a=Date.now()-o,s=t.toLowerCase(),c=r.method?.toUpperCase()??"GET",l=b(e),p=t==="SUCCESS"?"":"";y.Logit[s](`HTTP::${c}(…/${l}) ${p} ${a}ms`,{req:r,res:n,url:e})}function b(t){return t.replace(/^https?:\/\/[^/]+\//,"")}async function h(t,e,{id:r,retries:n=2}){const o=Date.now(),a=e.method?.toUpperCase()??"GET";try{const s=await fetch(t,e),c=await O(s);return f("SUCCESS",t,e,c,o),d(r,u.SUCCESS),{data:c,ok:s.ok,status:s.status}}catch(s){if(f("ERROR",t,e,s,o),d(r,u.ERROR),n>0&&s instanceof TypeError)return h(t,e,{id:r,retries:n-1});throw new E(s instanceof Error?s.message:"Request failed",t,a,void 0,s)}finally{r&&e.method!=="GET"&&i.delete(r)}}async function O(t){if(t.status===204||t.headers.get("content-length")==="0")return;const e=t.headers.get("content-type")??"";return e.includes("application/json")?t.json():e.includes("text")?t.text():t.blob()}function d(t,e){const r=t?i.get(t):void 0;r&&(r.status=e)}async function m(t,e,r){const n=T(t,e);let o=i.get(n);if(o&&U(e,o)&&(o.controller.abort("Request aborted"),i.delete(n),o=void 0),o&&D(o))return o.request;const a=new AbortController,s=I(t,r),c=g(e,r,a),l=h(s,c,{id:n});return i.set(n,{controller:a,expiresIn:Date.now()+(r?.expiresIn??R),request:l,status:u.PENDING}),l}function U(t,e){return t.invalidate?!0:e?t.cancelable&&e.status===u.PENDING||e.status===u.ERROR:!1}function D(t){return Date.now()<=t.expiresIn}function I(t,e){if(!e?.url)return t;const r=e.url.replace(/\/+$/,""),n=t.replace(/^\/+/,"");return`${r}/${n}`}function N(t){return t instanceof FormData||t instanceof Blob||t instanceof ArrayBuffer||t instanceof URLSearchParams||typeof t=="string"}function g(t,e,r){const n=e?.timeout??C,o=AbortSignal.any([r.signal,AbortSignal.timeout(n)]);let a;const s={};return t.body!==void 0&&(N(t.body)?a=t.body:(a=JSON.stringify(t.body),s["Content-Type"]="application/json")),{...t,body:a,headers:j(e?.headers,t.headers,s),signal:o}}function w(t){const e={};if(t instanceof Headers)t.forEach((r,n)=>{e[n]=r});else if(Array.isArray(t))for(const[r,n]of t)e[r]=n;else for(const[r,n]of Object.entries(t))e[r]=n;return e}function j(t,e,r){const n={};if(t)for(const[o,a]of Object.entries(t))a!==void 0&&(n[o]=a);return e&&Object.assign(n,w(e)),r&&Object.assign(n,r),n}function A(t,e){if(!e||Object.keys(e).length===0)return t;const r=new URL(t);for(const[n,o]of Object.entries(e))o!==void 0&&r.searchParams.append(n,String(o));return r.toString()}function P(t={url:""}){const e=r=>(n,o={})=>m(n,{...o,method:r},t);return{cleanupCache(){const r=Date.now(),n=Array.from(i.entries()).filter(([,o])=>r>o.expiresIn).map(([o])=>o);for(const o of n)i.delete(o);return n.length},clearCache(){i.clear()},delete:e("DELETE"),get:e("GET"),getCacheSize(){return i.size},invalidateCache(r){return i.delete(r)},patch:e("PATCH"),post:e("POST"),put:e("PUT"),setHeaders(r){t.headers=Object.fromEntries(Object.entries({...t.headers,...r}).filter(([,n])=>n!==void 0))}}}exports.HttpError=E;exports.RequestErrorType=S;exports.RequestStatus=u;exports.buildUrl=A;exports.createHttpClient=P;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require("./toolkit/dist/logit/dist/logit.cjs");const J=require("./toolkit/dist/async/retry.cjs"),V=0,Y=5*6e4,W=3e4,K=3,x=!0,v="application/json",z="content-type";class j extends Error{url;method;status;original;constructor(c,p,a,h,i){super(c),this.name="HttpError",this.url=p,this.method=a,this.status=h,this.original=i}}function _(t){return t instanceof Error?t:new Error(String(t))}function X(t,c,p){const a=(t||"").replace(/\/+$/,""),h=c.replace(/^\/+/,""),i=a?`${a}/${h}`:h;if(!p)return i;const m=Object.entries(p).filter(([,g])=>g!==void 0).map(([g,w])=>`${encodeURIComponent(g)}=${encodeURIComponent(String(w))}`).join("&");return m?`${i}${i.includes("?")?"&":"?"}${m}`:i}function Z(t){return typeof FormData<"u"&&t instanceof FormData||typeof Blob<"u"&&t instanceof Blob||typeof URLSearchParams<"u"&&t instanceof URLSearchParams||typeof t=="string"||t instanceof ArrayBuffer||t&&typeof t=="object"&&Object.prototype.toString.call(t)==="[object ArrayBuffer]"?!0:!!ArrayBuffer.isView?.(t)}function F(t){return t===null?"null":t===void 0?"undefined":typeof t!="object"?JSON.stringify(t):Array.isArray(t)?`[${t.map(a=>F(a)).join(",")}]`:`{${Object.keys(t).sort().map(a=>`${JSON.stringify(a)}:${F(t[a])}`).join(",")}}`}function tt(t){if(t==null)return"null";if(typeof FormData<"u"&&t instanceof FormData)return"[FormData]";if(typeof Blob<"u"&&t instanceof Blob)return`[Blob:${t.size}:${t.type}]`;if(typeof URLSearchParams<"u"&&t instanceof URLSearchParams)return`[URLSearchParams:${t.toString()}]`;if(t instanceof ArrayBuffer)return`[ArrayBuffer:${t.byteLength}]`;if(ArrayBuffer.isView?.(t))return`[ArrayBufferView:${t.byteLength}]`;if(typeof t=="string")return t;if(typeof t=="object")try{return F(t)}catch{return"[Object]"}try{return JSON.stringify(t)}catch{return"[Unknown]"}}function et(t,c){if(t===0||t===Number.POSITIVE_INFINITY){if(c)return{clear:()=>{},signal:c};const i=new AbortController;return{clear:()=>{},signal:i.signal}}if(typeof AbortSignal<"u"&&"timeout"in AbortSignal&&!c){const i=AbortSignal.timeout(t);return{clear:()=>{},signal:i}}const p=new AbortController,a=()=>p.abort();c&&(c.aborted?p.abort():c.addEventListener("abort",a,{once:!0}));const h=setTimeout(()=>p.abort(),t);return{clear:()=>{clearTimeout(h),c&&c.removeEventListener("abort",a)},signal:p.signal}}async function rt(t){if(t.status===204)return;const c=t.headers.get(z)??"";if(c.includes(v))return await t.json();if(c.startsWith("text/"))return await t.text();try{return await t.blob()}catch{return await t.text()}}function nt(t={}){const{baseUrl:c="",headers:p={},timeout:a=W,dedupe:h=x,logger:i}=t;let m={...p};const g=new Map;function w(f,d,C){i&&i(f,d,C)}async function A(f,d,C={}){const T=X(c,d,C.params),E=(f||"GET").toUpperCase(),{body:S,headers:R,dedupe:B,signal:N,...P}=C,$=B!==!1&&h,e=$?JSON.stringify({body:tt(S),full:T,m:E}):"";if($&&g.has(e))return g.get(e);const{signal:n,clear:r}=et(a,N??null),s={method:E,...P,headers:{...m,...R},signal:n};S!==void 0&&!Z(S)?(s.body=JSON.stringify(S),s.headers={[z]:v,...s.headers}):S!==void 0&&(s.body=S);const y=(async()=>{const l=Date.now();try{const u=await fetch(T,s),b=await rt(u);if(w("info",`${E} ${T} - ${u.status} (${Date.now()-l}ms)`,{req:s,res:b}),!u.ok)throw new j("Non-OK response",T,E,u.status,b);return b}catch(u){throw w("error",`${E} ${T} - ERROR`,u),u instanceof j?u:new j(_(u).message,T,E,void 0,u)}finally{r(),$&&g.delete(e)}})();return $&&g.set(e,y),y}return{delete:(f,d)=>A("DELETE",f,d),get:(f,d)=>A("GET",f,d),getHeaders:()=>({...m}),patch:(f,d)=>A("PATCH",f,d),post:(f,d)=>A("POST",f,d),put:(f,d)=>A("PUT",f,d),request:A,setHeaders(f){m=Object.fromEntries(Object.entries({...m,...f}).filter(([,d])=>d!==void 0))}}}function ot(t){const c=t?.cache?.staleTime??t?.staleTime??V,p=t?.cache?.gcTime??t?.gcTime??Y,a=new Map,h=new Map;function i(e){return F(e)}function m(e){const n=i(e);let r=a.get(n);return r||(r={abortController:null,data:void 0,dataUpdatedAt:0,error:null,errorUpdatedAt:0,fetchedAt:0,gcTimer:null,observers:new Set,promise:null,status:"idle"},a.set(n,r),h.set(n,n)),r}function g(e){const n={data:e.data,dataUpdatedAt:e.dataUpdatedAt,error:e.error,errorUpdatedAt:e.errorUpdatedAt,fetchedAt:e.fetchedAt,isError:e.status==="error",isIdle:e.status==="idle",isLoading:e.status==="pending",isSuccess:e.status==="success",status:e.status};e.observers.forEach(r=>{try{r(n)}catch{}})}function w(e,n,r){n.gcTimer&&(clearTimeout(n.gcTimer),n.gcTimer=null),r>0&&(n.gcTimer=setTimeout(()=>{a.delete(e),h.delete(e)},r))}function A(e){e.abortController?.abort(),e.gcTimer&&(clearTimeout(e.gcTimer),e.gcTimer=null)}function f(e,n,r=K){const s=e===!1?1:(e??r)+1;let y,l;return typeof n=="function"?(y=void 0,l=u=>n(u-1)):typeof n=="number"?(y=n,l=void 0):(y=1e3,l=(u,b)=>Math.min(b*2,3e4)),{backoff:l,delay:y,times:s}}async function d(e){const{queryKey:n,queryFn:r,staleTime:s=c,gcTime:y=p,enabled:l=!0,retry:u=K,retryDelay:b,onSuccess:L,onError:I}=e;if(!l)throw new Error("Query disabled");const H=i(n),o=m(n);if(o.status==="success"&&Date.now()-o.dataUpdatedAt<s)return o.data;if(o.promise)return o.promise;const U=new AbortController;o.abortController=U,o.status="pending",g(o);const{times:G,delay:M,backoff:Q}=f(u,b),k=(async()=>{try{const O=await J.retry(()=>r(),{backoff:Q,delay:M,signal:U.signal,times:G}),D=Date.now();o.data=O,o.status="success",o.dataUpdatedAt=D,o.fetchedAt=D,o.error=null,o.promise=null,o.abortController=null,w(H,o,y);try{L?.(O)}catch{}return g(o),O}catch(O){const D=_(O),q=U.signal.aborted||D.name==="AbortError";q?(o.status="idle",o.error=null):(o.status="error",o.error=D,o.errorUpdatedAt=Date.now()),o.promise=null,o.abortController=null;try{q||I?.(D)}catch{}throw g(o),D}})();return o.promise=k,k}async function C(e){return d({...e,enabled:!0}).catch(()=>{})}function T(e){const n=i(e),r=a.get(n);if(r){A(r),a.delete(n),h.delete(n);return}const s=[],y=n.slice(0,-1);for(const[l,u]of h.entries())if(u===n||u.startsWith(`${y},`)){const L=a.get(l);L&&A(L),s.push(l)}for(const l of s)a.delete(l),h.delete(l)}function E(){a.forEach(A),a.clear(),h.clear()}function S(e,n){const r=i(e),s=m(e);s.data=typeof n=="function"?n(s.data):n,s.dataUpdatedAt=Date.now(),s.fetchedAt=s.fetchedAt||Date.now(),s.status="success",w(r,s,p),g(s)}function R(e){const n=i(e);return a.get(n)?.data??void 0}function B(e){const n=i(e),r=a.get(n);return r?{data:r.data,dataUpdatedAt:r.dataUpdatedAt,error:r.error,errorUpdatedAt:r.errorUpdatedAt,fetchedAt:r.fetchedAt,isError:r.status==="error",isIdle:r.status==="idle",isLoading:r.status==="pending",isSuccess:r.status==="success",status:r.status}:null}function N(e,n){const r=m(e);r.observers.add(n);const s={data:r.data,dataUpdatedAt:r.dataUpdatedAt,error:r.error,errorUpdatedAt:r.errorUpdatedAt,fetchedAt:r.fetchedAt,isError:r.status==="error",isIdle:r.status==="idle",isLoading:r.status==="pending",isSuccess:r.status==="success",status:r.status};return n(s),()=>r.observers.delete(n)}function P(e,n){const r=i(e),s=a.get(r);s&&s.observers.delete(n)}async function $(e,n){const{mutationFn:r,onSuccess:s,onError:y,onSettled:l,retry:u=!1,retryDelay:b}=e,{times:L,delay:I,backoff:H}=f(u,b,0);try{const o=await J.retry(()=>r(n),{backoff:H,delay:I,times:L});try{s?.(o,n)}catch{}try{l?.(o,null,n)}catch{}return o}catch(o){const U=_(o);try{y?.(U,n)}catch{}try{l?.(void 0,U,n)}catch{}throw U}}return{clearCache:E,fetch:d,getCacheSize:()=>a.size,getData:R,getState:B,invalidate:T,mutate:$,prefetch:C,setData:S,subscribe:N,unsubscribe:P}}exports.HttpError=j;exports.createHttpClient=nt;exports.createQueryClient=ot;
2
2
  //# sourceMappingURL=fetchit.cjs.map