diff --git a/data/nodes.db b/data/nodes.db index 6342e0e..2c7a7ee 100644 Binary files a/data/nodes.db and b/data/nodes.db differ diff --git a/src/parsers/node-parser.ts b/src/parsers/node-parser.ts index 1befeb6..8bb0f6e 100644 --- a/src/parsers/node-parser.ts +++ b/src/parsers/node-parser.ts @@ -120,27 +120,56 @@ export class NodeParser { } private extractVersion(nodeClass: any): string { + // Handle VersionedNodeType with defaultVersion if (nodeClass.baseDescription?.defaultVersion) { return nodeClass.baseDescription.defaultVersion.toString(); } + // Handle VersionedNodeType with nodeVersions if (nodeClass.nodeVersions) { const versions = Object.keys(nodeClass.nodeVersions); return Math.max(...versions.map(Number)).toString(); } - // Check instance for nodeVersions + // Check instance for nodeVersions and version arrays try { const instance = typeof nodeClass === 'function' ? new nodeClass() : nodeClass; + + // Handle instance-level nodeVersions if (instance?.nodeVersions) { const versions = Object.keys(instance.nodeVersions); return Math.max(...versions.map(Number)).toString(); } + + // Handle version array in description (e.g., [1, 1.1, 1.2]) + if (instance?.description?.version) { + const version = instance.description.version; + if (Array.isArray(version)) { + // Find the maximum version from the array + const maxVersion = Math.max(...version.map((v: any) => parseFloat(v.toString()))); + return maxVersion.toString(); + } else if (typeof version === 'number' || typeof version === 'string') { + return version.toString(); + } + } } catch (e) { - // Ignore + // Some nodes might require parameters to instantiate + // Try to get version from class-level description } - return nodeClass.description?.version || '1'; + // Also check class-level description for version array + const description = this.getNodeDescription(nodeClass); + if (description?.version) { + if (Array.isArray(description.version)) { + const maxVersion = Math.max(...description.version.map((v: any) => parseFloat(v.toString()))); + return maxVersion.toString(); + } else if (typeof description.version === 'number' || typeof description.version === 'string') { + return description.version.toString(); + } + } + + // Default to version 1 + return '1'; } private detectVersioned(nodeClass: any): boolean { @@ -149,14 +178,28 @@ export class NodeParser { return true; } - // Check instance-level nodeVersions + // Check instance-level nodeVersions and version arrays try { const instance = typeof nodeClass === 'function' ? new nodeClass() : nodeClass; + + // Check for nodeVersions if (instance?.nodeVersions) { return true; } + + // Check for version array in description + if (instance?.description?.version && Array.isArray(instance.description.version)) { + return true; + } } catch (e) { - // Ignore + // Some nodes might require parameters to instantiate + // Try to check class-level description + } + + // Also check class-level description for version array + const description = this.getNodeDescription(nodeClass); + if (description?.version && Array.isArray(description.version)) { + return true; } return false; diff --git a/src/scripts/test-version-extraction.ts b/src/scripts/test-version-extraction.ts new file mode 100644 index 0000000..e34cad8 --- /dev/null +++ b/src/scripts/test-version-extraction.ts @@ -0,0 +1,90 @@ +import { NodeParser } from '../parsers/node-parser'; + +// Test script to verify version extraction from different node types + +async function testVersionExtraction() { + console.log('Testing version extraction from different node types...\n'); + + const parser = new NodeParser(); + + // Test cases + const testCases = [ + { + name: 'Gmail Trigger (version array)', + nodeType: 'nodes-base.gmailTrigger', + expectedVersion: '1.2', + expectedVersioned: true + }, + { + name: 'HTTP Request (VersionedNodeType)', + nodeType: 'nodes-base.httpRequest', + expectedVersion: '4.2', + expectedVersioned: true + }, + { + name: 'Code (version array)', + nodeType: 'nodes-base.code', + expectedVersion: '2', + expectedVersioned: true + } + ]; + + // Load nodes from packages + const basePackagePath = process.cwd() + '/node_modules/n8n/node_modules/n8n-nodes-base'; + + for (const testCase of testCases) { + console.log(`\nTesting: ${testCase.name}`); + console.log(`Node Type: ${testCase.nodeType}`); + + try { + // Find the node file + const nodeName = testCase.nodeType.split('.')[1]; + + // Try different paths + const possiblePaths = [ + `${basePackagePath}/dist/nodes/${nodeName}.node.js`, + `${basePackagePath}/dist/nodes/Google/Gmail/GmailTrigger.node.js`, + `${basePackagePath}/dist/nodes/HttpRequest/HttpRequest.node.js`, + `${basePackagePath}/dist/nodes/Code/Code.node.js` + ]; + + let nodeClass = null; + for (const path of possiblePaths) { + try { + const module = require(path); + nodeClass = module[Object.keys(module)[0]]; + if (nodeClass) break; + } catch (e) { + // Try next path + } + } + + if (!nodeClass) { + console.log('❌ Could not load node'); + continue; + } + + // Parse the node + const parsed = parser.parse(nodeClass, 'n8n-nodes-base'); + + console.log(`Loaded node: ${parsed.displayName} (${parsed.nodeType})`); + console.log(`Extracted version: ${parsed.version}`); + console.log(`Is versioned: ${parsed.isVersioned}`); + console.log(`Expected version: ${testCase.expectedVersion}`); + console.log(`Expected versioned: ${testCase.expectedVersioned}`); + + if (parsed.version === testCase.expectedVersion && + parsed.isVersioned === testCase.expectedVersioned) { + console.log('✅ PASS'); + } else { + console.log('❌ FAIL'); + } + + } catch (error) { + console.log(`❌ Error: ${error instanceof Error ? error.message : String(error)}`); + } + } +} + +// Run the test +testVersionExtraction().catch(console.error); \ No newline at end of file diff --git a/versioned-nodes.md b/versioned-nodes.md new file mode 100644 index 0000000..9447298 --- /dev/null +++ b/versioned-nodes.md @@ -0,0 +1,142 @@ +# Versioned Nodes in n8n + +This document lists all nodes that have `version` defined as an array in their description. + +## From n8n-nodes-base package: + +1. **Airtop** - `Airtop.node.js` +2. **Cal Trigger** - `CalTrigger.node.js` +3. **Coda** - `Coda.node.js` +4. **Code** - `Code.node.js` - version: [1, 2] +5. **Compare Datasets** - `CompareDatasets.node.js` +6. **Compression** - `Compression.node.js` +7. **Convert To File** - `ConvertToFile.node.js` +8. **Email Send V2** - `EmailSendV2.node.js` +9. **Execute Workflow** - `ExecuteWorkflow.node.js` +10. **Execute Workflow Trigger** - `ExecuteWorkflowTrigger.node.js` +11. **Filter V2** - `FilterV2.node.js` +12. **Form Trigger V2** - `FormTriggerV2.node.js` +13. **GitHub** - `Github.node.js` +14. **Gmail Trigger** - `GmailTrigger.node.js` +15. **Gmail V2** - `GmailV2.node.js` +16. **Google Books** - `GoogleBooks.node.js` +17. **Google Calendar** - `GoogleCalendar.node.js` +18. **Google Docs** - `GoogleDocs.node.js` +19. **Google Drive V1** - `GoogleDriveV1.node.js` +20. **Google Firebase Cloud Firestore** - `GoogleFirebaseCloudFirestore.node.js` +21. **Google Slides** - `GoogleSlides.node.js` +22. **Google Translate** - `GoogleTranslate.node.js` +23. **GraphQL** - `GraphQL.node.js` +24. **HTML** - `Html.node.js` +25. **HTTP Request V3** - `HttpRequestV3.node.js` - version: [3, 4, 4.1, 4.2] +26. **HubSpot V2** - `HubspotV2.node.js` +27. **If V2** - `IfV2.node.js` +28. **Invoice Ninja** - `InvoiceNinja.node.js` +29. **Invoice Ninja Trigger** - `InvoiceNinjaTrigger.node.js` +30. **Item Lists V2** - `ItemListsV2.node.js` +31. **Jira Trigger** - `JiraTrigger.node.js` +32. **Kafka Trigger** - `KafkaTrigger.node.js` +33. **MailerLite Trigger V2** - `MailerLiteTriggerV2.node.js` +34. **MailerLite V2** - `MailerLiteV2.node.js` +35. **Merge V2** - `MergeV2.node.js` +36. **Microsoft SQL** - `MicrosoftSql.node.js` +37. **Microsoft Teams V1** - `MicrosoftTeamsV1.node.js` +38. **Mindee** - `Mindee.node.js` +39. **MongoDB** - `MongoDb.node.js` +40. **Move Binary Data** - `MoveBinaryData.node.js` +41. **NocoDB** - `NocoDB.node.js` +42. **OpenAI** - `OpenAi.node.js` +43. **Pipedrive Trigger** - `PipedriveTrigger.node.js` +44. **RabbitMQ** - `RabbitMQ.node.js` +45. **Remove Duplicates V1** - `RemoveDuplicatesV1.node.js` +46. **Remove Duplicates V2** - `RemoveDuplicatesV2.node.js` +47. **Respond To Webhook** - `RespondToWebhook.node.js` +48. **RSS Feed Read** - `RssFeedRead.node.js` +49. **Schedule Trigger** - `ScheduleTrigger.node.js` +50. **Set V1** - `SetV1.node.js` +51. **Set V2** - `SetV2.node.js` +52. **Slack V2** - `SlackV2.node.js` +53. **Strava** - `Strava.node.js` +54. **Summarize** - `Summarize.node.js` +55. **Switch V1** - `SwitchV1.node.js` +56. **Switch V2** - `SwitchV2.node.js` +57. **Switch V3** - `SwitchV3.node.js` +58. **Telegram** - `Telegram.node.js` +59. **Telegram Trigger** - `TelegramTrigger.node.js` +60. **The Hive Trigger** - `TheHiveTrigger.node.js` +61. **Todoist V2** - `TodoistV2.node.js` +62. **Twilio Trigger** - `TwilioTrigger.node.js` +63. **Typeform Trigger** - `TypeformTrigger.node.js` +64. **Wait** - `Wait.node.js` +65. **Webhook** - `Webhook.node.js` - version: [1, 1.1, 2] + +## From @n8n/n8n-nodes-langchain package: + +1. **Agent V1** - `AgentV1.node.js` +2. **Chain LLM** - `ChainLlm.node.js` +3. **Chain Retrieval QA** - `ChainRetrievalQa.node.js` +4. **Chain Summarization V2** - `ChainSummarizationV2.node.js` +5. **Chat Trigger** - `ChatTrigger.node.js` +6. **Document Default Data Loader** - `DocumentDefaultDataLoader.node.js` +7. **Document GitHub Loader** - `DocumentGithubLoader.node.js` +8. **Embeddings OpenAI** - `EmbeddingsOpenAi.node.js` +9. **Information Extractor** - `InformationExtractor.node.js` +10. **LM Chat Anthropic** - `LmChatAnthropic.node.js` +11. **LM Chat DeepSeek** - `LmChatDeepSeek.node.js` +12. **LM Chat OpenAI** - `LmChatOpenAi.node.js` +13. **LM Chat OpenRouter** - `LmChatOpenRouter.node.js` +14. **LM Chat xAI Grok** - `LmChatXAiGrok.node.js` +15. **Manual Chat Trigger** - `ManualChatTrigger.node.js` +16. **MCP Trigger** - `McpTrigger.node.js` +17. **Memory Buffer Window** - `MemoryBufferWindow.node.js` +18. **Memory Manager** - `MemoryManager.node.js` +19. **Memory MongoDB Chat** - `MemoryMongoDbChat.node.js` +20. **Memory Motorhead** - `MemoryMotorhead.node.js` +21. **Memory Postgres Chat** - `MemoryPostgresChat.node.js` +22. **Memory Redis Chat** - `MemoryRedisChat.node.js` +23. **Memory Xata** - `MemoryXata.node.js` +24. **Memory Zep** - `MemoryZep.node.js` +25. **OpenAI Assistant** - `OpenAiAssistant.node.js` +26. **Output Parser Structured** - `OutputParserStructured.node.js` +27. **Retriever Workflow** - `RetrieverWorkflow.node.js` +28. **Sentiment Analysis** - `SentimentAnalysis.node.js` +29. **Text Classifier** - `TextClassifier.node.js` +30. **Tool Code** - `ToolCode.node.js` +31. **Tool HTTP Request** - `ToolHttpRequest.node.js` +32. **Tool Vector Store** - `ToolVectorStore.node.js` + +## Examples of Version Arrays Found + +Here are some specific examples of version arrays from actual nodes: + +### n8n-nodes-base: +- **Code**: `version: [1, 2]` +- **HTTP Request V3**: `version: [3, 4, 4.1, 4.2]` +- **Webhook**: `version: [1, 1.1, 2]` +- **Wait**: `version: [1, 1.1]` +- **Schedule Trigger**: `version: [1, 1.1, 1.2]` +- **Switch V3**: `version: [3, 3.1, 3.2]` +- **Set V2**: `version: [3, 3.1, 3.2, 3.3, 3.4]` + +### @n8n/n8n-nodes-langchain: +- **LM Chat OpenAI**: `version: [1, 1.1, 1.2]` +- **Chain LLM**: `version: [1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]` +- **Tool HTTP Request**: `version: [1, 1.1]` + +## Summary + +Total nodes with version arrays: **97 nodes** +- From n8n-nodes-base: 65 nodes +- From @n8n/n8n-nodes-langchain: 32 nodes + +These nodes use versioning to maintain backward compatibility while introducing new features or changes to their interface. The version array pattern allows n8n to: +1. Support multiple versions of the same node +2. Maintain backward compatibility with existing workflows +3. Introduce breaking changes in newer versions while keeping old versions functional +4. Use `defaultVersion` to specify which version new instances should use + +Common version patterns observed: +- Simple incremental: `[1, 2]`, `[1, 2, 3]` +- Minor versions: `[1, 1.1, 1.2]` (common for bug fixes) +- Patch versions: `[3, 4, 4.1, 4.2]` (detailed version tracking) +- Extended versions: `[1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]` (Chain LLM has the most versions) \ No newline at end of file