n8n-nodes-formfeed 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Philipp Staudt IT-Dienstleistungen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # n8n-nodes-formfeed
2
+
3
+ n8n community node for [Formfeed](https://formfeed.dev): render PDFs and images from templates,
4
+ read template schemas, and start workflows when a render or batch finishes.
5
+
6
+ ## Install
7
+
8
+ In n8n: **Settings → Community nodes → Install**, package name `n8n-nodes-formfeed`.
9
+ Self-hosted installs can also run `npm install n8n-nodes-formfeed` in the n8n user folder.
10
+
11
+ ## Credentials
12
+
13
+ Create an API key in the Formfeed app (**API keys**) and paste it into the *Formfeed API*
14
+ credential. A `ff_test_` key renders for free with a watermark, which is what you want while
15
+ building a workflow. The region selects the API host; a custom base URL points at staging or a
16
+ self-hosted gateway.
17
+
18
+ ## Nodes
19
+
20
+ **Formfeed**
21
+
22
+ | Resource | Operations |
23
+ |---|---|
24
+ | Render | Create (template, HTML or URL), Get, Get Many, Delete Outputs |
25
+ | Template | Get Many, Get, Get Schema |
26
+ | Job | Get |
27
+
28
+ Choosing a template loads its fields from the stored data schema, so you map named fields
29
+ (`invoice.number`) instead of pasting JSON; the JSON field covers lists and nested objects. With
30
+ *Download File* the rendered document is attached as binary data, ready for an email or an upload.
31
+
32
+ **Formfeed Trigger**
33
+
34
+ Registers a webhook endpoint in your workspace and starts the workflow on `render.completed`,
35
+ `render.failed`, `job.completed`, `job.failed`, `quota.warning` or `quota.exceeded`. The endpoint is
36
+ removed again when the workflow is deactivated.
37
+
38
+ ## Development
39
+
40
+ ```bash
41
+ pnpm nx bundle n8n-nodes-formfeed # compiles to dist/packages/n8n-nodes-formfeed
42
+ pnpm nx smoke n8n-nodes-formfeed # loads the compiled nodes the way n8n does
43
+ pnpm nx test n8n-nodes-formfeed # unit tests of the request and schema helpers
44
+ ```
45
+
46
+ To try it in a local n8n: `npm link` the built folder into `~/.n8n/nodes`, or copy it there.
@@ -0,0 +1,10 @@
1
+ import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ /** API key of one Formfeed workspace. Test keys render for free and watermark the output. */
3
+ export declare class FormfeedApi implements ICredentialType {
4
+ name: string;
5
+ displayName: string;
6
+ documentationUrl: string;
7
+ properties: INodeProperties[];
8
+ authenticate: IAuthenticateGeneric;
9
+ test: ICredentialTestRequest;
10
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FormfeedApi = void 0;
4
+ /** API key of one Formfeed workspace. Test keys render for free and watermark the output. */
5
+ class FormfeedApi {
6
+ name = 'formfeedApi';
7
+ displayName = 'Formfeed API';
8
+ documentationUrl = 'https://docs.formfeed.dev/integrations/n8n';
9
+ properties = [
10
+ {
11
+ displayName: 'API Key',
12
+ name: 'apiKey',
13
+ type: 'string',
14
+ typeOptions: { password: true },
15
+ default: '',
16
+ required: true,
17
+ placeholder: 'ff_live_… or ff_test_…',
18
+ description: 'Created in the app under API keys. A test key renders for free with a watermark.',
19
+ },
20
+ {
21
+ displayName: 'Region',
22
+ name: 'region',
23
+ type: 'options',
24
+ default: 'eu',
25
+ options: [
26
+ { name: 'Europe (Frankfurt)', value: 'eu' },
27
+ { name: 'United States (Ashburn)', value: 'us' },
28
+ ],
29
+ description: 'Where documents are rendered and stored. Only the EU region exists at launch.',
30
+ },
31
+ {
32
+ displayName: 'Custom Base URL',
33
+ name: 'baseUrl',
34
+ type: 'string',
35
+ default: '',
36
+ placeholder: 'https://api-eu.formfeed.dev/v1',
37
+ description: 'Overrides the region host, for staging or a self-hosted gateway',
38
+ },
39
+ ];
40
+ authenticate = {
41
+ type: 'generic',
42
+ properties: {
43
+ headers: { Authorization: '=Bearer {{$credentials.apiKey}}' },
44
+ },
45
+ };
46
+ test = {
47
+ request: {
48
+ baseURL: '={{$credentials.baseUrl || ($credentials.region === "us" ? "https://api-us.formfeed.dev/v1" : "https://api-eu.formfeed.dev/v1")}}',
49
+ url: '/account',
50
+ },
51
+ };
52
+ }
53
+ exports.FormfeedApi = FormfeedApi;
54
+ //# sourceMappingURL=FormfeedApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormfeedApi.credentials.js","sourceRoot":"","sources":["../../../../../packages/n8n-nodes-formfeed/src/credentials/FormfeedApi.credentials.ts"],"names":[],"mappings":";;;AAOA,6FAA6F;AAC7F,MAAa,WAAW;IACtB,IAAI,GAAG,aAAa,CAAC;IACrB,WAAW,GAAG,cAAc,CAAC;IAC7B,gBAAgB,GAAG,4CAA4C,CAAC;IAEhE,UAAU,GAAsB;QAC9B;YACE,WAAW,EAAE,SAAS;YACtB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC/B,OAAO,EAAE,EAAE;YACX,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,wBAAwB;YACrC,WAAW,EAAE,kFAAkF;SAChG;QACD;YACE,WAAW,EAAE,QAAQ;YACrB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;YACb,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,IAAI,EAAE;gBAC3C,EAAE,IAAI,EAAE,yBAAyB,EAAE,KAAK,EAAE,IAAI,EAAE;aACjD;YACD,WAAW,EAAE,+EAA+E;SAC7F;QACD;YACE,WAAW,EAAE,iBAAiB;YAC9B,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,EAAE;YACX,WAAW,EAAE,gCAAgC;YAC7C,WAAW,EAAE,iEAAiE;SAC/E;KACF,CAAC;IAEF,YAAY,GAAyB;QACnC,IAAI,EAAE,SAAS;QACf,UAAU,EAAE;YACV,OAAO,EAAE,EAAE,aAAa,EAAE,iCAAiC,EAAE;SAC9D;KACF,CAAC;IAEF,IAAI,GAA2B;QAC7B,OAAO,EAAE;YACP,OAAO,EACL,mIAAmI;YACrI,GAAG,EAAE,UAAU;SAChB;KACF,CAAC;CACH;AAnDD,kCAmDC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Pure helpers of the n8n node: everything that decides what to send, so the node classes stay
3
+ * declarative and this part is unit tested without the n8n runtime.
4
+ */
5
+ export type Region = 'eu' | 'us';
6
+ export interface FormfeedCredentials {
7
+ apiKey: string;
8
+ region: Region;
9
+ baseUrl?: string;
10
+ }
11
+ /** The API base a credential points at; a custom base URL wins (staging, hybrid rendering). */
12
+ export declare function baseUrl(credentials: FormfeedCredentials): string;
13
+ export interface RenderInput {
14
+ source: 'template' | 'html' | 'url';
15
+ template?: string;
16
+ html?: string;
17
+ url?: string;
18
+ engine?: 'jinja2' | 'liquid' | 'handlebars';
19
+ data?: Record<string, unknown>;
20
+ output?: 'pdf' | 'png' | 'jpg';
21
+ filename?: string;
22
+ locale?: string;
23
+ mode?: 'sync' | 'async';
24
+ webhookUrl?: string;
25
+ settings?: Record<string, unknown>;
26
+ meta?: Record<string, unknown>;
27
+ }
28
+ /** Builds the `POST /renders` body, leaving out what the user did not fill in. */
29
+ export declare function renderBody(input: RenderInput): Record<string, unknown>;
30
+ export interface SchemaField {
31
+ /** Dot path into the data object, e.g. `invoice.number`. */
32
+ path: string;
33
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'unknown';
34
+ required: boolean;
35
+ description?: string;
36
+ }
37
+ /**
38
+ * Flattens a template's JSON Schema into the fields the node offers, so a user maps named fields
39
+ * instead of pasting JSON (spec 10 §2). Arrays and free-form objects stay whole: their contents
40
+ * come from an expression.
41
+ */
42
+ export declare function schemaFields(schema: unknown, prefix?: string): SchemaField[];
43
+ /** Turns the node's flat field mapping back into the nested `data` object the API expects. */
44
+ export declare function nestData(values: Record<string, unknown>): Record<string, unknown>;
45
+ /** Deep merge for the data object: the JSON field wins per leaf, not per branch. Arrays replace. */
46
+ export declare function mergeData(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown>;
47
+ /** Events the trigger node can subscribe to; the API validates the same list. */
48
+ export declare const TRIGGER_EVENTS: readonly ["render.completed", "render.failed", "job.completed", "job.failed", "quota.warning", "quota.exceeded"];
49
+ export type TriggerEvent = (typeof TRIGGER_EVENTS)[number];
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ /**
3
+ * Pure helpers of the n8n node: everything that decides what to send, so the node classes stay
4
+ * declarative and this part is unit tested without the n8n runtime.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.TRIGGER_EVENTS = void 0;
8
+ exports.baseUrl = baseUrl;
9
+ exports.renderBody = renderBody;
10
+ exports.schemaFields = schemaFields;
11
+ exports.nestData = nestData;
12
+ exports.mergeData = mergeData;
13
+ const HOSTS = {
14
+ eu: 'https://api-eu.formfeed.dev/v1',
15
+ us: 'https://api-us.formfeed.dev/v1',
16
+ };
17
+ /** The API base a credential points at; a custom base URL wins (staging, hybrid rendering). */
18
+ function baseUrl(credentials) {
19
+ const custom = credentials.baseUrl?.trim();
20
+ return (custom ? custom : HOSTS[credentials.region] ?? HOSTS.eu).replace(/\/$/, '');
21
+ }
22
+ /** Builds the `POST /renders` body, leaving out what the user did not fill in. */
23
+ function renderBody(input) {
24
+ const body = {};
25
+ if (input.source === 'template')
26
+ body['template'] = input.template;
27
+ if (input.source === 'html') {
28
+ body['html'] = input.html;
29
+ body['engine'] = input.engine ?? 'jinja2';
30
+ }
31
+ if (input.source === 'url')
32
+ body['url'] = input.url;
33
+ if (input.data && Object.keys(input.data).length > 0)
34
+ body['data'] = input.data;
35
+ if (input.output)
36
+ body['output'] = input.output;
37
+ if (input.filename)
38
+ body['filename'] = input.filename;
39
+ if (input.locale)
40
+ body['locale'] = input.locale;
41
+ if (input.mode === 'async')
42
+ body['mode'] = 'async';
43
+ if (input.webhookUrl)
44
+ body['webhook_url'] = input.webhookUrl;
45
+ if (input.settings && Object.keys(input.settings).length > 0)
46
+ body['settings'] = input.settings;
47
+ body['meta'] = { source: 'n8n', ...(input.meta ?? {}) };
48
+ return body;
49
+ }
50
+ /**
51
+ * Flattens a template's JSON Schema into the fields the node offers, so a user maps named fields
52
+ * instead of pasting JSON (spec 10 §2). Arrays and free-form objects stay whole: their contents
53
+ * come from an expression.
54
+ */
55
+ function schemaFields(schema, prefix = '') {
56
+ const node = schema;
57
+ if (!node || typeof node !== 'object' || !node.properties)
58
+ return [];
59
+ const required = new Set(node.required ?? []);
60
+ const fields = [];
61
+ for (const [name, property] of Object.entries(node.properties)) {
62
+ const path = prefix ? `${prefix}.${name}` : name;
63
+ const type = typeOf(property);
64
+ if (type === 'object' && property.properties) {
65
+ fields.push(...schemaFields(property, path));
66
+ continue;
67
+ }
68
+ fields.push({
69
+ path,
70
+ type,
71
+ required: required.has(name),
72
+ ...(property.description ? { description: property.description } : {}),
73
+ });
74
+ }
75
+ return fields;
76
+ }
77
+ function typeOf(property) {
78
+ const raw = Array.isArray(property.type) ? property.type[0] : property.type;
79
+ switch (raw) {
80
+ case 'string':
81
+ case 'number':
82
+ case 'integer':
83
+ case 'boolean':
84
+ case 'object':
85
+ case 'array':
86
+ return raw === 'integer' ? 'number' : raw;
87
+ default:
88
+ return property.properties ? 'object' : 'unknown';
89
+ }
90
+ }
91
+ /** Turns the node's flat field mapping back into the nested `data` object the API expects. */
92
+ function nestData(values) {
93
+ const data = {};
94
+ for (const [path, value] of Object.entries(values)) {
95
+ if (value === undefined || value === '')
96
+ continue;
97
+ const parts = path.split('.');
98
+ let target = data;
99
+ for (const part of parts.slice(0, -1)) {
100
+ const next = target[part];
101
+ target = (typeof next === 'object' && next !== null ? next : (target[part] = {}));
102
+ }
103
+ target[parts[parts.length - 1]] = value;
104
+ }
105
+ return data;
106
+ }
107
+ /** Deep merge for the data object: the JSON field wins per leaf, not per branch. Arrays replace. */
108
+ function mergeData(base, override) {
109
+ const out = { ...base };
110
+ for (const [key, value] of Object.entries(override)) {
111
+ const current = out[key];
112
+ out[key] =
113
+ isPlainObject(current) && isPlainObject(value)
114
+ ? mergeData(current, value)
115
+ : value;
116
+ }
117
+ return out;
118
+ }
119
+ const isPlainObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
120
+ /** Events the trigger node can subscribe to; the API validates the same list. */
121
+ exports.TRIGGER_EVENTS = [
122
+ 'render.completed',
123
+ 'render.failed',
124
+ 'job.completed',
125
+ 'job.failed',
126
+ 'quota.warning',
127
+ 'quota.exceeded',
128
+ ];
129
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../../packages/n8n-nodes-formfeed/src/lib/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ /**
3
+ * Formfeed node: renders documents and reads templates, renders and jobs (spec 10 §2).
4
+ * Requests go through n8n's HTTP helper so credentials, proxies and retries behave as users expect.
5
+ */
6
+ export declare class Formfeed implements INodeType {
7
+ description: INodeTypeDescription;
8
+ methods: {
9
+ loadOptions: {
10
+ getTemplates(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
11
+ getTemplateFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
12
+ };
13
+ };
14
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
15
+ }
@@ -0,0 +1,398 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Formfeed = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const api_1 = require("../../lib/api");
6
+ /**
7
+ * Formfeed node: renders documents and reads templates, renders and jobs (spec 10 §2).
8
+ * Requests go through n8n's HTTP helper so credentials, proxies and retries behave as users expect.
9
+ */
10
+ class Formfeed {
11
+ description = {
12
+ displayName: 'Formfeed',
13
+ name: 'formfeed',
14
+ icon: 'file:formfeed.svg',
15
+ group: ['transform'],
16
+ version: 1,
17
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
18
+ description: 'Generate PDFs and images from templates',
19
+ defaults: { name: 'Formfeed' },
20
+ inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
21
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
22
+ credentials: [{ name: 'formfeedApi', required: true }],
23
+ properties: [
24
+ {
25
+ displayName: 'Resource',
26
+ name: 'resource',
27
+ type: 'options',
28
+ noDataExpression: true,
29
+ default: 'render',
30
+ options: [
31
+ { name: 'Render', value: 'render' },
32
+ { name: 'Template', value: 'template' },
33
+ { name: 'Job', value: 'job' },
34
+ ],
35
+ },
36
+ {
37
+ displayName: 'Operation',
38
+ name: 'operation',
39
+ type: 'options',
40
+ noDataExpression: true,
41
+ displayOptions: { show: { resource: ['render'] } },
42
+ default: 'create',
43
+ options: [
44
+ { name: 'Create', value: 'create', description: 'Render a document', action: 'Render a document' },
45
+ { name: 'Get', value: 'get', description: 'Read one render', action: 'Get a render' },
46
+ { name: 'Get Many', value: 'getAll', description: 'List renders', action: 'Get many renders' },
47
+ { name: 'Delete Outputs', value: 'deleteOutputs', description: 'Remove the stored files', action: 'Delete render outputs' },
48
+ ],
49
+ },
50
+ {
51
+ displayName: 'Operation',
52
+ name: 'operation',
53
+ type: 'options',
54
+ noDataExpression: true,
55
+ displayOptions: { show: { resource: ['template'] } },
56
+ default: 'getAll',
57
+ options: [
58
+ { name: 'Get Many', value: 'getAll', description: 'List templates', action: 'Get many templates' },
59
+ { name: 'Get', value: 'get', description: 'Read one template', action: 'Get a template' },
60
+ { name: 'Get Schema', value: 'getSchema', description: 'Read the data schema', action: 'Get a template schema' },
61
+ ],
62
+ },
63
+ {
64
+ displayName: 'Operation',
65
+ name: 'operation',
66
+ type: 'options',
67
+ noDataExpression: true,
68
+ displayOptions: { show: { resource: ['job'] } },
69
+ default: 'get',
70
+ options: [{ name: 'Get', value: 'get', description: 'Read a batch job', action: 'Get a job' }],
71
+ },
72
+ // --- render: create -------------------------------------------------------------------
73
+ {
74
+ displayName: 'Source',
75
+ name: 'source',
76
+ type: 'options',
77
+ displayOptions: { show: { resource: ['render'], operation: ['create'] } },
78
+ default: 'template',
79
+ options: [
80
+ { name: 'Template', value: 'template' },
81
+ { name: 'HTML', value: 'html' },
82
+ { name: 'URL', value: 'url' },
83
+ ],
84
+ },
85
+ {
86
+ displayName: 'Template Name or ID',
87
+ name: 'template',
88
+ type: 'options',
89
+ typeOptions: { loadOptionsMethod: 'getTemplates' },
90
+ displayOptions: { show: { resource: ['render'], operation: ['create'], source: ['template'] } },
91
+ default: '',
92
+ required: true,
93
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
94
+ },
95
+ {
96
+ displayName: 'Fields',
97
+ name: 'fields',
98
+ type: 'fixedCollection',
99
+ typeOptions: { multipleValues: true },
100
+ displayOptions: { show: { resource: ['render'], operation: ['create'], source: ['template'] } },
101
+ default: {},
102
+ description: 'Values for the template fields; the names come from the template data schema',
103
+ options: [
104
+ {
105
+ name: 'field',
106
+ displayName: 'Field',
107
+ values: [
108
+ {
109
+ displayName: 'Field Name or ID',
110
+ name: 'path',
111
+ type: 'options',
112
+ typeOptions: { loadOptionsMethod: 'getTemplateFields', loadOptionsDependsOn: ['template'] },
113
+ default: '',
114
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
115
+ },
116
+ { displayName: 'Value', name: 'value', type: 'string', default: '' },
117
+ ],
118
+ },
119
+ ],
120
+ },
121
+ {
122
+ displayName: 'Data (JSON)',
123
+ name: 'dataJson',
124
+ type: 'json',
125
+ displayOptions: { show: { resource: ['render'], operation: ['create'] } },
126
+ default: '{}',
127
+ description: 'Merged with the mapped fields; use it for lists and nested objects',
128
+ },
129
+ {
130
+ displayName: 'HTML',
131
+ name: 'html',
132
+ type: 'string',
133
+ typeOptions: { rows: 6 },
134
+ displayOptions: { show: { resource: ['render'], operation: ['create'], source: ['html'] } },
135
+ default: '',
136
+ required: true,
137
+ },
138
+ {
139
+ displayName: 'Engine',
140
+ name: 'engine',
141
+ type: 'options',
142
+ displayOptions: { show: { resource: ['render'], operation: ['create'], source: ['html'] } },
143
+ default: 'jinja2',
144
+ options: [
145
+ { name: 'Jinja2', value: 'jinja2' },
146
+ { name: 'Liquid', value: 'liquid' },
147
+ { name: 'Handlebars', value: 'handlebars' },
148
+ ],
149
+ },
150
+ {
151
+ displayName: 'URL',
152
+ name: 'url',
153
+ type: 'string',
154
+ displayOptions: { show: { resource: ['render'], operation: ['create'], source: ['url'] } },
155
+ default: '',
156
+ required: true,
157
+ placeholder: 'https://example.com/invoice/42',
158
+ },
159
+ {
160
+ displayName: 'Output',
161
+ name: 'output',
162
+ type: 'options',
163
+ displayOptions: { show: { resource: ['render'], operation: ['create'] } },
164
+ default: 'pdf',
165
+ options: [
166
+ { name: 'PDF', value: 'pdf' },
167
+ { name: 'PNG', value: 'png' },
168
+ { name: 'JPG', value: 'jpg' },
169
+ ],
170
+ },
171
+ {
172
+ displayName: 'Download File',
173
+ name: 'download',
174
+ type: 'boolean',
175
+ displayOptions: { show: { resource: ['render'], operation: ['create'] } },
176
+ default: true,
177
+ description: 'Whether to attach the rendered document as binary data instead of returning only the URL',
178
+ },
179
+ {
180
+ displayName: 'Options',
181
+ name: 'options',
182
+ type: 'collection',
183
+ placeholder: 'Add option',
184
+ displayOptions: { show: { resource: ['render'], operation: ['create'] } },
185
+ default: {},
186
+ options: [
187
+ { displayName: 'Filename', name: 'filename', type: 'string', default: '' },
188
+ { displayName: 'Locale', name: 'locale', type: 'string', default: '', placeholder: 'de-DE' },
189
+ {
190
+ displayName: 'Mode',
191
+ name: 'mode',
192
+ type: 'options',
193
+ default: 'sync',
194
+ options: [
195
+ { name: 'Wait for the Document', value: 'sync' },
196
+ { name: 'Queue and Return Immediately', value: 'async' },
197
+ ],
198
+ },
199
+ { displayName: 'Webhook URL', name: 'webhookUrl', type: 'string', default: '' },
200
+ { displayName: 'Settings (JSON)', name: 'settingsJson', type: 'json', default: '{}' },
201
+ ],
202
+ },
203
+ // --- ids -------------------------------------------------------------------------------
204
+ {
205
+ displayName: 'Render ID',
206
+ name: 'renderId',
207
+ type: 'string',
208
+ displayOptions: { show: { resource: ['render'], operation: ['get', 'deleteOutputs'] } },
209
+ default: '',
210
+ required: true,
211
+ },
212
+ {
213
+ displayName: 'Job ID',
214
+ name: 'jobId',
215
+ type: 'string',
216
+ displayOptions: { show: { resource: ['job'], operation: ['get'] } },
217
+ default: '',
218
+ required: true,
219
+ },
220
+ {
221
+ displayName: 'Template Name or ID',
222
+ name: 'templateId',
223
+ type: 'options',
224
+ typeOptions: { loadOptionsMethod: 'getTemplates' },
225
+ displayOptions: { show: { resource: ['template'], operation: ['get', 'getSchema'] } },
226
+ default: '',
227
+ required: true,
228
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
229
+ },
230
+ {
231
+ displayName: 'Return All',
232
+ name: 'returnAll',
233
+ type: 'boolean',
234
+ displayOptions: { show: { resource: ['render', 'template'], operation: ['getAll'] } },
235
+ default: false,
236
+ description: 'Whether to return all results or only up to a given limit',
237
+ },
238
+ {
239
+ displayName: 'Limit',
240
+ name: 'limit',
241
+ type: 'number',
242
+ typeOptions: { minValue: 1, maxValue: 100 },
243
+ displayOptions: { show: { resource: ['render', 'template'], operation: ['getAll'], returnAll: [false] } },
244
+ default: 25,
245
+ description: 'Max number of results to return',
246
+ },
247
+ ],
248
+ };
249
+ methods = {
250
+ loadOptions: {
251
+ async getTemplates() {
252
+ const page = (await request(this, 'GET', '/templates?limit=100'));
253
+ return (page.data ?? []).map((template) => ({
254
+ name: `${template.name} (${template.kind})`,
255
+ value: template.slug,
256
+ }));
257
+ },
258
+ async getTemplateFields() {
259
+ const template = this.getNodeParameter('template', '');
260
+ if (!template)
261
+ return [];
262
+ try {
263
+ const schema = await request(this, 'GET', `/templates/${encodeURIComponent(template)}/schema`);
264
+ return (0, api_1.schemaFields)(schema).map((field) => ({
265
+ name: `${field.path}${field.required ? ' *' : ''} (${field.type})`,
266
+ value: field.path,
267
+ description: field.description ?? '',
268
+ }));
269
+ }
270
+ catch {
271
+ // no stored schema: the user maps with the JSON field instead
272
+ return [];
273
+ }
274
+ },
275
+ },
276
+ };
277
+ async execute() {
278
+ const items = this.getInputData();
279
+ const out = [];
280
+ for (let i = 0; i < items.length; i++) {
281
+ try {
282
+ const resource = this.getNodeParameter('resource', i);
283
+ const operation = this.getNodeParameter('operation', i);
284
+ if (resource === 'render' && operation === 'create') {
285
+ const source = this.getNodeParameter('source', i);
286
+ const options = this.getNodeParameter('options', i, {});
287
+ const mapped = this.getNodeParameter('fields.field', i, []);
288
+ const json = parseJson(this, i, this.getNodeParameter('dataJson', i, '{}'));
289
+ const settings = parseJson(this, i, options['settingsJson'] ?? '{}');
290
+ const body = (0, api_1.renderBody)({
291
+ source,
292
+ template: source === 'template' ? this.getNodeParameter('template', i) : undefined,
293
+ html: source === 'html' ? this.getNodeParameter('html', i) : undefined,
294
+ engine: source === 'html' ? this.getNodeParameter('engine', i) : undefined,
295
+ url: source === 'url' ? this.getNodeParameter('url', i) : undefined,
296
+ data: (0, api_1.mergeData)((0, api_1.nestData)(Object.fromEntries(mapped.map((f) => [f.path, f.value]))), json),
297
+ output: this.getNodeParameter('output', i, 'pdf'),
298
+ filename: options['filename'],
299
+ locale: options['locale'],
300
+ mode: options['mode'],
301
+ webhookUrl: options['webhookUrl'],
302
+ settings,
303
+ });
304
+ const render = (await request(this, 'POST', '/renders', body));
305
+ const item = { json: render, pairedItem: { item: i } };
306
+ if (this.getNodeParameter('download', i, true) && render.download_url) {
307
+ const file = (await this.helpers.httpRequest({
308
+ method: 'GET',
309
+ url: render.download_url,
310
+ encoding: 'arraybuffer',
311
+ returnFullResponse: false,
312
+ }));
313
+ const name = options['filename'] || `${render['id']}.${render.output ?? 'pdf'}`;
314
+ item.binary = {
315
+ data: await this.helpers.prepareBinaryData(Buffer.from(file), name),
316
+ };
317
+ }
318
+ out.push(item);
319
+ continue;
320
+ }
321
+ if (resource === 'render' && operation === 'get') {
322
+ const id = this.getNodeParameter('renderId', i);
323
+ out.push({ json: (await request(this, 'GET', `/renders/${encodeURIComponent(id)}`)), pairedItem: { item: i } });
324
+ continue;
325
+ }
326
+ if (resource === 'render' && operation === 'deleteOutputs') {
327
+ const id = this.getNodeParameter('renderId', i);
328
+ await request(this, 'DELETE', `/renders/${encodeURIComponent(id)}/outputs`);
329
+ out.push({ json: { id, deleted: true }, pairedItem: { item: i } });
330
+ continue;
331
+ }
332
+ if ((resource === 'render' || resource === 'template') && operation === 'getAll') {
333
+ const path = resource === 'render' ? '/renders' : '/templates';
334
+ const returnAll = this.getNodeParameter('returnAll', i, false);
335
+ const limit = returnAll ? 100 : this.getNodeParameter('limit', i, 25);
336
+ let cursor = null;
337
+ do {
338
+ const query = `?limit=${limit}${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ''}`;
339
+ const page = (await request(this, 'GET', `${path}${query}`));
340
+ for (const row of page.data ?? [])
341
+ out.push({ json: row, pairedItem: { item: i } });
342
+ cursor = returnAll ? (page.next_cursor ?? null) : null;
343
+ } while (cursor);
344
+ continue;
345
+ }
346
+ if (resource === 'template' && operation === 'get') {
347
+ const id = this.getNodeParameter('templateId', i);
348
+ out.push({ json: (await request(this, 'GET', `/templates/${encodeURIComponent(id)}`)), pairedItem: { item: i } });
349
+ continue;
350
+ }
351
+ if (resource === 'template' && operation === 'getSchema') {
352
+ const id = this.getNodeParameter('templateId', i);
353
+ const schema = await request(this, 'GET', `/templates/${encodeURIComponent(id)}/schema`);
354
+ out.push({ json: { schema, fields: (0, api_1.schemaFields)(schema) }, pairedItem: { item: i } });
355
+ continue;
356
+ }
357
+ if (resource === 'job' && operation === 'get') {
358
+ const id = this.getNodeParameter('jobId', i);
359
+ out.push({ json: (await request(this, 'GET', `/jobs/${encodeURIComponent(id)}`)), pairedItem: { item: i } });
360
+ continue;
361
+ }
362
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported operation ${resource}.${operation}`, { itemIndex: i });
363
+ }
364
+ catch (error) {
365
+ if (this.continueOnFail()) {
366
+ out.push({ json: { error: error.message }, pairedItem: { item: i } });
367
+ continue;
368
+ }
369
+ throw error;
370
+ }
371
+ }
372
+ return [out];
373
+ }
374
+ }
375
+ exports.Formfeed = Formfeed;
376
+ /** One request against the workspace's API host, with the credential's key. */
377
+ async function request(context, method, path, body) {
378
+ const credentials = (await context.getCredentials('formfeedApi'));
379
+ return context.helpers.httpRequestWithAuthentication.call(context, 'formfeedApi', {
380
+ method,
381
+ url: `${(0, api_1.baseUrl)(credentials)}${path}`,
382
+ json: true,
383
+ ...(body === undefined ? {} : { body }),
384
+ });
385
+ }
386
+ function parseJson(context, itemIndex, value) {
387
+ if (value === undefined || value === null || value === '')
388
+ return {};
389
+ if (typeof value === 'object')
390
+ return value;
391
+ try {
392
+ return JSON.parse(String(value));
393
+ }
394
+ catch {
395
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'The JSON field is not valid JSON', { itemIndex });
396
+ }
397
+ }
398
+ //# sourceMappingURL=Formfeed.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Formfeed.node.js","sourceRoot":"","sources":["../../../../../../packages/n8n-nodes-formfeed/src/nodes/Formfeed/Formfeed.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { IHookFunctions, INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';
2
+ /**
3
+ * Starts a workflow when Formfeed reports a finished render, batch or quota warning. The node
4
+ * registers its own webhook endpoint on activation and removes it again on deactivation.
5
+ */
6
+ export declare class FormfeedTrigger implements INodeType {
7
+ description: INodeTypeDescription;
8
+ webhookMethods: {
9
+ default: {
10
+ checkExists(this: IHookFunctions): Promise<boolean>;
11
+ create(this: IHookFunctions): Promise<boolean>;
12
+ delete(this: IHookFunctions): Promise<boolean>;
13
+ };
14
+ };
15
+ webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
16
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FormfeedTrigger = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const api_1 = require("../../lib/api");
6
+ /**
7
+ * Starts a workflow when Formfeed reports a finished render, batch or quota warning. The node
8
+ * registers its own webhook endpoint on activation and removes it again on deactivation.
9
+ */
10
+ class FormfeedTrigger {
11
+ description = {
12
+ displayName: 'Formfeed Trigger',
13
+ name: 'formfeedTrigger',
14
+ icon: 'file:formfeed.svg',
15
+ group: ['trigger'],
16
+ version: 1,
17
+ description: 'Runs when a Formfeed render or batch finishes',
18
+ defaults: { name: 'Formfeed Trigger' },
19
+ inputs: [],
20
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
21
+ credentials: [{ name: 'formfeedApi', required: true }],
22
+ webhooks: [
23
+ {
24
+ name: 'default',
25
+ httpMethod: 'POST',
26
+ responseMode: 'onReceived',
27
+ path: 'webhook',
28
+ },
29
+ ],
30
+ properties: [
31
+ {
32
+ displayName: 'Events',
33
+ name: 'events',
34
+ type: 'multiOptions',
35
+ required: true,
36
+ default: ['render.completed'],
37
+ options: api_1.TRIGGER_EVENTS.map((event) => ({ name: event, value: event })),
38
+ description: 'Which events start this workflow',
39
+ },
40
+ ],
41
+ };
42
+ webhookMethods = {
43
+ default: {
44
+ async checkExists() {
45
+ const stored = this.getWorkflowStaticData('node')['webhookId'];
46
+ if (!stored)
47
+ return false;
48
+ const list = (await apiRequest(this, 'GET', '/webhooks'));
49
+ return (list.data ?? []).some((endpoint) => endpoint.id === stored);
50
+ },
51
+ async create() {
52
+ const created = (await apiRequest(this, 'POST', '/webhooks', {
53
+ url: this.getNodeWebhookUrl('default'),
54
+ events: this.getNodeParameter('events', 0),
55
+ description: 'n8n',
56
+ }));
57
+ const data = this.getWorkflowStaticData('node');
58
+ data['webhookId'] = created.id;
59
+ // shown once by the API; kept so a Code node can verify the signature if a workflow wants to
60
+ if (created.secret)
61
+ data['secret'] = created.secret;
62
+ return true;
63
+ },
64
+ async delete() {
65
+ const data = this.getWorkflowStaticData('node');
66
+ const id = data['webhookId'];
67
+ if (!id)
68
+ return true;
69
+ try {
70
+ await apiRequest(this, 'DELETE', `/webhooks/${encodeURIComponent(id)}`);
71
+ }
72
+ catch {
73
+ // already gone: nothing to clean up
74
+ }
75
+ delete data['webhookId'];
76
+ delete data['secret'];
77
+ return true;
78
+ },
79
+ },
80
+ };
81
+ async webhook() {
82
+ const body = this.getBodyData();
83
+ return { workflowData: [this.helpers.returnJsonArray([body])] };
84
+ }
85
+ }
86
+ exports.FormfeedTrigger = FormfeedTrigger;
87
+ async function apiRequest(context, method, path, body) {
88
+ const credentials = (await context.getCredentials('formfeedApi'));
89
+ return context.helpers.httpRequestWithAuthentication.call(context, 'formfeedApi', {
90
+ method,
91
+ url: `${(0, api_1.baseUrl)(credentials)}${path}`,
92
+ json: true,
93
+ ...(body === undefined ? {} : { body }),
94
+ });
95
+ }
96
+ //# sourceMappingURL=FormfeedTrigger.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormfeedTrigger.node.js","sourceRoot":"","sources":["../../../../../../packages/n8n-nodes-formfeed/src/nodes/Formfeed/FormfeedTrigger.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60" role="img" aria-label="Formfeed">
2
+ <rect width="60" height="60" rx="14" fill="#4f46e5"/>
3
+ <path d="M20 16h22a2 2 0 0 1 0 4H24v8h14a2 2 0 0 1 0 4H24v12a2 2 0 0 1-4 0V18a2 2 0 0 1 2-2z" fill="#fff"/>
4
+ <circle cx="42" cy="42" r="5" fill="#fff" opacity=".85"/>
5
+ </svg>
package/index.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = {};
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "n8n-nodes-formfeed",
3
+ "version": "0.1.0",
4
+ "description": "n8n community node for Formfeed: render PDFs and images from templates, wait for async renders and react to render webhooks.",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Formfeed",
8
+ "email": "support@formfeed.dev",
9
+ "url": "https://formfeed.dev"
10
+ },
11
+ "keywords": [
12
+ "n8n-community-node-package",
13
+ "n8n",
14
+ "formfeed",
15
+ "pdf",
16
+ "pdf-generation",
17
+ "template"
18
+ ],
19
+ "homepage": "https://docs.formfeed.dev/integrations/n8n",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/formfeed-dev/formfeed.git",
23
+ "directory": "packages/n8n-nodes-formfeed"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/formfeed-dev/formfeed/issues",
27
+ "email": "support@formfeed.dev"
28
+ },
29
+ "main": "index.js",
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "LICENSE"
36
+ ],
37
+ "n8n": {
38
+ "n8nNodesApiVersion": 1,
39
+ "credentials": [
40
+ "dist/credentials/FormfeedApi.credentials.js"
41
+ ],
42
+ "nodes": [
43
+ "dist/nodes/Formfeed/Formfeed.node.js",
44
+ "dist/nodes/Formfeed/FormfeedTrigger.node.js"
45
+ ]
46
+ },
47
+ "peerDependencies": {
48
+ "n8n-workflow": "*"
49
+ }
50
+ }