@bitavu/n8n-nodes-bitavu 0.1.3 → 0.1.4

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/README.md CHANGED
@@ -1,25 +1,31 @@
1
1
  # @bitavu/n8n-nodes-bitavu
2
2
 
3
- Generate PDFs from [Bitavu](https://bitavu.com)'s visual templates and data in
4
- your n8n workflow. Design and test the layout in Studio, save a version, then
5
- use **Bitavu → Generate PDF**.
3
+ Generate PDFs from [Bitavu](https://bitavu.com)'s visual templates and workflow
4
+ data. Dedicated template nodes list templates, inspect saved versions, and save
5
+ example JSON.
6
6
 
7
7
  The [HTTP Request guide](https://bitavu.com/support/n8n) remains available for
8
- n8n Cloud and self-hosted instances. n8n verification of this node is pending.
8
+ n8n Cloud and self-hosted instances.
9
9
 
10
10
  ## Installation
11
11
 
12
12
  Self-hosted n8n owners can install `@bitavu/n8n-nodes-bitavu`
13
13
  through **Settings → Community nodes → Install**. Follow
14
14
  [n8n's installation instructions](https://docs.n8n.io/integrations/community-nodes/installation/).
15
- n8n Cloud requires a verified community node; this package is not yet verified.
15
+ On n8n Cloud, install an n8n-verified version from the nodes panel.
16
16
 
17
17
  ## Credentials
18
18
 
19
- Create a **Bitavu API** credential and paste your API key. In Studio's
20
- **Integrations → API keys**, reuse a suitable active key or create one with
21
- **Generate PDFs** permission for the report. **Read templates** is not needed.
22
- Keep the secret in Credentials, never in exported workflow JSON.
19
+ Create a **Bitavu API** credential and paste your API key. Give it access to
20
+ the templates in the workflow and the permission for each operation.
21
+
22
+ | Operation | Permission |
23
+ | --- | --- |
24
+ | Generate PDF | Generate PDFs |
25
+ | List Templates, List Template Versions, Get Template Version | Read templates |
26
+ | Save Template Example | Save template examples |
27
+
28
+ Store the key in n8n Credentials.
23
29
 
24
30
  Credential testing sends an authenticated, read-only `GET /v1/credentials/check`
25
31
  request. It does not read reports, generate a PDF or consume render credits.
@@ -32,21 +38,56 @@ and remaining allowance are checked when rendering.
32
38
  content to fields, save a version and test it.
33
39
  2. Copy **templateId**, numeric **revision** and **data** from **Integrations →
34
40
  Prepare a template request → Use in your app**.
35
- 3. Add **Bitavu → Generate PDF** and select your credential.
36
- 4. Set **Template ID**, **Saved Version** and **Document Data**. Map the JSON
41
+ 3. Search for **Bitavu**, add **Generate PDF**, and select your credential.
42
+ 4. Set **Template**, **Version Selection**, and **Document Data**. Map the JSON
37
43
  object using an expression such as `{{ $json.data }}` or `{{ $json }}`.
38
44
  5. Execute. Each input item produces a PDF in the **pdf** binary field, ready
39
45
  for an email, file-storage or other node accepting binary data.
40
46
 
41
- The node calls `POST https://api.bitavu.com/v1/render`, handles Bearer
42
- authentication and binary PDF output, and keeps TLS verification enabled.
43
- It preserves input JSON, existing binary fields and item pairing, and adds
44
- `bitavu` metadata with the template, version, operation ID and filename.
47
+ Use **Template → From List** to search the templates your key can read, sorted by folder and name. The key
48
+ needs **Read templates** permission for the picker and **Generate PDFs** for the
49
+ render. **By ID** accepts a template ID or an expression and works with a key
50
+ that only has **Generate PDFs** permission.
51
+
52
+ Choose **Pinned** and select the **Pinned Version** to keep using a tested layout.
53
+ Use **From List** with **Read templates** permission to see saved versions from newest
54
+ to oldest, with the current publication marked **Published**. Use **By Number** to
55
+ enter the saved version directly. Template and version are node settings. The
56
+ incoming data can contain just the document fields.
57
+ Choose **Latest Published** to follow the current publication when its JSON
58
+ contract is compatible. The version field becomes **Base Version**, and carries
59
+ over the version you entered. Leave it empty to compare against the first
60
+ published version. If compatibility fails, review the changes in Studio or pin
61
+ the tested version. Set **Options → Mapping ID** to use a mapping saved in Studio.
62
+
63
+ The node preserves input JSON, existing binary fields, and item pairing. The
64
+ `bitavu` output includes the requested and rendered revisions, page count,
65
+ request ID, operation ID, and output file information. The rendered revision
66
+ comes from the API when available. A pinned render can fall back to its requested
67
+ revision.
45
68
 
46
69
  Any saved Bitavu report works when the supplied data matches that version.
47
70
  `examples/bitavu-generate-pdf.json` uses fictional data with the starter invoice
48
71
  (`invoice`, saved version 1) and contains no credentials.
49
72
 
73
+ ## Work with templates
74
+
75
+ Search for **Bitavu** in the nodes panel and add the action you need. Each node
76
+ has its own settings and uses the same **Bitavu API** credential. Results are
77
+ returned under `$json.bitavu`.
78
+
79
+ | Operation | Result |
80
+ | --- | --- |
81
+ | List Templates | Accessible templates with folder paths, latest saved revisions, and Studio links |
82
+ | List Template Versions | Saved versions with a `published` flag on each row, the current `publishedRevision`, the latest summary, and an optional selected summary |
83
+ | Get Template Version | A saved version with its bindings, example JSON, and missing or unused input paths |
84
+ | Save Template Example | A new saved version with the example JSON and updated input review |
85
+
86
+ For **Save Template Example**, set **Expected Version** to the latest saved version you
87
+ reviewed. Bitavu rejects the save if that version is no longer current. Read the
88
+ latest version and check its bindings before retrying a conflict or uncertain
89
+ save. Saving an example leaves the current publication in place.
90
+
50
91
  ## Options and retries
51
92
 
52
93
  - **File Name:** defaults to `<templateId>.pdf`.
@@ -60,15 +101,15 @@ new IDs and can consume new credits. For retries across executions, retain a
60
101
  unique ID upstream and map it to **Options → Operation ID**. Reusing an ID with
61
102
  different data returns a conflict; the node does not hide it by changing the ID.
62
103
 
63
- There are no internal automatic retries. Configure retries in n8n and respect
64
- `Retry-After` for rate limits. After an uncertain network result, retry with the
65
- same ID and input. Request timeout: 120 seconds.
104
+ Configure retries in n8n. For an uncertain render result, retry with the same
105
+ operation ID and input. API errors show the request ID and retry delay when
106
+ available. Request timeout is 120 seconds.
66
107
 
67
108
  ## Errors and privacy
68
109
 
69
- Errors explain invalid keys, missing permissions, template/version/data
70
- mismatches, operation conflicts and allowance limits. Use n8n error workflows
71
- or **On Error** settings to handle failures.
110
+ Errors explain invalid keys, missing permissions, compatibility checks,
111
+ revision conflicts, and allowance limits. Use n8n error workflows or
112
+ **On Error** settings to handle failures.
72
113
 
73
114
  The node does not log keys, document data or PDFs, read files or access
74
115
  environment variables. It calls only `api.bitavu.com` and follows no redirects.
@@ -83,9 +124,9 @@ uses `n8n-workflow` 2.46.5, matching the tested n8n 2.46.7 instance.
83
124
 
84
125
  ```sh
85
126
  npm ci --ignore-scripts
86
- npm test
87
127
  npm run lint
88
128
  npm run typecheck
129
+ npm test
89
130
  npm run vetting:check
90
131
  npm run dev
91
132
  ```
@@ -100,10 +141,10 @@ execution/node/run/item idempotency together. PDF binary output remains a
100
141
  regular workflow result, with AI tool generation disabled. Credentials use
101
142
  n8n's standard declarative credential test.
102
143
 
103
- Run the Publish workflow manually on the tag for the package version. Supply
104
- the SHA-256 of the reviewed tarball. The workflow builds and audits the package,
105
- then publishes with npm provenance if the tarball matches. Configure npm trusted
106
- publishing for `bitavu/n8n-nodes-bitavu`, workflow `publish.yml`, environment `npm`.
144
+ The publish workflow builds, tests and publishes `v*.*.*` tags with npm
145
+ provenance. Configure npm trusted publishing for repository
146
+ `bitavu/n8n-nodes-bitavu`, workflow `publish.yml`, environment `npm`.
147
+ The workflow audits the packed files and publishes that exact tarball.
107
148
 
108
149
  ## License
109
150
 
@@ -23,7 +23,7 @@ class BitavuApi {
23
23
  typeOptions: { password: true },
24
24
  required: true,
25
25
  default: '',
26
- description: 'A Bitavu key with Generate PDFs permission for your saved report. Paste only the key, without Bearer.',
26
+ description: 'A Bitavu key with permission for the selected operation and access to its templates. Paste only the key, without Bearer.',
27
27
  },
28
28
  {
29
29
  displayName: 'Allowed HTTP Request Domains',
@@ -1,65 +1,53 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Bitavu = void 0;
4
- const node_crypto_1 = require("node:crypto");
5
- const n8n_workflow_1 = require("n8n-workflow");
6
- const endpoint = 'https://api.bitavu.com/v1/render';
7
- const messages = {
8
- INVALID_API_KEY: 'Your API key is invalid, expired or revoked. Check your Bitavu credential.',
9
- KEY_REPORT: 'This key cannot access the report. Grant it access in Bitavu Integrations.',
10
- KEY_SCOPE: 'Enable Generate PDFs permission for this key in Bitavu Integrations.',
11
- IDEMPOTENCY_CONFLICT: 'This operation ID was already used with different data. Use a new ID for a new document.',
12
- API_VERSION_LIMIT: 'This report reached its rolling saved-version limit. Use a previously tested version or wait for the limit to reset.',
13
- };
4
+ const BitavuAction_1 = require("./BitavuAction");
14
5
  class Bitavu {
15
6
  constructor() {
16
7
  this.description = {
17
- displayName: 'Bitavu',
8
+ ...BitavuAction_1.bitavuNodeDefaults,
9
+ displayName: 'Generate PDF',
18
10
  name: 'bitavu',
19
11
  icon: { light: 'file:bitavu.svg', dark: 'file:bitavu.svg' },
20
- group: ['transform'],
21
- version: 1,
22
12
  usableAsTool: undefined,
23
- subtitle: '={{$parameter["operation"] === "generate" ? "Generate PDF" : ""}}',
24
- description: 'Generate PDFs from visual templates and JSON data',
25
- defaults: { name: 'Bitavu' },
26
- inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
27
- outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
28
- credentials: [{ name: 'bitavuApi', required: true }],
13
+ subtitle: '',
14
+ description: 'Generate a PDF from a saved Bitavu template and workflow data',
15
+ defaults: { name: 'Generate PDF' },
29
16
  properties: [
30
- { displayName: 'Resource', name: 'resource', type: 'hidden', default: 'document' },
17
+ BitavuAction_1.templatePicker,
31
18
  {
32
- displayName: 'Operation',
33
- name: 'operation',
19
+ displayName: 'Version Selection',
20
+ name: 'versionSelection',
34
21
  type: 'options',
35
- noDataExpression: true,
36
22
  options: [
37
23
  {
38
- name: 'Generate PDF',
39
- value: 'generate',
40
- description: 'Fill a saved visual template with JSON data',
41
- action: 'Generate a PDF',
24
+ name: 'Pinned',
25
+ value: 'pinned',
26
+ description: 'Render a specific saved version',
27
+ },
28
+ {
29
+ name: 'Latest Published',
30
+ value: 'latest-published',
31
+ description: 'Render the current publication if its JSON contract is compatible',
42
32
  },
43
33
  ],
44
- default: 'generate',
34
+ default: 'pinned',
45
35
  },
46
36
  {
47
- displayName: 'Template ID',
48
- name: 'templateId',
49
- type: 'string',
50
- required: true,
51
- default: '',
52
- placeholder: 'invoice',
53
- description: 'Copy templateId from your saved report in Bitavu Integrations → Use in your app',
37
+ displayName: 'Pinned Version',
38
+ name: 'revision',
39
+ ...BitavuAction_1.versionPicker,
40
+ displayOptions: { show: { versionSelection: ['pinned'] } },
41
+ description: 'The saved version to render. Defaults to version 1.',
54
42
  },
55
43
  {
56
- displayName: 'Saved Version',
44
+ displayName: 'Base Version',
57
45
  name: 'revision',
58
- type: 'number',
59
- required: true,
60
- default: 1,
61
- typeOptions: { minValue: 1, numberPrecision: 0 },
62
- description: 'The numeric revision of the saved report. Keep the tested version pinned so layout changes do not change your automation.',
46
+ ...BitavuAction_1.versionPicker,
47
+ displayOptions: {
48
+ show: { versionSelection: ['latest-published'] },
49
+ },
50
+ description: 'Optional tested version for the compatibility check. Leave empty to use the first published version.',
63
51
  },
64
52
  {
65
53
  displayName: 'Document Data',
@@ -67,7 +55,7 @@ class Bitavu {
67
55
  type: 'json',
68
56
  required: true,
69
57
  default: '={{ $json.data }}',
70
- description: 'The JSON object expected by your template. Map incoming fields or use an expression such as the entire incoming item.',
58
+ description: 'The JSON object expected by your template or saved mapping. Use an expression to pass data from a previous step.',
71
59
  },
72
60
  {
73
61
  displayName: 'Options',
@@ -76,6 +64,12 @@ class Bitavu {
76
64
  placeholder: 'Add Option',
77
65
  default: {},
78
66
  options: [
67
+ {
68
+ displayName: 'Base Version',
69
+ name: 'baseRevision',
70
+ type: 'hidden',
71
+ default: null,
72
+ },
79
73
  {
80
74
  displayName: 'File Name',
81
75
  name: 'fileName',
@@ -84,6 +78,13 @@ class Bitavu {
84
78
  placeholder: 'invoice.pdf',
85
79
  description: 'Name for the generated PDF. Defaults to the template ID followed by .pdf.',
86
80
  },
81
+ {
82
+ displayName: 'Mapping ID',
83
+ name: 'mappingId',
84
+ type: 'string',
85
+ default: '',
86
+ description: 'The ID of a saved Bitavu mapping for your incoming JSON fields',
87
+ },
87
88
  {
88
89
  displayName: 'Operation ID',
89
90
  name: 'operationId',
@@ -102,130 +103,10 @@ class Bitavu {
102
103
  },
103
104
  ],
104
105
  };
106
+ this.methods = { listSearch: BitavuAction_1.bitavuListSearch };
105
107
  }
106
108
  async execute() {
107
- var _a, _b;
108
- const items = this.getInputData();
109
- const output = [];
110
- for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
111
- try {
112
- const templateId = this.getNodeParameter('templateId', itemIndex).trim();
113
- const revision = this.getNodeParameter('revision', itemIndex);
114
- const options = this.getNodeParameter('options', itemIndex, {});
115
- const raw = this.getNodeParameter('data', itemIndex);
116
- let data;
117
- try {
118
- data = typeof raw === 'string' ? JSON.parse(raw) : raw;
119
- }
120
- catch {
121
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Document Data must be valid JSON.', {
122
- itemIndex,
123
- });
124
- }
125
- const invalid = (message) => {
126
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), message, { itemIndex });
127
- };
128
- if (!/^[a-zA-Z0-9_-]{1,80}$/.test(templateId))
129
- invalid('Enter a valid Template ID from Bitavu Integrations.');
130
- if (!Number.isSafeInteger(revision) || revision < 1)
131
- invalid('Saved Version must be a positive integer.');
132
- if (!data || typeof data !== 'object' || Array.isArray(data))
133
- invalid('Document Data must be a JSON object, rather than an array or a primitive value.');
134
- const outputField = String(options.outputField || 'pdf');
135
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(outputField) ||
136
- ['__proto__', 'constructor', 'prototype'].includes(outputField))
137
- invalid('Output Binary Field must be a simple field name, such as pdf.');
138
- const fileName = String(options.fileName || `${templateId}.pdf`).trim();
139
- if (!fileName ||
140
- /[/\\]/.test(fileName) ||
141
- [...fileName].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127) ||
142
- !fileName.toLowerCase().endsWith('.pdf'))
143
- invalid('File Name must end in .pdf and contain no path separators or control characters.');
144
- const operationId = String(options.operationId ||
145
- `bitavu-${(0, node_crypto_1.createHash)('sha256')
146
- .update(JSON.stringify([
147
- this.getExecutionId(),
148
- this.getNode().id,
149
- this.evaluateExpression('{{ $runIndex }}', itemIndex),
150
- itemIndex,
151
- ]))
152
- .digest('hex')}`);
153
- if (!/^[a-zA-Z0-9_-]{16,128}$/.test(operationId))
154
- invalid('Operation ID must contain 16–128 letters, digits, underscores or hyphens.');
155
- let response;
156
- try {
157
- response = await this.helpers.httpRequestWithAuthentication.call(this, 'bitavuApi', {
158
- method: 'POST',
159
- url: endpoint,
160
- headers: {
161
- 'Idempotency-Key': operationId,
162
- Accept: 'application/pdf',
163
- 'Content-Type': 'application/json',
164
- },
165
- body: JSON.stringify({ templateId, revision, data }),
166
- encoding: 'arraybuffer',
167
- returnFullResponse: true,
168
- ignoreHttpStatusErrors: true,
169
- disableFollowRedirect: true,
170
- timeout: 120000,
171
- });
172
- }
173
- catch {
174
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Could not reach Bitavu. Check your credential and network connection. Retry with the same operation ID if the result is uncertain.', { itemIndex });
175
- }
176
- const body = Buffer.isBuffer(response.body)
177
- ? response.body
178
- : Buffer.from((_a = response.body) !== null && _a !== void 0 ? _a : '');
179
- if (response.statusCode !== 200) {
180
- let details = {};
181
- try {
182
- details = JSON.parse(body.toString('utf8'));
183
- }
184
- catch {
185
- }
186
- const code = typeof details.code === 'string' && /^[A-Z0-9_]+$/.test(details.code)
187
- ? details.code
188
- : '';
189
- const message = messages[code] ||
190
- (response.statusCode === 401
191
- ? messages.INVALID_API_KEY
192
- : response.statusCode === 429
193
- ? 'Bitavu is limiting requests or your render allowance is exhausted. Check your subscription and respect Retry-After before retrying.'
194
- : response.statusCode === 403
195
- ? 'The key or workspace does not permit this render. Check report access and your subscription.'
196
- : [400, 404, 422].includes(response.statusCode)
197
- ? 'Check the Template ID, saved version and document fields against your tested Bitavu request.'
198
- : 'Bitavu could not generate the PDF. Retry later with the same operation ID.');
199
- throw new n8n_workflow_1.NodeApiError(this.getNode(), { message, code, statusCode: response.statusCode }, { message, httpCode: String(response.statusCode), itemIndex });
200
- }
201
- if (!String(response.headers['content-type'] || '').includes('application/pdf') ||
202
- !body.subarray(0, 5).equals(Buffer.from('%PDF-')))
203
- invalid('Bitavu returned an unexpected response instead of a PDF. Contact support with the execution reference.');
204
- const binary = await this.helpers.prepareBinaryData(body, fileName, 'application/pdf');
205
- output.push({
206
- json: {
207
- ...items[itemIndex].json,
208
- bitavu: { templateId, revision, operationId, fileName, outputField },
209
- },
210
- binary: { ...items[itemIndex].binary, [outputField]: binary },
211
- pairedItem: { item: itemIndex },
212
- });
213
- }
214
- catch (error) {
215
- if (!this.continueOnFail()) {
216
- if (error instanceof n8n_workflow_1.NodeApiError)
217
- throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: error.message }, { message: error.message, httpCode: (_b = error.httpCode) !== null && _b !== void 0 ? _b : undefined, itemIndex });
218
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), error instanceof n8n_workflow_1.NodeOperationError
219
- ? error.message
220
- : 'Bitavu PDF generation failed. Check the node configuration.', { itemIndex });
221
- }
222
- output.push({
223
- json: { error: error instanceof Error ? error.message : 'Bitavu PDF generation failed.' },
224
- pairedItem: { item: itemIndex },
225
- });
226
- }
227
- }
228
- return [output];
109
+ return await BitavuAction_1.executeBitavu.call(this, 'generate');
229
110
  }
230
111
  }
231
112
  exports.Bitavu = Bitavu;
@@ -2,10 +2,12 @@
2
2
  "node": "@bitavu/n8n-nodes-bitavu.bitavu",
3
3
  "nodeVersion": "1.0",
4
4
  "codexVersion": "1.0",
5
+ "alias": ["Bitavu"],
5
6
  "categories": [
6
7
  "Productivity",
7
8
  "Utility",
8
- "Finance & Accounting"
9
+ "Finance & Accounting",
10
+ "Marketing & Content"
9
11
  ],
10
12
  "resources": {
11
13
  "primaryDocumentation": [