Skip to content
Merged
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
8 changes: 4 additions & 4 deletions .github/workflows/n8n.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: 22

Expand Down Expand Up @@ -91,13 +91,13 @@ jobs:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: 22
registry-url: "https://registry.npmjs.org"
Expand Down
6 changes: 6 additions & 0 deletions integrations/n8n/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 2.0.2 (2026-04-07)

### Bug Fixes

- Fix Form JSON mode crash — `formTitle` hidden in JSON mode but required by bodyMap; now extracts `title`, `close_text`, `submit_text` from JSON payload

## 2.0.1 (2026-04-07)

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion integrations/n8n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Or install from archive (Docker, custom n8n images):
# Download from GitHub Releases
# Find the latest n8n-nodes-pachca.tgz at:
# https://github.com/pachca/openapi/releases?q=n8n
wget https://github.com/pachca/openapi/releases/download/n8n-v2.0.1/n8n-nodes-pachca.tgz
wget https://github.com/pachca/openapi/releases/download/n8n-v2.0.2/n8n-nodes-pachca.tgz

# Via npm (recommended)
cd ~/.n8n/nodes && npm install ./n8n-nodes-pachca.tgz
Expand Down
5 changes: 2 additions & 3 deletions integrations/n8n/nodes/Pachca/GenericFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
IN8nHttpFullResponse,
JsonObject,
} from 'n8n-workflow';
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
import { NodeApiError, NodeOperationError, sleep } from 'n8n-workflow';
import * as crypto from 'crypto';

// ============================================================================
Expand Down Expand Up @@ -421,8 +421,7 @@ export async function makeApiRequestAllPages(
totalRetries++;
const retryAfter = (err as NodeApiError & { retryAfter?: number })?.retryAfter;
const waitSec = retryAfter ?? (code === '429' ? 2 : 1);
// eslint-disable-next-line @n8n/community-nodes/no-restricted-globals
await new Promise(r => setTimeout(r, waitSec * 1000));
await sleep(waitSec * 1000);
continue;
}
throw error;
Expand Down
17 changes: 16 additions & 1 deletion integrations/n8n/nodes/Pachca/SharedRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ const ROUTES: Record<string, Record<string, RouteConfig>> = {
siblingFields: ['type', 'trigger_id', 'private_metadata', 'callback_id'],
special: 'formBlocks',
bodyMap: [
{ api: 'title', n8n: 'formTitle' },
{ api: 'title', n8n: 'formTitle', default: '' },
{ api: 'type', n8n: 'type', default: 'modal' },
{ api: 'trigger_id', n8n: 'triggerId' },
],
Expand Down Expand Up @@ -917,6 +917,21 @@ async function executeRoute(
if (route.special === 'formBlocks') {
const blocks = resolveFormBlocksFromParams(this, i);
if (blocks.length) body.blocks = blocks;
// v1 compat: in JSON mode, title/close_text/submit_text may be inside the JSON
// Override empty body fields with values from parsed JSON
let rawJson = '';
try { rawJson = this.getNodeParameter('formBlocks', i, '') as string; } catch { /* */ }
if (!rawJson) { try { rawJson = this.getNodeParameter('customFormJson', i, '') as string; } catch { /* */ } }
if (rawJson) {
try {
const parsed = JSON.parse(rawJson);
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
if (!body.title && parsed.title) body.title = parsed.title;
if (!body.close_text && parsed.close_text) body.close_text = parsed.close_text;
if (!body.submit_text && parsed.submit_text) body.submit_text = parsed.submit_text;
}
} catch { /* not valid JSON or not an object */ }
}
}
if (route.special === 'unfurlLinkPreviews') {
// v1 compat: linkPreviews was a fixedCollection { preview: [{ url, title, description, imageUrl }] }
Expand Down
2 changes: 1 addition & 1 deletion integrations/n8n/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "n8n-nodes-pachca",
"version": "2.0.1",
"version": "2.0.2",
"description": "Pachca node for n8n workflow automation",
"license": "MIT",
"main": "index.js",
Expand Down
20 changes: 19 additions & 1 deletion integrations/n8n/scripts/generate-n8n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2492,10 +2492,13 @@ function buildFieldMapStr(resource: string, op: OperationInfo, f: BodyField): st
parts.push(`subKey: '${subKey}'`);
}

// v1 compat: form.type didn't exist in v1 — default to 'modal'
// v1 compat: form fields that may be missing in v1 JSON mode or v1 workflows
if (resource === 'form' && f.name === 'type') {
parts.push(`default: 'modal'`);
}
if (resource === 'form' && f.name === 'title') {
parts.push(`default: ''`);
}

return `{ ${parts.join(', ')} }`;
}
Expand Down Expand Up @@ -3036,6 +3039,21 @@ async function executeRoute(
\tif (route.special === 'formBlocks') {
\t\tconst blocks = resolveFormBlocksFromParams(this, i);
\t\tif (blocks.length) body.blocks = blocks;
\t\t// v1 compat: in JSON mode, title/close_text/submit_text may be inside the JSON
\t\t// Override empty body fields with values from parsed JSON
\t\tlet rawJson = '';
\t\ttry { rawJson = this.getNodeParameter('formBlocks', i, '') as string; } catch { /* */ }
\t\tif (!rawJson) { try { rawJson = this.getNodeParameter('customFormJson', i, '') as string; } catch { /* */ } }
\t\tif (rawJson) {
\t\t\ttry {
\t\t\t\tconst parsed = JSON.parse(rawJson);
\t\t\t\tif (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
\t\t\t\t\tif (!body.title && parsed.title) body.title = parsed.title;
\t\t\t\t\tif (!body.close_text && parsed.close_text) body.close_text = parsed.close_text;
\t\t\t\t\tif (!body.submit_text && parsed.submit_text) body.submit_text = parsed.submit_text;
\t\t\t\t}
\t\t\t} catch { /* not valid JSON or not an object */ }
\t\t}
\t}
\tif (route.special === 'unfurlLinkPreviews') {
\t\t// v1 compat: linkPreviews was a fixedCollection { preview: [{ url, title, description, imageUrl }] }
Expand Down
Loading