n8n-nodes-apexapi 0.1.2 → 0.1.3

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.
@@ -117,12 +117,27 @@ class ApexApi {
117
117
  type: 'options',
118
118
  displayOptions: { show: { resource: ['image'] } },
119
119
  options: [
120
- { name: 'Square 1:1 (1024×1024)', value: '1024x1024' },
121
- { name: 'Landscape16:9 (1344×768)', value: '1344x768' },
122
- { name: 'Portrait 9:16 (768×1344)', value: '768x1344' },
120
+ { name: 'Square 1:1 1024×1024', value: '1024x1024' },
121
+ { name: 'Square 1:1 1080×1080 (Instagram post)', value: '1080x1080' },
122
+ { name: 'Portrait 9:16 — 1080×1920 (Story / Reel / TikTok)', value: '1080x1920' },
123
+ { name: 'Landscape 16:9 — 1920×1080 (YouTube / X)', value: '1920x1080' },
124
+ { name: 'Landscape 1.91:1 — 1200×630 (Facebook / link share)', value: '1200x630' },
125
+ { name: 'Portrait 4:5 — 1080×1350 (Instagram portrait)', value: '1080x1350' },
126
+ { name: 'Portrait 2:3 — 1024×1536', value: '1024x1536' },
127
+ { name: 'Landscape 3:2 — 1536×1024', value: '1536x1024' },
128
+ { name: 'Custom (WIDTHxHEIGHT)…', value: 'custom' },
123
129
  ],
124
130
  default: '1024x1024',
125
- description: 'Output resolution / aspect ratio. Note: some models only support specific sizes.',
131
+ description: 'Output resolution / aspect ratio. Note: some models only support specific sizes. Max 4096 per side.',
132
+ },
133
+ {
134
+ displayName: 'Custom Size',
135
+ name: 'customSize',
136
+ type: 'string',
137
+ placeholder: '1024x1024',
138
+ displayOptions: { show: { resource: ['image'], imageSize: ['custom'] } },
139
+ default: '1024x1024',
140
+ description: 'Width×height in pixels, e.g. 1024x1024. Each side 1–4096.',
126
141
  },
127
142
  {
128
143
  displayName: 'Options',
@@ -227,15 +242,33 @@ class ApexApi {
227
242
  }
228
243
  else if (resource === 'image') {
229
244
  const options = this.getNodeParameter('options', i, {});
245
+ const sizeChoice = this.getNodeParameter('imageSize', i, '1024x1024');
246
+ const size = sizeChoice === 'custom'
247
+ ? this.getNodeParameter('customSize', i, '1024x1024')
248
+ : sizeChoice;
230
249
  const body = (0, image_1.buildImageBody)({
231
250
  model: this.getNodeParameter('model', i),
232
251
  prompt: this.getNodeParameter('prompt', i),
233
- size: this.getNodeParameter('imageSize', i, '1024x1024'),
252
+ size,
234
253
  n: options.n,
235
254
  });
236
255
  const resp = await (0, transport_1.apexApiRequest)(ctx, 'POST', '/images/generations', body);
237
256
  const parsed = (0, image_1.parseImageResponse)(resp);
238
257
  json = { urls: parsed.urls, response: parsed.raw };
258
+ // Some providers (Vertex/Imagen) return inline base64 instead of a hosted
259
+ // link. Emit those as n8n binary so the next node can post/upload them
260
+ // directly — hosted-URL providers (fal/kie) just come through in `urls`.
261
+ const b64Images = parsed.images.filter((im) => im.base64);
262
+ if (b64Images.length > 0) {
263
+ const binary = {};
264
+ for (let k = 0; k < b64Images.length; k++) {
265
+ const im = b64Images[k];
266
+ const ext = (im.mimeType ?? 'image/png').split('/')[1] ?? 'png';
267
+ binary[k === 0 ? 'data' : `data${k}`] = await this.helpers.prepareBinaryData(Buffer.from(im.base64, 'base64'), `image_${k}.${ext}`, im.mimeType);
268
+ }
269
+ returnData.push({ json, binary, pairedItem: { item: i } });
270
+ continue;
271
+ }
239
272
  }
240
273
  else {
241
274
  // video
@@ -10,11 +10,29 @@ function buildImageBody(params) {
10
10
  body.n = params.n;
11
11
  return body;
12
12
  }
13
+ const DATA_URL_RE = /^data:([^;,]+);base64,(.+)$/;
14
+ /**
15
+ * Normalise the `/v1/images/generations` response. Providers return images two
16
+ * ways: a hosted URL (fal/kie) or inline base64 (Vertex/Imagen `data:` URLs, or
17
+ * `b64_json`). `urls` holds only real links; `images` holds every image with the
18
+ * base64 decoded out of `data:` URLs so the node can emit it as n8n binary.
19
+ */
13
20
  function parseImageResponse(resp) {
14
21
  const r = resp;
15
22
  const data = Array.isArray(r?.data) ? r.data : [];
16
- const urls = data
17
- .map((d) => d?.url)
18
- .filter((u) => typeof u === 'string');
19
- return { urls, raw: resp };
23
+ const images = [];
24
+ for (const d of data) {
25
+ if (typeof d?.url === 'string') {
26
+ const m = d.url.match(DATA_URL_RE);
27
+ if (m)
28
+ images.push({ base64: m[2], mimeType: m[1] });
29
+ else
30
+ images.push({ url: d.url });
31
+ }
32
+ else if (typeof d?.b64_json === 'string') {
33
+ images.push({ base64: d.b64_json, mimeType: 'image/png' });
34
+ }
35
+ }
36
+ const urls = images.map((i) => i.url).filter((u) => typeof u === 'string');
37
+ return { urls, images, raw: resp };
20
38
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-apexapi",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "n8n community node for ApexApi — call 14 AI providers (OpenAI, Anthropic, Google, Mistral, and more) through one OpenAI-compatible API.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
@@ -46,6 +46,7 @@
46
46
  "devDependencies": {
47
47
  "n8n-workflow": "^1.82.0",
48
48
  "typescript": "^5.9.3",
49
- "vitest": "^3.2.4"
49
+ "vitest": "^3.2.4",
50
+ "@types/node": "^22.0.0"
50
51
  }
51
52
  }