core-services-sdk 1.3.18 → 1.3.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-services-sdk",
3
- "version": "1.3.18",
3
+ "version": "1.3.20",
4
4
  "main": "src/index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -28,7 +28,6 @@
28
28
  "google-libphonenumber": "^3.2.42",
29
29
  "http-status": "^2.1.0",
30
30
  "mongodb": "^6.18.0",
31
- "node-fetch": "^3.3.2",
32
31
  "nodemailer": "^7.0.5",
33
32
  "pino": "^9.7.0",
34
33
  "uuid": "^11.1.0",
package/src/http/http.js CHANGED
@@ -1,11 +1,12 @@
1
1
  /**
2
- * A lightweight HTTP client wrapper around `node-fetch` supporting JSON, XML, and plain text responses.
3
- * Provides simplified helper methods for GET, POST, PUT, PATCH, and DELETE with automatic error handling.
2
+ * A lightweight HTTP client wrapper around Node.js native fetch
3
+ * supporting JSON, XML, and plain text responses.
4
+ * Provides simplified helper methods for GET, POST, PUT, PATCH, and DELETE
5
+ * with automatic error handling.
4
6
  *
5
7
  * @module http
6
8
  */
7
9
 
8
- import fetch from 'node-fetch'
9
10
  import httpStatus from 'http-status'
10
11
  import { parseStringPromise } from 'xml2js'
11
12
 
@@ -20,26 +21,27 @@ const JSON_HEADER = {
20
21
  /**
21
22
  * Checks if the HTTP status is considered successful (2xx).
22
23
  *
23
- * @param {import('node-fetch').Response} response
24
+ * @param {Response} response
24
25
  * @returns {boolean}
25
26
  */
26
27
  const isOkStatus = ({ status }) =>
27
28
  status >= httpStatus.OK && status < httpStatus.MULTIPLE_CHOICES
28
29
 
29
30
  /**
30
- * @param {import('node-fetch').Response} response
31
- * @returns {Promise<import('node-fetch').Response>}
31
+ * Ensures the response has a successful status, otherwise throws HttpError.
32
+ *
33
+ * @param {Response} response
34
+ * @returns {Promise<Response>}
32
35
  */
33
36
  const checkStatus = async (response) => {
34
37
  if (!isOkStatus(response)) {
35
38
  const text = await response.text()
36
- const info = tryConvertJsonResponse(text)
37
39
  const { status, statusText } = response
38
-
39
40
  throw new HttpError({
40
41
  code: status,
41
42
  httpStatusCode: status,
42
43
  httpStatusText: statusText,
44
+ extendInfo: { text },
43
45
  })
44
46
  }
45
47
  return response
@@ -48,19 +50,17 @@ const checkStatus = async (response) => {
48
50
  /**
49
51
  * Reads the raw text from a fetch response.
50
52
  *
51
- * @param {import('node-fetch').Response} response
52
- * @returns {Promise<string>} The plain text body.
53
+ * @param {Response} response
54
+ * @returns {Promise<string>}
53
55
  */
54
- const getTextResponse = async (response) => {
55
- return await response.text()
56
- }
56
+ const getTextResponse = async (response) => response.text()
57
57
 
58
58
  /**
59
59
  * Attempts to parse a JSON string.
60
60
  *
61
- * @param {string} responseText - A raw JSON string.
62
- * @returns {Object} Parsed JSON object.
63
- * @throws {SyntaxError} If the string is not valid JSON.
61
+ * @param {string} responseText
62
+ * @returns {Object}
63
+ * @throws {SyntaxError} If not valid JSON.
64
64
  */
65
65
  const tryConvertJsonResponse = (responseText) => {
66
66
  try {
@@ -72,10 +72,10 @@ const tryConvertJsonResponse = (responseText) => {
72
72
  }
73
73
 
74
74
  /**
75
- * Attempts to extract a JSON object from a fetch response.
75
+ * Extracts JSON from a response, or returns raw text on failure.
76
76
  *
77
- * @param {import('node-fetch').Response} response
78
- * @returns {Promise<Object|string>} Parsed JSON or raw string if parsing fails.
77
+ * @param {Response} response
78
+ * @returns {Promise<Object|string>}
79
79
  */
80
80
  const tryGetJsonResponse = async (response) => {
81
81
  let jsonText
@@ -89,10 +89,10 @@ const tryGetJsonResponse = async (response) => {
89
89
  }
90
90
 
91
91
  /**
92
- * Attempts to extract an XML object from a fetch response.
92
+ * Extracts XML from a response, or returns raw text on failure.
93
93
  *
94
- * @param {import('node-fetch').Response} response
95
- * @returns {Promise<Object|string>} Parsed XML object or raw string if parsing fails.
94
+ * @param {Response} response
95
+ * @returns {Promise<Object|string>}
96
96
  */
97
97
  const tryGetXmlResponse = async (response) => {
98
98
  let xmlText
@@ -106,11 +106,11 @@ const tryGetXmlResponse = async (response) => {
106
106
  }
107
107
 
108
108
  /**
109
- * Extracts and parses the fetch response body based on expected type.
109
+ * Parses the fetch response based on expected type.
110
110
  *
111
- * @param {import('node-fetch').Response} response
112
- * @param {string} responseType - Expected type: 'json', 'xml', or 'text'.
113
- * @returns {Promise<any>} The parsed response payload.
111
+ * @param {Response} response
112
+ * @param {string} responseType
113
+ * @returns {Promise<any>}
114
114
  */
115
115
  const getResponsePayload = async (response, responseType) => {
116
116
  switch (responseType) {
@@ -119,149 +119,100 @@ const getResponsePayload = async (response, responseType) => {
119
119
  case ResponseType.xml:
120
120
  return tryGetXmlResponse(response)
121
121
  default:
122
- case ResponseType.text:
123
122
  return getTextResponse(response)
124
123
  }
125
124
  }
126
125
 
127
126
  /**
128
127
  * Sends an HTTP GET request.
129
- *
130
- * @param {Object} params
131
- * @param {string} params.url - Target URL.
132
- * @param {Object} [params.headers] - Optional request headers.
133
- * @param {string} [params.credentials='include'] - Credential policy.
134
- * @param {string} [params.expectedType='json'] - Expected response format.
135
- * @returns {Promise<any>} Parsed response data.
136
128
  */
137
129
  export const get = async ({
138
130
  url,
139
131
  headers = {},
140
- credentials = 'include',
141
132
  expectedType = ResponseType.json,
142
133
  }) => {
143
- /** @type {import('node-fetch').Response} */
144
134
  const response = await fetch(url, {
145
135
  method: HTTP_METHODS.GET,
146
136
  headers: { ...JSON_HEADER, ...headers },
147
- ...(credentials ? { credentials } : {}),
148
137
  })
149
-
150
138
  await checkStatus(response)
151
- return await getResponsePayload(response, expectedType)
139
+ return getResponsePayload(response, expectedType)
152
140
  }
153
141
 
154
142
  /**
155
143
  * Sends an HTTP POST request.
156
- *
157
- * @param {Object} params
158
- * @param {string} params.url - Target URL.
159
- * @param {Object} params.body - Request body (will be JSON.stringify-ed).
160
- * @param {Object} [params.headers] - Optional request headers.
161
- * @param {string} [params.credentials='include'] - Credential policy.
162
- * @param {string} [params.expectedType='json'] - Expected response format.
163
- * @returns {Promise<any>} Parsed response data.
164
144
  */
165
145
  export const post = async ({
166
146
  url,
167
147
  body,
168
- headers,
169
- credentials = 'include',
148
+ headers = {},
170
149
  expectedType = ResponseType.json,
171
150
  }) => {
172
151
  const response = await fetch(url, {
173
152
  method: HTTP_METHODS.POST,
174
153
  headers: { ...JSON_HEADER, ...headers },
175
154
  body: JSON.stringify(body),
176
- ...(credentials ? { credentials } : {}),
177
155
  })
178
156
  await checkStatus(response)
179
- return await getResponsePayload(response, expectedType)
157
+ return getResponsePayload(response, expectedType)
180
158
  }
181
159
 
182
160
  /**
183
161
  * Sends an HTTP PUT request.
184
- *
185
- * @param {Object} params
186
- * @param {string} params.url
187
- * @param {Object} params.body
188
- * @param {Object} [params.headers]
189
- * @param {string} [params.credentials='include']
190
- * @param {string} [params.expectedType='json']
191
- * @returns {Promise<any>} Parsed response data.
192
162
  */
193
163
  export const put = async ({
194
164
  url,
195
165
  body,
196
166
  headers = {},
197
- credentials = 'include',
198
167
  expectedType = ResponseType.json,
199
168
  }) => {
200
169
  const response = await fetch(url, {
201
170
  method: HTTP_METHODS.PUT,
202
171
  headers: { ...JSON_HEADER, ...headers },
203
172
  body: JSON.stringify(body),
204
- ...(credentials ? { credentials } : {}),
205
173
  })
206
174
  await checkStatus(response)
207
- return await getResponsePayload(response, expectedType)
175
+ return getResponsePayload(response, expectedType)
208
176
  }
209
177
 
210
178
  /**
211
179
  * Sends an HTTP PATCH request.
212
- *
213
- * @param {Object} params - Same as `post`.
214
- * @returns {Promise<any>} Parsed response data.
215
180
  */
216
181
  export const patch = async ({
217
182
  url,
218
183
  body,
219
- headers,
220
- credentials = 'include',
184
+ headers = {},
221
185
  expectedType = ResponseType.json,
222
186
  }) => {
223
187
  const response = await fetch(url, {
224
188
  method: HTTP_METHODS.PATCH,
225
189
  headers: { ...JSON_HEADER, ...headers },
226
190
  body: JSON.stringify(body),
227
- ...(credentials ? { credentials } : {}),
228
191
  })
229
192
  await checkStatus(response)
230
- return await getResponsePayload(response, expectedType)
193
+ return getResponsePayload(response, expectedType)
231
194
  }
232
195
 
233
196
  /**
234
197
  * Sends an HTTP DELETE request.
235
- *
236
- * @param {Object} params - Same as `post`, but body is optional.
237
- * @returns {Promise<any>} Parsed response data.
238
198
  */
239
199
  export const deleteApi = async ({
240
200
  url,
241
201
  body,
242
- headers,
243
- credentials = 'include',
202
+ headers = {},
244
203
  expectedType = ResponseType.json,
245
204
  }) => {
246
205
  const response = await fetch(url, {
247
206
  method: HTTP_METHODS.DELETE,
248
207
  headers: { ...JSON_HEADER, ...headers },
249
208
  ...(body ? { body: JSON.stringify(body) } : {}),
250
- ...(credentials ? { credentials } : {}),
251
209
  })
252
210
  await checkStatus(response)
253
- return await getResponsePayload(response, expectedType)
211
+ return getResponsePayload(response, expectedType)
254
212
  }
255
213
 
256
214
  /**
257
- * Consolidated HTTP client with method shortcuts.
258
- *
259
- * @namespace http
260
- * @property {Function} get - HTTP GET method.
261
- * @property {Function} post - HTTP POST method.
262
- * @property {Function} put - HTTP PUT method.
263
- * @property {Function} patch - HTTP PATCH method.
264
- * @property {Function} deleteApi - HTTP DELETE method.
215
+ * Consolidated HTTP client.
265
216
  */
266
217
  export const http = {
267
218
  get,
@@ -1,20 +1,13 @@
1
1
  import { vi, it, expect, describe, afterEach } from 'vitest'
2
2
 
3
- import fetch from 'node-fetch'
4
3
  import { http } from '../../src/http/http.js'
5
4
  import { HttpError } from '../../src/http/HttpError.js'
6
5
  import { HTTP_METHODS } from '../../src/http/http-method.js'
7
6
  import { ResponseType } from '../../src/http/responseType.js'
8
7
 
9
- vi.mock('node-fetch', async () => {
10
- const actual = await vi.importActual('node-fetch')
11
- return {
12
- ...actual,
13
- default: vi.fn(),
14
- }
15
- })
16
-
17
- const mockFetch = /** @type {typeof fetch} */ (fetch)
8
+ // Mock the global fetch
9
+ const mockFetch = vi.fn()
10
+ global.fetch = mockFetch
18
11
 
19
12
  const createMockResponse = ({
20
13
  body = '',
@@ -23,22 +16,22 @@ const createMockResponse = ({
23
16
  headers = {},
24
17
  } = {}) => {
25
18
  return {
26
- ok: status >= 200 && status < 300,
27
19
  status,
28
20
  statusText,
29
21
  text: vi.fn().mockResolvedValue(body),
30
- headers: { get: vi.fn().mockImplementation((k) => headers[k]) },
22
+ headers: {
23
+ get: vi.fn().mockImplementation((k) => headers[k]),
24
+ },
31
25
  }
32
26
  }
33
27
 
34
- describe('http client', () => {
28
+ describe('http client (native fetch)', () => {
35
29
  afterEach(() => {
36
30
  vi.clearAllMocks()
37
31
  })
38
32
 
39
33
  describe('GET', () => {
40
34
  it('should return parsed JSON response', async () => {
41
- // @ts-ignore
42
35
  mockFetch.mockResolvedValueOnce(
43
36
  createMockResponse({ body: JSON.stringify({ hello: 'world' }) }),
44
37
  )
@@ -52,7 +45,6 @@ describe('http client', () => {
52
45
  })
53
46
 
54
47
  it('should throw HttpError on non-2xx status', async () => {
55
- // @ts-ignore
56
48
  mockFetch.mockResolvedValueOnce(
57
49
  createMockResponse({
58
50
  status: 404,
@@ -69,7 +61,6 @@ describe('http client', () => {
69
61
 
70
62
  describe('POST', () => {
71
63
  it('should send a JSON body and return parsed JSON', async () => {
72
- // @ts-ignore
73
64
  mockFetch.mockResolvedValueOnce(
74
65
  createMockResponse({ body: JSON.stringify({ ok: true }) }),
75
66
  )
@@ -92,7 +83,6 @@ describe('http client', () => {
92
83
 
93
84
  describe('PUT', () => {
94
85
  it('should send a PUT request and return JSON', async () => {
95
- // @ts-ignore
96
86
  mockFetch.mockResolvedValueOnce(
97
87
  createMockResponse({ body: JSON.stringify({ updated: true }) }),
98
88
  )
@@ -108,7 +98,6 @@ describe('http client', () => {
108
98
 
109
99
  describe('PATCH', () => {
110
100
  it('should send a PATCH request and return JSON', async () => {
111
- // @ts-ignore
112
101
  mockFetch.mockResolvedValueOnce(
113
102
  createMockResponse({ body: JSON.stringify({ patched: true }) }),
114
103
  )
@@ -124,7 +113,6 @@ describe('http client', () => {
124
113
 
125
114
  describe('DELETE', () => {
126
115
  it('should send a DELETE request with body and return JSON', async () => {
127
- // @ts-ignore
128
116
  mockFetch.mockResolvedValueOnce(
129
117
  createMockResponse({ body: JSON.stringify({ deleted: true }) }),
130
118
  )
@@ -140,7 +128,6 @@ describe('http client', () => {
140
128
 
141
129
  describe('ResponseType', () => {
142
130
  it('should return text if expectedType is text', async () => {
143
- // @ts-ignore
144
131
  mockFetch.mockResolvedValueOnce(createMockResponse({ body: 'hello' }))
145
132
 
146
133
  const result = await http.get({
@@ -153,7 +140,6 @@ describe('http client', () => {
153
140
 
154
141
  it('should return XML as parsed object if expectedType is xml', async () => {
155
142
  const xml = `<note><to>User</to><from>ChatGPT</from></note>`
156
- // @ts-ignore
157
143
  mockFetch.mockResolvedValueOnce(createMockResponse({ body: xml }))
158
144
 
159
145
  const result = await http.get({