Skip to content

feat: add ability to download deployed functions in debug mode#5657

Open
kaposke wants to merge 4 commits intomasterfrom
gui/NAN-5014/feat-download-script
Open

feat: add ability to download deployed functions in debug mode#5657
kaposke wants to merge 4 commits intomasterfrom
gui/NAN-5014/feat-download-script

Conversation

@kaposke
Copy link
Contributor

@kaposke kaposke commented Mar 17, 2026

  • Migrates old download endpoint to new format.
  • Changes route and simplifies parameters.
  • It only works for deployed functions
  • Conditionally render download icon besides toggle switch when in debug mode (when impersonating, and function is deployed).
  • It downloads the typescript file, not the bundled js.
image

The new download flow also adds server-side parameter validation and provider config key resolution, streams a zip via local or remote file services, introduces a download webhook and API typing, and expands the bundle to include nango.yaml plus both bundled JS and TypeScript sources when available.


This summary was automatically generated by @propel-code-bot

@kaposke kaposke requested review from a team and TBonnin March 17, 2026 18:58
@linear
Copy link

linear bot commented Mar 17, 2026

propel-code-bot[bot]

This comment was marked as outdated.

Copy link
Collaborator

@TBonnin TBonnin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It downloads the typescript file, not the bundled js.

I would really prefer to get the bundled js. Most of the involved function are using some imported logic. Only having the ts entrypoint will be useless in those cases


export const validationParams = z
.object({
id: z.coerce.number().positive()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about syncConfigId? it is not immediately clear which id we are talking about here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably leave it as id to keep consistency with other flow routes (/flows/:id/download). I'll rename it when destructuring thought to make it more clear

return result.id;
}

async getProviderConfigKeyById(environment_id: number, id: number): Promise<string | null> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's have all new function return a Result

@kaposke kaposke requested a review from TBonnin March 17, 2026 20:02
Copy link
Contributor

@propel-code-bot propel-code-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes suggested to ensure correct file resolution and align download contents with TS-only intent.

Status: Changes Suggested | Risk: Medium

Issues Identified & Suggestions
  • Use actual flow type for file path resolution: local.service.ts
  • Exclude bundled JS if TS-only download is intended: remote.service.ts
Review Details

📁 9 files reviewed | 💬 2 comments

Instruction Files
└── .claude/
    ├── agents/
    │   └── nango-docs-migrator.md
    └── skills/
        ├── agent-builder-skill/
        │   ├── EXAMPLES.md
        │   └── SKILL.md
        ├── creating-integration-docs/
        │   └── SKILL.md
        └── creating-skills-skill/
            └── SKILL.md

👍 / 👎 individual comments to help improve reviews for you


const scriptName = syncConfig.sync_name;

const jsFilePath = this.resolveIntegrationFile({ scriptType: 'sync', syncConfig, providerConfigKey });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

[Logic] The local download path hard-codes scriptType: 'sync'. For action flows this resolves to the syncs directory and the JS file lookup will fail. Use the flow’s actual type so actions resolve correctly.

Suggested change
const jsFilePath = this.resolveIntegrationFile({ scriptType: 'sync', syncConfig, providerConfigKey });
const jsFilePath = this.resolveIntegrationFile({ scriptType: syncConfig.type, syncConfig, providerConfigKey });
Context for Agents
The local download path hard-codes `scriptType: 'sync'`. For action flows this resolves to the syncs directory and the JS file lookup will fail. Use the flow’s actual type so actions resolve correctly.

```suggestion
        const jsFilePath = this.resolveIntegrationFile({ scriptType: syncConfig.type, syncConfig, providerConfigKey });
```

File: packages/shared/lib/services/file/local.service.ts
Line: 119

const { success: tsSuccess, error: tsError, response: tsFile } = await this.getStream(`${integrationFileLocation}/${scriptName}.ts`);
const scriptName = syncConfig.sync_name;

const jsFileLocation = syncConfig.file_location;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

[Requirements] This adds the compiled .js artifact to the download zip. The PR intent states the download should be the TypeScript source only; this block will include the bundled JS and change the payload size/contents. If TS-only is the intended behavior, remove the JS file inclusion here (and the corresponding JS block in LocalFileService.zipAndSendFlow).

Context for Agents
This adds the compiled `.js` artifact to the download zip. The PR intent states the download should be the TypeScript source only; this block will include the bundled JS and change the payload size/contents. If TS-only is the intended behavior, remove the JS file inclusion here (and the corresponding JS block in `LocalFileService.zipAndSendFlow`).

File: packages/shared/lib/services/file/remote.service.ts
Line: 257

web.route('/flows/:id/disable').patch(webAuth, patchFlowDisable);
web.route('/flows/:id/enable').patch(webAuth, patchFlowEnable);
web.route('/flows/:id/frequency').patch(webAuth, patchFlowFrequency);
web.route('/flows/:id/download').post(webAuth, postFlowDownload);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you know why it was a POST. Looks more like a GET to me?

if (!func || !func.enabled || !func.id) {
return;
}
await apiFlowDownload(env, { id: func.id }, func.name);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happen if apiFlowDownload fails?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants