This is page 51 of 67. Use http://codebase.md/czlonkowski/n8n-mcp?lines=true&page={x} to view the full context.
# Directory Structure
```
├── _config.yml
├── .claude
│ └── agents
│ ├── code-reviewer.md
│ ├── context-manager.md
│ ├── debugger.md
│ ├── deployment-engineer.md
│ ├── mcp-backend-engineer.md
│ ├── n8n-mcp-tester.md
│ ├── technical-researcher.md
│ └── test-automator.md
├── .dockerignore
├── .env.docker
├── .env.example
├── .env.n8n.example
├── .env.test
├── .env.test.example
├── .github
│ ├── ABOUT.md
│ ├── BENCHMARK_THRESHOLDS.md
│ ├── FUNDING.yml
│ ├── gh-pages.yml
│ ├── secret_scanning.yml
│ └── workflows
│ ├── benchmark-pr.yml
│ ├── benchmark.yml
│ ├── dependency-check.yml
│ ├── docker-build-fast.yml
│ ├── docker-build-n8n.yml
│ ├── docker-build.yml
│ ├── release.yml
│ ├── test.yml
│ └── update-n8n-deps.yml
├── .gitignore
├── .npmignore
├── ANALYSIS_QUICK_REFERENCE.md
├── ATTRIBUTION.md
├── CHANGELOG.md
├── CLAUDE.md
├── codecov.yml
├── coverage.json
├── data
│ ├── .gitkeep
│ ├── nodes.db
│ ├── nodes.db-shm
│ ├── nodes.db-wal
│ └── templates.db
├── deploy
│ └── quick-deploy-n8n.sh
├── docker
│ ├── docker-entrypoint.sh
│ ├── n8n-mcp
│ ├── parse-config.js
│ └── README.md
├── docker-compose.buildkit.yml
├── docker-compose.extract.yml
├── docker-compose.n8n.yml
├── docker-compose.override.yml.example
├── docker-compose.test-n8n.yml
├── docker-compose.yml
├── Dockerfile
├── Dockerfile.railway
├── Dockerfile.test
├── docs
│ ├── ANTIGRAVITY_SETUP.md
│ ├── AUTOMATED_RELEASES.md
│ ├── BENCHMARKS.md
│ ├── CHANGELOG.md
│ ├── CI_TEST_INFRASTRUCTURE.md
│ ├── CLAUDE_CODE_SETUP.md
│ ├── CLAUDE_INTERVIEW.md
│ ├── CODECOV_SETUP.md
│ ├── CODEX_SETUP.md
│ ├── CURSOR_SETUP.md
│ ├── DEPENDENCY_UPDATES.md
│ ├── DOCKER_README.md
│ ├── DOCKER_TROUBLESHOOTING.md
│ ├── FINAL_AI_VALIDATION_SPEC.md
│ ├── FLEXIBLE_INSTANCE_CONFIGURATION.md
│ ├── HTTP_DEPLOYMENT.md
│ ├── img
│ │ ├── cc_command.png
│ │ ├── cc_connected.png
│ │ ├── codex_connected.png
│ │ ├── cursor_tut.png
│ │ ├── Railway_api.png
│ │ ├── Railway_server_address.png
│ │ ├── skills.png
│ │ ├── vsc_ghcp_chat_agent_mode.png
│ │ ├── vsc_ghcp_chat_instruction_files.png
│ │ ├── vsc_ghcp_chat_thinking_tool.png
│ │ └── windsurf_tut.png
│ ├── INSTALLATION.md
│ ├── LIBRARY_USAGE.md
│ ├── local
│ │ ├── DEEP_DIVE_ANALYSIS_2025-10-02.md
│ │ ├── DEEP_DIVE_ANALYSIS_README.md
│ │ ├── Deep_dive_p1_p2.md
│ │ ├── integration-testing-plan.md
│ │ ├── integration-tests-phase1-summary.md
│ │ ├── N8N_AI_WORKFLOW_BUILDER_ANALYSIS.md
│ │ ├── P0_IMPLEMENTATION_PLAN.md
│ │ └── TEMPLATE_MINING_ANALYSIS.md
│ ├── MCP_ESSENTIALS_README.md
│ ├── MCP_QUICK_START_GUIDE.md
│ ├── N8N_DEPLOYMENT.md
│ ├── RAILWAY_DEPLOYMENT.md
│ ├── README_CLAUDE_SETUP.md
│ ├── README.md
│ ├── SESSION_PERSISTENCE.md
│ ├── tools-documentation-usage.md
│ ├── TYPE_STRUCTURE_VALIDATION.md
│ ├── VS_CODE_PROJECT_SETUP.md
│ ├── WINDSURF_SETUP.md
│ └── workflow-diff-examples.md
├── examples
│ └── enhanced-documentation-demo.js
├── fetch_log.txt
├── LICENSE
├── MEMORY_N8N_UPDATE.md
├── MEMORY_TEMPLATE_UPDATE.md
├── monitor_fetch.sh
├── N8N_HTTP_STREAMABLE_SETUP.md
├── n8n-nodes.db
├── P0-R3-TEST-PLAN.md
├── package-lock.json
├── package.json
├── package.runtime.json
├── PRIVACY.md
├── railway.json
├── README_ANALYSIS.md
├── README.md
├── renovate.json
├── scripts
│ ├── analyze-optimization.sh
│ ├── audit-schema-coverage.ts
│ ├── backfill-mutation-hashes.ts
│ ├── build-optimized.sh
│ ├── compare-benchmarks.js
│ ├── demo-optimization.sh
│ ├── deploy-http.sh
│ ├── deploy-to-vm.sh
│ ├── export-webhook-workflows.ts
│ ├── extract-changelog.js
│ ├── extract-from-docker.js
│ ├── extract-nodes-docker.sh
│ ├── extract-nodes-simple.sh
│ ├── format-benchmark-results.js
│ ├── generate-benchmark-stub.js
│ ├── generate-detailed-reports.js
│ ├── generate-initial-release-notes.js
│ ├── generate-release-notes.js
│ ├── generate-test-summary.js
│ ├── http-bridge.js
│ ├── mcp-http-client.js
│ ├── migrate-nodes-fts.ts
│ ├── migrate-tool-docs.ts
│ ├── n8n-docs-mcp.service
│ ├── nginx-n8n-mcp.conf
│ ├── prebuild-fts5.ts
│ ├── prepare-release.js
│ ├── process-batch-metadata.ts
│ ├── publish-npm-quick.sh
│ ├── publish-npm.sh
│ ├── quick-test.ts
│ ├── run-benchmarks-ci.js
│ ├── sync-runtime-version.js
│ ├── test-ai-validation-debug.ts
│ ├── test-code-node-enhancements.ts
│ ├── test-code-node-fixes.ts
│ ├── test-docker-config.sh
│ ├── test-docker-fingerprint.ts
│ ├── test-docker-optimization.sh
│ ├── test-docker.sh
│ ├── test-empty-connection-validation.ts
│ ├── test-error-message-tracking.ts
│ ├── test-error-output-validation.ts
│ ├── test-error-validation.js
│ ├── test-essentials.ts
│ ├── test-expression-code-validation.ts
│ ├── test-expression-format-validation.js
│ ├── test-fts5-search.ts
│ ├── test-fuzzy-fix.ts
│ ├── test-fuzzy-simple.ts
│ ├── test-helpers-validation.ts
│ ├── test-http-search.ts
│ ├── test-http.sh
│ ├── test-jmespath-validation.ts
│ ├── test-multi-tenant-simple.ts
│ ├── test-multi-tenant.ts
│ ├── test-n8n-integration.sh
│ ├── test-node-info.js
│ ├── test-node-type-validation.ts
│ ├── test-nodes-base-prefix.ts
│ ├── test-operation-validation.ts
│ ├── test-optimized-docker.sh
│ ├── test-release-automation.js
│ ├── test-search-improvements.ts
│ ├── test-security.ts
│ ├── test-single-session.sh
│ ├── test-sqljs-triggers.ts
│ ├── test-structure-validation.ts
│ ├── test-telemetry-debug.ts
│ ├── test-telemetry-direct.ts
│ ├── test-telemetry-env.ts
│ ├── test-telemetry-integration.ts
│ ├── test-telemetry-no-select.ts
│ ├── test-telemetry-security.ts
│ ├── test-telemetry-simple.ts
│ ├── test-typeversion-validation.ts
│ ├── test-url-configuration.ts
│ ├── test-user-id-persistence.ts
│ ├── test-webhook-validation.ts
│ ├── test-workflow-insert.ts
│ ├── test-workflow-sanitizer.ts
│ ├── test-workflow-tracking-debug.ts
│ ├── test-workflow-versioning.ts
│ ├── update-and-publish-prep.sh
│ ├── update-n8n-deps.js
│ ├── update-readme-version.js
│ ├── vitest-benchmark-json-reporter.js
│ └── vitest-benchmark-reporter.ts
├── SECURITY.md
├── src
│ ├── config
│ │ └── n8n-api.ts
│ ├── constants
│ │ └── type-structures.ts
│ ├── data
│ │ └── canonical-ai-tool-examples.json
│ ├── database
│ │ ├── database-adapter.ts
│ │ ├── migrations
│ │ │ └── add-template-node-configs.sql
│ │ ├── node-repository.ts
│ │ ├── nodes.db
│ │ ├── schema-optimized.sql
│ │ └── schema.sql
│ ├── errors
│ │ └── validation-service-error.ts
│ ├── http-server-single-session.ts
│ ├── http-server.ts
│ ├── index.ts
│ ├── loaders
│ │ └── node-loader.ts
│ ├── mappers
│ │ └── docs-mapper.ts
│ ├── mcp
│ │ ├── handlers-n8n-manager.ts
│ │ ├── handlers-workflow-diff.ts
│ │ ├── index.ts
│ │ ├── server.ts
│ │ ├── stdio-wrapper.ts
│ │ ├── tool-docs
│ │ │ ├── configuration
│ │ │ │ ├── get-node.ts
│ │ │ │ └── index.ts
│ │ │ ├── discovery
│ │ │ │ ├── index.ts
│ │ │ │ └── search-nodes.ts
│ │ │ ├── guides
│ │ │ │ ├── ai-agents-guide.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── system
│ │ │ │ ├── index.ts
│ │ │ │ ├── n8n-diagnostic.ts
│ │ │ │ ├── n8n-health-check.ts
│ │ │ │ ├── n8n-list-available-tools.ts
│ │ │ │ └── tools-documentation.ts
│ │ │ ├── templates
│ │ │ │ ├── get-template.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── search-templates.ts
│ │ │ ├── types.ts
│ │ │ ├── validation
│ │ │ │ ├── index.ts
│ │ │ │ ├── validate-node.ts
│ │ │ │ └── validate-workflow.ts
│ │ │ └── workflow_management
│ │ │ ├── index.ts
│ │ │ ├── n8n-autofix-workflow.ts
│ │ │ ├── n8n-create-workflow.ts
│ │ │ ├── n8n-delete-workflow.ts
│ │ │ ├── n8n-deploy-template.ts
│ │ │ ├── n8n-executions.ts
│ │ │ ├── n8n-get-workflow.ts
│ │ │ ├── n8n-list-workflows.ts
│ │ │ ├── n8n-trigger-webhook-workflow.ts
│ │ │ ├── n8n-update-full-workflow.ts
│ │ │ ├── n8n-update-partial-workflow.ts
│ │ │ ├── n8n-validate-workflow.ts
│ │ │ └── n8n-workflow-versions.ts
│ │ ├── tools-documentation.ts
│ │ ├── tools-n8n-friendly.ts
│ │ ├── tools-n8n-manager.ts
│ │ ├── tools.ts
│ │ └── workflow-examples.ts
│ ├── mcp-engine.ts
│ ├── mcp-tools-engine.ts
│ ├── n8n
│ │ ├── MCPApi.credentials.ts
│ │ └── MCPNode.node.ts
│ ├── parsers
│ │ ├── node-parser.ts
│ │ ├── property-extractor.ts
│ │ └── simple-parser.ts
│ ├── scripts
│ │ ├── debug-http-search.ts
│ │ ├── extract-from-docker.ts
│ │ ├── fetch-templates-robust.ts
│ │ ├── fetch-templates.ts
│ │ ├── rebuild-database.ts
│ │ ├── rebuild-optimized.ts
│ │ ├── rebuild.ts
│ │ ├── sanitize-templates.ts
│ │ ├── seed-canonical-ai-examples.ts
│ │ ├── test-autofix-documentation.ts
│ │ ├── test-autofix-workflow.ts
│ │ ├── test-execution-filtering.ts
│ │ ├── test-node-suggestions.ts
│ │ ├── test-protocol-negotiation.ts
│ │ ├── test-summary.ts
│ │ ├── test-telemetry-mutations-verbose.ts
│ │ ├── test-telemetry-mutations.ts
│ │ ├── test-webhook-autofix.ts
│ │ ├── validate.ts
│ │ └── validation-summary.ts
│ ├── services
│ │ ├── ai-node-validator.ts
│ │ ├── ai-tool-validators.ts
│ │ ├── breaking-change-detector.ts
│ │ ├── breaking-changes-registry.ts
│ │ ├── confidence-scorer.ts
│ │ ├── config-validator.ts
│ │ ├── enhanced-config-validator.ts
│ │ ├── example-generator.ts
│ │ ├── execution-processor.ts
│ │ ├── expression-format-validator.ts
│ │ ├── expression-validator.ts
│ │ ├── n8n-api-client.ts
│ │ ├── n8n-validation.ts
│ │ ├── node-documentation-service.ts
│ │ ├── node-migration-service.ts
│ │ ├── node-sanitizer.ts
│ │ ├── node-similarity-service.ts
│ │ ├── node-specific-validators.ts
│ │ ├── node-version-service.ts
│ │ ├── operation-similarity-service.ts
│ │ ├── post-update-validator.ts
│ │ ├── property-dependencies.ts
│ │ ├── property-filter.ts
│ │ ├── resource-similarity-service.ts
│ │ ├── sqlite-storage-service.ts
│ │ ├── task-templates.ts
│ │ ├── type-structure-service.ts
│ │ ├── universal-expression-validator.ts
│ │ ├── workflow-auto-fixer.ts
│ │ ├── workflow-diff-engine.ts
│ │ ├── workflow-validator.ts
│ │ └── workflow-versioning-service.ts
│ ├── telemetry
│ │ ├── batch-processor.ts
│ │ ├── config-manager.ts
│ │ ├── early-error-logger.ts
│ │ ├── error-sanitization-utils.ts
│ │ ├── error-sanitizer.ts
│ │ ├── event-tracker.ts
│ │ ├── event-validator.ts
│ │ ├── index.ts
│ │ ├── intent-classifier.ts
│ │ ├── intent-sanitizer.ts
│ │ ├── mutation-tracker.ts
│ │ ├── mutation-types.ts
│ │ ├── mutation-validator.ts
│ │ ├── performance-monitor.ts
│ │ ├── rate-limiter.ts
│ │ ├── startup-checkpoints.ts
│ │ ├── telemetry-error.ts
│ │ ├── telemetry-manager.ts
│ │ ├── telemetry-types.ts
│ │ └── workflow-sanitizer.ts
│ ├── templates
│ │ ├── batch-processor.ts
│ │ ├── metadata-generator.ts
│ │ ├── README.md
│ │ ├── template-fetcher.ts
│ │ ├── template-repository.ts
│ │ └── template-service.ts
│ ├── types
│ │ ├── index.ts
│ │ ├── instance-context.ts
│ │ ├── n8n-api.ts
│ │ ├── node-types.ts
│ │ ├── session-state.ts
│ │ ├── type-structures.ts
│ │ └── workflow-diff.ts
│ └── utils
│ ├── auth.ts
│ ├── bridge.ts
│ ├── cache-utils.ts
│ ├── console-manager.ts
│ ├── documentation-fetcher.ts
│ ├── enhanced-documentation-fetcher.ts
│ ├── error-handler.ts
│ ├── example-generator.ts
│ ├── expression-utils.ts
│ ├── fixed-collection-validator.ts
│ ├── logger.ts
│ ├── mcp-client.ts
│ ├── n8n-errors.ts
│ ├── node-classification.ts
│ ├── node-source-extractor.ts
│ ├── node-type-normalizer.ts
│ ├── node-type-utils.ts
│ ├── node-utils.ts
│ ├── npm-version-checker.ts
│ ├── protocol-version.ts
│ ├── simple-cache.ts
│ ├── ssrf-protection.ts
│ ├── template-node-resolver.ts
│ ├── template-sanitizer.ts
│ ├── url-detector.ts
│ ├── validation-schemas.ts
│ └── version.ts
├── test-output.txt
├── test-reinit-fix.sh
├── tests
│ ├── __snapshots__
│ │ └── .gitkeep
│ ├── auth.test.ts
│ ├── benchmarks
│ │ ├── database-queries.bench.ts
│ │ ├── index.ts
│ │ ├── mcp-tools.bench.ts
│ │ ├── mcp-tools.bench.ts.disabled
│ │ ├── mcp-tools.bench.ts.skip
│ │ ├── node-loading.bench.ts.disabled
│ │ ├── README.md
│ │ ├── search-operations.bench.ts.disabled
│ │ └── validation-performance.bench.ts.disabled
│ ├── bridge.test.ts
│ ├── comprehensive-extraction-test.js
│ ├── data
│ │ └── .gitkeep
│ ├── debug-slack-doc.js
│ ├── demo-enhanced-documentation.js
│ ├── docker-tests-README.md
│ ├── error-handler.test.ts
│ ├── examples
│ │ └── using-database-utils.test.ts
│ ├── extracted-nodes-db
│ │ ├── database-import.json
│ │ ├── extraction-report.json
│ │ ├── insert-nodes.sql
│ │ ├── n8n-nodes-base__Airtable.json
│ │ ├── n8n-nodes-base__Discord.json
│ │ ├── n8n-nodes-base__Function.json
│ │ ├── n8n-nodes-base__HttpRequest.json
│ │ ├── n8n-nodes-base__If.json
│ │ ├── n8n-nodes-base__Slack.json
│ │ ├── n8n-nodes-base__SplitInBatches.json
│ │ └── n8n-nodes-base__Webhook.json
│ ├── factories
│ │ ├── node-factory.ts
│ │ └── property-definition-factory.ts
│ ├── fixtures
│ │ ├── .gitkeep
│ │ ├── database
│ │ │ └── test-nodes.json
│ │ ├── factories
│ │ │ ├── node.factory.ts
│ │ │ └── parser-node.factory.ts
│ │ └── template-configs.ts
│ ├── helpers
│ │ └── env-helpers.ts
│ ├── http-server-auth.test.ts
│ ├── integration
│ │ ├── ai-validation
│ │ │ ├── ai-agent-validation.test.ts
│ │ │ ├── ai-tool-validation.test.ts
│ │ │ ├── chat-trigger-validation.test.ts
│ │ │ ├── e2e-validation.test.ts
│ │ │ ├── helpers.ts
│ │ │ ├── llm-chain-validation.test.ts
│ │ │ ├── README.md
│ │ │ └── TEST_REPORT.md
│ │ ├── ci
│ │ │ └── database-population.test.ts
│ │ ├── database
│ │ │ ├── connection-management.test.ts
│ │ │ ├── empty-database.test.ts
│ │ │ ├── fts5-search.test.ts
│ │ │ ├── node-fts5-search.test.ts
│ │ │ ├── node-repository.test.ts
│ │ │ ├── performance.test.ts
│ │ │ ├── sqljs-memory-leak.test.ts
│ │ │ ├── template-node-configs.test.ts
│ │ │ ├── template-repository.test.ts
│ │ │ ├── test-utils.ts
│ │ │ └── transactions.test.ts
│ │ ├── database-integration.test.ts
│ │ ├── docker
│ │ │ ├── docker-config.test.ts
│ │ │ ├── docker-entrypoint.test.ts
│ │ │ └── test-helpers.ts
│ │ ├── flexible-instance-config.test.ts
│ │ ├── mcp
│ │ │ └── template-examples-e2e.test.ts
│ │ ├── mcp-protocol
│ │ │ ├── basic-connection.test.ts
│ │ │ ├── error-handling.test.ts
│ │ │ ├── performance.test.ts
│ │ │ ├── protocol-compliance.test.ts
│ │ │ ├── README.md
│ │ │ ├── session-management.test.ts
│ │ │ ├── test-helpers.ts
│ │ │ ├── tool-invocation.test.ts
│ │ │ └── workflow-error-validation.test.ts
│ │ ├── msw-setup.test.ts
│ │ ├── n8n-api
│ │ │ ├── executions
│ │ │ │ ├── delete-execution.test.ts
│ │ │ │ ├── get-execution.test.ts
│ │ │ │ ├── list-executions.test.ts
│ │ │ │ └── trigger-webhook.test.ts
│ │ │ ├── scripts
│ │ │ │ └── cleanup-orphans.ts
│ │ │ ├── system
│ │ │ │ ├── diagnostic.test.ts
│ │ │ │ └── health-check.test.ts
│ │ │ ├── test-connection.ts
│ │ │ ├── types
│ │ │ │ └── mcp-responses.ts
│ │ │ ├── utils
│ │ │ │ ├── cleanup-helpers.ts
│ │ │ │ ├── credentials.ts
│ │ │ │ ├── factories.ts
│ │ │ │ ├── fixtures.ts
│ │ │ │ ├── mcp-context.ts
│ │ │ │ ├── n8n-client.ts
│ │ │ │ ├── node-repository.ts
│ │ │ │ ├── response-types.ts
│ │ │ │ ├── test-context.ts
│ │ │ │ └── webhook-workflows.ts
│ │ │ └── workflows
│ │ │ ├── autofix-workflow.test.ts
│ │ │ ├── create-workflow.test.ts
│ │ │ ├── delete-workflow.test.ts
│ │ │ ├── get-workflow-details.test.ts
│ │ │ ├── get-workflow-minimal.test.ts
│ │ │ ├── get-workflow-structure.test.ts
│ │ │ ├── get-workflow.test.ts
│ │ │ ├── list-workflows.test.ts
│ │ │ ├── smart-parameters.test.ts
│ │ │ ├── update-partial-workflow.test.ts
│ │ │ ├── update-workflow.test.ts
│ │ │ └── validate-workflow.test.ts
│ │ ├── security
│ │ │ ├── command-injection-prevention.test.ts
│ │ │ └── rate-limiting.test.ts
│ │ ├── setup
│ │ │ ├── integration-setup.ts
│ │ │ └── msw-test-server.ts
│ │ ├── telemetry
│ │ │ ├── docker-user-id-stability.test.ts
│ │ │ └── mcp-telemetry.test.ts
│ │ ├── templates
│ │ │ └── metadata-operations.test.ts
│ │ ├── validation
│ │ │ └── real-world-structure-validation.test.ts
│ │ ├── workflow-creation-node-type-format.test.ts
│ │ └── workflow-diff
│ │ ├── ai-node-connection-validation.test.ts
│ │ └── node-rename-integration.test.ts
│ ├── logger.test.ts
│ ├── MOCKING_STRATEGY.md
│ ├── mocks
│ │ ├── n8n-api
│ │ │ ├── data
│ │ │ │ ├── credentials.ts
│ │ │ │ ├── executions.ts
│ │ │ │ └── workflows.ts
│ │ │ ├── handlers.ts
│ │ │ └── index.ts
│ │ └── README.md
│ ├── node-storage-export.json
│ ├── setup
│ │ ├── global-setup.ts
│ │ ├── msw-setup.ts
│ │ ├── TEST_ENV_DOCUMENTATION.md
│ │ └── test-env.ts
│ ├── test-database-extraction.js
│ ├── test-direct-extraction.js
│ ├── test-enhanced-documentation.js
│ ├── test-enhanced-integration.js
│ ├── test-mcp-extraction.js
│ ├── test-mcp-server-extraction.js
│ ├── test-mcp-tools-integration.js
│ ├── test-node-documentation-service.js
│ ├── test-node-list.js
│ ├── test-package-info.js
│ ├── test-parsing-operations.js
│ ├── test-slack-node-complete.js
│ ├── test-small-rebuild.js
│ ├── test-sqlite-search.js
│ ├── test-storage-system.js
│ ├── unit
│ │ ├── __mocks__
│ │ │ ├── n8n-nodes-base.test.ts
│ │ │ ├── n8n-nodes-base.ts
│ │ │ └── README.md
│ │ ├── constants
│ │ │ └── type-structures.test.ts
│ │ ├── database
│ │ │ ├── __mocks__
│ │ │ │ └── better-sqlite3.ts
│ │ │ ├── database-adapter-unit.test.ts
│ │ │ ├── node-repository-core.test.ts
│ │ │ ├── node-repository-operations.test.ts
│ │ │ ├── node-repository-outputs.test.ts
│ │ │ ├── README.md
│ │ │ └── template-repository-core.test.ts
│ │ ├── docker
│ │ │ ├── config-security.test.ts
│ │ │ ├── edge-cases.test.ts
│ │ │ ├── parse-config.test.ts
│ │ │ └── serve-command.test.ts
│ │ ├── errors
│ │ │ └── validation-service-error.test.ts
│ │ ├── examples
│ │ │ └── using-n8n-nodes-base-mock.test.ts
│ │ ├── flexible-instance-security-advanced.test.ts
│ │ ├── flexible-instance-security.test.ts
│ │ ├── http-server
│ │ │ ├── multi-tenant-support.test.ts
│ │ │ └── session-persistence.test.ts
│ │ ├── http-server-n8n-mode.test.ts
│ │ ├── http-server-n8n-reinit.test.ts
│ │ ├── http-server-session-management.test.ts
│ │ ├── loaders
│ │ │ └── node-loader.test.ts
│ │ ├── mappers
│ │ │ └── docs-mapper.test.ts
│ │ ├── mcp
│ │ │ ├── disabled-tools-additional.test.ts
│ │ │ ├── disabled-tools.test.ts
│ │ │ ├── get-node-essentials-examples.test.ts
│ │ │ ├── get-node-unified.test.ts
│ │ │ ├── handlers-deploy-template.test.ts
│ │ │ ├── handlers-n8n-manager-simple.test.ts
│ │ │ ├── handlers-n8n-manager.test.ts
│ │ │ ├── handlers-workflow-diff.test.ts
│ │ │ ├── lru-cache-behavior.test.ts
│ │ │ ├── multi-tenant-tool-listing.test.ts.disabled
│ │ │ ├── parameter-validation.test.ts
│ │ │ ├── search-nodes-examples.test.ts
│ │ │ ├── tools-documentation.test.ts
│ │ │ └── tools.test.ts
│ │ ├── mcp-engine
│ │ │ └── session-persistence.test.ts
│ │ ├── monitoring
│ │ │ └── cache-metrics.test.ts
│ │ ├── MULTI_TENANT_TEST_COVERAGE.md
│ │ ├── multi-tenant-integration.test.ts
│ │ ├── parsers
│ │ │ ├── node-parser-outputs.test.ts
│ │ │ ├── node-parser.test.ts
│ │ │ ├── property-extractor.test.ts
│ │ │ └── simple-parser.test.ts
│ │ ├── scripts
│ │ │ └── fetch-templates-extraction.test.ts
│ │ ├── services
│ │ │ ├── ai-node-validator.test.ts
│ │ │ ├── ai-tool-validators.test.ts
│ │ │ ├── breaking-change-detector.test.ts
│ │ │ ├── confidence-scorer.test.ts
│ │ │ ├── config-validator-basic.test.ts
│ │ │ ├── config-validator-edge-cases.test.ts
│ │ │ ├── config-validator-node-specific.test.ts
│ │ │ ├── config-validator-security.test.ts
│ │ │ ├── debug-validator.test.ts
│ │ │ ├── enhanced-config-validator-integration.test.ts
│ │ │ ├── enhanced-config-validator-operations.test.ts
│ │ │ ├── enhanced-config-validator-type-structures.test.ts
│ │ │ ├── enhanced-config-validator.test.ts
│ │ │ ├── example-generator.test.ts
│ │ │ ├── execution-processor.test.ts
│ │ │ ├── expression-format-validator.test.ts
│ │ │ ├── expression-validator-edge-cases.test.ts
│ │ │ ├── expression-validator.test.ts
│ │ │ ├── fixed-collection-validation.test.ts
│ │ │ ├── loop-output-edge-cases.test.ts
│ │ │ ├── n8n-api-client.test.ts
│ │ │ ├── n8n-validation-sticky-notes.test.ts
│ │ │ ├── n8n-validation.test.ts
│ │ │ ├── node-migration-service.test.ts
│ │ │ ├── node-sanitizer.test.ts
│ │ │ ├── node-similarity-service.test.ts
│ │ │ ├── node-specific-validators.test.ts
│ │ │ ├── node-version-service.test.ts
│ │ │ ├── operation-similarity-service-comprehensive.test.ts
│ │ │ ├── operation-similarity-service.test.ts
│ │ │ ├── post-update-validator.test.ts
│ │ │ ├── property-dependencies.test.ts
│ │ │ ├── property-filter-edge-cases.test.ts
│ │ │ ├── property-filter.test.ts
│ │ │ ├── resource-similarity-service-comprehensive.test.ts
│ │ │ ├── resource-similarity-service.test.ts
│ │ │ ├── task-templates.test.ts
│ │ │ ├── template-service.test.ts
│ │ │ ├── type-structure-service.test.ts
│ │ │ ├── universal-expression-validator.test.ts
│ │ │ ├── validation-fixes.test.ts
│ │ │ ├── workflow-auto-fixer.test.ts
│ │ │ ├── workflow-diff-engine.test.ts
│ │ │ ├── workflow-diff-node-rename.test.ts
│ │ │ ├── workflow-fixed-collection-validation.test.ts
│ │ │ ├── workflow-validator-comprehensive.test.ts
│ │ │ ├── workflow-validator-edge-cases.test.ts
│ │ │ ├── workflow-validator-error-outputs.test.ts
│ │ │ ├── workflow-validator-expression-format.test.ts
│ │ │ ├── workflow-validator-loops-simple.test.ts
│ │ │ ├── workflow-validator-loops.test.ts
│ │ │ ├── workflow-validator-mocks.test.ts
│ │ │ ├── workflow-validator-performance.test.ts
│ │ │ ├── workflow-validator-with-mocks.test.ts
│ │ │ ├── workflow-validator.test.ts
│ │ │ └── workflow-versioning-service.test.ts
│ │ ├── telemetry
│ │ │ ├── batch-processor.test.ts
│ │ │ ├── config-manager.test.ts
│ │ │ ├── event-tracker.test.ts
│ │ │ ├── event-validator.test.ts
│ │ │ ├── mutation-tracker.test.ts
│ │ │ ├── mutation-validator.test.ts
│ │ │ ├── rate-limiter.test.ts
│ │ │ ├── telemetry-error.test.ts
│ │ │ ├── telemetry-manager.test.ts
│ │ │ ├── v2.18.3-fixes-verification.test.ts
│ │ │ └── workflow-sanitizer.test.ts
│ │ ├── templates
│ │ │ ├── batch-processor.test.ts
│ │ │ ├── metadata-generator.test.ts
│ │ │ ├── template-repository-metadata.test.ts
│ │ │ └── template-repository-security.test.ts
│ │ ├── test-env-example.test.ts
│ │ ├── test-infrastructure.test.ts
│ │ ├── types
│ │ │ ├── instance-context-coverage.test.ts
│ │ │ ├── instance-context-multi-tenant.test.ts
│ │ │ └── type-structures.test.ts
│ │ ├── utils
│ │ │ ├── auth-timing-safe.test.ts
│ │ │ ├── cache-utils.test.ts
│ │ │ ├── console-manager.test.ts
│ │ │ ├── database-utils.test.ts
│ │ │ ├── expression-utils.test.ts
│ │ │ ├── fixed-collection-validator.test.ts
│ │ │ ├── n8n-errors.test.ts
│ │ │ ├── node-classification.test.ts
│ │ │ ├── node-type-normalizer.test.ts
│ │ │ ├── node-type-utils.test.ts
│ │ │ ├── node-utils.test.ts
│ │ │ ├── simple-cache-memory-leak-fix.test.ts
│ │ │ ├── ssrf-protection.test.ts
│ │ │ └── template-node-resolver.test.ts
│ │ └── validation-fixes.test.ts
│ └── utils
│ ├── assertions.ts
│ ├── builders
│ │ └── workflow.builder.ts
│ ├── data-generators.ts
│ ├── database-utils.ts
│ ├── README.md
│ └── test-helpers.ts
├── thumbnail.png
├── tsconfig.build.json
├── tsconfig.json
├── types
│ ├── mcp.d.ts
│ └── test-env.d.ts
├── versioned-nodes.md
├── vitest.config.benchmark.ts
├── vitest.config.integration.ts
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/tests/extracted-nodes-db/n8n-nodes-base__HttpRequest.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "node_type": "n8n-nodes-base.HttpRequest",
3 | "name": "HttpRequest",
4 | "package_name": "n8n-nodes-base",
5 | "code_hash": "5b5e2328474b7e85361c940dfe942e167b3f0057f38062f56d6b693f0a7ffe7e",
6 | "code_length": 1343,
7 | "source_location": "node_modules/n8n-nodes-base/dist/nodes/HttpRequest/HttpRequest.node.js",
8 | "has_credentials": false,
9 | "source_code": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.HttpRequest = void 0;\nconst n8n_workflow_1 = require(\"n8n-workflow\");\nconst HttpRequestV1_node_1 = require(\"./V1/HttpRequestV1.node\");\nconst HttpRequestV2_node_1 = require(\"./V2/HttpRequestV2.node\");\nconst HttpRequestV3_node_1 = require(\"./V3/HttpRequestV3.node\");\nclass HttpRequest extends n8n_workflow_1.VersionedNodeType {\n constructor() {\n const baseDescription = {\n displayName: 'HTTP Request',\n name: 'httpRequest',\n icon: 'fa:at',\n group: ['output'],\n subtitle: '={{$parameter[\"requestMethod\"] + \": \" + $parameter[\"url\"]}}',\n description: 'Makes an HTTP request and returns the response data',\n defaultVersion: 4.1,\n };\n const nodeVersions = {\n 1: new HttpRequestV1_node_1.HttpRequestV1(baseDescription),\n 2: new HttpRequestV2_node_1.HttpRequestV2(baseDescription),\n 3: new HttpRequestV3_node_1.HttpRequestV3(baseDescription),\n 4: new HttpRequestV3_node_1.HttpRequestV3(baseDescription),\n 4.1: new HttpRequestV3_node_1.HttpRequestV3(baseDescription),\n };\n super(nodeVersions, baseDescription);\n }\n}\nexports.HttpRequest = HttpRequest;\n//# sourceMappingURL=HttpRequest.node.js.map",
10 | "package_info": {
11 | "name": "n8n-nodes-base",
12 | "version": "1.14.1",
13 | "description": "Base nodes of n8n",
14 | "license": "SEE LICENSE IN LICENSE.md",
15 | "homepage": "https://n8n.io",
16 | "author": {
17 | "name": "Jan Oberhauser",
18 | "email": "[email protected]"
19 | },
20 | "main": "index.js",
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/n8n-io/n8n.git"
24 | },
25 | "files": [
26 | "dist"
27 | ],
28 | "n8n": {
29 | "credentials": [
30 | "dist/credentials/ActionNetworkApi.credentials.js",
31 | "dist/credentials/ActiveCampaignApi.credentials.js",
32 | "dist/credentials/AcuitySchedulingApi.credentials.js",
33 | "dist/credentials/AcuitySchedulingOAuth2Api.credentials.js",
34 | "dist/credentials/AdaloApi.credentials.js",
35 | "dist/credentials/AffinityApi.credentials.js",
36 | "dist/credentials/AgileCrmApi.credentials.js",
37 | "dist/credentials/AirtableApi.credentials.js",
38 | "dist/credentials/AirtableOAuth2Api.credentials.js",
39 | "dist/credentials/AirtableTokenApi.credentials.js",
40 | "dist/credentials/AlienVaultApi.credentials.js",
41 | "dist/credentials/Amqp.credentials.js",
42 | "dist/credentials/ApiTemplateIoApi.credentials.js",
43 | "dist/credentials/AsanaApi.credentials.js",
44 | "dist/credentials/AsanaOAuth2Api.credentials.js",
45 | "dist/credentials/Auth0ManagementApi.credentials.js",
46 | "dist/credentials/AutomizyApi.credentials.js",
47 | "dist/credentials/AutopilotApi.credentials.js",
48 | "dist/credentials/Aws.credentials.js",
49 | "dist/credentials/BambooHrApi.credentials.js",
50 | "dist/credentials/BannerbearApi.credentials.js",
51 | "dist/credentials/BaserowApi.credentials.js",
52 | "dist/credentials/BeeminderApi.credentials.js",
53 | "dist/credentials/BitbucketApi.credentials.js",
54 | "dist/credentials/BitlyApi.credentials.js",
55 | "dist/credentials/BitlyOAuth2Api.credentials.js",
56 | "dist/credentials/BitwardenApi.credentials.js",
57 | "dist/credentials/BoxOAuth2Api.credentials.js",
58 | "dist/credentials/BrandfetchApi.credentials.js",
59 | "dist/credentials/BubbleApi.credentials.js",
60 | "dist/credentials/CalApi.credentials.js",
61 | "dist/credentials/CalendlyApi.credentials.js",
62 | "dist/credentials/CarbonBlackApi.credentials.js",
63 | "dist/credentials/ChargebeeApi.credentials.js",
64 | "dist/credentials/CircleCiApi.credentials.js",
65 | "dist/credentials/CiscoMerakiApi.credentials.js",
66 | "dist/credentials/CiscoSecureEndpointApi.credentials.js",
67 | "dist/credentials/CiscoWebexOAuth2Api.credentials.js",
68 | "dist/credentials/CiscoUmbrellaApi.credentials.js",
69 | "dist/credentials/CitrixAdcApi.credentials.js",
70 | "dist/credentials/CloudflareApi.credentials.js",
71 | "dist/credentials/ClearbitApi.credentials.js",
72 | "dist/credentials/ClickUpApi.credentials.js",
73 | "dist/credentials/ClickUpOAuth2Api.credentials.js",
74 | "dist/credentials/ClockifyApi.credentials.js",
75 | "dist/credentials/CockpitApi.credentials.js",
76 | "dist/credentials/CodaApi.credentials.js",
77 | "dist/credentials/ContentfulApi.credentials.js",
78 | "dist/credentials/ConvertKitApi.credentials.js",
79 | "dist/credentials/CopperApi.credentials.js",
80 | "dist/credentials/CortexApi.credentials.js",
81 | "dist/credentials/CrateDb.credentials.js",
82 | "dist/credentials/CrowdStrikeOAuth2Api.credentials.js",
83 | "dist/credentials/CrowdDevApi.credentials.js",
84 | "dist/credentials/CustomerIoApi.credentials.js",
85 | "dist/credentials/DeepLApi.credentials.js",
86 | "dist/credentials/DemioApi.credentials.js",
87 | "dist/credentials/DhlApi.credentials.js",
88 | "dist/credentials/DiscourseApi.credentials.js",
89 | "dist/credentials/DisqusApi.credentials.js",
90 | "dist/credentials/DriftApi.credentials.js",
91 | "dist/credentials/DriftOAuth2Api.credentials.js",
92 | "dist/credentials/DropboxApi.credentials.js",
93 | "dist/credentials/DropboxOAuth2Api.credentials.js",
94 | "dist/credentials/DropcontactApi.credentials.js",
95 | "dist/credentials/EgoiApi.credentials.js",
96 | "dist/credentials/ElasticsearchApi.credentials.js",
97 | "dist/credentials/ElasticSecurityApi.credentials.js",
98 | "dist/credentials/EmeliaApi.credentials.js",
99 | "dist/credentials/ERPNextApi.credentials.js",
100 | "dist/credentials/EventbriteApi.credentials.js",
101 | "dist/credentials/EventbriteOAuth2Api.credentials.js",
102 | "dist/credentials/F5BigIpApi.credentials.js",
103 | "dist/credentials/FacebookGraphApi.credentials.js",
104 | "dist/credentials/FacebookGraphAppApi.credentials.js",
105 | "dist/credentials/FacebookLeadAdsOAuth2Api.credentials.js",
106 | "dist/credentials/FigmaApi.credentials.js",
107 | "dist/credentials/FileMaker.credentials.js",
108 | "dist/credentials/FlowApi.credentials.js",
109 | "dist/credentials/FormIoApi.credentials.js",
110 | "dist/credentials/FormstackApi.credentials.js",
111 | "dist/credentials/FormstackOAuth2Api.credentials.js",
112 | "dist/credentials/FortiGateApi.credentials.js",
113 | "dist/credentials/FreshdeskApi.credentials.js",
114 | "dist/credentials/FreshserviceApi.credentials.js",
115 | "dist/credentials/FreshworksCrmApi.credentials.js",
116 | "dist/credentials/Ftp.credentials.js",
117 | "dist/credentials/GetResponseApi.credentials.js",
118 | "dist/credentials/GetResponseOAuth2Api.credentials.js",
119 | "dist/credentials/GhostAdminApi.credentials.js",
120 | "dist/credentials/GhostContentApi.credentials.js",
121 | "dist/credentials/GithubApi.credentials.js",
122 | "dist/credentials/GithubOAuth2Api.credentials.js",
123 | "dist/credentials/GitlabApi.credentials.js",
124 | "dist/credentials/GitlabOAuth2Api.credentials.js",
125 | "dist/credentials/GitPassword.credentials.js",
126 | "dist/credentials/GmailOAuth2Api.credentials.js",
127 | "dist/credentials/GoogleAdsOAuth2Api.credentials.js",
128 | "dist/credentials/GoogleAnalyticsOAuth2Api.credentials.js",
129 | "dist/credentials/GoogleApi.credentials.js",
130 | "dist/credentials/GoogleBigQueryOAuth2Api.credentials.js",
131 | "dist/credentials/GoogleBooksOAuth2Api.credentials.js",
132 | "dist/credentials/GoogleCalendarOAuth2Api.credentials.js",
133 | "dist/credentials/GoogleCloudNaturalLanguageOAuth2Api.credentials.js",
134 | "dist/credentials/GoogleCloudStorageOAuth2Api.credentials.js",
135 | "dist/credentials/GoogleContactsOAuth2Api.credentials.js",
136 | "dist/credentials/GoogleDocsOAuth2Api.credentials.js",
137 | "dist/credentials/GoogleDriveOAuth2Api.credentials.js",
138 | "dist/credentials/GoogleFirebaseCloudFirestoreOAuth2Api.credentials.js",
139 | "dist/credentials/GoogleFirebaseRealtimeDatabaseOAuth2Api.credentials.js",
140 | "dist/credentials/GoogleOAuth2Api.credentials.js",
141 | "dist/credentials/GooglePerspectiveOAuth2Api.credentials.js",
142 | "dist/credentials/GoogleSheetsOAuth2Api.credentials.js",
143 | "dist/credentials/GoogleSheetsTriggerOAuth2Api.credentials.js",
144 | "dist/credentials/GoogleSlidesOAuth2Api.credentials.js",
145 | "dist/credentials/GoogleTasksOAuth2Api.credentials.js",
146 | "dist/credentials/GoogleTranslateOAuth2Api.credentials.js",
147 | "dist/credentials/GotifyApi.credentials.js",
148 | "dist/credentials/GoToWebinarOAuth2Api.credentials.js",
149 | "dist/credentials/GristApi.credentials.js",
150 | "dist/credentials/GrafanaApi.credentials.js",
151 | "dist/credentials/GSuiteAdminOAuth2Api.credentials.js",
152 | "dist/credentials/GumroadApi.credentials.js",
153 | "dist/credentials/HaloPSAApi.credentials.js",
154 | "dist/credentials/HarvestApi.credentials.js",
155 | "dist/credentials/HarvestOAuth2Api.credentials.js",
156 | "dist/credentials/HelpScoutOAuth2Api.credentials.js",
157 | "dist/credentials/HighLevelApi.credentials.js",
158 | "dist/credentials/HomeAssistantApi.credentials.js",
159 | "dist/credentials/HttpBasicAuth.credentials.js",
160 | "dist/credentials/HttpDigestAuth.credentials.js",
161 | "dist/credentials/HttpHeaderAuth.credentials.js",
162 | "dist/credentials/HttpCustomAuth.credentials.js",
163 | "dist/credentials/HttpQueryAuth.credentials.js",
164 | "dist/credentials/HubspotApi.credentials.js",
165 | "dist/credentials/HubspotAppToken.credentials.js",
166 | "dist/credentials/HubspotDeveloperApi.credentials.js",
167 | "dist/credentials/HubspotOAuth2Api.credentials.js",
168 | "dist/credentials/HumanticAiApi.credentials.js",
169 | "dist/credentials/HunterApi.credentials.js",
170 | "dist/credentials/HybridAnalysisApi.credentials.js",
171 | "dist/credentials/Imap.credentials.js",
172 | "dist/credentials/ImpervaWafApi.credentials.js",
173 | "dist/credentials/IntercomApi.credentials.js",
174 | "dist/credentials/InvoiceNinjaApi.credentials.js",
175 | "dist/credentials/IterableApi.credentials.js",
176 | "dist/credentials/JenkinsApi.credentials.js",
177 | "dist/credentials/JiraSoftwareCloudApi.credentials.js",
178 | "dist/credentials/JiraSoftwareServerApi.credentials.js",
179 | "dist/credentials/JotFormApi.credentials.js",
180 | "dist/credentials/Kafka.credentials.js",
181 | "dist/credentials/KeapOAuth2Api.credentials.js",
182 | "dist/credentials/KibanaApi.credentials.js",
183 | "dist/credentials/KitemakerApi.credentials.js",
184 | "dist/credentials/KoBoToolboxApi.credentials.js",
185 | "dist/credentials/Ldap.credentials.js",
186 | "dist/credentials/LemlistApi.credentials.js",
187 | "dist/credentials/LinearApi.credentials.js",
188 | "dist/credentials/LinearOAuth2Api.credentials.js",
189 | "dist/credentials/LineNotifyOAuth2Api.credentials.js",
190 | "dist/credentials/LingvaNexApi.credentials.js",
191 | "dist/credentials/LinkedInOAuth2Api.credentials.js",
192 | "dist/credentials/LoneScaleApi.credentials.js",
193 | "dist/credentials/Magento2Api.credentials.js",
194 | "dist/credentials/MailcheckApi.credentials.js",
195 | "dist/credentials/MailchimpApi.credentials.js",
196 | "dist/credentials/MailchimpOAuth2Api.credentials.js",
197 | "dist/credentials/MailerLiteApi.credentials.js",
198 | "dist/credentials/MailgunApi.credentials.js",
199 | "dist/credentials/MailjetEmailApi.credentials.js",
200 | "dist/credentials/MailjetSmsApi.credentials.js",
201 | "dist/credentials/MandrillApi.credentials.js",
202 | "dist/credentials/MarketstackApi.credentials.js",
203 | "dist/credentials/MatrixApi.credentials.js",
204 | "dist/credentials/MattermostApi.credentials.js",
205 | "dist/credentials/MauticApi.credentials.js",
206 | "dist/credentials/MauticOAuth2Api.credentials.js",
207 | "dist/credentials/MediumApi.credentials.js",
208 | "dist/credentials/MediumOAuth2Api.credentials.js",
209 | "dist/credentials/MetabaseApi.credentials.js",
210 | "dist/credentials/MessageBirdApi.credentials.js",
211 | "dist/credentials/MetabaseApi.credentials.js",
212 | "dist/credentials/MicrosoftDynamicsOAuth2Api.credentials.js",
213 | "dist/credentials/MicrosoftEntraOAuth2Api.credentials.js",
214 | "dist/credentials/MicrosoftExcelOAuth2Api.credentials.js",
215 | "dist/credentials/MicrosoftGraphSecurityOAuth2Api.credentials.js",
216 | "dist/credentials/MicrosoftOAuth2Api.credentials.js",
217 | "dist/credentials/MicrosoftOneDriveOAuth2Api.credentials.js",
218 | "dist/credentials/MicrosoftOutlookOAuth2Api.credentials.js",
219 | "dist/credentials/MicrosoftSql.credentials.js",
220 | "dist/credentials/MicrosoftTeamsOAuth2Api.credentials.js",
221 | "dist/credentials/MicrosoftToDoOAuth2Api.credentials.js",
222 | "dist/credentials/MindeeInvoiceApi.credentials.js",
223 | "dist/credentials/MindeeReceiptApi.credentials.js",
224 | "dist/credentials/MispApi.credentials.js",
225 | "dist/credentials/MistApi.credentials.js",
226 | "dist/credentials/MoceanApi.credentials.js",
227 | "dist/credentials/MondayComApi.credentials.js",
228 | "dist/credentials/MondayComOAuth2Api.credentials.js",
229 | "dist/credentials/MongoDb.credentials.js",
230 | "dist/credentials/MonicaCrmApi.credentials.js",
231 | "dist/credentials/Mqtt.credentials.js",
232 | "dist/credentials/Msg91Api.credentials.js",
233 | "dist/credentials/MySql.credentials.js",
234 | "dist/credentials/N8nApi.credentials.js",
235 | "dist/credentials/NasaApi.credentials.js",
236 | "dist/credentials/NetlifyApi.credentials.js",
237 | "dist/credentials/NextCloudApi.credentials.js",
238 | "dist/credentials/NextCloudOAuth2Api.credentials.js",
239 | "dist/credentials/NocoDb.credentials.js",
240 | "dist/credentials/NocoDbApiToken.credentials.js",
241 | "dist/credentials/NotionApi.credentials.js",
242 | "dist/credentials/NotionOAuth2Api.credentials.js",
243 | "dist/credentials/NpmApi.credentials.js",
244 | "dist/credentials/OAuth1Api.credentials.js",
245 | "dist/credentials/OAuth2Api.credentials.js",
246 | "dist/credentials/OdooApi.credentials.js",
247 | "dist/credentials/OktaApi.credentials.js",
248 | "dist/credentials/OneSimpleApi.credentials.js",
249 | "dist/credentials/OnfleetApi.credentials.js",
250 | "dist/credentials/OpenAiApi.credentials.js",
251 | "dist/credentials/OpenCTIApi.credentials.js",
252 | "dist/credentials/OpenWeatherMapApi.credentials.js",
253 | "dist/credentials/OrbitApi.credentials.js",
254 | "dist/credentials/OuraApi.credentials.js",
255 | "dist/credentials/PaddleApi.credentials.js",
256 | "dist/credentials/PagerDutyApi.credentials.js",
257 | "dist/credentials/PagerDutyOAuth2Api.credentials.js",
258 | "dist/credentials/PayPalApi.credentials.js",
259 | "dist/credentials/PeekalinkApi.credentials.js",
260 | "dist/credentials/PhantombusterApi.credentials.js",
261 | "dist/credentials/PhilipsHueOAuth2Api.credentials.js",
262 | "dist/credentials/PipedriveApi.credentials.js",
263 | "dist/credentials/PipedriveOAuth2Api.credentials.js",
264 | "dist/credentials/PlivoApi.credentials.js",
265 | "dist/credentials/Postgres.credentials.js",
266 | "dist/credentials/PostHogApi.credentials.js",
267 | "dist/credentials/PostmarkApi.credentials.js",
268 | "dist/credentials/ProfitWellApi.credentials.js",
269 | "dist/credentials/PushbulletOAuth2Api.credentials.js",
270 | "dist/credentials/PushcutApi.credentials.js",
271 | "dist/credentials/PushoverApi.credentials.js",
272 | "dist/credentials/QRadarApi.credentials.js",
273 | "dist/credentials/QualysApi.credentials.js",
274 | "dist/credentials/QuestDb.credentials.js",
275 | "dist/credentials/QuickBaseApi.credentials.js",
276 | "dist/credentials/QuickBooksOAuth2Api.credentials.js",
277 | "dist/credentials/RabbitMQ.credentials.js",
278 | "dist/credentials/RaindropOAuth2Api.credentials.js",
279 | "dist/credentials/RecordedFutureApi.credentials.js",
280 | "dist/credentials/RedditOAuth2Api.credentials.js",
281 | "dist/credentials/Redis.credentials.js",
282 | "dist/credentials/RocketchatApi.credentials.js",
283 | "dist/credentials/RundeckApi.credentials.js",
284 | "dist/credentials/S3.credentials.js",
285 | "dist/credentials/SalesforceJwtApi.credentials.js",
286 | "dist/credentials/SalesforceOAuth2Api.credentials.js",
287 | "dist/credentials/SalesmateApi.credentials.js",
288 | "dist/credentials/SeaTableApi.credentials.js",
289 | "dist/credentials/SecurityScorecardApi.credentials.js",
290 | "dist/credentials/SegmentApi.credentials.js",
291 | "dist/credentials/SekoiaApi.credentials.js",
292 | "dist/credentials/SendGridApi.credentials.js",
293 | "dist/credentials/BrevoApi.credentials.js",
294 | "dist/credentials/SendyApi.credentials.js",
295 | "dist/credentials/SentryIoApi.credentials.js",
296 | "dist/credentials/SentryIoOAuth2Api.credentials.js",
297 | "dist/credentials/SentryIoServerApi.credentials.js",
298 | "dist/credentials/ServiceNowOAuth2Api.credentials.js",
299 | "dist/credentials/ServiceNowBasicApi.credentials.js",
300 | "dist/credentials/Sftp.credentials.js",
301 | "dist/credentials/ShopifyApi.credentials.js",
302 | "dist/credentials/ShopifyAccessTokenApi.credentials.js",
303 | "dist/credentials/ShopifyOAuth2Api.credentials.js",
304 | "dist/credentials/Signl4Api.credentials.js",
305 | "dist/credentials/SlackApi.credentials.js",
306 | "dist/credentials/SlackOAuth2Api.credentials.js",
307 | "dist/credentials/Sms77Api.credentials.js",
308 | "dist/credentials/Smtp.credentials.js",
309 | "dist/credentials/Snowflake.credentials.js",
310 | "dist/credentials/SplunkApi.credentials.js",
311 | "dist/credentials/SpontitApi.credentials.js",
312 | "dist/credentials/SpotifyOAuth2Api.credentials.js",
313 | "dist/credentials/ShufflerApi.credentials.js",
314 | "dist/credentials/SshPassword.credentials.js",
315 | "dist/credentials/SshPrivateKey.credentials.js",
316 | "dist/credentials/StackbyApi.credentials.js",
317 | "dist/credentials/StoryblokContentApi.credentials.js",
318 | "dist/credentials/StoryblokManagementApi.credentials.js",
319 | "dist/credentials/StrapiApi.credentials.js",
320 | "dist/credentials/StrapiTokenApi.credentials.js",
321 | "dist/credentials/StravaOAuth2Api.credentials.js",
322 | "dist/credentials/StripeApi.credentials.js",
323 | "dist/credentials/SupabaseApi.credentials.js",
324 | "dist/credentials/SurveyMonkeyApi.credentials.js",
325 | "dist/credentials/SurveyMonkeyOAuth2Api.credentials.js",
326 | "dist/credentials/SyncroMspApi.credentials.js",
327 | "dist/credentials/TaigaApi.credentials.js",
328 | "dist/credentials/TapfiliateApi.credentials.js",
329 | "dist/credentials/TelegramApi.credentials.js",
330 | "dist/credentials/TheHiveProjectApi.credentials.js",
331 | "dist/credentials/TheHiveApi.credentials.js",
332 | "dist/credentials/TimescaleDb.credentials.js",
333 | "dist/credentials/TodoistApi.credentials.js",
334 | "dist/credentials/TodoistOAuth2Api.credentials.js",
335 | "dist/credentials/TogglApi.credentials.js",
336 | "dist/credentials/TotpApi.credentials.js",
337 | "dist/credentials/TravisCiApi.credentials.js",
338 | "dist/credentials/TrellixEpoApi.credentials.js",
339 | "dist/credentials/TrelloApi.credentials.js",
340 | "dist/credentials/TwakeCloudApi.credentials.js",
341 | "dist/credentials/TwakeServerApi.credentials.js",
342 | "dist/credentials/TwilioApi.credentials.js",
343 | "dist/credentials/TwistOAuth2Api.credentials.js",
344 | "dist/credentials/TwitterOAuth1Api.credentials.js",
345 | "dist/credentials/TwitterOAuth2Api.credentials.js",
346 | "dist/credentials/TypeformApi.credentials.js",
347 | "dist/credentials/TypeformOAuth2Api.credentials.js",
348 | "dist/credentials/UnleashedSoftwareApi.credentials.js",
349 | "dist/credentials/UpleadApi.credentials.js",
350 | "dist/credentials/UProcApi.credentials.js",
351 | "dist/credentials/UptimeRobotApi.credentials.js",
352 | "dist/credentials/UrlScanIoApi.credentials.js",
353 | "dist/credentials/VeroApi.credentials.js",
354 | "dist/credentials/VirusTotalApi.credentials.js",
355 | "dist/credentials/VonageApi.credentials.js",
356 | "dist/credentials/VenafiTlsProtectCloudApi.credentials.js",
357 | "dist/credentials/VenafiTlsProtectDatacenterApi.credentials.js",
358 | "dist/credentials/WebflowApi.credentials.js",
359 | "dist/credentials/WebflowOAuth2Api.credentials.js",
360 | "dist/credentials/WekanApi.credentials.js",
361 | "dist/credentials/WhatsAppApi.credentials.js",
362 | "dist/credentials/WiseApi.credentials.js",
363 | "dist/credentials/WooCommerceApi.credentials.js",
364 | "dist/credentials/WordpressApi.credentials.js",
365 | "dist/credentials/WorkableApi.credentials.js",
366 | "dist/credentials/WufooApi.credentials.js",
367 | "dist/credentials/XeroOAuth2Api.credentials.js",
368 | "dist/credentials/YourlsApi.credentials.js",
369 | "dist/credentials/YouTubeOAuth2Api.credentials.js",
370 | "dist/credentials/ZammadBasicAuthApi.credentials.js",
371 | "dist/credentials/ZammadTokenAuthApi.credentials.js",
372 | "dist/credentials/ZendeskApi.credentials.js",
373 | "dist/credentials/ZendeskOAuth2Api.credentials.js",
374 | "dist/credentials/ZohoOAuth2Api.credentials.js",
375 | "dist/credentials/ZoomApi.credentials.js",
376 | "dist/credentials/ZoomOAuth2Api.credentials.js",
377 | "dist/credentials/ZscalerZiaApi.credentials.js",
378 | "dist/credentials/ZulipApi.credentials.js"
379 | ],
380 | "nodes": [
381 | "dist/nodes/ActionNetwork/ActionNetwork.node.js",
382 | "dist/nodes/ActiveCampaign/ActiveCampaign.node.js",
383 | "dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js",
384 | "dist/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.js",
385 | "dist/nodes/Adalo/Adalo.node.js",
386 | "dist/nodes/Affinity/Affinity.node.js",
387 | "dist/nodes/Affinity/AffinityTrigger.node.js",
388 | "dist/nodes/AgileCrm/AgileCrm.node.js",
389 | "dist/nodes/Airtable/Airtable.node.js",
390 | "dist/nodes/Airtable/AirtableTrigger.node.js",
391 | "dist/nodes/Amqp/Amqp.node.js",
392 | "dist/nodes/Amqp/AmqpTrigger.node.js",
393 | "dist/nodes/ApiTemplateIo/ApiTemplateIo.node.js",
394 | "dist/nodes/Asana/Asana.node.js",
395 | "dist/nodes/Asana/AsanaTrigger.node.js",
396 | "dist/nodes/Automizy/Automizy.node.js",
397 | "dist/nodes/Autopilot/Autopilot.node.js",
398 | "dist/nodes/Autopilot/AutopilotTrigger.node.js",
399 | "dist/nodes/Aws/AwsLambda.node.js",
400 | "dist/nodes/Aws/AwsSns.node.js",
401 | "dist/nodes/Aws/AwsSnsTrigger.node.js",
402 | "dist/nodes/Aws/CertificateManager/AwsCertificateManager.node.js",
403 | "dist/nodes/Aws/Comprehend/AwsComprehend.node.js",
404 | "dist/nodes/Aws/DynamoDB/AwsDynamoDB.node.js",
405 | "dist/nodes/Aws/ELB/AwsElb.node.js",
406 | "dist/nodes/Aws/Rekognition/AwsRekognition.node.js",
407 | "dist/nodes/Aws/S3/AwsS3.node.js",
408 | "dist/nodes/Aws/SES/AwsSes.node.js",
409 | "dist/nodes/Aws/SQS/AwsSqs.node.js",
410 | "dist/nodes/Aws/Textract/AwsTextract.node.js",
411 | "dist/nodes/Aws/Transcribe/AwsTranscribe.node.js",
412 | "dist/nodes/BambooHr/BambooHr.node.js",
413 | "dist/nodes/Bannerbear/Bannerbear.node.js",
414 | "dist/nodes/Baserow/Baserow.node.js",
415 | "dist/nodes/Beeminder/Beeminder.node.js",
416 | "dist/nodes/Bitbucket/BitbucketTrigger.node.js",
417 | "dist/nodes/Bitly/Bitly.node.js",
418 | "dist/nodes/Bitwarden/Bitwarden.node.js",
419 | "dist/nodes/Box/Box.node.js",
420 | "dist/nodes/Box/BoxTrigger.node.js",
421 | "dist/nodes/Brandfetch/Brandfetch.node.js",
422 | "dist/nodes/Bubble/Bubble.node.js",
423 | "dist/nodes/Cal/CalTrigger.node.js",
424 | "dist/nodes/Calendly/CalendlyTrigger.node.js",
425 | "dist/nodes/Chargebee/Chargebee.node.js",
426 | "dist/nodes/Chargebee/ChargebeeTrigger.node.js",
427 | "dist/nodes/CircleCi/CircleCi.node.js",
428 | "dist/nodes/Cisco/Webex/CiscoWebex.node.js",
429 | "dist/nodes/Citrix/ADC/CitrixAdc.node.js",
430 | "dist/nodes/Cisco/Webex/CiscoWebexTrigger.node.js",
431 | "dist/nodes/Cloudflare/Cloudflare.node.js",
432 | "dist/nodes/Clearbit/Clearbit.node.js",
433 | "dist/nodes/ClickUp/ClickUp.node.js",
434 | "dist/nodes/ClickUp/ClickUpTrigger.node.js",
435 | "dist/nodes/Clockify/Clockify.node.js",
436 | "dist/nodes/Clockify/ClockifyTrigger.node.js",
437 | "dist/nodes/Cockpit/Cockpit.node.js",
438 | "dist/nodes/Coda/Coda.node.js",
439 | "dist/nodes/Code/Code.node.js",
440 | "dist/nodes/CoinGecko/CoinGecko.node.js",
441 | "dist/nodes/CompareDatasets/CompareDatasets.node.js",
442 | "dist/nodes/Compression/Compression.node.js",
443 | "dist/nodes/Contentful/Contentful.node.js",
444 | "dist/nodes/ConvertKit/ConvertKit.node.js",
445 | "dist/nodes/ConvertKit/ConvertKitTrigger.node.js",
446 | "dist/nodes/Copper/Copper.node.js",
447 | "dist/nodes/Copper/CopperTrigger.node.js",
448 | "dist/nodes/Cortex/Cortex.node.js",
449 | "dist/nodes/CrateDb/CrateDb.node.js",
450 | "dist/nodes/Cron/Cron.node.js",
451 | "dist/nodes/CrowdDev/CrowdDev.node.js",
452 | "dist/nodes/CrowdDev/CrowdDevTrigger.node.js",
453 | "dist/nodes/Crypto/Crypto.node.js",
454 | "dist/nodes/CustomerIo/CustomerIo.node.js",
455 | "dist/nodes/CustomerIo/CustomerIoTrigger.node.js",
456 | "dist/nodes/DateTime/DateTime.node.js",
457 | "dist/nodes/DebugHelper/DebugHelper.node.js",
458 | "dist/nodes/DeepL/DeepL.node.js",
459 | "dist/nodes/Demio/Demio.node.js",
460 | "dist/nodes/Dhl/Dhl.node.js",
461 | "dist/nodes/Discord/Discord.node.js",
462 | "dist/nodes/Discourse/Discourse.node.js",
463 | "dist/nodes/Disqus/Disqus.node.js",
464 | "dist/nodes/Drift/Drift.node.js",
465 | "dist/nodes/Dropbox/Dropbox.node.js",
466 | "dist/nodes/Dropcontact/Dropcontact.node.js",
467 | "dist/nodes/EditImage/EditImage.node.js",
468 | "dist/nodes/E2eTest/E2eTest.node.js",
469 | "dist/nodes/Egoi/Egoi.node.js",
470 | "dist/nodes/Elastic/Elasticsearch/Elasticsearch.node.js",
471 | "dist/nodes/Elastic/ElasticSecurity/ElasticSecurity.node.js",
472 | "dist/nodes/EmailReadImap/EmailReadImap.node.js",
473 | "dist/nodes/EmailSend/EmailSend.node.js",
474 | "dist/nodes/Emelia/Emelia.node.js",
475 | "dist/nodes/Emelia/EmeliaTrigger.node.js",
476 | "dist/nodes/ERPNext/ERPNext.node.js",
477 | "dist/nodes/ErrorTrigger/ErrorTrigger.node.js",
478 | "dist/nodes/Eventbrite/EventbriteTrigger.node.js",
479 | "dist/nodes/ExecuteCommand/ExecuteCommand.node.js",
480 | "dist/nodes/ExecuteWorkflow/ExecuteWorkflow.node.js",
481 | "dist/nodes/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.js",
482 | "dist/nodes/ExecutionData/ExecutionData.node.js",
483 | "dist/nodes/Facebook/FacebookGraphApi.node.js",
484 | "dist/nodes/Facebook/FacebookTrigger.node.js",
485 | "dist/nodes/FacebookLeadAds/FacebookLeadAdsTrigger.node.js",
486 | "dist/nodes/Figma/FigmaTrigger.node.js",
487 | "dist/nodes/FileMaker/FileMaker.node.js",
488 | "dist/nodes/Filter/Filter.node.js",
489 | "dist/nodes/Flow/Flow.node.js",
490 | "dist/nodes/Flow/FlowTrigger.node.js",
491 | "dist/nodes/Form/FormTrigger.node.js",
492 | "dist/nodes/FormIo/FormIoTrigger.node.js",
493 | "dist/nodes/Formstack/FormstackTrigger.node.js",
494 | "dist/nodes/Freshdesk/Freshdesk.node.js",
495 | "dist/nodes/Freshservice/Freshservice.node.js",
496 | "dist/nodes/FreshworksCrm/FreshworksCrm.node.js",
497 | "dist/nodes/Ftp/Ftp.node.js",
498 | "dist/nodes/Function/Function.node.js",
499 | "dist/nodes/FunctionItem/FunctionItem.node.js",
500 | "dist/nodes/GetResponse/GetResponse.node.js",
501 | "dist/nodes/GetResponse/GetResponseTrigger.node.js",
502 | "dist/nodes/Ghost/Ghost.node.js",
503 | "dist/nodes/Git/Git.node.js",
504 | "dist/nodes/Github/Github.node.js",
505 | "dist/nodes/Github/GithubTrigger.node.js",
506 | "dist/nodes/Gitlab/Gitlab.node.js",
507 | "dist/nodes/Gitlab/GitlabTrigger.node.js",
508 | "dist/nodes/Google/Ads/GoogleAds.node.js",
509 | "dist/nodes/Google/Analytics/GoogleAnalytics.node.js",
510 | "dist/nodes/Google/BigQuery/GoogleBigQuery.node.js",
511 | "dist/nodes/Google/Books/GoogleBooks.node.js",
512 | "dist/nodes/Google/Calendar/GoogleCalendar.node.js",
513 | "dist/nodes/Google/Calendar/GoogleCalendarTrigger.node.js",
514 | "dist/nodes/Google/Chat/GoogleChat.node.js",
515 | "dist/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.js",
516 | "dist/nodes/Google/CloudStorage/GoogleCloudStorage.node.js",
517 | "dist/nodes/Google/Contacts/GoogleContacts.node.js",
518 | "dist/nodes/Google/Docs/GoogleDocs.node.js",
519 | "dist/nodes/Google/Drive/GoogleDrive.node.js",
520 | "dist/nodes/Google/Drive/GoogleDriveTrigger.node.js",
521 | "dist/nodes/Google/Firebase/CloudFirestore/GoogleFirebaseCloudFirestore.node.js",
522 | "dist/nodes/Google/Firebase/RealtimeDatabase/GoogleFirebaseRealtimeDatabase.node.js",
523 | "dist/nodes/Google/Gmail/Gmail.node.js",
524 | "dist/nodes/Google/Gmail/GmailTrigger.node.js",
525 | "dist/nodes/Google/GSuiteAdmin/GSuiteAdmin.node.js",
526 | "dist/nodes/Google/Perspective/GooglePerspective.node.js",
527 | "dist/nodes/Google/Sheet/GoogleSheets.node.js",
528 | "dist/nodes/Google/Sheet/GoogleSheetsTrigger.node.js",
529 | "dist/nodes/Google/Slides/GoogleSlides.node.js",
530 | "dist/nodes/Google/Task/GoogleTasks.node.js",
531 | "dist/nodes/Google/Translate/GoogleTranslate.node.js",
532 | "dist/nodes/Google/YouTube/YouTube.node.js",
533 | "dist/nodes/Gotify/Gotify.node.js",
534 | "dist/nodes/GoToWebinar/GoToWebinar.node.js",
535 | "dist/nodes/Grafana/Grafana.node.js",
536 | "dist/nodes/GraphQL/GraphQL.node.js",
537 | "dist/nodes/Grist/Grist.node.js",
538 | "dist/nodes/Gumroad/GumroadTrigger.node.js",
539 | "dist/nodes/HackerNews/HackerNews.node.js",
540 | "dist/nodes/HaloPSA/HaloPSA.node.js",
541 | "dist/nodes/Harvest/Harvest.node.js",
542 | "dist/nodes/HelpScout/HelpScout.node.js",
543 | "dist/nodes/HelpScout/HelpScoutTrigger.node.js",
544 | "dist/nodes/HighLevel/HighLevel.node.js",
545 | "dist/nodes/HomeAssistant/HomeAssistant.node.js",
546 | "dist/nodes/HtmlExtract/HtmlExtract.node.js",
547 | "dist/nodes/Html/Html.node.js",
548 | "dist/nodes/HttpRequest/HttpRequest.node.js",
549 | "dist/nodes/Hubspot/Hubspot.node.js",
550 | "dist/nodes/Hubspot/HubspotTrigger.node.js",
551 | "dist/nodes/HumanticAI/HumanticAi.node.js",
552 | "dist/nodes/Hunter/Hunter.node.js",
553 | "dist/nodes/ICalendar/ICalendar.node.js",
554 | "dist/nodes/If/If.node.js",
555 | "dist/nodes/Intercom/Intercom.node.js",
556 | "dist/nodes/Interval/Interval.node.js",
557 | "dist/nodes/InvoiceNinja/InvoiceNinja.node.js",
558 | "dist/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.js",
559 | "dist/nodes/ItemLists/ItemLists.node.js",
560 | "dist/nodes/Iterable/Iterable.node.js",
561 | "dist/nodes/Jenkins/Jenkins.node.js",
562 | "dist/nodes/Jira/Jira.node.js",
563 | "dist/nodes/Jira/JiraTrigger.node.js",
564 | "dist/nodes/JotForm/JotFormTrigger.node.js",
565 | "dist/nodes/Kafka/Kafka.node.js",
566 | "dist/nodes/Kafka/KafkaTrigger.node.js",
567 | "dist/nodes/Keap/Keap.node.js",
568 | "dist/nodes/Keap/KeapTrigger.node.js",
569 | "dist/nodes/Kitemaker/Kitemaker.node.js",
570 | "dist/nodes/KoBoToolbox/KoBoToolbox.node.js",
571 | "dist/nodes/KoBoToolbox/KoBoToolboxTrigger.node.js",
572 | "dist/nodes/Ldap/Ldap.node.js",
573 | "dist/nodes/Lemlist/Lemlist.node.js",
574 | "dist/nodes/Lemlist/LemlistTrigger.node.js",
575 | "dist/nodes/Line/Line.node.js",
576 | "dist/nodes/Linear/Linear.node.js",
577 | "dist/nodes/Linear/LinearTrigger.node.js",
578 | "dist/nodes/LingvaNex/LingvaNex.node.js",
579 | "dist/nodes/LinkedIn/LinkedIn.node.js",
580 | "dist/nodes/LocalFileTrigger/LocalFileTrigger.node.js",
581 | "dist/nodes/LoneScale/LoneScaleTrigger.node.js",
582 | "dist/nodes/LoneScale/LoneScale.node.js",
583 | "dist/nodes/Magento/Magento2.node.js",
584 | "dist/nodes/Mailcheck/Mailcheck.node.js",
585 | "dist/nodes/Mailchimp/Mailchimp.node.js",
586 | "dist/nodes/Mailchimp/MailchimpTrigger.node.js",
587 | "dist/nodes/MailerLite/MailerLite.node.js",
588 | "dist/nodes/MailerLite/MailerLiteTrigger.node.js",
589 | "dist/nodes/Mailgun/Mailgun.node.js",
590 | "dist/nodes/Mailjet/Mailjet.node.js",
591 | "dist/nodes/Mailjet/MailjetTrigger.node.js",
592 | "dist/nodes/Mandrill/Mandrill.node.js",
593 | "dist/nodes/ManualTrigger/ManualTrigger.node.js",
594 | "dist/nodes/Markdown/Markdown.node.js",
595 | "dist/nodes/Marketstack/Marketstack.node.js",
596 | "dist/nodes/Matrix/Matrix.node.js",
597 | "dist/nodes/Mattermost/Mattermost.node.js",
598 | "dist/nodes/Mautic/Mautic.node.js",
599 | "dist/nodes/Mautic/MauticTrigger.node.js",
600 | "dist/nodes/Medium/Medium.node.js",
601 | "dist/nodes/Merge/Merge.node.js",
602 | "dist/nodes/MessageBird/MessageBird.node.js",
603 | "dist/nodes/Metabase/Metabase.node.js",
604 | "dist/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.js",
605 | "dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js",
606 | "dist/nodes/Microsoft/GraphSecurity/MicrosoftGraphSecurity.node.js",
607 | "dist/nodes/Microsoft/OneDrive/MicrosoftOneDrive.node.js",
608 | "dist/nodes/Microsoft/Outlook/MicrosoftOutlook.node.js",
609 | "dist/nodes/Microsoft/Sql/MicrosoftSql.node.js",
610 | "dist/nodes/Microsoft/Teams/MicrosoftTeams.node.js",
611 | "dist/nodes/Microsoft/ToDo/MicrosoftToDo.node.js",
612 | "dist/nodes/Mindee/Mindee.node.js",
613 | "dist/nodes/Misp/Misp.node.js",
614 | "dist/nodes/Mocean/Mocean.node.js",
615 | "dist/nodes/MondayCom/MondayCom.node.js",
616 | "dist/nodes/MongoDb/MongoDb.node.js",
617 | "dist/nodes/MonicaCrm/MonicaCrm.node.js",
618 | "dist/nodes/MoveBinaryData/MoveBinaryData.node.js",
619 | "dist/nodes/MQTT/Mqtt.node.js",
620 | "dist/nodes/MQTT/MqttTrigger.node.js",
621 | "dist/nodes/Msg91/Msg91.node.js",
622 | "dist/nodes/MySql/MySql.node.js",
623 | "dist/nodes/N8n/N8n.node.js",
624 | "dist/nodes/N8nTrainingCustomerDatastore/N8nTrainingCustomerDatastore.node.js",
625 | "dist/nodes/N8nTrainingCustomerMessenger/N8nTrainingCustomerMessenger.node.js",
626 | "dist/nodes/N8nTrigger/N8nTrigger.node.js",
627 | "dist/nodes/Nasa/Nasa.node.js",
628 | "dist/nodes/Netlify/Netlify.node.js",
629 | "dist/nodes/Netlify/NetlifyTrigger.node.js",
630 | "dist/nodes/NextCloud/NextCloud.node.js",
631 | "dist/nodes/NocoDB/NocoDB.node.js",
632 | "dist/nodes/Brevo/Brevo.node.js",
633 | "dist/nodes/Brevo/BrevoTrigger.node.js",
634 | "dist/nodes/StickyNote/StickyNote.node.js",
635 | "dist/nodes/NoOp/NoOp.node.js",
636 | "dist/nodes/Onfleet/Onfleet.node.js",
637 | "dist/nodes/Onfleet/OnfleetTrigger.node.js",
638 | "dist/nodes/Notion/Notion.node.js",
639 | "dist/nodes/Notion/NotionTrigger.node.js",
640 | "dist/nodes/Npm/Npm.node.js",
641 | "dist/nodes/Odoo/Odoo.node.js",
642 | "dist/nodes/OneSimpleApi/OneSimpleApi.node.js",
643 | "dist/nodes/OpenAi/OpenAi.node.js",
644 | "dist/nodes/OpenThesaurus/OpenThesaurus.node.js",
645 | "dist/nodes/OpenWeatherMap/OpenWeatherMap.node.js",
646 | "dist/nodes/Orbit/Orbit.node.js",
647 | "dist/nodes/Oura/Oura.node.js",
648 | "dist/nodes/Paddle/Paddle.node.js",
649 | "dist/nodes/PagerDuty/PagerDuty.node.js",
650 | "dist/nodes/PayPal/PayPal.node.js",
651 | "dist/nodes/PayPal/PayPalTrigger.node.js",
652 | "dist/nodes/Peekalink/Peekalink.node.js",
653 | "dist/nodes/Phantombuster/Phantombuster.node.js",
654 | "dist/nodes/PhilipsHue/PhilipsHue.node.js",
655 | "dist/nodes/Pipedrive/Pipedrive.node.js",
656 | "dist/nodes/Pipedrive/PipedriveTrigger.node.js",
657 | "dist/nodes/Plivo/Plivo.node.js",
658 | "dist/nodes/PostBin/PostBin.node.js",
659 | "dist/nodes/Postgres/Postgres.node.js",
660 | "dist/nodes/Postgres/PostgresTrigger.node.js",
661 | "dist/nodes/PostHog/PostHog.node.js",
662 | "dist/nodes/Postmark/PostmarkTrigger.node.js",
663 | "dist/nodes/ProfitWell/ProfitWell.node.js",
664 | "dist/nodes/Pushbullet/Pushbullet.node.js",
665 | "dist/nodes/Pushcut/Pushcut.node.js",
666 | "dist/nodes/Pushcut/PushcutTrigger.node.js",
667 | "dist/nodes/Pushover/Pushover.node.js",
668 | "dist/nodes/QuestDb/QuestDb.node.js",
669 | "dist/nodes/QuickBase/QuickBase.node.js",
670 | "dist/nodes/QuickBooks/QuickBooks.node.js",
671 | "dist/nodes/QuickChart/QuickChart.node.js",
672 | "dist/nodes/RabbitMQ/RabbitMQ.node.js",
673 | "dist/nodes/RabbitMQ/RabbitMQTrigger.node.js",
674 | "dist/nodes/Raindrop/Raindrop.node.js",
675 | "dist/nodes/ReadBinaryFile/ReadBinaryFile.node.js",
676 | "dist/nodes/ReadBinaryFiles/ReadBinaryFiles.node.js",
677 | "dist/nodes/ReadPdf/ReadPDF.node.js",
678 | "dist/nodes/Reddit/Reddit.node.js",
679 | "dist/nodes/Redis/Redis.node.js",
680 | "dist/nodes/Redis/RedisTrigger.node.js",
681 | "dist/nodes/RenameKeys/RenameKeys.node.js",
682 | "dist/nodes/RespondToWebhook/RespondToWebhook.node.js",
683 | "dist/nodes/Rocketchat/Rocketchat.node.js",
684 | "dist/nodes/RssFeedRead/RssFeedRead.node.js",
685 | "dist/nodes/RssFeedRead/RssFeedReadTrigger.node.js",
686 | "dist/nodes/Rundeck/Rundeck.node.js",
687 | "dist/nodes/S3/S3.node.js",
688 | "dist/nodes/Salesforce/Salesforce.node.js",
689 | "dist/nodes/Salesmate/Salesmate.node.js",
690 | "dist/nodes/Schedule/ScheduleTrigger.node.js",
691 | "dist/nodes/SeaTable/SeaTable.node.js",
692 | "dist/nodes/SeaTable/SeaTableTrigger.node.js",
693 | "dist/nodes/SecurityScorecard/SecurityScorecard.node.js",
694 | "dist/nodes/Segment/Segment.node.js",
695 | "dist/nodes/SendGrid/SendGrid.node.js",
696 | "dist/nodes/Sendy/Sendy.node.js",
697 | "dist/nodes/SentryIo/SentryIo.node.js",
698 | "dist/nodes/ServiceNow/ServiceNow.node.js",
699 | "dist/nodes/Set/Set.node.js",
700 | "dist/nodes/Shopify/Shopify.node.js",
701 | "dist/nodes/Shopify/ShopifyTrigger.node.js",
702 | "dist/nodes/Signl4/Signl4.node.js",
703 | "dist/nodes/Slack/Slack.node.js",
704 | "dist/nodes/Sms77/Sms77.node.js",
705 | "dist/nodes/Snowflake/Snowflake.node.js",
706 | "dist/nodes/SplitInBatches/SplitInBatches.node.js",
707 | "dist/nodes/Splunk/Splunk.node.js",
708 | "dist/nodes/Spontit/Spontit.node.js",
709 | "dist/nodes/Spotify/Spotify.node.js",
710 | "dist/nodes/SpreadsheetFile/SpreadsheetFile.node.js",
711 | "dist/nodes/SseTrigger/SseTrigger.node.js",
712 | "dist/nodes/Ssh/Ssh.node.js",
713 | "dist/nodes/Stackby/Stackby.node.js",
714 | "dist/nodes/Start/Start.node.js",
715 | "dist/nodes/StopAndError/StopAndError.node.js",
716 | "dist/nodes/Storyblok/Storyblok.node.js",
717 | "dist/nodes/Strapi/Strapi.node.js",
718 | "dist/nodes/Strava/Strava.node.js",
719 | "dist/nodes/Strava/StravaTrigger.node.js",
720 | "dist/nodes/Stripe/Stripe.node.js",
721 | "dist/nodes/Stripe/StripeTrigger.node.js",
722 | "dist/nodes/Supabase/Supabase.node.js",
723 | "dist/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.js",
724 | "dist/nodes/Switch/Switch.node.js",
725 | "dist/nodes/SyncroMSP/SyncroMsp.node.js",
726 | "dist/nodes/Taiga/Taiga.node.js",
727 | "dist/nodes/Taiga/TaigaTrigger.node.js",
728 | "dist/nodes/Tapfiliate/Tapfiliate.node.js",
729 | "dist/nodes/Telegram/Telegram.node.js",
730 | "dist/nodes/Telegram/TelegramTrigger.node.js",
731 | "dist/nodes/TheHiveProject/TheHiveProject.node.js",
732 | "dist/nodes/TheHiveProject/TheHiveProjectTrigger.node.js",
733 | "dist/nodes/TheHive/TheHive.node.js",
734 | "dist/nodes/TheHive/TheHiveTrigger.node.js",
735 | "dist/nodes/TimescaleDb/TimescaleDb.node.js",
736 | "dist/nodes/Todoist/Todoist.node.js",
737 | "dist/nodes/Toggl/TogglTrigger.node.js",
738 | "dist/nodes/Totp/Totp.node.js",
739 | "dist/nodes/TravisCi/TravisCi.node.js",
740 | "dist/nodes/Trello/Trello.node.js",
741 | "dist/nodes/Trello/TrelloTrigger.node.js",
742 | "dist/nodes/Twake/Twake.node.js",
743 | "dist/nodes/Twilio/Twilio.node.js",
744 | "dist/nodes/Twist/Twist.node.js",
745 | "dist/nodes/Twitter/Twitter.node.js",
746 | "dist/nodes/Typeform/TypeformTrigger.node.js",
747 | "dist/nodes/UnleashedSoftware/UnleashedSoftware.node.js",
748 | "dist/nodes/Uplead/Uplead.node.js",
749 | "dist/nodes/UProc/UProc.node.js",
750 | "dist/nodes/UptimeRobot/UptimeRobot.node.js",
751 | "dist/nodes/UrlScanIo/UrlScanIo.node.js",
752 | "dist/nodes/Vero/Vero.node.js",
753 | "dist/nodes/Venafi/ProtectCloud/VenafiTlsProtectCloud.node.js",
754 | "dist/nodes/Venafi/ProtectCloud/VenafiTlsProtectCloudTrigger.node.js",
755 | "dist/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenter.node.js",
756 | "dist/nodes/Vonage/Vonage.node.js",
757 | "dist/nodes/Wait/Wait.node.js",
758 | "dist/nodes/Webflow/Webflow.node.js",
759 | "dist/nodes/Webflow/WebflowTrigger.node.js",
760 | "dist/nodes/Webhook/Webhook.node.js",
761 | "dist/nodes/Wekan/Wekan.node.js",
762 | "dist/nodes/WhatsApp/WhatsApp.node.js",
763 | "dist/nodes/Wise/Wise.node.js",
764 | "dist/nodes/Wise/WiseTrigger.node.js",
765 | "dist/nodes/WooCommerce/WooCommerce.node.js",
766 | "dist/nodes/WooCommerce/WooCommerceTrigger.node.js",
767 | "dist/nodes/Wordpress/Wordpress.node.js",
768 | "dist/nodes/Workable/WorkableTrigger.node.js",
769 | "dist/nodes/WorkflowTrigger/WorkflowTrigger.node.js",
770 | "dist/nodes/WriteBinaryFile/WriteBinaryFile.node.js",
771 | "dist/nodes/Wufoo/WufooTrigger.node.js",
772 | "dist/nodes/Xero/Xero.node.js",
773 | "dist/nodes/Xml/Xml.node.js",
774 | "dist/nodes/Yourls/Yourls.node.js",
775 | "dist/nodes/Zammad/Zammad.node.js",
776 | "dist/nodes/Zendesk/Zendesk.node.js",
777 | "dist/nodes/Zendesk/ZendeskTrigger.node.js",
778 | "dist/nodes/Zoho/ZohoCrm.node.js",
779 | "dist/nodes/Zoom/Zoom.node.js",
780 | "dist/nodes/Zulip/Zulip.node.js"
781 | ]
782 | },
783 | "devDependencies": {
784 | "@types/amqplib": "^0.10.1",
785 | "@types/aws4": "^1.5.1",
786 | "@types/basic-auth": "^1.1.3",
787 | "@types/cheerio": "^0.22.15",
788 | "@types/cron": "~1.7.1",
789 | "@types/eventsource": "^1.1.2",
790 | "@types/express": "^4.17.6",
791 | "@types/gm": "^1.25.0",
792 | "@types/imap-simple": "^4.2.0",
793 | "@types/js-nacl": "^1.3.0",
794 | "@types/jsonwebtoken": "^9.0.1",
795 | "@types/lodash": "^4.14.195",
796 | "@types/lossless-json": "^1.0.0",
797 | "@types/mailparser": "^2.7.3",
798 | "@types/mime-types": "^2.1.0",
799 | "@types/mssql": "^6.0.2",
800 | "@types/node-ssh": "^7.0.1",
801 | "@types/nodemailer": "^6.4.0",
802 | "@types/promise-ftp": "^1.3.4",
803 | "@types/redis": "^2.8.11",
804 | "@types/request-promise-native": "~1.0.15",
805 | "@types/rfc2047": "^2.0.1",
806 | "@types/showdown": "^1.9.4",
807 | "@types/snowflake-sdk": "^1.6.12",
808 | "@types/ssh2-sftp-client": "^5.1.0",
809 | "@types/tmp": "^0.2.0",
810 | "@types/uuid": "^8.3.2",
811 | "@types/xml2js": "^0.4.11",
812 | "eslint-plugin-n8n-nodes-base": "^1.16.0",
813 | "gulp": "^4.0.0",
814 | "n8n-core": "1.14.1"
815 | },
816 | "dependencies": {
817 | "@kafkajs/confluent-schema-registry": "1.0.6",
818 | "@n8n/vm2": "^3.9.20",
819 | "amqplib": "^0.10.3",
820 | "aws4": "^1.8.0",
821 | "basic-auth": "^2.0.1",
822 | "change-case": "^4.1.1",
823 | "cheerio": "1.0.0-rc.6",
824 | "chokidar": "3.5.2",
825 | "cron": "~1.7.2",
826 | "csv-parse": "^5.5.0",
827 | "currency-codes": "^2.1.0",
828 | "eventsource": "^2.0.2",
829 | "fast-glob": "^3.2.5",
830 | "fflate": "^0.7.0",
831 | "get-system-fonts": "^2.0.2",
832 | "gm": "^1.25.0",
833 | "iconv-lite": "^0.6.2",
834 | "ics": "^2.27.0",
835 | "imap-simple": "^4.3.0",
836 | "isbot": "^3.6.13",
837 | "iso-639-1": "^2.1.3",
838 | "js-nacl": "^1.4.0",
839 | "jsonwebtoken": "^9.0.0",
840 | "kafkajs": "^1.14.0",
841 | "ldapts": "^4.2.6",
842 | "lodash": "^4.17.21",
843 | "lossless-json": "^1.0.4",
844 | "luxon": "^3.3.0",
845 | "mailparser": "^3.2.0",
846 | "minifaker": "^1.34.1",
847 | "moment": "~2.29.2",
848 | "moment-timezone": "^0.5.28",
849 | "mongodb": "^4.17.1",
850 | "mqtt": "^5.0.2",
851 | "mssql": "^8.1.2",
852 | "mysql2": "~2.3.0",
853 | "nanoid": "^3.3.6",
854 | "node-html-markdown": "^1.1.3",
855 | "node-ssh": "^12.0.0",
856 | "nodemailer": "^6.7.1",
857 | "otpauth": "^9.1.1",
858 | "pdfjs-dist": "^2.16.105",
859 | "pg": "^8.3.0",
860 | "pg-promise": "^10.5.8",
861 | "pretty-bytes": "^5.6.0",
862 | "promise-ftp": "^1.3.5",
863 | "pyodide": "^0.23.4",
864 | "redis": "^3.1.1",
865 | "rfc2047": "^4.0.1",
866 | "rhea": "^1.0.11",
867 | "rss-parser": "^3.7.0",
868 | "semver": "^7.5.4",
869 | "showdown": "^2.0.3",
870 | "simple-git": "^3.17.0",
871 | "snowflake-sdk": "^1.8.0",
872 | "ssh2-sftp-client": "^7.0.0",
873 | "tmp-promise": "^3.0.2",
874 | "typedi": "^0.10.0",
875 | "uuid": "^8.3.2",
876 | "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz",
877 | "xml2js": "^0.5.0",
878 | "n8n-workflow": "1.14.1"
879 | },
880 | "scripts": {
881 | "clean": "rimraf dist .turbo",
882 | "dev": "pnpm watch",
883 | "typecheck": "tsc",
884 | "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && gulp build:icons && gulp build:translations && pnpm build:metadata",
885 | "build:translations": "gulp build:translations",
886 | "build:metadata": "pnpm n8n-generate-known && pnpm n8n-generate-ui-types",
887 | "format": "prettier --write . --ignore-path ../../.prettierignore",
888 | "lint": "eslint . --quiet && node ./scripts/validate-load-options-methods.js",
889 | "lintfix": "eslint . --fix",
890 | "watch": "tsc-watch -p tsconfig.build.json --onCompilationComplete \"tsc-alias -p tsconfig.build.json\" --onSuccess \"pnpm n8n-generate-ui-types\"",
891 | "test": "jest"
892 | }
893 | },
894 | "extraction_time_ms": 7,
895 | "extracted_at": "2025-06-07T17:49:22.717Z"
896 | }
```
--------------------------------------------------------------------------------
/src/services/enhanced-config-validator.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Enhanced Configuration Validator Service
3 | *
4 | * Provides operation-aware validation for n8n nodes with reduced false positives.
5 | * Supports multiple validation modes and node-specific logic.
6 | */
7 |
8 | import { ConfigValidator, ValidationResult, ValidationError, ValidationWarning } from './config-validator';
9 | import { NodeSpecificValidators, NodeValidationContext } from './node-specific-validators';
10 | import { FixedCollectionValidator } from '../utils/fixed-collection-validator';
11 | import { OperationSimilarityService } from './operation-similarity-service';
12 | import { ResourceSimilarityService } from './resource-similarity-service';
13 | import { NodeRepository } from '../database/node-repository';
14 | import { DatabaseAdapter } from '../database/database-adapter';
15 | import { NodeTypeNormalizer } from '../utils/node-type-normalizer';
16 | import { TypeStructureService } from './type-structure-service';
17 | import type { NodePropertyTypes } from 'n8n-workflow';
18 |
19 | export type ValidationMode = 'full' | 'operation' | 'minimal';
20 | export type ValidationProfile = 'strict' | 'runtime' | 'ai-friendly' | 'minimal';
21 |
22 | export interface EnhancedValidationResult extends ValidationResult {
23 | mode: ValidationMode;
24 | profile?: ValidationProfile;
25 | operation?: {
26 | resource?: string;
27 | operation?: string;
28 | action?: string;
29 | };
30 | examples?: Array<{
31 | description: string;
32 | config: Record<string, any>;
33 | }>;
34 | nextSteps?: string[];
35 | }
36 |
37 | export interface OperationContext {
38 | resource?: string;
39 | operation?: string;
40 | action?: string;
41 | mode?: string;
42 | }
43 |
44 | export class EnhancedConfigValidator extends ConfigValidator {
45 | private static operationSimilarityService: OperationSimilarityService | null = null;
46 | private static resourceSimilarityService: ResourceSimilarityService | null = null;
47 | private static nodeRepository: NodeRepository | null = null;
48 |
49 | /**
50 | * Initialize similarity services (called once at startup)
51 | */
52 | static initializeSimilarityServices(repository: NodeRepository): void {
53 | this.nodeRepository = repository;
54 | this.operationSimilarityService = new OperationSimilarityService(repository);
55 | this.resourceSimilarityService = new ResourceSimilarityService(repository);
56 | }
57 | /**
58 | * Validate with operation awareness
59 | */
60 | static validateWithMode(
61 | nodeType: string,
62 | config: Record<string, any>,
63 | properties: any[],
64 | mode: ValidationMode = 'operation',
65 | profile: ValidationProfile = 'ai-friendly'
66 | ): EnhancedValidationResult {
67 | // Input validation - ensure parameters are valid
68 | if (typeof nodeType !== 'string') {
69 | throw new Error(`Invalid nodeType: expected string, got ${typeof nodeType}`);
70 | }
71 |
72 | if (!config || typeof config !== 'object') {
73 | throw new Error(`Invalid config: expected object, got ${typeof config}`);
74 | }
75 |
76 | if (!Array.isArray(properties)) {
77 | throw new Error(`Invalid properties: expected array, got ${typeof properties}`);
78 | }
79 |
80 | // Extract operation context from config
81 | const operationContext = this.extractOperationContext(config);
82 |
83 | // Extract user-provided keys before applying defaults (CRITICAL FIX for warning system)
84 | const userProvidedKeys = new Set(Object.keys(config));
85 |
86 | // Filter properties based on mode and operation, and get config with defaults
87 | const { properties: filteredProperties, configWithDefaults } = this.filterPropertiesByMode(
88 | properties,
89 | config,
90 | mode,
91 | operationContext
92 | );
93 |
94 | // Perform base validation on filtered properties with defaults applied
95 | // Pass userProvidedKeys to prevent warnings about default values
96 | const baseResult = super.validate(nodeType, configWithDefaults, filteredProperties, userProvidedKeys);
97 |
98 | // Enhance the result
99 | const enhancedResult: EnhancedValidationResult = {
100 | ...baseResult,
101 | mode,
102 | profile,
103 | operation: operationContext,
104 | examples: [],
105 | nextSteps: [],
106 | // Ensure arrays are initialized (in case baseResult doesn't have them)
107 | errors: baseResult.errors || [],
108 | warnings: baseResult.warnings || [],
109 | suggestions: baseResult.suggestions || []
110 | };
111 |
112 | // Apply profile-based filtering
113 | this.applyProfileFilters(enhancedResult, profile);
114 |
115 | // Add operation-specific enhancements
116 | this.addOperationSpecificEnhancements(nodeType, config, filteredProperties, enhancedResult);
117 |
118 | // Deduplicate errors
119 | enhancedResult.errors = this.deduplicateErrors(enhancedResult.errors);
120 |
121 | // Examples removed - use validate_node_operation for configuration guidance
122 |
123 | // Generate next steps based on errors
124 | enhancedResult.nextSteps = this.generateNextSteps(enhancedResult);
125 |
126 | // Recalculate validity after all enhancements (crucial for fixedCollection validation)
127 | enhancedResult.valid = enhancedResult.errors.length === 0;
128 |
129 | return enhancedResult;
130 | }
131 |
132 | /**
133 | * Extract operation context from configuration
134 | */
135 | private static extractOperationContext(config: Record<string, any>): OperationContext {
136 | return {
137 | resource: config.resource,
138 | operation: config.operation,
139 | action: config.action,
140 | mode: config.mode
141 | };
142 | }
143 |
144 | /**
145 | * Filter properties based on validation mode and operation
146 | * Returns both filtered properties and config with defaults
147 | */
148 | private static filterPropertiesByMode(
149 | properties: any[],
150 | config: Record<string, any>,
151 | mode: ValidationMode,
152 | operation: OperationContext
153 | ): { properties: any[], configWithDefaults: Record<string, any> } {
154 | // Apply defaults for visibility checking
155 | const configWithDefaults = this.applyNodeDefaults(properties, config);
156 |
157 | let filteredProperties: any[];
158 | switch (mode) {
159 | case 'minimal':
160 | // Only required properties that are visible
161 | filteredProperties = properties.filter(prop =>
162 | prop.required && this.isPropertyVisible(prop, configWithDefaults)
163 | );
164 | break;
165 |
166 | case 'operation':
167 | // Only properties relevant to the current operation
168 | filteredProperties = properties.filter(prop =>
169 | this.isPropertyRelevantToOperation(prop, configWithDefaults, operation)
170 | );
171 | break;
172 |
173 | case 'full':
174 | default:
175 | // All properties (current behavior)
176 | filteredProperties = properties;
177 | break;
178 | }
179 |
180 | return { properties: filteredProperties, configWithDefaults };
181 | }
182 |
183 | /**
184 | * Apply node defaults to configuration for accurate visibility checking
185 | */
186 | private static applyNodeDefaults(properties: any[], config: Record<string, any>): Record<string, any> {
187 | const result = { ...config };
188 |
189 | for (const prop of properties) {
190 | if (prop.name && prop.default !== undefined && result[prop.name] === undefined) {
191 | result[prop.name] = prop.default;
192 | }
193 | }
194 |
195 | return result;
196 | }
197 |
198 | /**
199 | * Check if property is relevant to current operation
200 | */
201 | private static isPropertyRelevantToOperation(
202 | prop: any,
203 | config: Record<string, any>,
204 | operation: OperationContext
205 | ): boolean {
206 | // First check if visible
207 | if (!this.isPropertyVisible(prop, config)) {
208 | return false;
209 | }
210 |
211 | // If no operation context, include all visible
212 | if (!operation.resource && !operation.operation && !operation.action) {
213 | return true;
214 | }
215 |
216 | // Check if property has operation-specific display options
217 | if (prop.displayOptions?.show) {
218 | const show = prop.displayOptions.show;
219 |
220 | // Check each operation field
221 | if (operation.resource && show.resource) {
222 | const expectedResources = Array.isArray(show.resource) ? show.resource : [show.resource];
223 | if (!expectedResources.includes(operation.resource)) {
224 | return false;
225 | }
226 | }
227 |
228 | if (operation.operation && show.operation) {
229 | const expectedOps = Array.isArray(show.operation) ? show.operation : [show.operation];
230 | if (!expectedOps.includes(operation.operation)) {
231 | return false;
232 | }
233 | }
234 |
235 | if (operation.action && show.action) {
236 | const expectedActions = Array.isArray(show.action) ? show.action : [show.action];
237 | if (!expectedActions.includes(operation.action)) {
238 | return false;
239 | }
240 | }
241 | }
242 |
243 | return true;
244 | }
245 |
246 | /**
247 | * Add operation-specific enhancements to validation result
248 | */
249 | private static addOperationSpecificEnhancements(
250 | nodeType: string,
251 | config: Record<string, any>,
252 | properties: any[],
253 | result: EnhancedValidationResult
254 | ): void {
255 | // Type safety check - this should never happen with proper validation
256 | if (typeof nodeType !== 'string') {
257 | result.errors.push({
258 | type: 'invalid_type',
259 | property: 'nodeType',
260 | message: `Invalid nodeType: expected string, got ${typeof nodeType}`,
261 | fix: 'Provide a valid node type string (e.g., "nodes-base.webhook")'
262 | });
263 | return;
264 | }
265 |
266 | // Validate resource and operation using similarity services
267 | this.validateResourceAndOperation(nodeType, config, result);
268 |
269 | // Validate special type structures (filter, resourceMapper, assignmentCollection, resourceLocator)
270 | this.validateSpecialTypeStructures(config, properties, result);
271 |
272 | // First, validate fixedCollection properties for known problematic nodes
273 | this.validateFixedCollectionStructures(nodeType, config, result);
274 |
275 | // Create context for node-specific validators
276 | const context: NodeValidationContext = {
277 | config,
278 | errors: result.errors,
279 | warnings: result.warnings,
280 | suggestions: result.suggestions,
281 | autofix: result.autofix || {}
282 | };
283 |
284 | // Normalize node type (handle both 'n8n-nodes-base.x' and 'nodes-base.x' formats)
285 | const normalizedNodeType = nodeType.replace('n8n-nodes-base.', 'nodes-base.');
286 |
287 | // Use node-specific validators
288 | switch (normalizedNodeType) {
289 | case 'nodes-base.slack':
290 | NodeSpecificValidators.validateSlack(context);
291 | this.enhanceSlackValidation(config, result);
292 | break;
293 |
294 | case 'nodes-base.googleSheets':
295 | NodeSpecificValidators.validateGoogleSheets(context);
296 | this.enhanceGoogleSheetsValidation(config, result);
297 | break;
298 |
299 | case 'nodes-base.httpRequest':
300 | // Use existing HTTP validation from base class
301 | this.enhanceHttpRequestValidation(config, result);
302 | break;
303 |
304 | case 'nodes-base.code':
305 | NodeSpecificValidators.validateCode(context);
306 | break;
307 |
308 | case 'nodes-base.openAi':
309 | NodeSpecificValidators.validateOpenAI(context);
310 | break;
311 |
312 | case 'nodes-base.mongoDb':
313 | NodeSpecificValidators.validateMongoDB(context);
314 | break;
315 |
316 | case 'nodes-base.webhook':
317 | NodeSpecificValidators.validateWebhook(context);
318 | break;
319 |
320 | case 'nodes-base.postgres':
321 | NodeSpecificValidators.validatePostgres(context);
322 | break;
323 |
324 | case 'nodes-base.mysql':
325 | NodeSpecificValidators.validateMySQL(context);
326 | break;
327 |
328 | case 'nodes-langchain.agent':
329 | NodeSpecificValidators.validateAIAgent(context);
330 | break;
331 |
332 | case 'nodes-base.set':
333 | NodeSpecificValidators.validateSet(context);
334 | break;
335 |
336 | case 'nodes-base.switch':
337 | this.validateSwitchNodeStructure(config, result);
338 | break;
339 |
340 | case 'nodes-base.if':
341 | this.validateIfNodeStructure(config, result);
342 | break;
343 |
344 | case 'nodes-base.filter':
345 | this.validateFilterNodeStructure(config, result);
346 | break;
347 |
348 | // Additional nodes handled by FixedCollectionValidator
349 | // No need for specific validators as the generic utility handles them
350 | }
351 |
352 | // Update autofix if changes were made
353 | if (Object.keys(context.autofix).length > 0) {
354 | result.autofix = context.autofix;
355 | }
356 | }
357 |
358 | /**
359 | * Enhanced Slack validation with operation awareness
360 | */
361 | private static enhanceSlackValidation(
362 | config: Record<string, any>,
363 | result: EnhancedValidationResult
364 | ): void {
365 | const { resource, operation } = result.operation || {};
366 |
367 | if (resource === 'message' && operation === 'send') {
368 | // Examples removed - validation focuses on error detection
369 |
370 | // Check for common issues
371 | if (!config.channel && !config.channelId) {
372 | const channelError = result.errors.find(e =>
373 | e.property === 'channel' || e.property === 'channelId'
374 | );
375 | if (channelError) {
376 | channelError.message = 'To send a Slack message, specify either a channel name (e.g., "#general") or channel ID';
377 | channelError.fix = 'Add channel: "#general" or use a channel ID like "C1234567890"';
378 | }
379 | }
380 | }
381 | }
382 |
383 | /**
384 | * Enhanced Google Sheets validation
385 | */
386 | private static enhanceGoogleSheetsValidation(
387 | config: Record<string, any>,
388 | result: EnhancedValidationResult
389 | ): void {
390 | const { operation } = result.operation || {};
391 |
392 | if (operation === 'append') {
393 | // Examples removed - validation focuses on configuration correctness
394 |
395 | // Validate range format
396 | if (config.range && !config.range.includes('!')) {
397 | result.warnings.push({
398 | type: 'inefficient',
399 | property: 'range',
400 | message: 'Range should include sheet name (e.g., "Sheet1!A:B")',
401 | suggestion: 'Format: "SheetName!A1:B10" or "SheetName!A:B" for entire columns'
402 | });
403 | }
404 | }
405 | }
406 |
407 | /**
408 | * Enhanced HTTP Request validation
409 | */
410 | private static enhanceHttpRequestValidation(
411 | config: Record<string, any>,
412 | result: EnhancedValidationResult
413 | ): void {
414 | const url = String(config.url || '');
415 | const options = config.options || {};
416 |
417 | // 1. Suggest alwaysOutputData for better error handling (node-level property)
418 | // Note: We can't check if it exists (it's node-level, not in parameters),
419 | // but we can suggest it as a best practice
420 | if (!result.suggestions.some(s => typeof s === 'string' && s.includes('alwaysOutputData'))) {
421 | result.suggestions.push(
422 | 'Consider adding alwaysOutputData: true at node level (not in parameters) for better error handling. ' +
423 | 'This ensures the node produces output even when HTTP requests fail, allowing downstream error handling.'
424 | );
425 | }
426 |
427 | // 2. Suggest responseFormat for API endpoints
428 | const lowerUrl = url.toLowerCase();
429 | const isApiEndpoint =
430 | // Subdomain patterns (api.example.com)
431 | /^https?:\/\/api\./i.test(url) ||
432 | // Path patterns with word boundaries to prevent false positives like "therapist", "restaurant"
433 | /\/api[\/\?]|\/api$/i.test(url) ||
434 | /\/rest[\/\?]|\/rest$/i.test(url) ||
435 | // Known API service domains
436 | lowerUrl.includes('supabase.co') ||
437 | lowerUrl.includes('firebase') ||
438 | lowerUrl.includes('googleapis.com') ||
439 | // Versioned API paths (e.g., example.com/v1, example.com/v2)
440 | /\.com\/v\d+/i.test(url);
441 |
442 | if (isApiEndpoint && !options.response?.response?.responseFormat) {
443 | result.suggestions.push(
444 | 'API endpoints should explicitly set options.response.response.responseFormat to "json" or "text" ' +
445 | 'to prevent confusion about response parsing. Example: ' +
446 | '{ "options": { "response": { "response": { "responseFormat": "json" } } } }'
447 | );
448 | }
449 |
450 | // 3. Enhanced URL protocol validation for expressions
451 | if (url && url.startsWith('=')) {
452 | // Expression-based URL - check for common protocol issues
453 | const expressionContent = url.slice(1); // Remove = prefix
454 | const lowerExpression = expressionContent.toLowerCase();
455 |
456 | // Check for missing protocol in expression (case-insensitive)
457 | if (expressionContent.startsWith('www.') ||
458 | (expressionContent.includes('{{') && !lowerExpression.includes('http'))) {
459 | result.warnings.push({
460 | type: 'invalid_value',
461 | property: 'url',
462 | message: 'URL expression appears to be missing http:// or https:// protocol',
463 | suggestion: 'Include protocol in your expression. Example: ={{ "https://" + $json.domain + ".com" }}'
464 | });
465 | }
466 | }
467 | }
468 |
469 | /**
470 | * Generate actionable next steps based on validation results
471 | */
472 | private static generateNextSteps(result: EnhancedValidationResult): string[] {
473 | const steps: string[] = [];
474 |
475 | // Group errors by type
476 | const requiredErrors = result.errors.filter(e => e.type === 'missing_required');
477 | const typeErrors = result.errors.filter(e => e.type === 'invalid_type');
478 | const valueErrors = result.errors.filter(e => e.type === 'invalid_value');
479 |
480 | if (requiredErrors.length > 0) {
481 | steps.push(`Add required fields: ${requiredErrors.map(e => e.property).join(', ')}`);
482 | }
483 |
484 | if (typeErrors.length > 0) {
485 | steps.push(`Fix type mismatches: ${typeErrors.map(e => `${e.property} should be ${e.fix}`).join(', ')}`);
486 | }
487 |
488 | if (valueErrors.length > 0) {
489 | steps.push(`Correct invalid values: ${valueErrors.map(e => e.property).join(', ')}`);
490 | }
491 |
492 | if (result.warnings.length > 0 && result.errors.length === 0) {
493 | steps.push('Consider addressing warnings for better reliability');
494 | }
495 |
496 | if (result.errors.length > 0) {
497 | steps.push('Fix the errors above following the provided suggestions');
498 | }
499 |
500 | return steps;
501 | }
502 |
503 |
504 | /**
505 | * Deduplicate errors based on property and type
506 | * Prefers more specific error messages over generic ones
507 | */
508 | private static deduplicateErrors(errors: ValidationError[]): ValidationError[] {
509 | const seen = new Map<string, ValidationError>();
510 |
511 | for (const error of errors) {
512 | const key = `${error.property}-${error.type}`;
513 | const existing = seen.get(key);
514 |
515 | if (!existing) {
516 | seen.set(key, error);
517 | } else {
518 | // Keep the error with more specific message or fix
519 | const existingLength = (existing.message?.length || 0) + (existing.fix?.length || 0);
520 | const newLength = (error.message?.length || 0) + (error.fix?.length || 0);
521 |
522 | if (newLength > existingLength) {
523 | seen.set(key, error);
524 | }
525 | }
526 | }
527 |
528 | return Array.from(seen.values());
529 | }
530 |
531 | /**
532 | * Check if a warning should be filtered out (hardcoded credentials shown only in strict mode)
533 | */
534 | private static shouldFilterCredentialWarning(warning: ValidationWarning): boolean {
535 | return warning.type === 'security' &&
536 | warning.message !== undefined &&
537 | warning.message.includes('Hardcoded nodeCredentialType');
538 | }
539 |
540 | /**
541 | * Apply profile-based filtering to validation results
542 | */
543 | private static applyProfileFilters(
544 | result: EnhancedValidationResult,
545 | profile: ValidationProfile
546 | ): void {
547 | switch (profile) {
548 | case 'minimal':
549 | // Only keep missing required errors
550 | result.errors = result.errors.filter(e => e.type === 'missing_required');
551 | // Keep ONLY critical warnings (security and deprecated)
552 | // But filter out hardcoded credential type warnings (only show in strict mode)
553 | result.warnings = result.warnings.filter(w => {
554 | if (this.shouldFilterCredentialWarning(w)) {
555 | return false;
556 | }
557 | return w.type === 'security' || w.type === 'deprecated';
558 | });
559 | result.suggestions = [];
560 | break;
561 |
562 | case 'runtime':
563 | // Keep critical runtime errors only
564 | result.errors = result.errors.filter(e =>
565 | e.type === 'missing_required' ||
566 | e.type === 'invalid_value' ||
567 | (e.type === 'invalid_type' && e.message.includes('undefined'))
568 | );
569 | // Keep security and deprecated warnings, REMOVE property visibility warnings
570 | result.warnings = result.warnings.filter(w => {
571 | // Filter out hardcoded credential type warnings (only show in strict mode)
572 | if (this.shouldFilterCredentialWarning(w)) {
573 | return false;
574 | }
575 | if (w.type === 'security' || w.type === 'deprecated') return true;
576 | // FILTER OUT property visibility warnings (too noisy)
577 | if (w.type === 'inefficient' && w.message && w.message.includes('not visible')) {
578 | return false;
579 | }
580 | return false;
581 | });
582 | result.suggestions = [];
583 | break;
584 |
585 | case 'strict':
586 | // Keep everything, add more suggestions
587 | if (result.warnings.length === 0 && result.errors.length === 0) {
588 | result.suggestions.push('Consider adding error handling with onError property and timeout configuration');
589 | result.suggestions.push('Add authentication if connecting to external services');
590 | }
591 | // Require error handling for external service nodes
592 | this.enforceErrorHandlingForProfile(result, profile);
593 | break;
594 |
595 | case 'ai-friendly':
596 | default:
597 | // Current behavior - balanced for AI agents
598 | // Filter out noise but keep helpful warnings
599 | result.warnings = result.warnings.filter(w => {
600 | // Filter out hardcoded credential type warnings (only show in strict mode)
601 | if (this.shouldFilterCredentialWarning(w)) {
602 | return false;
603 | }
604 | // Keep security and deprecated warnings
605 | if (w.type === 'security' || w.type === 'deprecated') return true;
606 | // Keep missing common properties
607 | if (w.type === 'missing_common') return true;
608 | // Keep best practice warnings
609 | if (w.type === 'best_practice') return true;
610 | // FILTER OUT inefficient warnings about property visibility (now fixed at source)
611 | if (w.type === 'inefficient' && w.message && w.message.includes('not visible')) {
612 | return false; // These are now rare due to userProvidedKeys fix
613 | }
614 | // Filter out internal property warnings
615 | if (w.type === 'inefficient' && w.property?.startsWith('_')) {
616 | return false;
617 | }
618 | return true;
619 | });
620 | // Add error handling suggestions for AI-friendly profile
621 | this.addErrorHandlingSuggestions(result);
622 | break;
623 | }
624 | }
625 |
626 | /**
627 | * Enforce error handling requirements based on profile
628 | */
629 | private static enforceErrorHandlingForProfile(
630 | result: EnhancedValidationResult,
631 | profile: ValidationProfile
632 | ): void {
633 | // Only enforce for strict profile on external service nodes
634 | if (profile !== 'strict') return;
635 |
636 | const nodeType = result.operation?.resource || '';
637 | const errorProneTypes = ['httpRequest', 'webhook', 'database', 'api', 'slack', 'email', 'openai'];
638 |
639 | if (errorProneTypes.some(type => nodeType.toLowerCase().includes(type))) {
640 | // Add general warning for strict profile
641 | // The actual error handling validation is done in node-specific validators
642 | result.warnings.push({
643 | type: 'best_practice',
644 | property: 'errorHandling',
645 | message: 'External service nodes should have error handling configured',
646 | suggestion: 'Add onError: "continueRegularOutput" or "stopWorkflow" with retryOnFail: true for resilience'
647 | });
648 | }
649 | }
650 |
651 | /**
652 | * Add error handling suggestions for AI-friendly profile
653 | */
654 | private static addErrorHandlingSuggestions(
655 | result: EnhancedValidationResult
656 | ): void {
657 | // Check if there are any network/API related errors
658 | const hasNetworkErrors = result.errors.some(e =>
659 | e.message.toLowerCase().includes('url') ||
660 | e.message.toLowerCase().includes('endpoint') ||
661 | e.message.toLowerCase().includes('api')
662 | );
663 |
664 | if (hasNetworkErrors) {
665 | result.suggestions.push(
666 | 'For API calls, consider adding onError: "continueRegularOutput" with retryOnFail: true and maxTries: 3'
667 | );
668 | }
669 |
670 | // Check for webhook configurations
671 | const isWebhook = result.operation?.resource === 'webhook' ||
672 | result.errors.some(e => e.message.toLowerCase().includes('webhook'));
673 |
674 | if (isWebhook) {
675 | result.suggestions.push(
676 | 'Webhooks should use onError: "continueRegularOutput" to ensure responses are always sent'
677 | );
678 | }
679 | }
680 |
681 | /**
682 | * Validate fixedCollection structures for known problematic nodes
683 | * This prevents the "propertyValues[itemName] is not iterable" error
684 | */
685 | private static validateFixedCollectionStructures(
686 | nodeType: string,
687 | config: Record<string, any>,
688 | result: EnhancedValidationResult
689 | ): void {
690 | // Use the generic FixedCollectionValidator
691 | const validationResult = FixedCollectionValidator.validate(nodeType, config);
692 |
693 | if (!validationResult.isValid) {
694 | // Add errors to the result
695 | for (const error of validationResult.errors) {
696 | result.errors.push({
697 | type: 'invalid_value',
698 | property: error.pattern.split('.')[0], // Get the root property
699 | message: error.message,
700 | fix: error.fix
701 | });
702 | }
703 |
704 | // Apply autofix if available
705 | if (validationResult.autofix) {
706 | // For nodes like If/Filter where the entire config might be replaced,
707 | // we need to handle it specially
708 | if (typeof validationResult.autofix === 'object' && !Array.isArray(validationResult.autofix)) {
709 | result.autofix = {
710 | ...result.autofix,
711 | ...validationResult.autofix
712 | };
713 | } else {
714 | // If the autofix is an array (like for If/Filter nodes), wrap it properly
715 | const firstError = validationResult.errors[0];
716 | if (firstError) {
717 | const rootProperty = firstError.pattern.split('.')[0];
718 | result.autofix = {
719 | ...result.autofix,
720 | [rootProperty]: validationResult.autofix
721 | };
722 | }
723 | }
724 | }
725 | }
726 | }
727 |
728 |
729 | /**
730 | * Validate Switch node structure specifically
731 | */
732 | private static validateSwitchNodeStructure(
733 | config: Record<string, any>,
734 | result: EnhancedValidationResult
735 | ): void {
736 | if (!config.rules) return;
737 |
738 | // Skip if already caught by validateFixedCollectionStructures
739 | const hasFixedCollectionError = result.errors.some(e =>
740 | e.property === 'rules' && e.message.includes('propertyValues[itemName] is not iterable')
741 | );
742 |
743 | if (hasFixedCollectionError) return;
744 |
745 | // Validate rules.values structure if present
746 | if (config.rules.values && Array.isArray(config.rules.values)) {
747 | config.rules.values.forEach((rule: any, index: number) => {
748 | if (!rule.conditions) {
749 | result.warnings.push({
750 | type: 'missing_common',
751 | property: 'rules',
752 | message: `Switch rule ${index + 1} is missing "conditions" property`,
753 | suggestion: 'Each rule in the values array should have a "conditions" property'
754 | });
755 | }
756 | if (!rule.outputKey && rule.renameOutput !== false) {
757 | result.warnings.push({
758 | type: 'missing_common',
759 | property: 'rules',
760 | message: `Switch rule ${index + 1} is missing "outputKey" property`,
761 | suggestion: 'Add "outputKey" to specify which output to use when this rule matches'
762 | });
763 | }
764 | });
765 | }
766 | }
767 |
768 | /**
769 | * Validate If node structure specifically
770 | */
771 | private static validateIfNodeStructure(
772 | config: Record<string, any>,
773 | result: EnhancedValidationResult
774 | ): void {
775 | if (!config.conditions) return;
776 |
777 | // Skip if already caught by validateFixedCollectionStructures
778 | const hasFixedCollectionError = result.errors.some(e =>
779 | e.property === 'conditions' && e.message.includes('propertyValues[itemName] is not iterable')
780 | );
781 |
782 | if (hasFixedCollectionError) return;
783 |
784 | // Add any If-node-specific validation here in the future
785 | }
786 |
787 | /**
788 | * Validate Filter node structure specifically
789 | */
790 | private static validateFilterNodeStructure(
791 | config: Record<string, any>,
792 | result: EnhancedValidationResult
793 | ): void {
794 | if (!config.conditions) return;
795 |
796 | // Skip if already caught by validateFixedCollectionStructures
797 | const hasFixedCollectionError = result.errors.some(e =>
798 | e.property === 'conditions' && e.message.includes('propertyValues[itemName] is not iterable')
799 | );
800 |
801 | if (hasFixedCollectionError) return;
802 |
803 | // Add any Filter-node-specific validation here in the future
804 | }
805 |
806 | /**
807 | * Validate resource and operation values using similarity services
808 | */
809 | private static validateResourceAndOperation(
810 | nodeType: string,
811 | config: Record<string, any>,
812 | result: EnhancedValidationResult
813 | ): void {
814 | // Skip if similarity services not initialized
815 | if (!this.operationSimilarityService || !this.resourceSimilarityService || !this.nodeRepository) {
816 | return;
817 | }
818 |
819 | // Normalize the node type for repository lookups
820 | const normalizedNodeType = NodeTypeNormalizer.normalizeToFullForm(nodeType);
821 |
822 | // Apply defaults for validation
823 | const configWithDefaults = { ...config };
824 |
825 | // If operation is undefined but resource is set, get the default operation for that resource
826 | if (configWithDefaults.operation === undefined && configWithDefaults.resource !== undefined) {
827 | const defaultOperation = this.nodeRepository.getDefaultOperationForResource(normalizedNodeType, configWithDefaults.resource);
828 | if (defaultOperation !== undefined) {
829 | configWithDefaults.operation = defaultOperation;
830 | }
831 | }
832 |
833 | // Validate resource field if present
834 | if (config.resource !== undefined) {
835 | // Remove any existing resource error from base validator to replace with our enhanced version
836 | result.errors = result.errors.filter(e => e.property !== 'resource');
837 | const validResources = this.nodeRepository.getNodeResources(normalizedNodeType);
838 | const resourceIsValid = validResources.some(r => {
839 | const resourceValue = typeof r === 'string' ? r : r.value;
840 | return resourceValue === config.resource;
841 | });
842 |
843 | if (!resourceIsValid && config.resource !== '') {
844 | // Find similar resources
845 | let suggestions: any[] = [];
846 | try {
847 | suggestions = this.resourceSimilarityService.findSimilarResources(
848 | normalizedNodeType,
849 | config.resource,
850 | 3
851 | );
852 | } catch (error) {
853 | // If similarity service fails, continue with validation without suggestions
854 | console.error('Resource similarity service error:', error);
855 | }
856 |
857 | // Build error message with suggestions
858 | let errorMessage = `Invalid resource "${config.resource}" for node ${nodeType}.`;
859 | let fix = '';
860 |
861 | if (suggestions.length > 0) {
862 | const topSuggestion = suggestions[0];
863 | // Always use "Did you mean" for the top suggestion
864 | errorMessage += ` Did you mean "${topSuggestion.value}"?`;
865 | if (topSuggestion.confidence >= 0.8) {
866 | fix = `Change resource to "${topSuggestion.value}". ${topSuggestion.reason}`;
867 | } else {
868 | // For lower confidence, still show valid resources in the fix
869 | fix = `Valid resources: ${validResources.slice(0, 5).map(r => {
870 | const val = typeof r === 'string' ? r : r.value;
871 | return `"${val}"`;
872 | }).join(', ')}${validResources.length > 5 ? '...' : ''}`;
873 | }
874 | } else {
875 | // No similar resources found, list valid ones
876 | fix = `Valid resources: ${validResources.slice(0, 5).map(r => {
877 | const val = typeof r === 'string' ? r : r.value;
878 | return `"${val}"`;
879 | }).join(', ')}${validResources.length > 5 ? '...' : ''}`;
880 | }
881 |
882 | const error: any = {
883 | type: 'invalid_value',
884 | property: 'resource',
885 | message: errorMessage,
886 | fix
887 | };
888 |
889 | // Add suggestion property if we have high confidence suggestions
890 | if (suggestions.length > 0 && suggestions[0].confidence >= 0.5) {
891 | error.suggestion = `Did you mean "${suggestions[0].value}"? ${suggestions[0].reason}`;
892 | }
893 |
894 | result.errors.push(error);
895 |
896 | // Add suggestions to result.suggestions array
897 | if (suggestions.length > 0) {
898 | for (const suggestion of suggestions) {
899 | result.suggestions.push(
900 | `Resource "${config.resource}" not found. Did you mean "${suggestion.value}"? ${suggestion.reason}`
901 | );
902 | }
903 | }
904 | }
905 | }
906 |
907 | // Validate operation field - now we check configWithDefaults which has defaults applied
908 | // Only validate if operation was explicitly set (not undefined) OR if we're using a default
909 | if (config.operation !== undefined || configWithDefaults.operation !== undefined) {
910 | // Remove any existing operation error from base validator to replace with our enhanced version
911 | result.errors = result.errors.filter(e => e.property !== 'operation');
912 |
913 | // Use the operation from configWithDefaults for validation (which includes the default if applied)
914 | const operationToValidate = configWithDefaults.operation || config.operation;
915 | const validOperations = this.nodeRepository.getNodeOperations(normalizedNodeType, config.resource);
916 | const operationIsValid = validOperations.some(op => {
917 | const opValue = op.operation || op.value || op;
918 | return opValue === operationToValidate;
919 | });
920 |
921 | // Only report error if the explicit operation is invalid (not for defaults)
922 | if (!operationIsValid && config.operation !== undefined && config.operation !== '') {
923 | // Find similar operations
924 | let suggestions: any[] = [];
925 | try {
926 | suggestions = this.operationSimilarityService.findSimilarOperations(
927 | normalizedNodeType,
928 | config.operation,
929 | config.resource,
930 | 3
931 | );
932 | } catch (error) {
933 | // If similarity service fails, continue with validation without suggestions
934 | console.error('Operation similarity service error:', error);
935 | }
936 |
937 | // Build error message with suggestions
938 | let errorMessage = `Invalid operation "${config.operation}" for node ${nodeType}`;
939 | if (config.resource) {
940 | errorMessage += ` with resource "${config.resource}"`;
941 | }
942 | errorMessage += '.';
943 |
944 | let fix = '';
945 |
946 | if (suggestions.length > 0) {
947 | const topSuggestion = suggestions[0];
948 | if (topSuggestion.confidence >= 0.8) {
949 | errorMessage += ` Did you mean "${topSuggestion.value}"?`;
950 | fix = `Change operation to "${topSuggestion.value}". ${topSuggestion.reason}`;
951 | } else {
952 | errorMessage += ` Similar operations: ${suggestions.map(s => `"${s.value}"`).join(', ')}`;
953 | fix = `Valid operations${config.resource ? ` for resource "${config.resource}"` : ''}: ${validOperations.slice(0, 5).map(op => {
954 | const val = op.operation || op.value || op;
955 | return `"${val}"`;
956 | }).join(', ')}${validOperations.length > 5 ? '...' : ''}`;
957 | }
958 | } else {
959 | // No similar operations found, list valid ones
960 | fix = `Valid operations${config.resource ? ` for resource "${config.resource}"` : ''}: ${validOperations.slice(0, 5).map(op => {
961 | const val = op.operation || op.value || op;
962 | return `"${val}"`;
963 | }).join(', ')}${validOperations.length > 5 ? '...' : ''}`;
964 | }
965 |
966 | const error: any = {
967 | type: 'invalid_value',
968 | property: 'operation',
969 | message: errorMessage,
970 | fix
971 | };
972 |
973 | // Add suggestion property if we have high confidence suggestions
974 | if (suggestions.length > 0 && suggestions[0].confidence >= 0.5) {
975 | error.suggestion = `Did you mean "${suggestions[0].value}"? ${suggestions[0].reason}`;
976 | }
977 |
978 | result.errors.push(error);
979 |
980 | // Add suggestions to result.suggestions array
981 | if (suggestions.length > 0) {
982 | for (const suggestion of suggestions) {
983 | result.suggestions.push(
984 | `Operation "${config.operation}" not found. Did you mean "${suggestion.value}"? ${suggestion.reason}`
985 | );
986 | }
987 | }
988 | }
989 | }
990 | }
991 |
992 | /**
993 | * Validate special type structures (filter, resourceMapper, assignmentCollection, resourceLocator)
994 | *
995 | * Integrates TypeStructureService to validate complex property types against their
996 | * expected structures. This catches configuration errors for advanced node types.
997 | *
998 | * @param config - Node configuration to validate
999 | * @param properties - Property definitions from node schema
1000 | * @param result - Validation result to populate with errors/warnings
1001 | */
1002 | private static validateSpecialTypeStructures(
1003 | config: Record<string, any>,
1004 | properties: any[],
1005 | result: EnhancedValidationResult
1006 | ): void {
1007 | for (const [key, value] of Object.entries(config)) {
1008 | if (value === undefined || value === null) continue;
1009 |
1010 | // Find property definition
1011 | const propDef = properties.find(p => p.name === key);
1012 | if (!propDef) continue;
1013 |
1014 | // Check if this property uses a special type
1015 | let structureType: NodePropertyTypes | null = null;
1016 |
1017 | if (propDef.type === 'filter') {
1018 | structureType = 'filter';
1019 | } else if (propDef.type === 'resourceMapper') {
1020 | structureType = 'resourceMapper';
1021 | } else if (propDef.type === 'assignmentCollection') {
1022 | structureType = 'assignmentCollection';
1023 | } else if (propDef.type === 'resourceLocator') {
1024 | structureType = 'resourceLocator';
1025 | }
1026 |
1027 | if (!structureType) continue;
1028 |
1029 | // Get structure definition
1030 | const structure = TypeStructureService.getStructure(structureType);
1031 | if (!structure) {
1032 | console.warn(`No structure definition found for type: ${structureType}`);
1033 | continue;
1034 | }
1035 |
1036 | // Validate using TypeStructureService for basic type checking
1037 | const validationResult = TypeStructureService.validateTypeCompatibility(
1038 | value,
1039 | structureType
1040 | );
1041 |
1042 | // Add errors from structure validation
1043 | if (!validationResult.valid) {
1044 | for (const error of validationResult.errors) {
1045 | result.errors.push({
1046 | type: 'invalid_configuration',
1047 | property: key,
1048 | message: error,
1049 | fix: `Ensure ${key} follows the expected structure for ${structureType} type. Example: ${JSON.stringify(structure.example)}`
1050 | });
1051 | }
1052 | }
1053 |
1054 | // Add warnings
1055 | for (const warning of validationResult.warnings) {
1056 | result.warnings.push({
1057 | type: 'best_practice',
1058 | property: key,
1059 | message: warning
1060 | });
1061 | }
1062 |
1063 | // Perform deep structure validation for complex types
1064 | if (typeof value === 'object' && value !== null) {
1065 | this.validateComplexTypeStructure(key, value, structureType, structure, result);
1066 | }
1067 |
1068 | // Special handling for filter operation validation
1069 | if (structureType === 'filter' && value.conditions) {
1070 | this.validateFilterOperations(value.conditions, key, result);
1071 | }
1072 | }
1073 | }
1074 |
1075 | /**
1076 | * Deep validation for complex type structures
1077 | */
1078 | private static validateComplexTypeStructure(
1079 | propertyName: string,
1080 | value: any,
1081 | type: NodePropertyTypes,
1082 | structure: any,
1083 | result: EnhancedValidationResult
1084 | ): void {
1085 | switch (type) {
1086 | case 'filter':
1087 | // Validate filter structure: must have combinator and conditions
1088 | if (!value.combinator) {
1089 | result.errors.push({
1090 | type: 'invalid_configuration',
1091 | property: `${propertyName}.combinator`,
1092 | message: 'Filter must have a combinator field',
1093 | fix: 'Add combinator: "and" or combinator: "or" to the filter configuration'
1094 | });
1095 | } else if (value.combinator !== 'and' && value.combinator !== 'or') {
1096 | result.errors.push({
1097 | type: 'invalid_configuration',
1098 | property: `${propertyName}.combinator`,
1099 | message: `Invalid combinator value: ${value.combinator}. Must be "and" or "or"`,
1100 | fix: 'Set combinator to either "and" or "or"'
1101 | });
1102 | }
1103 |
1104 | if (!value.conditions) {
1105 | result.errors.push({
1106 | type: 'invalid_configuration',
1107 | property: `${propertyName}.conditions`,
1108 | message: 'Filter must have a conditions field',
1109 | fix: 'Add conditions array to the filter configuration'
1110 | });
1111 | } else if (!Array.isArray(value.conditions)) {
1112 | result.errors.push({
1113 | type: 'invalid_configuration',
1114 | property: `${propertyName}.conditions`,
1115 | message: 'Filter conditions must be an array',
1116 | fix: 'Ensure conditions is an array of condition objects'
1117 | });
1118 | }
1119 | break;
1120 |
1121 | case 'resourceLocator':
1122 | // Validate resourceLocator structure: must have mode and value
1123 | if (!value.mode) {
1124 | result.errors.push({
1125 | type: 'invalid_configuration',
1126 | property: `${propertyName}.mode`,
1127 | message: 'ResourceLocator must have a mode field',
1128 | fix: 'Add mode: "id", mode: "url", or mode: "list" to the resourceLocator configuration'
1129 | });
1130 | } else if (!['id', 'url', 'list', 'name'].includes(value.mode)) {
1131 | result.errors.push({
1132 | type: 'invalid_configuration',
1133 | property: `${propertyName}.mode`,
1134 | message: `Invalid mode value: ${value.mode}. Must be "id", "url", "list", or "name"`,
1135 | fix: 'Set mode to one of: "id", "url", "list", "name"'
1136 | });
1137 | }
1138 |
1139 | if (!value.hasOwnProperty('value')) {
1140 | result.errors.push({
1141 | type: 'invalid_configuration',
1142 | property: `${propertyName}.value`,
1143 | message: 'ResourceLocator must have a value field',
1144 | fix: 'Add value field to the resourceLocator configuration'
1145 | });
1146 | }
1147 | break;
1148 |
1149 | case 'assignmentCollection':
1150 | // Validate assignmentCollection structure: must have assignments array
1151 | if (!value.assignments) {
1152 | result.errors.push({
1153 | type: 'invalid_configuration',
1154 | property: `${propertyName}.assignments`,
1155 | message: 'AssignmentCollection must have an assignments field',
1156 | fix: 'Add assignments array to the assignmentCollection configuration'
1157 | });
1158 | } else if (!Array.isArray(value.assignments)) {
1159 | result.errors.push({
1160 | type: 'invalid_configuration',
1161 | property: `${propertyName}.assignments`,
1162 | message: 'AssignmentCollection assignments must be an array',
1163 | fix: 'Ensure assignments is an array of assignment objects'
1164 | });
1165 | }
1166 | break;
1167 |
1168 | case 'resourceMapper':
1169 | // Validate resourceMapper structure: must have mappingMode
1170 | if (!value.mappingMode) {
1171 | result.errors.push({
1172 | type: 'invalid_configuration',
1173 | property: `${propertyName}.mappingMode`,
1174 | message: 'ResourceMapper must have a mappingMode field',
1175 | fix: 'Add mappingMode: "defineBelow" or mappingMode: "autoMapInputData"'
1176 | });
1177 | } else if (!['defineBelow', 'autoMapInputData'].includes(value.mappingMode)) {
1178 | result.errors.push({
1179 | type: 'invalid_configuration',
1180 | property: `${propertyName}.mappingMode`,
1181 | message: `Invalid mappingMode: ${value.mappingMode}. Must be "defineBelow" or "autoMapInputData"`,
1182 | fix: 'Set mappingMode to either "defineBelow" or "autoMapInputData"'
1183 | });
1184 | }
1185 | break;
1186 | }
1187 | }
1188 |
1189 | /**
1190 | * Validate filter operations match operator types
1191 | *
1192 | * Ensures that filter operations are compatible with their operator types.
1193 | * For example, 'gt' (greater than) is only valid for numbers, not strings.
1194 | *
1195 | * @param conditions - Array of filter conditions to validate
1196 | * @param propertyName - Name of the filter property (for error reporting)
1197 | * @param result - Validation result to populate with errors
1198 | */
1199 | private static validateFilterOperations(
1200 | conditions: any,
1201 | propertyName: string,
1202 | result: EnhancedValidationResult
1203 | ): void {
1204 | if (!Array.isArray(conditions)) return;
1205 |
1206 | // Operation validation rules based on n8n filter type definitions
1207 | const VALID_OPERATIONS_BY_TYPE: Record<string, string[]> = {
1208 | string: [
1209 | 'empty', 'notEmpty', 'equals', 'notEquals',
1210 | 'contains', 'notContains', 'startsWith', 'notStartsWith',
1211 | 'endsWith', 'notEndsWith', 'regex', 'notRegex',
1212 | 'exists', 'notExists', 'isNotEmpty' // exists checks field presence, isNotEmpty alias for notEmpty
1213 | ],
1214 | number: [
1215 | 'empty', 'notEmpty', 'equals', 'notEquals', 'gt', 'lt', 'gte', 'lte',
1216 | 'exists', 'notExists', 'isNotEmpty'
1217 | ],
1218 | dateTime: [
1219 | 'empty', 'notEmpty', 'equals', 'notEquals', 'after', 'before', 'afterOrEquals', 'beforeOrEquals',
1220 | 'exists', 'notExists', 'isNotEmpty'
1221 | ],
1222 | boolean: [
1223 | 'empty', 'notEmpty', 'true', 'false', 'equals', 'notEquals',
1224 | 'exists', 'notExists', 'isNotEmpty'
1225 | ],
1226 | array: [
1227 | 'contains', 'notContains', 'lengthEquals', 'lengthNotEquals',
1228 | 'lengthGt', 'lengthLt', 'lengthGte', 'lengthLte', 'empty', 'notEmpty',
1229 | 'exists', 'notExists', 'isNotEmpty'
1230 | ],
1231 | object: [
1232 | 'empty', 'notEmpty',
1233 | 'exists', 'notExists', 'isNotEmpty'
1234 | ],
1235 | any: ['exists', 'notExists', 'isNotEmpty']
1236 | };
1237 |
1238 | for (let i = 0; i < conditions.length; i++) {
1239 | const condition = conditions[i];
1240 | if (!condition.operator || typeof condition.operator !== 'object') continue;
1241 |
1242 | const { type, operation } = condition.operator;
1243 | if (!type || !operation) continue;
1244 |
1245 | // Get valid operations for this type
1246 | const validOperations = VALID_OPERATIONS_BY_TYPE[type];
1247 | if (!validOperations) {
1248 | result.warnings.push({
1249 | type: 'best_practice',
1250 | property: `${propertyName}.conditions[${i}].operator.type`,
1251 | message: `Unknown operator type: ${type}`
1252 | });
1253 | continue;
1254 | }
1255 |
1256 | // Check if operation is valid for this type
1257 | if (!validOperations.includes(operation)) {
1258 | result.errors.push({
1259 | type: 'invalid_value',
1260 | property: `${propertyName}.conditions[${i}].operator.operation`,
1261 | message: `Operation '${operation}' is not valid for type '${type}'`,
1262 | fix: `Use one of the valid operations for ${type}: ${validOperations.join(', ')}`
1263 | });
1264 | }
1265 | }
1266 | }
1267 | }
1268 |
```