n8n-mcp 2.14.6 → 2.15.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/data/nodes.db +0 -0
- package/dist/database/node-repository.d.ts.map +1 -1
- package/dist/database/node-repository.js +12 -20
- package/dist/database/node-repository.js.map +1 -1
- package/dist/mcp/handlers-n8n-manager.d.ts.map +1 -1
- package/dist/mcp/handlers-n8n-manager.js +10 -4
- package/dist/mcp/handlers-n8n-manager.js.map +1 -1
- package/dist/mcp/server.d.ts +0 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +146 -53
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tool-docs/index.d.ts.map +1 -1
- package/dist/mcp/tool-docs/index.js +0 -1
- package/dist/mcp/tool-docs/index.js.map +1 -1
- package/dist/mcp/tool-docs/templates/index.d.ts +0 -1
- package/dist/mcp/tool-docs/templates/index.d.ts.map +1 -1
- package/dist/mcp/tool-docs/templates/index.js +1 -3
- package/dist/mcp/tool-docs/templates/index.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +12 -16
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp-tools-engine.d.ts +0 -1
- package/dist/mcp-tools-engine.d.ts.map +1 -1
- package/dist/mcp-tools-engine.js +0 -4
- package/dist/mcp-tools-engine.js.map +1 -1
- package/dist/scripts/fetch-templates.d.ts +1 -1
- package/dist/scripts/fetch-templates.d.ts.map +1 -1
- package/dist/scripts/fetch-templates.js +147 -4
- package/dist/scripts/fetch-templates.js.map +1 -1
- package/dist/services/enhanced-config-validator.js +2 -2
- package/dist/services/enhanced-config-validator.js.map +1 -1
- package/dist/services/n8n-validation.d.ts +3 -0
- package/dist/services/n8n-validation.d.ts.map +1 -1
- package/dist/services/n8n-validation.js +2 -0
- package/dist/services/n8n-validation.js.map +1 -1
- package/dist/services/task-templates.d.ts.map +1 -1
- package/dist/services/task-templates.js.map +1 -1
- package/dist/services/workflow-diff-engine.d.ts.map +1 -1
- package/dist/services/workflow-diff-engine.js +19 -2
- package/dist/services/workflow-diff-engine.js.map +1 -1
- package/dist/services/workflow-validator.d.ts.map +1 -1
- package/dist/services/workflow-validator.js +14 -18
- package/dist/services/workflow-validator.js.map +1 -1
- package/dist/utils/node-type-normalizer.d.ts +16 -0
- package/dist/utils/node-type-normalizer.d.ts.map +1 -0
- package/dist/utils/node-type-normalizer.js +75 -0
- package/dist/utils/node-type-normalizer.js.map +1 -0
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -62,6 +62,7 @@ const handlers_workflow_diff_1 = require("./handlers-workflow-diff");
|
|
|
62
62
|
const tools_documentation_1 = require("./tools-documentation");
|
|
63
63
|
const version_1 = require("../utils/version");
|
|
64
64
|
const node_utils_1 = require("../utils/node-utils");
|
|
65
|
+
const node_type_normalizer_1 = require("../utils/node-type-normalizer");
|
|
65
66
|
const validation_schemas_1 = require("../utils/validation-schemas");
|
|
66
67
|
const protocol_version_1 = require("../utils/protocol-version");
|
|
67
68
|
const telemetry_1 = require("../telemetry");
|
|
@@ -555,7 +556,7 @@ class N8NDocumentationMCPServer {
|
|
|
555
556
|
case 'search_nodes':
|
|
556
557
|
this.validateToolParams(name, args, ['query']);
|
|
557
558
|
const limit = args.limit !== undefined ? Number(args.limit) || 20 : 20;
|
|
558
|
-
return this.searchNodes(args.query, limit, { mode: args.mode });
|
|
559
|
+
return this.searchNodes(args.query, limit, { mode: args.mode, includeExamples: args.includeExamples });
|
|
559
560
|
case 'list_ai_tools':
|
|
560
561
|
return this.listAITools();
|
|
561
562
|
case 'get_node_documentation':
|
|
@@ -565,14 +566,11 @@ class N8NDocumentationMCPServer {
|
|
|
565
566
|
return this.getDatabaseStatistics();
|
|
566
567
|
case 'get_node_essentials':
|
|
567
568
|
this.validateToolParams(name, args, ['nodeType']);
|
|
568
|
-
return this.getNodeEssentials(args.nodeType);
|
|
569
|
+
return this.getNodeEssentials(args.nodeType, args.includeExamples);
|
|
569
570
|
case 'search_node_properties':
|
|
570
571
|
this.validateToolParams(name, args, ['nodeType', 'query']);
|
|
571
572
|
const maxResults = args.maxResults !== undefined ? Number(args.maxResults) || 20 : 20;
|
|
572
573
|
return this.searchNodeProperties(args.nodeType, args.query, maxResults);
|
|
573
|
-
case 'get_node_for_task':
|
|
574
|
-
this.validateToolParams(name, args, ['task']);
|
|
575
|
-
return this.getNodeForTask(args.task);
|
|
576
574
|
case 'list_tasks':
|
|
577
575
|
return this.listTasks(args.category);
|
|
578
576
|
case 'validate_node_operation':
|
|
@@ -783,7 +781,7 @@ class N8NDocumentationMCPServer {
|
|
|
783
781
|
await this.ensureInitialized();
|
|
784
782
|
if (!this.repository)
|
|
785
783
|
throw new Error('Repository not initialized');
|
|
786
|
-
const normalizedType =
|
|
784
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
787
785
|
let node = this.repository.getNode(normalizedType);
|
|
788
786
|
if (!node && normalizedType !== nodeType) {
|
|
789
787
|
node = this.repository.getNode(nodeType);
|
|
@@ -846,13 +844,15 @@ class N8NDocumentationMCPServer {
|
|
|
846
844
|
WHERE type='table' AND name='nodes_fts'
|
|
847
845
|
`).get();
|
|
848
846
|
if (ftsExists) {
|
|
849
|
-
|
|
847
|
+
logger_1.logger.debug(`Using FTS5 search with includeExamples=${options?.includeExamples}`);
|
|
848
|
+
return this.searchNodesFTS(normalizedQuery, limit, searchMode, options);
|
|
850
849
|
}
|
|
851
850
|
else {
|
|
852
|
-
|
|
851
|
+
logger_1.logger.debug('Using LIKE search (no FTS5)');
|
|
852
|
+
return this.searchNodesLIKE(normalizedQuery, limit, options);
|
|
853
853
|
}
|
|
854
854
|
}
|
|
855
|
-
async searchNodesFTS(query, limit, mode) {
|
|
855
|
+
async searchNodesFTS(query, limit, mode, options) {
|
|
856
856
|
if (!this.db)
|
|
857
857
|
throw new Error('Database not initialized');
|
|
858
858
|
const cleanedQuery = query.trim();
|
|
@@ -932,6 +932,32 @@ class N8NDocumentationMCPServer {
|
|
|
932
932
|
if (mode !== 'OR') {
|
|
933
933
|
result.mode = mode;
|
|
934
934
|
}
|
|
935
|
+
if (options && options.includeExamples) {
|
|
936
|
+
try {
|
|
937
|
+
for (const nodeResult of result.results) {
|
|
938
|
+
const examples = this.db.prepare(`
|
|
939
|
+
SELECT
|
|
940
|
+
parameters_json,
|
|
941
|
+
template_name,
|
|
942
|
+
template_views
|
|
943
|
+
FROM template_node_configs
|
|
944
|
+
WHERE node_type = ?
|
|
945
|
+
ORDER BY rank
|
|
946
|
+
LIMIT 2
|
|
947
|
+
`).all(nodeResult.workflowNodeType);
|
|
948
|
+
if (examples.length > 0) {
|
|
949
|
+
nodeResult.examples = examples.map((ex) => ({
|
|
950
|
+
configuration: JSON.parse(ex.parameters_json),
|
|
951
|
+
template: ex.template_name,
|
|
952
|
+
views: ex.template_views
|
|
953
|
+
}));
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
catch (error) {
|
|
958
|
+
logger_1.logger.error(`Failed to add examples:`, error);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
935
961
|
telemetry_1.telemetry.trackSearchQuery(query, scoredNodes.length, mode ?? 'OR');
|
|
936
962
|
return result;
|
|
937
963
|
}
|
|
@@ -1059,18 +1085,18 @@ class N8NDocumentationMCPServer {
|
|
|
1059
1085
|
}
|
|
1060
1086
|
return dp[m][n];
|
|
1061
1087
|
}
|
|
1062
|
-
async searchNodesLIKE(query, limit) {
|
|
1088
|
+
async searchNodesLIKE(query, limit, options) {
|
|
1063
1089
|
if (!this.db)
|
|
1064
1090
|
throw new Error('Database not initialized');
|
|
1065
1091
|
if (query.startsWith('"') && query.endsWith('"')) {
|
|
1066
1092
|
const exactPhrase = query.slice(1, -1);
|
|
1067
1093
|
const nodes = this.db.prepare(`
|
|
1068
|
-
SELECT * FROM nodes
|
|
1094
|
+
SELECT * FROM nodes
|
|
1069
1095
|
WHERE node_type LIKE ? OR display_name LIKE ? OR description LIKE ?
|
|
1070
1096
|
LIMIT ?
|
|
1071
1097
|
`).all(`%${exactPhrase}%`, `%${exactPhrase}%`, `%${exactPhrase}%`, limit * 3);
|
|
1072
1098
|
const rankedNodes = this.rankSearchResults(nodes, exactPhrase, limit);
|
|
1073
|
-
|
|
1099
|
+
const result = {
|
|
1074
1100
|
query,
|
|
1075
1101
|
results: rankedNodes.map(node => ({
|
|
1076
1102
|
nodeType: node.node_type,
|
|
@@ -1082,6 +1108,33 @@ class N8NDocumentationMCPServer {
|
|
|
1082
1108
|
})),
|
|
1083
1109
|
totalCount: rankedNodes.length
|
|
1084
1110
|
};
|
|
1111
|
+
if (options?.includeExamples) {
|
|
1112
|
+
for (const nodeResult of result.results) {
|
|
1113
|
+
try {
|
|
1114
|
+
const examples = this.db.prepare(`
|
|
1115
|
+
SELECT
|
|
1116
|
+
parameters_json,
|
|
1117
|
+
template_name,
|
|
1118
|
+
template_views
|
|
1119
|
+
FROM template_node_configs
|
|
1120
|
+
WHERE node_type = ?
|
|
1121
|
+
ORDER BY rank
|
|
1122
|
+
LIMIT 2
|
|
1123
|
+
`).all(nodeResult.workflowNodeType);
|
|
1124
|
+
if (examples.length > 0) {
|
|
1125
|
+
nodeResult.examples = examples.map((ex) => ({
|
|
1126
|
+
configuration: JSON.parse(ex.parameters_json),
|
|
1127
|
+
template: ex.template_name,
|
|
1128
|
+
views: ex.template_views
|
|
1129
|
+
}));
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
catch (error) {
|
|
1133
|
+
logger_1.logger.warn(`Failed to fetch examples for ${nodeResult.nodeType}:`, error.message);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return result;
|
|
1085
1138
|
}
|
|
1086
1139
|
const words = query.toLowerCase().split(/\s+/).filter(w => w.length > 0);
|
|
1087
1140
|
if (words.length === 0) {
|
|
@@ -1096,7 +1149,7 @@ class N8NDocumentationMCPServer {
|
|
|
1096
1149
|
LIMIT ?
|
|
1097
1150
|
`).all(...params);
|
|
1098
1151
|
const rankedNodes = this.rankSearchResults(nodes, query, limit);
|
|
1099
|
-
|
|
1152
|
+
const result = {
|
|
1100
1153
|
query,
|
|
1101
1154
|
results: rankedNodes.map(node => ({
|
|
1102
1155
|
nodeType: node.node_type,
|
|
@@ -1108,6 +1161,33 @@ class N8NDocumentationMCPServer {
|
|
|
1108
1161
|
})),
|
|
1109
1162
|
totalCount: rankedNodes.length
|
|
1110
1163
|
};
|
|
1164
|
+
if (options?.includeExamples) {
|
|
1165
|
+
for (const nodeResult of result.results) {
|
|
1166
|
+
try {
|
|
1167
|
+
const examples = this.db.prepare(`
|
|
1168
|
+
SELECT
|
|
1169
|
+
parameters_json,
|
|
1170
|
+
template_name,
|
|
1171
|
+
template_views
|
|
1172
|
+
FROM template_node_configs
|
|
1173
|
+
WHERE node_type = ?
|
|
1174
|
+
ORDER BY rank
|
|
1175
|
+
LIMIT 2
|
|
1176
|
+
`).all(nodeResult.workflowNodeType);
|
|
1177
|
+
if (examples.length > 0) {
|
|
1178
|
+
nodeResult.examples = examples.map((ex) => ({
|
|
1179
|
+
configuration: JSON.parse(ex.parameters_json),
|
|
1180
|
+
template: ex.template_name,
|
|
1181
|
+
views: ex.template_views
|
|
1182
|
+
}));
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
catch (error) {
|
|
1186
|
+
logger_1.logger.warn(`Failed to fetch examples for ${nodeResult.nodeType}:`, error.message);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
return result;
|
|
1111
1191
|
}
|
|
1112
1192
|
calculateRelevance(node, query) {
|
|
1113
1193
|
const lowerQuery = query.toLowerCase();
|
|
@@ -1253,7 +1333,7 @@ class N8NDocumentationMCPServer {
|
|
|
1253
1333
|
await this.ensureInitialized();
|
|
1254
1334
|
if (!this.db)
|
|
1255
1335
|
throw new Error('Database not initialized');
|
|
1256
|
-
const normalizedType =
|
|
1336
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1257
1337
|
let node = this.db.prepare(`
|
|
1258
1338
|
SELECT node_type, display_name, documentation, description
|
|
1259
1339
|
FROM nodes
|
|
@@ -1362,15 +1442,15 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1362
1442
|
})),
|
|
1363
1443
|
};
|
|
1364
1444
|
}
|
|
1365
|
-
async getNodeEssentials(nodeType) {
|
|
1445
|
+
async getNodeEssentials(nodeType, includeExamples) {
|
|
1366
1446
|
await this.ensureInitialized();
|
|
1367
1447
|
if (!this.repository)
|
|
1368
1448
|
throw new Error('Repository not initialized');
|
|
1369
|
-
const cacheKey = `essentials:${nodeType}`;
|
|
1449
|
+
const cacheKey = `essentials:${nodeType}:${includeExamples ? 'withExamples' : 'basic'}`;
|
|
1370
1450
|
const cached = this.cache.get(cacheKey);
|
|
1371
1451
|
if (cached)
|
|
1372
1452
|
return cached;
|
|
1373
|
-
const normalizedType =
|
|
1453
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1374
1454
|
let node = this.repository.getNode(normalizedType);
|
|
1375
1455
|
if (!node && normalizedType !== nodeType) {
|
|
1376
1456
|
node = this.repository.getNode(nodeType);
|
|
@@ -1417,6 +1497,50 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1417
1497
|
developmentStyle: node.developmentStyle ?? 'programmatic'
|
|
1418
1498
|
}
|
|
1419
1499
|
};
|
|
1500
|
+
if (includeExamples) {
|
|
1501
|
+
try {
|
|
1502
|
+
const fullNodeType = (0, node_utils_1.getWorkflowNodeType)(node.package ?? 'n8n-nodes-base', node.nodeType);
|
|
1503
|
+
const examples = this.db.prepare(`
|
|
1504
|
+
SELECT
|
|
1505
|
+
parameters_json,
|
|
1506
|
+
template_name,
|
|
1507
|
+
template_views,
|
|
1508
|
+
complexity,
|
|
1509
|
+
use_cases,
|
|
1510
|
+
has_credentials,
|
|
1511
|
+
has_expressions
|
|
1512
|
+
FROM template_node_configs
|
|
1513
|
+
WHERE node_type = ?
|
|
1514
|
+
ORDER BY rank
|
|
1515
|
+
LIMIT 3
|
|
1516
|
+
`).all(fullNodeType);
|
|
1517
|
+
if (examples.length > 0) {
|
|
1518
|
+
result.examples = examples.map((ex) => ({
|
|
1519
|
+
configuration: JSON.parse(ex.parameters_json),
|
|
1520
|
+
source: {
|
|
1521
|
+
template: ex.template_name,
|
|
1522
|
+
views: ex.template_views,
|
|
1523
|
+
complexity: ex.complexity
|
|
1524
|
+
},
|
|
1525
|
+
useCases: ex.use_cases ? JSON.parse(ex.use_cases).slice(0, 2) : [],
|
|
1526
|
+
metadata: {
|
|
1527
|
+
hasCredentials: ex.has_credentials === 1,
|
|
1528
|
+
hasExpressions: ex.has_expressions === 1
|
|
1529
|
+
}
|
|
1530
|
+
}));
|
|
1531
|
+
result.examplesCount = examples.length;
|
|
1532
|
+
}
|
|
1533
|
+
else {
|
|
1534
|
+
result.examples = [];
|
|
1535
|
+
result.examplesCount = 0;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
catch (error) {
|
|
1539
|
+
logger_1.logger.warn(`Failed to fetch examples for ${nodeType}:`, error.message);
|
|
1540
|
+
result.examples = [];
|
|
1541
|
+
result.examplesCount = 0;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1420
1544
|
this.cache.set(cacheKey, result, 3600);
|
|
1421
1545
|
return result;
|
|
1422
1546
|
}
|
|
@@ -1424,7 +1548,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1424
1548
|
await this.ensureInitialized();
|
|
1425
1549
|
if (!this.repository)
|
|
1426
1550
|
throw new Error('Repository not initialized');
|
|
1427
|
-
const normalizedType =
|
|
1551
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1428
1552
|
let node = this.repository.getNode(normalizedType);
|
|
1429
1553
|
if (!node && normalizedType !== nodeType) {
|
|
1430
1554
|
node = this.repository.getNode(nodeType);
|
|
@@ -1462,37 +1586,6 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1462
1586
|
searchedIn: allProperties.length + ' properties'
|
|
1463
1587
|
};
|
|
1464
1588
|
}
|
|
1465
|
-
async getNodeForTask(task) {
|
|
1466
|
-
const template = task_templates_1.TaskTemplates.getTaskTemplate(task);
|
|
1467
|
-
if (!template) {
|
|
1468
|
-
const similar = task_templates_1.TaskTemplates.searchTasks(task);
|
|
1469
|
-
throw new Error(`Unknown task: ${task}. ` +
|
|
1470
|
-
(similar.length > 0
|
|
1471
|
-
? `Did you mean: ${similar.slice(0, 3).join(', ')}?`
|
|
1472
|
-
: `Use 'list_tasks' to see available tasks.`));
|
|
1473
|
-
}
|
|
1474
|
-
return {
|
|
1475
|
-
task: template.task,
|
|
1476
|
-
description: template.description,
|
|
1477
|
-
nodeType: template.nodeType,
|
|
1478
|
-
configuration: template.configuration,
|
|
1479
|
-
userMustProvide: template.userMustProvide,
|
|
1480
|
-
optionalEnhancements: template.optionalEnhancements || [],
|
|
1481
|
-
notes: template.notes || [],
|
|
1482
|
-
example: {
|
|
1483
|
-
node: {
|
|
1484
|
-
type: template.nodeType,
|
|
1485
|
-
parameters: template.configuration
|
|
1486
|
-
},
|
|
1487
|
-
userInputsNeeded: template.userMustProvide.map(p => ({
|
|
1488
|
-
property: p.property,
|
|
1489
|
-
currentValue: this.getPropertyValue(template.configuration, p.property),
|
|
1490
|
-
description: p.description,
|
|
1491
|
-
example: p.example
|
|
1492
|
-
}))
|
|
1493
|
-
}
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
1589
|
getPropertyValue(config, path) {
|
|
1497
1590
|
const parts = path.split('.');
|
|
1498
1591
|
let value = config;
|
|
@@ -1547,7 +1640,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1547
1640
|
await this.ensureInitialized();
|
|
1548
1641
|
if (!this.repository)
|
|
1549
1642
|
throw new Error('Repository not initialized');
|
|
1550
|
-
const normalizedType =
|
|
1643
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1551
1644
|
let node = this.repository.getNode(normalizedType);
|
|
1552
1645
|
if (!node && normalizedType !== nodeType) {
|
|
1553
1646
|
node = this.repository.getNode(nodeType);
|
|
@@ -1584,7 +1677,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1584
1677
|
await this.ensureInitialized();
|
|
1585
1678
|
if (!this.repository)
|
|
1586
1679
|
throw new Error('Repository not initialized');
|
|
1587
|
-
const normalizedType =
|
|
1680
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1588
1681
|
let node = this.repository.getNode(normalizedType);
|
|
1589
1682
|
if (!node && normalizedType !== nodeType) {
|
|
1590
1683
|
node = this.repository.getNode(nodeType);
|
|
@@ -1622,7 +1715,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1622
1715
|
await this.ensureInitialized();
|
|
1623
1716
|
if (!this.repository)
|
|
1624
1717
|
throw new Error('Repository not initialized');
|
|
1625
|
-
const normalizedType =
|
|
1718
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1626
1719
|
let node = this.repository.getNode(normalizedType);
|
|
1627
1720
|
if (!node && normalizedType !== nodeType) {
|
|
1628
1721
|
node = this.repository.getNode(nodeType);
|
|
@@ -1815,7 +1908,7 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
|
|
1815
1908
|
await this.ensureInitialized();
|
|
1816
1909
|
if (!this.repository)
|
|
1817
1910
|
throw new Error('Repository not initialized');
|
|
1818
|
-
const normalizedType =
|
|
1911
|
+
const normalizedType = node_type_normalizer_1.NodeTypeNormalizer.normalizeToFullForm(nodeType);
|
|
1819
1912
|
let node = this.repository.getNode(normalizedType);
|
|
1820
1913
|
if (!node && normalizedType !== nodeType) {
|
|
1821
1914
|
node = this.repository.getNode(nodeType);
|