make-service 3.0.0 → 3.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/README.md CHANGED
@@ -408,8 +408,11 @@ await enhancedFetch("https://example.com/api/users/:role", {
408
408
  // method: 'POST',
409
409
  // body: '{"some":{"object":{"as":{"body":{}}}}}',
410
410
  // }
411
+ // Response {}
411
412
  ```
412
413
 
414
+ The `trace` function can also return a `Promise<void>` in order to send traces to an external service or database.
415
+
413
416
  ## typedResponse
414
417
 
415
418
  A type-safe wrapper around the `Response` object. It adds a `json` and `text` method that will parse the response with a given zod schema. If you don't provide a schema, it will return `unknown` instead of `any`, then you can also give it a generic to type cast the result.
@@ -525,7 +528,7 @@ You create a `getApiURL` function by giving it a `baseURL` and then it accepts a
525
528
  import { makeGetApiURL } from 'make-service'
526
529
 
527
530
  const getApiURL = makeGetApiURL("https://example.com/api")
528
- const url = getApiURL("/users?admin=true", { query: { page: "2" } })
531
+ const url = getApiURL("/users?admin=true", { page: "2" })
529
532
 
530
533
  // "https://example.com/api/users?admin=true&page=2"
531
534
  ```
@@ -590,9 +593,9 @@ The params will be **strongly-typed** which means they will be validated against
590
593
  <tr>
591
594
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/gustavoguichard"><img src="https://avatars.githubusercontent.com/u/566971?v=4?s=100" width="100px;" alt="Guga Guichard"/><br /><sub><b>Guga Guichard</b></sub></a><br /><a href="#code-gustavoguichard" title="Code">💻</a> <a href="#projectManagement-gustavoguichard" title="Project Management">📆</a> <a href="#promotion-gustavoguichard" title="Promotion">📣</a> <a href="#maintenance-gustavoguichard" title="Maintenance">🚧</a> <a href="#doc-gustavoguichard" title="Documentation">📖</a> <a href="#bug-gustavoguichard" title="Bug reports">🐛</a> <a href="#infra-gustavoguichard" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#question-gustavoguichard" title="Answering Questions">💬</a> <a href="#research-gustavoguichard" title="Research">🔬</a> <a href="#review-gustavoguichard" title="Reviewed Pull Requests">👀</a> <a href="#ideas-gustavoguichard" title="Ideas, Planning, & Feedback">🤔</a> <a href="#example-gustavoguichard" title="Examples">💡</a></td>
592
595
  <td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/danielweinmann"><img src="https://avatars.githubusercontent.com/u/204765?v=4?s=100" width="100px;" alt="Daniel Weinmann"/><br /><sub><b>Daniel Weinmann</b></sub></a><br /><a href="#code-danielweinmann" title="Code">💻</a> <a href="#promotion-danielweinmann" title="Promotion">📣</a> <a href="#ideas-danielweinmann" title="Ideas, Planning, & Feedback">🤔</a> <a href="#doc-danielweinmann" title="Documentation">📖</a> <a href="#bug-danielweinmann" title="Bug reports">🐛</a></td>
596
+ <td align="center" valign="top" width="14.28%"><a href="https://luca.md"><img src="https://avatars.githubusercontent.com/u/1881266?v=4?s=100" width="100px;" alt="Andrei Luca"/><br /><sub><b>Andrei Luca</b></sub></a><br /><a href="#doc-iamandrewluca" title="Documentation">📖</a> <a href="#code-iamandrewluca" title="Code">💻</a> <a href="#promotion-iamandrewluca" title="Promotion">📣</a> <a href="#maintenance-iamandrewluca" title="Maintenance">🚧</a> <a href="#bug-iamandrewluca" title="Bug reports">🐛</a> <a href="#ideas-iamandrewluca" title="Ideas, Planning, & Feedback">🤔</a></td>
593
597
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/diogob"><img src="https://avatars.githubusercontent.com/u/20662?v=4?s=100" width="100px;" alt="Diogo Biazus"/><br /><sub><b>Diogo Biazus</b></sub></a><br /><a href="#code-diogob" title="Code">💻</a></td>
594
598
  <td align="center" valign="top" width="14.28%"><a href="https://github.com/garusis"><img src="https://avatars.githubusercontent.com/u/15615652?v=4?s=100" width="100px;" alt="Marcos Javier Alvarez Maestre"/><br /><sub><b>Marcos Javier Alvarez Maestre</b></sub></a><br /><a href="#code-garusis" title="Code">💻</a> <a href="#bug-garusis" title="Bug reports">🐛</a></td>
595
- <td align="center" valign="top" width="14.28%"><a href="https://luca.md"><img src="https://avatars.githubusercontent.com/u/1881266?v=4?s=100" width="100px;" alt="Andrei Luca"/><br /><sub><b>Andrei Luca</b></sub></a><br /><a href="#doc-iamandrewluca" title="Documentation">📖</a></td>
596
599
  </tr>
597
600
  </tbody>
598
601
  </table>
@@ -0,0 +1,144 @@
1
+ declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD", "CONNECT"];
2
+
3
+ type Schema<T> = {
4
+ parse: (d: unknown) => T;
5
+ };
6
+ type JSONValue = string | number | boolean | Date | {
7
+ [x: string]: JSONValue | undefined | null;
8
+ } | Array<JSONValue | undefined | null>;
9
+ type SearchParams = ConstructorParameters<typeof URLSearchParams>[0];
10
+ type TypedResponse = Omit<Response, 'json' | 'text'> & {
11
+ json: TypedResponseJson;
12
+ text: TypedResponseText;
13
+ };
14
+ type PathParams<T> = T extends string ? ExtractPathParams<T> extends Record<string, unknown> ? ExtractPathParams<T> : Record<string, string> : Record<string, string>;
15
+ type EnhancedRequestInit<T = string> = Omit<RequestInit, 'body' | 'method'> & {
16
+ method?: HTTPMethod | Lowercase<HTTPMethod>;
17
+ body?: JSONValue | BodyInit | null;
18
+ query?: SearchParams;
19
+ params?: PathParams<T>;
20
+ trace?: (fullUrl: string | URL, init: EnhancedRequestInit, response: TypedResponse) => void | Promise<void>;
21
+ };
22
+ type ServiceRequestInit<T = string> = Omit<EnhancedRequestInit<T>, 'method'>;
23
+ type RequestTransformer = (request: EnhancedRequestInit) => EnhancedRequestInit | Promise<EnhancedRequestInit>;
24
+ type ResponseTransformer = (response: TypedResponse) => TypedResponse | Promise<TypedResponse>;
25
+ type BaseOptions = {
26
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
27
+ requestTransformer?: RequestTransformer;
28
+ responseTransformer?: ResponseTransformer;
29
+ };
30
+ type HTTPMethod = (typeof HTTP_METHODS)[number];
31
+ type TypedResponseJson = <T = unknown>(schema?: Schema<T>) => Promise<T>;
32
+ type TypedResponseText = <T extends string = string>(schema?: Schema<T>) => Promise<T>;
33
+ type GetJson = (response: Response) => TypedResponseJson;
34
+ type GetText = (response: Response) => TypedResponseText;
35
+ type Prettify<T> = {
36
+ [K in keyof T]: T[K];
37
+ } & {};
38
+ type ExtractPathParams<T extends string> = T extends `${infer _}:${infer Param}/${infer Rest}` ? Prettify<Omit<{
39
+ [K in Param]: string;
40
+ } & ExtractPathParams<Rest>, ''>> : T extends `${infer _}:${infer Param}` ? {
41
+ [K in Param]: string;
42
+ } : {};
43
+
44
+ /**
45
+ * It hacks the Response object to add typed json and text methods
46
+ * @param response the Response to be proxied
47
+ * @returns a Response with typed json and text methods
48
+ * @example const response = await fetch("https://example.com/api/users");
49
+ * const users = await response.json(userSchema);
50
+ * // ^? User[]
51
+ * const untyped = await response.json();
52
+ * // ^? unknown
53
+ * const text = await response.text();
54
+ * // ^? string
55
+ * const typedJson = await response.json<User[]>();
56
+ * // ^? User[]
57
+ */
58
+ declare function typedResponse(response: Response, options?: {
59
+ getJson?: GetJson;
60
+ getText?: GetText;
61
+ }): TypedResponse;
62
+ /**
63
+ *
64
+ * @param url a string or URL to be fetched
65
+ * @param requestInit the requestInit to be passed to the fetch request. It is the same as the `RequestInit` type, but it also accepts a JSON-like `body` and an object-like `query` parameter.
66
+ * @param requestInit.body the body of the request. It will be automatically stringified so you can send a JSON-like object
67
+ * @param requestInit.query the query parameters to be added to the URL
68
+ * @param requestInit.trace a function that receives the URL, the requestInit and a clone of the response in order to log or troubleshoot the request
69
+ * @returns a Response with typed json and text methods
70
+ * @example const response = await fetch("https://example.com/api/users");
71
+ * const users = await response.json(userSchema);
72
+ * // ^? User[]
73
+ * const untyped = await response.json();
74
+ * // ^? unknown
75
+ */
76
+ declare function enhancedFetch<T extends string | URL>(url: T, requestInit?: EnhancedRequestInit<T>): Promise<TypedResponse>;
77
+ /**
78
+ *
79
+ * @param baseURL the base URL to be fetched in every request
80
+ * @param baseOptions options that will be applied to all requests
81
+ * @param baseOptions.headers any headers that should be sent with every request
82
+ * @param baseOptions.requestTransformer a function that will transform the enhanced request init of every request
83
+ * @param baseOptions.responseTransformer a function that will transform the typed response of every request
84
+ * @returns a function that receive a path and requestInit and return a serialized json response that can be typed or not.
85
+ * @example const headers = { Authorization: "Bearer 123" }
86
+ * const fetcher = makeFetcher("https://example.com/api", headers);
87
+ * const response = await fetcher("/users", { method: "GET" })
88
+ * const users = await response.json(userSchema);
89
+ * // ^? User[]
90
+ */
91
+ declare function makeFetcher(baseURL: string | URL, baseOptions?: BaseOptions): <T extends string>(path: T, requestInit?: EnhancedRequestInit<T>) => Promise<TypedResponse>;
92
+ /**
93
+ *
94
+ * @param baseURL the base URL to the API
95
+ * @param baseOptions options that will be applied to all requests
96
+ * @param baseOptions.headers any headers that should be sent with every request
97
+ * @param baseOptions.requestTransformer a function that will transform the enhanced request init of every request
98
+ * @param baseOptions.responseTransformer a function that will transform the typed response of every request
99
+ * @returns a service object with HTTP methods that are functions that receive a path and requestInit and return a serialized json response that can be typed or not.
100
+ * @example const headers = { Authorization: "Bearer 123" }
101
+ * const api = makeService("https://example.com/api", headers);
102
+ * const response = await api.get("/users")
103
+ * const users = await response.json(userSchema);
104
+ * // ^? User[]
105
+ */
106
+ declare function makeService(baseURL: string | URL, baseOptions?: BaseOptions): Record<"get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "connect", <T extends string>(path: T, requestInit?: ServiceRequestInit<T>) => Promise<TypedResponse>>;
107
+
108
+ /**
109
+ * @param url a string or URL to which the query parameters will be added
110
+ * @param searchParams the query parameters
111
+ * @returns the url with the query parameters added with the same type as the url
112
+ */
113
+ declare function addQueryToURL<T extends string | URL>(url: T, searchParams?: SearchParams): T;
114
+ /**
115
+ * @param body the JSON-like body of the request
116
+ * @returns the body is stringified if it is not a string and it is a JSON-like object. It also accepts other types of BodyInit such as Blob, ReadableStream, etc.
117
+ */
118
+ declare function ensureStringBody<B extends JSONValue | BodyInit | null>(body?: B): B extends JSONValue ? string : B;
119
+ /**
120
+ * @param baseURL the base path to the API
121
+ * @returns a function that receives a path and an object of query parameters and returns a URL
122
+ */
123
+ declare function makeGetApiURL<T extends string | URL>(baseURL: T): (path: string, searchParams?: SearchParams) => T;
124
+ /**
125
+ * It merges multiple HeadersInit objects into a single Headers object
126
+ * @param entries Any number of HeadersInit objects
127
+ * @returns a new Headers object with the merged headers
128
+ */
129
+ declare function mergeHeaders(...entries: (HeadersInit | [string, undefined][] | Record<string, undefined>)[]): Headers;
130
+ /**
131
+ *
132
+ * @param url the url string or URL object to replace the params
133
+ * @param params the params map to be replaced in the url
134
+ * @returns the url with the params replaced and with the same type as the given url
135
+ */
136
+ declare function replaceURLParams<T extends string | URL>(url: T, params: PathParams<T>): T;
137
+ /**
138
+ * This is an enhanced version of the typeof operator to check the type of more complex values.
139
+ * @param t the value to be checked
140
+ * @returns the type of the value
141
+ */
142
+ declare function typeOf(t: unknown): "array" | "arraybuffer" | "bigint" | "blob" | "boolean" | "formdata" | "function" | "null" | "number" | "object" | "readablestream" | "string" | "symbol" | "undefined" | "url" | "urlsearchparams";
143
+
144
+ export { type BaseOptions, type EnhancedRequestInit, type GetJson, type GetText, type HTTPMethod, type JSONValue, type PathParams, type RequestTransformer, type ResponseTransformer, type Schema, type SearchParams, type ServiceRequestInit, type TypedResponse, type TypedResponseJson, type TypedResponseText, addQueryToURL, enhancedFetch, ensureStringBody, makeFetcher, makeGetApiURL, makeService, mergeHeaders, replaceURLParams, typeOf, typedResponse };
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ type EnhancedRequestInit<T = string> = Omit<RequestInit, 'body' | 'method'> & {
17
17
  body?: JSONValue | BodyInit | null;
18
18
  query?: SearchParams;
19
19
  params?: PathParams<T>;
20
- trace?: (...args: Parameters<typeof fetch>) => void;
20
+ trace?: (fullUrl: string | URL, init: EnhancedRequestInit, response: TypedResponse) => void | Promise<void>;
21
21
  };
22
22
  type ServiceRequestInit<T = string> = Omit<EnhancedRequestInit<T>, 'method'>;
23
23
  type RequestTransformer = (request: EnhancedRequestInit) => EnhancedRequestInit | Promise<EnhancedRequestInit>;
@@ -65,7 +65,7 @@ declare function typedResponse(response: Response, options?: {
65
65
  * @param requestInit the requestInit to be passed to the fetch request. It is the same as the `RequestInit` type, but it also accepts a JSON-like `body` and an object-like `query` parameter.
66
66
  * @param requestInit.body the body of the request. It will be automatically stringified so you can send a JSON-like object
67
67
  * @param requestInit.query the query parameters to be added to the URL
68
- * @param requestInit.trace a function that receives the URL and the requestInit and can be used to log the request
68
+ * @param requestInit.trace a function that receives the URL, the requestInit and a clone of the response in order to log or troubleshoot the request
69
69
  * @returns a Response with typed json and text methods
70
70
  * @example const response = await fetch("https://example.com/api/users");
71
71
  * const users = await response.json(userSchema);
@@ -139,6 +139,6 @@ declare function replaceURLParams<T extends string | URL>(url: T, params: PathPa
139
139
  * @param t the value to be checked
140
140
  * @returns the type of the value
141
141
  */
142
- declare function typeOf(t: unknown): "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "url" | "blob" | "array" | "arraybuffer" | "formdata" | "null" | "readablestream" | "urlsearchparams";
142
+ declare function typeOf(t: unknown): "array" | "arraybuffer" | "bigint" | "blob" | "boolean" | "formdata" | "function" | "null" | "number" | "object" | "readablestream" | "string" | "symbol" | "undefined" | "url" | "urlsearchparams";
143
143
 
144
- export { BaseOptions, EnhancedRequestInit, GetJson, GetText, HTTPMethod, JSONValue, PathParams, RequestTransformer, ResponseTransformer, Schema, SearchParams, ServiceRequestInit, TypedResponse, TypedResponseJson, TypedResponseText, addQueryToURL, enhancedFetch, ensureStringBody, makeFetcher, makeGetApiURL, makeService, mergeHeaders, replaceURLParams, typeOf, typedResponse };
144
+ export { type BaseOptions, type EnhancedRequestInit, type GetJson, type GetText, type HTTPMethod, type JSONValue, type PathParams, type RequestTransformer, type ResponseTransformer, type Schema, type SearchParams, type ServiceRequestInit, type TypedResponse, type TypedResponseJson, type TypedResponseText, addQueryToURL, enhancedFetch, ensureStringBody, makeFetcher, makeGetApiURL, makeService, mergeHeaders, replaceURLParams, typeOf, typedResponse };
package/dist/index.js CHANGED
@@ -58,8 +58,7 @@ var getText = (response) => async (schema) => {
58
58
 
59
59
  // src/primitives.ts
60
60
  function addQueryToURL(url, searchParams) {
61
- if (!searchParams)
62
- return url;
61
+ if (!searchParams) return url;
63
62
  if (typeof url === "string") {
64
63
  const separator = url.includes("?") ? "&" : "?";
65
64
  return `${url}${separator}${new URLSearchParams(searchParams)}`;
@@ -72,10 +71,8 @@ function addQueryToURL(url, searchParams) {
72
71
  return url;
73
72
  }
74
73
  function ensureStringBody(body) {
75
- if (typeof body === "undefined")
76
- return body;
77
- if (typeof body === "string")
78
- return body;
74
+ if (typeof body === "undefined") return body;
75
+ if (typeof body === "string") return body;
79
76
  return ["number", "boolean", "array", "object"].includes(typeOf(body)) ? JSON.stringify(body) : body;
80
77
  }
81
78
  function makeGetApiURL(baseURL) {
@@ -100,8 +97,7 @@ function mergeHeaders(...entries) {
100
97
  return new Headers(Array.from(result.entries()));
101
98
  }
102
99
  function replaceURLParams(url, params) {
103
- if (!params)
104
- return url;
100
+ if (!params) return url;
105
101
  let urlString = String(url);
106
102
  Object.entries(params).forEach(([key, value]) => {
107
103
  urlString = urlString.replace(new RegExp(`:${key}($|/)`), `${value}$1`);
@@ -115,15 +111,12 @@ function typeOf(t) {
115
111
  // src/api.ts
116
112
  var identity = (value) => value;
117
113
  function typedResponse(response, options) {
118
- var _a, _b;
119
- const getJsonFn = (_a = options == null ? void 0 : options.getJson) != null ? _a : getJson;
120
- const getTextFn = (_b = options == null ? void 0 : options.getText) != null ? _b : getText;
114
+ const getJsonFn = options?.getJson ?? getJson;
115
+ const getTextFn = options?.getText ?? getText;
121
116
  return new Proxy(response, {
122
117
  get(target, prop) {
123
- if (prop === "json")
124
- return getJsonFn(target);
125
- if (prop === "text")
126
- return getTextFn(target);
118
+ if (prop === "json") return getJsonFn(target);
119
+ if (prop === "text") return getTextFn(target);
127
120
  const value = Reflect.get(target, prop);
128
121
  if (typeof value === "function") {
129
122
  return value.bind(target);
@@ -133,32 +126,28 @@ function typedResponse(response, options) {
133
126
  });
134
127
  }
135
128
  async function enhancedFetch(url, requestInit) {
136
- var _a;
137
- const { query, trace, ...reqInit } = requestInit != null ? requestInit : {};
129
+ const { query, trace, ...reqInit } = requestInit ?? {};
138
130
  const body = ensureStringBody(reqInit.body);
139
- const withParams = replaceURLParams(url, (_a = reqInit.params) != null ? _a : {});
131
+ const withParams = replaceURLParams(url, reqInit.params ?? {});
140
132
  const fullURL = addQueryToURL(withParams, query);
141
133
  const enhancedReqInit = { ...reqInit, body };
142
- trace == null ? void 0 : trace(fullURL, enhancedReqInit);
143
134
  const response = await fetch(fullURL, enhancedReqInit);
135
+ await trace?.(fullURL, enhancedReqInit, typedResponse(response.clone()));
144
136
  return typedResponse(response);
145
137
  }
146
138
  function makeFetcher(baseURL, baseOptions = {}) {
147
139
  return async (path, requestInit = {}) => {
148
- var _a, _b;
149
140
  const { headers } = baseOptions;
150
- const requestTransformer = (_a = baseOptions.requestTransformer) != null ? _a : identity;
151
- const responseTransformer = (_b = baseOptions.responseTransformer) != null ? _b : identity;
152
- const headerTransformer = async (ri) => {
153
- var _a2;
154
- return {
155
- ...ri,
156
- headers: mergeHeaders(
157
- typeof headers === "function" ? await headers() : headers != null ? headers : {},
158
- (_a2 = requestInit == null ? void 0 : requestInit.headers) != null ? _a2 : {}
159
- )
160
- };
161
- };
141
+ const requestTransformer = baseOptions.requestTransformer ?? identity;
142
+ const responseTransformer = baseOptions.responseTransformer ?? identity;
143
+ const headerTransformer = async (ri) => ({
144
+ ...ri,
145
+ headers: mergeHeaders(
146
+ typeof headers === "function" ? await headers() : headers ?? {},
147
+ ri.headers ?? {},
148
+ requestInit?.headers ?? {}
149
+ )
150
+ });
162
151
  const url = makeGetApiURL(baseURL)(path);
163
152
  const response = await enhancedFetch(
164
153
  url,
package/dist/index.mjs CHANGED
@@ -23,8 +23,7 @@ var getText = (response) => async (schema) => {
23
23
 
24
24
  // src/primitives.ts
25
25
  function addQueryToURL(url, searchParams) {
26
- if (!searchParams)
27
- return url;
26
+ if (!searchParams) return url;
28
27
  if (typeof url === "string") {
29
28
  const separator = url.includes("?") ? "&" : "?";
30
29
  return `${url}${separator}${new URLSearchParams(searchParams)}`;
@@ -37,10 +36,8 @@ function addQueryToURL(url, searchParams) {
37
36
  return url;
38
37
  }
39
38
  function ensureStringBody(body) {
40
- if (typeof body === "undefined")
41
- return body;
42
- if (typeof body === "string")
43
- return body;
39
+ if (typeof body === "undefined") return body;
40
+ if (typeof body === "string") return body;
44
41
  return ["number", "boolean", "array", "object"].includes(typeOf(body)) ? JSON.stringify(body) : body;
45
42
  }
46
43
  function makeGetApiURL(baseURL) {
@@ -65,8 +62,7 @@ function mergeHeaders(...entries) {
65
62
  return new Headers(Array.from(result.entries()));
66
63
  }
67
64
  function replaceURLParams(url, params) {
68
- if (!params)
69
- return url;
65
+ if (!params) return url;
70
66
  let urlString = String(url);
71
67
  Object.entries(params).forEach(([key, value]) => {
72
68
  urlString = urlString.replace(new RegExp(`:${key}($|/)`), `${value}$1`);
@@ -80,15 +76,12 @@ function typeOf(t) {
80
76
  // src/api.ts
81
77
  var identity = (value) => value;
82
78
  function typedResponse(response, options) {
83
- var _a, _b;
84
- const getJsonFn = (_a = options == null ? void 0 : options.getJson) != null ? _a : getJson;
85
- const getTextFn = (_b = options == null ? void 0 : options.getText) != null ? _b : getText;
79
+ const getJsonFn = options?.getJson ?? getJson;
80
+ const getTextFn = options?.getText ?? getText;
86
81
  return new Proxy(response, {
87
82
  get(target, prop) {
88
- if (prop === "json")
89
- return getJsonFn(target);
90
- if (prop === "text")
91
- return getTextFn(target);
83
+ if (prop === "json") return getJsonFn(target);
84
+ if (prop === "text") return getTextFn(target);
92
85
  const value = Reflect.get(target, prop);
93
86
  if (typeof value === "function") {
94
87
  return value.bind(target);
@@ -98,32 +91,28 @@ function typedResponse(response, options) {
98
91
  });
99
92
  }
100
93
  async function enhancedFetch(url, requestInit) {
101
- var _a;
102
- const { query, trace, ...reqInit } = requestInit != null ? requestInit : {};
94
+ const { query, trace, ...reqInit } = requestInit ?? {};
103
95
  const body = ensureStringBody(reqInit.body);
104
- const withParams = replaceURLParams(url, (_a = reqInit.params) != null ? _a : {});
96
+ const withParams = replaceURLParams(url, reqInit.params ?? {});
105
97
  const fullURL = addQueryToURL(withParams, query);
106
98
  const enhancedReqInit = { ...reqInit, body };
107
- trace == null ? void 0 : trace(fullURL, enhancedReqInit);
108
99
  const response = await fetch(fullURL, enhancedReqInit);
100
+ await trace?.(fullURL, enhancedReqInit, typedResponse(response.clone()));
109
101
  return typedResponse(response);
110
102
  }
111
103
  function makeFetcher(baseURL, baseOptions = {}) {
112
104
  return async (path, requestInit = {}) => {
113
- var _a, _b;
114
105
  const { headers } = baseOptions;
115
- const requestTransformer = (_a = baseOptions.requestTransformer) != null ? _a : identity;
116
- const responseTransformer = (_b = baseOptions.responseTransformer) != null ? _b : identity;
117
- const headerTransformer = async (ri) => {
118
- var _a2;
119
- return {
120
- ...ri,
121
- headers: mergeHeaders(
122
- typeof headers === "function" ? await headers() : headers != null ? headers : {},
123
- (_a2 = requestInit == null ? void 0 : requestInit.headers) != null ? _a2 : {}
124
- )
125
- };
126
- };
106
+ const requestTransformer = baseOptions.requestTransformer ?? identity;
107
+ const responseTransformer = baseOptions.responseTransformer ?? identity;
108
+ const headerTransformer = async (ri) => ({
109
+ ...ri,
110
+ headers: mergeHeaders(
111
+ typeof headers === "function" ? await headers() : headers ?? {},
112
+ ri.headers ?? {},
113
+ requestInit?.headers ?? {}
114
+ )
115
+ });
127
116
  const url = makeGetApiURL(baseURL)(path);
128
117
  const response = await enhancedFetch(
129
118
  url,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-service",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Some utilities to extend the 'fetch' API to better interact with external APIs.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -10,18 +10,20 @@
10
10
  "scripts": {
11
11
  "build": "tsup ./src/index.ts --format esm,cjs --dts",
12
12
  "dev": "tsup ./src/index.ts --format esm,cjs --watch --dts",
13
- "lint": "eslint *.ts*",
14
- "tsc": "tsc",
13
+ "lint": "eslint --config eslint.config.mjs .",
14
+ "tsc": "tsc --noEmit",
15
15
  "test": "vitest run"
16
16
  },
17
17
  "devDependencies": {
18
- "@typescript-eslint/eslint-plugin": "^6.3.0",
19
- "eslint": "latest",
20
- "jsdom": "^22.1.0",
18
+ "@types/node": "^22.1.0",
19
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
20
+ "@typescript-eslint/parser": "^8.0.0",
21
+ "eslint": "^9.8.0",
22
+ "jsdom": "^24.1.1",
21
23
  "prettier": "latest",
22
- "string-ts": "^0.4.1",
23
- "tsup": "^6.7.0",
24
- "typescript": "^5.1.3",
24
+ "string-ts": "^2.2.0",
25
+ "tsup": "^8.2.4",
26
+ "typescript": "^5.5.4",
25
27
  "vitest": "latest",
26
28
  "zod": "latest"
27
29
  },