@unito/integration-sdk 4.1.0 → 4.2.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.
@@ -6,6 +6,7 @@ var crypto = require('crypto');
6
6
  var util = require('util');
7
7
  var express = require('express');
8
8
  var busboy = require('busboy');
9
+ var fs = require('fs');
9
10
  var https = require('https');
10
11
 
11
12
  function _interopNamespaceDefault(e) {
@@ -1190,6 +1191,10 @@ class Provider {
1190
1191
  * The Rate Limiter function to use to limit the rate of calls made to the provider based on the caller's credentials.
1191
1192
  */
1192
1193
  rateLimiter = undefined;
1194
+ static recordingSeq = 0;
1195
+ static get recordingPath() {
1196
+ return process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1197
+ }
1193
1198
  /**
1194
1199
  * Function called before each request to define the Provider's base URL and any specific headers to add to the requests.
1195
1200
  *
@@ -1313,9 +1318,7 @@ class Provider {
1313
1318
  }
1314
1319
  resolve({
1315
1320
  status: 201,
1316
- headers: Object.fromEntries(Object.entries(response.headers)
1317
- .filter((entry) => entry[1] !== undefined)
1318
- .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value])),
1321
+ headers: Provider.normalizeNodeHeaders(response.headers),
1319
1322
  body,
1320
1323
  });
1321
1324
  }
@@ -1416,9 +1419,7 @@ class Provider {
1416
1419
  const body = responseBody ? JSON.parse(responseBody) : undefined;
1417
1420
  safeResolve({
1418
1421
  status: response.statusCode || 200,
1419
- headers: Object.fromEntries(Object.entries(response.headers)
1420
- .filter((entry) => entry[1] !== undefined)
1421
- .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value])),
1422
+ headers: Provider.normalizeNodeHeaders(response.headers),
1422
1423
  body,
1423
1424
  });
1424
1425
  }
@@ -1632,6 +1633,32 @@ class Provider {
1632
1633
  },
1633
1634
  },
1634
1635
  });
1636
+ // Record raw response for schema-snapshot coverage analysis.
1637
+ // Clone the response before consuming the body so recording doesn't interfere with normal flow.
1638
+ // Only record JSON-like responses (skip binary streams and raw body requests).
1639
+ if (Provider.recordingPath &&
1640
+ !options.rawBody &&
1641
+ headers.Accept !== 'application/octet-stream' &&
1642
+ response.status < 400) {
1643
+ try {
1644
+ const cloned = response.clone();
1645
+ const recordBody = await cloned.json().catch(() => undefined);
1646
+ if (recordBody !== undefined) {
1647
+ Provider.recordingSeq++;
1648
+ const entry = JSON.stringify({
1649
+ seq: Provider.recordingSeq,
1650
+ url: absoluteUrl,
1651
+ method: options.method,
1652
+ status: response.status,
1653
+ body: recordBody,
1654
+ });
1655
+ fs.appendFileSync(Provider.recordingPath, entry + '\n');
1656
+ }
1657
+ }
1658
+ catch {
1659
+ // Recording failure should never break the integration.
1660
+ }
1661
+ }
1635
1662
  if (response.status >= 400) {
1636
1663
  const textResult = await response.text();
1637
1664
  throw this.handleError(response.status, textResult, options);
@@ -1677,6 +1704,15 @@ class Provider {
1677
1704
  };
1678
1705
  return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
1679
1706
  }
1707
+ /**
1708
+ * Normalizes Node.js IncomingHttpHeaders (which may have undefined or string[] values)
1709
+ * into a flat Record<string, string> by joining array values and dropping undefined entries.
1710
+ */
1711
+ static normalizeNodeHeaders(headers) {
1712
+ return Object.fromEntries(Object.entries(headers)
1713
+ .filter((entry) => entry[1] !== undefined)
1714
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value]));
1715
+ }
1680
1716
  handleError(responseStatus, message, options) {
1681
1717
  const customError = this.customErrorHandler?.(responseStatus, message, options);
1682
1718
  return customError ?? buildHttpError(responseStatus, message);
@@ -80,6 +80,8 @@ export declare class Provider {
80
80
  * The Rate Limiter function to use to limit the rate of calls made to the provider based on the caller's credentials.
81
81
  */
82
82
  protected rateLimiter: RateLimiter | undefined;
83
+ private static recordingSeq;
84
+ private static get recordingPath();
83
85
  /**
84
86
  * Function called before each request to define the Provider's base URL and any specific headers to add to the requests.
85
87
  *
@@ -219,5 +221,10 @@ export declare class Provider {
219
221
  delete<T = undefined>(endpoint: string, options: RequestOptions, body?: RequestBody | null): Promise<Response<T>>;
220
222
  private generateAbsoluteUrl;
221
223
  private fetchWrapper;
224
+ /**
225
+ * Normalizes Node.js IncomingHttpHeaders (which may have undefined or string[] values)
226
+ * into a flat Record<string, string> by joining array values and dropping undefined entries.
227
+ */
228
+ private static normalizeNodeHeaders;
222
229
  private handleError;
223
230
  }
@@ -1,3 +1,4 @@
1
+ import fs from 'fs';
1
2
  import https from 'https';
2
3
  import { buildHttpError } from '../errors.js';
3
4
  /**
@@ -19,6 +20,10 @@ export class Provider {
19
20
  * The Rate Limiter function to use to limit the rate of calls made to the provider based on the caller's credentials.
20
21
  */
21
22
  rateLimiter = undefined;
23
+ static recordingSeq = 0;
24
+ static get recordingPath() {
25
+ return process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
26
+ }
22
27
  /**
23
28
  * Function called before each request to define the Provider's base URL and any specific headers to add to the requests.
24
29
  *
@@ -142,9 +147,7 @@ export class Provider {
142
147
  }
143
148
  resolve({
144
149
  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])),
150
+ headers: Provider.normalizeNodeHeaders(response.headers),
148
151
  body,
149
152
  });
150
153
  }
@@ -245,9 +248,7 @@ export class Provider {
245
248
  const body = responseBody ? JSON.parse(responseBody) : undefined;
246
249
  safeResolve({
247
250
  status: response.statusCode || 200,
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])),
251
+ headers: Provider.normalizeNodeHeaders(response.headers),
251
252
  body,
252
253
  });
253
254
  }
@@ -461,6 +462,32 @@ export class Provider {
461
462
  },
462
463
  },
463
464
  });
465
+ // Record raw response for schema-snapshot coverage analysis.
466
+ // Clone the response before consuming the body so recording doesn't interfere with normal flow.
467
+ // Only record JSON-like responses (skip binary streams and raw body requests).
468
+ if (Provider.recordingPath &&
469
+ !options.rawBody &&
470
+ headers.Accept !== 'application/octet-stream' &&
471
+ response.status < 400) {
472
+ try {
473
+ const cloned = response.clone();
474
+ const recordBody = await cloned.json().catch(() => undefined);
475
+ if (recordBody !== undefined) {
476
+ Provider.recordingSeq++;
477
+ const entry = JSON.stringify({
478
+ seq: Provider.recordingSeq,
479
+ url: absoluteUrl,
480
+ method: options.method,
481
+ status: response.status,
482
+ body: recordBody,
483
+ });
484
+ fs.appendFileSync(Provider.recordingPath, entry + '\n');
485
+ }
486
+ }
487
+ catch {
488
+ // Recording failure should never break the integration.
489
+ }
490
+ }
464
491
  if (response.status >= 400) {
465
492
  const textResult = await response.text();
466
493
  throw this.handleError(response.status, textResult, options);
@@ -506,6 +533,15 @@ export class Provider {
506
533
  };
507
534
  return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
508
535
  }
536
+ /**
537
+ * Normalizes Node.js IncomingHttpHeaders (which may have undefined or string[] values)
538
+ * into a flat Record<string, string> by joining array values and dropping undefined entries.
539
+ */
540
+ static normalizeNodeHeaders(headers) {
541
+ return Object.fromEntries(Object.entries(headers)
542
+ .filter((entry) => entry[1] !== undefined)
543
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value]));
544
+ }
509
545
  handleError(responseStatus, message, options) {
510
546
  const customError = this.customErrorHandler?.(responseStatus, message, options);
511
547
  return customError ?? buildHttpError(responseStatus, message);
@@ -1,5 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { describe, it } from 'node:test';
3
+ import fs from 'fs';
3
4
  import { Readable } from 'stream';
4
5
  import nock from 'nock';
5
6
  import https from 'https';
@@ -1225,4 +1226,114 @@ describe('Provider', () => {
1225
1226
  // Verify the signal is indeed aborted
1226
1227
  assert.ok(abortController.signal.aborted, 'Signal should be aborted');
1227
1228
  });
1229
+ describe('response recording', () => {
1230
+ it('records JSON responses to JSONL file when env var is set', async () => {
1231
+ const tmpFile = `/tmp/provider-recording-test-${Date.now()}.jsonl`;
1232
+ process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH = tmpFile;
1233
+ try {
1234
+ const recordingProvider = new Provider({
1235
+ prepareRequest: () => ({
1236
+ url: 'https://www.myApi.com',
1237
+ headers: { 'Content-Type': 'application/json' },
1238
+ }),
1239
+ });
1240
+ nock('https://www.myApi.com').get('/items').reply(200, { id: '123', name: 'Test', priority: 'high' });
1241
+ await recordingProvider.get('/items', {
1242
+ credentials: { unitoCredentialId: '123' },
1243
+ logger,
1244
+ });
1245
+ const content = fs.readFileSync(tmpFile, 'utf-8');
1246
+ const lines = content.trim().split('\n');
1247
+ assert.equal(lines.length, 1);
1248
+ const entry = JSON.parse(lines[0]);
1249
+ assert.ok(typeof entry.seq === 'number' && entry.seq > 0, `seq should be a positive number, got ${entry.seq}`);
1250
+ assert.equal(entry.url, 'https://www.myApi.com/items');
1251
+ assert.equal(entry.method, 'GET');
1252
+ assert.equal(entry.status, 200);
1253
+ assert.deepEqual(entry.body, { id: '123', name: 'Test', priority: 'high' });
1254
+ }
1255
+ finally {
1256
+ delete process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1257
+ try {
1258
+ fs.unlinkSync(tmpFile);
1259
+ }
1260
+ catch {
1261
+ /* ignore */
1262
+ }
1263
+ }
1264
+ });
1265
+ it('does not record when env var is not set', async () => {
1266
+ delete process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1267
+ const noRecordProvider = new Provider({
1268
+ prepareRequest: () => ({
1269
+ url: 'https://www.myApi.com',
1270
+ headers: { 'Content-Type': 'application/json' },
1271
+ }),
1272
+ });
1273
+ nock('https://www.myApi.com').get('/no-record').reply(200, { id: '123' });
1274
+ await noRecordProvider.get('/no-record', {
1275
+ credentials: { unitoCredentialId: '123' },
1276
+ logger,
1277
+ });
1278
+ // No assertion needed — just ensure no crash.
1279
+ });
1280
+ it('does not record stream responses', async () => {
1281
+ const tmpFile = `/tmp/provider-recording-stream-test-${Date.now()}.jsonl`;
1282
+ process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH = tmpFile;
1283
+ try {
1284
+ const recordingProvider = new Provider({
1285
+ prepareRequest: () => ({
1286
+ url: 'https://www.myApi.com',
1287
+ headers: {},
1288
+ }),
1289
+ });
1290
+ nock('https://www.myApi.com')
1291
+ .get('/download')
1292
+ .reply(200, 'binary-data', { 'Content-Type': 'application/octet-stream' });
1293
+ await recordingProvider.streamingGet('/download', {
1294
+ credentials: { unitoCredentialId: '123' },
1295
+ logger,
1296
+ });
1297
+ assert.equal(fs.existsSync(tmpFile), false);
1298
+ }
1299
+ finally {
1300
+ delete process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1301
+ try {
1302
+ fs.unlinkSync(tmpFile);
1303
+ }
1304
+ catch {
1305
+ /* ignore */
1306
+ }
1307
+ }
1308
+ });
1309
+ it('increments seq across multiple requests', async () => {
1310
+ const tmpFile = `/tmp/provider-recording-seq-test-${Date.now()}.jsonl`;
1311
+ process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH = tmpFile;
1312
+ try {
1313
+ const recordingProvider = new Provider({
1314
+ prepareRequest: () => ({
1315
+ url: 'https://www.myApi.com',
1316
+ headers: { 'Content-Type': 'application/json' },
1317
+ }),
1318
+ });
1319
+ nock('https://www.myApi.com').get('/a').reply(200, { id: '1' }).get('/b').reply(200, { id: '2' });
1320
+ await recordingProvider.get('/a', { credentials: { unitoCredentialId: '123' }, logger });
1321
+ await recordingProvider.get('/b', { credentials: { unitoCredentialId: '123' }, logger });
1322
+ const lines = fs.readFileSync(tmpFile, 'utf-8').trim().split('\n');
1323
+ assert.equal(lines.length, 2);
1324
+ const seq1 = JSON.parse(lines[0]).seq;
1325
+ const seq2 = JSON.parse(lines[1]).seq;
1326
+ assert.equal(seq2, seq1 + 1, `second seq (${seq2}) should be first seq (${seq1}) + 1`);
1327
+ }
1328
+ finally {
1329
+ delete process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1330
+ try {
1331
+ fs.unlinkSync(tmpFile);
1332
+ }
1333
+ catch {
1334
+ /* ignore */
1335
+ }
1336
+ }
1337
+ });
1338
+ });
1228
1339
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unito/integration-sdk",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Integration SDK",
5
5
  "type": "module",
6
6
  "types": "dist/src/index.d.ts",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "@types/express": "5.x",
56
- "@unito/integration-api": "5.x",
56
+ "@unito/integration-api": "6.x",
57
57
  "busboy": "^1.6.0",
58
58
  "cachette": "4.x",
59
59
  "express": "^5.1",
@@ -1,3 +1,4 @@
1
+ import fs from 'fs';
1
2
  import https from 'https';
2
3
  import FormData from 'form-data';
3
4
  import * as stream from 'stream';
@@ -85,6 +86,12 @@ export class Provider {
85
86
  * The Rate Limiter function to use to limit the rate of calls made to the provider based on the caller's credentials.
86
87
  */
87
88
  protected rateLimiter: RateLimiter | undefined = undefined;
89
+
90
+ private static recordingSeq = 0;
91
+ private static get recordingPath(): string | undefined {
92
+ return process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
93
+ }
94
+
88
95
  /**
89
96
  * Function called before each request to define the Provider's base URL and any specific headers to add to the requests.
90
97
  *
@@ -232,11 +239,7 @@ export class Provider {
232
239
  }
233
240
  resolve({
234
241
  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
- ),
242
+ headers: Provider.normalizeNodeHeaders(response.headers),
240
243
  body,
241
244
  });
242
245
  } catch (error) {
@@ -353,11 +356,7 @@ export class Provider {
353
356
  const body = responseBody ? JSON.parse(responseBody) : undefined;
354
357
  safeResolve({
355
358
  status: response.statusCode || 200,
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
- ),
359
+ headers: Provider.normalizeNodeHeaders(response.headers),
361
360
  body,
362
361
  });
363
362
  } catch (error) {
@@ -613,6 +612,34 @@ export class Provider {
613
612
  },
614
613
  );
615
614
 
615
+ // Record raw response for schema-snapshot coverage analysis.
616
+ // Clone the response before consuming the body so recording doesn't interfere with normal flow.
617
+ // Only record JSON-like responses (skip binary streams and raw body requests).
618
+ if (
619
+ Provider.recordingPath &&
620
+ !options.rawBody &&
621
+ headers.Accept !== 'application/octet-stream' &&
622
+ response.status < 400
623
+ ) {
624
+ try {
625
+ const cloned = response.clone();
626
+ const recordBody = await cloned.json().catch(() => undefined);
627
+ if (recordBody !== undefined) {
628
+ Provider.recordingSeq++;
629
+ const entry = JSON.stringify({
630
+ seq: Provider.recordingSeq,
631
+ url: absoluteUrl,
632
+ method: options.method,
633
+ status: response.status,
634
+ body: recordBody,
635
+ });
636
+ fs.appendFileSync(Provider.recordingPath, entry + '\n');
637
+ }
638
+ } catch {
639
+ // Recording failure should never break the integration.
640
+ }
641
+ }
642
+
616
643
  if (response.status >= 400) {
617
644
  const textResult = await response.text();
618
645
  throw this.handleError(response.status, textResult, options);
@@ -663,6 +690,18 @@ export class Provider {
663
690
  return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
664
691
  }
665
692
 
693
+ /**
694
+ * Normalizes Node.js IncomingHttpHeaders (which may have undefined or string[] values)
695
+ * into a flat Record<string, string> by joining array values and dropping undefined entries.
696
+ */
697
+ private static normalizeNodeHeaders(headers: Record<string, string | string[] | undefined>): Record<string, string> {
698
+ return Object.fromEntries(
699
+ Object.entries(headers)
700
+ .filter((entry): entry is [string, string | string[]] => entry[1] !== undefined)
701
+ .map(([key, value]) => [key, Array.isArray(value) ? value.join(',') : value]),
702
+ );
703
+ }
704
+
666
705
  private handleError(responseStatus: number, message: string, options: RequestOptions): HttpErrors.HttpError {
667
706
  const customError = this.customErrorHandler?.(responseStatus, message, options);
668
707
 
@@ -1,5 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { describe, it } from 'node:test';
3
+ import fs from 'fs';
3
4
  import { Readable } from 'stream';
4
5
  import nock from 'nock';
5
6
  import https from 'https';
@@ -1461,4 +1462,128 @@ describe('Provider', () => {
1461
1462
  // Verify the signal is indeed aborted
1462
1463
  assert.ok(abortController.signal.aborted, 'Signal should be aborted');
1463
1464
  });
1465
+
1466
+ describe('response recording', () => {
1467
+ it('records JSON responses to JSONL file when env var is set', async () => {
1468
+ const tmpFile = `/tmp/provider-recording-test-${Date.now()}.jsonl`;
1469
+ process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH = tmpFile;
1470
+
1471
+ try {
1472
+ const recordingProvider = new Provider({
1473
+ prepareRequest: () => ({
1474
+ url: 'https://www.myApi.com',
1475
+ headers: { 'Content-Type': 'application/json' },
1476
+ }),
1477
+ });
1478
+
1479
+ nock('https://www.myApi.com').get('/items').reply(200, { id: '123', name: 'Test', priority: 'high' });
1480
+
1481
+ await recordingProvider.get('/items', {
1482
+ credentials: { unitoCredentialId: '123' },
1483
+ logger,
1484
+ });
1485
+
1486
+ const content = fs.readFileSync(tmpFile, 'utf-8');
1487
+ const lines = content.trim().split('\n');
1488
+ assert.equal(lines.length, 1);
1489
+
1490
+ const entry = JSON.parse(lines[0]!);
1491
+ assert.ok(typeof entry.seq === 'number' && entry.seq > 0, `seq should be a positive number, got ${entry.seq}`);
1492
+ assert.equal(entry.url, 'https://www.myApi.com/items');
1493
+ assert.equal(entry.method, 'GET');
1494
+ assert.equal(entry.status, 200);
1495
+ assert.deepEqual(entry.body, { id: '123', name: 'Test', priority: 'high' });
1496
+ } finally {
1497
+ delete process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1498
+ try {
1499
+ fs.unlinkSync(tmpFile);
1500
+ } catch {
1501
+ /* ignore */
1502
+ }
1503
+ }
1504
+ });
1505
+
1506
+ it('does not record when env var is not set', async () => {
1507
+ delete process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1508
+
1509
+ const noRecordProvider = new Provider({
1510
+ prepareRequest: () => ({
1511
+ url: 'https://www.myApi.com',
1512
+ headers: { 'Content-Type': 'application/json' },
1513
+ }),
1514
+ });
1515
+
1516
+ nock('https://www.myApi.com').get('/no-record').reply(200, { id: '123' });
1517
+
1518
+ await noRecordProvider.get('/no-record', {
1519
+ credentials: { unitoCredentialId: '123' },
1520
+ logger,
1521
+ });
1522
+ // No assertion needed — just ensure no crash.
1523
+ });
1524
+
1525
+ it('does not record stream responses', async () => {
1526
+ const tmpFile = `/tmp/provider-recording-stream-test-${Date.now()}.jsonl`;
1527
+ process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH = tmpFile;
1528
+
1529
+ try {
1530
+ const recordingProvider = new Provider({
1531
+ prepareRequest: () => ({
1532
+ url: 'https://www.myApi.com',
1533
+ headers: {},
1534
+ }),
1535
+ });
1536
+
1537
+ nock('https://www.myApi.com')
1538
+ .get('/download')
1539
+ .reply(200, 'binary-data', { 'Content-Type': 'application/octet-stream' });
1540
+
1541
+ await recordingProvider.streamingGet('/download', {
1542
+ credentials: { unitoCredentialId: '123' },
1543
+ logger,
1544
+ });
1545
+
1546
+ assert.equal(fs.existsSync(tmpFile), false);
1547
+ } finally {
1548
+ delete process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1549
+ try {
1550
+ fs.unlinkSync(tmpFile);
1551
+ } catch {
1552
+ /* ignore */
1553
+ }
1554
+ }
1555
+ });
1556
+
1557
+ it('increments seq across multiple requests', async () => {
1558
+ const tmpFile = `/tmp/provider-recording-seq-test-${Date.now()}.jsonl`;
1559
+ process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH = tmpFile;
1560
+
1561
+ try {
1562
+ const recordingProvider = new Provider({
1563
+ prepareRequest: () => ({
1564
+ url: 'https://www.myApi.com',
1565
+ headers: { 'Content-Type': 'application/json' },
1566
+ }),
1567
+ });
1568
+
1569
+ nock('https://www.myApi.com').get('/a').reply(200, { id: '1' }).get('/b').reply(200, { id: '2' });
1570
+
1571
+ await recordingProvider.get('/a', { credentials: { unitoCredentialId: '123' }, logger });
1572
+ await recordingProvider.get('/b', { credentials: { unitoCredentialId: '123' }, logger });
1573
+
1574
+ const lines = fs.readFileSync(tmpFile, 'utf-8').trim().split('\n');
1575
+ assert.equal(lines.length, 2);
1576
+ const seq1 = JSON.parse(lines[0]!).seq;
1577
+ const seq2 = JSON.parse(lines[1]!).seq;
1578
+ assert.equal(seq2, seq1 + 1, `second seq (${seq2}) should be first seq (${seq1}) + 1`);
1579
+ } finally {
1580
+ delete process.env.UNITO_SCHEMA_SNAPSHOT_RECORD_PATH;
1581
+ try {
1582
+ fs.unlinkSync(tmpFile);
1583
+ } catch {
1584
+ /* ignore */
1585
+ }
1586
+ }
1587
+ });
1588
+ });
1464
1589
  });