make-service 2.1.2 → 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 +6 -6
- package/dist/index.d.mts +144 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +28 -41
- package/dist/index.mjs +28 -41
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -102,7 +102,6 @@ On the example above, the request will be sent with the following arguments:
|
|
|
102
102
|
// {
|
|
103
103
|
// method: 'GET',
|
|
104
104
|
// headers: {
|
|
105
|
-
// 'content-type': 'application/json',
|
|
106
105
|
// 'authorization': 'Bearer 123',
|
|
107
106
|
// }
|
|
108
107
|
// }
|
|
@@ -202,7 +201,7 @@ const service = makeService("https://example.com/api", {
|
|
|
202
201
|
})
|
|
203
202
|
|
|
204
203
|
const response = await service.get("/users", {
|
|
205
|
-
headers: new Headers({ authorization: 'undefined'
|
|
204
|
+
headers: new Headers({ authorization: 'undefined' }),
|
|
206
205
|
})
|
|
207
206
|
// headers will be empty.
|
|
208
207
|
```
|
|
@@ -344,7 +343,7 @@ const service = makeService("https://example.com/api")
|
|
|
344
343
|
const response = await service.get("/users/:id", {
|
|
345
344
|
params: { id: "2" },
|
|
346
345
|
query: { page: "2"},
|
|
347
|
-
headers: { Accept: "application/json" },
|
|
346
|
+
headers: { Accept: "application/json", "Content-type": "application/json" },
|
|
348
347
|
trace: (url, requestInit) => {
|
|
349
348
|
console.log("The request was sent to " + url)
|
|
350
349
|
console.log("with the following params: " + JSON.stringify(requestInit))
|
|
@@ -408,11 +407,11 @@ await enhancedFetch("https://example.com/api/users/:role", {
|
|
|
408
407
|
// {
|
|
409
408
|
// method: 'POST',
|
|
410
409
|
// body: '{"some":{"object":{"as":{"body":{}}}}}',
|
|
411
|
-
// headers: { 'content-type': 'application/json' }
|
|
412
410
|
// }
|
|
411
|
+
// Response {}
|
|
413
412
|
```
|
|
414
413
|
|
|
415
|
-
|
|
414
|
+
The `trace` function can also return a `Promise<void>` in order to send traces to an external service or database.
|
|
416
415
|
|
|
417
416
|
## typedResponse
|
|
418
417
|
|
|
@@ -529,7 +528,7 @@ You create a `getApiURL` function by giving it a `baseURL` and then it accepts a
|
|
|
529
528
|
import { makeGetApiURL } from 'make-service'
|
|
530
529
|
|
|
531
530
|
const getApiURL = makeGetApiURL("https://example.com/api")
|
|
532
|
-
const url = getApiURL("/users?admin=true", {
|
|
531
|
+
const url = getApiURL("/users?admin=true", { page: "2" })
|
|
533
532
|
|
|
534
533
|
// "https://example.com/api/users?admin=true&page=2"
|
|
535
534
|
```
|
|
@@ -594,6 +593,7 @@ The params will be **strongly-typed** which means they will be validated against
|
|
|
594
593
|
<tr>
|
|
595
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>
|
|
596
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>
|
|
597
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>
|
|
598
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>
|
|
599
599
|
</tr>
|
package/dist/index.d.mts
ADDED
|
@@ -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?: (
|
|
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
|
|
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): "
|
|
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
|
-
|
|
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,52 +111,43 @@ function typeOf(t) {
|
|
|
115
111
|
// src/api.ts
|
|
116
112
|
var identity = (value) => value;
|
|
117
113
|
function typedResponse(response, options) {
|
|
118
|
-
|
|
119
|
-
const
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
118
|
+
if (prop === "json") return getJsonFn(target);
|
|
119
|
+
if (prop === "text") return getTextFn(target);
|
|
120
|
+
const value = Reflect.get(target, prop);
|
|
121
|
+
if (typeof value === "function") {
|
|
122
|
+
return value.bind(target);
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
128
125
|
}
|
|
129
126
|
});
|
|
130
127
|
}
|
|
131
128
|
async function enhancedFetch(url, requestInit) {
|
|
132
|
-
|
|
133
|
-
const { query, trace, ...reqInit } = requestInit != null ? requestInit : {};
|
|
134
|
-
const headers = mergeHeaders(
|
|
135
|
-
{
|
|
136
|
-
"content-type": "application/json"
|
|
137
|
-
},
|
|
138
|
-
(_a = reqInit.headers) != null ? _a : {}
|
|
139
|
-
);
|
|
140
|
-
const withParams = replaceURLParams(url, (_b = reqInit.params) != null ? _b : {});
|
|
141
|
-
const fullURL = addQueryToURL(withParams, query);
|
|
129
|
+
const { query, trace, ...reqInit } = requestInit ?? {};
|
|
142
130
|
const body = ensureStringBody(reqInit.body);
|
|
143
|
-
const
|
|
144
|
-
|
|
131
|
+
const withParams = replaceURLParams(url, reqInit.params ?? {});
|
|
132
|
+
const fullURL = addQueryToURL(withParams, query);
|
|
133
|
+
const enhancedReqInit = { ...reqInit, body };
|
|
145
134
|
const response = await fetch(fullURL, enhancedReqInit);
|
|
135
|
+
await trace?.(fullURL, enhancedReqInit, typedResponse(response.clone()));
|
|
146
136
|
return typedResponse(response);
|
|
147
137
|
}
|
|
148
138
|
function makeFetcher(baseURL, baseOptions = {}) {
|
|
149
139
|
return async (path, requestInit = {}) => {
|
|
150
|
-
var _a, _b;
|
|
151
140
|
const { headers } = baseOptions;
|
|
152
|
-
const requestTransformer =
|
|
153
|
-
const responseTransformer =
|
|
154
|
-
const headerTransformer = async (ri) => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
headers
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
};
|
|
163
|
-
};
|
|
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
|
+
});
|
|
164
151
|
const url = makeGetApiURL(baseURL)(path);
|
|
165
152
|
const response = await enhancedFetch(
|
|
166
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
|
-
|
|
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,52 +76,43 @@ function typeOf(t) {
|
|
|
80
76
|
// src/api.ts
|
|
81
77
|
var identity = (value) => value;
|
|
82
78
|
function typedResponse(response, options) {
|
|
83
|
-
|
|
84
|
-
const
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
83
|
+
if (prop === "json") return getJsonFn(target);
|
|
84
|
+
if (prop === "text") return getTextFn(target);
|
|
85
|
+
const value = Reflect.get(target, prop);
|
|
86
|
+
if (typeof value === "function") {
|
|
87
|
+
return value.bind(target);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
93
90
|
}
|
|
94
91
|
});
|
|
95
92
|
}
|
|
96
93
|
async function enhancedFetch(url, requestInit) {
|
|
97
|
-
|
|
98
|
-
const { query, trace, ...reqInit } = requestInit != null ? requestInit : {};
|
|
99
|
-
const headers = mergeHeaders(
|
|
100
|
-
{
|
|
101
|
-
"content-type": "application/json"
|
|
102
|
-
},
|
|
103
|
-
(_a = reqInit.headers) != null ? _a : {}
|
|
104
|
-
);
|
|
105
|
-
const withParams = replaceURLParams(url, (_b = reqInit.params) != null ? _b : {});
|
|
106
|
-
const fullURL = addQueryToURL(withParams, query);
|
|
94
|
+
const { query, trace, ...reqInit } = requestInit ?? {};
|
|
107
95
|
const body = ensureStringBody(reqInit.body);
|
|
108
|
-
const
|
|
109
|
-
|
|
96
|
+
const withParams = replaceURLParams(url, reqInit.params ?? {});
|
|
97
|
+
const fullURL = addQueryToURL(withParams, query);
|
|
98
|
+
const enhancedReqInit = { ...reqInit, body };
|
|
110
99
|
const response = await fetch(fullURL, enhancedReqInit);
|
|
100
|
+
await trace?.(fullURL, enhancedReqInit, typedResponse(response.clone()));
|
|
111
101
|
return typedResponse(response);
|
|
112
102
|
}
|
|
113
103
|
function makeFetcher(baseURL, baseOptions = {}) {
|
|
114
104
|
return async (path, requestInit = {}) => {
|
|
115
|
-
var _a, _b;
|
|
116
105
|
const { headers } = baseOptions;
|
|
117
|
-
const requestTransformer =
|
|
118
|
-
const responseTransformer =
|
|
119
|
-
const headerTransformer = async (ri) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
headers
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
};
|
|
128
|
-
};
|
|
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
|
+
});
|
|
129
116
|
const url = makeGetApiURL(baseURL)(path);
|
|
130
117
|
const response = await enhancedFetch(
|
|
131
118
|
url,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "make-service",
|
|
3
|
-
"version": "
|
|
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
|
|
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
|
-
"@
|
|
19
|
-
"eslint": "
|
|
20
|
-
"
|
|
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": "^
|
|
23
|
-
"tsup": "^
|
|
24
|
-
"typescript": "^5.
|
|
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
|
},
|