diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c49ea673f007..9e5ffd4e1b12a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,56 @@ +# [1.73.0](https://github.com/n8n-io/n8n/compare/n8n@1.72.0...n8n@1.73.0) (2024-12-19) + + +### Bug Fixes + +* **core:** Ensure runners do not throw on unsupported console methods ([#12167](https://github.com/n8n-io/n8n/issues/12167)) ([57c6a61](https://github.com/n8n-io/n8n/commit/57c6a6167dd2b30f0082a416daefce994ecad33a)) +* **core:** Fix `$getWorkflowStaticData` on task runners ([#12153](https://github.com/n8n-io/n8n/issues/12153)) ([b479f14](https://github.com/n8n-io/n8n/commit/b479f14ef5012551b823bea5d2ffbddedfd50a77)) +* **core:** Fix binary data helpers (like `prepareBinaryData`) with task runner ([#12259](https://github.com/n8n-io/n8n/issues/12259)) ([0f1461f](https://github.com/n8n-io/n8n/commit/0f1461f2d5d7ec34236ed7fcec3e2f9ee7eb73c4)) +* **core:** Fix race condition in AI tool invocation with multiple items from the parent ([#12169](https://github.com/n8n-io/n8n/issues/12169)) ([dce0c58](https://github.com/n8n-io/n8n/commit/dce0c58f8605c33fc50ec8aa422f0fb5eee07637)) +* **core:** Fix serialization of circular json with task runner ([#12288](https://github.com/n8n-io/n8n/issues/12288)) ([a99d726](https://github.com/n8n-io/n8n/commit/a99d726f42d027b64f94eda0d385b597c5d5be2e)) +* **core:** Upgrade nanoid to address CVE-2024-55565 ([#12171](https://github.com/n8n-io/n8n/issues/12171)) ([8c0bd02](https://github.com/n8n-io/n8n/commit/8c0bd0200c386b122f495c453ccc97a001e4729c)) +* **editor:** Add new create first project CTA ([#12189](https://github.com/n8n-io/n8n/issues/12189)) ([878b419](https://github.com/n8n-io/n8n/commit/878b41904d76eda3ee230f850127b4d56993de24)) +* **editor:** Fix canvas ready opacity transition on new canvas ([#12264](https://github.com/n8n-io/n8n/issues/12264)) ([5d33a6b](https://github.com/n8n-io/n8n/commit/5d33a6ba8a2bccea097402fd04c0e2b00e423e76)) +* **editor:** Fix rendering of code-blocks in sticky notes ([#12227](https://github.com/n8n-io/n8n/issues/12227)) ([9b59035](https://github.com/n8n-io/n8n/commit/9b5903524b95bd21d5915908780942790cf88d27)) +* **editor:** Fix sticky color picker getting covered by nodes on new canvas ([#12263](https://github.com/n8n-io/n8n/issues/12263)) ([27bd3c8](https://github.com/n8n-io/n8n/commit/27bd3c85b3a4ddcf763a543b232069bb108130cf)) +* **editor:** Improve commit modal user facing messaging ([#12161](https://github.com/n8n-io/n8n/issues/12161)) ([ad39243](https://github.com/n8n-io/n8n/commit/ad392439826b17bd0b84f981e0958d88f09e7fe9)) +* **editor:** Prevent connection line from showing when clicking the plus button of a node ([#12265](https://github.com/n8n-io/n8n/issues/12265)) ([9180b46](https://github.com/n8n-io/n8n/commit/9180b46b52302b203eecf3bb81c3f2132527a1e6)) +* **editor:** Prevent stickies from being edited in preview mode in the new canvas ([#12222](https://github.com/n8n-io/n8n/issues/12222)) ([6706dcd](https://github.com/n8n-io/n8n/commit/6706dcdf72d54f33c1cf4956602c3a64a1578826)) +* **editor:** Reduce cases for Auto-Add of ChatTrigger for AI Agents ([#12154](https://github.com/n8n-io/n8n/issues/12154)) ([365e82d](https://github.com/n8n-io/n8n/commit/365e82d2008dff2f9c91664ee04d7a78363a8b30)) +* **editor:** Remove invalid connections after node handles change ([#12247](https://github.com/n8n-io/n8n/issues/12247)) ([6330bec](https://github.com/n8n-io/n8n/commit/6330bec4db0175b558f2747837323fdbb25b634a)) +* **editor:** Set dangerouslyUseHTMLString in composable ([#12280](https://github.com/n8n-io/n8n/issues/12280)) ([6ba91b5](https://github.com/n8n-io/n8n/commit/6ba91b5e1ed197c67146347a6f6e663ecdf3de48)) +* **editor:** Set RunData outputIndex based on incoming data ([#12182](https://github.com/n8n-io/n8n/issues/12182)) ([dc4261a](https://github.com/n8n-io/n8n/commit/dc4261ae7eca6cf277404cd514c90fad42f14ae0)) +* **editor:** Update the universal create button interaction ([#12105](https://github.com/n8n-io/n8n/issues/12105)) ([5300e0a](https://github.com/n8n-io/n8n/commit/5300e0ac45bf832b3d2957198a49a1c687f3fe1f)) +* **Elasticsearch Node:** Fix issue stopping search queries being sent ([#11464](https://github.com/n8n-io/n8n/issues/11464)) ([388a83d](https://github.com/n8n-io/n8n/commit/388a83dfbdc6ac301e4df704666df9f09fb7d0b3)) +* **Extract from File Node:** Detect file encoding ([#12081](https://github.com/n8n-io/n8n/issues/12081)) ([92af245](https://github.com/n8n-io/n8n/commit/92af245d1aab5bfad8618fda69b2405f5206875d)) +* **Github Node:** Fix fetch of file names with ? character ([#12206](https://github.com/n8n-io/n8n/issues/12206)) ([39462ab](https://github.com/n8n-io/n8n/commit/39462abe1fde7e82b5e5b8f3ceebfcadbfd7c925)) +* **Invoice Ninja Node:** Fix actions for bank transactions ([#11511](https://github.com/n8n-io/n8n/issues/11511)) ([80eea49](https://github.com/n8n-io/n8n/commit/80eea49cf0bf9db438eb85af7cd22aeb11fbfed2)) +* **Linear Node:** Fix issue with error handling ([#12191](https://github.com/n8n-io/n8n/issues/12191)) ([b8eae5f](https://github.com/n8n-io/n8n/commit/b8eae5f28a7d523195f4715cd8da77b3a884ae4c)) +* **MongoDB Node:** Fix checks on projection feature call ([#10563](https://github.com/n8n-io/n8n/issues/10563)) ([58bab46](https://github.com/n8n-io/n8n/commit/58bab461c4c5026b2ca5ea143cbcf98bf3a4ced8)) +* **Postgres Node:** Allow users to wrap strings with $$ ([#12034](https://github.com/n8n-io/n8n/issues/12034)) ([0c15e30](https://github.com/n8n-io/n8n/commit/0c15e30778cc5cb10ed368df144d6fbb2504ec70)) +* **Redis Node:** Add support for username auth ([#12274](https://github.com/n8n-io/n8n/issues/12274)) ([64c0414](https://github.com/n8n-io/n8n/commit/64c0414ef28acf0f7ec42b4b0bb21cbf2921ebe7)) + + +### Features + +* Add solarwinds ipam credentials ([#12005](https://github.com/n8n-io/n8n/issues/12005)) ([882484e](https://github.com/n8n-io/n8n/commit/882484e8ee7d1841d5d600414ca48e9915abcfa8)) +* Add SolarWinds Observability node credentials ([#11805](https://github.com/n8n-io/n8n/issues/11805)) ([e8a5db5](https://github.com/n8n-io/n8n/commit/e8a5db5beb572edbb61dd9100b70827ccc4cca58)) +* **AI Agent Node:** Update descriptions and titles for Chat Trigger options in AI Agents and Memory ([#12155](https://github.com/n8n-io/n8n/issues/12155)) ([07a6ae1](https://github.com/n8n-io/n8n/commit/07a6ae11b3291c1805553d55ba089fe8dd919fd8)) +* **API:** Exclude pinned data from workflows ([#12261](https://github.com/n8n-io/n8n/issues/12261)) ([e0dc385](https://github.com/n8n-io/n8n/commit/e0dc385f8bc8ee13fbc5bbf35e07654e52b193e9)) +* **editor:** Params pane collection improvements ([#11607](https://github.com/n8n-io/n8n/issues/11607)) ([6e44c71](https://github.com/n8n-io/n8n/commit/6e44c71c9ca82cce20eb55bb9003930bbf66a16c)) +* **editor:** Support adding nodes via drag and drop from node creator on new canvas ([#12197](https://github.com/n8n-io/n8n/issues/12197)) ([1bfd9c0](https://github.com/n8n-io/n8n/commit/1bfd9c0e913f3eefc4593f6c344db1ae1f6e4df4)) +* **Facebook Graph API Node:** Update node to support API v21.0 ([#12116](https://github.com/n8n-io/n8n/issues/12116)) ([14c33f6](https://github.com/n8n-io/n8n/commit/14c33f666fe92f7173e4f471fb478e629e775c62)) +* **Linear Trigger Node:** Add support for admin scope ([#12211](https://github.com/n8n-io/n8n/issues/12211)) ([410ea9a](https://github.com/n8n-io/n8n/commit/410ea9a2ef2e14b5e8e4493e5db66cfc2290d8f6)) +* **MailerLite Node:** Update node to support new api ([#11933](https://github.com/n8n-io/n8n/issues/11933)) ([d6b8e65](https://github.com/n8n-io/n8n/commit/d6b8e65abeb411f86538c1630dcce832ee0846a9)) +* Send and wait operation - freeText and customForm response types ([#12106](https://github.com/n8n-io/n8n/issues/12106)) ([e98c7f1](https://github.com/n8n-io/n8n/commit/e98c7f160b018243dc88490d46fb1047a4d7fcdc)) + + +### Performance Improvements + +* **editor:** SchemaView performance improvement by ≈90% 🚀 ([#12180](https://github.com/n8n-io/n8n/issues/12180)) ([6a58309](https://github.com/n8n-io/n8n/commit/6a5830959f5fb493a4119869b8298d8ed702c84a)) + + + # [1.72.0](https://github.com/n8n-io/n8n/compare/n8n@1.71.0...n8n@1.72.0) (2024-12-11) diff --git a/cypress/e2e/48-subworkflow-inputs.cy.ts b/cypress/e2e/48-subworkflow-inputs.cy.ts index d3315f6cca724..02eff605f8494 100644 --- a/cypress/e2e/48-subworkflow-inputs.cy.ts +++ b/cypress/e2e/48-subworkflow-inputs.cy.ts @@ -107,6 +107,7 @@ function validateAndReturnToParent(targetChild: string, offset: number, fields: // Due to our workaround to remain in the same tab we need to select the correct tab manually navigateWorkflowSelectionDropdown(offset, targetChild); + // This fails, pointing to `usePushConnection` `const triggerNode = subWorkflow?.nodes.find` being `undefined.find()`I ndv.actions.execute(); getOutputTableHeaders().should('have.length', fields.length + 1); diff --git a/cypress/fixtures/Test_Subworkflow-Inputs.json b/cypress/fixtures/Test_Subworkflow-Inputs.json index 1b2320510fdea..5b96c0e3f2c8e 100644 --- a/cypress/fixtures/Test_Subworkflow-Inputs.json +++ b/cypress/fixtures/Test_Subworkflow-Inputs.json @@ -23,13 +23,23 @@ }, { "parameters": { + "workflowId": {}, + "workflowInputs": { + "mappingMode": "defineBelow", + "value": {}, + "matchingColumns": [], + "schema": [], + "ignoreTypeMismatchErrors": false, + "attemptToConvertTypes": false, + "convertFieldsToString": true + }, "options": {} }, - "id": "ddc82976-edd9-4488-a5a5-7f558a7d905b", - "name": "Execute Workflow", "type": "n8n-nodes-base.executeWorkflow", - "typeVersion": 1.1, - "position": [500, 240] + "typeVersion": 1.2, + "position": [500, 240], + "id": "6b6e2e34-c6ab-4083-b8e3-6b0d56be5453", + "name": "Execute Workflow" } ], "connections": { diff --git a/docker/images/n8n-custom/Dockerfile b/docker/images/n8n-custom/Dockerfile index 2b72365eb82f3..13592140a4e07 100644 --- a/docker/images/n8n-custom/Dockerfile +++ b/docker/images/n8n-custom/Dockerfile @@ -33,7 +33,7 @@ COPY docker/images/n8n/docker-entrypoint.sh / # Setup the Task Runner Launcher ARG TARGETPLATFORM -ARG LAUNCHER_VERSION=1.0.0 +ARG LAUNCHER_VERSION=1.1.0 COPY docker/images/n8n/n8n-task-runners.json /etc/n8n-task-runners.json # Download, verify, then extract the launcher binary RUN \ diff --git a/docker/images/n8n/Dockerfile b/docker/images/n8n/Dockerfile index 740773618536e..10720c63f20e3 100644 --- a/docker/images/n8n/Dockerfile +++ b/docker/images/n8n/Dockerfile @@ -24,7 +24,7 @@ RUN set -eux; \ # Setup the Task Runner Launcher ARG TARGETPLATFORM -ARG LAUNCHER_VERSION=1.0.0 +ARG LAUNCHER_VERSION=1.1.0 COPY n8n-task-runners.json /etc/n8n-task-runners.json # Download, verify, then extract the launcher binary RUN \ diff --git a/package.json b/package.json index 29be8d868af7f..a42a17ee441b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-monorepo", - "version": "1.72.0", + "version": "1.73.0", "private": true, "engines": { "node": ">=20.15", diff --git a/packages/@n8n/api-types/package.json b/packages/@n8n/api-types/package.json index fac0011437895..c14e18992236e 100644 --- a/packages/@n8n/api-types/package.json +++ b/packages/@n8n/api-types/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/api-types", - "version": "0.10.0", + "version": "0.11.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/config/package.json b/packages/@n8n/config/package.json index 961079acd6e9c..c4368a75c555b 100644 --- a/packages/@n8n/config/package.json +++ b/packages/@n8n/config/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/config", - "version": "1.22.0", + "version": "1.23.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts index d4e205ec8840c..4b2ddf5db98fb 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainLLM/ChainLlm.node.ts @@ -254,6 +254,7 @@ export class ChainLlm implements INodeType { displayName: 'Basic LLM Chain', name: 'chainLlm', icon: 'fa:link', + iconColor: 'black', group: ['transform'], version: [1, 1.1, 1.2, 1.3, 1.4, 1.5], description: 'A simple chain to prompt a large language model', diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts index 9c7c7397011d5..7829bc7813cb2 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainRetrievalQA/ChainRetrievalQa.node.ts @@ -31,6 +31,7 @@ export class ChainRetrievalQa implements INodeType { displayName: 'Question and Answer Chain', name: 'chainRetrievalQa', icon: 'fa:link', + iconColor: 'black', group: ['transform'], version: [1, 1.1, 1.2, 1.3, 1.4], description: 'Answer questions about retrieved documents', diff --git a/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/ChainSummarization.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/ChainSummarization.node.ts index cd47eb6a1531f..9c97190952167 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/ChainSummarization.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/ChainSummarization/ChainSummarization.node.ts @@ -10,6 +10,7 @@ export class ChainSummarization extends VersionedNodeType { displayName: 'Summarization Chain', name: 'chainSummarization', icon: 'fa:link', + iconColor: 'black', group: ['transform'], description: 'Transforms text into a concise summary', codex: { diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts index 480bed68f9591..3bdcd0d1fd094 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryBufferWindow/MemoryBufferWindow.node.ts @@ -78,6 +78,7 @@ export class MemoryBufferWindow implements INodeType { displayName: 'Window Buffer Memory (easiest)', name: 'memoryBufferWindow', icon: 'fa:database', + iconColor: 'black', group: ['transform'], version: [1, 1.1, 1.2, 1.3], description: 'Stores in n8n memory, so no credentials required', diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.ts index fa54f25a16424..82fcba22a66ce 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryChatRetriever/MemoryChatRetriever.node.ts @@ -38,6 +38,7 @@ export class MemoryChatRetriever implements INodeType { displayName: 'Chat Messages Retriever', name: 'memoryChatRetriever', icon: 'fa:database', + iconColor: 'black', group: ['transform'], hidden: true, version: 1, diff --git a/packages/@n8n/nodes-langchain/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.ts b/packages/@n8n/nodes-langchain/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.ts index f5184d7e9353c..06fa387ee65a1 100644 --- a/packages/@n8n/nodes-langchain/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/memory/MemoryMotorhead/MemoryMotorhead.node.ts @@ -19,6 +19,7 @@ export class MemoryMotorhead implements INodeType { displayName: 'Motorhead', name: 'memoryMotorhead', icon: 'fa:file-export', + iconColor: 'black', group: ['transform'], version: [1, 1.1, 1.2, 1.3], description: 'Use Motorhead Memory', diff --git a/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserAutofixing/OutputParserAutofixing.node.ts b/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserAutofixing/OutputParserAutofixing.node.ts index 0ccf4c27c01e6..f9e6cd2968516 100644 --- a/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserAutofixing/OutputParserAutofixing.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserAutofixing/OutputParserAutofixing.node.ts @@ -21,6 +21,7 @@ export class OutputParserAutofixing implements INodeType { displayName: 'Auto-fixing Output Parser', name: 'outputParserAutofixing', icon: 'fa:tools', + iconColor: 'black', group: ['transform'], version: 1, description: 'Automatically fix the output if it is not in the correct format', diff --git a/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserItemList/OutputParserItemList.node.ts b/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserItemList/OutputParserItemList.node.ts index 696a6be79c1be..b94b82fadac49 100644 --- a/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserItemList/OutputParserItemList.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserItemList/OutputParserItemList.node.ts @@ -15,6 +15,7 @@ export class OutputParserItemList implements INodeType { displayName: 'Item List Output Parser', name: 'outputParserItemList', icon: 'fa:bars', + iconColor: 'black', group: ['transform'], version: 1, description: 'Return the results as separate items', diff --git a/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserStructured/OutputParserStructured.node.ts b/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserStructured/OutputParserStructured.node.ts index 8da4cb05d875e..08690209970c3 100644 --- a/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserStructured/OutputParserStructured.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/output_parser/OutputParserStructured/OutputParserStructured.node.ts @@ -20,6 +20,7 @@ export class OutputParserStructured implements INodeType { displayName: 'Structured Output Parser', name: 'outputParserStructured', icon: 'fa:code', + iconColor: 'black', group: ['transform'], version: [1, 1.1, 1.2], defaultVersion: 1.2, diff --git a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverContextualCompression/RetrieverContextualCompression.node.ts b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverContextualCompression/RetrieverContextualCompression.node.ts index 74db608551f6f..feb70ecb43d31 100644 --- a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverContextualCompression/RetrieverContextualCompression.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverContextualCompression/RetrieverContextualCompression.node.ts @@ -19,6 +19,7 @@ export class RetrieverContextualCompression implements INodeType { displayName: 'Contextual Compression Retriever', name: 'retrieverContextualCompression', icon: 'fa:box-open', + iconColor: 'black', group: ['transform'], version: 1, description: 'Enhances document similarity search by contextual compression.', diff --git a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverMultiQuery/RetrieverMultiQuery.node.ts b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverMultiQuery/RetrieverMultiQuery.node.ts index 3805eb5374482..4bbc45f6d14aa 100644 --- a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverMultiQuery/RetrieverMultiQuery.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverMultiQuery/RetrieverMultiQuery.node.ts @@ -18,6 +18,7 @@ export class RetrieverMultiQuery implements INodeType { displayName: 'MultiQuery Retriever', name: 'retrieverMultiQuery', icon: 'fa:box-open', + iconColor: 'black', group: ['transform'], version: 1, description: diff --git a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverVectorStore/RetrieverVectorStore.node.ts b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverVectorStore/RetrieverVectorStore.node.ts index 74f88e5561930..915d9766dccec 100644 --- a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverVectorStore/RetrieverVectorStore.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverVectorStore/RetrieverVectorStore.node.ts @@ -15,6 +15,7 @@ export class RetrieverVectorStore implements INodeType { displayName: 'Vector Store Retriever', name: 'retrieverVectorStore', icon: 'fa:box-open', + iconColor: 'black', group: ['transform'], version: 1, description: 'Use a Vector Store as Retriever', diff --git a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverWorkflow/RetrieverWorkflow.node.ts b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverWorkflow/RetrieverWorkflow.node.ts index 5e9fecd47aeb4..1291b92252f36 100644 --- a/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverWorkflow/RetrieverWorkflow.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/retrievers/RetrieverWorkflow/RetrieverWorkflow.node.ts @@ -41,6 +41,7 @@ export class RetrieverWorkflow implements INodeType { displayName: 'Workflow Retriever', name: 'retrieverWorkflow', icon: 'fa:box-open', + iconColor: 'black', group: ['transform'], version: [1, 1.1], description: 'Use an n8n Workflow as Retriever', diff --git a/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterCharacterTextSplitter/TextSplitterCharacterTextSplitter.node.ts b/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterCharacterTextSplitter/TextSplitterCharacterTextSplitter.node.ts index c78bd39a6c90b..962af5bde2321 100644 --- a/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterCharacterTextSplitter/TextSplitterCharacterTextSplitter.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterCharacterTextSplitter/TextSplitterCharacterTextSplitter.node.ts @@ -17,6 +17,7 @@ export class TextSplitterCharacterTextSplitter implements INodeType { displayName: 'Character Text Splitter', name: 'textSplitterCharacterTextSplitter', icon: 'fa:grip-lines-vertical', + iconColor: 'black', group: ['transform'], version: 1, description: 'Split text into chunks by characters', diff --git a/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterRecursiveCharacterTextSplitter/TextSplitterRecursiveCharacterTextSplitter.node.ts b/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterRecursiveCharacterTextSplitter/TextSplitterRecursiveCharacterTextSplitter.node.ts index cfe8a3275747f..4e376c39a37d2 100644 --- a/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterRecursiveCharacterTextSplitter/TextSplitterRecursiveCharacterTextSplitter.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterRecursiveCharacterTextSplitter/TextSplitterRecursiveCharacterTextSplitter.node.ts @@ -37,6 +37,7 @@ export class TextSplitterRecursiveCharacterTextSplitter implements INodeType { displayName: 'Recursive Character Text Splitter', name: 'textSplitterRecursiveCharacterTextSplitter', icon: 'fa:grip-lines-vertical', + iconColor: 'black', group: ['transform'], version: 1, description: 'Split text into chunks by characters recursively, recommended for most use cases', diff --git a/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterTokenSplitter/TextSplitterTokenSplitter.node.ts b/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterTokenSplitter/TextSplitterTokenSplitter.node.ts index cd881916d6ac1..b5dade396d767 100644 --- a/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterTokenSplitter/TextSplitterTokenSplitter.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/text_splitters/TextSplitterTokenSplitter/TextSplitterTokenSplitter.node.ts @@ -16,6 +16,7 @@ export class TextSplitterTokenSplitter implements INodeType { displayName: 'Token Splitter', name: 'textSplitterTokenSplitter', icon: 'fa:grip-lines-vertical', + iconColor: 'black', group: ['transform'], version: 1, description: 'Split text into chunks by tokens', diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolCalculator/ToolCalculator.node.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolCalculator/ToolCalculator.node.ts index b3ed23c5769d6..6d67a04555138 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolCalculator/ToolCalculator.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolCalculator/ToolCalculator.node.ts @@ -16,6 +16,7 @@ export class ToolCalculator implements INodeType { displayName: 'Calculator', name: 'toolCalculator', icon: 'fa:calculator', + iconColor: 'black', group: ['transform'], version: 1, description: 'Make it easier for AI agents to perform arithmetic', diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts index 214d4ed82aada..029bce48f68f7 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts @@ -26,6 +26,7 @@ export class ToolCode implements INodeType { displayName: 'Code Tool', name: 'toolCode', icon: 'fa:code', + iconColor: 'black', group: ['transform'], version: [1, 1.1], description: 'Write a tool in JS or Python', diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolVectorStore/ToolVectorStore.node.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolVectorStore/ToolVectorStore.node.ts index 4b539e7e85dd1..aaa2ca37d96aa 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolVectorStore/ToolVectorStore.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolVectorStore/ToolVectorStore.node.ts @@ -18,6 +18,7 @@ export class ToolVectorStore implements INodeType { displayName: 'Vector Store Tool', name: 'toolVectorStore', icon: 'fa:database', + iconColor: 'black', group: ['transform'], version: [1], description: 'Retrieve context from vector store', diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v1/versionDescription.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v1/versionDescription.ts index d092e2327813b..da7a0e9815cea 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v1/versionDescription.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v1/versionDescription.ts @@ -14,13 +14,30 @@ export const versionDescription: INodeTypeDescription = { displayName: 'Call n8n Workflow Tool', name: 'toolWorkflow', icon: 'fa:network-wired', + iconColor: 'black', group: ['transform'], - description: 'Uses another n8n workflow as a tool. Allows packaging any n8n node(s) as a tool.', version: [1, 1.1, 1.2, 1.3], + description: 'Uses another n8n workflow as a tool. Allows packaging any n8n node(s) as a tool.', defaults: { name: 'Call n8n Workflow Tool', }, + codex: { + categories: ['AI'], + subcategories: { + AI: ['Tools'], + Tools: ['Recommended Tools'], + }, + resources: { + primaryDocumentation: [ + { + url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolworkflow/', + }, + ], + }, + }, + // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node inputs: [], + // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong outputs: [NodeConnectionType.AiTool], outputNames: ['Tool'], properties: [ @@ -65,7 +82,7 @@ export const versionDescription: INodeTypeDescription = { type: 'string', default: '', placeholder: - 'Call this tool to get a random color. The input should be a string with comma separated names of colors to exclude.', + 'Call this tool to get a random color. The input should be a string with comma separted names of colors to exclude.', typeOptions: { rows: 3, }, diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/ToolWorkflowV2.node.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/ToolWorkflowV2.node.ts index 5ab1604262bc4..22ca31e4da2b1 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/ToolWorkflowV2.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/ToolWorkflowV2.node.ts @@ -1,3 +1,4 @@ +import { loadWorkflowInputMappings } from 'n8n-nodes-base/dist/utils/workflowInputsResourceMapping/GenericFunctions'; import type { INodeTypeBaseDescription, ISupplyDataFunctions, @@ -6,7 +7,6 @@ import type { INodeTypeDescription, } from 'n8n-workflow'; -import { loadWorkflowInputMappings } from './methods/resourceMapping'; import { WorkflowToolService } from './utils/WorkflowToolService'; import { versionDescription } from './versionDescription'; diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/methods/resourceMapping.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/methods/resourceMapping.ts deleted file mode 100644 index e0c0b0d6650d2..0000000000000 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/methods/resourceMapping.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { - getFieldEntries, - getWorkflowInputData, -} from 'n8n-nodes-base/dist/utils/workflowInputsResourceMapping/GenericFunctions'; -import type { - ISupplyDataFunctions, - IDataObject, - FieldValueOption, - ResourceMapperField, - ILocalLoadOptionsFunctions, - ResourceMapperFields, -} from 'n8n-workflow'; -import { EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE } from 'n8n-workflow'; - -export async function loadWorkflowInputMappings( - this: ILocalLoadOptionsFunctions, -): Promise { - const nodeLoadContext = await this.getWorkflowNodeContext(EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE); - let fields: ResourceMapperField[] = []; - if (nodeLoadContext) { - const fieldValues = getFieldEntries(nodeLoadContext); - fields = fieldValues.map((currentWorkflowInput) => { - const field: ResourceMapperField = { - id: currentWorkflowInput.name, - displayName: currentWorkflowInput.name, - required: false, - defaultMatch: false, - display: true, - canBeUsedToMatch: true, - }; - - if (currentWorkflowInput.type !== 'any') { - field.type = currentWorkflowInput.type; - } - - return field; - }); - } - return { fields }; -} - -export function getWorkflowInputValues(this: ISupplyDataFunctions) { - const inputData = this.getInputData(); - - return inputData.map((item, itemIndex) => { - const itemFieldValues = this.getNodeParameter( - 'workflowInputs.value', - itemIndex, - {}, - ) as IDataObject; - - return { - json: { - ...item.json, - ...itemFieldValues, - }, - index: itemIndex, - pairedItem: { - item: itemIndex, - }, - }; - }); -} - -export function getCurrentWorkflowInputData(this: ISupplyDataFunctions) { - const inputData = getWorkflowInputValues.call(this); - - const schema = this.getNodeParameter('workflowInputs.schema', 0, []) as ResourceMapperField[]; - - if (schema.length === 0) { - return inputData; - } else { - const newParams = schema - .filter((x) => !x.removed) - .map((x) => ({ name: x.displayName, type: x.type ?? 'any' })) as FieldValueOption[]; - - return getWorkflowInputData.call(this, inputData, newParams); - } -} diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/utils/WorkflowToolService.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/utils/WorkflowToolService.ts index 7ea2bf0284174..5412f17ccd46c 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/utils/WorkflowToolService.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolWorkflow/v2/utils/WorkflowToolService.ts @@ -4,6 +4,7 @@ import get from 'lodash/get'; import isObject from 'lodash/isObject'; import type { SetField, SetNodeOptions } from 'n8n-nodes-base/dist/nodes/Set/v2/helpers/interfaces'; import * as manual from 'n8n-nodes-base/dist/nodes/Set/v2/manual.mode'; +import { getCurrentWorkflowInputData } from 'n8n-nodes-base/dist/utils/workflowInputsResourceMapping/GenericFunctions'; import type { ExecuteWorkflowData, ExecutionError, @@ -22,7 +23,6 @@ import { z } from 'zod'; import type { FromAIArgument } from './FromAIParser'; import { AIParametersParser } from './FromAIParser'; -import { getCurrentWorkflowInputData } from '../methods/resourceMapping'; /** Main class for creating the Workflow tool diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts index dc99db630d942..0323478ee8c58 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStoreInMemory/VectorStoreInMemory.node.ts @@ -26,6 +26,7 @@ export class VectorStoreInMemory extends createVectorStoreNode({ name: 'vectorStoreInMemory', description: 'Work with your data in In-Memory Vector Store', icon: 'fa:database', + iconColor: 'black', docsUrl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstoreinmemory/', }, diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.ts index 6e684ebed3bd4..711425df55edf 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/VectorStorePinecone.node.ts @@ -56,7 +56,7 @@ export class VectorStorePinecone extends createVectorStoreNode({ displayName: 'Pinecone Vector Store', name: 'vectorStorePinecone', description: 'Work with your data in Pinecone Vector Store', - icon: 'file:pinecone.svg', + icon: { light: 'file:pinecone.svg', dark: 'file:pinecone.dark.svg' }, docsUrl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstorepinecone/', credentials: [ diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/pinecone.dark.svg b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/pinecone.dark.svg new file mode 100644 index 0000000000000..4d163c6784eb6 --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/pinecone.dark.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/pinecone.svg b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/pinecone.svg index b94b8b3af6696..e9884a4249f2c 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/pinecone.svg +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/VectorStorePinecone/pinecone.svg @@ -1 +1,21 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts index 6d4abfb0cd262..84f1d550e5032 100644 --- a/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts +++ b/packages/@n8n/nodes-langchain/nodes/vector_store/shared/createVectorStoreNode.ts @@ -17,6 +17,7 @@ import type { INodeListSearchResult, Icon, INodePropertyOptions, + ThemeIconColor, } from 'n8n-workflow'; import { getMetadataFiltersValues, logAiEvent } from '@utils/helpers'; @@ -37,6 +38,7 @@ interface NodeMeta { description: string; docsUrl: string; icon: Icon; + iconColor?: ThemeIconColor; credentials?: INodeCredentialDescription[]; operationModes?: NodeOperationMode[]; } @@ -125,6 +127,7 @@ export const createVectorStoreNode = (args: VectorStoreNodeConstructorArgs) => name: args.meta.name, description: args.meta.description, icon: args.meta.icon, + iconColor: args.meta.iconColor, group: ['transform'], version: 1, defaults: { diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 05715fcf7cd75..f19b8c005852f 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/n8n-nodes-langchain", - "version": "1.72.0", + "version": "1.73.0", "description": "", "main": "index.js", "scripts": { diff --git a/packages/@n8n/task-runner/package.json b/packages/@n8n/task-runner/package.json index f82692db77c42..212909990e5da 100644 --- a/packages/@n8n/task-runner/package.json +++ b/packages/@n8n/task-runner/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/task-runner", - "version": "1.10.0", + "version": "1.11.0", "scripts": { "clean": "rimraf dist .turbo", "start": "node dist/start.js", diff --git a/packages/cli/package.json b/packages/cli/package.json index 317aeb0d9ce36..8e9ff0f7ca483 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.72.0", + "version": "1.73.0", "description": "n8n Workflow Automation Tool", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/cli/src/runners/__tests__/task-runner-ws-server.test.ts b/packages/cli/src/runners/__tests__/task-runner-ws-server.test.ts index b092e08fedccc..24b12fa1906c1 100644 --- a/packages/cli/src/runners/__tests__/task-runner-ws-server.test.ts +++ b/packages/cli/src/runners/__tests__/task-runner-ws-server.test.ts @@ -58,4 +58,28 @@ describe('TaskRunnerWsServer', () => { expect(clearIntervalSpy).toHaveBeenCalled(); }); }); + + describe('sendMessage', () => { + it('should work with a message containing circular references', () => { + const server = new TaskRunnerWsServer(mock(), mock(), mock(), mock(), mock()); + const ws = mock(); + server.runnerConnections.set('test-runner', ws); + + const messageData: Record = {}; + messageData.circular = messageData; + + expect(() => + server.sendMessage('test-runner', { + type: 'broker:taskdataresponse', + taskId: 'taskId', + requestId: 'requestId', + data: messageData, + }), + ).not.toThrow(); + + expect(ws.send).toHaveBeenCalledWith( + '{"type":"broker:taskdataresponse","taskId":"taskId","requestId":"requestId","data":{"circular":"[Circular Reference]"}}', + ); + }); + }); }); diff --git a/packages/cli/src/runners/runner-ws-server.ts b/packages/cli/src/runners/runner-ws-server.ts index 3a5fa53029697..8ea3a7edbe547 100644 --- a/packages/cli/src/runners/runner-ws-server.ts +++ b/packages/cli/src/runners/runner-ws-server.ts @@ -1,6 +1,6 @@ import { TaskRunnersConfig } from '@n8n/config'; import type { BrokerMessage, RunnerMessage } from '@n8n/task-runner'; -import { ApplicationError } from 'n8n-workflow'; +import { ApplicationError, jsonStringify } from 'n8n-workflow'; import { Service } from 'typedi'; import type WebSocket from 'ws'; @@ -83,7 +83,7 @@ export class TaskRunnerWsServer { } sendMessage(id: TaskRunner['id'], message: BrokerMessage.ToRunner.All) { - this.runnerConnections.get(id)?.send(JSON.stringify(message)); + this.runnerConnections.get(id)?.send(jsonStringify(message, { replaceCircularRefs: true })); } add(id: TaskRunner['id'], connection: WebSocket) { diff --git a/packages/cli/test/setup-test-folder.ts b/packages/cli/test/setup-test-folder.ts index 997a0ec80f811..8a58c48f865f4 100644 --- a/packages/cli/test/setup-test-folder.ts +++ b/packages/cli/test/setup-test-folder.ts @@ -10,6 +10,7 @@ mkdirSync(baseDir, { recursive: true }); const testDir = mkdtempSync(baseDir); mkdirSync(join(testDir, '.n8n')); process.env.N8N_USER_FOLDER = testDir; +process.env.N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS = 'false'; writeFileSync( join(testDir, '.n8n/config'), diff --git a/packages/core/package.json b/packages/core/package.json index 84cd8f7c8a596..26b41800d9c75 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "1.72.0", + "version": "1.73.0", "description": "Core functionality of n8n", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/core/src/node-execution-context/utils.ts b/packages/core/src/node-execution-context/utils.ts index a09147d543b23..bcde2fc6d7365 100644 --- a/packages/core/src/node-execution-context/utils.ts +++ b/packages/core/src/node-execution-context/utils.ts @@ -83,7 +83,6 @@ const validateResourceMapperValue = ( for (let i = 0; i < paramValueNames.length; i++) { const key = paramValueNames[i]; const resolvedValue = paramValues[key]; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call const schemaEntry = schema.find((s) => s.id === key); if ( @@ -99,15 +98,19 @@ const validateResourceMapperValue = ( }; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (schemaEntry?.type) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const validationResult = validateFieldType(key, resolvedValue, schemaEntry.type, { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access valueOptions: schemaEntry.options, + strict: !resourceMapperField.attemptToConvertTypes, + parseStrings: !!resourceMapperField.convertFieldsToString, }); + if (!validationResult.valid) { - return { ...validationResult, fieldName: key }; + if (!resourceMapperField.ignoreTypeMismatchErrors) { + return { ...validationResult, fieldName: key }; + } else { + paramValues[key] = resolvedValue; + } } else { // If it's valid, set the casted value paramValues[key] = validationResult.newValue; diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 47e5a2d13eb04..d4a7e1dcec9db 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,6 +1,6 @@ { "name": "n8n-design-system", - "version": "1.62.0", + "version": "1.63.0", "main": "src/main.ts", "import": "src/main.ts", "scripts": { diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 68b13e13f492d..3293abfe3b3d6 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "1.72.0", + "version": "1.73.0", "description": "Workflow Editor UI for n8n", "main": "index.js", "scripts": { diff --git a/packages/editor-ui/src/components/ResourceMapper/ResourceMapper.vue b/packages/editor-ui/src/components/ResourceMapper/ResourceMapper.vue index 39850fea0a36e..0e592d27db43c 100644 --- a/packages/editor-ui/src/components/ResourceMapper/ResourceMapper.vue +++ b/packages/editor-ui/src/components/ResourceMapper/ResourceMapper.vue @@ -7,6 +7,7 @@ import type { INodeParameters, INodeProperties, INodeTypeDescription, + NodeParameterValueType, ResourceMapperField, ResourceMapperFields, ResourceMapperValue, @@ -61,6 +62,12 @@ const state = reactive({ value: {}, matchingColumns: [] as string[], schema: [] as ResourceMapperField[], + ignoreTypeMismatchErrors: false, + attemptToConvertTypes: false, + // This should always be true if `showTypeConversionOptions` is provided + // It's used to avoid accepting any value as string without casting it + // Which is the legacy behavior without these type options. + convertFieldsToString: false, } as ResourceMapperValue, parameterValues: {} as INodeParameters, loading: false, @@ -122,6 +129,10 @@ onMounted(async () => { ...state.parameterValues, parameters: props.node.parameters, }; + + if (showTypeConversionOptions.value) { + state.paramValue.convertFieldsToString = true; + } } const params = state.parameterValues.parameters as INodeParameters; const parameterName = props.parameter.name; @@ -188,6 +199,10 @@ const showMappingModeSelect = computed(() => { return props.parameter.typeOptions?.resourceMapper?.supportAutoMap !== false; }); +const showTypeConversionOptions = computed(() => { + return props.parameter.typeOptions?.resourceMapper?.showTypeConversionOptions === true; +}); + const showMatchingColumnsSelector = computed(() => { return ( !state.loading && @@ -620,5 +635,52 @@ defineExpose({ }) }} +
+ + +
+ + diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 9795e0189e985..740cc8afb7025 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -1591,6 +1591,10 @@ "resourceMapper.refreshFieldList": "Refresh {fieldWord} List", "resourceMapper.staleDataWarning.tooltip": "{fieldWord} are outdated. Refresh to see the changes.", "resourceMapper.staleDataWarning.notice": "Refresh to see the updated fields", + "resourceMapper.attemptToConvertTypes.displayName": "Attempt to convert types", + "resourceMapper.attemptToConvertTypes.description": "Attempt to convert types when mapping fields", + "resourceMapper.ignoreTypeMismatchErrors.displayName": "Ignore type mismatch errors", + "resourceMapper.ignoreTypeMismatchErrors.description": "Whether type mismatches should be ignored, rather than returning an Error", "runData.openSubExecution": "Inspect Sub-Execution {id}", "runData.openParentExecution": "Inspect Parent Execution {id}", "runData.emptyItemHint": "This is an item, but it's empty.", diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 6a08e4b96c674..96e133aa174ae 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "1.72.0", + "version": "1.73.0", "description": "CLI to simplify n8n credentials/node development", "main": "dist/src/index", "types": "dist/src/index.d.ts", diff --git a/packages/nodes-base/credentials/Redis.credentials.ts b/packages/nodes-base/credentials/Redis.credentials.ts index 194c2f59e3acf..9d9bbab3942d5 100644 --- a/packages/nodes-base/credentials/Redis.credentials.ts +++ b/packages/nodes-base/credentials/Redis.credentials.ts @@ -17,6 +17,13 @@ export class Redis implements ICredentialType { }, default: '', }, + { + displayName: 'User', + name: 'user', + type: 'string', + default: '', + hint: 'Leave blank for password-only auth', + }, { displayName: 'Host', name: 'host', diff --git a/packages/nodes-base/credentials/SolarWindsIpamApi.credentials.ts b/packages/nodes-base/credentials/SolarWindsIpamApi.credentials.ts new file mode 100644 index 0000000000000..2980978031736 --- /dev/null +++ b/packages/nodes-base/credentials/SolarWindsIpamApi.credentials.ts @@ -0,0 +1,85 @@ +import type { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; + +export class SolarWindsIpamApi implements ICredentialType { + name = 'solarWindsIpamApi'; + + displayName = 'SolarWinds IPAM'; + + documentationUrl = 'solarwindsipam'; + + icon = { + light: 'file:icons/SolarWindsIpam.svg', + dark: 'file:icons/SolarWindsIpam.svg', + } as const; + + httpRequestNode = { + name: 'SolarWinds IPAM', + docsUrl: 'https://www.solarwinds.com/ip-address-manager', + apiBaseUrlPlaceholder: 'https://your-ipam-server', + }; + + properties: INodeProperties[] = [ + { + displayName: 'Base URL', + name: 'url', + required: true, + type: 'string', + default: '', + placeholder: 'https://your-ipam-server', + description: 'The base URL of your SolarWinds IPAM server.', + }, + { + displayName: 'Username', + name: 'username', + required: true, + type: 'string', + default: '', + description: 'The username for SolarWinds IPAM API.', + }, + { + displayName: 'Password', + name: 'password', + required: true, + type: 'string', + typeOptions: { password: true }, + default: '', + description: 'The password for SolarWinds IPAM API.', + }, + ]; + + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + auth: { + username: '={{$credentials.username}}', + password: '={{$credentials.password}}', + }, + }, + }; + + test: ICredentialTestRequest = { + request: { + baseURL: '={{$credentials.url}}'.replace(/\/$/, ''), + url: '/SolarWinds/InformationService/v3/Json/Query', + method: 'GET', + qs: { + query: 'SELECT TOP 1 AccountID FROM IPAM.AccountRoles', + }, + skipSslCertificateValidation: true, + }, + rules: [ + { + type: 'responseCode', + properties: { + value: 403, + message: 'Connection failed: Invalid credentials or unreachable server', + }, + }, + ], + }; +} diff --git a/packages/nodes-base/credentials/icons/SolarWindsIpam.svg b/packages/nodes-base/credentials/icons/SolarWindsIpam.svg new file mode 100644 index 0000000000000..814afbdb7cac7 --- /dev/null +++ b/packages/nodes-base/credentials/icons/SolarWindsIpam.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/ExecuteWorkflow.node.ts b/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/ExecuteWorkflow.node.ts index 121a5ad38bc26..2f67d7bfe9b6f 100644 --- a/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/ExecuteWorkflow.node.ts +++ b/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/ExecuteWorkflow.node.ts @@ -1,59 +1,18 @@ import { NodeConnectionType, NodeOperationError } from 'n8n-workflow'; import type { ExecuteWorkflowData, - FieldValueOption, - IDataObject, IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, - ResourceMapperField, } from 'n8n-workflow'; import { getWorkflowInfo } from './GenericFunctions'; -import { loadWorkflowInputMappings } from './methods/resourceMapping'; import { generatePairedItemData } from '../../../utils/utilities'; -import { getWorkflowInputData } from '../../../utils/workflowInputsResourceMapping/GenericFunctions'; - -function getWorkflowInputValues(this: IExecuteFunctions) { - const inputData = this.getInputData(); - - return inputData.map((item, itemIndex) => { - const itemFieldValues = this.getNodeParameter( - 'workflowInputs.value', - itemIndex, - {}, - ) as IDataObject; - - return { - json: { - ...item.json, - ...itemFieldValues, - }, - index: itemIndex, - pairedItem: { - item: itemIndex, - }, - }; - }); -} - -function getCurrentWorkflowInputData(this: IExecuteFunctions) { - const inputData = getWorkflowInputValues.call(this); - - const schema = this.getNodeParameter('workflowInputs.schema', 0, []) as ResourceMapperField[]; - - if (schema.length === 0) { - return inputData; - } else { - const newParams = schema - .filter((x) => !x.removed) - .map((x) => ({ name: x.displayName, type: x.type ?? 'any' })) as FieldValueOption[]; - - return getWorkflowInputData.call(this, inputData, newParams); - } -} - +import { + getCurrentWorkflowInputData, + loadWorkflowInputMappings, +} from '../../../utils/workflowInputsResourceMapping/GenericFunctions'; export class ExecuteWorkflow implements INodeType { description: INodeTypeDescription = { displayName: 'Execute Workflow', @@ -84,6 +43,13 @@ export class ExecuteWorkflow implements INodeType { }, ], }, + { + displayName: 'This node is out of date. Please upgrade by removing it and adding a new one', + name: 'outdatedVersionWarning', + type: 'notice', + displayOptions: { show: { '@version': [{ _cnd: { lte: 1.1 } }] } }, + default: '', + }, { displayName: 'Source', name: 'source', @@ -254,6 +220,7 @@ export class ExecuteWorkflow implements INodeType { addAllFields: true, multiKeyMatch: false, supportAutoMap: false, + showTypeConversionOptions: true, }, }, displayOptions: { diff --git a/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/methods/resourceMapping.ts b/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/methods/resourceMapping.ts deleted file mode 100644 index c747f6f505b9d..0000000000000 --- a/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflow/methods/resourceMapping.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, - type ILocalLoadOptionsFunctions, - type ResourceMapperField, - type ResourceMapperFields, -} from 'n8n-workflow'; - -import { getFieldEntries } from '../../../../utils/workflowInputsResourceMapping/GenericFunctions'; - -export async function loadWorkflowInputMappings( - this: ILocalLoadOptionsFunctions, -): Promise { - const nodeLoadContext = await this.getWorkflowNodeContext(EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE); - let fields: ResourceMapperField[] = []; - - if (nodeLoadContext) { - const fieldValues = getFieldEntries(nodeLoadContext); - - fields = fieldValues.map((currentWorkflowInput) => { - const field: ResourceMapperField = { - id: currentWorkflowInput.name, - displayName: currentWorkflowInput.name, - required: false, - defaultMatch: false, - display: true, - canBeUsedToMatch: true, - }; - - if (currentWorkflowInput.type !== 'any') { - field.type = currentWorkflowInput.type; - } - - return field; - }); - } - return { fields }; -} diff --git a/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.test.ts b/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.test.ts index 953f78fd21d3b..b479538c3a727 100644 --- a/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.test.ts +++ b/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.test.ts @@ -2,10 +2,8 @@ import { mock } from 'jest-mock-extended'; import type { FieldValueOption, IExecuteFunctions, INode, INodeExecutionData } from 'n8n-workflow'; import { ExecuteWorkflowTrigger } from './ExecuteWorkflowTrigger.node'; -import { - getFieldEntries, - getWorkflowInputData, -} from '../../../utils/workflowInputsResourceMapping/GenericFunctions'; +import { WORKFLOW_INPUTS } from '../../../utils/workflowInputsResourceMapping/constants'; +import { getFieldEntries } from '../../../utils/workflowInputsResourceMapping/GenericFunctions'; jest.mock('../../../utils/workflowInputsResourceMapping/GenericFunctions', () => ({ getFieldEntries: jest.fn(), @@ -14,8 +12,8 @@ jest.mock('../../../utils/workflowInputsResourceMapping/GenericFunctions', () => describe('ExecuteWorkflowTrigger', () => { const mockInputData: INodeExecutionData[] = [ - { json: { item: 0, foo: 'bar' } }, - { json: { item: 1, foo: 'quz' } }, + { json: { item: 0, foo: 'bar' }, index: 0 }, + { json: { item: 1, foo: 'quz' }, index: 1 }, ]; const mockNode = mock({ typeVersion: 1 }); const executeFns = mock({ @@ -24,28 +22,32 @@ describe('ExecuteWorkflowTrigger', () => { getNodeParameter: jest.fn(), }); - it('should return its input data on V1', async () => { + it('should return its input data on V1 or V1.1 passthrough', async () => { + // User selection in V1.1, or fallback return value in V1 with dropdown not displayed executeFns.getNodeParameter.mockReturnValueOnce('passthrough'); const result = await new ExecuteWorkflowTrigger().execute.call(executeFns); expect(result).toEqual([mockInputData]); }); - it('should return transformed input data based on newParams when input source is not passthrough', async () => { - executeFns.getNodeParameter.mockReturnValueOnce('usingFieldsBelow'); + it('should filter out parent input in `Using Fields below` mode', async () => { + executeFns.getNodeParameter.mockReturnValueOnce(WORKFLOW_INPUTS); const mockNewParams = [ { name: 'value1', type: 'string' }, { name: 'value2', type: 'number' }, + { name: 'foo', type: 'string' }, ] as FieldValueOption[]; const getFieldEntriesMock = (getFieldEntries as jest.Mock).mockReturnValue(mockNewParams); - const getWorkflowInputDataMock = (getWorkflowInputData as jest.Mock).mockReturnValue( - mockInputData, - ); const result = await new ExecuteWorkflowTrigger().execute.call(executeFns); - - expect(result).toEqual([mockInputData]); + const expected = [ + [ + { index: 0, json: { value1: null, value2: null, foo: mockInputData[0].json.foo } }, + { index: 1, json: { value1: null, value2: null, foo: mockInputData[1].json.foo } }, + ], + ]; + + expect(result).toEqual(expected); expect(getFieldEntriesMock).toHaveBeenCalledWith(executeFns); - expect(getWorkflowInputDataMock).toHaveBeenCalledWith(mockInputData, mockNewParams); }); }); diff --git a/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.ts b/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.ts index 0c8e5f64ce7cf..b2c4321cdb666 100644 --- a/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.ts +++ b/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.ts @@ -1,4 +1,6 @@ +import _ from 'lodash'; import { + type INodeExecutionData, NodeConnectionType, type IExecuteFunctions, type INodeType, @@ -10,14 +12,11 @@ import { WORKFLOW_INPUTS, JSON_EXAMPLE, VALUES, - INPUT_OPTIONS, TYPE_OPTIONS, PASSTHROUGH, + FALLBACK_DEFAULT_VALUE, } from '../../../utils/workflowInputsResourceMapping/constants'; -import { - getFieldEntries, - getWorkflowInputData, -} from '../../../utils/workflowInputsResourceMapping/GenericFunctions'; +import { getFieldEntries } from '../../../utils/workflowInputsResourceMapping/GenericFunctions'; export class ExecuteWorkflowTrigger implements INodeType { description: INodeTypeDescription = { @@ -66,6 +65,23 @@ export class ExecuteWorkflowTrigger implements INodeType { ], default: 'worklfow_call', }, + { + displayName: + "When an ‘execute workflow’ node calls this workflow, the execution starts here. Any data passed into the 'execute workflow' node will be output by this node.", + name: 'notice', + type: 'notice', + default: '', + displayOptions: { + show: { '@version': [{ _cnd: { eq: 1 } }] }, + }, + }, + { + displayName: 'This node is out of date. Please upgrade by removing it and adding a new one', + name: 'outdatedVersionWarning', + type: 'notice', + displayOptions: { show: { '@version': [{ _cnd: { eq: 1 } }] } }, + default: '', + }, { displayName: 'Input Source', name: INPUT_SOURCE, @@ -166,39 +182,6 @@ export class ExecuteWorkflowTrigger implements INodeType { }, ], }, - { - displayName: 'Input Options', - name: INPUT_OPTIONS, - placeholder: 'Options', - type: 'collection', - description: 'Options controlling how input data is handled, converted and rejected', - displayOptions: { - show: { '@version': [{ _cnd: { gte: 1.1 } }] }, - }, - default: {}, - // Note that, while the defaults are true, the user has to add these in the first place - // We default to false if absent in the execute function below - options: [ - { - displayName: 'Attempt to Convert Types', - name: 'attemptToConvertTypes', - type: 'boolean', - default: true, - description: - 'Whether to attempt conversion on type mismatch, rather than directly returning an Error', - noDataExpression: true, - }, - { - displayName: 'Ignore Type Mismatch Errors', - name: 'ignoreTypeErrors', - type: 'boolean', - default: true, - description: - 'Whether type mismatches should be ignored, rather than returning an Error', - noDataExpression: true, - }, - ], - }, ], }; @@ -206,12 +189,33 @@ export class ExecuteWorkflowTrigger implements INodeType { const inputData = this.getInputData(); const inputSource = this.getNodeParameter(INPUT_SOURCE, 0, PASSTHROUGH) as string; + // Note on the data we receive from ExecuteWorkflow caller: + // + // The ExecuteWorkflow node typechecks all fields explicitly provided by the user here via the resourceMapper + // and removes all fields that are in the schema, but `removed` in the resourceMapper. + // + // In passthrough and legacy node versions, inputData will line up since the resourceMapper is empty, + // in which case all input is passed through. + // In other cases we will already have matching types and fields provided by the resource mapper, + // so we just need to be permissive on this end, + // while ensuring we provide default values for fields in our schema, which are removed in the resourceMapper. + if (inputSource === PASSTHROUGH) { return [inputData]; } else { const newParams = getFieldEntries(this); + const newKeys = new Set(newParams.map((x) => x.name)); + const itemsInSchema: INodeExecutionData[] = inputData.map((row, index) => ({ + json: { + ...Object.fromEntries(newParams.map((x) => [x.name, FALLBACK_DEFAULT_VALUE])), + // Need to trim to the expected schema to support legacy Execute Workflow callers passing through all their data + // which we do not want to expose past this node. + ..._.pickBy(row.json, (_value, key) => newKeys.has(key)), + }, + index, + })); - return [getWorkflowInputData.call(this, inputData, newParams)]; + return [itemsInSchema]; } } } diff --git a/packages/nodes-base/nodes/Redis/Redis.node.ts b/packages/nodes-base/nodes/Redis/Redis.node.ts index ccd826eb892eb..014c28d3d605f 100644 --- a/packages/nodes-base/nodes/Redis/Redis.node.ts +++ b/packages/nodes-base/nodes/Redis/Redis.node.ts @@ -15,6 +15,7 @@ import { getValue, setValue, } from './utils'; +import type { RedisCredential } from './types'; export class Redis implements INodeType { description: INodeTypeDescription = { @@ -512,7 +513,7 @@ export class Redis implements INodeType { // have a parameter field for a path. Because it is not possible to set // array, object via parameter directly (should maybe be possible?!?!) // Should maybe have a parameter which is JSON. - const credentials = await this.getCredentials('redis'); + const credentials = await this.getCredentials('redis'); const client = setupRedisClient(credentials); await client.connect(); diff --git a/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts b/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts index 34b4bb8d62c3b..680d19b026dd8 100644 --- a/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts +++ b/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts @@ -7,6 +7,7 @@ import type { import { NodeConnectionType, NodeOperationError } from 'n8n-workflow'; import { redisConnectionTest, setupRedisClient } from './utils'; +import type { RedisCredential } from './types'; interface Options { jsonParseBody: boolean; @@ -74,7 +75,7 @@ export class RedisTrigger implements INodeType { }; async trigger(this: ITriggerFunctions): Promise { - const credentials = await this.getCredentials('redis'); + const credentials = await this.getCredentials('redis'); const channels = (this.getNodeParameter('channels') as string).split(','); const options = this.getNodeParameter('options') as Options; diff --git a/packages/nodes-base/nodes/Redis/test/Redis.node.test.ts b/packages/nodes-base/nodes/Redis/__tests__/Redis.node.test.ts similarity index 83% rename from packages/nodes-base/nodes/Redis/test/Redis.node.test.ts rename to packages/nodes-base/nodes/Redis/__tests__/Redis.node.test.ts index 6339459eb1b92..789aef524248a 100644 --- a/packages/nodes-base/nodes/Redis/test/Redis.node.test.ts +++ b/packages/nodes-base/nodes/Redis/__tests__/Redis.node.test.ts @@ -1,18 +1,24 @@ -import type { RedisClientType } from '@redis/client'; import { mock } from 'jest-mock-extended'; -import { NodeOperationError, type IExecuteFunctions } from 'n8n-workflow'; - -const mockClient = mock(); +import type { + ICredentialsDecrypted, + ICredentialTestFunctions, + IExecuteFunctions, +} from 'n8n-workflow'; +import { NodeOperationError } from 'n8n-workflow'; + +const mockClient = mock(); const createClient = jest.fn().mockReturnValue(mockClient); jest.mock('redis', () => ({ createClient })); import { Redis } from '../Redis.node'; -import { setupRedisClient } from '../utils'; +import { redisConnectionTest, setupRedisClient } from '../utils'; +import type { RedisClient } from '../types'; describe('Redis Node', () => { const node = new Redis(); beforeEach(() => { + jest.clearAllMocks(); createClient.mockReturnValue(mockClient); }); @@ -27,7 +33,6 @@ describe('Redis Node', () => { }); expect(createClient).toHaveBeenCalledWith({ database: 0, - password: undefined, socket: { host: 'redis.domain', port: 1234, @@ -45,7 +50,6 @@ describe('Redis Node', () => { }); expect(createClient).toHaveBeenCalledWith({ database: 0, - password: undefined, socket: { host: 'redis.domain', port: 1234, @@ -53,6 +57,75 @@ describe('Redis Node', () => { }, }); }); + + it('should set user on auth', () => { + setupRedisClient({ + host: 'redis.domain', + port: 1234, + database: 0, + user: 'test_user', + password: 'test_password', + }); + expect(createClient).toHaveBeenCalledWith({ + database: 0, + username: 'test_user', + password: 'test_password', + socket: { + host: 'redis.domain', + port: 1234, + tls: false, + }, + }); + }); + }); + + describe('redisConnectionTest', () => { + const thisArg = mock({}); + const credentials = mock({ + data: { + host: 'localhost', + port: 6379, + user: 'username', + password: 'password', + database: 0, + }, + }); + const redisOptions = { + socket: { + host: 'localhost', + port: 6379, + tls: false, + }, + database: 0, + username: 'username', + password: 'password', + }; + + it('should return success when connection is established', async () => { + const result = await redisConnectionTest.call(thisArg, credentials); + + expect(result).toEqual({ + status: 'OK', + message: 'Connection successful!', + }); + expect(createClient).toHaveBeenCalledWith(redisOptions); + expect(mockClient.connect).toHaveBeenCalled(); + expect(mockClient.ping).toHaveBeenCalled(); + }); + + it('should return error when connection fails', async () => { + mockClient.connect.mockRejectedValue(new Error('Connection failed')); + + const result = await redisConnectionTest.call(thisArg, credentials); + + expect(result).toEqual({ + status: 'Error', + message: 'Connection failed', + }); + expect(createClient).toHaveBeenCalledWith(redisOptions); + expect(mockClient.connect).toHaveBeenCalled(); + expect(mockClient.ping).not.toHaveBeenCalled(); + }); }); describe('operations', () => { diff --git a/packages/nodes-base/nodes/Redis/test/RedisTrigger.node.test.ts b/packages/nodes-base/nodes/Redis/__tests__/RedisTrigger.node.test.ts similarity index 97% rename from packages/nodes-base/nodes/Redis/test/RedisTrigger.node.test.ts rename to packages/nodes-base/nodes/Redis/__tests__/RedisTrigger.node.test.ts index eadad5fe9042a..97227fbe04cee 100644 --- a/packages/nodes-base/nodes/Redis/test/RedisTrigger.node.test.ts +++ b/packages/nodes-base/nodes/Redis/__tests__/RedisTrigger.node.test.ts @@ -3,10 +3,11 @@ import { captor, mock } from 'jest-mock-extended'; import type { ICredentialDataDecryptedObject, ITriggerFunctions } from 'n8n-workflow'; import { RedisTrigger } from '../RedisTrigger.node'; -import { type RedisClientType, setupRedisClient } from '../utils'; +import { setupRedisClient } from '../utils'; +import type { RedisClient } from '../types'; jest.mock('../utils', () => { - const mockRedisClient = mock(); + const mockRedisClient = mock(); return { setupRedisClient: jest.fn().mockReturnValue(mockRedisClient), }; diff --git a/packages/nodes-base/nodes/Redis/types.ts b/packages/nodes-base/nodes/Redis/types.ts new file mode 100644 index 0000000000000..63e873acce6b2 --- /dev/null +++ b/packages/nodes-base/nodes/Redis/types.ts @@ -0,0 +1,12 @@ +import type { createClient } from 'redis'; + +export type RedisClient = ReturnType; + +export type RedisCredential = { + host: string; + port: number; + ssl?: boolean; + database: number; + user?: string; + password?: string; +}; diff --git a/packages/nodes-base/nodes/Redis/utils.ts b/packages/nodes-base/nodes/Redis/utils.ts index effdd99778600..4364bab892438 100644 --- a/packages/nodes-base/nodes/Redis/utils.ts +++ b/packages/nodes-base/nodes/Redis/utils.ts @@ -1,5 +1,4 @@ import type { - ICredentialDataDecryptedObject, ICredentialTestFunctions, ICredentialsDecrypted, IDataObject, @@ -7,29 +6,28 @@ import type { INodeCredentialTestResult, } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; +import { createClient } from 'redis'; -import { type RedisClientOptions, createClient } from 'redis'; -export type RedisClientType = ReturnType; +import type { RedisCredential, RedisClient } from './types'; -export function setupRedisClient(credentials: ICredentialDataDecryptedObject): RedisClientType { - const redisOptions: RedisClientOptions = { +export function setupRedisClient(credentials: RedisCredential): RedisClient { + return createClient({ socket: { - host: credentials.host as string, - port: credentials.port as number, + host: credentials.host, + port: credentials.port, tls: credentials.ssl === true, }, - database: credentials.database as number, - password: (credentials.password as string) || undefined, - }; - - return createClient(redisOptions); + database: credentials.database, + username: credentials.user || undefined, + password: credentials.password || undefined, + }); } export async function redisConnectionTest( this: ICredentialTestFunctions, credential: ICredentialsDecrypted, ): Promise { - const credentials = credential.data as ICredentialDataDecryptedObject; + const credentials = credential.data as RedisCredential; try { const client = setupRedisClient(credentials); @@ -88,7 +86,7 @@ export function convertInfoToObject(stringData: string): IDataObject { return returnData; } -export async function getValue(client: RedisClientType, keyName: string, type?: string) { +export async function getValue(client: RedisClient, keyName: string, type?: string) { if (type === undefined || type === 'automatic') { // Request the type first type = await client.type(keyName); @@ -107,7 +105,7 @@ export async function getValue(client: RedisClientType, keyName: string, type?: export async function setValue( this: IExecuteFunctions, - client: RedisClientType, + client: RedisClient, keyName: string, value: string | number | object | string[] | number[], expire: boolean, diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 4482d3fe55871..d52f56ed67ad6 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "1.72.0", + "version": "1.73.0", "description": "Base nodes of n8n", "main": "index.js", "scripts": { @@ -318,6 +318,7 @@ "dist/credentials/Sms77Api.credentials.js", "dist/credentials/Smtp.credentials.js", "dist/credentials/Snowflake.credentials.js", + "dist/credentials/SolarWindsIpamApi.credentials.js", "dist/credentials/SolarWindsObservabilityApi.credentials.js", "dist/credentials/SplunkApi.credentials.js", "dist/credentials/SpontitApi.credentials.js", diff --git a/packages/nodes-base/utils/workflowInputsResourceMapping/.readme b/packages/nodes-base/utils/workflowInputsResourceMapping/.readme index d92560581d2fe..e5556cc0cccaf 100644 --- a/packages/nodes-base/utils/workflowInputsResourceMapping/.readme +++ b/packages/nodes-base/utils/workflowInputsResourceMapping/.readme @@ -2,4 +2,4 @@ These files contain reusable logic for workflow inputs mapping used in these nod - n8n-nodes-base.executeWorkflow - n8n-nodes-base.executeWorkflowTrigger - - @n8n/n8n-nodes-langchain.toolWorkflow" + - @n8n/n8n-nodes-langchain.toolWorkflow diff --git a/packages/nodes-base/utils/workflowInputsResourceMapping/GenericFunctions.ts b/packages/nodes-base/utils/workflowInputsResourceMapping/GenericFunctions.ts index 88aa5e971cf87..8708b8e18aa8a 100644 --- a/packages/nodes-base/utils/workflowInputsResourceMapping/GenericFunctions.ts +++ b/packages/nodes-base/utils/workflowInputsResourceMapping/GenericFunctions.ts @@ -1,14 +1,20 @@ import { json as generateSchemaFromExample, type SchemaObject } from 'generate-schema'; import type { JSONSchema7 } from 'json-schema'; -import type { - FieldValueOption, - FieldType, - IWorkflowNodeContext, - INodeExecutionData, - IExecuteFunctions, - ISupplyDataFunctions, +import _ from 'lodash'; +import { + type FieldValueOption, + type FieldType, + type IWorkflowNodeContext, + jsonParse, + NodeOperationError, + type INodeExecutionData, + type IDataObject, + type ResourceMapperField, + type ILocalLoadOptionsFunctions, + type ResourceMapperFields, + EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE, + type ISupplyDataFunctions, } from 'n8n-workflow'; -import { jsonParse, NodeOperationError, validateFieldType } from 'n8n-workflow'; import { JSON_EXAMPLE, @@ -16,8 +22,6 @@ import { WORKFLOW_INPUTS, VALUES, TYPE_OPTIONS, - INPUT_OPTIONS, - FALLBACK_DEFAULT_VALUE, PASSTHROUGH, } from './constants'; @@ -92,84 +96,74 @@ export function getFieldEntries(context: IWorkflowNodeContext): FieldValueOption throw new NodeOperationError(context.getNode(), result); } -export function getWorkflowInputData( - this: IExecuteFunctions | ISupplyDataFunctions, - inputData: INodeExecutionData[], - newParams: FieldValueOption[], -): INodeExecutionData[] { - const items: INodeExecutionData[] = []; +export function getWorkflowInputValues(this: ISupplyDataFunctions): INodeExecutionData[] { + const inputData = this.getInputData(); - for (const [itemIndex, item] of inputData.entries()) { - const attemptToConvertTypes = this.getNodeParameter( - `${INPUT_OPTIONS}.attemptToConvertTypes`, - itemIndex, - false, - ); - const ignoreTypeErrors = this.getNodeParameter( - `${INPUT_OPTIONS}.ignoreTypeErrors`, + return inputData.map((item, itemIndex) => { + const itemFieldValues = this.getNodeParameter( + 'workflowInputs.value', itemIndex, - false, - ); - - // Fields listed here will explicitly overwrite original fields - const newItem: INodeExecutionData = { - json: {}, + {}, + ) as IDataObject; + + return { + json: { + ...item.json, + ...itemFieldValues, + }, index: itemIndex, - // TODO: Ensure we handle sub-execution jumps correctly. - // metadata: { - // subExecution: { - // executionId: 'uhh', - // workflowId: 'maybe?', - // }, - // }, - pairedItem: { item: itemIndex }, + pairedItem: { + item: itemIndex, + }, }; - try { - for (const { name, type } of newParams) { - if (!item.json.hasOwnProperty(name)) { - newItem.json[name] = FALLBACK_DEFAULT_VALUE; - continue; - } - - const result = - type === 'any' - ? ({ valid: true, newValue: item.json[name] } as const) - : validateFieldType(name, item.json[name], type, { - strict: !attemptToConvertTypes, - parseStrings: true, // Default behavior is to accept anything as a string, this is a good opportunity for a stricter boundary - }); - - if (!result.valid) { - if (ignoreTypeErrors) { - newItem.json[name] = item.json[name]; - continue; - } - - throw new NodeOperationError(this.getNode(), result.errorMessage, { - itemIndex, - }); - } else { - // If the value is `null` or `undefined`, then `newValue` is not in the returned object - if (result.hasOwnProperty('newValue')) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - newItem.json[name] = result.newValue; - } else { - newItem.json[name] = item.json[name]; - } - } - } + }); +} - items.push(newItem); - } catch (error) { - if (this.continueOnFail()) { - /** todo error case? */ - } else { - throw new NodeOperationError(this.getNode(), error, { - itemIndex, - }); - } - } +export function getCurrentWorkflowInputData(this: ISupplyDataFunctions) { + const inputData: INodeExecutionData[] = getWorkflowInputValues.call(this); + + const schema = this.getNodeParameter('workflowInputs.schema', 0, []) as ResourceMapperField[]; + + if (schema.length === 0) { + return inputData; + } else { + const removedKeys = new Set(schema.filter((x) => x.removed).map((x) => x.displayName)); + + const filteredInputData: INodeExecutionData[] = inputData.map((item, index) => ({ + index, + pairedItem: { item: index }, + json: _.pickBy(item.json, (_v, key) => !removedKeys.has(key)), + })); + + return filteredInputData; } +} - return items; +export async function loadWorkflowInputMappings( + this: ILocalLoadOptionsFunctions, +): Promise { + const nodeLoadContext = await this.getWorkflowNodeContext(EXECUTE_WORKFLOW_TRIGGER_NODE_TYPE); + let fields: ResourceMapperField[] = []; + + if (nodeLoadContext) { + const fieldValues = getFieldEntries(nodeLoadContext); + + fields = fieldValues.map((currentWorkflowInput) => { + const field: ResourceMapperField = { + id: currentWorkflowInput.name, + displayName: currentWorkflowInput.name, + required: false, + defaultMatch: false, + display: true, + canBeUsedToMatch: true, + }; + + if (currentWorkflowInput.type !== 'any') { + field.type = currentWorkflowInput.type; + } + + return field; + }); + } + return { fields }; } diff --git a/packages/nodes-base/utils/workflowInputsResourceMapping/constants.ts b/packages/nodes-base/utils/workflowInputsResourceMapping/constants.ts index 69bb41af7397f..409d8d703ecab 100644 --- a/packages/nodes-base/utils/workflowInputsResourceMapping/constants.ts +++ b/packages/nodes-base/utils/workflowInputsResourceMapping/constants.ts @@ -2,7 +2,6 @@ import type { FieldType } from 'n8n-workflow'; export const INPUT_SOURCE = 'inputSource'; export const WORKFLOW_INPUTS = 'workflowInputs'; -export const INPUT_OPTIONS = 'inputOptions'; export const VALUES = 'values'; export const JSON_EXAMPLE = 'jsonExample'; export const PASSTHROUGH = 'passthrough'; diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 9254422b9069f..cabbeaaaaa618 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "1.71.0", + "version": "1.72.0", "description": "Workflow base code of n8n", "main": "dist/index.js", "module": "src/index.ts", diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 8689999a6b2a7..4fe84136c303a 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -1328,6 +1328,7 @@ export interface ResourceMapperTypeOptionsBase { description?: string; hint?: string; }; + showTypeConversionOptions?: boolean; } // Enforce at least one of resourceMapperMethod or localResourceMapperMethod @@ -2665,6 +2666,9 @@ export type ResourceMapperValue = { value: { [key: string]: string | number | boolean | null } | null; matchingColumns: string[]; schema: ResourceMapperField[]; + ignoreTypeMismatchErrors: boolean; + attemptToConvertTypes: boolean; + convertFieldsToString: boolean; }; export type FilterOperatorType = diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba2200020b469..955d19b18e3ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -283,7 +283,7 @@ importers: version: 4.0.7 axios: specifier: 'catalog:' - version: 1.7.4(debug@4.3.7) + version: 1.7.4 dotenv: specifier: 8.6.0 version: 8.6.0 @@ -354,7 +354,7 @@ importers: dependencies: axios: specifier: 'catalog:' - version: 1.7.4(debug@4.3.7) + version: 1.7.4 packages/@n8n/codemirror-lang: dependencies: @@ -428,7 +428,7 @@ importers: version: 3.666.0(@aws-sdk/client-sts@3.666.0) '@getzep/zep-cloud': specifier: 1.0.12 - version: 1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i)) + version: 1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(e4rnrwhosnp2xiru36mqgdy2bu)) '@getzep/zep-js': specifier: 0.9.0 version: 0.9.0 @@ -455,7 +455,7 @@ importers: version: 0.3.1(@aws-sdk/client-sso-oidc@3.666.0(@aws-sdk/client-sts@3.666.0))(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) '@langchain/community': specifier: 0.3.15 - version: 0.3.15(v4qhcw25bevfr6xzz4fnsvjiqe) + version: 0.3.15(vc5hvyy27o4cmm4jplsptc2fqm) '@langchain/core': specifier: 'catalog:' version: 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) @@ -542,7 +542,7 @@ importers: version: 23.0.1 langchain: specifier: 0.3.6 - version: 0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i) + version: 0.3.6(e4rnrwhosnp2xiru36mqgdy2bu) lodash: specifier: 'catalog:' version: 4.17.21 @@ -801,7 +801,7 @@ importers: version: 1.11.0 axios: specifier: 'catalog:' - version: 1.7.4(debug@4.3.7) + version: 1.7.4 bcryptjs: specifier: 2.4.3 version: 2.4.3 @@ -1120,7 +1120,7 @@ importers: dependencies: '@langchain/core': specifier: 'catalog:' - version: 0.3.19(openai@4.73.1(zod@3.23.8)) + version: 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) '@n8n/client-oauth2': specifier: workspace:* version: link:../@n8n/client-oauth2 @@ -1135,7 +1135,7 @@ importers: version: 1.11.0 axios: specifier: 'catalog:' - version: 1.7.4(debug@4.3.7) + version: 1.7.4 chardet: specifier: 2.0.0 version: 2.0.0 @@ -1431,7 +1431,7 @@ importers: version: 10.11.0(vue@3.5.13(typescript@5.7.2)) axios: specifier: 'catalog:' - version: 1.7.4(debug@4.3.7) + version: 1.7.4 bowser: specifier: 2.11.0 version: 2.11.0 @@ -1932,7 +1932,7 @@ importers: version: 0.15.2 axios: specifier: 'catalog:' - version: 1.7.4(debug@4.3.7) + version: 1.7.4 callsites: specifier: 3.1.0 version: 3.1.0 @@ -1978,7 +1978,7 @@ importers: devDependencies: '@langchain/core': specifier: 'catalog:' - version: 0.3.19(openai@4.73.1) + version: 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) '@types/deep-equal': specifier: ^1.0.1 version: 1.0.1 @@ -15666,7 +15666,7 @@ snapshots: '@gar/promisify@1.1.3': optional: true - '@getzep/zep-cloud@1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i))': + '@getzep/zep-cloud@1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(e4rnrwhosnp2xiru36mqgdy2bu))': dependencies: form-data: 4.0.0 node-fetch: 2.7.0(encoding@0.1.13) @@ -15675,7 +15675,7 @@ snapshots: zod: 3.23.8 optionalDependencies: '@langchain/core': 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) - langchain: 0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i) + langchain: 0.3.6(e4rnrwhosnp2xiru36mqgdy2bu) transitivePeerDependencies: - encoding @@ -16139,7 +16139,7 @@ snapshots: - aws-crt - encoding - '@langchain/community@0.3.15(v4qhcw25bevfr6xzz4fnsvjiqe)': + '@langchain/community@0.3.15(vc5hvyy27o4cmm4jplsptc2fqm)': dependencies: '@ibm-cloud/watsonx-ai': 1.1.2 '@langchain/core': 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) @@ -16149,7 +16149,7 @@ snapshots: flat: 5.0.2 ibm-cloud-sdk-core: 5.1.0 js-yaml: 4.1.0 - langchain: 0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i) + langchain: 0.3.6(e4rnrwhosnp2xiru36mqgdy2bu) langsmith: 0.2.3(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) uuid: 10.0.0 zod: 3.23.8 @@ -16162,7 +16162,7 @@ snapshots: '@aws-sdk/client-s3': 3.666.0 '@aws-sdk/credential-provider-node': 3.666.0(@aws-sdk/client-sso-oidc@3.666.0(@aws-sdk/client-sts@3.666.0))(@aws-sdk/client-sts@3.666.0) '@azure/storage-blob': 12.18.0(encoding@0.1.13) - '@getzep/zep-cloud': 1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i)) + '@getzep/zep-cloud': 1.0.12(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)(langchain@0.3.6(e4rnrwhosnp2xiru36mqgdy2bu)) '@getzep/zep-js': 0.9.0 '@google-ai/generativelanguage': 2.6.0(encoding@0.1.13) '@google-cloud/storage': 7.12.1(encoding@0.1.13) @@ -16226,38 +16226,6 @@ snapshots: transitivePeerDependencies: - openai - '@langchain/core@0.3.19(openai@4.73.1(zod@3.23.8))': - dependencies: - ansi-styles: 5.2.0 - camelcase: 6.3.0 - decamelize: 1.2.0 - js-tiktoken: 1.0.12 - langsmith: 0.2.3(openai@4.73.1(zod@3.23.8)) - mustache: 4.2.0 - p-queue: 6.6.2 - p-retry: 4.6.2 - uuid: 10.0.0 - zod: 3.23.8 - zod-to-json-schema: 3.23.3(zod@3.23.8) - transitivePeerDependencies: - - openai - - '@langchain/core@0.3.19(openai@4.73.1)': - dependencies: - ansi-styles: 5.2.0 - camelcase: 6.3.0 - decamelize: 1.2.0 - js-tiktoken: 1.0.12 - langsmith: 0.2.3(openai@4.73.1) - mustache: 4.2.0 - p-queue: 6.6.2 - p-retry: 4.6.2 - uuid: 10.0.0 - zod: 3.23.8 - zod-to-json-schema: 3.23.3(zod@3.23.8) - transitivePeerDependencies: - - openai - '@langchain/google-common@0.1.3(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(zod@3.23.8)': dependencies: '@langchain/core': 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) @@ -17173,7 +17141,7 @@ snapshots: '@rudderstack/rudder-sdk-node@2.0.9(tslib@2.6.2)': dependencies: - axios: 1.7.4(debug@4.3.7) + axios: 1.7.4 axios-retry: 3.7.0 component-type: 1.2.1 join-component: 1.1.0 @@ -19470,7 +19438,7 @@ snapshots: '@babel/runtime': 7.24.7 is-retry-allowed: 2.2.0 - axios@1.7.4(debug@4.3.7): + axios@1.7.4: dependencies: follow-redirects: 1.15.6(debug@4.3.6) form-data: 4.0.0 @@ -19478,9 +19446,9 @@ snapshots: transitivePeerDependencies: - debug - axios@1.7.7: + axios@1.7.4(debug@4.3.7): dependencies: - follow-redirects: 1.15.6(debug@4.3.6) + follow-redirects: 1.15.6(debug@4.3.7) form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -22347,7 +22315,7 @@ snapshots: isstream: 0.1.2 jsonwebtoken: 9.0.2 mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.7.4(debug@4.3.7)) + retry-axios: 2.6.0(axios@1.7.4) tough-cookie: 4.1.3 transitivePeerDependencies: - supports-color @@ -23346,7 +23314,7 @@ snapshots: kuler@2.0.0: {} - langchain@0.3.6(4axcxpjbcq5bce7ff6ajxrpp4i): + langchain@0.3.6(e4rnrwhosnp2xiru36mqgdy2bu): dependencies: '@langchain/core': 0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)) '@langchain/openai': 0.3.14(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) @@ -23370,7 +23338,7 @@ snapshots: '@langchain/groq': 0.1.2(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13) '@langchain/mistralai': 0.2.0(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8))) '@langchain/ollama': 0.1.2(@langchain/core@0.3.19(openai@4.73.1(encoding@0.1.13)(zod@3.23.8))) - axios: 1.7.4(debug@4.3.7) + axios: 1.7.4 cheerio: 1.0.0 handlebars: 4.7.8 transitivePeerDependencies: @@ -23389,28 +23357,6 @@ snapshots: optionalDependencies: openai: 4.73.1(encoding@0.1.13)(zod@3.23.8) - langsmith@0.2.3(openai@4.73.1(zod@3.23.8)): - dependencies: - '@types/uuid': 10.0.0 - commander: 10.0.1 - p-queue: 6.6.2 - p-retry: 4.6.2 - semver: 7.6.0 - uuid: 10.0.0 - optionalDependencies: - openai: 4.73.1(zod@3.23.8) - - langsmith@0.2.3(openai@4.73.1): - dependencies: - '@types/uuid': 10.0.0 - commander: 10.0.1 - p-queue: 6.6.2 - p-retry: 4.6.2 - semver: 7.6.0 - uuid: 10.0.0 - optionalDependencies: - openai: 4.73.1(zod@3.23.8) - lazy-ass@1.6.0: {} ldapts@4.2.6: @@ -24743,22 +24689,6 @@ snapshots: - encoding - supports-color - openai@4.73.1(zod@3.23.8): - dependencies: - '@types/node': 18.16.16 - '@types/node-fetch': 2.6.4 - abort-controller: 3.0.0 - agentkeepalive: 4.2.1 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0(encoding@0.1.13) - optionalDependencies: - zod: 3.23.8 - transitivePeerDependencies: - - encoding - - supports-color - optional: true - openapi-sampler@1.5.1: dependencies: '@types/json-schema': 7.0.15 @@ -25754,9 +25684,9 @@ snapshots: ret@0.1.15: {} - retry-axios@2.6.0(axios@1.7.4(debug@4.3.7)): + retry-axios@2.6.0(axios@1.7.4): dependencies: - axios: 1.7.4(debug@4.3.7) + axios: 1.7.4 retry-request@7.0.2(encoding@0.1.13): dependencies: