n8n-nodes-data-mapper 0.2.0 → 0.4.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.
@@ -4,6 +4,7 @@ exports.DataMapper = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
5
  const mapping_1 = require("./mapping");
6
6
  const profile_1 = require("./profile");
7
+ const visual_1 = require("./visual");
7
8
  const xml_1 = require("./xml");
8
9
  const show = (values) => ({ show: values });
9
10
  const mappingFields = [
@@ -13,10 +14,24 @@ const mappingFields = [
13
14
  type: 'options',
14
15
  options: [
15
16
  { name: 'Input Field', value: 'field', description: 'Read a value from the input by path' },
17
+ {
18
+ name: 'From Source Profile',
19
+ value: 'profile',
20
+ description: 'Pick a field from the source profile',
21
+ },
16
22
  { name: 'Fixed Value', value: 'value', description: 'Write a fixed value or the result of an expression' },
17
23
  ],
18
24
  default: 'field',
19
25
  },
26
+ {
27
+ displayName: 'Source Field Name or ID',
28
+ name: 'sourceField',
29
+ type: 'options',
30
+ typeOptions: { loadOptionsMethod: 'getSourceFields' },
31
+ default: '',
32
+ description: 'Field from the source profile. Fill the Source Profile field above first. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
33
+ displayOptions: show({ sourceType: ['profile'] }),
34
+ },
20
35
  {
21
36
  displayName: 'Source Path',
22
37
  name: 'source',
@@ -54,7 +69,7 @@ const mappingFields = [
54
69
  type: 'options',
55
70
  typeOptions: { loadOptionsMethod: 'getTargetFields' },
56
71
  default: '',
57
- description: 'Field from the target profile. Fill the Target Profile field above first. Choose from the list, or specify an ID using an expression. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
72
+ description: 'Field from the target profile. Fill the Target Profile field above first. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
58
73
  displayOptions: show({ targetMode: ['profile'] }),
59
74
  },
60
75
  {
@@ -161,14 +176,14 @@ const mappingFields = [
161
176
  { name: 'Stop With Error', value: 'error' },
162
177
  ],
163
178
  default: 'skip',
164
- displayOptions: show({ sourceType: ['field'] }),
179
+ displayOptions: show({ sourceType: ['field', 'profile'] }),
165
180
  },
166
181
  {
167
182
  displayName: 'Default Value',
168
183
  name: 'defaultValue',
169
184
  type: 'string',
170
185
  default: '',
171
- displayOptions: show({ sourceType: ['field'], ifMissing: ['default'] }),
186
+ displayOptions: show({ sourceType: ['field', 'profile'], ifMissing: ['default'] }),
172
187
  },
173
188
  ];
174
189
  const exampleDefinition = JSON.stringify([
@@ -178,6 +193,329 @@ const exampleDefinition = JSON.stringify([
178
193
  { source: 'items[*].qty', target: 'lines[*].quantity', transform: 'integer' },
179
194
  { value: 'EUR', target: 'lines[*].currency' },
180
195
  ], null, 2);
196
+ const v1Properties = [
197
+ {
198
+ displayName: 'Input',
199
+ name: 'inputSource',
200
+ type: 'options',
201
+ noDataExpression: true,
202
+ options: [
203
+ { name: 'Item JSON', value: 'json', description: 'Map the fields of each incoming item' },
204
+ {
205
+ name: 'Text Field',
206
+ value: 'field',
207
+ description: 'Parse JSON or XML text stored in a field first',
208
+ },
209
+ { name: 'File', value: 'binary', description: 'Parse a JSON or XML file first' },
210
+ ],
211
+ default: 'json',
212
+ },
213
+ {
214
+ displayName: 'Input Field',
215
+ name: 'inputField',
216
+ type: 'string',
217
+ default: 'data',
218
+ required: true,
219
+ displayOptions: show({ inputSource: ['field'] }),
220
+ },
221
+ {
222
+ displayName: 'Input Binary Field',
223
+ name: 'inputBinaryField',
224
+ type: 'string',
225
+ default: 'data',
226
+ required: true,
227
+ displayOptions: show({ inputSource: ['binary'] }),
228
+ },
229
+ {
230
+ displayName: 'Input Format',
231
+ name: 'inputFormat',
232
+ type: 'options',
233
+ options: [
234
+ { name: 'Detect Automatically', value: 'auto' },
235
+ { name: 'JSON', value: 'json' },
236
+ { name: 'XML', value: 'xml' },
237
+ ],
238
+ default: 'auto',
239
+ description: 'XML is read with attributes under "$" and text under "_", so a path looks like order.$.ID or order.total._',
240
+ displayOptions: show({ inputSource: ['field', 'binary'] }),
241
+ },
242
+ {
243
+ displayName: 'Define Mappings',
244
+ name: 'definitionMode',
245
+ type: 'options',
246
+ noDataExpression: true,
247
+ options: [
248
+ { name: 'Using Fields Below', value: 'fields' },
249
+ {
250
+ name: 'Auto-Map From Profile',
251
+ value: 'auto',
252
+ description: 'Match source fields to a target profile automatically by name',
253
+ },
254
+ {
255
+ name: 'Using JSON',
256
+ value: 'json',
257
+ description: 'Paste or reference a mapping definition, so one mapping can be reused across workflows',
258
+ },
259
+ ],
260
+ default: 'fields',
261
+ },
262
+ {
263
+ displayName: 'Source Profile',
264
+ name: 'sourceProfile',
265
+ type: 'string',
266
+ typeOptions: { rows: 4 },
267
+ default: '',
268
+ placeholder: 'Optional: paste the structure of the incoming data (profile component, JSON Schema, sample JSON or XML, XSD, CSV header)',
269
+ description: 'Optional. Describes the incoming data, so you can pick source fields from a dropdown (Source: From Source Profile) instead of typing paths.',
270
+ displayOptions: show({ definitionMode: ['fields'] }),
271
+ },
272
+ {
273
+ displayName: 'Source Profile Format',
274
+ name: 'sourceProfileFormat',
275
+ type: 'options',
276
+ options: [
277
+ { name: 'Detect Automatically', value: 'auto' },
278
+ { name: 'Profile Component (XML/JSON/Flat File)', value: 'component' },
279
+ { name: 'JSON Schema', value: 'jsonSchema' },
280
+ { name: 'Sample JSON', value: 'sampleJson' },
281
+ { name: 'Sample XML', value: 'sampleXml' },
282
+ { name: 'XSD', value: 'xsd' },
283
+ { name: 'CSV Header', value: 'csv' },
284
+ ],
285
+ default: 'auto',
286
+ displayOptions: show({ definitionMode: ['fields'] }),
287
+ },
288
+ {
289
+ displayName: 'Define the target structure with a profile, then map into it — pick target fields from a dropdown, or let Auto-Map match them for you',
290
+ name: 'profileNotice',
291
+ type: 'notice',
292
+ default: '',
293
+ displayOptions: show({ definitionMode: ['fields', 'auto'] }),
294
+ },
295
+ {
296
+ displayName: 'Target Profile',
297
+ name: 'targetProfileSource',
298
+ type: 'options',
299
+ options: [
300
+ {
301
+ name: 'Paste It',
302
+ value: 'paste',
303
+ description: 'Paste the profile text. Lets you pick target fields from a dropdown.',
304
+ },
305
+ {
306
+ name: 'Upload a File',
307
+ value: 'binary',
308
+ description: 'Read the profile from an uploaded file (a binary field from a Read/Write Files, Form or HTTP node)',
309
+ },
310
+ ],
311
+ default: 'paste',
312
+ displayOptions: show({ definitionMode: ['fields', 'auto'] }),
313
+ },
314
+ {
315
+ displayName: 'Profile',
316
+ name: 'targetProfile',
317
+ type: 'string',
318
+ typeOptions: { rows: 6 },
319
+ default: '',
320
+ placeholder: 'Paste a profile component (XML/JSON/Flat File), JSON Schema, sample JSON or XML, XSD, or a CSV header row',
321
+ description: 'The structure to map into. The target fields come from this.',
322
+ displayOptions: show({ definitionMode: ['fields', 'auto'], targetProfileSource: ['paste'] }),
323
+ },
324
+ {
325
+ displayName: 'Profile File Field',
326
+ name: 'profileBinaryField',
327
+ type: 'string',
328
+ default: 'data',
329
+ required: true,
330
+ hint: 'The name of the binary field that holds the uploaded profile file',
331
+ description: 'Add a Read/Write Files From Disk, Form Trigger (file upload) or HTTP Request node before this one to bring the profile in, then name its binary field here. Auto-Map uses it; the field dropdown is only available when you paste the profile.',
332
+ displayOptions: show({ definitionMode: ['fields', 'auto'], targetProfileSource: ['binary'] }),
333
+ },
334
+ {
335
+ displayName: 'Profile Format',
336
+ name: 'targetProfileFormat',
337
+ type: 'options',
338
+ options: [
339
+ { name: 'Detect Automatically', value: 'auto' },
340
+ { name: 'Profile Component (XML/JSON/Flat File)', value: 'component' },
341
+ { name: 'JSON Schema', value: 'jsonSchema' },
342
+ { name: 'Sample JSON', value: 'sampleJson' },
343
+ { name: 'Sample XML', value: 'sampleXml' },
344
+ { name: 'XSD', value: 'xsd' },
345
+ { name: 'CSV Header', value: 'csv' },
346
+ ],
347
+ default: 'auto',
348
+ description: 'The kind of profile you provided. Detect Automatically works for most profiles.',
349
+ displayOptions: show({ definitionMode: ['fields', 'auto'] }),
350
+ },
351
+ {
352
+ displayName: 'Mappings',
353
+ name: 'mappings',
354
+ placeholder: 'Add Mapping',
355
+ type: 'fixedCollection',
356
+ typeOptions: { multipleValues: true, sortable: true },
357
+ default: {},
358
+ description: 'Applied in order; a later mapping to the same target overwrites an earlier one',
359
+ displayOptions: show({ definitionMode: ['fields'] }),
360
+ options: [{ displayName: 'Mapping', name: 'mapping', values: mappingFields }],
361
+ },
362
+ {
363
+ displayName: 'Auto-Map Remaining Target Fields',
364
+ name: 'autoMapRemaining',
365
+ type: 'boolean',
366
+ default: false,
367
+ description: 'Whether to also fill any target-profile fields you have not mapped above, by matching them to source fields by name',
368
+ displayOptions: show({ definitionMode: ['fields'] }),
369
+ },
370
+ {
371
+ displayName: 'Mapping Definition',
372
+ name: 'mappingJson',
373
+ type: 'json',
374
+ default: exampleDefinition,
375
+ description: 'An array of mappings, or { "mappings": [...] }. Each mapping has "target" and either "source" or "value", plus optional "transform", "ifMissing", "default", "separator", "decimals", "dateInputFormat", "dateOutputFormat", "lookup", "lookupFallback" and "lookupDefault".',
376
+ displayOptions: show({ definitionMode: ['json'] }),
377
+ },
378
+ {
379
+ displayName: 'Auto-Map Options',
380
+ name: 'autoOptions',
381
+ type: 'collection',
382
+ placeholder: 'Add option',
383
+ default: {},
384
+ displayOptions: show({ definitionMode: ['auto'] }),
385
+ options: [
386
+ {
387
+ displayName: 'Add Auto-Map Report',
388
+ name: 'report',
389
+ type: 'boolean',
390
+ default: false,
391
+ description: 'Whether to add an _autoMap field listing which fields matched and which did not (JSON output only)',
392
+ },
393
+ {
394
+ displayName: 'Case Sensitive',
395
+ name: 'caseSensitive',
396
+ type: 'boolean',
397
+ default: false,
398
+ description: 'Whether field names must match in the same letter case',
399
+ },
400
+ {
401
+ displayName: 'Match',
402
+ name: 'strategy',
403
+ type: 'options',
404
+ options: [
405
+ { name: 'Fuzzy (Similar Names)', value: 'fuzzy' },
406
+ { name: 'Exact Names Only', value: 'exact' },
407
+ ],
408
+ default: 'fuzzy',
409
+ },
410
+ {
411
+ displayName: 'Minimum Confidence',
412
+ name: 'threshold',
413
+ type: 'number',
414
+ typeOptions: { minValue: 0, maxValue: 1, numberPrecision: 2 },
415
+ default: 0.6,
416
+ description: 'How close names must be to match, from 0 (match anything) to 1 (identical names)',
417
+ },
418
+ {
419
+ displayName: 'Reuse a Source for Several Targets',
420
+ name: 'reuseSources',
421
+ type: 'boolean',
422
+ default: false,
423
+ description: 'Whether one source field may be mapped to more than one target field',
424
+ },
425
+ {
426
+ displayName: 'Unmatched Target Fields',
427
+ name: 'unmatched',
428
+ type: 'options',
429
+ options: [
430
+ { name: 'Leave Out', value: 'skip' },
431
+ { name: 'Set to Null', value: 'null' },
432
+ ],
433
+ default: 'skip',
434
+ },
435
+ ],
436
+ },
437
+ {
438
+ displayName: 'Unmapped Fields',
439
+ name: 'unmapped',
440
+ type: 'options',
441
+ options: [
442
+ { name: 'Drop', value: 'drop', description: 'Output only the mapped fields' },
443
+ {
444
+ name: 'Keep',
445
+ value: 'keep',
446
+ description: 'Start from a copy of the input and apply the mappings on top',
447
+ },
448
+ ],
449
+ default: 'drop',
450
+ },
451
+ {
452
+ displayName: 'Output Format',
453
+ name: 'outputFormat',
454
+ type: 'options',
455
+ noDataExpression: true,
456
+ options: [
457
+ { name: 'JSON', value: 'json', description: 'Output the mapped data as the item' },
458
+ { name: 'JSON File', value: 'jsonFile', description: 'Output the mapped data as a JSON file' },
459
+ { name: 'XML', value: 'xml', description: 'Output the mapped data as XML text in a field' },
460
+ { name: 'XML File', value: 'xmlFile', description: 'Output the mapped data as an XML file' },
461
+ ],
462
+ default: 'json',
463
+ },
464
+ {
465
+ displayName: 'Put Output in Field',
466
+ name: 'outputField',
467
+ type: 'string',
468
+ default: 'xml',
469
+ required: true,
470
+ displayOptions: show({ outputFormat: ['xml'] }),
471
+ },
472
+ {
473
+ displayName: 'Put Output File in Field',
474
+ name: 'outputBinaryField',
475
+ type: 'string',
476
+ default: 'data',
477
+ required: true,
478
+ displayOptions: show({ outputFormat: ['xmlFile', 'jsonFile'] }),
479
+ },
480
+ {
481
+ displayName: 'File Name',
482
+ name: 'fileName',
483
+ type: 'string',
484
+ default: '',
485
+ placeholder: 'output.xml',
486
+ description: 'Leave empty to name the file output with the matching extension',
487
+ displayOptions: show({ outputFormat: ['xmlFile', 'jsonFile'] }),
488
+ },
489
+ {
490
+ displayName: 'Root Element',
491
+ name: 'rootElement',
492
+ type: 'string',
493
+ default: '',
494
+ placeholder: 'order',
495
+ description: 'Name of the XML root element. Leave empty to use the single top-level field, or "root" when there are several.',
496
+ displayOptions: show({ outputFormat: ['xml', 'xmlFile'] }),
497
+ },
498
+ {
499
+ displayName: 'Include XML Declaration',
500
+ name: 'xmlDeclaration',
501
+ type: 'boolean',
502
+ default: true,
503
+ description: 'Whether to start the XML with &lt;?xml version="1.0" encoding="UTF-8"?&gt;',
504
+ displayOptions: show({ outputFormat: ['xml', 'xmlFile'] }),
505
+ },
506
+ ];
507
+ function forVersions(properties, versions) {
508
+ return properties.map((property) => {
509
+ var _a;
510
+ return ({
511
+ ...property,
512
+ displayOptions: {
513
+ ...property.displayOptions,
514
+ show: { ...(_a = property.displayOptions) === null || _a === void 0 ? void 0 : _a.show, '@version': versions },
515
+ },
516
+ });
517
+ });
518
+ }
181
519
  class DataMapper {
182
520
  constructor() {
183
521
  this.description = {
@@ -185,303 +523,45 @@ class DataMapper {
185
523
  name: 'dataMapper',
186
524
  icon: { light: 'file:mapper.svg', dark: 'file:mapper.dark.svg' },
187
525
  group: ['transform'],
188
- version: 1,
189
- subtitle: '={{ $parameter["definitionMode"] === "auto" ? "Auto-map from profile" : $parameter["definitionMode"] === "json" ? "JSON definition" : "Field mappings" }}',
190
- description: 'Map and transform data from one structure to another, between JSON and XML',
526
+ version: [1, 2],
527
+ defaultVersion: 2,
528
+ subtitle: '={{ $parameter["targetFormat"] ? "Map to " + $parameter["targetFormat"].toUpperCase() : $parameter["definitionMode"] === "auto" ? "Auto-map from profile" : $parameter["definitionMode"] === "json" ? "JSON definition" : "Field mappings" }}',
529
+ description: 'Drag and drop incoming data onto a target profile and output XML, JSON or CSV',
191
530
  defaults: {
192
531
  name: 'Data Mapper',
193
532
  },
194
533
  inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
195
534
  outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
196
535
  usableAsTool: true,
197
- properties: [
198
- {
199
- displayName: 'Input',
200
- name: 'inputSource',
201
- type: 'options',
202
- noDataExpression: true,
203
- options: [
204
- { name: 'Item JSON', value: 'json', description: 'Map the fields of each incoming item' },
205
- {
206
- name: 'Text Field',
207
- value: 'field',
208
- description: 'Parse JSON or XML text stored in a field first',
209
- },
210
- { name: 'File', value: 'binary', description: 'Parse a JSON or XML file first' },
211
- ],
212
- default: 'json',
213
- },
214
- {
215
- displayName: 'Input Field',
216
- name: 'inputField',
217
- type: 'string',
218
- default: 'data',
219
- required: true,
220
- displayOptions: show({ inputSource: ['field'] }),
221
- },
222
- {
223
- displayName: 'Input Binary Field',
224
- name: 'inputBinaryField',
225
- type: 'string',
226
- default: 'data',
227
- required: true,
228
- displayOptions: show({ inputSource: ['binary'] }),
229
- },
230
- {
231
- displayName: 'Input Format',
232
- name: 'inputFormat',
233
- type: 'options',
234
- options: [
235
- { name: 'Detect Automatically', value: 'auto' },
236
- { name: 'JSON', value: 'json' },
237
- { name: 'XML', value: 'xml' },
238
- ],
239
- default: 'auto',
240
- description: 'XML is read with attributes under "$" and text under "_", so a path looks like order.$.ID or order.total._',
241
- displayOptions: show({ inputSource: ['field', 'binary'] }),
242
- },
243
- {
244
- displayName: 'Define Mappings',
245
- name: 'definitionMode',
246
- type: 'options',
247
- noDataExpression: true,
248
- options: [
249
- { name: 'Using Fields Below', value: 'fields' },
250
- {
251
- name: 'Auto-Map From Profile',
252
- value: 'auto',
253
- description: 'Match source fields to a target profile automatically by name',
254
- },
255
- {
256
- name: 'Using JSON',
257
- value: 'json',
258
- description: 'Paste or reference a mapping definition, so one mapping can be reused across workflows',
259
- },
260
- ],
261
- default: 'fields',
262
- },
263
- {
264
- displayName: 'Define the target structure with a profile, then map into it — pick target fields from a dropdown, or let Auto-Map match them for you',
265
- name: 'profileNotice',
266
- type: 'notice',
267
- default: '',
268
- displayOptions: show({ definitionMode: ['fields', 'auto'] }),
269
- },
270
- {
271
- displayName: 'Target Profile',
272
- name: 'targetProfileSource',
273
- type: 'options',
274
- options: [
275
- {
276
- name: 'Paste It',
277
- value: 'paste',
278
- description: 'Paste the profile text. Lets you pick target fields from a dropdown.',
279
- },
280
- {
281
- name: 'Upload a File',
282
- value: 'binary',
283
- description: 'Read the profile from an uploaded file (a binary field from a Read/Write Files, Form or HTTP node)',
284
- },
285
- ],
286
- default: 'paste',
287
- displayOptions: show({ definitionMode: ['fields', 'auto'] }),
288
- },
289
- {
290
- displayName: 'Profile',
291
- name: 'targetProfile',
292
- type: 'string',
293
- typeOptions: { rows: 6 },
294
- default: '',
295
- placeholder: 'Paste a profile component (XML/JSON/Flat File), JSON Schema, sample JSON or XML, XSD, or a CSV header row',
296
- description: 'The structure to map into. The target fields come from this.',
297
- displayOptions: show({ definitionMode: ['fields', 'auto'], targetProfileSource: ['paste'] }),
298
- },
299
- {
300
- displayName: 'Profile File Field',
301
- name: 'profileBinaryField',
302
- type: 'string',
303
- default: 'data',
304
- required: true,
305
- hint: 'The name of the binary field that holds the uploaded profile file',
306
- description: 'Add a Read/Write Files From Disk, Form Trigger (file upload) or HTTP Request node before this one to bring the profile in, then name its binary field here. Auto-Map uses it; the field dropdown is only available when you paste the profile.',
307
- displayOptions: show({ definitionMode: ['fields', 'auto'], targetProfileSource: ['binary'] }),
308
- },
309
- {
310
- displayName: 'Profile Format',
311
- name: 'targetProfileFormat',
312
- type: 'options',
313
- options: [
314
- { name: 'Detect Automatically', value: 'auto' },
315
- { name: 'Profile Component (XML/JSON/Flat File)', value: 'component' },
316
- { name: 'JSON Schema', value: 'jsonSchema' },
317
- { name: 'Sample JSON', value: 'sampleJson' },
318
- { name: 'Sample XML', value: 'sampleXml' },
319
- { name: 'XSD', value: 'xsd' },
320
- { name: 'CSV Header', value: 'csv' },
321
- ],
322
- default: 'auto',
323
- description: 'The kind of profile you provided. Detect Automatically works for most profiles.',
324
- displayOptions: show({ definitionMode: ['fields', 'auto'] }),
325
- },
326
- {
327
- displayName: 'Mappings',
328
- name: 'mappings',
329
- placeholder: 'Add Mapping',
330
- type: 'fixedCollection',
331
- typeOptions: { multipleValues: true, sortable: true },
332
- default: {},
333
- description: 'Applied in order; a later mapping to the same target overwrites an earlier one',
334
- displayOptions: show({ definitionMode: ['fields'] }),
335
- options: [{ displayName: 'Mapping', name: 'mapping', values: mappingFields }],
336
- },
337
- {
338
- displayName: 'Auto-Map Remaining Target Fields',
339
- name: 'autoMapRemaining',
340
- type: 'boolean',
341
- default: false,
342
- description: 'Whether to also fill any target-profile fields you have not mapped above, by matching them to source fields by name',
343
- displayOptions: show({ definitionMode: ['fields'] }),
344
- },
345
- {
346
- displayName: 'Mapping Definition',
347
- name: 'mappingJson',
348
- type: 'json',
349
- default: exampleDefinition,
350
- description: 'An array of mappings, or { "mappings": [...] }. Each mapping has "target" and either "source" or "value", plus optional "transform", "ifMissing", "default", "separator", "decimals", "dateInputFormat", "dateOutputFormat", "lookup", "lookupFallback" and "lookupDefault".',
351
- displayOptions: show({ definitionMode: ['json'] }),
352
- },
353
- {
354
- displayName: 'Auto-Map Options',
355
- name: 'autoOptions',
356
- type: 'collection',
357
- placeholder: 'Add option',
358
- default: {},
359
- displayOptions: show({ definitionMode: ['auto'] }),
360
- options: [
361
- {
362
- displayName: 'Add Auto-Map Report',
363
- name: 'report',
364
- type: 'boolean',
365
- default: false,
366
- description: 'Whether to add an _autoMap field listing which fields matched and which did not (JSON output only)',
367
- },
368
- {
369
- displayName: 'Case Sensitive',
370
- name: 'caseSensitive',
371
- type: 'boolean',
372
- default: false,
373
- description: 'Whether field names must match in the same letter case',
374
- },
375
- {
376
- displayName: 'Match',
377
- name: 'strategy',
378
- type: 'options',
379
- options: [
380
- { name: 'Fuzzy (Similar Names)', value: 'fuzzy' },
381
- { name: 'Exact Names Only', value: 'exact' },
382
- ],
383
- default: 'fuzzy',
384
- },
385
- {
386
- displayName: 'Minimum Confidence',
387
- name: 'threshold',
388
- type: 'number',
389
- typeOptions: { minValue: 0, maxValue: 1, numberPrecision: 2 },
390
- default: 0.6,
391
- description: 'How close names must be to match, from 0 (match anything) to 1 (identical names)',
392
- },
393
- {
394
- displayName: 'Reuse a Source for Several Targets',
395
- name: 'reuseSources',
396
- type: 'boolean',
397
- default: false,
398
- description: 'Whether one source field may be mapped to more than one target field',
399
- },
400
- {
401
- displayName: 'Unmatched Target Fields',
402
- name: 'unmatched',
403
- type: 'options',
404
- options: [
405
- { name: 'Leave Out', value: 'skip' },
406
- { name: 'Set to Null', value: 'null' },
407
- ],
408
- default: 'skip',
409
- },
410
- ],
411
- },
412
- {
413
- displayName: 'Unmapped Fields',
414
- name: 'unmapped',
415
- type: 'options',
416
- options: [
417
- { name: 'Drop', value: 'drop', description: 'Output only the mapped fields' },
418
- {
419
- name: 'Keep',
420
- value: 'keep',
421
- description: 'Start from a copy of the input and apply the mappings on top',
422
- },
423
- ],
424
- default: 'drop',
425
- },
426
- {
427
- displayName: 'Output Format',
428
- name: 'outputFormat',
429
- type: 'options',
430
- noDataExpression: true,
431
- options: [
432
- { name: 'JSON', value: 'json', description: 'Output the mapped data as the item' },
433
- { name: 'JSON File', value: 'jsonFile', description: 'Output the mapped data as a JSON file' },
434
- { name: 'XML', value: 'xml', description: 'Output the mapped data as XML text in a field' },
435
- { name: 'XML File', value: 'xmlFile', description: 'Output the mapped data as an XML file' },
436
- ],
437
- default: 'json',
438
- },
439
- {
440
- displayName: 'Put Output in Field',
441
- name: 'outputField',
442
- type: 'string',
443
- default: 'xml',
444
- required: true,
445
- displayOptions: show({ outputFormat: ['xml'] }),
446
- },
447
- {
448
- displayName: 'Put Output File in Field',
449
- name: 'outputBinaryField',
450
- type: 'string',
451
- default: 'data',
452
- required: true,
453
- displayOptions: show({ outputFormat: ['xmlFile', 'jsonFile'] }),
454
- },
455
- {
456
- displayName: 'File Name',
457
- name: 'fileName',
458
- type: 'string',
459
- default: '',
460
- placeholder: 'output.xml',
461
- description: 'Leave empty to name the file output with the matching extension',
462
- displayOptions: show({ outputFormat: ['xmlFile', 'jsonFile'] }),
463
- },
464
- {
465
- displayName: 'Root Element',
466
- name: 'rootElement',
467
- type: 'string',
468
- default: '',
469
- placeholder: 'order',
470
- description: 'Name of the XML root element. Leave empty to use the single top-level field, or "root" when there are several.',
471
- displayOptions: show({ outputFormat: ['xml', 'xmlFile'] }),
472
- },
473
- {
474
- displayName: 'Include XML Declaration',
475
- name: 'xmlDeclaration',
476
- type: 'boolean',
477
- default: true,
478
- description: 'Whether to start the XML with &lt;?xml version="1.0" encoding="UTF-8"?&gt;',
479
- displayOptions: show({ outputFormat: ['xml', 'xmlFile'] }),
480
- },
481
- ],
536
+ properties: [...forVersions(v1Properties, [1]), ...forVersions(visual_1.visualProperties, [2])],
482
537
  };
483
538
  this.methods = {
539
+ resourceMapping: {
540
+ getProfileFields: visual_1.getProfileFields,
541
+ },
484
542
  loadOptions: {
543
+ async getSourceFields() {
544
+ var _a, _b;
545
+ const text = ((_a = safeParam(this, 'sourceProfile')) !== null && _a !== void 0 ? _a : '').trim();
546
+ if (text === '')
547
+ return [{ name: 'Paste A Source Profile First', value: '' }];
548
+ if (text.startsWith('=') || text.includes('{{')) {
549
+ return [{ name: 'Source Profile Is Set By An Expression — Type The Source Path Instead', value: '' }];
550
+ }
551
+ const format = ((_b = safeParam(this, 'sourceProfileFormat')) !== null && _b !== void 0 ? _b : 'auto');
552
+ try {
553
+ const fields = (0, profile_1.sourceFields)((0, profile_1.parseProfile)(text, format));
554
+ if (fields.length === 0)
555
+ return [{ name: 'No Fields Found In The Source Profile', value: '' }];
556
+ return fields.map((field) => ({
557
+ name: `${field.path}${field.repeating ? ' (list)' : ''} [${field.type}]`,
558
+ value: field.path,
559
+ }));
560
+ }
561
+ catch (error) {
562
+ return [{ name: `Profile Error: ${error.message}`, value: '' }];
563
+ }
564
+ },
485
565
  async getTargetFields() {
486
566
  var _a, _b, _c;
487
567
  const source = (_a = safeParam(this, 'targetProfileSource')) !== null && _a !== void 0 ? _a : 'paste';
@@ -514,6 +594,8 @@ class DataMapper {
514
594
  };
515
595
  }
516
596
  async execute() {
597
+ if (this.getNode().typeVersion >= 2)
598
+ return await visual_1.executeVisual.call(this);
517
599
  const items = this.getInputData();
518
600
  const returnData = [];
519
601
  const profileCache = new Map();
@@ -642,8 +724,19 @@ function uiMappingToRule(m, ctx, itemIndex) {
642
724
  throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'A mapping is set to "From Target Profile" but no target field is selected', { itemIndex });
643
725
  }
644
726
  }
727
+ let sourcePart;
728
+ if (m.sourceType === 'value')
729
+ sourcePart = { value: (_b = m.value) !== null && _b !== void 0 ? _b : '' };
730
+ else if (m.sourceType === 'profile') {
731
+ if (!m.sourceField) {
732
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'A mapping is set to "From Source Profile" but no source field is selected', { itemIndex });
733
+ }
734
+ sourcePart = { source: m.sourceField, broadcast: true };
735
+ }
736
+ else
737
+ sourcePart = { source: (_c = m.source) !== null && _c !== void 0 ? _c : '' };
645
738
  return {
646
- ...(m.sourceType === 'value' ? { value: (_b = m.value) !== null && _b !== void 0 ? _b : '' } : { source: (_c = m.source) !== null && _c !== void 0 ? _c : '' }),
739
+ ...sourcePart,
647
740
  target,
648
741
  transform: m.transform,
649
742
  separator: m.separator,