endpoint-fetcher 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -10,13 +10,25 @@ type EndpointConfig<TInput = any, TOutput = any, TError = any> = {
10
10
  baseUrl: string;
11
11
  }) => Promise<TOutput>;
12
12
  };
13
+ type Hooks = {
14
+ beforeRequest?: (url: string, init: RequestInit) => Promise<{
15
+ url: string;
16
+ init: RequestInit;
17
+ }> | {
18
+ url: string;
19
+ init: RequestInit;
20
+ };
21
+ afterResponse?: (response: Response, url: string, init: RequestInit) => Promise<Response> | Response;
22
+ onError?: (error: unknown) => Promise<void> | void;
23
+ };
13
24
  type ApiConfig = {
14
25
  baseUrl: string;
15
26
  fetch?: typeof fetch;
16
27
  defaultHeaders?: HeadersInit;
28
+ hooks?: Hooks;
17
29
  };
18
30
  type EndpointDefinitions = Record<string, EndpointConfig>;
19
31
 
20
32
  declare function createApiClient<TEndpoints extends EndpointDefinitions>(endpoints: TEndpoints, config: ApiConfig): { [K in keyof TEndpoints]: TEndpoints[K] extends infer T ? T extends TEndpoints[K] ? T extends EndpointConfig<infer TInput, infer TOutput, any> ? (input: TInput) => Promise<TOutput> : never : never : never; };
21
33
 
22
- export { type ApiConfig, type EndpointConfig, type EndpointDefinitions, type HttpMethod, createApiClient };
34
+ export { type ApiConfig, type EndpointConfig, type EndpointDefinitions, type Hooks, type HttpMethod, createApiClient };
package/dist/index.d.ts CHANGED
@@ -10,13 +10,25 @@ type EndpointConfig<TInput = any, TOutput = any, TError = any> = {
10
10
  baseUrl: string;
11
11
  }) => Promise<TOutput>;
12
12
  };
13
+ type Hooks = {
14
+ beforeRequest?: (url: string, init: RequestInit) => Promise<{
15
+ url: string;
16
+ init: RequestInit;
17
+ }> | {
18
+ url: string;
19
+ init: RequestInit;
20
+ };
21
+ afterResponse?: (response: Response, url: string, init: RequestInit) => Promise<Response> | Response;
22
+ onError?: (error: unknown) => Promise<void> | void;
23
+ };
13
24
  type ApiConfig = {
14
25
  baseUrl: string;
15
26
  fetch?: typeof fetch;
16
27
  defaultHeaders?: HeadersInit;
28
+ hooks?: Hooks;
17
29
  };
18
30
  type EndpointDefinitions = Record<string, EndpointConfig>;
19
31
 
20
32
  declare function createApiClient<TEndpoints extends EndpointDefinitions>(endpoints: TEndpoints, config: ApiConfig): { [K in keyof TEndpoints]: TEndpoints[K] extends infer T ? T extends TEndpoints[K] ? T extends EndpointConfig<infer TInput, infer TOutput, any> ? (input: TInput) => Promise<TOutput> : never : never : never; };
21
33
 
22
- export { type ApiConfig, type EndpointConfig, type EndpointDefinitions, type HttpMethod, createApiClient };
34
+ export { type ApiConfig, type EndpointConfig, type EndpointDefinitions, type Hooks, type HttpMethod, createApiClient };
package/dist/index.js CHANGED
@@ -24,11 +24,33 @@ __export(index_exports, {
24
24
  module.exports = __toCommonJS(index_exports);
25
25
  function createApiClient(endpoints, config) {
26
26
  const fetchInstance = config.fetch ?? globalThis.fetch;
27
+ const hooks = config.hooks;
27
28
  const buildUrl = (path, baseUrl) => {
28
29
  const normalizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
29
30
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
30
31
  return `${normalizedBase}${normalizedPath}`;
31
32
  };
33
+ const enhancedFetch = async (input, init) => {
34
+ let url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
35
+ let finalInit = { ...init };
36
+ if (hooks == null ? void 0 : hooks.beforeRequest) {
37
+ const result = await hooks.beforeRequest(url, finalInit);
38
+ url = result.url;
39
+ finalInit = result.init;
40
+ }
41
+ try {
42
+ let response = await fetchInstance(url, finalInit);
43
+ if (hooks == null ? void 0 : hooks.afterResponse) {
44
+ response = await hooks.afterResponse(response, url, finalInit);
45
+ }
46
+ return response;
47
+ } catch (error) {
48
+ if (hooks == null ? void 0 : hooks.onError) {
49
+ await hooks.onError(error);
50
+ }
51
+ throw error;
52
+ }
53
+ };
32
54
  const defaultHandler = async (method, path, input) => {
33
55
  const url = buildUrl(path, config.baseUrl);
34
56
  const options = {
@@ -41,7 +63,7 @@ function createApiClient(endpoints, config) {
41
63
  if (method !== "GET" && method !== "DELETE" && input !== void 0) {
42
64
  options.body = JSON.stringify(input);
43
65
  }
44
- const response = await fetchInstance(url, options);
66
+ const response = await enhancedFetch(url, options);
45
67
  if (!response.ok) {
46
68
  const error = await response.json().catch(() => ({}));
47
69
  throw {
@@ -59,7 +81,7 @@ function createApiClient(endpoints, config) {
59
81
  if (endpoint.handler) {
60
82
  return endpoint.handler({
61
83
  input,
62
- fetch: fetchInstance,
84
+ fetch: enhancedFetch,
63
85
  method: endpoint.method,
64
86
  path,
65
87
  baseUrl: config.baseUrl
package/dist/index.mjs CHANGED
@@ -1,11 +1,33 @@
1
1
  // src/index.ts
2
2
  function createApiClient(endpoints, config) {
3
3
  const fetchInstance = config.fetch ?? globalThis.fetch;
4
+ const hooks = config.hooks;
4
5
  const buildUrl = (path, baseUrl) => {
5
6
  const normalizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
6
7
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
7
8
  return `${normalizedBase}${normalizedPath}`;
8
9
  };
10
+ const enhancedFetch = async (input, init) => {
11
+ let url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
12
+ let finalInit = { ...init };
13
+ if (hooks == null ? void 0 : hooks.beforeRequest) {
14
+ const result = await hooks.beforeRequest(url, finalInit);
15
+ url = result.url;
16
+ finalInit = result.init;
17
+ }
18
+ try {
19
+ let response = await fetchInstance(url, finalInit);
20
+ if (hooks == null ? void 0 : hooks.afterResponse) {
21
+ response = await hooks.afterResponse(response, url, finalInit);
22
+ }
23
+ return response;
24
+ } catch (error) {
25
+ if (hooks == null ? void 0 : hooks.onError) {
26
+ await hooks.onError(error);
27
+ }
28
+ throw error;
29
+ }
30
+ };
9
31
  const defaultHandler = async (method, path, input) => {
10
32
  const url = buildUrl(path, config.baseUrl);
11
33
  const options = {
@@ -18,7 +40,7 @@ function createApiClient(endpoints, config) {
18
40
  if (method !== "GET" && method !== "DELETE" && input !== void 0) {
19
41
  options.body = JSON.stringify(input);
20
42
  }
21
- const response = await fetchInstance(url, options);
43
+ const response = await enhancedFetch(url, options);
22
44
  if (!response.ok) {
23
45
  const error = await response.json().catch(() => ({}));
24
46
  throw {
@@ -36,7 +58,7 @@ function createApiClient(endpoints, config) {
36
58
  if (endpoint.handler) {
37
59
  return endpoint.handler({
38
60
  input,
39
- fetch: fetchInstance,
61
+ fetch: enhancedFetch,
40
62
  method: endpoint.method,
41
63
  path,
42
64
  baseUrl: config.baseUrl
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "endpoint-fetcher",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Type-safe API client builder using fetch",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",