@unito/integration-sdk 2.4.3 → 3.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.
@@ -1305,7 +1305,13 @@ class Provider {
1305
1305
  if (body.error) {
1306
1306
  reject(this.handleError(400, body.error.message, options));
1307
1307
  }
1308
- resolve({ status: 201, headers: response.headers, body });
1308
+ resolve({
1309
+ status: 201,
1310
+ headers: Object.fromEntries(Object.entries(response.headers)
1311
+ .filter((entry) => entry[1] !== undefined)
1312
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value])),
1313
+ body,
1314
+ });
1309
1315
  }
1310
1316
  catch (error) {
1311
1317
  reject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
@@ -1404,7 +1410,9 @@ class Provider {
1404
1410
  const body = responseBody ? JSON.parse(responseBody) : undefined;
1405
1411
  safeResolve({
1406
1412
  status: response.statusCode || 200,
1407
- headers: response.headers,
1413
+ headers: Object.fromEntries(Object.entries(response.headers)
1414
+ .filter((entry) => entry[1] !== undefined)
1415
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value])),
1408
1416
  body,
1409
1417
  });
1410
1418
  }
@@ -1624,7 +1632,11 @@ class Provider {
1624
1632
  }
1625
1633
  else if (response.status === 204 || response.body === null) {
1626
1634
  // No content: return without inspecting the body
1627
- return { status: response.status, headers: response.headers, body: undefined };
1635
+ return {
1636
+ status: response.status,
1637
+ headers: Object.fromEntries(response.headers.entries()),
1638
+ body: undefined,
1639
+ };
1628
1640
  }
1629
1641
  const responseContentType = response.headers.get('content-type');
1630
1642
  let body;
@@ -1655,7 +1667,7 @@ class Provider {
1655
1667
  else {
1656
1668
  throw this.handleError(500, 'Unsupported Content-Type', options);
1657
1669
  }
1658
- return { status: response.status, headers: response.headers, body };
1670
+ return { status: response.status, headers: Object.fromEntries(response.headers.entries()), body };
1659
1671
  };
1660
1672
  return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
1661
1673
  }
@@ -1,6 +1,5 @@
1
1
  import FormData from 'form-data';
2
2
  import * as stream from 'stream';
3
- import { IncomingHttpHeaders } from 'http';
4
3
  import * as HttpErrors from '../httpErrors.js';
5
4
  import { Credentials } from '../middlewares/credentials.js';
6
5
  import Logger from '../resources/logger.js';
@@ -55,7 +54,7 @@ export type RequestOptions = {
55
54
  export type Response<T> = {
56
55
  body: T;
57
56
  status: number;
58
- headers: Headers | IncomingHttpHeaders;
57
+ headers: Record<string, string>;
59
58
  };
60
59
  export type PreparedRequest = {
61
60
  url: string;
@@ -140,7 +140,13 @@ export class Provider {
140
140
  if (body.error) {
141
141
  reject(this.handleError(400, body.error.message, options));
142
142
  }
143
- resolve({ status: 201, headers: response.headers, body });
143
+ resolve({
144
+ status: 201,
145
+ headers: Object.fromEntries(Object.entries(response.headers)
146
+ .filter((entry) => entry[1] !== undefined)
147
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value])),
148
+ body,
149
+ });
144
150
  }
145
151
  catch (error) {
146
152
  reject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
@@ -239,7 +245,9 @@ export class Provider {
239
245
  const body = responseBody ? JSON.parse(responseBody) : undefined;
240
246
  safeResolve({
241
247
  status: response.statusCode || 200,
242
- headers: response.headers,
248
+ headers: Object.fromEntries(Object.entries(response.headers)
249
+ .filter((entry) => entry[1] !== undefined)
250
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value])),
243
251
  body,
244
252
  });
245
253
  }
@@ -459,7 +467,11 @@ export class Provider {
459
467
  }
460
468
  else if (response.status === 204 || response.body === null) {
461
469
  // No content: return without inspecting the body
462
- return { status: response.status, headers: response.headers, body: undefined };
470
+ return {
471
+ status: response.status,
472
+ headers: Object.fromEntries(response.headers.entries()),
473
+ body: undefined,
474
+ };
463
475
  }
464
476
  const responseContentType = response.headers.get('content-type');
465
477
  let body;
@@ -490,7 +502,7 @@ export class Provider {
490
502
  else {
491
503
  throw this.handleError(500, 'Unsupported Content-Type', options);
492
504
  }
493
- return { status: response.status, headers: response.headers, body };
505
+ return { status: response.status, headers: Object.fromEntries(response.headers.entries()), body };
494
506
  };
495
507
  return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
496
508
  }
@@ -42,7 +42,7 @@ describe('Provider', () => {
42
42
  it('get', async (context) => {
43
43
  const response = new Response('{"data": "value"}', {
44
44
  status: 200,
45
- headers: { 'Content-Type': 'application/json' },
45
+ headers: new Headers({ 'Content-Type': 'application/json' }),
46
46
  });
47
47
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
48
48
  const actualResponse = await provider.get('/endpoint', {
@@ -66,12 +66,16 @@ describe('Provider', () => {
66
66
  },
67
67
  },
68
68
  ]);
69
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
69
+ assert.deepEqual(actualResponse, {
70
+ status: 200,
71
+ headers: Object.fromEntries(response.headers.entries()),
72
+ body: { data: 'value' },
73
+ });
70
74
  });
71
75
  it('accepts text/html type response', async (context) => {
72
76
  const response = new Response('', {
73
77
  status: 200,
74
- headers: { 'Content-Type': 'text/html; charset=UTF-8' },
78
+ headers: new Headers({ 'Content-Type': 'text/html; charset=UTF-8' }),
75
79
  });
76
80
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
77
81
  const actualResponse = await provider.get('/endpoint', {
@@ -95,12 +99,16 @@ describe('Provider', () => {
95
99
  },
96
100
  },
97
101
  ]);
98
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: '' });
102
+ assert.deepEqual(actualResponse, {
103
+ status: 200,
104
+ headers: Object.fromEntries(response.headers.entries()),
105
+ body: '',
106
+ });
99
107
  });
100
108
  it('accepts application/schema+json type response', async (context) => {
101
109
  const response = new Response('{"data": "value"}', {
102
110
  status: 200,
103
- headers: { 'Content-Type': 'application/schema+json; charset=UTF-8' },
111
+ headers: new Headers({ 'Content-Type': 'application/schema+json; charset=UTF-8' }),
104
112
  });
105
113
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
106
114
  const actualResponse = await provider.get('/endpoint', {
@@ -124,12 +132,16 @@ describe('Provider', () => {
124
132
  },
125
133
  },
126
134
  ]);
127
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
135
+ assert.deepEqual(actualResponse, {
136
+ status: 200,
137
+ headers: Object.fromEntries(response.headers.entries()),
138
+ body: { data: 'value' },
139
+ });
128
140
  });
129
141
  it('accepts application/swagger+json type response', async (context) => {
130
142
  const response = new Response('{"data": "value"}', {
131
143
  status: 200,
132
- headers: { 'Content-Type': 'application/swagger+json; charset=UTF-8' },
144
+ headers: new Headers({ 'Content-Type': 'application/swagger+json; charset=UTF-8' }),
133
145
  });
134
146
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
135
147
  const actualResponse = await provider.get('/endpoint', {
@@ -153,12 +165,16 @@ describe('Provider', () => {
153
165
  },
154
166
  },
155
167
  ]);
156
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
168
+ assert.deepEqual(actualResponse, {
169
+ status: 200,
170
+ headers: Object.fromEntries(response.headers.entries()),
171
+ body: { data: 'value' },
172
+ });
157
173
  });
158
174
  it('accepts application/vnd.oracle.resource+json type response', async (context) => {
159
175
  const response = new Response('{"data": "value"}', {
160
176
  status: 200,
161
- headers: { 'Content-Type': 'application/vnd.oracle.resource+json; type=collection; charset=UTF-8' },
177
+ headers: new Headers({ 'Content-Type': 'application/vnd.oracle.resource+json; type=collection; charset=UTF-8' }),
162
178
  });
163
179
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
164
180
  const actualResponse = await provider.get('/endpoint', {
@@ -182,12 +198,16 @@ describe('Provider', () => {
182
198
  },
183
199
  },
184
200
  ]);
185
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
201
+ assert.deepEqual(actualResponse, {
202
+ status: 200,
203
+ headers: Object.fromEntries(response.headers.entries()),
204
+ body: { data: 'value' },
205
+ });
186
206
  });
187
207
  it('returns the raw response body if specified', async (context) => {
188
208
  const response = new Response(`IMAGINE A HUGE PAYLOAD`, {
189
209
  status: 200,
190
- headers: { 'Content-Type': 'image/png' },
210
+ headers: new Headers({ 'Content-Type': 'image/png' }),
191
211
  });
192
212
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
193
213
  const providerResponse = await provider.streamingGet('/endpoint/123', {
@@ -206,7 +226,7 @@ describe('Provider', () => {
206
226
  it('gets an endpoint which is an absolute url', async (context) => {
207
227
  const response = new Response('{"data": "value"}', {
208
228
  status: 200,
209
- headers: { 'Content-Type': 'application/json' },
229
+ headers: new Headers({ 'Content-Type': 'application/json' }),
210
230
  });
211
231
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
212
232
  const actualResponse = await provider.get('https://my-cdn.my-domain.com/file.png', {
@@ -228,12 +248,16 @@ describe('Provider', () => {
228
248
  },
229
249
  },
230
250
  ]);
231
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
251
+ assert.deepEqual(actualResponse, {
252
+ status: 200,
253
+ headers: Object.fromEntries(response.headers.entries()),
254
+ body: { data: 'value' },
255
+ });
232
256
  });
233
257
  it('gets on provider url', async (context) => {
234
258
  const response = new Response('{"data": "value"}', {
235
259
  status: 200,
236
- headers: { 'Content-Type': 'application/json' },
260
+ headers: new Headers({ 'Content-Type': 'application/json' }),
237
261
  });
238
262
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
239
263
  const actualResponse = await provider.get('', {
@@ -255,12 +279,16 @@ describe('Provider', () => {
255
279
  },
256
280
  },
257
281
  ]);
258
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
282
+ assert.deepEqual(actualResponse, {
283
+ status: 200,
284
+ headers: Object.fromEntries(response.headers.entries()),
285
+ body: { data: 'value' },
286
+ });
259
287
  });
260
288
  it('post with url encoded body', async (context) => {
261
289
  const response = new Response('{"data": "value"}', {
262
290
  status: 201,
263
- headers: { 'Content-Type': 'application/json' },
291
+ headers: new Headers({ 'Content-Type': 'application/json' }),
264
292
  });
265
293
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
266
294
  const actualResponse = await provider.post('/endpoint', {
@@ -287,12 +315,16 @@ describe('Provider', () => {
287
315
  },
288
316
  },
289
317
  ]);
290
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
318
+ assert.deepEqual(actualResponse, {
319
+ status: 201,
320
+ headers: Object.fromEntries(response.headers.entries()),
321
+ body: { data: 'value' },
322
+ });
291
323
  });
292
324
  it('accepts an array as body for post request', async (context) => {
293
325
  const response = new Response('{"data": "value"}', {
294
326
  status: 201,
295
- headers: { 'Content-Type': 'application/json' },
327
+ headers: new Headers({ 'Content-Type': 'application/json' }),
296
328
  });
297
329
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
298
330
  const actualResponse = await provider.post('/endpoint', [
@@ -320,12 +352,16 @@ describe('Provider', () => {
320
352
  },
321
353
  },
322
354
  ]);
323
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
355
+ assert.deepEqual(actualResponse, {
356
+ status: 201,
357
+ headers: Object.fromEntries(response.headers.entries()),
358
+ body: { data: 'value' },
359
+ });
324
360
  });
325
361
  it('put with json body', async (context) => {
326
362
  const response = new Response('{"data": "value"}', {
327
363
  status: 201,
328
- headers: { 'Content-Type': 'application/json' },
364
+ headers: new Headers({ 'Content-Type': 'application/json' }),
329
365
  });
330
366
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
331
367
  // Removing leading '/' on endpoint to make sure we support both cases
@@ -353,12 +389,16 @@ describe('Provider', () => {
353
389
  },
354
390
  },
355
391
  ]);
356
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
392
+ assert.deepEqual(actualResponse, {
393
+ status: 201,
394
+ headers: Object.fromEntries(response.headers.entries()),
395
+ body: { data: 'value' },
396
+ });
357
397
  });
358
398
  it('putBuffer with Buffer body', async (context) => {
359
399
  const response = new Response('{"data": "value"}', {
360
400
  status: 201,
361
- headers: { 'Content-Type': 'application/json' },
401
+ headers: new Headers({ 'Content-Type': 'application/json' }),
362
402
  });
363
403
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
364
404
  const buffer = Buffer.from('binary data content');
@@ -385,12 +425,16 @@ describe('Provider', () => {
385
425
  },
386
426
  },
387
427
  ]);
388
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
428
+ assert.deepEqual(actualResponse, {
429
+ status: 201,
430
+ headers: Object.fromEntries(response.headers.entries()),
431
+ body: { data: 'value' },
432
+ });
389
433
  });
390
434
  it('patch with query params', async (context) => {
391
435
  const response = new Response('{"data": "value"}', {
392
436
  status: 201,
393
- headers: { 'Content-Type': 'application/json' },
437
+ headers: new Headers({ 'Content-Type': 'application/json' }),
394
438
  });
395
439
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
396
440
  const actualResponse = await provider.patch('/endpoint/123', {
@@ -418,12 +462,16 @@ describe('Provider', () => {
418
462
  },
419
463
  },
420
464
  ]);
421
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
465
+ assert.deepEqual(actualResponse, {
466
+ status: 201,
467
+ headers: Object.fromEntries(response.headers.entries()),
468
+ body: { data: 'value' },
469
+ });
422
470
  });
423
471
  it('delete', async (context) => {
424
472
  const response = new Response(undefined, {
425
473
  status: 204,
426
- headers: { 'Content-Type': 'application/json' },
474
+ headers: new Headers({ 'Content-Type': 'application/json' }),
427
475
  });
428
476
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
429
477
  const actualResponse = await provider.delete('/endpoint/123', {
@@ -447,12 +495,16 @@ describe('Provider', () => {
447
495
  },
448
496
  },
449
497
  ]);
450
- assert.deepEqual(actualResponse, { status: 204, headers: response.headers, body: undefined });
498
+ assert.deepEqual(actualResponse, {
499
+ status: 204,
500
+ headers: Object.fromEntries(response.headers.entries()),
501
+ body: undefined,
502
+ });
451
503
  });
452
504
  it('deleteWithBody', async (context) => {
453
505
  const response = new Response('{"success": true}', {
454
506
  status: 200,
455
- headers: { 'Content-Type': 'application/json' },
507
+ headers: new Headers({ 'Content-Type': 'application/json' }),
456
508
  });
457
509
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
458
510
  const requestBody = { webhookIds: [1, 2, 3] };
@@ -478,7 +530,11 @@ describe('Provider', () => {
478
530
  },
479
531
  },
480
532
  ]);
481
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { success: true } });
533
+ assert.deepEqual(actualResponse, {
534
+ status: 200,
535
+ headers: Object.fromEntries(response.headers.entries()),
536
+ body: { success: true },
537
+ });
482
538
  });
483
539
  it('uses rate limiter if provided', async (context) => {
484
540
  const mockRateLimiter = context.mock.fn((_context, request) => Promise.resolve(request()));
@@ -496,7 +552,7 @@ describe('Provider', () => {
496
552
  });
497
553
  const response = new Response(undefined, {
498
554
  status: 204,
499
- headers: { 'Content-Type': 'application/json' },
555
+ headers: new Headers({ 'Content-Type': 'application/json' }),
500
556
  });
501
557
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
502
558
  const options = {
@@ -523,7 +579,11 @@ describe('Provider', () => {
523
579
  },
524
580
  },
525
581
  ]);
526
- assert.deepEqual(actualResponse, { status: 204, headers: response.headers, body: undefined });
582
+ assert.deepEqual(actualResponse, {
583
+ status: 204,
584
+ headers: Object.fromEntries(response.headers.entries()),
585
+ body: undefined,
586
+ });
527
587
  });
528
588
  it('uses custom error handler if provided', async (context) => {
529
589
  const rateLimitedProvider = new Provider({
@@ -542,7 +602,7 @@ describe('Provider', () => {
542
602
  });
543
603
  const response = new Response(undefined, {
544
604
  status: 400,
545
- headers: { 'Content-Type': 'application/json' },
605
+ headers: new Headers({ 'Content-Type': 'application/json' }),
546
606
  });
547
607
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
548
608
  const options = {
@@ -583,7 +643,7 @@ describe('Provider', () => {
583
643
  });
584
644
  const response = new Response(undefined, {
585
645
  status: 400,
586
- headers: { 'Content-Type': 'application/json' },
646
+ headers: new Headers({ 'Content-Type': 'application/json' }),
587
647
  });
588
648
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
589
649
  const options = {
@@ -619,7 +679,7 @@ describe('Provider', () => {
619
679
  });
620
680
  const response = new Response(undefined, {
621
681
  status: 404,
622
- headers: { 'Content-Type': 'application/json' },
682
+ headers: new Headers({ 'Content-Type': 'application/json' }),
623
683
  });
624
684
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
625
685
  const options = {
@@ -641,7 +701,7 @@ describe('Provider', () => {
641
701
  it('returns valid json response', async (context) => {
642
702
  const response = new Response(`{ "validJson": true }`, {
643
703
  status: 200,
644
- headers: { 'Content-Type': 'application/json;charset=utf-8' },
704
+ headers: new Headers({ 'Content-Type': 'application/json;charset=utf-8' }),
645
705
  });
646
706
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
647
707
  const providerResponse = await provider.get('/endpoint/123', {
@@ -669,7 +729,7 @@ describe('Provider', () => {
669
729
  it('returns streamable response on streaming get calls', async (context) => {
670
730
  const response = new Response(`IMAGINE A HUGE PAYLOAD`, {
671
731
  status: 200,
672
- headers: { 'Content-Type': 'video/mp4' },
732
+ headers: new Headers({ 'Content-Type': 'video/mp4' }),
673
733
  });
674
734
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
675
735
  const providerResponse = await provider.streamingGet('/endpoint/123', {
@@ -683,7 +743,7 @@ describe('Provider', () => {
683
743
  it('returns successfully on unexpected content-type response with no body', async (context) => {
684
744
  const response = new Response(null, {
685
745
  status: 201,
686
- headers: { 'Content-Type': 'html/text' },
746
+ headers: new Headers({ 'Content-Type': 'html/text' }),
687
747
  });
688
748
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
689
749
  const providerResponse = await provider.post('/endpoint/123', {}, {
@@ -693,13 +753,13 @@ describe('Provider', () => {
693
753
  });
694
754
  assert.ok(providerResponse);
695
755
  assert.strictEqual(providerResponse.status, response.status);
696
- assert.strictEqual(providerResponse.headers, response.headers);
756
+ assert.deepEqual(providerResponse.headers, Object.fromEntries(response.headers.entries()));
697
757
  assert.strictEqual(providerResponse.body, undefined);
698
758
  });
699
759
  it('throws on invalid json response', async (context) => {
700
760
  const response = new Response('{invalidJSON}', {
701
761
  status: 200,
702
- headers: { 'Content-Type': 'application/json' },
762
+ headers: new Headers({ 'Content-Type': 'application/json' }),
703
763
  });
704
764
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
705
765
  let error;
@@ -719,7 +779,7 @@ describe('Provider', () => {
719
779
  it('throws on unexpected content-type response', async (context) => {
720
780
  const response = new Response('text', {
721
781
  status: 200,
722
- headers: { 'Content-Type': 'application/text' },
782
+ headers: new Headers({ 'Content-Type': 'application/text' }),
723
783
  });
724
784
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
725
785
  let error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unito/integration-sdk",
3
- "version": "2.4.3",
3
+ "version": "3.0.0",
4
4
  "description": "Integration SDK",
5
5
  "type": "module",
6
6
  "types": "dist/src/index.d.ts",
@@ -1,7 +1,6 @@
1
1
  import https from 'https';
2
2
  import FormData from 'form-data';
3
3
  import * as stream from 'stream';
4
- import { IncomingHttpHeaders } from 'http';
5
4
 
6
5
  import { buildHttpError } from '../errors.js';
7
6
  import * as HttpErrors from '../httpErrors.js';
@@ -57,7 +56,7 @@ export type RequestOptions = {
57
56
  export type Response<T> = {
58
57
  body: T;
59
58
  status: number;
60
- headers: Headers | IncomingHttpHeaders;
59
+ headers: Record<string, string>;
61
60
  };
62
61
 
63
62
  export type PreparedRequest = {
@@ -231,7 +230,15 @@ export class Provider {
231
230
  if (body.error) {
232
231
  reject(this.handleError(400, body.error.message, options));
233
232
  }
234
- resolve({ status: 201, headers: response.headers, body });
233
+ resolve({
234
+ status: 201,
235
+ headers: Object.fromEntries(
236
+ Object.entries(response.headers)
237
+ .filter((entry): entry is [string, string | string[]] => entry[1] !== undefined)
238
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value]),
239
+ ),
240
+ body,
241
+ });
235
242
  } catch (error) {
236
243
  reject(this.handleError(500, `Failed to parse response body: "${error}"`, options));
237
244
  }
@@ -346,7 +353,11 @@ export class Provider {
346
353
  const body = responseBody ? JSON.parse(responseBody) : undefined;
347
354
  safeResolve({
348
355
  status: response.statusCode || 200,
349
- headers: response.headers,
356
+ headers: Object.fromEntries(
357
+ Object.entries(response.headers)
358
+ .filter((entry): entry is [string, string | string[]] => entry[1] !== undefined)
359
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value]),
360
+ ),
350
361
  body,
351
362
  });
352
363
  } catch (error) {
@@ -607,7 +618,11 @@ export class Provider {
607
618
  throw this.handleError(response.status, textResult, options);
608
619
  } else if (response.status === 204 || response.body === null) {
609
620
  // No content: return without inspecting the body
610
- return { status: response.status, headers: response.headers, body: undefined as unknown as T };
621
+ return {
622
+ status: response.status,
623
+ headers: Object.fromEntries(response.headers.entries()),
624
+ body: undefined as unknown as T,
625
+ };
611
626
  }
612
627
 
613
628
  const responseContentType = response.headers.get('content-type');
@@ -642,7 +657,7 @@ export class Provider {
642
657
  throw this.handleError(500, 'Unsupported Content-Type', options);
643
658
  }
644
659
 
645
- return { status: response.status, headers: response.headers, body };
660
+ return { status: response.status, headers: Object.fromEntries(response.headers.entries()), body };
646
661
  };
647
662
 
648
663
  return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
@@ -50,7 +50,7 @@ describe('Provider', () => {
50
50
  it('get', async context => {
51
51
  const response = new Response('{"data": "value"}', {
52
52
  status: 200,
53
- headers: { 'Content-Type': 'application/json' },
53
+ headers: new Headers({ 'Content-Type': 'application/json' }),
54
54
  });
55
55
 
56
56
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -77,13 +77,17 @@ describe('Provider', () => {
77
77
  },
78
78
  },
79
79
  ]);
80
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
80
+ assert.deepEqual(actualResponse, {
81
+ status: 200,
82
+ headers: Object.fromEntries(response.headers.entries()),
83
+ body: { data: 'value' },
84
+ });
81
85
  });
82
86
 
83
87
  it('accepts text/html type response', async context => {
84
88
  const response = new Response('', {
85
89
  status: 200,
86
- headers: { 'Content-Type': 'text/html; charset=UTF-8' },
90
+ headers: new Headers({ 'Content-Type': 'text/html; charset=UTF-8' }),
87
91
  });
88
92
 
89
93
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -112,13 +116,17 @@ describe('Provider', () => {
112
116
  },
113
117
  ]);
114
118
 
115
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: '' });
119
+ assert.deepEqual(actualResponse, {
120
+ status: 200,
121
+ headers: Object.fromEntries(response.headers.entries()),
122
+ body: '',
123
+ });
116
124
  });
117
125
 
118
126
  it('accepts application/schema+json type response', async context => {
119
127
  const response = new Response('{"data": "value"}', {
120
128
  status: 200,
121
- headers: { 'Content-Type': 'application/schema+json; charset=UTF-8' },
129
+ headers: new Headers({ 'Content-Type': 'application/schema+json; charset=UTF-8' }),
122
130
  });
123
131
 
124
132
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -147,13 +155,17 @@ describe('Provider', () => {
147
155
  },
148
156
  ]);
149
157
 
150
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
158
+ assert.deepEqual(actualResponse, {
159
+ status: 200,
160
+ headers: Object.fromEntries(response.headers.entries()),
161
+ body: { data: 'value' },
162
+ });
151
163
  });
152
164
 
153
165
  it('accepts application/swagger+json type response', async context => {
154
166
  const response = new Response('{"data": "value"}', {
155
167
  status: 200,
156
- headers: { 'Content-Type': 'application/swagger+json; charset=UTF-8' },
168
+ headers: new Headers({ 'Content-Type': 'application/swagger+json; charset=UTF-8' }),
157
169
  });
158
170
 
159
171
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -182,13 +194,17 @@ describe('Provider', () => {
182
194
  },
183
195
  ]);
184
196
 
185
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
197
+ assert.deepEqual(actualResponse, {
198
+ status: 200,
199
+ headers: Object.fromEntries(response.headers.entries()),
200
+ body: { data: 'value' },
201
+ });
186
202
  });
187
203
 
188
204
  it('accepts application/vnd.oracle.resource+json type response', async context => {
189
205
  const response = new Response('{"data": "value"}', {
190
206
  status: 200,
191
- headers: { 'Content-Type': 'application/vnd.oracle.resource+json; type=collection; charset=UTF-8' },
207
+ headers: new Headers({ 'Content-Type': 'application/vnd.oracle.resource+json; type=collection; charset=UTF-8' }),
192
208
  });
193
209
 
194
210
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -217,13 +233,17 @@ describe('Provider', () => {
217
233
  },
218
234
  ]);
219
235
 
220
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
236
+ assert.deepEqual(actualResponse, {
237
+ status: 200,
238
+ headers: Object.fromEntries(response.headers.entries()),
239
+ body: { data: 'value' },
240
+ });
221
241
  });
222
242
 
223
243
  it('returns the raw response body if specified', async context => {
224
244
  const response = new Response(`IMAGINE A HUGE PAYLOAD`, {
225
245
  status: 200,
226
- headers: { 'Content-Type': 'image/png' },
246
+ headers: new Headers({ 'Content-Type': 'image/png' }),
227
247
  });
228
248
 
229
249
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -246,7 +266,7 @@ describe('Provider', () => {
246
266
  it('gets an endpoint which is an absolute url', async context => {
247
267
  const response = new Response('{"data": "value"}', {
248
268
  status: 200,
249
- headers: { 'Content-Type': 'application/json' },
269
+ headers: new Headers({ 'Content-Type': 'application/json' }),
250
270
  });
251
271
 
252
272
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -271,13 +291,17 @@ describe('Provider', () => {
271
291
  },
272
292
  },
273
293
  ]);
274
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
294
+ assert.deepEqual(actualResponse, {
295
+ status: 200,
296
+ headers: Object.fromEntries(response.headers.entries()),
297
+ body: { data: 'value' },
298
+ });
275
299
  });
276
300
 
277
301
  it('gets on provider url', async context => {
278
302
  const response = new Response('{"data": "value"}', {
279
303
  status: 200,
280
- headers: { 'Content-Type': 'application/json' },
304
+ headers: new Headers({ 'Content-Type': 'application/json' }),
281
305
  });
282
306
 
283
307
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -302,13 +326,17 @@ describe('Provider', () => {
302
326
  },
303
327
  },
304
328
  ]);
305
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { data: 'value' } });
329
+ assert.deepEqual(actualResponse, {
330
+ status: 200,
331
+ headers: Object.fromEntries(response.headers.entries()),
332
+ body: { data: 'value' },
333
+ });
306
334
  });
307
335
 
308
336
  it('post with url encoded body', async context => {
309
337
  const response = new Response('{"data": "value"}', {
310
338
  status: 201,
311
- headers: { 'Content-Type': 'application/json' },
339
+ headers: new Headers({ 'Content-Type': 'application/json' }),
312
340
  });
313
341
 
314
342
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -342,13 +370,17 @@ describe('Provider', () => {
342
370
  },
343
371
  },
344
372
  ]);
345
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
373
+ assert.deepEqual(actualResponse, {
374
+ status: 201,
375
+ headers: Object.fromEntries(response.headers.entries()),
376
+ body: { data: 'value' },
377
+ });
346
378
  });
347
379
 
348
380
  it('accepts an array as body for post request', async context => {
349
381
  const response = new Response('{"data": "value"}', {
350
382
  status: 201,
351
- headers: { 'Content-Type': 'application/json' },
383
+ headers: new Headers({ 'Content-Type': 'application/json' }),
352
384
  });
353
385
 
354
386
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -383,13 +415,17 @@ describe('Provider', () => {
383
415
  },
384
416
  },
385
417
  ]);
386
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
418
+ assert.deepEqual(actualResponse, {
419
+ status: 201,
420
+ headers: Object.fromEntries(response.headers.entries()),
421
+ body: { data: 'value' },
422
+ });
387
423
  });
388
424
 
389
425
  it('put with json body', async context => {
390
426
  const response = new Response('{"data": "value"}', {
391
427
  status: 201,
392
- headers: { 'Content-Type': 'application/json' },
428
+ headers: new Headers({ 'Content-Type': 'application/json' }),
393
429
  });
394
430
 
395
431
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -424,13 +460,17 @@ describe('Provider', () => {
424
460
  },
425
461
  },
426
462
  ]);
427
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
463
+ assert.deepEqual(actualResponse, {
464
+ status: 201,
465
+ headers: Object.fromEntries(response.headers.entries()),
466
+ body: { data: 'value' },
467
+ });
428
468
  });
429
469
 
430
470
  it('putBuffer with Buffer body', async context => {
431
471
  const response = new Response('{"data": "value"}', {
432
472
  status: 201,
433
- headers: { 'Content-Type': 'application/json' },
473
+ headers: new Headers({ 'Content-Type': 'application/json' }),
434
474
  });
435
475
 
436
476
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -461,13 +501,17 @@ describe('Provider', () => {
461
501
  },
462
502
  },
463
503
  ]);
464
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
504
+ assert.deepEqual(actualResponse, {
505
+ status: 201,
506
+ headers: Object.fromEntries(response.headers.entries()),
507
+ body: { data: 'value' },
508
+ });
465
509
  });
466
510
 
467
511
  it('patch with query params', async context => {
468
512
  const response = new Response('{"data": "value"}', {
469
513
  status: 201,
470
- headers: { 'Content-Type': 'application/json' },
514
+ headers: new Headers({ 'Content-Type': 'application/json' }),
471
515
  });
472
516
 
473
517
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -502,13 +546,17 @@ describe('Provider', () => {
502
546
  },
503
547
  },
504
548
  ]);
505
- assert.deepEqual(actualResponse, { status: 201, headers: response.headers, body: { data: 'value' } });
549
+ assert.deepEqual(actualResponse, {
550
+ status: 201,
551
+ headers: Object.fromEntries(response.headers.entries()),
552
+ body: { data: 'value' },
553
+ });
506
554
  });
507
555
 
508
556
  it('delete', async context => {
509
557
  const response = new Response(undefined, {
510
558
  status: 204,
511
- headers: { 'Content-Type': 'application/json' },
559
+ headers: new Headers({ 'Content-Type': 'application/json' }),
512
560
  });
513
561
 
514
562
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -535,13 +583,17 @@ describe('Provider', () => {
535
583
  },
536
584
  },
537
585
  ]);
538
- assert.deepEqual(actualResponse, { status: 204, headers: response.headers, body: undefined });
586
+ assert.deepEqual(actualResponse, {
587
+ status: 204,
588
+ headers: Object.fromEntries(response.headers.entries()),
589
+ body: undefined,
590
+ });
539
591
  });
540
592
 
541
593
  it('deleteWithBody', async context => {
542
594
  const response = new Response('{"success": true}', {
543
595
  status: 200,
544
- headers: { 'Content-Type': 'application/json' },
596
+ headers: new Headers({ 'Content-Type': 'application/json' }),
545
597
  });
546
598
 
547
599
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -574,7 +626,11 @@ describe('Provider', () => {
574
626
  },
575
627
  },
576
628
  ]);
577
- assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { success: true } });
629
+ assert.deepEqual(actualResponse, {
630
+ status: 200,
631
+ headers: Object.fromEntries(response.headers.entries()),
632
+ body: { success: true },
633
+ });
578
634
  });
579
635
 
580
636
  it('uses rate limiter if provided', async context => {
@@ -595,7 +651,7 @@ describe('Provider', () => {
595
651
 
596
652
  const response = new Response(undefined, {
597
653
  status: 204,
598
- headers: { 'Content-Type': 'application/json' },
654
+ headers: new Headers({ 'Content-Type': 'application/json' }),
599
655
  });
600
656
 
601
657
  const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -626,7 +682,11 @@ describe('Provider', () => {
626
682
  },
627
683
  },
628
684
  ]);
629
- assert.deepEqual(actualResponse, { status: 204, headers: response.headers, body: undefined });
685
+ assert.deepEqual(actualResponse, {
686
+ status: 204,
687
+ headers: Object.fromEntries(response.headers.entries()),
688
+ body: undefined,
689
+ });
630
690
  });
631
691
 
632
692
  it('uses custom error handler if provided', async context => {
@@ -648,7 +708,7 @@ describe('Provider', () => {
648
708
 
649
709
  const response = new Response(undefined, {
650
710
  status: 400,
651
- headers: { 'Content-Type': 'application/json' },
711
+ headers: new Headers({ 'Content-Type': 'application/json' }),
652
712
  });
653
713
 
654
714
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -695,7 +755,7 @@ describe('Provider', () => {
695
755
 
696
756
  const response = new Response(undefined, {
697
757
  status: 400,
698
- headers: { 'Content-Type': 'application/json' },
758
+ headers: new Headers({ 'Content-Type': 'application/json' }),
699
759
  });
700
760
 
701
761
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -736,7 +796,7 @@ describe('Provider', () => {
736
796
 
737
797
  const response = new Response(undefined, {
738
798
  status: 404,
739
- headers: { 'Content-Type': 'application/json' },
799
+ headers: new Headers({ 'Content-Type': 'application/json' }),
740
800
  });
741
801
 
742
802
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -762,7 +822,7 @@ describe('Provider', () => {
762
822
  it('returns valid json response', async context => {
763
823
  const response = new Response(`{ "validJson": true }`, {
764
824
  status: 200,
765
- headers: { 'Content-Type': 'application/json;charset=utf-8' },
825
+ headers: new Headers({ 'Content-Type': 'application/json;charset=utf-8' }),
766
826
  });
767
827
 
768
828
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -798,7 +858,7 @@ describe('Provider', () => {
798
858
  it('returns streamable response on streaming get calls', async context => {
799
859
  const response = new Response(`IMAGINE A HUGE PAYLOAD`, {
800
860
  status: 200,
801
- headers: { 'Content-Type': 'video/mp4' },
861
+ headers: new Headers({ 'Content-Type': 'video/mp4' }),
802
862
  });
803
863
 
804
864
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -816,7 +876,7 @@ describe('Provider', () => {
816
876
  it('returns successfully on unexpected content-type response with no body', async context => {
817
877
  const response = new Response(null, {
818
878
  status: 201,
819
- headers: { 'Content-Type': 'html/text' },
879
+ headers: new Headers({ 'Content-Type': 'html/text' }),
820
880
  });
821
881
 
822
882
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -833,14 +893,14 @@ describe('Provider', () => {
833
893
 
834
894
  assert.ok(providerResponse);
835
895
  assert.strictEqual(providerResponse.status, response.status);
836
- assert.strictEqual(providerResponse.headers, response.headers);
896
+ assert.deepEqual(providerResponse.headers, Object.fromEntries(response.headers.entries()));
837
897
  assert.strictEqual(providerResponse.body, undefined);
838
898
  });
839
899
 
840
900
  it('throws on invalid json response', async context => {
841
901
  const response = new Response('{invalidJSON}', {
842
902
  status: 200,
843
- headers: { 'Content-Type': 'application/json' },
903
+ headers: new Headers({ 'Content-Type': 'application/json' }),
844
904
  });
845
905
 
846
906
  context.mock.method(global, 'fetch', () => Promise.resolve(response));
@@ -864,7 +924,7 @@ describe('Provider', () => {
864
924
  it('throws on unexpected content-type response', async context => {
865
925
  const response = new Response('text', {
866
926
  status: 200,
867
- headers: { 'Content-Type': 'application/text' },
927
+ headers: new Headers({ 'Content-Type': 'application/text' }),
868
928
  });
869
929
 
870
930
  context.mock.method(global, 'fetch', () => Promise.resolve(response));