make-service 2.0.0-next.0 → 2.0.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
@@ -11,7 +11,7 @@ It adds a set of little features and allows you to parse responses with [zod](ht
11
11
  - 🐾 Replaces URL wildcards with a **strongly-typed** object of `params`.
12
12
  - 🧙‍♀️ Automatically stringifies the `body` of a request so you can give it a JSON-like structure.
13
13
  - 🐛 Accepts a `trace` function for debugging.
14
- - 🔥 Transforms responses and payloads back and forth to support interchangeability of casing styles (kebab-case -> camelCase -> snake_case -> kebab-case).
14
+ - 🔥 It can transform responses and payloads back and forth to (e.g.) support interchangeability of casing styles (kebab-case -> camelCase -> snake_case -> kebab-case).
15
15
 
16
16
  ## Example
17
17
 
@@ -48,7 +48,7 @@ const users = await response.json(usersSchema);
48
48
  - [makeFetcher](#makefetcher)
49
49
  - [enhancedFetch](#enhancedfetch)
50
50
  - [typedResponse](#typedresponse)
51
- - [Payload transformers](#payload-transformers)
51
+ - [Transform the Payload](#transform-the-payload)
52
52
  - [Other available primitives](#other-available-primitives)
53
53
  - [addQueryToURL](#addquerytourl)
54
54
  - [ensureStringBody](#ensurestringbody)
@@ -437,33 +437,27 @@ const text = await response.text(z.string().email())
437
437
  // ^? string
438
438
  ```
439
439
 
440
- # Payload transformers
441
- The `make-service` library has a few payload transformers that you can use to transform the request body before sending it or the response body after returning from the server.
440
+ # Transform the payload
441
+ The combination of `make-service` and [`string-ts`](https://github.com/gustavoguichard/string-ts) libraries makes it easy to work with APIs that follow a different convention for object key's casing, so you can transform the request body before sending it or the response body after returning from the server.
442
442
  The resulting type will be **properly typed** 🤩.
443
443
  ```ts
444
- import { makeService, kebabToCamel, camelToKebab } from 'make-service'
444
+ import { makeService } from 'make-service'
445
+ import { deepCamelKeys, deepKebabKeys } from 'string-ts'
445
446
 
446
447
  const service = makeService("https://example.com/api")
447
448
  const response = service.get("/users")
448
449
  const users = await response.json(
449
450
  z
450
451
  .array(z.object({ "first-name": z.string(), contact: z.object({ "home-address": z.string() }) }))
451
- .transform(kebabToCamel)
452
+ .transform(deepCamelKeys)
452
453
  )
453
454
  console.log(users)
454
455
  // ^? { firstName: string, contact: { homeAddress: string } }[]
455
456
 
456
- const body = camelToKebab({ firstName: "John", contact: { homeAddress: "123 Main St" } })
457
+ const body = deepKebabKeys({ firstName: "John", contact: { homeAddress: "123 Main St" } })
457
458
  // ^? { "first-name": string, contact: { "home-address": string } }
458
459
  service.patch("/users/:id", { body, params: { id: "1" } })
459
460
  ```
460
- The available transformations are:
461
- - `camelToKebab`: `"someProp" -> "some-prop"`
462
- - `camelToSnake`: `"someProp" -> "some_prop"`
463
- - `kebabToCamel`: `"some-prop" -> "someProp"`
464
- - `kebabToSnake`: `"some-prop" -> "some_prop"`
465
- - `snakeToCamel`: `"some_prop" -> "someProp"`
466
- - `snakeToKebab`: `"some_prop" -> "some-prop"`
467
461
 
468
462
  # Other available primitives
469
463
  This little library has plenty of other useful functions that you can use to build your own services and interactions with external APIs.
package/dist/index.d.ts CHANGED
@@ -135,50 +135,4 @@ declare function mergeHeaders(...entries: (HeadersInit | [string, undefined][] |
135
135
  */
136
136
  declare function replaceURLParams<T extends string | URL>(url: T, params: PathParams<T>): T;
137
137
 
138
- type KebabToCamel<Str> = Str extends `${infer First}-${infer Rest}` ? `${First}${Capitalize<KebabToCamel<Rest>>}` : Str;
139
- type SnakeToCamel<Str> = Str extends `${infer First}_${infer Rest}` ? `${First}${Capitalize<SnakeToCamel<Rest>>}` : Str;
140
- type KebabToSnake<Str> = Str extends `${infer First}-${infer Rest}` ? `${First}_${KebabToSnake<Rest>}` : Str;
141
- type SnakeToKebab<Str> = Str extends `${infer First}_${infer Rest}` ? `${First}-${SnakeToKebab<Rest>}` : Str;
142
- type HandleFirstChar<Str> = Str extends `${infer First}${infer Rest}` ? `${Lowercase<First>}${Rest}` : Str;
143
- type CamelToSnakeFn<Str> = Str extends `${infer First}${infer Rest}` ? `${First extends Capitalize<First> ? '_' : ''}${Lowercase<First>}${CamelToSnakeFn<Rest>}` : Str;
144
- type CamelToSnake<Str> = CamelToSnakeFn<HandleFirstChar<Str>>;
145
- type CamelToKebabFn<Str> = Str extends `${infer First}${infer Rest}` ? `${First extends Capitalize<First> ? '-' : ''}${Lowercase<First>}${CamelToKebabFn<Rest>}` : Str;
146
- type CamelToKebab<Str> = CamelToKebabFn<HandleFirstChar<Str>>;
147
- type DeepKebabToCamel<T> = T extends [any, ...any] ? {
148
- [I in keyof T]: DeepKebabToCamel<T[I]>;
149
- } : T extends (infer V)[] ? DeepKebabToCamel<V>[] : {
150
- [K in keyof T as KebabToCamel<K>]: DeepKebabToCamel<T[K]>;
151
- };
152
- declare function kebabToCamel<T>(obj: T): DeepKebabToCamel<T>;
153
- type DeepSnakeToCamel<T> = T extends [any, ...any] ? {
154
- [I in keyof T]: DeepSnakeToCamel<T[I]>;
155
- } : T extends (infer V)[] ? DeepSnakeToCamel<V>[] : {
156
- [K in keyof T as SnakeToCamel<K>]: DeepSnakeToCamel<T[K]>;
157
- };
158
- declare function snakeToCamel<T>(obj: T): DeepSnakeToCamel<T>;
159
- type DeepCamelToSnake<T> = T extends [any, ...any] ? {
160
- [I in keyof T]: DeepCamelToSnake<T[I]>;
161
- } : T extends (infer V)[] ? DeepCamelToSnake<V>[] : {
162
- [K in keyof T as CamelToSnake<K>]: DeepCamelToSnake<T[K]>;
163
- };
164
- declare function camelToSnake<T>(obj: T): DeepCamelToSnake<T>;
165
- type DeepCamelToKebab<T> = T extends [any, ...any] ? {
166
- [I in keyof T]: DeepCamelToKebab<T[I]>;
167
- } : T extends (infer V)[] ? DeepCamelToKebab<V>[] : {
168
- [K in keyof T as CamelToKebab<K>]: DeepCamelToKebab<T[K]>;
169
- };
170
- declare function camelToKebab<T>(obj: T): DeepCamelToKebab<T>;
171
- type DeepSnakeToKebab<T> = T extends [any, ...any] ? {
172
- [I in keyof T]: DeepSnakeToKebab<T[I]>;
173
- } : T extends (infer V)[] ? DeepSnakeToKebab<V>[] : {
174
- [K in keyof T as SnakeToKebab<K>]: DeepSnakeToKebab<T[K]>;
175
- };
176
- declare function snakeToKebab<T>(obj: T): DeepSnakeToKebab<T>;
177
- type DeepKebabToSnake<T> = T extends [any, ...any] ? {
178
- [I in keyof T]: DeepKebabToSnake<T[I]>;
179
- } : T extends (infer V)[] ? DeepKebabToSnake<V>[] : {
180
- [K in keyof T as KebabToSnake<K>]: DeepKebabToSnake<T[K]>;
181
- };
182
- declare function kebabToSnake<T>(obj: T): DeepKebabToSnake<T>;
183
-
184
- export { BaseOptions, CamelToKebab, CamelToSnake, DeepCamelToKebab, DeepCamelToSnake, DeepKebabToCamel, DeepKebabToSnake, DeepSnakeToCamel, DeepSnakeToKebab, EnhancedRequestInit, GetJson, GetText, HTTPMethod, JSONValue, KebabToCamel, KebabToSnake, PathParams, RequestTransformer, ResponseTransformer, Schema, SearchParams, ServiceRequestInit, SnakeToCamel, SnakeToKebab, TypedResponse, TypedResponseJson, TypedResponseText, addQueryToURL, camelToKebab, camelToSnake, enhancedFetch, ensureStringBody, kebabToCamel, kebabToSnake, makeFetcher, makeGetApiURL, makeService, mergeHeaders, replaceURLParams, snakeToCamel, snakeToKebab, typedResponse };
138
+ export { BaseOptions, EnhancedRequestInit, GetJson, GetText, HTTPMethod, JSONValue, PathParams, RequestTransformer, ResponseTransformer, Schema, SearchParams, ServiceRequestInit, TypedResponse, TypedResponseJson, TypedResponseText, addQueryToURL, enhancedFetch, ensureStringBody, makeFetcher, makeGetApiURL, makeService, mergeHeaders, replaceURLParams, typedResponse };
package/dist/index.js CHANGED
@@ -21,19 +21,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  addQueryToURL: () => addQueryToURL,
24
- camelToKebab: () => camelToKebab,
25
- camelToSnake: () => camelToSnake,
26
24
  enhancedFetch: () => enhancedFetch,
27
25
  ensureStringBody: () => ensureStringBody,
28
- kebabToCamel: () => kebabToCamel,
29
- kebabToSnake: () => kebabToSnake,
30
26
  makeFetcher: () => makeFetcher,
31
27
  makeGetApiURL: () => makeGetApiURL,
32
28
  makeService: () => makeService,
33
29
  mergeHeaders: () => mergeHeaders,
34
30
  replaceURLParams: () => replaceURLParams,
35
- snakeToCamel: () => snakeToCamel,
36
- snakeToKebab: () => snakeToKebab,
37
31
  typedResponse: () => typedResponse
38
32
  });
39
33
  module.exports = __toCommonJS(src_exports);
@@ -181,76 +175,22 @@ function makeService(baseURL, baseOptions) {
181
175
  function appliedService(method) {
182
176
  return async (path, requestInit = {}) => fetcher(path, { ...requestInit, method });
183
177
  }
184
- let service = {};
178
+ const service = {};
185
179
  for (const method of HTTP_METHODS) {
186
180
  const lowerMethod = method.toLowerCase();
187
181
  service[lowerMethod] = appliedService(method);
188
182
  }
189
183
  return service;
190
184
  }
191
-
192
- // src/transforms.ts
193
- function words(str) {
194
- const matches = str.match(
195
- /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g
196
- );
197
- return matches ? Array.from(matches) : [str];
198
- }
199
- function toCamelCase(str) {
200
- const result = words(str).map((x) => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()).join("");
201
- return result.slice(0, 1).toLowerCase() + result.slice(1);
202
- }
203
- function toKebabCase(str) {
204
- return words(str).map((x) => x.toLowerCase()).join("-");
205
- }
206
- function toSnakeCase(str) {
207
- return words(str).map((x) => x.toLowerCase()).join("_");
208
- }
209
- function deepTransformKeys(obj, transform) {
210
- if (!["object", "array"].includes(typeOf(obj)))
211
- return obj;
212
- if (Array.isArray(obj)) {
213
- return obj.map((x) => deepTransformKeys(x, transform));
214
- }
215
- const res = {};
216
- for (const key in obj) {
217
- res[transform(key)] = deepTransformKeys(obj[key], transform);
218
- }
219
- return res;
220
- }
221
- function kebabToCamel(obj) {
222
- return deepTransformKeys(obj, toCamelCase);
223
- }
224
- function snakeToCamel(obj) {
225
- return deepTransformKeys(obj, toCamelCase);
226
- }
227
- function camelToSnake(obj) {
228
- return deepTransformKeys(obj, toSnakeCase);
229
- }
230
- function camelToKebab(obj) {
231
- return deepTransformKeys(obj, toKebabCase);
232
- }
233
- function snakeToKebab(obj) {
234
- return deepTransformKeys(obj, toKebabCase);
235
- }
236
- function kebabToSnake(obj) {
237
- return deepTransformKeys(obj, toSnakeCase);
238
- }
239
185
  // Annotate the CommonJS export names for ESM import in node:
240
186
  0 && (module.exports = {
241
187
  addQueryToURL,
242
- camelToKebab,
243
- camelToSnake,
244
188
  enhancedFetch,
245
189
  ensureStringBody,
246
- kebabToCamel,
247
- kebabToSnake,
248
190
  makeFetcher,
249
191
  makeGetApiURL,
250
192
  makeService,
251
193
  mergeHeaders,
252
194
  replaceURLParams,
253
- snakeToCamel,
254
- snakeToKebab,
255
195
  typedResponse
256
196
  });
package/dist/index.mjs CHANGED
@@ -141,75 +141,21 @@ function makeService(baseURL, baseOptions) {
141
141
  function appliedService(method) {
142
142
  return async (path, requestInit = {}) => fetcher(path, { ...requestInit, method });
143
143
  }
144
- let service = {};
144
+ const service = {};
145
145
  for (const method of HTTP_METHODS) {
146
146
  const lowerMethod = method.toLowerCase();
147
147
  service[lowerMethod] = appliedService(method);
148
148
  }
149
149
  return service;
150
150
  }
151
-
152
- // src/transforms.ts
153
- function words(str) {
154
- const matches = str.match(
155
- /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g
156
- );
157
- return matches ? Array.from(matches) : [str];
158
- }
159
- function toCamelCase(str) {
160
- const result = words(str).map((x) => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()).join("");
161
- return result.slice(0, 1).toLowerCase() + result.slice(1);
162
- }
163
- function toKebabCase(str) {
164
- return words(str).map((x) => x.toLowerCase()).join("-");
165
- }
166
- function toSnakeCase(str) {
167
- return words(str).map((x) => x.toLowerCase()).join("_");
168
- }
169
- function deepTransformKeys(obj, transform) {
170
- if (!["object", "array"].includes(typeOf(obj)))
171
- return obj;
172
- if (Array.isArray(obj)) {
173
- return obj.map((x) => deepTransformKeys(x, transform));
174
- }
175
- const res = {};
176
- for (const key in obj) {
177
- res[transform(key)] = deepTransformKeys(obj[key], transform);
178
- }
179
- return res;
180
- }
181
- function kebabToCamel(obj) {
182
- return deepTransformKeys(obj, toCamelCase);
183
- }
184
- function snakeToCamel(obj) {
185
- return deepTransformKeys(obj, toCamelCase);
186
- }
187
- function camelToSnake(obj) {
188
- return deepTransformKeys(obj, toSnakeCase);
189
- }
190
- function camelToKebab(obj) {
191
- return deepTransformKeys(obj, toKebabCase);
192
- }
193
- function snakeToKebab(obj) {
194
- return deepTransformKeys(obj, toKebabCase);
195
- }
196
- function kebabToSnake(obj) {
197
- return deepTransformKeys(obj, toSnakeCase);
198
- }
199
151
  export {
200
152
  addQueryToURL,
201
- camelToKebab,
202
- camelToSnake,
203
153
  enhancedFetch,
204
154
  ensureStringBody,
205
- kebabToCamel,
206
- kebabToSnake,
207
155
  makeFetcher,
208
156
  makeGetApiURL,
209
157
  makeService,
210
158
  mergeHeaders,
211
159
  replaceURLParams,
212
- snakeToCamel,
213
- snakeToKebab,
214
160
  typedResponse
215
161
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-service",
3
- "version": "2.0.0-next.0",
3
+ "version": "2.0.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",
@@ -15,9 +15,11 @@
15
15
  "test": "vitest run"
16
16
  },
17
17
  "devDependencies": {
18
+ "@typescript-eslint/eslint-plugin": "^6.3.0",
18
19
  "eslint": "latest",
19
20
  "jsdom": "^22.1.0",
20
21
  "prettier": "latest",
22
+ "string-ts": "^0.4.1",
21
23
  "tsup": "^6.7.0",
22
24
  "typescript": "^5.1.3",
23
25
  "vitest": "latest",