@fgrzl/fetch 1.1.0-alpha.8 → 1.2.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.
Files changed (224) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/CONTRIBUTING.md +327 -0
  3. package/README.md +90 -20
  4. package/dist/cjs/index.js +1168 -105
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/index.min.js +2 -0
  7. package/dist/cjs/index.min.js.map +1 -0
  8. package/dist/index.d.ts +1428 -10
  9. package/dist/index.js +1124 -91
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.min.js +2 -0
  12. package/dist/index.min.js.map +1 -0
  13. package/package.json +14 -5
  14. package/dist/cjs/client/fetch-client.d.ts +0 -189
  15. package/dist/cjs/client/fetch-client.d.ts.map +0 -1
  16. package/dist/cjs/client/fetch-client.js +0 -339
  17. package/dist/cjs/client/fetch-client.js.map +0 -1
  18. package/dist/cjs/client/index.d.ts +0 -11
  19. package/dist/cjs/client/index.d.ts.map +0 -1
  20. package/dist/cjs/client/index.js +0 -14
  21. package/dist/cjs/client/index.js.map +0 -1
  22. package/dist/cjs/client/types.d.ts +0 -63
  23. package/dist/cjs/client/types.d.ts.map +0 -1
  24. package/dist/cjs/client/types.js +0 -9
  25. package/dist/cjs/client/types.js.map +0 -1
  26. package/dist/cjs/errors/index.d.ts +0 -64
  27. package/dist/cjs/errors/index.d.ts.map +0 -1
  28. package/dist/cjs/errors/index.js +0 -79
  29. package/dist/cjs/errors/index.js.map +0 -1
  30. package/dist/cjs/index.d.ts +0 -65
  31. package/dist/cjs/index.d.ts.map +0 -1
  32. package/dist/cjs/middleware/authentication/authentication.d.ts +0 -31
  33. package/dist/cjs/middleware/authentication/authentication.d.ts.map +0 -1
  34. package/dist/cjs/middleware/authentication/authentication.js +0 -93
  35. package/dist/cjs/middleware/authentication/authentication.js.map +0 -1
  36. package/dist/cjs/middleware/authentication/index.d.ts +0 -37
  37. package/dist/cjs/middleware/authentication/index.d.ts.map +0 -1
  38. package/dist/cjs/middleware/authentication/index.js +0 -42
  39. package/dist/cjs/middleware/authentication/index.js.map +0 -1
  40. package/dist/cjs/middleware/authentication/types.d.ts +0 -73
  41. package/dist/cjs/middleware/authentication/types.d.ts.map +0 -1
  42. package/dist/cjs/middleware/authentication/types.js +0 -6
  43. package/dist/cjs/middleware/authentication/types.js.map +0 -1
  44. package/dist/cjs/middleware/authorization/authorization.d.ts +0 -30
  45. package/dist/cjs/middleware/authorization/authorization.d.ts.map +0 -1
  46. package/dist/cjs/middleware/authorization/authorization.js +0 -82
  47. package/dist/cjs/middleware/authorization/authorization.js.map +0 -1
  48. package/dist/cjs/middleware/authorization/index.d.ts +0 -36
  49. package/dist/cjs/middleware/authorization/index.d.ts.map +0 -1
  50. package/dist/cjs/middleware/authorization/index.js +0 -41
  51. package/dist/cjs/middleware/authorization/index.js.map +0 -1
  52. package/dist/cjs/middleware/authorization/types.d.ts +0 -67
  53. package/dist/cjs/middleware/authorization/types.d.ts.map +0 -1
  54. package/dist/cjs/middleware/authorization/types.js +0 -6
  55. package/dist/cjs/middleware/authorization/types.js.map +0 -1
  56. package/dist/cjs/middleware/cache/cache.d.ts +0 -41
  57. package/dist/cjs/middleware/cache/cache.d.ts.map +0 -1
  58. package/dist/cjs/middleware/cache/cache.js +0 -191
  59. package/dist/cjs/middleware/cache/cache.js.map +0 -1
  60. package/dist/cjs/middleware/cache/index.d.ts +0 -44
  61. package/dist/cjs/middleware/cache/index.d.ts.map +0 -1
  62. package/dist/cjs/middleware/cache/index.js +0 -50
  63. package/dist/cjs/middleware/cache/index.js.map +0 -1
  64. package/dist/cjs/middleware/cache/types.d.ts +0 -89
  65. package/dist/cjs/middleware/cache/types.d.ts.map +0 -1
  66. package/dist/cjs/middleware/cache/types.js +0 -6
  67. package/dist/cjs/middleware/cache/types.js.map +0 -1
  68. package/dist/cjs/middleware/csrf/csrf.d.ts +0 -34
  69. package/dist/cjs/middleware/csrf/csrf.d.ts.map +0 -1
  70. package/dist/cjs/middleware/csrf/csrf.js +0 -94
  71. package/dist/cjs/middleware/csrf/csrf.js.map +0 -1
  72. package/dist/cjs/middleware/csrf/index.d.ts +0 -57
  73. package/dist/cjs/middleware/csrf/index.d.ts.map +0 -1
  74. package/dist/cjs/middleware/csrf/index.js +0 -62
  75. package/dist/cjs/middleware/csrf/index.js.map +0 -1
  76. package/dist/cjs/middleware/csrf/types.d.ts +0 -57
  77. package/dist/cjs/middleware/csrf/types.d.ts.map +0 -1
  78. package/dist/cjs/middleware/csrf/types.js +0 -6
  79. package/dist/cjs/middleware/csrf/types.js.map +0 -1
  80. package/dist/cjs/middleware/index.d.ts +0 -115
  81. package/dist/cjs/middleware/index.d.ts.map +0 -1
  82. package/dist/cjs/middleware/index.js +0 -153
  83. package/dist/cjs/middleware/index.js.map +0 -1
  84. package/dist/cjs/middleware/logging/index.d.ts +0 -42
  85. package/dist/cjs/middleware/logging/index.d.ts.map +0 -1
  86. package/dist/cjs/middleware/logging/index.js +0 -47
  87. package/dist/cjs/middleware/logging/index.js.map +0 -1
  88. package/dist/cjs/middleware/logging/logging.d.ts +0 -29
  89. package/dist/cjs/middleware/logging/logging.d.ts.map +0 -1
  90. package/dist/cjs/middleware/logging/logging.js +0 -171
  91. package/dist/cjs/middleware/logging/logging.js.map +0 -1
  92. package/dist/cjs/middleware/logging/types.d.ts +0 -90
  93. package/dist/cjs/middleware/logging/types.d.ts.map +0 -1
  94. package/dist/cjs/middleware/logging/types.js +0 -6
  95. package/dist/cjs/middleware/logging/types.js.map +0 -1
  96. package/dist/cjs/middleware/rate-limit/index.d.ts +0 -16
  97. package/dist/cjs/middleware/rate-limit/index.d.ts.map +0 -1
  98. package/dist/cjs/middleware/rate-limit/index.js +0 -21
  99. package/dist/cjs/middleware/rate-limit/index.js.map +0 -1
  100. package/dist/cjs/middleware/rate-limit/rate-limit.d.ts +0 -14
  101. package/dist/cjs/middleware/rate-limit/rate-limit.d.ts.map +0 -1
  102. package/dist/cjs/middleware/rate-limit/rate-limit.js +0 -87
  103. package/dist/cjs/middleware/rate-limit/rate-limit.js.map +0 -1
  104. package/dist/cjs/middleware/rate-limit/types.d.ts +0 -97
  105. package/dist/cjs/middleware/rate-limit/types.d.ts.map +0 -1
  106. package/dist/cjs/middleware/rate-limit/types.js +0 -6
  107. package/dist/cjs/middleware/rate-limit/types.js.map +0 -1
  108. package/dist/cjs/middleware/retry/index.d.ts +0 -6
  109. package/dist/cjs/middleware/retry/index.d.ts.map +0 -1
  110. package/dist/cjs/middleware/retry/index.js +0 -11
  111. package/dist/cjs/middleware/retry/index.js.map +0 -1
  112. package/dist/cjs/middleware/retry/retry.d.ts +0 -39
  113. package/dist/cjs/middleware/retry/retry.d.ts.map +0 -1
  114. package/dist/cjs/middleware/retry/retry.js +0 -144
  115. package/dist/cjs/middleware/retry/retry.js.map +0 -1
  116. package/dist/cjs/middleware/retry/types.d.ts +0 -61
  117. package/dist/cjs/middleware/retry/types.d.ts.map +0 -1
  118. package/dist/cjs/middleware/retry/types.js +0 -6
  119. package/dist/cjs/middleware/retry/types.js.map +0 -1
  120. package/dist/client/fetch-client.d.ts +0 -189
  121. package/dist/client/fetch-client.d.ts.map +0 -1
  122. package/dist/client/fetch-client.js +0 -335
  123. package/dist/client/fetch-client.js.map +0 -1
  124. package/dist/client/index.d.ts +0 -11
  125. package/dist/client/index.d.ts.map +0 -1
  126. package/dist/client/index.js +0 -10
  127. package/dist/client/index.js.map +0 -1
  128. package/dist/client/types.d.ts +0 -63
  129. package/dist/client/types.d.ts.map +0 -1
  130. package/dist/client/types.js +0 -8
  131. package/dist/client/types.js.map +0 -1
  132. package/dist/errors/index.d.ts +0 -64
  133. package/dist/errors/index.d.ts.map +0 -1
  134. package/dist/errors/index.js +0 -73
  135. package/dist/errors/index.js.map +0 -1
  136. package/dist/index.d.ts.map +0 -1
  137. package/dist/middleware/authentication/authentication.d.ts +0 -31
  138. package/dist/middleware/authentication/authentication.d.ts.map +0 -1
  139. package/dist/middleware/authentication/authentication.js +0 -90
  140. package/dist/middleware/authentication/authentication.js.map +0 -1
  141. package/dist/middleware/authentication/index.d.ts +0 -37
  142. package/dist/middleware/authentication/index.d.ts.map +0 -1
  143. package/dist/middleware/authentication/index.js +0 -37
  144. package/dist/middleware/authentication/index.js.map +0 -1
  145. package/dist/middleware/authentication/types.d.ts +0 -73
  146. package/dist/middleware/authentication/types.d.ts.map +0 -1
  147. package/dist/middleware/authentication/types.js +0 -5
  148. package/dist/middleware/authentication/types.js.map +0 -1
  149. package/dist/middleware/authorization/authorization.d.ts +0 -30
  150. package/dist/middleware/authorization/authorization.d.ts.map +0 -1
  151. package/dist/middleware/authorization/authorization.js +0 -79
  152. package/dist/middleware/authorization/authorization.js.map +0 -1
  153. package/dist/middleware/authorization/index.d.ts +0 -36
  154. package/dist/middleware/authorization/index.d.ts.map +0 -1
  155. package/dist/middleware/authorization/index.js +0 -36
  156. package/dist/middleware/authorization/index.js.map +0 -1
  157. package/dist/middleware/authorization/types.d.ts +0 -67
  158. package/dist/middleware/authorization/types.d.ts.map +0 -1
  159. package/dist/middleware/authorization/types.js +0 -5
  160. package/dist/middleware/authorization/types.js.map +0 -1
  161. package/dist/middleware/cache/cache.d.ts +0 -41
  162. package/dist/middleware/cache/cache.d.ts.map +0 -1
  163. package/dist/middleware/cache/cache.js +0 -186
  164. package/dist/middleware/cache/cache.js.map +0 -1
  165. package/dist/middleware/cache/index.d.ts +0 -44
  166. package/dist/middleware/cache/index.d.ts.map +0 -1
  167. package/dist/middleware/cache/index.js +0 -44
  168. package/dist/middleware/cache/index.js.map +0 -1
  169. package/dist/middleware/cache/types.d.ts +0 -89
  170. package/dist/middleware/cache/types.d.ts.map +0 -1
  171. package/dist/middleware/cache/types.js +0 -5
  172. package/dist/middleware/cache/types.js.map +0 -1
  173. package/dist/middleware/csrf/csrf.d.ts +0 -34
  174. package/dist/middleware/csrf/csrf.d.ts.map +0 -1
  175. package/dist/middleware/csrf/csrf.js +0 -91
  176. package/dist/middleware/csrf/csrf.js.map +0 -1
  177. package/dist/middleware/csrf/index.d.ts +0 -57
  178. package/dist/middleware/csrf/index.d.ts.map +0 -1
  179. package/dist/middleware/csrf/index.js +0 -57
  180. package/dist/middleware/csrf/index.js.map +0 -1
  181. package/dist/middleware/csrf/types.d.ts +0 -57
  182. package/dist/middleware/csrf/types.d.ts.map +0 -1
  183. package/dist/middleware/csrf/types.js +0 -5
  184. package/dist/middleware/csrf/types.js.map +0 -1
  185. package/dist/middleware/index.d.ts +0 -115
  186. package/dist/middleware/index.d.ts.map +0 -1
  187. package/dist/middleware/index.js +0 -134
  188. package/dist/middleware/index.js.map +0 -1
  189. package/dist/middleware/logging/index.d.ts +0 -42
  190. package/dist/middleware/logging/index.d.ts.map +0 -1
  191. package/dist/middleware/logging/index.js +0 -42
  192. package/dist/middleware/logging/index.js.map +0 -1
  193. package/dist/middleware/logging/logging.d.ts +0 -29
  194. package/dist/middleware/logging/logging.d.ts.map +0 -1
  195. package/dist/middleware/logging/logging.js +0 -168
  196. package/dist/middleware/logging/logging.js.map +0 -1
  197. package/dist/middleware/logging/types.d.ts +0 -90
  198. package/dist/middleware/logging/types.d.ts.map +0 -1
  199. package/dist/middleware/logging/types.js +0 -5
  200. package/dist/middleware/logging/types.js.map +0 -1
  201. package/dist/middleware/rate-limit/index.d.ts +0 -16
  202. package/dist/middleware/rate-limit/index.d.ts.map +0 -1
  203. package/dist/middleware/rate-limit/index.js +0 -16
  204. package/dist/middleware/rate-limit/index.js.map +0 -1
  205. package/dist/middleware/rate-limit/rate-limit.d.ts +0 -14
  206. package/dist/middleware/rate-limit/rate-limit.d.ts.map +0 -1
  207. package/dist/middleware/rate-limit/rate-limit.js +0 -84
  208. package/dist/middleware/rate-limit/rate-limit.js.map +0 -1
  209. package/dist/middleware/rate-limit/types.d.ts +0 -97
  210. package/dist/middleware/rate-limit/types.d.ts.map +0 -1
  211. package/dist/middleware/rate-limit/types.js +0 -5
  212. package/dist/middleware/rate-limit/types.js.map +0 -1
  213. package/dist/middleware/retry/index.d.ts +0 -6
  214. package/dist/middleware/retry/index.d.ts.map +0 -1
  215. package/dist/middleware/retry/index.js +0 -6
  216. package/dist/middleware/retry/index.js.map +0 -1
  217. package/dist/middleware/retry/retry.d.ts +0 -39
  218. package/dist/middleware/retry/retry.d.ts.map +0 -1
  219. package/dist/middleware/retry/retry.js +0 -141
  220. package/dist/middleware/retry/retry.js.map +0 -1
  221. package/dist/middleware/retry/types.d.ts +0 -61
  222. package/dist/middleware/retry/types.d.ts.map +0 -1
  223. package/dist/middleware/retry/types.js +0 -5
  224. package/dist/middleware/retry/types.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,102 +1,1135 @@
1
- /**
2
- * @fileoverview Main library entry point with "pit of success" architecture.
3
- *
4
- * This module exports everything users need in order of discoverability:
5
- * 1. Pre-configured client with smart defaults (80% of users start here)
6
- * 2. FetchClient for custom configurations
7
- * 3. Individual middleware functions for specific needs
8
- * 4. Pre-built middleware stacks for common scenarios
9
- * 5. Types for TypeScript users
10
- */
11
- import { FetchClient } from './client/fetch-client';
12
- import { useProductionStack } from './middleware';
13
- /**
14
- * 🎯 PIT OF SUCCESS: Pre-configured fetch client (Level 1 - 80% of users)
15
- *
16
- * This client is ready to use out of the box with production-ready middleware:
17
- * - Authentication support (configure your token provider)
18
- * - Automatic retries with exponential backoff
19
- * - Response caching for GET requests
20
- * - Request/response logging
21
- * - Rate limiting protection
22
- * - Same-origin credentials for session-based auth (cookies)
23
- *
24
- * @example Just import and use:
25
- * ```typescript
26
- * import api from '@fgrzl/fetch';
27
- *
28
- * // Works immediately - no setup required!
29
- * const users = await api.get('/api/users');
30
- * const newUser = await api.post('/api/users', { name: 'John' });
31
- *
32
- * // With query parameters
33
- * const activeUsers = await api.get('/api/users', { status: 'active', limit: 10 });
34
- * ```
35
- *
36
- * @example Configure authentication:
37
- * ```typescript
38
- * import api from '@fgrzl/fetch';
39
- * import { useAuthentication } from '@fgrzl/fetch/middleware';
40
- *
41
- * const authClient = useAuthentication(api, {
42
- * tokenProvider: () => localStorage.getItem('auth-token') || ''
43
- * });
44
- * ```
45
- *
46
- * @example For token-only auth (no cookies):
47
- * ```typescript
48
- * import { FetchClient, useAuthentication } from '@fgrzl/fetch';
49
- *
50
- * const tokenClient = useAuthentication(new FetchClient({
51
- * credentials: 'omit' // Don't send cookies
52
- * }), {
53
- * tokenProvider: () => getJWTToken()
54
- * });
55
- * ```
56
- */
57
- const api = useProductionStack(new FetchClient({
1
+ // src/client/fetch-client.ts
2
+ var FetchClient = class {
3
+ constructor(config = {}) {
4
+ this.middlewares = [];
5
+ this.credentials = config.credentials ?? "same-origin";
6
+ this.baseUrl = config.baseUrl;
7
+ }
8
+ use(middleware) {
9
+ this.middlewares.push(middleware);
10
+ return this;
11
+ }
12
+ /**
13
+ * Set or update the base URL for this client instance.
14
+ *
15
+ * When a base URL is set, relative URLs will be resolved against it.
16
+ * Absolute URLs will continue to work unchanged.
17
+ *
18
+ * @param baseUrl - The base URL to set, or undefined to clear it
19
+ * @returns The client instance for method chaining
20
+ *
21
+ * @example Set base URL:
22
+ * ```typescript
23
+ * const client = new FetchClient();
24
+ * client.setBaseUrl('https://api.example.com');
25
+ *
26
+ * // Now relative URLs work
27
+ * await client.get('/users'); // → GET https://api.example.com/users
28
+ * ```
29
+ *
30
+ * @example Chain with middleware:
31
+ * ```typescript
32
+ * const client = useProductionStack(new FetchClient())
33
+ * .setBaseUrl(process.env.API_BASE_URL);
34
+ * ```
35
+ */
36
+ setBaseUrl(baseUrl) {
37
+ this.baseUrl = baseUrl;
38
+ return this;
39
+ }
40
+ async request(url, init = {}) {
41
+ const resolvedUrl = this.resolveUrl(url);
42
+ let index = 0;
43
+ const execute = async (request) => {
44
+ const currentRequest = request || { ...init, url: resolvedUrl };
45
+ const currentUrl = currentRequest.url || resolvedUrl;
46
+ if (index >= this.middlewares.length) {
47
+ const { url: _, ...requestInit } = currentRequest;
48
+ return this.coreFetch(requestInit, currentUrl);
49
+ }
50
+ const middleware = this.middlewares[index++];
51
+ if (!middleware) {
52
+ const { url: _, ...requestInit } = currentRequest;
53
+ return this.coreFetch(requestInit, currentUrl);
54
+ }
55
+ return middleware(currentRequest, execute);
56
+ };
57
+ const result = await execute();
58
+ return result;
59
+ }
60
+ async coreFetch(request, url) {
61
+ try {
62
+ const finalInit = {
63
+ credentials: this.credentials,
64
+ ...request
65
+ };
66
+ if (finalInit.headers instanceof Headers) {
67
+ const headersObj = {};
68
+ finalInit.headers.forEach((value, key) => {
69
+ headersObj[key] = value;
70
+ });
71
+ finalInit.headers = headersObj;
72
+ }
73
+ const response = await fetch(url, finalInit);
74
+ const data = await this.parseResponse(response);
75
+ return {
76
+ data: response.ok ? data : null,
77
+ status: response.status,
78
+ statusText: response.statusText,
79
+ headers: response.headers,
80
+ url: response.url,
81
+ ok: response.ok,
82
+ ...response.ok ? {} : {
83
+ error: {
84
+ message: response.statusText,
85
+ body: data
86
+ }
87
+ }
88
+ };
89
+ } catch (error) {
90
+ if (error instanceof TypeError && error.message.includes("fetch")) {
91
+ return {
92
+ data: null,
93
+ status: 0,
94
+ statusText: "Network Error",
95
+ headers: new Headers(),
96
+ url,
97
+ ok: false,
98
+ error: {
99
+ message: "Failed to fetch",
100
+ body: error
101
+ }
102
+ };
103
+ }
104
+ throw error;
105
+ }
106
+ }
107
+ async parseResponse(res) {
108
+ const contentType = res.headers.get("content-type") || "";
109
+ if (contentType.includes("application/json")) {
110
+ return res.json();
111
+ }
112
+ if (contentType.includes("text/")) {
113
+ return res.text();
114
+ }
115
+ if (contentType.includes("application/octet-stream") || contentType.includes("image/") || contentType.includes("video/") || contentType.includes("audio/")) {
116
+ return res.blob();
117
+ }
118
+ if (res.body) {
119
+ const text = await res.text();
120
+ return text || null;
121
+ }
122
+ return null;
123
+ }
124
+ // Helper method to build URL with query parameters
125
+ buildUrlWithParams(url, params) {
126
+ if (!params) {
127
+ return url;
128
+ }
129
+ const resolvedUrl = this.resolveUrl(url);
130
+ if (!resolvedUrl.startsWith("http://") && !resolvedUrl.startsWith("https://") && !resolvedUrl.startsWith("//")) {
131
+ const searchParams = new URLSearchParams();
132
+ Object.entries(params).forEach(([key, value]) => {
133
+ if (value !== void 0 && value !== null) {
134
+ searchParams.set(key, String(value));
135
+ }
136
+ });
137
+ const queryString = searchParams.toString();
138
+ return queryString ? `${resolvedUrl}?${queryString}` : resolvedUrl;
139
+ }
140
+ const urlObj = new URL(resolvedUrl);
141
+ Object.entries(params).forEach(([key, value]) => {
142
+ if (value !== void 0 && value !== null) {
143
+ urlObj.searchParams.set(key, String(value));
144
+ }
145
+ });
146
+ return urlObj.toString();
147
+ }
148
+ /**
149
+ * Resolves a URL with the base URL if it's relative and base URL is configured
150
+ * @param url - The URL to resolve
151
+ * @returns The resolved URL
152
+ * @private
153
+ */
154
+ resolveUrl(url) {
155
+ if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//")) {
156
+ return url;
157
+ }
158
+ if (!this.baseUrl) {
159
+ return url;
160
+ }
161
+ try {
162
+ const baseUrl = new URL(this.baseUrl);
163
+ const resolvedUrl = new URL(url, baseUrl);
164
+ return resolvedUrl.toString();
165
+ } catch {
166
+ throw new Error(
167
+ `Invalid URL: Unable to resolve "${url}" with baseUrl "${this.baseUrl}"`
168
+ );
169
+ }
170
+ }
171
+ // 🎯 PIT OF SUCCESS: Convenience methods with smart defaults
172
+ /**
173
+ * HEAD request with query parameter support.
174
+ *
175
+ * HEAD requests are used to retrieve metadata about a resource without downloading
176
+ * the response body. Useful for checking if a resource exists, getting content length,
177
+ * last modified date, etc.
178
+ *
179
+ * @template T - Expected response data type (will be null for HEAD requests)
180
+ * @param url - Request URL
181
+ * @param params - Query parameters to append to URL
182
+ * @returns Promise resolving to typed response (data will always be null)
183
+ *
184
+ * @example Check if resource exists:
185
+ * ```typescript
186
+ * const headResponse = await client.head('/api/large-file.zip');
187
+ * if (headResponse.ok) {
188
+ * const contentLength = headResponse.headers.get('content-length');
189
+ * const lastModified = headResponse.headers.get('last-modified');
190
+ * console.log(`File size: ${contentLength} bytes`);
191
+ * }
192
+ * ```
193
+ *
194
+ * @example Check with query parameters:
195
+ * ```typescript
196
+ * const exists = await client.head('/api/users', { id: 123 });
197
+ * if (exists.status === 404) {
198
+ * console.log('User not found');
199
+ * }
200
+ * ```
201
+ */
202
+ head(url, params) {
203
+ const finalUrl = this.buildUrlWithParams(url, params);
204
+ return this.request(finalUrl, { method: "HEAD" });
205
+ }
206
+ /**
207
+ * HEAD request that returns useful metadata about a resource.
208
+ *
209
+ * This is a convenience method that extracts common metadata from HEAD responses
210
+ * for easier consumption.
211
+ *
212
+ * @param url - Request URL
213
+ * @param params - Query parameters to append to URL
214
+ * @returns Promise resolving to response with extracted metadata
215
+ *
216
+ * @example Get resource metadata:
217
+ * ```typescript
218
+ * const metadata = await client.headMetadata('/api/large-file.zip');
219
+ * if (metadata.ok) {
220
+ * console.log('File exists:', metadata.exists);
221
+ * console.log('Content type:', metadata.contentType);
222
+ * console.log('Size:', metadata.contentLength, 'bytes');
223
+ * console.log('Last modified:', metadata.lastModified);
224
+ * }
225
+ * ```
226
+ */
227
+ async headMetadata(url, params) {
228
+ const response = await this.head(url, params);
229
+ const contentLengthHeader = response.headers.get("content-length");
230
+ const lastModifiedHeader = response.headers.get("last-modified");
231
+ return {
232
+ ...response,
233
+ exists: response.ok,
234
+ contentType: response.headers.get("content-type") || void 0,
235
+ contentLength: contentLengthHeader ? parseInt(contentLengthHeader, 10) : void 0,
236
+ lastModified: lastModifiedHeader ? new Date(lastModifiedHeader) : void 0,
237
+ etag: response.headers.get("etag") || void 0,
238
+ cacheControl: response.headers.get("cache-control") || void 0
239
+ };
240
+ }
241
+ /**
242
+ * GET request with query parameter support.
243
+ *
244
+ * @template T - Expected response data type
245
+ * @param url - Request URL
246
+ * @param params - Query parameters to append to URL
247
+ * @returns Promise resolving to typed response
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * const users = await client.get<User[]>('/api/users');
252
+ * const filteredUsers = await client.get<User[]>('/api/users', { status: 'active', limit: 10 });
253
+ * if (users.ok) console.log(users.data);
254
+ * ```
255
+ */
256
+ get(url, params) {
257
+ const finalUrl = this.buildUrlWithParams(url, params);
258
+ return this.request(finalUrl, { method: "GET" });
259
+ }
260
+ /**
261
+ * POST request with automatic JSON serialization.
262
+ *
263
+ * @template T - Expected response data type
264
+ * @param url - Request URL
265
+ * @param body - Request body (auto-serialized to JSON)
266
+ * @param headers - Additional headers (Content-Type: application/json is default)
267
+ * @returns Promise resolving to typed response
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * const result = await client.post<User>('/api/users', { name: 'John' });
272
+ * ```
273
+ */
274
+ post(url, body, headers) {
275
+ const requestHeaders = {
276
+ "Content-Type": "application/json",
277
+ ...headers ?? {}
278
+ };
279
+ return this.request(url, {
280
+ method: "POST",
281
+ headers: requestHeaders,
282
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
283
+ });
284
+ }
285
+ /**
286
+ * PUT request with automatic JSON serialization.
287
+ *
288
+ * @template T - Expected response data type
289
+ * @param url - Request URL
290
+ * @param body - Request body (auto-serialized to JSON)
291
+ * @param headers - Additional headers (Content-Type: application/json is default)
292
+ * @returns Promise resolving to typed response
293
+ */
294
+ put(url, body, headers) {
295
+ const requestHeaders = {
296
+ "Content-Type": "application/json",
297
+ ...headers ?? {}
298
+ };
299
+ return this.request(url, {
300
+ method: "PUT",
301
+ headers: requestHeaders,
302
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
303
+ });
304
+ }
305
+ /**
306
+ * PATCH request with automatic JSON serialization.
307
+ *
308
+ * @template T - Expected response data type
309
+ * @param url - Request URL
310
+ * @param body - Request body (auto-serialized to JSON)
311
+ * @param headers - Additional headers (Content-Type: application/json is default)
312
+ * @returns Promise resolving to typed response
313
+ */
314
+ patch(url, body, headers) {
315
+ const requestHeaders = {
316
+ "Content-Type": "application/json",
317
+ ...headers ?? {}
318
+ };
319
+ return this.request(url, {
320
+ method: "PATCH",
321
+ headers: requestHeaders,
322
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
323
+ });
324
+ }
325
+ /**
326
+ * DELETE request with query parameter support.
327
+ *
328
+ * @template T - Expected response data type
329
+ * @param url - Request URL
330
+ * @param params - Query parameters to append to URL
331
+ * @returns Promise resolving to typed response
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * const result = await client.del('/api/users/123');
336
+ * const bulkResult = await client.del('/api/users', { status: 'inactive', force: true });
337
+ * if (result.ok) console.log('Deleted successfully');
338
+ * ```
339
+ */
340
+ del(url, params) {
341
+ const finalUrl = this.buildUrlWithParams(url, params);
342
+ return this.request(finalUrl, { method: "DELETE" });
343
+ }
344
+ };
345
+
346
+ // src/middleware/authentication/authentication.ts
347
+ function shouldSkipAuth(url, skipPatterns = []) {
348
+ return skipPatterns.some((pattern) => {
349
+ if (typeof pattern === "string") {
350
+ return url.includes(pattern);
351
+ }
352
+ return pattern.test(url);
353
+ });
354
+ }
355
+ function shouldIncludeAuth(url, includePatterns) {
356
+ if (!includePatterns || includePatterns.length === 0) {
357
+ return true;
358
+ }
359
+ return includePatterns.some((pattern) => {
360
+ if (typeof pattern === "string") {
361
+ return url.includes(pattern);
362
+ }
363
+ return pattern.test(url);
364
+ });
365
+ }
366
+ function createAuthenticationMiddleware(options) {
367
+ const {
368
+ tokenProvider,
369
+ headerName = "Authorization",
370
+ tokenType = "Bearer",
371
+ skipPatterns = [],
372
+ includePatterns
373
+ } = options;
374
+ return async (request, next) => {
375
+ const url = request.url || "";
376
+ const parsedUrl = new URL(url);
377
+ const pathname = parsedUrl.pathname;
378
+ if (shouldSkipAuth(pathname, skipPatterns) || !shouldIncludeAuth(pathname, includePatterns)) {
379
+ return next(request);
380
+ }
381
+ try {
382
+ const token = await tokenProvider();
383
+ if (!token) {
384
+ return next(request);
385
+ }
386
+ const headers = new Headers(request.headers);
387
+ headers.set(headerName, `${tokenType} ${token}`);
388
+ const modifiedRequest = {
389
+ ...request,
390
+ headers
391
+ };
392
+ return next(modifiedRequest);
393
+ } catch {
394
+ return next(request);
395
+ }
396
+ };
397
+ }
398
+
399
+ // src/middleware/authentication/index.ts
400
+ function useAuthentication(client, options) {
401
+ return client.use(createAuthenticationMiddleware(options));
402
+ }
403
+
404
+ // src/middleware/authorization/authorization.ts
405
+ function createRedirectHandler(config = {}) {
406
+ const {
407
+ redirectPath = "/login",
408
+ returnUrlParam = "return_url",
409
+ includeReturnUrl = true
410
+ } = config;
411
+ return () => {
412
+ let redirectUrl = redirectPath;
413
+ if (includeReturnUrl && typeof window !== "undefined") {
414
+ const currentUrl = encodeURIComponent(window.location.href);
415
+ const separator = redirectPath.includes("?") ? "&" : "?";
416
+ redirectUrl = `${redirectPath}${separator}${returnUrlParam}=${currentUrl}`;
417
+ }
418
+ if (typeof window !== "undefined") {
419
+ window.location.href = redirectUrl;
420
+ }
421
+ };
422
+ }
423
+ function selectUnauthorizedHandler(providedHandler, redirectConfig) {
424
+ if (providedHandler) {
425
+ return providedHandler;
426
+ }
427
+ return createRedirectHandler(redirectConfig);
428
+ }
429
+ function shouldSkipAuth2(url, skipPatterns = []) {
430
+ let pathname;
431
+ try {
432
+ pathname = new URL(url).pathname;
433
+ } catch {
434
+ pathname = url;
435
+ }
436
+ return skipPatterns.some((pattern) => {
437
+ if (typeof pattern === "string") {
438
+ return pathname.includes(pattern);
439
+ }
440
+ return pattern.test(pathname);
441
+ });
442
+ }
443
+ function createAuthorizationMiddleware(options = {}) {
444
+ const {
445
+ onUnauthorized: providedOnUnauthorized,
446
+ redirectConfig,
447
+ onForbidden,
448
+ skipPatterns = [],
449
+ statusCodes = [401]
450
+ } = options;
451
+ const onUnauthorized = selectUnauthorizedHandler(
452
+ providedOnUnauthorized,
453
+ redirectConfig
454
+ );
455
+ return async (request, next) => {
456
+ const url = request.url || "";
457
+ if (shouldSkipAuth2(url, skipPatterns)) {
458
+ return next(request);
459
+ }
460
+ const response = await next(request);
461
+ if (statusCodes.includes(response.status)) {
462
+ try {
463
+ if (response.status === 401 && onUnauthorized) {
464
+ await onUnauthorized(response, request);
465
+ } else if (response.status === 403 && onForbidden) {
466
+ await onForbidden(response, request);
467
+ } else if (onUnauthorized) {
468
+ await onUnauthorized(response, request);
469
+ }
470
+ } catch (error) {
471
+ console.warn("Authorization handler failed:", error);
472
+ }
473
+ }
474
+ return response;
475
+ };
476
+ }
477
+
478
+ // src/middleware/authorization/index.ts
479
+ function useAuthorization(client, options = {}) {
480
+ return client.use(createAuthorizationMiddleware(options));
481
+ }
482
+
483
+ // src/middleware/cache/cache.ts
484
+ var MemoryStorage = class {
485
+ constructor() {
486
+ this.cache = /* @__PURE__ */ new Map();
487
+ }
488
+ async get(key) {
489
+ const entry = this.cache.get(key);
490
+ if (!entry) {
491
+ return null;
492
+ }
493
+ if (Date.now() > entry.expiresAt) {
494
+ this.cache.delete(key);
495
+ return null;
496
+ }
497
+ return entry;
498
+ }
499
+ async getWithExpiry(key) {
500
+ const entry = this.cache.get(key);
501
+ if (!entry) {
502
+ return { entry: null, isExpired: false };
503
+ }
504
+ const isExpired = Date.now() > entry.expiresAt;
505
+ return { entry, isExpired };
506
+ }
507
+ async set(key, entry) {
508
+ this.cache.set(key, entry);
509
+ }
510
+ async delete(key) {
511
+ this.cache.delete(key);
512
+ }
513
+ async clear() {
514
+ this.cache.clear();
515
+ }
516
+ };
517
+ var defaultKeyGenerator = (request) => {
518
+ const url = request.url || "";
519
+ const method = request.method || "GET";
520
+ const headers = request.headers ? JSON.stringify(request.headers) : "";
521
+ return `${method}:${url}:${headers}`;
522
+ };
523
+ function shouldSkipCache(url, skipPatterns = []) {
524
+ return skipPatterns.some((pattern) => {
525
+ if (typeof pattern === "string") {
526
+ return url.includes(pattern);
527
+ }
528
+ return pattern.test(url);
529
+ });
530
+ }
531
+ function createCacheMiddleware(options = {}) {
532
+ const {
533
+ ttl = 5 * 60 * 1e3,
534
+ // 5 minutes
535
+ methods = ["GET"],
536
+ storage = new MemoryStorage(),
537
+ keyGenerator = defaultKeyGenerator,
538
+ skipPatterns = [],
539
+ staleWhileRevalidate = false
540
+ } = options;
541
+ return async (request, next) => {
542
+ const method = (request.method || "GET").toUpperCase();
543
+ const url = request.url || "";
544
+ if (!methods.includes(method) || shouldSkipCache(url, skipPatterns)) {
545
+ return next(request);
546
+ }
547
+ const cacheKey = keyGenerator(request);
548
+ try {
549
+ const { entry: cached, isExpired } = storage.getWithExpiry ? await storage.getWithExpiry(cacheKey) : await (async () => {
550
+ const entry = await storage.get(cacheKey);
551
+ return { entry, isExpired: false };
552
+ })();
553
+ if (cached && !isExpired) {
554
+ return {
555
+ ...cached.response,
556
+ headers: new Headers(cached.response.headers),
557
+ data: cached.response.data
558
+ };
559
+ }
560
+ if (cached && staleWhileRevalidate) {
561
+ const cachedResponse = {
562
+ ...cached.response,
563
+ headers: new Headers(cached.response.headers),
564
+ data: cached.response.data
565
+ };
566
+ if (isExpired) {
567
+ next(request).then(async (freshResponse) => {
568
+ const headersObj = {};
569
+ freshResponse.headers.forEach((value, key) => {
570
+ headersObj[key] = value;
571
+ });
572
+ const cacheEntry = {
573
+ response: {
574
+ status: freshResponse.status,
575
+ statusText: freshResponse.statusText,
576
+ headers: headersObj,
577
+ data: freshResponse.data
578
+ },
579
+ timestamp: Date.now(),
580
+ expiresAt: Date.now() + ttl
581
+ };
582
+ await storage.set(cacheKey, cacheEntry);
583
+ }).catch(() => {
584
+ });
585
+ }
586
+ return cachedResponse;
587
+ }
588
+ const response = await next(request);
589
+ if (response.ok) {
590
+ try {
591
+ const headersObj = {};
592
+ response.headers.forEach((value, key) => {
593
+ headersObj[key] = value;
594
+ });
595
+ const cacheEntry = {
596
+ response: {
597
+ status: response.status,
598
+ statusText: response.statusText,
599
+ headers: headersObj,
600
+ data: response.data
601
+ },
602
+ timestamp: Date.now(),
603
+ expiresAt: Date.now() + ttl
604
+ };
605
+ await storage.set(cacheKey, cacheEntry);
606
+ } catch {
607
+ }
608
+ }
609
+ return response;
610
+ } catch (error) {
611
+ if (error && typeof error === "object" && "message" in error) {
612
+ const errorMessage = error.message;
613
+ if (errorMessage.includes("Network") || errorMessage.includes("fetch")) {
614
+ throw error;
615
+ }
616
+ }
617
+ return next(request);
618
+ }
619
+ };
620
+ }
621
+
622
+ // src/middleware/cache/index.ts
623
+ function useCache(client, options = {}) {
624
+ return client.use(createCacheMiddleware(options));
625
+ }
626
+
627
+ // src/middleware/csrf/csrf.ts
628
+ function getTokenFromCookie(cookieName = "XSRF-TOKEN") {
629
+ if (typeof document === "undefined") {
630
+ return "";
631
+ }
632
+ const name = `${cookieName}=`;
633
+ const decodedCookie = decodeURIComponent(document.cookie);
634
+ const cookies = decodedCookie.split(";");
635
+ for (const cookie of cookies) {
636
+ const c = cookie.trim();
637
+ if (c.indexOf(name) === 0) {
638
+ return c.substring(name.length);
639
+ }
640
+ }
641
+ return "";
642
+ }
643
+ function shouldSkipCSRF(url, skipPatterns = []) {
644
+ return skipPatterns.some((pattern) => {
645
+ if (typeof pattern === "string") {
646
+ return url.includes(pattern);
647
+ }
648
+ return pattern.test(url);
649
+ });
650
+ }
651
+ function createCSRFMiddleware(options = {}) {
652
+ const {
653
+ headerName = "X-XSRF-TOKEN",
654
+ cookieName = "XSRF-TOKEN",
655
+ protectedMethods = ["POST", "PUT", "PATCH", "DELETE"],
656
+ skipPatterns = [],
657
+ tokenProvider = () => getTokenFromCookie(cookieName)
658
+ } = options;
659
+ return async (request, next) => {
660
+ const method = (request.method || "GET").toUpperCase();
661
+ const url = request.url || "";
662
+ if (!protectedMethods.includes(method) || shouldSkipCSRF(url, skipPatterns)) {
663
+ return next(request);
664
+ }
665
+ const token = tokenProvider();
666
+ if (!token) {
667
+ return next(request);
668
+ }
669
+ const headers = new Headers(request.headers);
670
+ headers.set(headerName, token);
671
+ const modifiedRequest = {
672
+ ...request,
673
+ headers
674
+ };
675
+ return next(modifiedRequest);
676
+ };
677
+ }
678
+
679
+ // src/middleware/csrf/index.ts
680
+ function useCSRF(client, options = {}) {
681
+ return client.use(createCSRFMiddleware(options));
682
+ }
683
+
684
+ // src/middleware/logging/logging.ts
685
+ var defaultLogger = {
686
+ // eslint-disable-next-line no-console -- allow console.debug in logger implementation
687
+ debug: (message, data) => console.debug(message, data),
688
+ // eslint-disable-next-line no-console -- allow console.info in logger implementation
689
+ info: (message, data) => console.info(message, data),
690
+ // eslint-disable-next-line no-console -- allow console.warn in logger implementation
691
+ warn: (message, data) => console.warn(message, data),
692
+ // eslint-disable-next-line no-console -- allow console.error in logger implementation
693
+ error: (message, data) => console.error(message, data)
694
+ };
695
+ var LOG_LEVELS = {
696
+ debug: 0,
697
+ info: 1,
698
+ warn: 2,
699
+ error: 3
700
+ };
701
+ var defaultFormatter = (entry) => {
702
+ const { method, url, status, duration } = entry;
703
+ let message = `${method} ${url}`;
704
+ if (status) {
705
+ message += ` \u2192 ${status}`;
706
+ }
707
+ if (duration) {
708
+ message += ` (${duration}ms)`;
709
+ }
710
+ return message;
711
+ };
712
+ function shouldSkipLogging(url, skipPatterns = []) {
713
+ return skipPatterns.some((pattern) => {
714
+ if (typeof pattern === "string") {
715
+ return url.includes(pattern);
716
+ }
717
+ return pattern.test(url);
718
+ });
719
+ }
720
+ function createLoggingMiddleware(options = {}) {
721
+ const {
722
+ level = "info",
723
+ logger = defaultLogger,
724
+ includeRequestHeaders = false,
725
+ includeResponseHeaders = false,
726
+ includeRequestBody = false,
727
+ includeResponseBody = false,
728
+ skipPatterns = [],
729
+ formatter = defaultFormatter
730
+ } = options;
731
+ const minLevel = LOG_LEVELS[level];
732
+ return async (request, next) => {
733
+ const url = request.url || "";
734
+ const method = (request.method || "GET").toUpperCase();
735
+ if (shouldSkipLogging(url, skipPatterns)) {
736
+ return next(request);
737
+ }
738
+ const startTime = Date.now();
739
+ if (LOG_LEVELS.debug >= minLevel) {
740
+ const requestHeaders = includeRequestHeaders ? getHeadersObject(
741
+ request.headers
742
+ ) : void 0;
743
+ const requestBody = includeRequestBody ? request.body : void 0;
744
+ const requestEntry = {
745
+ level: "debug",
746
+ timestamp: startTime,
747
+ method,
748
+ url,
749
+ ...requestHeaders && { requestHeaders },
750
+ ...requestBody && { requestBody }
751
+ };
752
+ logger.debug(`\u2192 ${formatter(requestEntry)}`, requestEntry);
753
+ }
754
+ try {
755
+ const response = await next(request);
756
+ const duration = Date.now() - startTime;
757
+ const logLevel = response.status >= 400 ? "error" : "info";
758
+ if (LOG_LEVELS[logLevel] >= minLevel) {
759
+ const responseHeaders = includeResponseHeaders ? getHeadersObject(response.headers) : void 0;
760
+ const responseBody = includeResponseBody ? response.data : void 0;
761
+ const responseEntry = {
762
+ level: logLevel,
763
+ timestamp: Date.now(),
764
+ method,
765
+ url,
766
+ status: response.status,
767
+ duration,
768
+ ...responseHeaders ? { responseHeaders } : {},
769
+ ...responseBody !== void 0 ? { responseBody } : {}
770
+ };
771
+ const logMessage = `\u2190 ${formatter(responseEntry)}`;
772
+ if (logLevel === "error") {
773
+ logger.error(logMessage, responseEntry);
774
+ } else {
775
+ logger.info(logMessage, responseEntry);
776
+ }
777
+ }
778
+ return response;
779
+ } catch (error) {
780
+ const duration = Date.now() - startTime;
781
+ if (LOG_LEVELS.error >= minLevel) {
782
+ const errorEntry = {
783
+ level: "error",
784
+ timestamp: Date.now(),
785
+ method,
786
+ url,
787
+ duration,
788
+ error: error instanceof Error ? error : new Error(String(error))
789
+ };
790
+ logger.error(`\u2717 ${formatter(errorEntry)}`, errorEntry);
791
+ }
792
+ throw error;
793
+ }
794
+ };
795
+ }
796
+ function getHeadersObject(headers) {
797
+ if (!headers) {
798
+ return void 0;
799
+ }
800
+ const obj = {};
801
+ if (headers instanceof Headers) {
802
+ headers.forEach((value, key) => {
803
+ obj[key] = value;
804
+ });
805
+ return obj;
806
+ } else {
807
+ return headers;
808
+ }
809
+ }
810
+
811
+ // src/middleware/logging/index.ts
812
+ function useLogging(client, options = {}) {
813
+ return client.use(createLoggingMiddleware(options));
814
+ }
815
+
816
+ // src/middleware/rate-limit/rate-limit.ts
817
+ var TokenBucket = class {
818
+ constructor(maxTokens, refillRate, timeProvider = () => Date.now()) {
819
+ this.maxTokens = maxTokens;
820
+ this.refillRate = refillRate;
821
+ this.timeProvider = timeProvider;
822
+ this.tokens = maxTokens;
823
+ this.lastRefill = this.timeProvider();
824
+ }
825
+ tryConsume() {
826
+ this.refill();
827
+ if (this.tokens >= 1) {
828
+ this.tokens--;
829
+ return { allowed: true };
830
+ }
831
+ const retryAfter = (1 - this.tokens) / this.refillRate;
832
+ return { allowed: false, retryAfter: Math.ceil(retryAfter) };
833
+ }
834
+ refill() {
835
+ const now = this.timeProvider();
836
+ const timePassed = now - this.lastRefill;
837
+ const tokensToAdd = timePassed * this.refillRate;
838
+ this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
839
+ this.lastRefill = now;
840
+ }
841
+ };
842
+ function createRateLimitMiddleware(options = {}) {
843
+ const {
844
+ maxRequests = 60,
845
+ windowMs = 6e4,
846
+ keyGenerator = () => "default",
847
+ skipPatterns = [],
848
+ onRateLimitExceeded
849
+ } = options;
850
+ const buckets = /* @__PURE__ */ new Map();
851
+ const refillRate = maxRequests / windowMs;
852
+ return async (request, next) => {
853
+ const url = request.url || "";
854
+ if (skipPatterns.some(
855
+ (pattern) => typeof pattern === "string" ? url.includes(pattern) : pattern.test(url)
856
+ )) {
857
+ return next(request);
858
+ }
859
+ const key = keyGenerator(request);
860
+ if (!buckets.has(key)) {
861
+ buckets.set(key, new TokenBucket(maxRequests, refillRate));
862
+ }
863
+ const bucket = buckets.get(key);
864
+ const result = bucket.tryConsume();
865
+ if (!result.allowed) {
866
+ if (onRateLimitExceeded) {
867
+ const customResponse = await onRateLimitExceeded(
868
+ result.retryAfter || 0,
869
+ request
870
+ );
871
+ if (customResponse) {
872
+ return customResponse;
873
+ }
874
+ }
875
+ return {
876
+ data: null,
877
+ status: 429,
878
+ statusText: "Too Many Requests",
879
+ headers: new Headers({
880
+ "Retry-After": Math.ceil((result.retryAfter || 0) / 1e3).toString()
881
+ }),
882
+ url: request.url || "",
883
+ ok: false,
884
+ error: {
885
+ message: `Rate limit exceeded. Retry after ${result.retryAfter}ms`,
886
+ body: { retryAfter: result.retryAfter }
887
+ }
888
+ };
889
+ }
890
+ return next(request);
891
+ };
892
+ }
893
+
894
+ // src/middleware/rate-limit/index.ts
895
+ function useRateLimit(client, options = {}) {
896
+ return client.use(createRateLimitMiddleware(options));
897
+ }
898
+
899
+ // src/middleware/retry/retry.ts
900
+ var defaultShouldRetry = (response) => {
901
+ return response.status === 0 || response.status >= 500 && response.status < 600;
902
+ };
903
+ var calculateDelay = (attempt, baseDelay, backoff, maxDelay) => {
904
+ let delay;
905
+ switch (backoff) {
906
+ case "exponential":
907
+ delay = baseDelay * Math.pow(2, attempt - 1);
908
+ break;
909
+ case "linear":
910
+ delay = baseDelay * attempt;
911
+ break;
912
+ case "fixed":
913
+ default:
914
+ delay = baseDelay;
915
+ break;
916
+ }
917
+ return Math.min(delay, maxDelay);
918
+ };
919
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
920
+ function createRetryMiddleware(options = {}) {
921
+ const {
922
+ maxRetries = 3,
923
+ delay = 1e3,
924
+ backoff = "exponential",
925
+ maxDelay = 3e4,
926
+ shouldRetry = defaultShouldRetry,
927
+ onRetry
928
+ } = options;
929
+ return async (request, next) => {
930
+ let lastResponse;
931
+ let attempt = 0;
932
+ while (attempt <= maxRetries) {
933
+ try {
934
+ const response = await next(request);
935
+ if (response.ok) {
936
+ return response;
937
+ }
938
+ if (!shouldRetry(
939
+ { status: response.status, ok: response.ok },
940
+ attempt + 1
941
+ )) {
942
+ return response;
943
+ }
944
+ if (attempt >= maxRetries) {
945
+ return response;
946
+ }
947
+ lastResponse = response;
948
+ attempt++;
949
+ const retryDelay = calculateDelay(attempt, delay, backoff, maxDelay);
950
+ if (onRetry) {
951
+ onRetry(attempt, retryDelay, {
952
+ status: response.status,
953
+ statusText: response.statusText
954
+ });
955
+ }
956
+ await sleep(retryDelay);
957
+ } catch (error) {
958
+ const errorResponse = {
959
+ data: null,
960
+ status: 0,
961
+ statusText: "Network Error",
962
+ headers: new Headers(),
963
+ url: request.url || "",
964
+ ok: false,
965
+ error: {
966
+ message: error instanceof Error ? error.message : "Unknown error",
967
+ body: error
968
+ }
969
+ };
970
+ if (!shouldRetry(errorResponse, attempt + 1)) {
971
+ return errorResponse;
972
+ }
973
+ if (attempt >= maxRetries) {
974
+ return errorResponse;
975
+ }
976
+ lastResponse = errorResponse;
977
+ attempt++;
978
+ const retryDelay = calculateDelay(attempt, delay, backoff, maxDelay);
979
+ if (onRetry) {
980
+ onRetry(attempt, retryDelay, {
981
+ status: errorResponse.status,
982
+ statusText: errorResponse.statusText
983
+ });
984
+ }
985
+ await sleep(retryDelay);
986
+ }
987
+ }
988
+ return lastResponse;
989
+ };
990
+ }
991
+
992
+ // src/middleware/retry/index.ts
993
+ function useRetry(client, options = {}) {
994
+ return client.use(createRetryMiddleware(options));
995
+ }
996
+
997
+ // src/middleware/index.ts
998
+ function useProductionStack(client, config = {}) {
999
+ let enhanced = client;
1000
+ if (config.auth) {
1001
+ enhanced = useAuthentication(enhanced, config.auth);
1002
+ }
1003
+ if (config.cache !== void 0) {
1004
+ enhanced = useCache(enhanced, config.cache);
1005
+ }
1006
+ if (config.retry !== void 0) {
1007
+ enhanced = useRetry(enhanced, config.retry);
1008
+ }
1009
+ if (config.rateLimit !== void 0) {
1010
+ enhanced = useRateLimit(enhanced, config.rateLimit);
1011
+ }
1012
+ if (config.logging !== void 0) {
1013
+ enhanced = useLogging(enhanced, config.logging);
1014
+ }
1015
+ return enhanced;
1016
+ }
1017
+ function useDevelopmentStack(client, config = {}) {
1018
+ let enhanced = client;
1019
+ if (config.auth) {
1020
+ enhanced = useAuthentication(enhanced, config.auth);
1021
+ }
1022
+ enhanced = useRetry(enhanced, {
1023
+ maxRetries: 1,
1024
+ delay: 100
1025
+ });
1026
+ enhanced = useLogging(enhanced, {
1027
+ level: "debug",
1028
+ includeRequestHeaders: true,
1029
+ includeResponseHeaders: true,
1030
+ includeRequestBody: true,
1031
+ includeResponseBody: true
1032
+ });
1033
+ return enhanced;
1034
+ }
1035
+ function useBasicStack(client, config) {
1036
+ return useRetry(useAuthentication(client, config.auth), { maxRetries: 2 });
1037
+ }
1038
+
1039
+ // src/errors/index.ts
1040
+ var FetchError = class extends Error {
1041
+ /**
1042
+ * Creates a new FetchError.
1043
+ * @param message - Error message
1044
+ * @param cause - Optional underlying cause
1045
+ */
1046
+ constructor(message, cause) {
1047
+ super(message);
1048
+ this.name = "FetchError";
1049
+ if (cause !== void 0) {
1050
+ this.cause = cause;
1051
+ }
1052
+ }
1053
+ };
1054
+ var HttpError = class extends FetchError {
1055
+ /**
1056
+ * Creates a new HttpError.
1057
+ * @param status - HTTP status code
1058
+ * @param statusText - HTTP status text
1059
+ * @param body - Response body
1060
+ * @param url - The request URL
1061
+ */
1062
+ constructor(status, statusText, body, url) {
1063
+ super(`HTTP ${status} ${statusText} at ${url}`);
1064
+ this.name = "HttpError";
1065
+ this.status = status;
1066
+ this.statusText = statusText;
1067
+ this.body = body;
1068
+ }
1069
+ };
1070
+ var NetworkError = class extends FetchError {
1071
+ /**
1072
+ * Creates a new NetworkError.
1073
+ * @param message - Error message
1074
+ * @param url - The request URL
1075
+ * @param cause - The underlying network error
1076
+ */
1077
+ constructor(message, url, cause) {
1078
+ super(`Network error for ${url}: ${message}`, cause);
1079
+ this.name = "NetworkError";
1080
+ }
1081
+ };
1082
+
1083
+ // src/index.ts
1084
+ var api = useProductionStack(
1085
+ new FetchClient({
58
1086
  // Smart default: include cookies for session-based auth
59
1087
  // Can be overridden by creating a custom FetchClient
60
- credentials: 'same-origin',
61
- }), {
1088
+ credentials: "same-origin"
1089
+ }),
1090
+ {
62
1091
  // Smart defaults - users can override as needed
63
1092
  retry: {
64
- maxRetries: 2,
65
- delay: 1000,
1093
+ maxRetries: 2,
1094
+ delay: 1e3
66
1095
  },
67
1096
  cache: {
68
- ttl: 5 * 60 * 1000, // 5 minutes
69
- methods: ['GET'],
1097
+ ttl: 5 * 60 * 1e3,
1098
+ // 5 minutes
1099
+ methods: ["GET"]
70
1100
  },
71
1101
  logging: {
72
- level: 'info',
1102
+ level: "info"
73
1103
  },
74
1104
  rateLimit: {
75
- maxRequests: 100,
76
- windowMs: 60 * 1000, // 100 requests per minute
77
- },
78
- });
79
- // 🎯 LEVEL 1: Export the production-ready client as default
80
- export default api;
81
- // 🎯 LEVEL 2: FetchClient for custom configurations
82
- export { FetchClient } from './client/fetch-client';
83
- export { FetchError, HttpError, NetworkError } from './errors';
84
- // 🎯 LEVEL 3: Individual middleware functions (import from our comprehensive middleware index)
85
- export {
86
- // Authentication
87
- useAuthentication, createAuthenticationMiddleware,
88
- // Authorization
89
- useAuthorization, createAuthorizationMiddleware,
90
- // Cache
91
- useCache, createCacheMiddleware,
92
- // CSRF
93
- useCSRF,
94
- // Logging
95
- useLogging, createLoggingMiddleware,
96
- // Rate Limiting
97
- useRateLimit, createRateLimitMiddleware,
98
- // Retry
99
- useRetry, createRetryMiddleware, } from './middleware';
100
- // 🎯 LEVEL 4: Pre-built middleware stacks for common scenarios
101
- export { useProductionStack, useDevelopmentStack, useBasicStack, } from './middleware';
1105
+ maxRequests: 100,
1106
+ windowMs: 60 * 1e3
1107
+ // 100 requests per minute
1108
+ }
1109
+ }
1110
+ );
1111
+ var index_default = api;
1112
+ export {
1113
+ FetchClient,
1114
+ FetchError,
1115
+ HttpError,
1116
+ NetworkError,
1117
+ createAuthenticationMiddleware,
1118
+ createAuthorizationMiddleware,
1119
+ createCacheMiddleware,
1120
+ createLoggingMiddleware,
1121
+ createRateLimitMiddleware,
1122
+ createRetryMiddleware,
1123
+ index_default as default,
1124
+ useAuthentication,
1125
+ useAuthorization,
1126
+ useBasicStack,
1127
+ useCSRF,
1128
+ useCache,
1129
+ useDevelopmentStack,
1130
+ useLogging,
1131
+ useProductionStack,
1132
+ useRateLimit,
1133
+ useRetry
1134
+ };
102
1135
  //# sourceMappingURL=index.js.map