Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const compat = new FlatCompat({

export default tseslint.config(
{
ignores: [".next", "tests/**", "next-env.d.ts"],
ignores: [".next", "tests/**", "next-env.d.ts", "nango-integrations/**"],
},
...compat.extends("next/core-web-vitals"),
{
Expand Down
8 changes: 8 additions & 0 deletions nango-integrations/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Environment files
.env

# Nango compiled output
.nango/

# Node modules
node_modules/
74 changes: 74 additions & 0 deletions nango-integrations/build/github_actions_createIssue.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// nango-integrations/github/actions/createIssue.ts
var createIssue_exports = {};
__export(createIssue_exports, {
default: () => createIssue_default
});
module.exports = __toCommonJS(createIssue_exports);
var z = __toESM(require("zod"), 1);
var issueSchema = z.object({
id: z.string(),
owner: z.string(),
repo: z.string(),
issue_number: z.number(),
title: z.string(),
state: z.string(),
author: z.string(),
author_id: z.number(),
body: z.string(),
date_created: z.string(),
date_last_modified: z.string()
});
var action = {
type: "action",
description: `Create an issue in GitHub`,
version: "1.0.0",
endpoint: {
method: "POST",
path: "/example/github/issues",
group: "Issues"
},
input: issueSchema,
output: z.void(),
// Action execution
exec: async (nango, input) => {
await nango.proxy({
endpoint: "/repos/NangoHQ/interactive-demo/issues",
data: {
title: `[demo] ${input.title}`,
body: `This issue was created automatically using Nango Action.`,
labels: ["automatic"]
}
});
}
};
var createIssue_default = action;
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibmFuZ28taW50ZWdyYXRpb25zL2dpdGh1Yi9hY3Rpb25zL2NyZWF0ZUlzc3VlLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgeyBjcmVhdGVBY3Rpb24gfSBmcm9tICduYW5nbyc7XG5pbXBvcnQgKiBhcyB6IGZyb20gJ3pvZCc7XG5jb25zdCBpc3N1ZVNjaGVtYSA9IHoub2JqZWN0KHtcbiAgaWQ6IHouc3RyaW5nKCksXG4gIG93bmVyOiB6LnN0cmluZygpLFxuICByZXBvOiB6LnN0cmluZygpLFxuICBpc3N1ZV9udW1iZXI6IHoubnVtYmVyKCksXG4gIHRpdGxlOiB6LnN0cmluZygpLFxuICBzdGF0ZTogei5zdHJpbmcoKSxcbiAgYXV0aG9yOiB6LnN0cmluZygpLFxuICBhdXRob3JfaWQ6IHoubnVtYmVyKCksXG4gIGJvZHk6IHouc3RyaW5nKCksXG4gIGRhdGVfY3JlYXRlZDogei5zdHJpbmcoKSxcbiAgZGF0ZV9sYXN0X21vZGlmaWVkOiB6LnN0cmluZygpXG59KTtcbmNvbnN0IGFjdGlvbiA9IHtcbiAgdHlwZTogXCJhY3Rpb25cIixcbiAgZGVzY3JpcHRpb246IGBDcmVhdGUgYW4gaXNzdWUgaW4gR2l0SHViYCxcbiAgdmVyc2lvbjogJzEuMC4wJyxcbiAgZW5kcG9pbnQ6IHtcbiAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICBwYXRoOiAnL2V4YW1wbGUvZ2l0aHViL2lzc3VlcycsXG4gICAgZ3JvdXA6ICdJc3N1ZXMnXG4gIH0sXG4gIGlucHV0OiBpc3N1ZVNjaGVtYSxcbiAgb3V0cHV0OiB6LnZvaWQoKSxcbiAgLy8gQWN0aW9uIGV4ZWN1dGlvblxuICBleGVjOiBhc3luYyAobmFuZ28sIGlucHV0KSA9PiB7XG4gICAgYXdhaXQgbmFuZ28ucHJveHkoe1xuICAgICAgZW5kcG9pbnQ6ICcvcmVwb3MvTmFuZ29IUS9pbnRlcmFjdGl2ZS1kZW1vL2lzc3VlcycsXG4gICAgICBkYXRhOiB7XG4gICAgICAgIHRpdGxlOiBgW2RlbW9dICR7aW5wdXQudGl0bGV9YCxcbiAgICAgICAgYm9keTogYFRoaXMgaXNzdWUgd2FzIGNyZWF0ZWQgYXV0b21hdGljYWxseSB1c2luZyBOYW5nbyBBY3Rpb24uYCxcbiAgICAgICAgbGFiZWxzOiBbJ2F1dG9tYXRpYyddXG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn07XG5leHBvcnQgdHlwZSBOYW5nb0FjdGlvbkxvY2FsID0gUGFyYW1ldGVyczwodHlwZW9mIGFjdGlvbilbJ2V4ZWMnXT5bMF07XG5leHBvcnQgZGVmYXVsdCBhY3Rpb247Il0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUNBLFFBQW1CO0FBQ25CLElBQU0sY0FBZ0IsU0FBTztBQUFBLEVBQzNCLElBQU0sU0FBTztBQUFBLEVBQ2IsT0FBUyxTQUFPO0FBQUEsRUFDaEIsTUFBUSxTQUFPO0FBQUEsRUFDZixjQUFnQixTQUFPO0FBQUEsRUFDdkIsT0FBUyxTQUFPO0FBQUEsRUFDaEIsT0FBUyxTQUFPO0FBQUEsRUFDaEIsUUFBVSxTQUFPO0FBQUEsRUFDakIsV0FBYSxTQUFPO0FBQUEsRUFDcEIsTUFBUSxTQUFPO0FBQUEsRUFDZixjQUFnQixTQUFPO0FBQUEsRUFDdkIsb0JBQXNCLFNBQU87QUFDL0IsQ0FBQztBQUNELElBQU0sU0FBUztBQUFBLEVBQ2IsTUFBTTtBQUFBLEVBQ04sYUFBYTtBQUFBLEVBQ2IsU0FBUztBQUFBLEVBQ1QsVUFBVTtBQUFBLElBQ1IsUUFBUTtBQUFBLElBQ1IsTUFBTTtBQUFBLElBQ04sT0FBTztBQUFBLEVBQ1Q7QUFBQSxFQUNBLE9BQU87QUFBQSxFQUNQLFFBQVUsT0FBSztBQUFBO0FBQUEsRUFFZixNQUFNLE9BQU8sT0FBTyxVQUFVO0FBQzVCLFVBQU0sTUFBTSxNQUFNO0FBQUEsTUFDaEIsVUFBVTtBQUFBLE1BQ1YsTUFBTTtBQUFBLFFBQ0osT0FBTyxVQUFVLE1BQU0sS0FBSztBQUFBLFFBQzVCLE1BQU07QUFBQSxRQUNOLFFBQVEsQ0FBQyxXQUFXO0FBQUEsTUFDdEI7QUFBQSxJQUNGLENBQUM7QUFBQSxFQUNIO0FBQ0Y7QUFFQSxJQUFPLHNCQUFROyIsCiAgIm5hbWVzIjogW10KfQo=
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// nango-integrations/github/on-events/pre-connection-deletion.ts
var pre_connection_deletion_exports = {};
__export(pre_connection_deletion_exports, {
default: () => pre_connection_deletion_default,
onEvent: () => onEvent
});
module.exports = __toCommonJS(pre_connection_deletion_exports);
var onEvent = {
type: "onEvent",
event: "pre-connection-deletion",
// 'post-connection-creation' | 'validate-connection'
description: "This script is executed before a connection is deleted",
exec: async (nango) => {
await nango.log("Executed");
}
};
var pre_connection_deletion_default = onEvent;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
onEvent
});
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibmFuZ28taW50ZWdyYXRpb25zL2dpdGh1Yi9vbi1ldmVudHMvcHJlLWNvbm5lY3Rpb24tZGVsZXRpb24udHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImltcG9ydCB7IGNyZWF0ZU9uRXZlbnQgfSBmcm9tICduYW5nbyc7XG5leHBvcnQgY29uc3Qgb25FdmVudCA9IHtcbiAgdHlwZTogXCJvbkV2ZW50XCIsXG4gIGV2ZW50OiAncHJlLWNvbm5lY3Rpb24tZGVsZXRpb24nLFxuICAvLyAncG9zdC1jb25uZWN0aW9uLWNyZWF0aW9uJyB8ICd2YWxpZGF0ZS1jb25uZWN0aW9uJ1xuICBkZXNjcmlwdGlvbjogJ1RoaXMgc2NyaXB0IGlzIGV4ZWN1dGVkIGJlZm9yZSBhIGNvbm5lY3Rpb24gaXMgZGVsZXRlZCcsXG4gIGV4ZWM6IGFzeW5jIG5hbmdvID0+IHtcbiAgICBhd2FpdCBuYW5nby5sb2coJ0V4ZWN1dGVkJyk7XG4gIH1cbn07XG5leHBvcnQgZGVmYXVsdCBvbkV2ZW50OyJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ08sSUFBTSxVQUFVO0FBQUEsRUFDckIsTUFBTTtBQUFBLEVBQ04sT0FBTztBQUFBO0FBQUEsRUFFUCxhQUFhO0FBQUEsRUFDYixNQUFNLE9BQU0sVUFBUztBQUNuQixVQUFNLE1BQU0sSUFBSSxVQUFVO0FBQUEsRUFDNUI7QUFDRjtBQUNBLElBQU8sa0NBQVE7IiwKICAibmFtZXMiOiBbXQp9Cg==
Comment on lines +1 to +41
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Nango integration framework build artifacts deployment requirements

💡 Result:

Summary of key build-artifact / deployment requirements for Nango integrations:

  • Nango CLI + project layout: install the nango CLI (npm) and keep integrations in a version-controlled nango-integrations/ folder (nango init creates structure). [1][2]
  • Runtime tooling: Node.js/npm for building functions, and Docker (or Docker Compose) when using local/self‑hosted Nango or sample apps. [2][3][4]
  • Environment variables / secrets: provide NANGO_SECRET_KEY_DEV and NANGO_SECRET_KEY_PROD for CLI auth, set NANGO_HOSTPORT to your instance URL for self‑hosted, and any .env in ./nango-integrations with these values. CI deploys must supply those secrets. [1][2]
  • Encryption key (self-hosted): a persistent NANGO_ENCRYPTION_KEY is required for self‑hosted deployments (cannot be re‑generated later). [5]
  • Deployment commands / CI: use nango deploy (can target envs like dev/prod); CI should install nango CLI, load env secrets, and may pass flags like --auto-confirm or --allow-destructive when needed. [1][2][5]
  • Ports / infra (self-hosted): default OSS ports (server 3003, Postgres 5432) when using docker-compose; adapt as needed in .env/docker-compose. [4]
  • Best practices: keep integrations in source control, include .env templates, and automate deploys in CI with appropriate secret management. [2][3]

Sources: Nango CLI & env docs; Functions/setup and deploy guides; Sample app deployment; Self-hosting (docker compose); note on NANGO_ENCRYPTION_KEY. [1][2][3][4][5]


🏁 Script executed:

# Check if there are other similar compiled artifacts in the build directory
fd -e cjs nango-integrations/build/ | head -20

Repository: drifter089/orgOS

Length of output: 465


🏁 Script executed:

# Look for build configuration and CI/CD setup
fd -e json -e yaml -e yml -e toml . | grep -E "(package\.json|tsconfig|esbuild|webpack|build|ci)" | head -30

Repository: drifter089/orgOS

Length of output: 151


🏁 Script executed:

# Search for documentation about nango-integrations structure
find . -name "README*" -o -name "*.md" | xargs grep -l "nango-integrations\|build artifact" 2>/dev/null | head -10

Repository: drifter089/orgOS

Length of output: 42


🏁 Script executed:

# Explore nango-integrations directory structure
ls -la nango-integrations/ 2>/dev/null | head -20

Repository: drifter089/orgOS

Length of output: 620


🏁 Script executed:

# Check the actual build directory
ls -la nango-integrations/build/ 2>/dev/null | head -30

Repository: drifter089/orgOS

Length of output: 423


🏁 Script executed:

# Check package.json build scripts
cat nango-integrations/package.json | head -50

Repository: drifter089/orgOS

Length of output: 342


🏁 Script executed:

# Check .gitignore for build patterns
cat .gitignore 2>/dev/null

Repository: drifter089/orgOS

Length of output: 874


🏁 Script executed:

# Look for nango CLI config (nango.yaml or similar)
find nango-integrations -name "nango.yaml" -o -name "nango.yml" -o -name ".nangorc*" 2>/dev/null

Repository: drifter089/orgOS

Length of output: 42


🏁 Script executed:

# Verify the nango-integrations/.gitignore specifically
cat nango-integrations/.gitignore

Repository: drifter089/orgOS

Length of output: 147


🏁 Script executed:

# Check if build files are tracked by git
cd nango-integrations && git ls-files build/ 2>/dev/null || echo "Not in git or error"

Repository: drifter089/orgOS

Length of output: 182


🏁 Script executed:

# Look at the source TypeScript files
find nango-integrations/github -name "*.ts" -type f | head -10

Repository: drifter089/orgOS

Length of output: 218


🏁 Script executed:

# Check git history to see if build files were recently added
git log --oneline nango-integrations/build/ 2>/dev/null | head -5

Repository: drifter089/orgOS

Length of output: 127


🏁 Script executed:

# Verify the compilation flow - does nango compile generate these files?
cat nango-integrations/tsconfig.json

Repository: drifter089/orgOS

Length of output: 986


Remove committed build artifacts from the nango-integrations/build directory.

The compiled .cjs files in nango-integrations/build/ are build artifacts generated by nango compile from TypeScript sources. Nango's own tsconfig.json explicitly excludes the build directory, indicating these files should not be in version control. They are regenerated during deployment via nango deploy. Remove all .cjs files from nango-integrations/build/ and add build/ to nango-integrations/.gitignore. Keep only the source .ts files in nango-integrations/github/.

🤖 Prompt for AI Agents
In nango-integrations/build/github_on-events_pre-connection-deletion.cjs (lines
1-41): this is a compiled build artifact that should not be committed; remove
this .cjs file (and any other files under nango-integrations/build/), add build/
to nango-integrations/.gitignore, ensure only the original TypeScript source
files remain under nango-integrations/github/, and update the commit so the
build output is no longer tracked.

119 changes: 119 additions & 0 deletions nango-integrations/build/github_syncs_fetchIssues.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// nango-integrations/github/syncs/fetchIssues.ts
var fetchIssues_exports = {};
__export(fetchIssues_exports, {
default: () => fetchIssues_default
});
module.exports = __toCommonJS(fetchIssues_exports);
var z = __toESM(require("zod"), 1);
var LIMIT = 100;
var issueSchema = z.object({
id: z.string(),
owner: z.string(),
repo: z.string(),
issue_number: z.number(),
title: z.string(),
state: z.string(),
author: z.string(),
author_id: z.number(),
body: z.string(),
date_created: z.string(),
date_last_modified: z.string()
});
Comment on lines +38 to +50
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find the source TypeScript file
find . -name "fetchIssues.ts" -type f

Repository: drifter089/orgOS

Length of output: 108


🌐 Web query:

GitHub REST API issue object body field null

💡 Result:

Common causes

  • The issue really has no body (empty) — then the JSON "body" is null.
  • You're requesting the default/other media type that doesn't return the raw "body" field; GitHub exposes different representations (body, body_text, body_html) only when the appropriate Accept media type is used.
    What to do
  • Verify the issue actually has text in the web UI.
  • Request the body explicitly by sending an Accept header such as:
    • application/vnd.github.raw+json (returns body)
    • application/vnd.github.text+json (returns body_text)
    • application/vnd.github.html+json (returns body_html)
    • application/vnd.github.full+json (returns body, body_text, body_html)
      References
  • GitHub REST issues docs (media types & body fields). [1] [2]

Sources
[1] REST API endpoints for issues — GitHub Docs. [turn0search0]
[2] REST API endpoints for issues (Enterprise) — GitHub Docs. [turn0search3]


🏁 Script executed:

cat -n ./nango-integrations/github/syncs/fetchIssues.ts

Repository: drifter089/orgOS

Length of output: 3295


Fix nullable body field in schema.

The body field is defined as z.string(), but GitHub's API returns null for issues without a description. This causes validation failures when syncing such issues. Update the schema in the source TypeScript file:

- body: z.string(),
+ body: z.string().nullable(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var issueSchema = z.object({
id: z.string(),
owner: z.string(),
repo: z.string(),
issue_number: z.number(),
title: z.string(),
state: z.string(),
author: z.string(),
author_id: z.number(),
body: z.string(),
date_created: z.string(),
date_last_modified: z.string()
});
var issueSchema = z.object({
id: z.string(),
owner: z.string(),
repo: z.string(),
issue_number: z.number(),
title: z.string(),
state: z.string(),
author: z.string(),
author_id: z.number(),
body: z.string().nullable(),
date_created: z.string(),
date_last_modified: z.string()
});
🤖 Prompt for AI Agents
In nango-integrations/build/github_syncs_fetchIssues.cjs around lines 38 to 50,
the issueSchema defines body as z.string() which fails when GitHub returns null;
replace the body schema with a nullable string (e.g., change body: z.string() to
body: z.string().nullable()) so validation accepts both string and null; update
the source TypeScript schema (not just the built CJS) and rebuild to propagate
the change.

var sync = {
type: "sync",
description: `Fetches the Github issues from all a user's repositories.`,
version: "1.0.0",
endpoints: [{
method: "GET",
path: "/example/github/issues",
group: "Issues"
}],
frequency: "every hour",
autoStart: true,
syncType: "full",
metadata: z.void(),
models: {
GithubIssue: issueSchema
},
// Sync execution
exec: async (nango) => {
const repos = await getAllRepositories(nango);
for (const repo of repos) {
const proxyConfig = {
endpoint: `/repos/${repo.owner.login}/${repo.name}/issues`,
paginate: {
limit: LIMIT
}
};
for await (const issueBatch of nango.paginate(proxyConfig)) {
const issues = issueBatch.filter((issue) => !("pull_request" in issue));
const mappedIssues = issues.map((issue) => ({
id: issue.id,
owner: repo.owner.login,
repo: repo.name,
issue_number: issue.number,
title: issue.title,
state: issue.state,
author: issue.user.login,
author_id: issue.user.id,
body: issue.body,
date_created: issue.created_at,
date_last_modified: issue.updated_at
}));
if (mappedIssues.length > 0) {
await nango.batchSave(mappedIssues, "GithubIssue");
await nango.log(`Sent ${mappedIssues.length} issues from ${repo.owner.login}/${repo.name}`);
}
}
}
Comment on lines +68 to +97
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling for individual repository failures.

The sync iterates through all repositories without try-catch blocks. If fetching issues from one repository fails (e.g., due to permissions or API errors), the entire sync will fail and subsequent repositories won't be processed.

Consider wrapping the per-repository logic in try-catch in the source TypeScript file:

for (const repo of repos) {
  try {
    const proxyConfig = { /* ... */ };
    for await (const issueBatch of nango.paginate(proxyConfig)) {
      // ... existing logic
    }
  } catch (error) {
    await nango.log(`Failed to fetch issues from ${repo.owner.login}/${repo.name}: ${error}`);
    // Continue with next repository
  }
}
🤖 Prompt for AI Agents
In nango-integrations/build/github_syncs_fetchIssues.cjs around lines 68 to 97,
the loop over repos lacks per-repository error handling so a failure fetching
one repo's issues can abort the whole sync; wrap the logic that builds
proxyConfig and the for-await-of nango.paginate(...) processing (including
filtering, mapping, batchSave and log) in a try-catch that on catch awaits
nango.log with a message containing the repo owner/name and the caught error,
then continues to the next repo so other repositories still get processed.

await nango.deleteRecordsFromPreviousExecutions("GithubIssue");
},
// Webhook handler
onWebhook: async (nango, payload) => {
await nango.log("This is a webhook script", payload);
}
};
var fetchIssues_default = sync;
async function getAllRepositories(nango) {
const records = [];
const proxyConfig = {
endpoint: "/user/repos",
paginate: {
limit: LIMIT
}
};
for await (const recordBatch of nango.paginate(proxyConfig)) {
records.push(...recordBatch);
}
return records;
}
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibmFuZ28taW50ZWdyYXRpb25zL2dpdGh1Yi9zeW5jcy9mZXRjaElzc3Vlcy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiaW1wb3J0IHsgY3JlYXRlU3luYyB9IGZyb20gJ25hbmdvJztcbmltcG9ydCAqIGFzIHogZnJvbSAnem9kJztcbmNvbnN0IExJTUlUID0gMTAwO1xuY29uc3QgaXNzdWVTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIGlkOiB6LnN0cmluZygpLFxuICBvd25lcjogei5zdHJpbmcoKSxcbiAgcmVwbzogei5zdHJpbmcoKSxcbiAgaXNzdWVfbnVtYmVyOiB6Lm51bWJlcigpLFxuICB0aXRsZTogei5zdHJpbmcoKSxcbiAgc3RhdGU6IHouc3RyaW5nKCksXG4gIGF1dGhvcjogei5zdHJpbmcoKSxcbiAgYXV0aG9yX2lkOiB6Lm51bWJlcigpLFxuICBib2R5OiB6LnN0cmluZygpLFxuICBkYXRlX2NyZWF0ZWQ6IHouc3RyaW5nKCksXG4gIGRhdGVfbGFzdF9tb2RpZmllZDogei5zdHJpbmcoKVxufSk7XG50eXBlIEdpdGh1Yklzc3VlID0gei5pbmZlcjx0eXBlb2YgaXNzdWVTY2hlbWE+O1xuY29uc3Qgc3luYyA9IHtcbiAgdHlwZTogXCJzeW5jXCIsXG4gIGRlc2NyaXB0aW9uOiBgRmV0Y2hlcyB0aGUgR2l0aHViIGlzc3VlcyBmcm9tIGFsbCBhIHVzZXIncyByZXBvc2l0b3JpZXMuYCxcbiAgdmVyc2lvbjogJzEuMC4wJyxcbiAgZW5kcG9pbnRzOiBbe1xuICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgcGF0aDogJy9leGFtcGxlL2dpdGh1Yi9pc3N1ZXMnLFxuICAgIGdyb3VwOiAnSXNzdWVzJ1xuICB9XSxcbiAgZnJlcXVlbmN5OiAnZXZlcnkgaG91cicsXG4gIGF1dG9TdGFydDogdHJ1ZSxcbiAgc3luY1R5cGU6ICdmdWxsJyxcbiAgbWV0YWRhdGE6IHoudm9pZCgpLFxuICBtb2RlbHM6IHtcbiAgICBHaXRodWJJc3N1ZTogaXNzdWVTY2hlbWFcbiAgfSxcbiAgLy8gU3luYyBleGVjdXRpb25cbiAgZXhlYzogYXN5bmMgbmFuZ28gPT4ge1xuICAgIGNvbnN0IHJlcG9zID0gYXdhaXQgZ2V0QWxsUmVwb3NpdG9yaWVzKG5hbmdvKTtcbiAgICBmb3IgKGNvbnN0IHJlcG8gb2YgcmVwb3MpIHtcbiAgICAgIGNvbnN0IHByb3h5Q29uZmlnID0ge1xuICAgICAgICBlbmRwb2ludDogYC9yZXBvcy8ke3JlcG8ub3duZXIubG9naW59LyR7cmVwby5uYW1lfS9pc3N1ZXNgLFxuICAgICAgICBwYWdpbmF0ZToge1xuICAgICAgICAgIGxpbWl0OiBMSU1JVFxuICAgICAgICB9XG4gICAgICB9O1xuICAgICAgZm9yIGF3YWl0IChjb25zdCBpc3N1ZUJhdGNoIG9mIG5hbmdvLnBhZ2luYXRlKHByb3h5Q29uZmlnKSkge1xuICAgICAgICBjb25zdCBpc3N1ZXMgPSBpc3N1ZUJhdGNoLmZpbHRlcihpc3N1ZSA9PiAhKCdwdWxsX3JlcXVlc3QnIGluIGlzc3VlKSk7XG4gICAgICAgIGNvbnN0IG1hcHBlZElzc3VlczogR2l0aHViSXNzdWVbXSA9IGlzc3Vlcy5tYXAoaXNzdWUgPT4gKHtcbiAgICAgICAgICBpZDogaXNzdWUuaWQsXG4gICAgICAgICAgb3duZXI6IHJlcG8ub3duZXIubG9naW4sXG4gICAgICAgICAgcmVwbzogcmVwby5uYW1lLFxuICAgICAgICAgIGlzc3VlX251bWJlcjogaXNzdWUubnVtYmVyLFxuICAgICAgICAgIHRpdGxlOiBpc3N1ZS50aXRsZSxcbiAgICAgICAgICBzdGF0ZTogaXNzdWUuc3RhdGUsXG4gICAgICAgICAgYXV0aG9yOiBpc3N1ZS51c2VyLmxvZ2luLFxuICAgICAgICAgIGF1dGhvcl9pZDogaXNzdWUudXNlci5pZCxcbiAgICAgICAgICBib2R5OiBpc3N1ZS5ib2R5LFxuICAgICAgICAgIGRhdGVfY3JlYXRlZDogaXNzdWUuY3JlYXRlZF9hdCxcbiAgICAgICAgICBkYXRlX2xhc3RfbW9kaWZpZWQ6IGlzc3VlLnVwZGF0ZWRfYXRcbiAgICAgICAgfSkpO1xuICAgICAgICBpZiAobWFwcGVkSXNzdWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBhd2FpdCBuYW5nby5iYXRjaFNhdmUobWFwcGVkSXNzdWVzLCAnR2l0aHViSXNzdWUnKTtcbiAgICAgICAgICBhd2FpdCBuYW5nby5sb2coYFNlbnQgJHttYXBwZWRJc3N1ZXMubGVuZ3RofSBpc3N1ZXMgZnJvbSAke3JlcG8ub3duZXIubG9naW59LyR7cmVwby5uYW1lfWApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIGF3YWl0IG5hbmdvLmRlbGV0ZVJlY29yZHNGcm9tUHJldmlvdXNFeGVjdXRpb25zKCdHaXRodWJJc3N1ZScpO1xuICB9LFxuICAvLyBXZWJob29rIGhhbmRsZXJcbiAgb25XZWJob29rOiBhc3luYyAobmFuZ28sIHBheWxvYWQpID0+IHtcbiAgICBhd2FpdCBuYW5nby5sb2coJ1RoaXMgaXMgYSB3ZWJob29rIHNjcmlwdCcsIHBheWxvYWQpO1xuICB9XG59O1xuZXhwb3J0IHR5cGUgTmFuZ29TeW5jTG9jYWwgPSBQYXJhbWV0ZXJzPCh0eXBlb2Ygc3luYylbJ2V4ZWMnXT5bMF07XG5leHBvcnQgZGVmYXVsdCBzeW5jO1xuYXN5bmMgZnVuY3Rpb24gZ2V0QWxsUmVwb3NpdG9yaWVzKG5hbmdvOiBOYW5nb1N5bmNMb2NhbCk6IFByb21pc2U8YW55W10+IHtcbiAgY29uc3QgcmVjb3JkczogYW55W10gPSBbXTtcbiAgY29uc3QgcHJveHlDb25maWcgPSB7XG4gICAgZW5kcG9pbnQ6ICcvdXNlci9yZXBvcycsXG4gICAgcGFnaW5hdGU6IHtcbiAgICAgIGxpbWl0OiBMSU1JVFxuICAgIH1cbiAgfTtcbiAgZm9yIGF3YWl0IChjb25zdCByZWNvcmRCYXRjaCBvZiBuYW5nby5wYWdpbmF0ZShwcm94eUNvbmZpZykpIHtcbiAgICByZWNvcmRzLnB1c2goLi4ucmVjb3JkQmF0Y2gpO1xuICB9XG4gIHJldHVybiByZWNvcmRzO1xufSJd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
39 changes: 39 additions & 0 deletions nango-integrations/github/actions/createIssue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createAction } from "nango";
import * as z from "zod";

const issueSchema = z.object({
id: z.string(),
owner: z.string(),
repo: z.string(),
issue_number: z.number(),
title: z.string(),
state: z.string(),
author: z.string(),
author_id: z.number(),
body: z.string(),
date_created: z.string(),
date_last_modified: z.string(),
});

const action = createAction({
description: `Create an issue in GitHub`,
version: "1.0.0",
endpoint: { method: "POST", path: "/example/github/issues", group: "Issues" },
input: issueSchema,
output: z.void(),

// Action execution
exec: async (nango, input) => {
await nango.proxy({
endpoint: "/repos/NangoHQ/interactive-demo/issues",
data: {
title: `[demo] ${input.title}`,
body: `This issue was created automatically using Nango Action.`,
labels: ["automatic"],
},
});
},
});

export type NangoActionLocal = Parameters<(typeof action)["exec"]>[0];
export default action;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createOnEvent } from "nango";

export default createOnEvent({
event: "pre-connection-deletion", // 'post-connection-creation' | 'validate-connection'
description: "This script is executed before a connection is deleted",
exec: async (nango) => {
await nango.log("Executed");
},
});
Loading
Loading