@geekmidas/client 0.3.0 → 0.5.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 (80) hide show
  1. package/dist/auth-fetcher.cjs +2 -3
  2. package/dist/auth-fetcher.cjs.map +1 -1
  3. package/dist/auth-fetcher.d.cts +2 -1
  4. package/dist/auth-fetcher.d.cts.map +1 -0
  5. package/dist/auth-fetcher.d.mts +2 -1
  6. package/dist/auth-fetcher.d.mts.map +1 -0
  7. package/dist/auth-fetcher.mjs +2 -3
  8. package/dist/auth-fetcher.mjs.map +1 -1
  9. package/dist/endpoint-hooks.cjs +10 -5
  10. package/dist/endpoint-hooks.cjs.map +1 -1
  11. package/dist/endpoint-hooks.d.cts +2 -2
  12. package/dist/endpoint-hooks.d.cts.map +1 -0
  13. package/dist/endpoint-hooks.d.mts +2 -2
  14. package/dist/endpoint-hooks.d.mts.map +1 -0
  15. package/dist/endpoint-hooks.mjs +10 -5
  16. package/dist/endpoint-hooks.mjs.map +1 -1
  17. package/dist/{fetcher-DLDD_7Sa.mjs → fetcher-5fnBE7Dk.mjs} +2 -2
  18. package/dist/fetcher-5fnBE7Dk.mjs.map +1 -0
  19. package/dist/{fetcher-KdwHgdAl.cjs → fetcher-CgLEaIAC.cjs} +2 -2
  20. package/dist/fetcher-CgLEaIAC.cjs.map +1 -0
  21. package/dist/fetcher.cjs +1 -1
  22. package/dist/fetcher.d.cts +3 -1
  23. package/dist/fetcher.d.cts.map +1 -0
  24. package/dist/fetcher.d.mts +3 -1
  25. package/dist/fetcher.d.mts.map +1 -0
  26. package/dist/fetcher.mjs +1 -1
  27. package/dist/infer.d.cts.map +1 -0
  28. package/dist/infer.d.mts.map +1 -0
  29. package/dist/openapi-hooks.cjs +3 -5
  30. package/dist/openapi-hooks.cjs.map +1 -1
  31. package/dist/openapi-hooks.d.cts +3 -3
  32. package/dist/openapi-hooks.d.cts.map +1 -0
  33. package/dist/openapi-hooks.d.mts +6 -6
  34. package/dist/openapi-hooks.d.mts.map +1 -0
  35. package/dist/openapi-hooks.mjs +3 -5
  36. package/dist/openapi-hooks.mjs.map +1 -1
  37. package/dist/openapi-types.d.cts.map +1 -0
  38. package/dist/openapi-types.d.mts.map +1 -0
  39. package/dist/openapi.cjs.map +1 -1
  40. package/dist/openapi.mjs.map +1 -1
  41. package/dist/react-query.cjs +1 -1
  42. package/dist/react-query.cjs.map +1 -1
  43. package/dist/react-query.d.cts +3 -1
  44. package/dist/react-query.d.cts.map +1 -0
  45. package/dist/react-query.d.mts +10 -8
  46. package/dist/react-query.d.mts.map +1 -0
  47. package/dist/react-query.mjs +1 -1
  48. package/dist/react-query.mjs.map +1 -1
  49. package/dist/{types-D4OSWveN.d.cts → types-4K4N-Fl1.d.cts} +8 -6
  50. package/dist/types-4K4N-Fl1.d.cts.map +1 -0
  51. package/dist/{types-Cdv1XAWr.d.mts → types-hhkZWjQn.d.mts} +8 -6
  52. package/dist/types-hhkZWjQn.d.mts.map +1 -0
  53. package/dist/types.d.cts +1 -1
  54. package/dist/types.d.mts +1 -1
  55. package/package.json +6 -5
  56. package/src/__tests__/auth-fetcher.spec.ts +505 -0
  57. package/src/__tests__/endpoint-hooks.spec.tsx +348 -348
  58. package/src/__tests__/fetcher.spec.ts +403 -403
  59. package/src/__tests__/infer.integration.spec.ts +171 -171
  60. package/src/__tests__/infer.spec.ts +182 -182
  61. package/src/__tests__/method-restrictions.spec.tsx +165 -165
  62. package/src/__tests__/openapi-hooks.spec.tsx +536 -537
  63. package/src/__tests__/react-query-infinite.spec.tsx +989 -989
  64. package/src/__tests__/react-query-invalidation.spec.tsx +223 -222
  65. package/src/__tests__/react-query.spec.tsx +567 -567
  66. package/src/__tests__/setup.ts +260 -260
  67. package/src/__tests__/types.spec.ts +127 -127
  68. package/src/__tests__/url-parsing.spec.ts +191 -191
  69. package/src/auth-fetcher.ts +160 -162
  70. package/src/endpoint-hooks.ts +151 -150
  71. package/src/fetcher.ts +163 -163
  72. package/src/infer.ts +64 -63
  73. package/src/openapi-hooks.ts +146 -146
  74. package/src/openapi-types.d.ts +428 -428
  75. package/src/openapi.json +593 -593
  76. package/src/react-query.ts +281 -281
  77. package/src/types.ts +124 -125
  78. package/tsconfig.json +12 -0
  79. package/dist/fetcher-DLDD_7Sa.mjs.map +0 -1
  80. package/dist/fetcher-KdwHgdAl.cjs.map +0 -1
package/src/types.ts CHANGED
@@ -1,69 +1,69 @@
1
1
  export type OpenAPIRoutes<Paths> = keyof Paths;
2
2
 
3
3
  type HttpMethod =
4
- | 'get'
5
- | 'post'
6
- | 'put'
7
- | 'patch'
8
- | 'delete'
9
- | 'head'
10
- | 'options';
4
+ | 'get'
5
+ | 'post'
6
+ | 'put'
7
+ | 'patch'
8
+ | 'delete'
9
+ | 'head'
10
+ | 'options';
11
11
 
12
12
  export type ExtractMethod<Paths, Route extends OpenAPIRoutes<Paths>> = Extract<
13
- keyof Paths[Route],
14
- HttpMethod
13
+ keyof Paths[Route],
14
+ HttpMethod
15
15
  >;
16
16
 
17
17
  export type ExtractPathParams<
18
- Paths,
19
- Route extends OpenAPIRoutes<Paths>,
18
+ Paths,
19
+ Route extends OpenAPIRoutes<Paths>,
20
20
  > = Paths[Route] extends {
21
- parameters?: { path?: infer P };
21
+ parameters?: { path?: infer P };
22
22
  }
23
- ? P
24
- : never;
23
+ ? P
24
+ : never;
25
25
 
26
26
  export type ExtractQueryParams<
27
- Paths,
28
- Route extends OpenAPIRoutes<Paths>,
29
- Method extends ExtractMethod<Paths, Route>,
27
+ Paths,
28
+ Route extends OpenAPIRoutes<Paths>,
29
+ Method extends ExtractMethod<Paths, Route>,
30
30
  > = Paths[Route][Method] extends { parameters?: { query?: infer Q } }
31
- ? Q
32
- : never;
31
+ ? Q
32
+ : never;
33
33
 
34
34
  export type ExtractRequestBody<
35
- Paths,
36
- Route extends OpenAPIRoutes<Paths>,
37
- Method extends ExtractMethod<Paths, Route>,
35
+ Paths,
36
+ Route extends OpenAPIRoutes<Paths>,
37
+ Method extends ExtractMethod<Paths, Route>,
38
38
  > = Paths[Route][Method] extends {
39
- requestBody?: { content?: { 'application/json'?: infer B } };
39
+ requestBody?: { content?: { 'application/json'?: infer B } };
40
40
  }
41
- ? B
42
- : never;
41
+ ? B
42
+ : never;
43
43
 
44
44
  export type ExtractResponse<
45
- Paths,
46
- Route extends OpenAPIRoutes<Paths>,
47
- Method extends ExtractMethod<Paths, Route>,
45
+ Paths,
46
+ Route extends OpenAPIRoutes<Paths>,
47
+ Method extends ExtractMethod<Paths, Route>,
48
48
  > = Paths[Route][Method] extends {
49
- responses?: { 200?: { content?: { 'application/json'?: infer R } } };
49
+ responses?: { 200?: { content?: { 'application/json'?: infer R } } };
50
50
  }
51
- ? R
52
- : Paths[Route][Method] extends {
53
- responses?: { 201?: { content?: { 'application/json'?: infer R } } };
54
- }
55
- ? R
56
- : never;
51
+ ? R
52
+ : Paths[Route][Method] extends {
53
+ responses?: { 201?: { content?: { 'application/json'?: infer R } } };
54
+ }
55
+ ? R
56
+ : never;
57
57
 
58
58
  export type RequestConfig<
59
- Paths,
60
- Route extends OpenAPIRoutes<Paths>,
61
- Method extends ExtractMethod<Paths, Route>,
59
+ Paths,
60
+ Route extends OpenAPIRoutes<Paths>,
61
+ Method extends ExtractMethod<Paths, Route>,
62
62
  > = {
63
- params?: ExtractPathParams<Paths, Route>;
64
- query?: ExtractQueryParams<Paths, Route, Method>;
65
- body?: ExtractRequestBody<Paths, Route, Method>;
66
- headers?: Record<string, string>;
63
+ params?: ExtractPathParams<Paths, Route>;
64
+ query?: ExtractQueryParams<Paths, Route, Method>;
65
+ body?: ExtractRequestBody<Paths, Route, Method>;
66
+ headers?: Record<string, string>;
67
67
  };
68
68
 
69
69
  export type EndpointString = `${Uppercase<string>} ${string}`;
@@ -71,66 +71,63 @@ export type EndpointString = `${Uppercase<string>} ${string}`;
71
71
  // Generate all valid endpoint strings from OpenAPI paths
72
72
  // Uses Extract with HttpMethod to filter out non-HTTP keys like 'parameters'
73
73
  export type ValidEndpoint<Paths> = {
74
- [Route in keyof Paths]: {
75
- [Method in Extract<keyof Paths[Route], HttpMethod>]: `${Uppercase<
76
- string & Method
77
- >} ${string & Route}`;
78
- }[Extract<keyof Paths[Route], HttpMethod>];
74
+ [Route in keyof Paths]: {
75
+ [Method in Extract<keyof Paths[Route], HttpMethod>]: `${Uppercase<
76
+ string & Method
77
+ >} ${string & Route}`;
78
+ }[Extract<keyof Paths[Route], HttpMethod>];
79
79
  }[keyof Paths];
80
80
 
81
81
  // Helper type to get autocomplete for endpoint strings
82
- export type TypedEndpoint<Paths> = ValidEndpoint<Paths> extends infer E
83
- ? E extends string
84
- ? E
85
- : never
86
- : never;
82
+ export type TypedEndpoint<Paths> =
83
+ ValidEndpoint<Paths> extends infer E ? (E extends string ? E : never) : never;
87
84
 
88
85
  // Filter endpoints by HTTP method
89
86
  export type FilterEndpointByMethod<
90
- Paths,
91
- Method extends string,
87
+ Paths,
88
+ Method extends string,
92
89
  > = ValidEndpoint<Paths> extends infer E
93
- ? E extends `${Method} ${string}`
94
- ? E
95
- : never
96
- : never;
90
+ ? E extends `${Method} ${string}`
91
+ ? E
92
+ : never
93
+ : never;
97
94
 
98
95
  // Query endpoints (GET only)
99
96
  export type QueryEndpoint<Paths> = FilterEndpointByMethod<Paths, 'GET'>;
100
97
 
101
98
  // Mutation endpoints (POST, PATCH, PUT, DELETE)
102
99
  export type MutationEndpoint<Paths> =
103
- | FilterEndpointByMethod<Paths, 'POST'>
104
- | FilterEndpointByMethod<Paths, 'PATCH'>
105
- | FilterEndpointByMethod<Paths, 'PUT'>
106
- | FilterEndpointByMethod<Paths, 'DELETE'>;
100
+ | FilterEndpointByMethod<Paths, 'POST'>
101
+ | FilterEndpointByMethod<Paths, 'PATCH'>
102
+ | FilterEndpointByMethod<Paths, 'PUT'>
103
+ | FilterEndpointByMethod<Paths, 'DELETE'>;
107
104
 
108
105
  export type ParseEndpoint<T extends EndpointString> =
109
- T extends `${infer Method} ${infer Route}`
110
- ? { method: Lowercase<Method>; route: Route }
111
- : never;
106
+ T extends `${infer Method} ${infer Route}`
107
+ ? { method: Lowercase<Method>; route: Route }
108
+ : never;
112
109
 
113
110
  export type ExtractEndpointResponse<
114
- Paths,
115
- T extends EndpointString,
111
+ Paths,
112
+ T extends EndpointString,
116
113
  > = T extends `${infer Method} ${infer Route}`
117
- ? Route extends OpenAPIRoutes<Paths>
118
- ? Lowercase<Method> extends ExtractMethod<Paths, Route>
119
- ? ExtractResponse<Paths, Route, Lowercase<Method>>
120
- : never
121
- : never
122
- : never;
114
+ ? Route extends OpenAPIRoutes<Paths>
115
+ ? Lowercase<Method> extends ExtractMethod<Paths, Route>
116
+ ? ExtractResponse<Paths, Route, Lowercase<Method>>
117
+ : never
118
+ : never
119
+ : never;
123
120
 
124
121
  export type ExtractEndpointConfig<
125
- Paths,
126
- T extends EndpointString,
122
+ Paths,
123
+ T extends EndpointString,
127
124
  > = T extends `${infer Method} ${infer Route}`
128
- ? Route extends OpenAPIRoutes<Paths>
129
- ? Lowercase<Method> extends ExtractMethod<Paths, Route>
130
- ? RequestConfig<Paths, Route, Lowercase<Method>>
131
- : never
132
- : never
133
- : never;
125
+ ? Route extends OpenAPIRoutes<Paths>
126
+ ? Lowercase<Method> extends ExtractMethod<Paths, Route>
127
+ ? RequestConfig<Paths, Route, Lowercase<Method>>
128
+ : never
129
+ : never
130
+ : never;
134
131
 
135
132
  /**
136
133
  * Build a request config type where:
@@ -139,33 +136,35 @@ export type ExtractEndpointConfig<
139
136
  * - `query` and `headers` are always optional
140
137
  */
141
138
  export type FilteredRequestConfig<
142
- Paths,
143
- T extends EndpointString,
139
+ Paths,
140
+ T extends EndpointString,
144
141
  > = T extends `${infer Method} ${infer Route}`
145
- ? Route extends OpenAPIRoutes<Paths>
146
- ? Lowercase<Method> extends ExtractMethod<Paths, Route>
147
- ? BuildRequestConfig<
148
- ExtractPathParams<Paths, Route>,
149
- ExtractQueryParams<Paths, Route, Lowercase<Method>>,
150
- ExtractRequestBody<Paths, Route, Lowercase<Method>>
151
- >
152
- : never
153
- : never
154
- : never;
142
+ ? Route extends OpenAPIRoutes<Paths>
143
+ ? Lowercase<Method> extends ExtractMethod<Paths, Route>
144
+ ? BuildRequestConfig<
145
+ ExtractPathParams<Paths, Route>,
146
+ ExtractQueryParams<Paths, Route, Lowercase<Method>>,
147
+ ExtractRequestBody<Paths, Route, Lowercase<Method>>
148
+ >
149
+ : never
150
+ : never
151
+ : never;
155
152
 
156
153
  /**
157
- * Helper to build request config with correct required/optional fields
154
+ * Helper to build request config with correct required/optional fields.
155
+ * Uses [T] extends [never] pattern to prevent distribution over union types,
156
+ * which would cause the entire type to become `never`.
158
157
  */
159
158
  type BuildRequestConfig<TParams, TQuery, TBody> = SimplifyIntersection<
160
- // params: required if not never
161
- (TParams extends never ? {} : { params: TParams }) &
162
- // body: required if not never
163
- (TBody extends never ? {} : { body: TBody }) &
164
- // query: optional if not never
165
- (TQuery extends never ? {} : { query?: TQuery }) & {
166
- // headers: always optional
167
- headers?: Record<string, string>;
168
- }
159
+ // params: required if not never
160
+ ([TParams] extends [never] ? {} : { params: TParams }) &
161
+ // body: required if not never
162
+ ([TBody] extends [never] ? {} : { body: TBody }) &
163
+ // query: optional if not never
164
+ ([TQuery] extends [never] ? {} : { query?: TQuery }) & {
165
+ // headers: always optional
166
+ headers?: Record<string, string>;
167
+ }
169
168
  >;
170
169
 
171
170
  /**
@@ -177,36 +176,36 @@ type SimplifyIntersection<T> = { [K in keyof T]: T[K] };
177
176
  * Check if the config object is required (has any required fields)
178
177
  */
179
178
  export type IsConfigRequired<
180
- Paths,
181
- T extends EndpointString,
179
+ Paths,
180
+ T extends EndpointString,
182
181
  > = T extends `${infer Method} ${infer Route}`
183
- ? Route extends OpenAPIRoutes<Paths>
184
- ? Lowercase<Method> extends ExtractMethod<Paths, Route>
185
- ? ExtractPathParams<Paths, Route> extends never
186
- ? ExtractRequestBody<Paths, Route, Lowercase<Method>> extends never
187
- ? false
188
- : true
189
- : true
190
- : false
191
- : false
192
- : false;
182
+ ? Route extends OpenAPIRoutes<Paths>
183
+ ? Lowercase<Method> extends ExtractMethod<Paths, Route>
184
+ ? ExtractPathParams<Paths, Route> extends never
185
+ ? ExtractRequestBody<Paths, Route, Lowercase<Method>> extends never
186
+ ? false
187
+ : true
188
+ : true
189
+ : false
190
+ : false
191
+ : false;
193
192
 
194
193
  /**
195
194
  * Typed function signature for the API client.
196
195
  * Config is required when endpoint has path params or body.
197
196
  */
198
197
  export type TypedApiFunction<Paths> = <T extends TypedEndpoint<Paths>>(
199
- endpoint: T,
200
- ...args: IsConfigRequired<Paths, T> extends true
201
- ? [config: FilteredRequestConfig<Paths, T>]
202
- : [config?: FilteredRequestConfig<Paths, T>]
198
+ endpoint: T,
199
+ ...args: IsConfigRequired<Paths, T> extends true
200
+ ? [config: FilteredRequestConfig<Paths, T>]
201
+ : [config?: FilteredRequestConfig<Paths, T>]
203
202
  ) => Promise<ExtractEndpointResponse<Paths, T>>;
204
203
 
205
204
  export interface FetcherOptions {
206
- baseURL?: string;
207
- headers?: Record<string, string>;
208
- onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;
209
- onResponse?: (response: Response) => Response | Promise<Response>;
210
- onError?: (error: Error) => void | Promise<void>;
211
- fetch?: typeof fetch;
205
+ baseURL?: string;
206
+ headers?: Record<string, string>;
207
+ onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;
208
+ onResponse?: (response: Response) => Response | Promise<Response>;
209
+ onError?: (error: Error) => void | Promise<void>;
210
+ fetch?: typeof fetch;
212
211
  }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "composite": true,
7
+ "lib": ["ES2023", "DOM"],
8
+ "jsx": "react-jsx"
9
+ },
10
+ "include": ["src/**/*"],
11
+ "exclude": ["src/__tests__/**/*"]
12
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"fetcher-DLDD_7Sa.mjs","names":["fn?: FetchFn","options: FetcherOptions","endpoint: T","config?: FilteredRequestConfig<Paths, T>","prefix: string","value: unknown","requestConfig: RequestInit","options?: FetcherOptions"],"sources":["../src/fetcher.ts"],"sourcesContent":["import type {\n EndpointString,\n ExtractEndpointResponse,\n FetcherOptions,\n FilteredRequestConfig,\n ParseEndpoint,\n TypedEndpoint,\n} from './types';\n\nexport type { FetcherOptions } from './types';\n\nexport class TypedFetcher<Paths> {\n private baseURL: string;\n private defaultHeaders: Record<string, string>;\n private options: FetcherOptions;\n private fetchFn: FetchFn;\n\n static getFetchFn(fn?: FetchFn): FetchFn {\n if (fn) {\n return fn;\n }\n\n if (typeof window !== 'undefined' && typeof window.fetch === 'function') {\n return window.fetch.bind(window);\n }\n\n if (\n typeof globalThis !== 'undefined' &&\n typeof globalThis.fetch === 'function'\n ) {\n return globalThis.fetch.bind(globalThis);\n }\n\n throw new Error('No fetch implementation found');\n }\n\n constructor(options: FetcherOptions = {}) {\n this.baseURL = options.baseURL || '';\n this.defaultHeaders = options.headers || {};\n this.options = options;\n this.fetchFn = TypedFetcher.getFetchFn(options.fetch);\n }\n\n async request<T extends TypedEndpoint<Paths>>(\n endpoint: T,\n config?: FilteredRequestConfig<Paths, T>,\n ): Promise<ExtractEndpointResponse<Paths, T>> {\n const { method, route } = this.parseEndpoint(endpoint);\n\n // Replace path parameters\n let url = route;\n if (config && 'params' in config && config.params) {\n Object.entries(config.params as Record<string, unknown>).forEach(\n ([key, value]) => {\n url = url.replace(`{${key}}`, encodeURIComponent(String(value)));\n },\n );\n }\n\n // Add query parameters\n if (config && 'query' in config && config.query) {\n const queryParams = new URLSearchParams();\n\n // Recursive function to handle nested objects and arrays\n const appendQueryParam = (prefix: string, value: unknown) => {\n if (value === undefined || value === null) {\n return;\n }\n\n if (Array.isArray(value)) {\n // Handle arrays by appending multiple values with the same key\n value.forEach((item) => {\n queryParams.append(prefix, String(item));\n });\n } else if (typeof value === 'object') {\n // For objects, recursively flatten into dot notation\n Object.entries(value as Record<string, unknown>).forEach(\n ([subKey, subValue]) => {\n appendQueryParam(`${prefix}.${subKey}`, subValue);\n },\n );\n } else {\n queryParams.append(prefix, String(value));\n }\n };\n\n // Process all query parameters\n Object.entries(config.query as Record<string, unknown>).forEach(\n ([key, value]) => {\n appendQueryParam(key, value);\n },\n );\n\n const queryString = queryParams.toString();\n if (queryString) {\n url += `?${queryString}`;\n }\n }\n\n // Build request configuration\n let requestConfig: RequestInit = {\n method: method.toUpperCase(),\n headers: {\n ...this.defaultHeaders,\n ...((config && 'headers' in config && config.headers) || {}),\n },\n };\n\n // Add body if present\n if (config && 'body' in config && config.body) {\n requestConfig.body = JSON.stringify(config.body);\n requestConfig.headers = {\n ...requestConfig.headers,\n 'Content-Type': 'application/json',\n };\n }\n\n // Apply request interceptor\n if (this.options.onRequest) {\n requestConfig = await this.options.onRequest(requestConfig);\n }\n\n try {\n // Make the request\n let response = await this.fetchFn(`${this.baseURL}${url}`, requestConfig);\n\n // Apply response interceptor\n if (this.options.onResponse) {\n response = await this.options.onResponse(response);\n }\n\n // Handle errors\n if (!response.ok) {\n throw response;\n }\n\n // Handle empty responses (204 No Content, etc.)\n if (\n response.status === 204 ||\n response.headers.get('content-length') === '0'\n ) {\n return undefined as ExtractEndpointResponse<Paths, T>;\n }\n\n // Parse JSON response\n const data = await response.json();\n return data as ExtractEndpointResponse<Paths, T>;\n } catch (error) {\n // Apply error handler\n if (this.options.onError) {\n // @ts-ignore\n await this.options.onError(error);\n }\n throw error;\n }\n }\n\n private parseEndpoint<T extends EndpointString>(\n endpoint: T,\n ): ParseEndpoint<T> {\n const [method, ...routeParts] = endpoint.split(' ');\n const route = routeParts.join(' ');\n return { method: method.toLowerCase(), route } as ParseEndpoint<T>;\n }\n}\n\nexport function createTypedFetcher<Paths>(options?: FetcherOptions) {\n const fetcher = new TypedFetcher<Paths>(options);\n return <T extends TypedEndpoint<Paths>>(\n endpoint: T,\n config?: FilteredRequestConfig<Paths, T>,\n ) => fetcher.request(endpoint, config);\n}\n\nexport type FetchFn = typeof fetch;\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"fetcher-KdwHgdAl.cjs","names":["fn?: FetchFn","options: FetcherOptions","endpoint: T","config?: FilteredRequestConfig<Paths, T>","prefix: string","value: unknown","requestConfig: RequestInit","options?: FetcherOptions"],"sources":["../src/fetcher.ts"],"sourcesContent":["import type {\n EndpointString,\n ExtractEndpointResponse,\n FetcherOptions,\n FilteredRequestConfig,\n ParseEndpoint,\n TypedEndpoint,\n} from './types';\n\nexport type { FetcherOptions } from './types';\n\nexport class TypedFetcher<Paths> {\n private baseURL: string;\n private defaultHeaders: Record<string, string>;\n private options: FetcherOptions;\n private fetchFn: FetchFn;\n\n static getFetchFn(fn?: FetchFn): FetchFn {\n if (fn) {\n return fn;\n }\n\n if (typeof window !== 'undefined' && typeof window.fetch === 'function') {\n return window.fetch.bind(window);\n }\n\n if (\n typeof globalThis !== 'undefined' &&\n typeof globalThis.fetch === 'function'\n ) {\n return globalThis.fetch.bind(globalThis);\n }\n\n throw new Error('No fetch implementation found');\n }\n\n constructor(options: FetcherOptions = {}) {\n this.baseURL = options.baseURL || '';\n this.defaultHeaders = options.headers || {};\n this.options = options;\n this.fetchFn = TypedFetcher.getFetchFn(options.fetch);\n }\n\n async request<T extends TypedEndpoint<Paths>>(\n endpoint: T,\n config?: FilteredRequestConfig<Paths, T>,\n ): Promise<ExtractEndpointResponse<Paths, T>> {\n const { method, route } = this.parseEndpoint(endpoint);\n\n // Replace path parameters\n let url = route;\n if (config && 'params' in config && config.params) {\n Object.entries(config.params as Record<string, unknown>).forEach(\n ([key, value]) => {\n url = url.replace(`{${key}}`, encodeURIComponent(String(value)));\n },\n );\n }\n\n // Add query parameters\n if (config && 'query' in config && config.query) {\n const queryParams = new URLSearchParams();\n\n // Recursive function to handle nested objects and arrays\n const appendQueryParam = (prefix: string, value: unknown) => {\n if (value === undefined || value === null) {\n return;\n }\n\n if (Array.isArray(value)) {\n // Handle arrays by appending multiple values with the same key\n value.forEach((item) => {\n queryParams.append(prefix, String(item));\n });\n } else if (typeof value === 'object') {\n // For objects, recursively flatten into dot notation\n Object.entries(value as Record<string, unknown>).forEach(\n ([subKey, subValue]) => {\n appendQueryParam(`${prefix}.${subKey}`, subValue);\n },\n );\n } else {\n queryParams.append(prefix, String(value));\n }\n };\n\n // Process all query parameters\n Object.entries(config.query as Record<string, unknown>).forEach(\n ([key, value]) => {\n appendQueryParam(key, value);\n },\n );\n\n const queryString = queryParams.toString();\n if (queryString) {\n url += `?${queryString}`;\n }\n }\n\n // Build request configuration\n let requestConfig: RequestInit = {\n method: method.toUpperCase(),\n headers: {\n ...this.defaultHeaders,\n ...((config && 'headers' in config && config.headers) || {}),\n },\n };\n\n // Add body if present\n if (config && 'body' in config && config.body) {\n requestConfig.body = JSON.stringify(config.body);\n requestConfig.headers = {\n ...requestConfig.headers,\n 'Content-Type': 'application/json',\n };\n }\n\n // Apply request interceptor\n if (this.options.onRequest) {\n requestConfig = await this.options.onRequest(requestConfig);\n }\n\n try {\n // Make the request\n let response = await this.fetchFn(`${this.baseURL}${url}`, requestConfig);\n\n // Apply response interceptor\n if (this.options.onResponse) {\n response = await this.options.onResponse(response);\n }\n\n // Handle errors\n if (!response.ok) {\n throw response;\n }\n\n // Handle empty responses (204 No Content, etc.)\n if (\n response.status === 204 ||\n response.headers.get('content-length') === '0'\n ) {\n return undefined as ExtractEndpointResponse<Paths, T>;\n }\n\n // Parse JSON response\n const data = await response.json();\n return data as ExtractEndpointResponse<Paths, T>;\n } catch (error) {\n // Apply error handler\n if (this.options.onError) {\n // @ts-ignore\n await this.options.onError(error);\n }\n throw error;\n }\n }\n\n private parseEndpoint<T extends EndpointString>(\n endpoint: T,\n ): ParseEndpoint<T> {\n const [method, ...routeParts] = endpoint.split(' ');\n const route = routeParts.join(' ');\n return { method: method.toLowerCase(), route } as ParseEndpoint<T>;\n }\n}\n\nexport function createTypedFetcher<Paths>(options?: FetcherOptions) {\n const fetcher = new TypedFetcher<Paths>(options);\n return <T extends TypedEndpoint<Paths>>(\n endpoint: T,\n config?: FilteredRequestConfig<Paths, T>,\n ) => fetcher.request(endpoint, config);\n}\n\nexport type FetchFn = typeof fetch;\n"],"mappings":"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"}