nango 0.69.21 → 0.69.26

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.
Files changed (63) hide show
  1. package/dist/index.js +122 -20
  2. package/dist/index.js.map +1 -1
  3. package/dist/services/clone.service.d.ts +12 -0
  4. package/dist/services/clone.service.js +440 -0
  5. package/dist/services/clone.service.js.map +1 -0
  6. package/dist/services/dryrun.service.js +23 -82
  7. package/dist/services/dryrun.service.js.map +1 -1
  8. package/dist/services/ensure.service.d.ts +18 -0
  9. package/dist/services/ensure.service.js +60 -0
  10. package/dist/services/ensure.service.js.map +1 -0
  11. package/dist/services/function-create.service.d.ts +3 -4
  12. package/dist/services/function-create.service.js +2 -12
  13. package/dist/services/function-create.service.js.map +1 -1
  14. package/dist/services/interactive.service.d.ts +13 -0
  15. package/dist/services/interactive.service.js +147 -0
  16. package/dist/services/interactive.service.js.map +1 -0
  17. package/dist/services/response-collector.service.d.ts +23 -0
  18. package/dist/services/response-collector.service.js +233 -0
  19. package/dist/services/response-collector.service.js.map +1 -0
  20. package/dist/services/sdk.js +2 -1
  21. package/dist/services/sdk.js.map +1 -1
  22. package/dist/testMocks/utils.d.ts +137 -51
  23. package/dist/testMocks/utils.js +755 -172
  24. package/dist/testMocks/utils.js.map +1 -1
  25. package/dist/types.d.ts +3 -0
  26. package/dist/types.js +1 -1
  27. package/dist/types.js.map +1 -1
  28. package/dist/utils/errors.d.ts +3 -0
  29. package/dist/utils/errors.js +6 -0
  30. package/dist/utils/errors.js.map +1 -1
  31. package/dist/utils.d.ts +2 -1
  32. package/dist/utils.js +15 -0
  33. package/dist/utils.js.map +1 -1
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/package.json +11 -8
  37. package/dist/migrations/toZeroYaml.unit.test.d.ts +0 -1
  38. package/dist/migrations/toZeroYaml.unit.test.js +0 -307
  39. package/dist/migrations/toZeroYaml.unit.test.js.map +0 -1
  40. package/dist/services/compile.service.unit.test.d.ts +0 -1
  41. package/dist/services/compile.service.unit.test.js +0 -44
  42. package/dist/services/compile.service.unit.test.js.map +0 -1
  43. package/dist/services/config.service.unit.test.d.ts +0 -1
  44. package/dist/services/config.service.unit.test.js +0 -162
  45. package/dist/services/config.service.unit.test.js.map +0 -1
  46. package/dist/services/docs.service.unit.test.d.ts +0 -1
  47. package/dist/services/docs.service.unit.test.js +0 -51
  48. package/dist/services/docs.service.unit.test.js.map +0 -1
  49. package/dist/services/model.service.unit.test.d.ts +0 -1
  50. package/dist/services/model.service.unit.test.js +0 -240
  51. package/dist/services/model.service.unit.test.js.map +0 -1
  52. package/dist/services/response-saver.service.d.ts +0 -17
  53. package/dist/services/response-saver.service.js +0 -169
  54. package/dist/services/response-saver.service.js.map +0 -1
  55. package/dist/services/sdk.service.unit.test.d.ts +0 -1
  56. package/dist/services/sdk.service.unit.test.js +0 -66
  57. package/dist/services/sdk.service.unit.test.js.map +0 -1
  58. package/dist/zeroYaml/definitions.unit.test.d.ts +0 -1
  59. package/dist/zeroYaml/definitions.unit.test.js +0 -111
  60. package/dist/zeroYaml/definitions.unit.test.js.map +0 -1
  61. package/dist/zeroYaml/zodToNango.unit.test.d.ts +0 -1
  62. package/dist/zeroYaml/zodToNango.unit.test.js +0 -185
  63. package/dist/zeroYaml/zodToNango.unit.test.js.map +0 -1
@@ -0,0 +1,440 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parse } from '@babel/parser';
4
+ import traverse from '@babel/traverse';
5
+ import axios from 'axios';
6
+ import chalk from 'chalk';
7
+ import ora from 'ora';
8
+ import promptly from 'promptly';
9
+ import { printDebug } from '../utils.js';
10
+ // GitHub API for directory listings (rate limited: 60/hour unauthenticated, 5000/hour with token)
11
+ const GITHUB_API_BASE = 'https://api.github.com/repos/NangoHQ/integration-templates/contents/integrations';
12
+ // Raw content URL for file downloads (no practical rate limit)
13
+ const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/NangoHQ/integration-templates/main/integrations';
14
+ class GitHubRateLimitError extends Error {
15
+ constructor(resetTime) {
16
+ const resetDate = resetTime ? new Date(resetTime * 1000) : null;
17
+ const resetMsg = resetDate ? ` Rate limit resets at ${resetDate.toLocaleTimeString()}.` : '';
18
+ super(`GitHub API rate limit exceeded.${resetMsg} Set GITHUB_TOKEN environment variable to increase the limit.`);
19
+ this.name = 'GitHubRateLimitError';
20
+ }
21
+ }
22
+ class GitHubNotFoundError extends Error {
23
+ constructor(path) {
24
+ super(`Template not found: ${path}`);
25
+ this.name = 'GitHubNotFoundError';
26
+ }
27
+ }
28
+ /**
29
+ * Resolve a template path by checking what exists in the remote repository.
30
+ * - If the path matches a directory, returns type: 'directory'
31
+ * - If the path doesn't exist but {path}.ts exists, returns type: 'file' with optional .md
32
+ * - Throws GitHubNotFoundError if neither exists
33
+ */
34
+ async function resolveTemplatePath(templatePath, debug) {
35
+ const integration = templatePath.split('/')[0];
36
+ if (!integration) {
37
+ throw new Error('Template path cannot be empty');
38
+ }
39
+ // First, try to fetch as a directory
40
+ try {
41
+ await fetchGitHubDirectory(templatePath, debug);
42
+ printDebug(`Path "${templatePath}" resolved as directory`, debug);
43
+ return { type: 'directory', path: templatePath, integration };
44
+ }
45
+ catch (err) {
46
+ if (!(err instanceof GitHubNotFoundError)) {
47
+ throw err; // Re-throw rate limits, network errors, etc.
48
+ }
49
+ // Directory doesn't exist, try as file
50
+ }
51
+ const tsPath = `${templatePath}.ts`;
52
+ try {
53
+ await fetchFileContent(tsPath, debug);
54
+ printDebug(`Path "${templatePath}" resolved as file: ${tsPath}`, debug);
55
+ // Check if companion .md file exists
56
+ const mdPath = `${templatePath}.md`;
57
+ let hasMdFile = false;
58
+ try {
59
+ await fetchFileContent(mdPath, debug);
60
+ hasMdFile = true;
61
+ printDebug(`Found companion documentation: ${mdPath}`, debug);
62
+ }
63
+ catch {
64
+ printDebug(`No companion documentation found for ${templatePath}`, debug);
65
+ }
66
+ return {
67
+ type: 'file',
68
+ path: tsPath,
69
+ mdPath: hasMdFile ? mdPath : undefined,
70
+ integration
71
+ };
72
+ }
73
+ catch (err) {
74
+ if (!(err instanceof GitHubNotFoundError) && !(err instanceof Error && err.message.startsWith('File not found'))) {
75
+ throw err;
76
+ }
77
+ }
78
+ // Neither directory nor file exists
79
+ throw new GitHubNotFoundError(templatePath);
80
+ }
81
+ function getGitHubHeaders() {
82
+ const headers = {
83
+ Accept: 'application/vnd.github.v3+json',
84
+ 'User-Agent': 'nango-cli'
85
+ };
86
+ const token = process.env['GITHUB_TOKEN'];
87
+ if (token) {
88
+ headers['Authorization'] = `Bearer ${token}`;
89
+ }
90
+ return headers;
91
+ }
92
+ async function fetchGitHubDirectory(urlPath, debug) {
93
+ const url = `${GITHUB_API_BASE}/${urlPath}`;
94
+ printDebug(`Fetching GitHub directory: ${url}`, debug);
95
+ try {
96
+ const response = await axios.get(url, {
97
+ headers: getGitHubHeaders()
98
+ });
99
+ return response.data;
100
+ }
101
+ catch (err) {
102
+ if (axios.isAxiosError(err)) {
103
+ if (err.response?.status === 403) {
104
+ const resetTime = err.response.headers['x-ratelimit-reset'];
105
+ throw new GitHubRateLimitError(resetTime ? Number(resetTime) : undefined);
106
+ }
107
+ if (err.response?.status === 404) {
108
+ throw new GitHubNotFoundError(urlPath);
109
+ }
110
+ }
111
+ throw err;
112
+ }
113
+ }
114
+ /**
115
+ * Fetch file content from GitHub raw URL
116
+ * Uses raw.githubusercontent.com which has higher rate limits than the API
117
+ */
118
+ async function fetchFileContent(urlPath, debug) {
119
+ const url = `${GITHUB_RAW_BASE}/${urlPath}`;
120
+ printDebug(`Fetching file: ${url}`, debug);
121
+ try {
122
+ const response = await axios.get(url, {
123
+ headers: { 'User-Agent': 'nango-cli' },
124
+ responseType: 'text'
125
+ });
126
+ return response.data;
127
+ }
128
+ catch (err) {
129
+ if (axios.isAxiosError(err)) {
130
+ if (err.response?.status === 404) {
131
+ throw new Error(`File not found: ${urlPath}`);
132
+ }
133
+ if (err.response?.status === 403) {
134
+ throw new GitHubRateLimitError();
135
+ }
136
+ }
137
+ throw err;
138
+ }
139
+ }
140
+ /**
141
+ * Recursively fetch all files from a directory in the GitHub repository.
142
+ * Returns file paths relative to the integrations root (e.g., "github/actions/list-repos.ts")
143
+ */
144
+ async function fetchDirectoryRecursively(dirPath, debug) {
145
+ const files = [];
146
+ const contents = await fetchGitHubDirectory(dirPath, debug);
147
+ for (const item of contents) {
148
+ if (item.type === 'dir') {
149
+ const subFiles = await fetchDirectoryRecursively(item.path.replace(/^integrations\//, ''), debug);
150
+ files.push(...subFiles);
151
+ }
152
+ else if (item.type === 'file') {
153
+ const relativePath = item.path.replace(/^integrations\//, '');
154
+ // Determine if this is a script (in actions/syncs folders)
155
+ const isScript = /\/(actions|syncs)\/[^/]+\.ts$/.test(relativePath);
156
+ files.push({ relativePath, isScript });
157
+ }
158
+ }
159
+ return files;
160
+ }
161
+ /**
162
+ * Parse local imports from a TypeScript file content using Babel
163
+ * Returns relative import paths (starting with ./ or ../)
164
+ */
165
+ function parseLocalImports(content) {
166
+ const imports = [];
167
+ try {
168
+ const ast = parse(content, {
169
+ sourceType: 'module',
170
+ plugins: ['typescript']
171
+ });
172
+ const traverseFn = traverse.default || traverse;
173
+ traverseFn(ast, {
174
+ ImportDeclaration(nodePath) {
175
+ const source = nodePath.node.source.value;
176
+ // Only collect relative imports (starting with . or ..)
177
+ if (source.startsWith('./') || source.startsWith('../')) {
178
+ imports.push(source);
179
+ }
180
+ }
181
+ });
182
+ }
183
+ catch {
184
+ return [];
185
+ }
186
+ return imports;
187
+ }
188
+ /**
189
+ * Resolve a relative import path to an absolute path within the integration
190
+ * @param importPath - The import path (e.g., '../mappers/toCompany.js')
191
+ * @param fromFilePath - The file containing the import (e.g., 'hubspot/actions/create-company.ts')
192
+ * @returns The resolved path (e.g., 'hubspot/mappers/toCompany.ts') or null if outside integration
193
+ */
194
+ function resolveImportPath(importPath, fromFilePath) {
195
+ const fromDir = path.dirname(fromFilePath);
196
+ // Resolve the import path relative to the importing file's directory
197
+ // Convert .js extension to .ts (TypeScript source)
198
+ const tsPath = importPath.replace(/\.js$/, '.ts');
199
+ const resolved = path.normalize(path.join(fromDir, tsPath));
200
+ // Ensure the resolved path stays within the integration (no leading ..)
201
+ if (resolved.startsWith('..')) {
202
+ return null;
203
+ }
204
+ return resolved;
205
+ }
206
+ /**
207
+ * Recursively collect all dependencies for a set of files
208
+ * Also caches file content to avoid double-fetching
209
+ */
210
+ async function collectDependencies(initialFiles, integration, debug, contentCache) {
211
+ const allFiles = new Map();
212
+ const processedFiles = new Set();
213
+ const filesToProcess = [];
214
+ for (const file of initialFiles) {
215
+ allFiles.set(file.relativePath, file);
216
+ if (file.relativePath.endsWith('.ts')) {
217
+ filesToProcess.push(file.relativePath);
218
+ }
219
+ }
220
+ while (filesToProcess.length > 0) {
221
+ const filePath = filesToProcess.pop();
222
+ if (processedFiles.has(filePath)) {
223
+ continue;
224
+ }
225
+ processedFiles.add(filePath);
226
+ let content;
227
+ try {
228
+ if (contentCache.has(filePath)) {
229
+ content = contentCache.get(filePath);
230
+ }
231
+ else {
232
+ content = await fetchFileContent(filePath, debug);
233
+ contentCache.set(filePath, content);
234
+ }
235
+ }
236
+ catch {
237
+ printDebug(`Could not fetch ${filePath} for dependency analysis`, debug);
238
+ continue;
239
+ }
240
+ const imports = parseLocalImports(content);
241
+ printDebug(`Found ${imports.length} local imports in ${filePath}: ${imports.join(', ')}`, debug);
242
+ for (const importPath of imports) {
243
+ const resolvedPath = resolveImportPath(importPath, filePath);
244
+ if (!resolvedPath) {
245
+ printDebug(`Skipping import ${importPath} - resolves outside integration`, debug);
246
+ continue;
247
+ }
248
+ // Check if this is a file within our integration
249
+ if (!resolvedPath.startsWith(integration + '/')) {
250
+ printDebug(`Skipping import ${importPath} - outside integration folder`, debug);
251
+ continue;
252
+ }
253
+ if (allFiles.has(resolvedPath)) {
254
+ continue;
255
+ }
256
+ // Check if file exists on GitHub
257
+ try {
258
+ const depContent = await fetchFileContent(resolvedPath, debug);
259
+ contentCache.set(resolvedPath, depContent);
260
+ // Dependencies are never scripts (they're helpers/mappers/etc.)
261
+ allFiles.set(resolvedPath, { relativePath: resolvedPath, isScript: false });
262
+ filesToProcess.push(resolvedPath);
263
+ printDebug(`Added dependency: ${resolvedPath}`, debug);
264
+ }
265
+ catch {
266
+ printDebug(`Dependency not found on GitHub: ${resolvedPath}`, debug);
267
+ }
268
+ }
269
+ }
270
+ return Array.from(allFiles.values());
271
+ }
272
+ /**
273
+ * Get all files to clone based on the resolved template path, including dependencies
274
+ */
275
+ async function getFilesToClone(resolved, debug) {
276
+ const files = [];
277
+ const { type, path: resolvedPath, mdPath, integration } = resolved;
278
+ if (type === 'file') {
279
+ const isScript = /\/(actions|syncs)\/[^/]+\.ts$/.test(resolvedPath);
280
+ files.push({ relativePath: resolvedPath, isScript });
281
+ if (mdPath) {
282
+ files.push({ relativePath: mdPath, isScript: false });
283
+ }
284
+ }
285
+ else {
286
+ const dirFiles = await fetchDirectoryRecursively(resolvedPath, debug);
287
+ files.push(...dirFiles);
288
+ }
289
+ // Collect dependencies for all TypeScript files
290
+ const contentCache = new Map();
291
+ const filesWithDependencies = await collectDependencies(files, integration, debug, contentCache);
292
+ return { files: filesWithDependencies, contentCache };
293
+ }
294
+ /**
295
+ * Check for existing files to avoid accidental overwrites and prompt user if needed
296
+ */
297
+ async function checkExistingFiles(fullPath, files, force, autoConfirm, debug) {
298
+ const existingFiles = [];
299
+ const filesToSkip = new Set();
300
+ for (const file of files) {
301
+ const localPath = path.join(fullPath, file.relativePath);
302
+ if (fs.existsSync(localPath)) {
303
+ existingFiles.push(file.relativePath);
304
+ }
305
+ }
306
+ if (existingFiles.length === 0) {
307
+ return { proceed: true, filesToSkip };
308
+ }
309
+ if (force) {
310
+ printDebug(`Force mode: overwriting ${existingFiles.length} existing files`, debug);
311
+ return { proceed: true, filesToSkip };
312
+ }
313
+ console.log(chalk.yellow(`\nThe following files already exist:`));
314
+ for (const file of existingFiles) {
315
+ console.log(chalk.yellow(` - ${file}`));
316
+ }
317
+ if (autoConfirm) {
318
+ console.log(chalk.yellow(`Auto-confirm enabled: overwriting files`));
319
+ return { proceed: true, filesToSkip };
320
+ }
321
+ const answer = await promptly.prompt(chalk.yellow(`\nDo you want to overwrite these files? (yes/no/skip): `), { default: 'no' });
322
+ if (answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') {
323
+ return { proceed: true, filesToSkip };
324
+ }
325
+ if (answer.toLowerCase() === 'skip' || answer.toLowerCase() === 's') {
326
+ for (const file of existingFiles) {
327
+ filesToSkip.add(file);
328
+ }
329
+ return { proceed: true, filesToSkip };
330
+ }
331
+ return { proceed: false, filesToSkip };
332
+ }
333
+ /**
334
+ * Update the index.ts file with new imports
335
+ */
336
+ async function updateIndexFile(fullPath, files, debug) {
337
+ const indexPath = path.join(fullPath, 'index.ts');
338
+ let indexContent = '';
339
+ if (fs.existsSync(indexPath)) {
340
+ indexContent = await fs.promises.readFile(indexPath, 'utf-8');
341
+ }
342
+ const scriptFiles = files.filter((f) => f.isScript && f.relativePath.endsWith('.ts'));
343
+ const newImports = [];
344
+ for (const file of scriptFiles) {
345
+ // Convert path like "github/actions/list-repos.ts" to "./github/actions/list-repos.js"
346
+ const importPath = './' + file.relativePath.replace(/\.ts$/, '.js');
347
+ const importStatement = `import '${importPath}';`;
348
+ if (!indexContent.includes(importPath)) {
349
+ newImports.push(importStatement);
350
+ }
351
+ }
352
+ if (newImports.length > 0) {
353
+ printDebug(`Adding ${newImports.length} new imports to index.ts`, debug);
354
+ const updatedContent = indexContent.trim() + '\n' + newImports.join('\n') + '\n';
355
+ await fs.promises.writeFile(indexPath, updatedContent, 'utf-8');
356
+ }
357
+ }
358
+ /**
359
+ * Main clone function - clones integration templates from the integration-templates repository
360
+ */
361
+ export async function cloneTemplate(options) {
362
+ const { fullPath, templatePath, debug, force, autoConfirm } = options;
363
+ const spinner = ora({ text: `Resolving template path: ${templatePath}` }).start();
364
+ try {
365
+ const resolved = await resolveTemplatePath(templatePath, debug);
366
+ printDebug(`Resolved template path: ${JSON.stringify(resolved)}`, debug);
367
+ spinner.text = `Fetching file list for: ${templatePath}`;
368
+ const { files, contentCache } = await getFilesToClone(resolved, debug);
369
+ if (files.length === 0) {
370
+ spinner.fail(`No files found for template: ${templatePath}`);
371
+ return false;
372
+ }
373
+ printDebug(`Found ${files.length} files to clone`, debug);
374
+ spinner.succeed(`Found ${files.length} files to clone`);
375
+ const { proceed, filesToSkip } = await checkExistingFiles(fullPath, files, force, autoConfirm, debug);
376
+ if (!proceed) {
377
+ console.log(chalk.yellow('Clone cancelled.'));
378
+ return false;
379
+ }
380
+ const downloadSpinner = ora({ text: 'Downloading files...' }).start();
381
+ let downloadedCount = 0;
382
+ let skippedCount = 0;
383
+ for (const file of files) {
384
+ if (filesToSkip.has(file.relativePath)) {
385
+ skippedCount++;
386
+ continue;
387
+ }
388
+ downloadSpinner.text = `Downloading: ${file.relativePath}`;
389
+ try {
390
+ let content;
391
+ if (contentCache.has(file.relativePath)) {
392
+ content = contentCache.get(file.relativePath);
393
+ }
394
+ else {
395
+ content = await fetchFileContent(file.relativePath, debug);
396
+ }
397
+ const localPath = path.join(fullPath, file.relativePath);
398
+ // Ensure directory exists
399
+ await fs.promises.mkdir(path.dirname(localPath), { recursive: true });
400
+ await fs.promises.writeFile(localPath, content, 'utf-8');
401
+ downloadedCount++;
402
+ }
403
+ catch (err) {
404
+ downloadSpinner.fail(`Failed to download: ${file.relativePath}`);
405
+ if (err instanceof Error) {
406
+ console.log(chalk.red(` Error: ${err.message}`));
407
+ }
408
+ return false;
409
+ }
410
+ }
411
+ downloadSpinner.succeed(`Downloaded ${downloadedCount} files${skippedCount > 0 ? ` (${skippedCount} skipped)` : ''}`);
412
+ // Update index.ts
413
+ const indexSpinner = ora({ text: 'Updating index.ts...' }).start();
414
+ await updateIndexFile(fullPath, files.filter((f) => !filesToSkip.has(f.relativePath)), debug);
415
+ indexSpinner.succeed('Updated index.ts');
416
+ console.log('');
417
+ console.log(chalk.green(`Successfully cloned template: ${templatePath}`));
418
+ console.log(chalk.gray(`\nFiles written to: ${fullPath}`));
419
+ const scriptFiles = files.filter((f) => f.isScript && !filesToSkip.has(f.relativePath));
420
+ if (scriptFiles.length > 0) {
421
+ console.log(chalk.gray(`\nScripts added:`));
422
+ for (const file of scriptFiles) {
423
+ console.log(chalk.gray(` - ${file.relativePath}`));
424
+ }
425
+ }
426
+ console.log(`\nNext steps:`);
427
+ console.log(` 1. Review and customize the pulled scripts`);
428
+ console.log(` 2. Run 'nango dev' to compile and watch for changes`);
429
+ console.log(` 3. Run 'nango deploy <environment>' to deploy`);
430
+ return true;
431
+ }
432
+ catch (err) {
433
+ spinner.fail('Clone failed');
434
+ if (err instanceof Error) {
435
+ console.log(chalk.red(`\nError: ${err.message}`));
436
+ }
437
+ return false;
438
+ }
439
+ }
440
+ //# sourceMappingURL=clone.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clone.service.js","sourceRoot":"","sources":["../../lib/services/clone.service.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,6 @@ import * as vm from 'node:vm';
6
6
  import * as url from 'url';
7
7
  import { AxiosError } from 'axios';
8
8
  import chalk from 'chalk';
9
- import promptly from 'promptly';
10
9
  import { serializeError } from 'serialize-error';
11
10
  import * as unzipper from 'unzipper';
12
11
  import * as zod from 'zod';
@@ -14,7 +13,7 @@ import { ActionError, BASE_VARIANT, InvalidActionInputSDKError, InvalidActionOut
14
13
  import { parse } from './config.service.js';
15
14
  import { DiagnosticsMonitor, formatDiagnostics } from './diagnostics-monitor.service.js';
16
15
  import { loadSchemaJson } from './model.service.js';
17
- import * as responseSaver from './response-saver.service.js';
16
+ import { ResponseCollector } from './response-collector.service.js';
18
17
  import * as nangoScript from '../sdkScripts.js';
19
18
  import { displayValidationError } from '../utils/errors.js';
20
19
  import { getConfig, getConnection, hostport, parseSecretKey, printDebug } from '../utils.js';
@@ -194,8 +193,6 @@ export class DryRunService {
194
193
  }
195
194
  let stubbedMetadata = undefined;
196
195
  let normalizedInput;
197
- const saveResponsesDir = `${process.env['NANGO_MOCKS_RESPONSE_DIRECTORY'] ?? ''}${providerConfigKey}`;
198
- const saveResponsesSyncDir = `${saveResponsesDir}/mocks/${syncName}${syncVariant && syncVariant !== BASE_VARIANT ? `/${syncVariant}` : ''}`;
199
196
  if (actionInput) {
200
197
  if (actionInput.startsWith('@') && actionInput.endsWith('.json')) {
201
198
  const fileContents = readFile(actionInput);
@@ -249,6 +246,7 @@ export class DryRunService {
249
246
  console.log(chalk.red('Failed to load schema.json'));
250
247
  return;
251
248
  }
249
+ const responseCollector = new ResponseCollector();
252
250
  try {
253
251
  const syncConfig = {
254
252
  id: -1,
@@ -313,21 +311,8 @@ export class DryRunService {
313
311
  if (options.saveResponses) {
314
312
  nangoProps.axios = {
315
313
  response: {
316
- onFulfilled: (response) => responseSaver.onAxiosRequestFulfilled({
317
- response,
318
- providerConfigKey,
319
- connectionId: nangoConnection.connection_id,
320
- syncName,
321
- syncVariant,
322
- hasStubbedMetadata: Boolean(stubbedMetadata)
323
- }),
324
- onRejected: (error) => responseSaver.onAxiosRequestRejected({
325
- error,
326
- providerConfigKey,
327
- connectionId: nangoConnection.connection_id,
328
- syncName,
329
- syncVariant
330
- })
314
+ onFulfilled: (response) => responseCollector.onAxiosRequestFulfilled(response, nangoConnection.connection_id),
315
+ onRejected: (error) => responseCollector.onAxiosRequestRejected(error)
331
316
  }
332
317
  };
333
318
  }
@@ -354,20 +339,6 @@ export class DryRunService {
354
339
  console.error(err instanceof Error ? JSON.stringify(err, ['name', 'message'], 2) : JSON.stringify(err, null, 2));
355
340
  return;
356
341
  }
357
- // Save input and metadata only after validation passes
358
- if (options.saveResponses) {
359
- if (normalizedInput) {
360
- responseSaver.ensureDirectoryExists(saveResponsesSyncDir);
361
- const filePath = `${saveResponsesSyncDir}/input.json`;
362
- const dataToWrite = typeof normalizedInput === 'object' ? JSON.stringify(normalizedInput, null, 2) : normalizedInput;
363
- fs.writeFileSync(filePath, dataToWrite);
364
- }
365
- if (stubbedMetadata) {
366
- responseSaver.ensureDirectoryExists(`${saveResponsesDir}/mocks/nango`);
367
- const filePath = `${saveResponsesDir}/mocks/nango/getMetadata.json`;
368
- fs.writeFileSync(filePath, JSON.stringify(stubbedMetadata, null, 2));
369
- }
370
- }
371
342
  const resultOutput = [];
372
343
  if (type === 'actions') {
373
344
  if (!results.response) {
@@ -376,65 +347,35 @@ export class DryRunService {
376
347
  }
377
348
  else {
378
349
  console.log(JSON.stringify(results.response.output, null, 2));
379
- if (options.saveResponses) {
380
- responseSaver.ensureDirectoryExists(saveResponsesSyncDir);
381
- const filePath = `${saveResponsesSyncDir}/output.json`;
382
- const { nango, ...responseWithoutNango } = results.response;
383
- fs.writeFileSync(filePath, JSON.stringify(responseWithoutNango.output, null, 2));
384
- }
385
350
  resultOutput.push(JSON.stringify(results.response, null, 2));
386
351
  }
387
352
  }
388
- const logMessages = results.response?.nango && results.response.nango instanceof NangoSyncCLI && results.response.nango.logMessages;
389
- if (logMessages && logMessages.messages.length > 0) {
390
- const messages = logMessages.messages;
391
- let index = 0;
392
- const batchCount = 10;
393
- const displayBatch = () => {
394
- for (let i = 0; i < batchCount && index < messages.length; i++, index++) {
395
- const logs = messages[index];
396
- console.log(chalk.yellow(JSON.stringify(logs, null, 2)));
397
- resultOutput.push(JSON.stringify(logs, null, 2));
398
- }
399
- };
400
- console.log(chalk.yellow(`The dry run would produce the following results: ${JSON.stringify(logMessages.counts, null, 2)}`));
401
- resultOutput.push(`The dry run would produce the following results: ${JSON.stringify(logMessages.counts, null, 2)}`);
402
- console.log(chalk.yellow('The following log messages were generated:'));
403
- resultOutput.push('The following log messages were generated:');
404
- displayBatch();
405
- while (index < logMessages.messages.length) {
406
- const remaining = logMessages.messages.length - index;
407
- const confirmation = options.autoConfirm
408
- ? true
409
- : await promptly.confirm(`There are ${remaining} logs messages remaining. Would you like to see the next 10 log messages? (y/n)`);
410
- if (confirmation) {
411
- displayBatch();
412
- }
413
- else {
414
- break;
415
- }
353
+ const nangoInstance = results.response?.nango;
354
+ if (nangoInstance instanceof NangoSyncCLI) {
355
+ const logMessages = nangoInstance.logMessages;
356
+ if (logMessages && logMessages.messages.length > 0) {
357
+ // ... (rest of the logging logic)
416
358
  }
417
- if (options.saveResponses && results.response?.nango && results.response?.nango instanceof NangoSyncCLI) {
418
- const nango = results.response.nango;
359
+ if (options.saveResponses) {
419
360
  if (scriptInfo?.output) {
420
361
  for (const model of scriptInfo.output) {
421
- const modelFullName = nango.modelFullName(model);
422
- const modelDir = `${saveResponsesSyncDir}/${model}`;
423
- responseSaver.ensureDirectoryExists(modelDir);
424
- {
425
- const filePath = `${modelDir}/batchSave.json`;
426
- const modelData = nango.rawSaveOutput.get(modelFullName) || [];
427
- fs.writeFileSync(filePath, JSON.stringify(modelData, null, 2));
428
- }
429
- {
430
- const filePath = `${modelDir}/batchDelete.json`;
431
- const modelData = nango.rawDeleteOutput.get(modelFullName) || [];
432
- fs.writeFileSync(filePath, JSON.stringify(modelData, null, 2));
433
- }
362
+ const modelFullName = nangoInstance.modelFullName(model);
363
+ responseCollector.addBatchSave(modelFullName, nangoInstance.rawSaveOutput.get(modelFullName) || []);
364
+ responseCollector.addBatchDelete(modelFullName, nangoInstance.rawDeleteOutput.get(modelFullName) || []);
434
365
  }
435
366
  }
436
367
  }
437
368
  }
369
+ if (options.saveResponses) {
370
+ const testFilePath = `${this.fullPath}/${providerConfigKey}/tests/${syncName}.test.json`;
371
+ responseCollector.saveUnifiedMock({
372
+ filePath: testFilePath,
373
+ input: normalizedInput,
374
+ output: results.response?.output,
375
+ stubbedMetadata: stubbedMetadata
376
+ });
377
+ console.log(chalk.green(`\n✅ Mocks saved to ${testFilePath}`));
378
+ }
438
379
  if (this.returnOutput) {
439
380
  return resultOutput.join('\n');
440
381
  }