mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
Merge remote-tracking branch 'upstream/master' into node-360-quick-overhaul-of-date-and-time-node
This commit is contained in:
commit
e81868f1bb
2
.github/workflows/docker-images.yml
vendored
2
.github/workflows/docker-images.yml
vendored
|
@ -49,6 +49,4 @@ jobs:
|
|||
push: true
|
||||
tags: |
|
||||
${{ secrets.DOCKER_USERNAME }}/n8n:${{ steps.vars.outputs.tag }}${{ matrix.docker-context }}
|
||||
${{ secrets.DOCKER_USERNAME }}/n8n:latest${{ matrix.docker-context }}
|
||||
ghcr.io/${{ github.repository_owner }}/n8n:${{ steps.vars.outputs.tag }}${{ matrix.docker-context }}
|
||||
ghcr.io/${{ github.repository_owner }}/n8n:latest${{ matrix.docker-context }}
|
||||
|
|
23
.github/workflows/e2e-tests-pr.yml
vendored
23
.github/workflows/e2e-tests-pr.yml
vendored
|
@ -32,3 +32,26 @@ jobs:
|
|||
run-env: base:16.18.1
|
||||
secrets:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
|
||||
post-e2e-tests:
|
||||
runs-on: ubuntu-latest
|
||||
name: E2E [Electron/Node 16] - Checks
|
||||
needs: [run-e2e-tests]
|
||||
steps:
|
||||
- name: E2E success comment
|
||||
if: success()
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
:white_check_mark: All Cypress E2E specs passed
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: E2E fail comment
|
||||
if: failure()
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
:warning: Some Cypress E2E specs are failing, please fix them before merging
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
6
.github/workflows/release-publish.yml
vendored
6
.github/workflows/release-publish.yml
vendored
|
@ -31,14 +31,16 @@ jobs:
|
|||
cache: 'pnpm'
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Set release version in env
|
||||
run: echo "RELEASE=$(node -e 'console.log(require("./package.json").version)')" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Publish to NPM
|
||||
run: |
|
||||
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
||||
pnpm publish -r --publish-branch ${{github.event.pull_request.base.ref}} --access public
|
||||
echo "RELEASE=$(node -e 'console.log(require("./package.json").version)')" >> $GITHUB_ENV
|
||||
pnpm publish -r --publish-branch ${{github.event.pull_request.base.ref}} --access public --tag rc
|
||||
|
||||
- name: Create Release
|
||||
uses: ncipollo/release-action@v1
|
||||
|
|
93
CHANGELOG.md
93
CHANGELOG.md
|
@ -1,3 +1,96 @@
|
|||
# [0.223.0](https://github.com/n8n-io/n8n/compare/n8n@0.222.1...n8n@0.223.0) (2023-04-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add droppable state for booleans when mapping ([#5838](https://github.com/n8n-io/n8n/issues/5838)) ([e3884ce](https://github.com/n8n-io/n8n/commit/e3884ce378e488905735fbfdb986aa26f1cf952b))
|
||||
* **AWS SNS Node:** Fix an issue with messages failing to send if they contain certain characters ([#5807](https://github.com/n8n-io/n8n/issues/5807)) ([32c4eef](https://github.com/n8n-io/n8n/commit/32c4eef574a14ed599554382496a99a8240be74b))
|
||||
* **Compare Datasets Node:** Fuzzy compare not comparing keys missing in one of inputs ([d1945d9](https://github.com/n8n-io/n8n/commit/d1945d9b72fc11e7201e22a7ae0399acf2ffd5f1))
|
||||
* **Compare Datasets Node:** Support for dot notation in skip fields ([83e25c0](https://github.com/n8n-io/n8n/commit/83e25c066a845fc95c3474ae93f36993ca7ce699))
|
||||
* **core:** `augmentObject` should clone Buffer/Uint8Array instead of wrapping them in a proxy ([#5902](https://github.com/n8n-io/n8n/issues/5902)) ([a721734](https://github.com/n8n-io/n8n/commit/a72173414d9d31ab1824f87713709818955b8956))
|
||||
* **core:** `augmentObject` should use existing property descriptors whenever possible ([#5872](https://github.com/n8n-io/n8n/issues/5872)) ([6a1b7c3](https://github.com/n8n-io/n8n/commit/6a1b7c306bc9b7c469c5299af6beaf5af568b6b6))
|
||||
* **core:** Deactivate active workflows during import ([#5840](https://github.com/n8n-io/n8n/issues/5840)) ([fa5bc81](https://github.com/n8n-io/n8n/commit/fa5bc814b04273cff817d4a94b1d1ec6685807e0))
|
||||
* **core:** Do not mark duplicates as circular references in `jsonStringify` ([#5789](https://github.com/n8n-io/n8n/issues/5789)) ([18efaf3](https://github.com/n8n-io/n8n/commit/18efaf397a6bab8bd5dba881bbdfeceef8dbafb0))
|
||||
* **core:** Do not user `util.types.isProxy` for tracking of augmented objects ([#5836](https://github.com/n8n-io/n8n/issues/5836)) ([aacbb54](https://github.com/n8n-io/n8n/commit/aacbb54bef0743a1c5c5e2467dd7e00e50e325de))
|
||||
* **core:** Fix curl import error when no data ([085660d](https://github.com/n8n-io/n8n/commit/085660d7d7faf475b695724cabb6387c46adcc5f))
|
||||
* **core:** Fix the issue of nodes not loading when run via npx ([#5888](https://github.com/n8n-io/n8n/issues/5888)) ([e47190b](https://github.com/n8n-io/n8n/commit/e47190b5607dfd1284a1d2c3b8f678e8032627de))
|
||||
* **core:** Handle Date and RegExp correctly in jsonStringify ([#5812](https://github.com/n8n-io/n8n/issues/5812)) ([4f91525](https://github.com/n8n-io/n8n/commit/4f91525022716e7c1745185fae6fc2582a4252fb))
|
||||
* **core:** Handle Date and RegExp objects in AugmentObject ([#5809](https://github.com/n8n-io/n8n/issues/5809)) ([6c35ffa](https://github.com/n8n-io/n8n/commit/6c35ffa82c45434dadee0354b75a901d3f3d6e98))
|
||||
* **core:** Improve axios error handling in nodes ([#5891](https://github.com/n8n-io/n8n/issues/5891)) ([a260c05](https://github.com/n8n-io/n8n/commit/a260c05fa859c0bdd90f9abdecac59fd35978c55))
|
||||
* **core:** Password reset should pass in the correct values to external hooks ([#5842](https://github.com/n8n-io/n8n/issues/5842)) ([5bcab8f](https://github.com/n8n-io/n8n/commit/5bcab8fcbea546cd57ef728131f9e16cc57e675d))
|
||||
* **core:** Prevent augmentObject from creating infinitely deep proxies ([#5893](https://github.com/n8n-io/n8n/issues/5893)) ([31cd04c](https://github.com/n8n-io/n8n/commit/31cd04c4769b92c7f19ae8babf5df2deddef1fb3)), closes [#5848](https://github.com/n8n-io/n8n/issues/5848)
|
||||
* **core:** Service account private key as a password field ([739b9b0](https://github.com/n8n-io/n8n/commit/739b9b07f0e364f98d3c2d0ce8911cd4f53e8455))
|
||||
* **core:** Update lock file ([#5801](https://github.com/n8n-io/n8n/issues/5801)) ([06d7a46](https://github.com/n8n-io/n8n/commit/06d7a46bdcd2173835ede762aff3c37b21f0a530))
|
||||
* **core:** Use table-prefixes in queries in import commands ([#5887](https://github.com/n8n-io/n8n/issues/5887)) ([ddbfcc7](https://github.com/n8n-io/n8n/commit/ddbfcc7d93cc3645fc3bb0f0de059ac76adaa475))
|
||||
* **core:** Waiting workflows not stopping ([#5811](https://github.com/n8n-io/n8n/issues/5811)) ([744c3fd](https://github.com/n8n-io/n8n/commit/744c3fd21130b6ee3c722df3fab096b169fd0ff8))
|
||||
* **Date & Time Node:** Add info box at top of date and time explaining expressions ([b7a20dd](https://github.com/n8n-io/n8n/commit/b7a20dd3a2e69a8e4e8ba76c63a6b1f4c26b6a87))
|
||||
* **Date & Time Node:** Convert luxon DateTime object to ISO ([7710652](https://github.com/n8n-io/n8n/commit/77106520c8942c746bb1ddffcddcde68a7059805))
|
||||
* **editor:** Add $if, $min, $max to root expression autocomplete ([#5858](https://github.com/n8n-io/n8n/issues/5858)) ([a13866e](https://github.com/n8n-io/n8n/commit/a13866e233430ec6aa9fcaa5f3861b3a4470b458))
|
||||
* **editor:** Curb overeager item access linting ([#5865](https://github.com/n8n-io/n8n/issues/5865)) ([3ae6933](https://github.com/n8n-io/n8n/commit/3ae69337eeb1f8a4d698f2099bb190c49cc5f8fd))
|
||||
* **editor:** Disable Grammarly in expression editors ([#5826](https://github.com/n8n-io/n8n/issues/5826)) ([ddc8f30](https://github.com/n8n-io/n8n/commit/ddc8f30e6d410f7453395f17754b8ee9a546d9b7))
|
||||
* **editor:** Disable password reset on desktop with no user management ([#5853](https://github.com/n8n-io/n8n/issues/5853)) ([96533a9](https://github.com/n8n-io/n8n/commit/96533a995c1e7ac653d3f135f954619b098bb609))
|
||||
* **editor:** Fix connection lost hover text not showing ([#5828](https://github.com/n8n-io/n8n/issues/5828)) ([b69129b](https://github.com/n8n-io/n8n/commit/b69129bd78689bd56c3a9b07c2e30f58735347d1))
|
||||
* **editor:** Fix focused state in Code node editor ([#5869](https://github.com/n8n-io/n8n/issues/5869)) ([48446f5](https://github.com/n8n-io/n8n/commit/48446f5d674c335716c86e30079eb35c75e32b66))
|
||||
* **editor:** Fix issue preventing execution preview loading when in an iframe ([#5817](https://github.com/n8n-io/n8n/issues/5817)) ([d86e693](https://github.com/n8n-io/n8n/commit/d86e693019db1fa034d43f8e7e18df09f785b2e1))
|
||||
* **editor:** Fix loading executions in long execution list ([#5843](https://github.com/n8n-io/n8n/issues/5843)) ([5c9343c](https://github.com/n8n-io/n8n/commit/5c9343c7c0febdeb3dfa449d6b18d744d909724a))
|
||||
* **editor:** Fix mapping with special characters ([#5837](https://github.com/n8n-io/n8n/issues/5837)) ([f8f584c](https://github.com/n8n-io/n8n/commit/f8f584c136da8ad8b17f82f6f4e95f0d69014b40))
|
||||
* **editor:** Prevent error from showing-up when duplicating unsaved workflow ([#5833](https://github.com/n8n-io/n8n/issues/5833)) ([0b0024d](https://github.com/n8n-io/n8n/commit/0b0024d7222ac1f6f7872b26eceefab93a17ef22))
|
||||
* **editor:** Prevent NDV schema view pagination ([#5844](https://github.com/n8n-io/n8n/issues/5844)) ([1eba478](https://github.com/n8n-io/n8n/commit/1eba4788f26d0f5472fa4156b317d8b14d19b927))
|
||||
* **editor:** Show correct status on canceled executions ([#5813](https://github.com/n8n-io/n8n/issues/5813)) ([d0788ee](https://github.com/n8n-io/n8n/commit/d0788ee8e150167a65561552494046d8e506f93c))
|
||||
* **editor:** Support backspacing with modifier key ([#5845](https://github.com/n8n-io/n8n/issues/5845)) ([11692c5](https://github.com/n8n-io/n8n/commit/11692c55f381f17a7a137262d85dfd6c7fda7ad5))
|
||||
* **Gmail Node:** Gmail luxon object support, fix for timestamp ([2b9ca0d](https://github.com/n8n-io/n8n/commit/2b9ca0d240b403a5f12b115956bbc11672f3a04a))
|
||||
* **Google Sheets Node:** Fix insertOrUpdate cell update with object ([0625e2e](https://github.com/n8n-io/n8n/commit/0625e2e6bc67092848f719f8fede87af0f3df891))
|
||||
* **HTML Extract Node:** Support for dot notation in JSON property ([0da3b96](https://github.com/n8n-io/n8n/commit/0da3b96cfc943bf8036a48df946873fb32f3f5d9))
|
||||
* **HTTP Request Node:** Detect mime-type from streaming responses ([#5896](https://github.com/n8n-io/n8n/issues/5896)) ([69efde7](https://github.com/n8n-io/n8n/commit/69efde7a094b0bf3e3ca04b456ba3a792838a0b9))
|
||||
* **HTTP Request Node:** Fix AWS credentials to stop removing url params for STS ([#5790](https://github.com/n8n-io/n8n/issues/5790)) ([a1306c6](https://github.com/n8n-io/n8n/commit/a1306c690398828ed9acb72af0161c4ff827b217))
|
||||
* **HTTP Request Node:** Refresh token properly on never fail option ([#5861](https://github.com/n8n-io/n8n/issues/5861)) ([33c67f4](https://github.com/n8n-io/n8n/commit/33c67f45ba959b90c8bebbe0b27b2a7c4152a116))
|
||||
* **HTTP Request Node:** Support for dot notation in JSON body ([b29cf9a](https://github.com/n8n-io/n8n/commit/b29cf9a2492a444cb1dd72e74c9ed1d8722bbc5a))
|
||||
* **HubSpot Trigger Node:** Developer API key is required for webhooks ([e11a30a](https://github.com/n8n-io/n8n/commit/e11a30a640700d2bc53919422cb8ddbf66aafddd))
|
||||
* **LinkedIn Node:** Update the version of the API ([#5720](https://github.com/n8n-io/n8n/issues/5720)) ([18d2e7c](https://github.com/n8n-io/n8n/commit/18d2e7cd57745f0969b0df383572b3874fe65f2c))
|
||||
* **Redis Node:** Fix issue with hash set not working as expected ([#5832](https://github.com/n8n-io/n8n/issues/5832)) ([db25441](https://github.com/n8n-io/n8n/commit/db2544146f646ec9a2c38787bc94eafc1edb1228))
|
||||
* **Set Node:** Convert string to number ([b408550](https://github.com/n8n-io/n8n/commit/b408550e9f486351198f0ce5c10895c42df45835))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Convert eventBus controller to decorator style and improve permissions ([#5779](https://github.com/n8n-io/n8n/issues/5779)) ([f15f4bd](https://github.com/n8n-io/n8n/commit/f15f4bdcf204fa43a652022babf03e577602b2b5))
|
||||
* **core:** Prevent non owners password reset when saml is enabled ([#5788](https://github.com/n8n-io/n8n/issues/5788)) ([2216455](https://github.com/n8n-io/n8n/commit/221645576087e4cd828b34ea33e874e1bff5f34a))
|
||||
* **core:** Read ephemeral license from environment and clean up ee flags ([#5808](https://github.com/n8n-io/n8n/issues/5808)) ([83aef17](https://github.com/n8n-io/n8n/commit/83aef1712070c29fea5d0522c95b1208af4cd2e4))
|
||||
* **editor:** Allow `tab` to accept completion ([#5855](https://github.com/n8n-io/n8n/issues/5855)) ([1b8c35a](https://github.com/n8n-io/n8n/commit/1b8c35ab87ce7ea24d00d13faddba9daf9f2ab39))
|
||||
* **editor:** Enable saving workflow when node details view is open ([#5856](https://github.com/n8n-io/n8n/issues/5856)) ([0a59002](https://github.com/n8n-io/n8n/commit/0a59002ef878ff8836d3ca63956f7a444d329d0b))
|
||||
* **editor:** SSO onboarding ([#5756](https://github.com/n8n-io/n8n/issues/5756)) ([04f8600](https://github.com/n8n-io/n8n/commit/04f8600bbd220204b5e5a90f22c3dc9c137afb54))
|
||||
* **editor:** SSO setup ([#5736](https://github.com/n8n-io/n8n/issues/5736)) ([f4e5949](https://github.com/n8n-io/n8n/commit/f4e59499fc0168295c5df20b1525c7ecea4ea15b)), closes [#5899](https://github.com/n8n-io/n8n/issues/5899)
|
||||
* **Filter Node:** Show discarded items ([f7f9d91](https://github.com/n8n-io/n8n/commit/f7f9d915b174d5c17efa918032741d4fa6da85e9))
|
||||
* **HTTP Request Node:** Follow redirects by default ([#5895](https://github.com/n8n-io/n8n/issues/5895)) ([f7e610b](https://github.com/n8n-io/n8n/commit/f7e610b15c4699880edffd7f10f223e820052784))
|
||||
* **Postgres Node:** Overhaul node ([07dc0e4](https://github.com/n8n-io/n8n/commit/07dc0e4b4075f1fac98d5685a99a38187bca741b))
|
||||
* **ServiceNow Node:** Add support for work notes when updating an incident ([#5791](https://github.com/n8n-io/n8n/issues/5791)) ([1409f5d](https://github.com/n8n-io/n8n/commit/1409f5d65262b9783e690408f5dabd074c709f22))
|
||||
* **SSH Node:** Hide the private key within the ssh credential ([#5871](https://github.com/n8n-io/n8n/issues/5871)) ([d877361](https://github.com/n8n-io/n8n/commit/d87736103d09042d2f74e74b57be429f2ca3550d))
|
||||
|
||||
|
||||
|
||||
## [0.222.1](https://github.com/n8n-io/n8n/compare/n8n@0.222.0...n8n@0.222.1) (2023-04-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **AWS SNS Node:** Fix an issue with messages failing to send if they contain certain characters ([#5807](https://github.com/n8n-io/n8n/issues/5807)) ([f0954b9](https://github.com/n8n-io/n8n/commit/f0954b94e164f0ff3e809849731137cf479670a4))
|
||||
* **core:** `augmentObject` should clone Buffer/Uint8Array instead of wrapping them in a proxy ([#5902](https://github.com/n8n-io/n8n/issues/5902)) ([a877b02](https://github.com/n8n-io/n8n/commit/a877b025b8c93a31822e7ccb5b935dca00595439))
|
||||
* **core:** `augmentObject` should use existing property descriptors whenever possible ([#5872](https://github.com/n8n-io/n8n/issues/5872)) ([b1ee8f4](https://github.com/n8n-io/n8n/commit/b1ee8f4d991ffc5017894f588e9bd21f652f23c6))
|
||||
* **core:** Fix the issue of nodes not loading when run via npx ([#5888](https://github.com/n8n-io/n8n/issues/5888)) ([163446c](https://github.com/n8n-io/n8n/commit/163446c674d07c060eaa0d7ec54a087a4cb671d5))
|
||||
* **core:** Improve axios error handling in nodes ([#5891](https://github.com/n8n-io/n8n/issues/5891)) ([f0a51a0](https://github.com/n8n-io/n8n/commit/f0a51a0b7671945b84e18774483dc7079f559845))
|
||||
* **core:** Password reset should pass in the correct values to external hooks ([#5842](https://github.com/n8n-io/n8n/issues/5842)) ([3bf267c](https://github.com/n8n-io/n8n/commit/3bf267c14757fa67dcfa0edf0b4db74ab1b7415c))
|
||||
* **core:** Prevent augmentObject from creating infinitely deep proxies ([#5893](https://github.com/n8n-io/n8n/issues/5893)) ([6906b00](https://github.com/n8n-io/n8n/commit/6906b00b0e734dbe59d1a3a91f07ec1007166b72)), closes [#5848](https://github.com/n8n-io/n8n/issues/5848)
|
||||
* **core:** Use table-prefixes in queries in import commands ([#5887](https://github.com/n8n-io/n8n/issues/5887)) ([de58fb9](https://github.com/n8n-io/n8n/commit/de58fb9860d37a39a1e8963e304b8fc87f234bf6))
|
||||
* **editor:** Fix focused state in Code node editor ([#5869](https://github.com/n8n-io/n8n/issues/5869)) ([3be37e2](https://github.com/n8n-io/n8n/commit/3be37e25a52983b43b4eed3847922e99f2bf07bc))
|
||||
* **editor:** Fix loading executions in long execution list ([#5843](https://github.com/n8n-io/n8n/issues/5843)) ([d5d9f58](https://github.com/n8n-io/n8n/commit/d5d9f58f1777040c63a2a0aa3c0d7bf632dc7b26))
|
||||
* **editor:** Show correct status on canceled executions ([#5813](https://github.com/n8n-io/n8n/issues/5813)) ([00181cd](https://github.com/n8n-io/n8n/commit/00181cd803f5a0cca50b5fc1ab84d0a26dd618ae))
|
||||
* **Gmail Node:** Gmail luxon object support, fix for timestamp ([695fabb](https://github.com/n8n-io/n8n/commit/695fabb28465d01caa85aefb1e873f88720ce304))
|
||||
* **HTTP Request Node:** Detect mime-type from streaming responses ([#5896](https://github.com/n8n-io/n8n/issues/5896)) ([0be1292](https://github.com/n8n-io/n8n/commit/0be129254e96c822dceddbfffaf36e0e6b2ef5e8))
|
||||
* **HubSpot Trigger Node:** Developer API key is required for webhooks ([918c79c](https://github.com/n8n-io/n8n/commit/918c79c137f781764f11ad3a33ead337efce681a))
|
||||
* **Set Node:** Convert string to number ([72eea0d](https://github.com/n8n-io/n8n/commit/72eea0dfb9e679bde95996d99684e23d081db5a7))
|
||||
|
||||
|
||||
|
||||
# [0.222.0](https://github.com/n8n-io/n8n/compare/n8n@0.221.2...n8n@0.222.0) (2023-03-30)
|
||||
|
||||
|
||||
|
|
15
package.json
15
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n",
|
||||
"version": "0.222.0",
|
||||
"version": "0.223.0",
|
||||
"private": true,
|
||||
"homepage": "https://n8n.io",
|
||||
"engines": {
|
||||
|
@ -47,7 +47,7 @@
|
|||
"jest": "^29.5.0",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"jest-mock": "^29.5.0",
|
||||
"jest-mock-extended": "^3.0.3",
|
||||
"jest-mock-extended": "^3.0.4",
|
||||
"nock": "^13.2.9",
|
||||
"node-fetch": "^2.6.7",
|
||||
"p-limit": "^3.1.0",
|
||||
|
@ -56,10 +56,10 @@
|
|||
"run-script-os": "^1.0.7",
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"supertest": "^6.3.3",
|
||||
"ts-jest": "^29.0.5",
|
||||
"ts-jest": "^29.1.0",
|
||||
"tsc-watch": "^6.0.0",
|
||||
"typescript": "*",
|
||||
"turbo": "1.8.8"
|
||||
"turbo": "1.8.8",
|
||||
"typescript": "*"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
|
@ -78,13 +78,14 @@
|
|||
"prettier": "^2.8.3",
|
||||
"tslib": "^2.5.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.0.2",
|
||||
"typescript": "^5.0.3",
|
||||
"cpy@8>globby": "^11.1.0",
|
||||
"qqjs>globby": "^11.1.0"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"element-ui@2.15.12": "patches/element-ui@2.15.12.patch",
|
||||
"typedi@0.10.0": "patches/typedi@0.10.0.patch"
|
||||
"typedi@0.10.0": "patches/typedi@0.10.0.patch",
|
||||
"@sentry/cli@2.17.0": "patches/@sentry__cli@2.17.0.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n",
|
||||
"version": "0.222.0",
|
||||
"version": "0.223.0",
|
||||
"description": "n8n Workflow Automation Tool",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
|
|
@ -24,6 +24,7 @@ import type {
|
|||
IExecutionsSummary,
|
||||
FeatureFlags,
|
||||
WorkflowSettings,
|
||||
AuthenticationMethod,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||
|
@ -552,6 +553,7 @@ export interface IUserManagementSettings {
|
|||
enabled: boolean;
|
||||
showSetupOnFirstLoad?: boolean;
|
||||
smtpSetup: boolean;
|
||||
authenticationMethod: AuthenticationMethod;
|
||||
}
|
||||
export interface IActiveDirectorySettings {
|
||||
enabled: boolean;
|
||||
|
@ -726,6 +728,7 @@ export interface IProcessMessageDataHook {
|
|||
|
||||
export interface IWorkflowExecutionDataProcess {
|
||||
destinationNode?: string;
|
||||
restartExecutionId?: string;
|
||||
executionMode: WorkflowExecuteMode;
|
||||
executionData?: IRunExecutionData;
|
||||
runData?: IRunData;
|
||||
|
|
|
@ -26,10 +26,12 @@ import { jsonParse, LoggerProxy as Logger } from 'n8n-workflow';
|
|||
import { License } from '@/License';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import {
|
||||
getCurrentAuthenticationMethod,
|
||||
isEmailCurrentAuthenticationMethod,
|
||||
isLdapCurrentAuthenticationMethod,
|
||||
setCurrentAuthenticationMethod,
|
||||
} from '@/sso/ssoHelpers';
|
||||
import { InternalServerError } from '../ResponseHelper';
|
||||
|
||||
/**
|
||||
* Check whether the LDAP feature is disabled in the instance
|
||||
|
@ -54,25 +56,21 @@ export const setLdapLoginLabel = (value: string): void => {
|
|||
/**
|
||||
* Set the LDAP login enabled to the configuration object
|
||||
*/
|
||||
export const setLdapLoginEnabled = async (value: boolean): Promise<void> => {
|
||||
if (config.get(LDAP_LOGIN_ENABLED) === value) {
|
||||
return;
|
||||
}
|
||||
// only one auth method can be active at a time, with email being the default
|
||||
if (value && isEmailCurrentAuthenticationMethod()) {
|
||||
// enable ldap login and disable email login, but only if email is the current auth method
|
||||
config.set(LDAP_LOGIN_ENABLED, true);
|
||||
await setCurrentAuthenticationMethod('ldap');
|
||||
} else if (!value && isLdapCurrentAuthenticationMethod()) {
|
||||
// disable ldap login, but only if ldap is the current auth method
|
||||
config.set(LDAP_LOGIN_ENABLED, false);
|
||||
await setCurrentAuthenticationMethod('email');
|
||||
export async function setLdapLoginEnabled(enabled: boolean): Promise<void> {
|
||||
if (isEmailCurrentAuthenticationMethod() || isLdapCurrentAuthenticationMethod()) {
|
||||
if (enabled) {
|
||||
config.set(LDAP_LOGIN_ENABLED, true);
|
||||
await setCurrentAuthenticationMethod('ldap');
|
||||
} else if (!enabled) {
|
||||
config.set(LDAP_LOGIN_ENABLED, false);
|
||||
await setCurrentAuthenticationMethod('email');
|
||||
}
|
||||
} else {
|
||||
Logger.warn(
|
||||
'Cannot switch LDAP login enabled state when an authentication method other than email is active',
|
||||
throw new InternalServerError(
|
||||
`Cannot switch LDAP login enabled state when an authentication method other than email or ldap is active (current: ${getCurrentAuthenticationMethod()})`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the LDAP login label from the configuration object
|
||||
|
@ -217,7 +215,15 @@ export const handleLdapInit = async (): Promise<void> => {
|
|||
|
||||
const ldapConfig = await getLdapConfig();
|
||||
|
||||
await setGlobalLdapConfigVariables(ldapConfig);
|
||||
try {
|
||||
await setGlobalLdapConfigVariables(ldapConfig);
|
||||
} catch (error) {
|
||||
Logger.error(
|
||||
`Cannot set LDAP login enabled state when an authentication method other than email or ldap is active (current: ${getCurrentAuthenticationMethod()})`,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
error,
|
||||
);
|
||||
}
|
||||
|
||||
// init LDAP manager with the current
|
||||
// configuration
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
import assert from 'assert';
|
||||
import { exec as callbackExec } from 'child_process';
|
||||
import { access as fsAccess } from 'fs/promises';
|
||||
import os from 'os';
|
||||
|
@ -157,6 +157,7 @@ import { getSamlLoginLabel, isSamlLoginEnabled, isSamlLicensed } from './sso/sam
|
|||
import { SamlController } from './sso/saml/routes/saml.controller.ee';
|
||||
import { SamlService } from './sso/saml/saml.service.ee';
|
||||
import { LdapManager } from './Ldap/LdapManager.ee';
|
||||
import { getCurrentAuthenticationMethod } from './sso/ssoHelpers';
|
||||
|
||||
const exec = promisify(callbackExec);
|
||||
|
||||
|
@ -269,6 +270,7 @@ class Server extends AbstractServer {
|
|||
config.getEnv('userManagement.isInstanceOwnerSetUp') === false &&
|
||||
config.getEnv('userManagement.skipInstanceOwnerSetup') === false,
|
||||
smtpSetup: isEmailSetUp(),
|
||||
authenticationMethod: getCurrentAuthenticationMethod(),
|
||||
},
|
||||
sso: {
|
||||
saml: {
|
||||
|
@ -328,6 +330,7 @@ class Server extends AbstractServer {
|
|||
// refresh user management status
|
||||
Object.assign(this.frontendSettings.userManagement, {
|
||||
enabled: isUserManagementEnabled(),
|
||||
authenticationMethod: getCurrentAuthenticationMethod(),
|
||||
showSetupOnFirstLoad:
|
||||
config.getEnv('userManagement.disabled') === false &&
|
||||
config.getEnv('userManagement.isInstanceOwnerSetUp') === false &&
|
||||
|
@ -449,6 +452,11 @@ class Server extends AbstractServer {
|
|||
...excludeEndpoints.split(':'),
|
||||
].filter((u) => !!u);
|
||||
|
||||
assert(
|
||||
!ignoredEndpoints.includes(this.restEndpoint),
|
||||
`REST endpoint cannot be set to any of these values: ${ignoredEndpoints.join()} `,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const authIgnoreRegex = new RegExp(`^\/(${ignoredEndpoints.join('|')})\/?.*$`);
|
||||
|
||||
|
|
|
@ -638,7 +638,7 @@ function hookFunctionsSave(parentProcessMode?: string): IWorkflowExecuteHooks {
|
|||
};
|
||||
|
||||
if (this.retryOf !== undefined) {
|
||||
fullExecutionData.retryOf = this.retryOf.toString();
|
||||
fullExecutionData.retryOf = this.retryOf?.toString();
|
||||
}
|
||||
|
||||
const workflowId = this.workflowData.id;
|
||||
|
@ -1054,7 +1054,7 @@ async function executeWorkflow(
|
|||
mode: 'integrated',
|
||||
startedAt: new Date(),
|
||||
stoppedAt: new Date(),
|
||||
status: 'error',
|
||||
status: 'failed',
|
||||
};
|
||||
// When failing, we might not have finished the execution
|
||||
// Therefore, database might not contain finished errors.
|
||||
|
@ -1073,6 +1073,9 @@ async function executeWorkflow(
|
|||
fullExecutionData.workflowId = workflowData.id;
|
||||
}
|
||||
|
||||
// remove execution from active executions
|
||||
Container.get(ActiveExecutions).remove(executionId, fullRunData);
|
||||
|
||||
const executionData = ResponseHelper.flattenExecutionData(fullExecutionData);
|
||||
|
||||
await Db.collections.Execution.update(executionId, executionData as IExecutionFlattedDb);
|
||||
|
|
|
@ -271,6 +271,7 @@ export class WorkflowRunner {
|
|||
undefined,
|
||||
workflowTimeout <= 0 ? undefined : Date.now() + workflowTimeout * 1000,
|
||||
);
|
||||
additionalData.restartExecutionId = restartExecutionId;
|
||||
|
||||
// Register the active execution
|
||||
const executionId = await this.activeExecutions.add(data, undefined, restartExecutionId);
|
||||
|
@ -644,6 +645,8 @@ export class WorkflowRunner {
|
|||
data.workflowData.staticData = await WorkflowHelpers.getStaticDataById(workflowId);
|
||||
}
|
||||
|
||||
data.restartExecutionId = restartExecutionId;
|
||||
|
||||
// Register the active execution
|
||||
const executionId = await this.activeExecutions.add(data, subprocess, restartExecutionId);
|
||||
|
||||
|
@ -741,8 +744,22 @@ export class WorkflowRunner {
|
|||
childExecutionIds.splice(executionIdIndex, 1);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/await-thenable
|
||||
this.activeExecutions.remove(message.data.executionId, message.data.result);
|
||||
if (message.data.result === undefined) {
|
||||
const noDataError = new WorkflowOperationError('Workflow finished with no result data');
|
||||
const subWorkflowHooks = WorkflowExecuteAdditionalData.getWorkflowHooksMain(
|
||||
data,
|
||||
message.data.executionId,
|
||||
);
|
||||
await this.processError(
|
||||
noDataError,
|
||||
startedAt,
|
||||
data.executionMode,
|
||||
message.data?.executionId,
|
||||
subWorkflowHooks,
|
||||
);
|
||||
} else {
|
||||
this.activeExecutions.remove(message.data.executionId, message.data.result);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -170,6 +170,7 @@ class WorkflowRunnerProcess {
|
|||
undefined,
|
||||
workflowTimeout <= 0 ? undefined : Date.now() + workflowTimeout * 1000,
|
||||
);
|
||||
additionalData.restartExecutionId = this.data.restartExecutionId;
|
||||
additionalData.hooks = this.getProcessForwardHooks();
|
||||
|
||||
additionalData.hooks.hookFunctions.sendResponse = [
|
||||
|
|
|
@ -144,6 +144,7 @@ export class Start extends BaseCommand {
|
|||
private async generateStaticAssets() {
|
||||
// Read the index file and replace the path placeholder
|
||||
const n8nPath = config.getEnv('path');
|
||||
const restEndpoint = config.getEnv('endpoints.rest');
|
||||
const hooksUrls = config.getEnv('externalFrontendHooksUrls');
|
||||
|
||||
let scriptsString = '';
|
||||
|
@ -167,6 +168,7 @@ export class Start extends BaseCommand {
|
|||
];
|
||||
if (filePath.endsWith('index.html')) {
|
||||
streams.push(
|
||||
replaceStream('{{REST_ENDPOINT}}', restEndpoint, { ignoreCase: false }),
|
||||
replaceStream(closingTitleTag, closingTitleTag + scriptsString, {
|
||||
ignoreCase: false,
|
||||
}),
|
||||
|
|
|
@ -155,9 +155,10 @@ export async function recoverExecutionDataFromEventLogMessages(
|
|||
}
|
||||
|
||||
if (applyToDb) {
|
||||
const newStatus = executionEntry.status === 'failed' ? 'failed' : 'crashed';
|
||||
await Db.collections.Execution.update(executionId, {
|
||||
data: stringify(executionData),
|
||||
status: 'crashed',
|
||||
status: newStatus,
|
||||
stoppedAt: lastNodeRunTimestamp?.toJSDate(),
|
||||
});
|
||||
await Container.get(InternalHooks).onWorkflowPostExecute(
|
||||
|
@ -170,7 +171,7 @@ export async function recoverExecutionDataFromEventLogMessages(
|
|||
waitTill: executionEntry.waitTill ?? undefined,
|
||||
startedAt: executionEntry.startedAt,
|
||||
stoppedAt: lastNodeRunTimestamp?.toJSDate(),
|
||||
status: 'crashed',
|
||||
status: newStatus,
|
||||
},
|
||||
);
|
||||
const iRunData: IRun = {
|
||||
|
@ -180,7 +181,7 @@ export async function recoverExecutionDataFromEventLogMessages(
|
|||
waitTill: executionEntry.waitTill ?? undefined,
|
||||
startedAt: executionEntry.startedAt,
|
||||
stoppedAt: lastNodeRunTimestamp?.toJSDate(),
|
||||
status: 'crashed',
|
||||
status: newStatus,
|
||||
};
|
||||
const workflowHooks = getWorkflowHooksMain(
|
||||
{
|
||||
|
|
|
@ -1,10 +1,83 @@
|
|||
import type { INode, IRun, IWorkflowBase } from 'n8n-workflow';
|
||||
import { LoggerProxy } from 'n8n-workflow';
|
||||
import * as Db from '@/Db';
|
||||
import { StatisticsNames } from '@db/entities/WorkflowStatistics';
|
||||
import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
|
||||
import { QueryFailedError } from 'typeorm';
|
||||
import { Container } from 'typedi';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import config from '@/config';
|
||||
|
||||
enum StatisticsUpsertResult {
|
||||
insert = 'insert',
|
||||
update = 'update',
|
||||
failed = 'failed',
|
||||
}
|
||||
|
||||
async function upsertWorkflowStatistics(
|
||||
eventName: StatisticsNames,
|
||||
workflowId: string,
|
||||
): Promise<StatisticsUpsertResult> {
|
||||
const dbType = config.getEnv('database.type');
|
||||
const { tableName } = Db.collections.WorkflowStatistics.metadata;
|
||||
try {
|
||||
if (dbType === 'sqlite') {
|
||||
await Db.collections.WorkflowStatistics.query(
|
||||
`INSERT INTO "${tableName}" ("count", "name", "workflowId", "latestEvent")
|
||||
VALUES (1, "${eventName}", "${workflowId}", CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (workflowId, name)
|
||||
DO UPDATE SET count = count + 1, latestEvent = CURRENT_TIMESTAMP`,
|
||||
);
|
||||
// SQLite does not offer a reliable way to know whether or not an insert or update happened.
|
||||
// We'll use a naive approach in this case. Query again after and it might cause us to miss the
|
||||
// first production execution sometimes due to concurrency, but it's the only way.
|
||||
|
||||
const counter = await Db.collections.WorkflowStatistics.findOne({
|
||||
select: ['count'],
|
||||
where: {
|
||||
name: eventName,
|
||||
workflowId,
|
||||
},
|
||||
});
|
||||
|
||||
if (counter?.count === 1) {
|
||||
return StatisticsUpsertResult.insert;
|
||||
}
|
||||
return StatisticsUpsertResult.update;
|
||||
} else if (dbType === 'postgresdb') {
|
||||
const queryResult = (await Db.collections.WorkflowStatistics.query(
|
||||
`INSERT INTO "${tableName}" ("count", "name", "workflowId", "latestEvent")
|
||||
VALUES (1, '${eventName}', '${workflowId}', CURRENT_TIMESTAMP)
|
||||
ON CONFLICT ("name", "workflowId")
|
||||
DO UPDATE SET "count" = "${tableName}"."count" + 1, "latestEvent" = CURRENT_TIMESTAMP
|
||||
RETURNING *;`,
|
||||
)) as Array<{
|
||||
count: number;
|
||||
}>;
|
||||
if (queryResult[0].count === 1) {
|
||||
return StatisticsUpsertResult.insert;
|
||||
}
|
||||
return StatisticsUpsertResult.update;
|
||||
} else {
|
||||
const queryResult = (await Db.collections.WorkflowStatistics.query(
|
||||
`INSERT INTO \`${tableName}\` (count, name, workflowId, latestEvent)
|
||||
VALUES (1, "${eventName}", "${workflowId}", NOW())
|
||||
ON DUPLICATE KEY
|
||||
UPDATE count = count + 1, latestEvent = NOW();`,
|
||||
)) as {
|
||||
affectedRows: number;
|
||||
};
|
||||
if (queryResult.affectedRows === 1) {
|
||||
return StatisticsUpsertResult.insert;
|
||||
}
|
||||
// MySQL returns 2 affected rows on update
|
||||
return StatisticsUpsertResult.update;
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof QueryFailedError) return StatisticsUpsertResult.failed;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function workflowExecutionCompleted(
|
||||
workflowData: IWorkflowBase,
|
||||
|
@ -27,36 +100,23 @@ export async function workflowExecutionCompleted(
|
|||
const workflowId = workflowData.id;
|
||||
if (!workflowId) return;
|
||||
|
||||
// Try insertion and if it fails due to key conflicts then update the existing entry instead
|
||||
try {
|
||||
await Db.collections.WorkflowStatistics.insert({
|
||||
count: 1,
|
||||
name,
|
||||
workflowId,
|
||||
latestEvent: new Date(),
|
||||
});
|
||||
const upsertResult = await upsertWorkflowStatistics(name, workflowId);
|
||||
|
||||
// If we're here we can check if we're sending the first production success metric
|
||||
if (name !== StatisticsNames.productionSuccess) return;
|
||||
|
||||
// Get the owner of the workflow so we can send the metric
|
||||
const owner = await getWorkflowOwner(workflowId);
|
||||
const metrics = {
|
||||
user_id: owner.id,
|
||||
workflow_id: workflowId,
|
||||
};
|
||||
|
||||
// Send the metrics
|
||||
await Container.get(InternalHooks).onFirstProductionWorkflowSuccess(metrics);
|
||||
} catch (error) {
|
||||
if (!(error instanceof QueryFailedError)) {
|
||||
throw error;
|
||||
if (
|
||||
name === StatisticsNames.productionSuccess &&
|
||||
upsertResult === StatisticsUpsertResult.insert
|
||||
) {
|
||||
const owner = await getWorkflowOwner(workflowId);
|
||||
const metrics = {
|
||||
user_id: owner.id,
|
||||
workflow_id: workflowId,
|
||||
};
|
||||
// Send the metrics
|
||||
await Container.get(InternalHooks).onFirstProductionWorkflowSuccess(metrics);
|
||||
}
|
||||
|
||||
await Db.collections.WorkflowStatistics.update(
|
||||
{ workflowId, name },
|
||||
{ count: () => 'count + 1', latestEvent: new Date() },
|
||||
);
|
||||
} catch (error) {
|
||||
LoggerProxy.verbose('Unable to fire first workflow success telemetry event');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ export class SamlUrls {
|
|||
|
||||
static readonly initSSO = '/initsso';
|
||||
|
||||
static readonly restInitSSO = this.samlRESTRoot + this.initSSO;
|
||||
|
||||
static readonly acs = '/acs';
|
||||
|
||||
static readonly restAcs = this.samlRESTRoot + this.acs;
|
||||
|
@ -17,13 +15,13 @@ export class SamlUrls {
|
|||
|
||||
static readonly configTest = '/config/test';
|
||||
|
||||
static readonly configToggleEnabled = '/config/toggle';
|
||||
static readonly configTestReturn = '/config/test/return';
|
||||
|
||||
static readonly restConfig = this.samlRESTRoot + this.config;
|
||||
static readonly configToggleEnabled = '/config/toggle';
|
||||
|
||||
static readonly defaultRedirect = '/';
|
||||
|
||||
static readonly samlOnboarding = '/settings/personal'; // TODO:SAML: implement signup page
|
||||
static readonly samlOnboarding = '/saml/onboarding';
|
||||
}
|
||||
|
||||
export const SAML_PREFERENCES_DB_KEY = 'features.saml';
|
||||
|
|
|
@ -16,7 +16,11 @@ import type { PostBindingContext } from 'samlify/types/src/entity';
|
|||
import { isSamlLicensedAndEnabled } from '../samlHelpers';
|
||||
import type { SamlLoginBinding } from '../types';
|
||||
import { AuthenticatedRequest } from '@/requests';
|
||||
import { getServiceProviderEntityId, getServiceProviderReturnUrl } from '../serviceProvider.ee';
|
||||
import {
|
||||
getServiceProviderConfigTestReturnUrl,
|
||||
getServiceProviderEntityId,
|
||||
getServiceProviderReturnUrl,
|
||||
} from '../serviceProvider.ee';
|
||||
|
||||
@RestController('/sso/saml')
|
||||
export class SamlController {
|
||||
|
@ -34,13 +38,13 @@ export class SamlController {
|
|||
* Return SAML config
|
||||
*/
|
||||
@Get(SamlUrls.config, { middlewares: [samlLicensedOwnerMiddleware] })
|
||||
async configGet(req: AuthenticatedRequest, res: express.Response) {
|
||||
async configGet() {
|
||||
const prefs = this.samlService.samlPreferences;
|
||||
return res.send({
|
||||
return {
|
||||
...prefs,
|
||||
entityID: getServiceProviderEntityId(),
|
||||
returnUrl: getServiceProviderReturnUrl(),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,11 +52,11 @@ export class SamlController {
|
|||
* Set SAML config
|
||||
*/
|
||||
@Post(SamlUrls.config, { middlewares: [samlLicensedOwnerMiddleware] })
|
||||
async configPost(req: SamlConfiguration.Update, res: express.Response) {
|
||||
async configPost(req: SamlConfiguration.Update) {
|
||||
const validationResult = await validate(req.body);
|
||||
if (validationResult.length === 0) {
|
||||
const result = await this.samlService.setSamlPreferences(req.body);
|
||||
return res.send(result);
|
||||
return result;
|
||||
} else {
|
||||
throw new BadRequestError(
|
||||
'Body is not a valid SamlPreferences object: ' +
|
||||
|
@ -100,6 +104,10 @@ export class SamlController {
|
|||
private async acsHandler(req: express.Request, res: express.Response, binding: SamlLoginBinding) {
|
||||
const loginResult = await this.samlService.handleSamlLogin(req, binding);
|
||||
if (loginResult) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (req.body.RelayState && req.body.RelayState === getServiceProviderConfigTestReturnUrl()) {
|
||||
return res.status(202).send(loginResult.attributes);
|
||||
}
|
||||
if (loginResult.authenticatedUser) {
|
||||
// Only sign in user if SAML is enabled, otherwise treat as test connection
|
||||
if (isSamlLicensedAndEnabled()) {
|
||||
|
@ -134,13 +142,13 @@ export class SamlController {
|
|||
*/
|
||||
@Get(SamlUrls.configTest, { middlewares: [samlLicensedOwnerMiddleware] })
|
||||
async configTestGet(req: AuthenticatedRequest, res: express.Response) {
|
||||
return this.handleInitSSO(res);
|
||||
return this.handleInitSSO(res, getServiceProviderConfigTestReturnUrl());
|
||||
}
|
||||
|
||||
private async handleInitSSO(res: express.Response) {
|
||||
const result = this.samlService.getLoginRequestUrl();
|
||||
private async handleInitSSO(res: express.Response, relayState?: string) {
|
||||
const result = this.samlService.getLoginRequestUrl(relayState);
|
||||
if (result?.binding === 'redirect') {
|
||||
return res.send(result.context.context);
|
||||
return result.context.context;
|
||||
} else if (result?.binding === 'post') {
|
||||
return res.send(getInitSSOFormView(result.context as PostBindingContext));
|
||||
} else {
|
||||
|
|
|
@ -20,12 +20,13 @@ import {
|
|||
setSamlLoginLabel,
|
||||
updateUserFromSamlAttributes,
|
||||
} from './samlHelpers';
|
||||
import type { Settings } from '../../databases/entities/Settings';
|
||||
import type { Settings } from '@/databases/entities/Settings';
|
||||
import axios from 'axios';
|
||||
import https from 'https';
|
||||
import type { SamlLoginBinding } from './types';
|
||||
import type { BindingContext, PostBindingContext } from 'samlify/types/src/entity';
|
||||
import { validateMetadata, validateResponse } from './samlValidator';
|
||||
import { getInstanceBaseUrl } from '@/UserManagement/UserManagementHelper';
|
||||
|
||||
@Service()
|
||||
export class SamlService {
|
||||
|
@ -48,6 +49,7 @@ export class SamlService {
|
|||
loginLabel: 'SAML',
|
||||
wantAssertionsSigned: true,
|
||||
wantMessageSigned: true,
|
||||
relayState: getInstanceBaseUrl(),
|
||||
signatureConfig: {
|
||||
prefix: 'ds',
|
||||
location: {
|
||||
|
@ -92,7 +94,10 @@ export class SamlService {
|
|||
return getServiceProviderInstance(this._samlPreferences);
|
||||
}
|
||||
|
||||
getLoginRequestUrl(binding?: SamlLoginBinding): {
|
||||
getLoginRequestUrl(
|
||||
relayState?: string,
|
||||
binding?: SamlLoginBinding,
|
||||
): {
|
||||
binding: SamlLoginBinding;
|
||||
context: BindingContext | PostBindingContext;
|
||||
} {
|
||||
|
@ -100,28 +105,29 @@ export class SamlService {
|
|||
if (binding === 'post') {
|
||||
return {
|
||||
binding,
|
||||
context: this.getPostLoginRequestUrl(),
|
||||
context: this.getPostLoginRequestUrl(relayState),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
binding,
|
||||
context: this.getRedirectLoginRequestUrl(),
|
||||
context: this.getRedirectLoginRequestUrl(relayState),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private getRedirectLoginRequestUrl(): BindingContext {
|
||||
const loginRequest = this.getServiceProviderInstance().createLoginRequest(
|
||||
this.getIdentityProviderInstance(),
|
||||
'redirect',
|
||||
);
|
||||
private getRedirectLoginRequestUrl(relayState?: string): BindingContext {
|
||||
const sp = this.getServiceProviderInstance();
|
||||
sp.entitySetting.relayState = relayState ?? getInstanceBaseUrl();
|
||||
const loginRequest = sp.createLoginRequest(this.getIdentityProviderInstance(), 'redirect');
|
||||
//TODO:SAML: debug logging
|
||||
LoggerProxy.debug(loginRequest.context);
|
||||
return loginRequest;
|
||||
}
|
||||
|
||||
private getPostLoginRequestUrl(): PostBindingContext {
|
||||
const loginRequest = this.getServiceProviderInstance().createLoginRequest(
|
||||
private getPostLoginRequestUrl(relayState?: string): PostBindingContext {
|
||||
const sp = this.getServiceProviderInstance();
|
||||
sp.entitySetting.relayState = relayState ?? getInstanceBaseUrl();
|
||||
const loginRequest = sp.createLoginRequest(
|
||||
this.getIdentityProviderInstance(),
|
||||
'post',
|
||||
) as PostBindingContext;
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as Db from '@/Db';
|
|||
import { AuthIdentity } from '@db/entities/AuthIdentity';
|
||||
import { User } from '@db/entities/User';
|
||||
import { License } from '@/License';
|
||||
import { AuthError } from '@/ResponseHelper';
|
||||
import { AuthError, InternalServerError } from '@/ResponseHelper';
|
||||
import { hashPassword, isUserManagementEnabled } from '@/UserManagement/UserManagementHelper';
|
||||
import type { SamlPreferences } from './types/samlPreferences';
|
||||
import type { SamlUserAttributes } from './types/samlUserAttributes';
|
||||
|
@ -12,11 +12,11 @@ import type { FlowResult } from 'samlify/types/src/flow';
|
|||
import type { SamlAttributeMapping } from './types/samlAttributeMapping';
|
||||
import { SAML_LOGIN_ENABLED, SAML_LOGIN_LABEL } from './constants';
|
||||
import {
|
||||
getCurrentAuthenticationMethod,
|
||||
isEmailCurrentAuthenticationMethod,
|
||||
isSamlCurrentAuthenticationMethod,
|
||||
setCurrentAuthenticationMethod,
|
||||
} from '../ssoHelpers';
|
||||
import { LoggerProxy } from 'n8n-workflow';
|
||||
/**
|
||||
* Check whether the SAML feature is licensed and enabled in the instance
|
||||
*/
|
||||
|
@ -30,18 +30,17 @@ export function getSamlLoginLabel(): string {
|
|||
|
||||
// can only toggle between email and saml, not directly to e.g. ldap
|
||||
export async function setSamlLoginEnabled(enabled: boolean): Promise<void> {
|
||||
if (config.get(SAML_LOGIN_ENABLED) === enabled) {
|
||||
return;
|
||||
}
|
||||
if (enabled && isEmailCurrentAuthenticationMethod()) {
|
||||
config.set(SAML_LOGIN_ENABLED, true);
|
||||
await setCurrentAuthenticationMethod('saml');
|
||||
} else if (!enabled && isSamlCurrentAuthenticationMethod()) {
|
||||
config.set(SAML_LOGIN_ENABLED, false);
|
||||
await setCurrentAuthenticationMethod('email');
|
||||
if (isEmailCurrentAuthenticationMethod() || isSamlCurrentAuthenticationMethod()) {
|
||||
if (enabled) {
|
||||
config.set(SAML_LOGIN_ENABLED, true);
|
||||
await setCurrentAuthenticationMethod('saml');
|
||||
} else if (!enabled) {
|
||||
config.set(SAML_LOGIN_ENABLED, false);
|
||||
await setCurrentAuthenticationMethod('email');
|
||||
}
|
||||
} else {
|
||||
LoggerProxy.warn(
|
||||
'Cannot switch SAML login enabled state when an authentication method other than email is active',
|
||||
throw new InternalServerError(
|
||||
`Cannot switch SAML login enabled state when an authentication method other than email or saml is active (current: ${getCurrentAuthenticationMethod()})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@ export function getServiceProviderReturnUrl(): string {
|
|||
return getInstanceBaseUrl() + SamlUrls.restAcs;
|
||||
}
|
||||
|
||||
export function getServiceProviderConfigTestReturnUrl(): string {
|
||||
return getInstanceBaseUrl() + SamlUrls.configTestReturn;
|
||||
}
|
||||
|
||||
// TODO:SAML: make these configurable for the end user
|
||||
export function getServiceProviderInstance(prefs: SamlPreferences): ServiceProviderInstance {
|
||||
if (serviceProviderInstance === undefined) {
|
||||
|
@ -24,6 +28,7 @@ export function getServiceProviderInstance(prefs: SamlPreferences): ServiceProvi
|
|||
wantAssertionsSigned: prefs.wantAssertionsSigned,
|
||||
wantMessageSigned: prefs.wantMessageSigned,
|
||||
signatureConfig: prefs.signatureConfig,
|
||||
relayState: prefs.relayState,
|
||||
nameIDFormat: ['urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'],
|
||||
assertionConsumerService: [
|
||||
{
|
||||
|
|
|
@ -57,4 +57,8 @@ export class SamlPreferences {
|
|||
action: 'after',
|
||||
},
|
||||
};
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
relayState?: string = '';
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ describe('Instance owner', () => {
|
|||
.send({
|
||||
loginEnabled: true,
|
||||
})
|
||||
.expect(200);
|
||||
.expect(500);
|
||||
|
||||
expect(getCurrentAuthenticationMethod()).toBe('ldap');
|
||||
});
|
||||
|
|
|
@ -17,12 +17,15 @@ type WorkflowStatisticsRepository = Repository<WorkflowStatistics>;
|
|||
jest.mock('@/Db', () => {
|
||||
return {
|
||||
collections: {
|
||||
WorkflowStatistics: mock<WorkflowStatisticsRepository>(),
|
||||
WorkflowStatistics: mock<WorkflowStatisticsRepository>({
|
||||
metadata: { tableName: 'workflow_statistics' },
|
||||
}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('Events', () => {
|
||||
const dbType = config.getEnv('database.type');
|
||||
const fakeUser = Object.assign(new User(), { id: 'abcde-fghij' });
|
||||
const internalHooks = mockInstance(InternalHooks);
|
||||
|
||||
|
@ -44,11 +47,28 @@ describe('Events', () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
if (dbType === 'sqlite') {
|
||||
workflowStatisticsRepository.findOne.mockClear();
|
||||
} else {
|
||||
workflowStatisticsRepository.query.mockClear();
|
||||
}
|
||||
|
||||
internalHooks.onFirstProductionWorkflowSuccess.mockClear();
|
||||
internalHooks.onFirstWorkflowDataLoad.mockClear();
|
||||
});
|
||||
|
||||
afterEach(() => {});
|
||||
const mockDBCall = (count = 1) => {
|
||||
if (dbType === 'sqlite') {
|
||||
workflowStatisticsRepository.findOne.mockResolvedValueOnce(
|
||||
mock<WorkflowStatistics>({ count }),
|
||||
);
|
||||
} else {
|
||||
const result = dbType === 'postgresdb' ? [{ count }] : { affectedRows: count };
|
||||
workflowStatisticsRepository.query.mockImplementationOnce(async (query) =>
|
||||
query.startsWith('INSERT INTO') ? result : null,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
describe('workflowExecutionCompleted', () => {
|
||||
test('should create metrics for production successes', async () => {
|
||||
|
@ -69,6 +89,8 @@ describe('Events', () => {
|
|||
mode: 'internal' as WorkflowExecuteMode,
|
||||
startedAt: new Date(),
|
||||
};
|
||||
mockDBCall();
|
||||
|
||||
await workflowExecutionCompleted(workflow, runData);
|
||||
expect(internalHooks.onFirstProductionWorkflowSuccess).toBeCalledTimes(1);
|
||||
expect(internalHooks.onFirstProductionWorkflowSuccess).toHaveBeenNthCalledWith(1, {
|
||||
|
@ -101,9 +123,6 @@ describe('Events', () => {
|
|||
|
||||
test('should not send metrics for updated entries', async () => {
|
||||
// Call the function with a fail insert, ensure update is called *and* metrics aren't sent
|
||||
workflowStatisticsRepository.insert.mockImplementationOnce(() => {
|
||||
throw new QueryFailedError('invalid insert', [], '');
|
||||
});
|
||||
const workflow = {
|
||||
id: '1',
|
||||
name: '',
|
||||
|
@ -120,6 +139,7 @@ describe('Events', () => {
|
|||
mode: 'internal' as WorkflowExecuteMode,
|
||||
startedAt: new Date(),
|
||||
};
|
||||
mockDBCall(2);
|
||||
await workflowExecutionCompleted(workflow, runData);
|
||||
expect(internalHooks.onFirstProductionWorkflowSuccess).toBeCalledTimes(0);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-core",
|
||||
"version": "0.161.0",
|
||||
"version": "0.162.0",
|
||||
"description": "Core functionality of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
|
|
@ -1117,7 +1117,6 @@ export async function requestOAuth2(
|
|||
[oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken,
|
||||
});
|
||||
}
|
||||
|
||||
if (isN8nRequest) {
|
||||
return this.helpers.httpRequest(newRequestOptions).catch(async (error: AxiosError) => {
|
||||
if (error.response?.status === 401) {
|
||||
|
@ -1179,85 +1178,95 @@ export async function requestOAuth2(
|
|||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
return this.helpers.request(newRequestOptions).catch(async (error: IResponseError) => {
|
||||
const statusCodeReturned =
|
||||
oAuth2Options?.tokenExpiredStatusCode === undefined
|
||||
? 401
|
||||
: oAuth2Options?.tokenExpiredStatusCode;
|
||||
|
||||
if (error.statusCode === statusCodeReturned) {
|
||||
// Token is probably not valid anymore. So try refresh it.
|
||||
|
||||
const tokenRefreshOptions: IDataObject = {};
|
||||
|
||||
if (oAuth2Options?.includeCredentialsOnRefreshOnBody) {
|
||||
const body: IDataObject = {
|
||||
client_id: credentials.clientId,
|
||||
client_secret: credentials.clientSecret,
|
||||
};
|
||||
tokenRefreshOptions.body = body;
|
||||
// Override authorization property so the credentials are not included in it
|
||||
tokenRefreshOptions.headers = {
|
||||
Authorization: '',
|
||||
};
|
||||
return this.helpers
|
||||
.request(newRequestOptions)
|
||||
.then((response) => {
|
||||
const requestOptions = newRequestOptions as any;
|
||||
if (
|
||||
requestOptions.resolveWithFullResponse === true &&
|
||||
requestOptions.simple === false &&
|
||||
response.statusCode ===
|
||||
(oAuth2Options?.tokenExpiredStatusCode === undefined
|
||||
? 401
|
||||
: oAuth2Options?.tokenExpiredStatusCode)
|
||||
) {
|
||||
throw response;
|
||||
}
|
||||
|
||||
Logger.debug(
|
||||
`OAuth2 token for "${credentialsType}" used by node "${node.name}" expired. Should revalidate.`,
|
||||
);
|
||||
|
||||
let newToken;
|
||||
|
||||
// if it's OAuth2 with client credentials grant type, get a new token
|
||||
// instead of refreshing it.
|
||||
if (OAuth2GrantType.clientCredentials === credentials.grantType) {
|
||||
newToken = await getClientCredentialsToken(token.client, credentials);
|
||||
} else {
|
||||
newToken = await token.refresh(tokenRefreshOptions);
|
||||
}
|
||||
|
||||
Logger.debug(
|
||||
`OAuth2 token for "${credentialsType}" used by node "${node.name}" has been renewed.`,
|
||||
);
|
||||
|
||||
credentials.oauthTokenData = newToken.data;
|
||||
|
||||
// Find the credentials
|
||||
if (!node.credentials?.[credentialsType]) {
|
||||
throw new Error(
|
||||
`The node "${node.name}" does not have credentials of type "${credentialsType}"!`,
|
||||
return response;
|
||||
})
|
||||
.catch(async (error: IResponseError) => {
|
||||
const statusCodeReturned =
|
||||
oAuth2Options?.tokenExpiredStatusCode === undefined
|
||||
? 401
|
||||
: oAuth2Options?.tokenExpiredStatusCode;
|
||||
if (error.statusCode === statusCodeReturned) {
|
||||
// Token is probably not valid anymore. So try refresh it.
|
||||
const tokenRefreshOptions: IDataObject = {};
|
||||
if (oAuth2Options?.includeCredentialsOnRefreshOnBody) {
|
||||
const body: IDataObject = {
|
||||
client_id: credentials.clientId,
|
||||
client_secret: credentials.clientSecret,
|
||||
};
|
||||
tokenRefreshOptions.body = body;
|
||||
// Override authorization property so the credentials are not included in it
|
||||
tokenRefreshOptions.headers = {
|
||||
Authorization: '',
|
||||
};
|
||||
}
|
||||
Logger.debug(
|
||||
`OAuth2 token for "${credentialsType}" used by node "${node.name}" expired. Should revalidate.`,
|
||||
);
|
||||
}
|
||||
const nodeCredentials = node.credentials[credentialsType];
|
||||
|
||||
// Save the refreshed token
|
||||
await additionalData.credentialsHelper.updateCredentials(
|
||||
nodeCredentials,
|
||||
credentialsType,
|
||||
credentials as unknown as ICredentialDataDecryptedObject,
|
||||
);
|
||||
let newToken;
|
||||
|
||||
Logger.debug(
|
||||
`OAuth2 token for "${credentialsType}" used by node "${node.name}" has been saved to database successfully.`,
|
||||
);
|
||||
// if it's OAuth2 with client credentials grant type, get a new token
|
||||
// instead of refreshing it.
|
||||
if (OAuth2GrantType.clientCredentials === credentials.grantType) {
|
||||
newToken = await getClientCredentialsToken(token.client, credentials);
|
||||
} else {
|
||||
newToken = await token.refresh(tokenRefreshOptions);
|
||||
}
|
||||
Logger.debug(
|
||||
`OAuth2 token for "${credentialsType}" used by node "${node.name}" has been renewed.`,
|
||||
);
|
||||
|
||||
// Make the request again with the new token
|
||||
const newRequestOptions = newToken.sign(requestOptions as clientOAuth2.RequestObject);
|
||||
newRequestOptions.headers = newRequestOptions.headers ?? {};
|
||||
credentials.oauthTokenData = newToken.data;
|
||||
|
||||
if (oAuth2Options?.keyToIncludeInAccessTokenHeader) {
|
||||
Object.assign(newRequestOptions.headers, {
|
||||
[oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken,
|
||||
});
|
||||
// Find the credentials
|
||||
if (!node.credentials?.[credentialsType]) {
|
||||
throw new Error(
|
||||
`The node "${node.name}" does not have credentials of type "${credentialsType}"!`,
|
||||
);
|
||||
}
|
||||
const nodeCredentials = node.credentials[credentialsType];
|
||||
|
||||
// Save the refreshed token
|
||||
await additionalData.credentialsHelper.updateCredentials(
|
||||
nodeCredentials,
|
||||
credentialsType,
|
||||
credentials as unknown as ICredentialDataDecryptedObject,
|
||||
);
|
||||
|
||||
Logger.debug(
|
||||
`OAuth2 token for "${credentialsType}" used by node "${node.name}" has been saved to database successfully.`,
|
||||
);
|
||||
|
||||
// Make the request again with the new token
|
||||
const newRequestOptions = newToken.sign(requestOptions as clientOAuth2.RequestObject);
|
||||
newRequestOptions.headers = newRequestOptions.headers ?? {};
|
||||
|
||||
if (oAuth2Options?.keyToIncludeInAccessTokenHeader) {
|
||||
Object.assign(newRequestOptions.headers, {
|
||||
[oAuth2Options.keyToIncludeInAccessTokenHeader]: token.accessToken,
|
||||
});
|
||||
}
|
||||
|
||||
return this.helpers.request(newRequestOptions);
|
||||
}
|
||||
|
||||
return this.helpers.request(newRequestOptions);
|
||||
}
|
||||
|
||||
// Unknown error so simply throw it
|
||||
throw error;
|
||||
});
|
||||
// Unknown error so simply throw it
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -746,7 +746,9 @@ export class WorkflowExecute {
|
|||
|
||||
const returnPromise = (async () => {
|
||||
try {
|
||||
await this.executeHook('workflowExecuteBefore', [workflow]);
|
||||
if (!this.additionalData.restartExecutionId) {
|
||||
await this.executeHook('workflowExecuteBefore', [workflow]);
|
||||
}
|
||||
} catch (error) {
|
||||
// Set the error that it can be saved correctly
|
||||
executionError = {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<link rel="icon" href="/favicon.ico" />
|
||||
<script type="text/javascript">
|
||||
window.BASE_PATH = '/{{BASE_PATH}}/';
|
||||
window.REST_ENDPOINT = '{{REST_ENDPOINT}}';
|
||||
</script>
|
||||
<script>!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled getFeatureFlag onFeatureFlags reloadFeatureFlags".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[])</script>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-editor-ui",
|
||||
"version": "0.188.0",
|
||||
"version": "0.189.0",
|
||||
"description": "Workflow Editor UI for n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -85,6 +85,7 @@
|
|||
"devDependencies": {
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@pinia/testing": "^0.0.14",
|
||||
"@sentry/vite-plugin": "^0.4.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@testing-library/vue": "^5.8.3",
|
||||
|
@ -99,9 +100,9 @@
|
|||
"@types/lodash.set": "^4.3.6",
|
||||
"@types/luxon": "^3.2.0",
|
||||
"@types/uuid": "^8.3.2",
|
||||
"@vitest/coverage-c8": "^0.28.5",
|
||||
"@vitejs/plugin-legacy": "^3.0.1",
|
||||
"@vitejs/plugin-vue2": "^2.2.0",
|
||||
"@vitest/coverage-c8": "^0.28.5",
|
||||
"c8": "^7.12.0",
|
||||
"jshint": "^2.9.7",
|
||||
"miragejs": "^0.1.47",
|
||||
|
|
|
@ -37,6 +37,7 @@ import {
|
|||
import { SignInType } from './constants';
|
||||
import { FAKE_DOOR_FEATURES, TRIGGER_NODE_FILTER, REGULAR_NODE_FILTER } from './constants';
|
||||
import { BulkCommand, Undoable } from '@/models/history';
|
||||
import { PartialBy } from '@/utils/typeHelpers';
|
||||
|
||||
export * from 'n8n-design-system/types';
|
||||
|
||||
|
@ -984,6 +985,7 @@ export interface WorkflowsState {
|
|||
|
||||
export interface RootState {
|
||||
baseUrl: string;
|
||||
restEndpoint: string;
|
||||
defaultLocale: string;
|
||||
endpointWebhook: string;
|
||||
endpointWebhookTest: string;
|
||||
|
@ -1484,3 +1486,43 @@ export type ExecutionsQueryFilter = {
|
|||
startedAfter?: string;
|
||||
startedBefore?: string;
|
||||
};
|
||||
|
||||
export type SamlAttributeMapping = {
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
userPrincipalName: string;
|
||||
};
|
||||
|
||||
export type SamlLoginBinding = 'post' | 'redirect';
|
||||
|
||||
export type SamlSignatureConfig = {
|
||||
prefix: 'ds';
|
||||
location: {
|
||||
reference: '/samlp:Response/saml:Issuer';
|
||||
action: 'after';
|
||||
};
|
||||
};
|
||||
|
||||
export type SamlPreferencesLoginEnabled = {
|
||||
loginEnabled: boolean;
|
||||
};
|
||||
|
||||
export type SamlPreferences = {
|
||||
mapping?: SamlAttributeMapping;
|
||||
metadata?: string;
|
||||
metadataUrl?: string;
|
||||
ignoreSSL?: boolean;
|
||||
loginBinding?: SamlLoginBinding;
|
||||
acsBinding?: SamlLoginBinding;
|
||||
authnRequestsSigned?: boolean;
|
||||
loginLabel?: string;
|
||||
wantAssertionsSigned?: boolean;
|
||||
wantMessageSigned?: boolean;
|
||||
signatureConfig?: SamlSignatureConfig;
|
||||
} & PartialBy<SamlPreferencesLoginEnabled, 'loginEnabled'>;
|
||||
|
||||
export type SamlPreferencesExtractedData = {
|
||||
entityID: string;
|
||||
returnUrl: string;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,39 @@
|
|||
import { makeRestApiRequest } from '@/utils';
|
||||
import { IRestApiContext } from '@/Interface';
|
||||
import {
|
||||
IRestApiContext,
|
||||
SamlPreferencesLoginEnabled,
|
||||
SamlPreferences,
|
||||
SamlPreferencesExtractedData,
|
||||
} from '@/Interface';
|
||||
|
||||
export const initSSO = (context: IRestApiContext): Promise<string> => {
|
||||
return makeRestApiRequest(context, 'GET', '/sso/saml/initsso');
|
||||
};
|
||||
|
||||
export const getSamlMetadata = (context: IRestApiContext): Promise<SamlPreferences> => {
|
||||
return makeRestApiRequest(context, 'GET', '/sso/saml/metadata');
|
||||
};
|
||||
|
||||
export const getSamlConfig = (
|
||||
context: IRestApiContext,
|
||||
): Promise<SamlPreferences & SamlPreferencesExtractedData> => {
|
||||
return makeRestApiRequest(context, 'GET', '/sso/saml/config');
|
||||
};
|
||||
|
||||
export const saveSamlConfig = (
|
||||
context: IRestApiContext,
|
||||
data: SamlPreferences,
|
||||
): Promise<SamlPreferences | undefined> => {
|
||||
return makeRestApiRequest(context, 'POST', '/sso/saml/config', data);
|
||||
};
|
||||
|
||||
export const toggleSamlConfig = (
|
||||
context: IRestApiContext,
|
||||
data: SamlPreferencesLoginEnabled,
|
||||
): Promise<void> => {
|
||||
return makeRestApiRequest(context, 'POST', '/sso/saml/config/toggle', data);
|
||||
};
|
||||
|
||||
export const testSamlConfig = (context: IRestApiContext): Promise<string> => {
|
||||
return makeRestApiRequest(context, 'GET', '/sso/saml/config/test');
|
||||
};
|
||||
|
|
|
@ -74,6 +74,14 @@ export default mixins(userHelpers, pushConnection).extend({
|
|||
available: this.canAccessApiSettings(),
|
||||
activateOnRouteNames: [VIEWS.API_SETTINGS],
|
||||
},
|
||||
{
|
||||
id: 'settings-sso',
|
||||
icon: 'user-lock',
|
||||
label: this.$locale.baseText('settings.sso'),
|
||||
position: 'top',
|
||||
available: this.canAccessSso(),
|
||||
activateOnRouteNames: [VIEWS.SSO_SETTINGS],
|
||||
},
|
||||
{
|
||||
id: 'settings-ldap',
|
||||
icon: 'network-wired',
|
||||
|
@ -143,6 +151,9 @@ export default mixins(userHelpers, pushConnection).extend({
|
|||
canAccessUsageAndPlan(): boolean {
|
||||
return this.canUserAccessRouteByName(VIEWS.USAGE);
|
||||
},
|
||||
canAccessSso(): boolean {
|
||||
return this.canUserAccessRouteByName(VIEWS.SSO_SETTINGS);
|
||||
},
|
||||
onVersionClick() {
|
||||
this.uiStore.openModal(ABOUT_MODAL_KEY);
|
||||
},
|
||||
|
@ -191,6 +202,11 @@ export default mixins(userHelpers, pushConnection).extend({
|
|||
this.$router.push({ name: VIEWS.USAGE });
|
||||
}
|
||||
break;
|
||||
case 'settings-sso':
|
||||
if (this.$router.currentRoute.name !== VIEWS.SSO_SETTINGS) {
|
||||
this.$router.push({ name: VIEWS.SSO_SETTINGS });
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -388,6 +388,8 @@ export enum VIEWS {
|
|||
WORKFLOW_EXECUTIONS = 'WorkflowExecutions',
|
||||
USAGE = 'Usage',
|
||||
LOG_STREAMING_SETTINGS = 'LogStreamingSettingsView',
|
||||
SSO_SETTINGS = 'SSoSettings',
|
||||
SAML_ONBOARDING = 'SamlOnboarding',
|
||||
}
|
||||
|
||||
export enum FAKE_DOOR_FEATURES {
|
||||
|
|
|
@ -1718,7 +1718,24 @@
|
|||
"settings.ldap.form.searchTimeout.label": "Search Timeout (Seconds)",
|
||||
"settings.ldap.form.searchTimeout.infoText": "The timeout value for queries to the AD/LDAP server. Increase if you are getting timeout errors caused by a slow AD/LDAP server",
|
||||
"settings.ldap.section.synchronization.title": "Synchronization",
|
||||
|
||||
"settings.sso": "SSO",
|
||||
"settings.sso.title": "Single Sign On",
|
||||
"settings.sso.subtitle": "SAML 2.0",
|
||||
"settings.sso.info": "SAML SSO (Security Assertion Markup Language Single Sign-On) is a type of authentication process that enables users to access multiple applications with a single set of login credentials. {link}",
|
||||
"settings.sso.info.link": "More info.",
|
||||
"settings.sso.activation.tooltip": "You need to save the settings first before activating SAML",
|
||||
"settings.sso.activated": "Activated",
|
||||
"settings.sso.deactivated": "Deactivated",
|
||||
"settings.sso.settings.redirectUrl.label": "Redirect URL",
|
||||
"settings.sso.settings.redirectUrl.copied": "Redirect URL copied to clipboard",
|
||||
"settings.sso.settings.redirectUrl.help": "Save the Redirect URL as you’ll need it to configure these in the SAML provider’s settings.",
|
||||
"settings.sso.settings.entityId.label": "Entity ID",
|
||||
"settings.sso.settings.entityId.copied": "Entity ID copied to clipboard",
|
||||
"settings.sso.settings.entityId.help": "Save the Entity URL as you’ll need it to configure these in the SAML provider’s settings.",
|
||||
"settings.sso.settings.ips.label": "Identity Provider Settings",
|
||||
"settings.sso.settings.ips.help": "Add the raw Metadata XML provided by your Identity Provider",
|
||||
"settings.sso.settings.test": "Test settings",
|
||||
"settings.sso.settings.save": "Save settings",
|
||||
"sso.login.divider": "or",
|
||||
"sso.login.button": "Continue with SSO"
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ import {
|
|||
faVideo,
|
||||
faTree,
|
||||
faStickyNote as faSolidStickyNote,
|
||||
faUserLock,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { faStickyNote } from '@fortawesome/free-regular-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||
|
@ -258,5 +259,6 @@ addIcon(faUserFriends);
|
|||
addIcon(faUsers);
|
||||
addIcon(faVideo);
|
||||
addIcon(faTree);
|
||||
addIcon(faUserLock);
|
||||
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon);
|
||||
|
|
File diff suppressed because it is too large
Load diff
2
packages/editor-ui/src/shims.d.ts
vendored
2
packages/editor-ui/src/shims.d.ts
vendored
|
@ -11,12 +11,12 @@ declare global {
|
|||
PROD: boolean;
|
||||
NODE_ENV: 'development' | 'production';
|
||||
VUE_APP_URL_BASE_API: string;
|
||||
VUE_APP_ENDPOINT_REST?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Window {
|
||||
BASE_PATH: string;
|
||||
REST_ENDPOINT: string;
|
||||
}
|
||||
|
||||
namespace JSX {
|
||||
|
|
|
@ -5,12 +5,16 @@ import { defineStore } from 'pinia';
|
|||
import Vue from 'vue';
|
||||
import { useNodeTypesStore } from './nodeTypes';
|
||||
|
||||
const { VUE_APP_URL_BASE_API, VUE_APP_ENDPOINT_REST } = import.meta.env;
|
||||
const { VUE_APP_URL_BASE_API } = import.meta.env;
|
||||
|
||||
export const useRootStore = defineStore(STORES.ROOT, {
|
||||
state: (): RootState => ({
|
||||
baseUrl:
|
||||
VUE_APP_URL_BASE_API ?? (window.BASE_PATH === '/{{BASE_PATH}}/' ? '/' : window.BASE_PATH),
|
||||
restEndpoint:
|
||||
!window.REST_ENDPOINT || window.REST_ENDPOINT === '{{REST_ENDPOINT}}'
|
||||
? 'rest'
|
||||
: window.REST_ENDPOINT,
|
||||
defaultLocale: 'en',
|
||||
endpointWebhook: 'webhook',
|
||||
endpointWebhookTest: 'webhook-test',
|
||||
|
@ -41,20 +45,12 @@ export const useRootStore = defineStore(STORES.ROOT, {
|
|||
},
|
||||
|
||||
getRestUrl(): string {
|
||||
let endpoint = 'rest';
|
||||
if (VUE_APP_ENDPOINT_REST) {
|
||||
endpoint = VUE_APP_ENDPOINT_REST;
|
||||
}
|
||||
return `${this.baseUrl}${endpoint}`;
|
||||
return `${this.baseUrl}${this.restEndpoint}`;
|
||||
},
|
||||
|
||||
getRestApiContext(): IRestApiContext {
|
||||
let endpoint = 'rest';
|
||||
if (VUE_APP_ENDPOINT_REST) {
|
||||
endpoint = VUE_APP_ENDPOINT_REST;
|
||||
}
|
||||
return {
|
||||
baseUrl: `${this.baseUrl}${endpoint}`,
|
||||
baseUrl: this.getRestUrl,
|
||||
sessionId: this.sessionId,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import { computed, reactive } from 'vue';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { EnterpriseEditionFeature } from '@/constants';
|
||||
import { useRootStore } from '@/stores/n8nRootStore';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { initSSO } from '@/api/sso';
|
||||
import * as ssoApi from '@/api/sso';
|
||||
import { SamlPreferences } from '@/Interface';
|
||||
import { updateCurrentUser } from '@/api/users';
|
||||
import { useUsersStore } from '@/stores/users';
|
||||
|
||||
export const useSSOStore = defineStore('sso', () => {
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const usersStore = useUsersStore();
|
||||
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
|
@ -19,7 +23,22 @@ export const useSSOStore = defineStore('sso', () => {
|
|||
state.loading = loading;
|
||||
};
|
||||
|
||||
const isSamlLoginEnabled = computed(() => settingsStore.isSamlLoginEnabled);
|
||||
const isSamlLoginEnabled = computed({
|
||||
get: () => settingsStore.isSamlLoginEnabled,
|
||||
set: (value: boolean) => {
|
||||
settingsStore.setSettings({
|
||||
...settingsStore.settings,
|
||||
sso: {
|
||||
...settingsStore.settings.sso,
|
||||
saml: {
|
||||
...settingsStore.settings.sso.saml,
|
||||
loginEnabled: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
toggleLoginEnabled(value);
|
||||
},
|
||||
});
|
||||
const isEnterpriseSamlEnabled = computed(() =>
|
||||
settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Saml),
|
||||
);
|
||||
|
@ -31,12 +50,35 @@ export const useSSOStore = defineStore('sso', () => {
|
|||
isDefaultAuthenticationSaml.value,
|
||||
);
|
||||
|
||||
const getSSORedirectUrl = () => initSSO(rootStore.getRestApiContext);
|
||||
const getSSORedirectUrl = () => ssoApi.initSSO(rootStore.getRestApiContext);
|
||||
|
||||
const toggleLoginEnabled = (enabled: boolean) =>
|
||||
ssoApi.toggleSamlConfig(rootStore.getRestApiContext, { loginEnabled: enabled });
|
||||
|
||||
const getSamlMetadata = () => ssoApi.getSamlMetadata(rootStore.getRestApiContext);
|
||||
const getSamlConfig = () => ssoApi.getSamlConfig(rootStore.getRestApiContext);
|
||||
const saveSamlConfig = (config: SamlPreferences) =>
|
||||
ssoApi.saveSamlConfig(rootStore.getRestApiContext, config);
|
||||
const testSamlConfig = () => ssoApi.testSamlConfig(rootStore.getRestApiContext);
|
||||
|
||||
const updateUser = async (params: { firstName: string; lastName: string }) =>
|
||||
updateCurrentUser(rootStore.getRestApiContext, {
|
||||
id: usersStore.currentUser!.id,
|
||||
email: usersStore.currentUser!.email!,
|
||||
...params,
|
||||
});
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
setLoading,
|
||||
isSamlLoginEnabled,
|
||||
isEnterpriseSamlEnabled,
|
||||
showSsoLoginButton,
|
||||
getSSORedirectUrl,
|
||||
getSamlMetadata,
|
||||
getSamlConfig,
|
||||
saveSamlConfig,
|
||||
testSamlConfig,
|
||||
updateUser,
|
||||
};
|
||||
});
|
||||
|
|
124
packages/editor-ui/src/utils/__tests__/userUtils.test.ts
Normal file
124
packages/editor-ui/src/utils/__tests__/userUtils.test.ts
Normal file
|
@ -0,0 +1,124 @@
|
|||
import { beforeAll } from 'vitest';
|
||||
import { setActivePinia, createPinia } from 'pinia';
|
||||
import { isAuthorized } from '@/utils';
|
||||
import { useSettingsStore } from '@/stores/settings';
|
||||
import { useSSOStore } from '@/stores/sso';
|
||||
import { IN8nUISettings, IUser, UserManagementAuthenticationMethod } from '@/Interface';
|
||||
import { routes } from '@/router';
|
||||
import { VIEWS } from '@/constants';
|
||||
|
||||
const DEFAULT_SETTINGS: IN8nUISettings = {
|
||||
allowedModules: {},
|
||||
communityNodesEnabled: false,
|
||||
defaultLocale: '',
|
||||
endpointWebhook: '',
|
||||
endpointWebhookTest: '',
|
||||
enterprise: {
|
||||
advancedExecutionFilters: false,
|
||||
sharing: false,
|
||||
ldap: false,
|
||||
saml: false,
|
||||
logStreaming: false,
|
||||
},
|
||||
executionMode: '',
|
||||
executionTimeout: 0,
|
||||
hideUsagePage: false,
|
||||
hiringBannerEnabled: false,
|
||||
instanceId: '',
|
||||
isNpmAvailable: false,
|
||||
license: { environment: 'production' },
|
||||
logLevel: 'info',
|
||||
maxExecutionTimeout: 0,
|
||||
oauthCallbackUrls: { oauth1: '', oauth2: '' },
|
||||
onboardingCallPromptEnabled: false,
|
||||
personalizationSurveyEnabled: false,
|
||||
posthog: {
|
||||
apiHost: '',
|
||||
apiKey: '',
|
||||
autocapture: false,
|
||||
debug: false,
|
||||
disableSessionRecording: false,
|
||||
enabled: false,
|
||||
},
|
||||
publicApi: { enabled: false, latestVersion: 0, path: '', swaggerUi: { enabled: false } },
|
||||
pushBackend: 'sse',
|
||||
saveDataErrorExecution: '',
|
||||
saveDataSuccessExecution: '',
|
||||
saveManualExecutions: false,
|
||||
sso: {
|
||||
ldap: { loginEnabled: false, loginLabel: '' },
|
||||
saml: { loginEnabled: false, loginLabel: '' },
|
||||
},
|
||||
telemetry: { enabled: false },
|
||||
templates: { enabled: false, host: '' },
|
||||
timezone: '',
|
||||
urlBaseEditor: '',
|
||||
urlBaseWebhook: '',
|
||||
userManagement: {
|
||||
enabled: false,
|
||||
smtpSetup: false,
|
||||
authenticationMethod: UserManagementAuthenticationMethod.Email,
|
||||
},
|
||||
versionCli: '',
|
||||
versionNotifications: {
|
||||
enabled: false,
|
||||
endpoint: '',
|
||||
infoUrl: '',
|
||||
},
|
||||
workflowCallerPolicyDefaultOption: 'any',
|
||||
workflowTagsDisabled: false,
|
||||
deployment: {
|
||||
type: 'default',
|
||||
},
|
||||
};
|
||||
|
||||
const DEFAULT_USER: IUser = {
|
||||
id: '1',
|
||||
isPending: false,
|
||||
isDefaultUser: true,
|
||||
isOwner: false,
|
||||
isPendingUser: false,
|
||||
globalRole: {
|
||||
name: 'default',
|
||||
id: '1',
|
||||
createdAt: new Date(),
|
||||
},
|
||||
};
|
||||
|
||||
describe('userUtils', () => {
|
||||
let settingsStore: ReturnType<typeof useSettingsStore>;
|
||||
let ssoStore: ReturnType<typeof useSSOStore>;
|
||||
|
||||
describe('isAuthorized', () => {
|
||||
beforeAll(() => {
|
||||
setActivePinia(createPinia());
|
||||
settingsStore = useSettingsStore();
|
||||
ssoStore = useSSOStore();
|
||||
});
|
||||
|
||||
it('should check SSO settings route permissions', () => {
|
||||
const ssoSettingsPermissions = routes
|
||||
.find((route) => route.path.startsWith('/settings'))
|
||||
?.children?.find((route) => route.name === VIEWS.SSO_SETTINGS)?.meta?.permissions;
|
||||
|
||||
const user: IUser = {
|
||||
...DEFAULT_USER,
|
||||
isDefaultUser: false,
|
||||
isOwner: true,
|
||||
globalRole: {
|
||||
...DEFAULT_USER.globalRole,
|
||||
id: '1',
|
||||
name: 'owner',
|
||||
createdAt: new Date(),
|
||||
},
|
||||
};
|
||||
|
||||
settingsStore.setSettings({
|
||||
...DEFAULT_SETTINGS,
|
||||
enterprise: { ...DEFAULT_SETTINGS.enterprise, saml: true },
|
||||
});
|
||||
|
||||
expect(isAuthorized(ssoSettingsPermissions, user)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
1
packages/editor-ui/src/utils/typeHelpers.ts
Normal file
1
packages/editor-ui/src/utils/typeHelpers.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
44
packages/editor-ui/src/views/SamlOnboarding.vue
Normal file
44
packages/editor-ui/src/views/SamlOnboarding.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { IFormBoxConfig } from 'n8n-design-system';
|
||||
import AuthView from '@/views/AuthView.vue';
|
||||
import { i18n as locale } from '@/plugins/i18n';
|
||||
import { useSSOStore } from '@/stores/sso';
|
||||
|
||||
const ssoStore = useSSOStore();
|
||||
|
||||
const loading = ref(false);
|
||||
const FORM_CONFIG: IFormBoxConfig = reactive({
|
||||
title: locale.baseText('auth.signup.setupYourAccount'),
|
||||
buttonText: locale.baseText('auth.signup.finishAccountSetup'),
|
||||
inputs: [
|
||||
{
|
||||
name: 'firstName',
|
||||
properties: {
|
||||
label: locale.baseText('auth.firstName'),
|
||||
maxlength: 32,
|
||||
required: true,
|
||||
autocomplete: 'given-name',
|
||||
capitalize: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'lastName',
|
||||
properties: {
|
||||
label: locale.baseText('auth.lastName'),
|
||||
maxlength: 32,
|
||||
required: true,
|
||||
autocomplete: 'family-name',
|
||||
capitalize: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const onSubmit = (values: { firstName: string; lastName: string }) => {
|
||||
ssoStore.updateUser(values);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthView :form="FORM_CONFIG" :formLoading="loading" @submit="onSubmit" />
|
||||
</template>
|
174
packages/editor-ui/src/views/SettingsSso.vue
Normal file
174
packages/editor-ui/src/views/SettingsSso.vue
Normal file
|
@ -0,0 +1,174 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, ref, onBeforeMount } from 'vue';
|
||||
import { Notification } from 'element-ui';
|
||||
import { useSSOStore } from '@/stores/sso';
|
||||
import { i18n as locale } from '@/plugins/i18n';
|
||||
import CopyInput from '@/components/CopyInput.vue';
|
||||
|
||||
const ssoStore = useSSOStore();
|
||||
|
||||
const ssoActivatedLabel = computed(() =>
|
||||
ssoStore.isSamlLoginEnabled
|
||||
? locale.baseText('settings.sso.activated')
|
||||
: locale.baseText('settings.sso.deactivated'),
|
||||
);
|
||||
const ssoSettingsSaved = ref(false);
|
||||
const metadata = ref();
|
||||
const redirectUrl = ref();
|
||||
const entityId = ref();
|
||||
|
||||
const getSamlConfig = async () => {
|
||||
const config = await ssoStore.getSamlConfig();
|
||||
entityId.value = config?.entityID;
|
||||
redirectUrl.value = config?.returnUrl;
|
||||
metadata.value = config?.metadata;
|
||||
ssoSettingsSaved.value = !!config?.metadata;
|
||||
};
|
||||
|
||||
const onSave = async () => {
|
||||
try {
|
||||
await ssoStore.saveSamlConfig({ metadata: metadata.value });
|
||||
await getSamlConfig();
|
||||
} catch (error) {
|
||||
Notification.error({
|
||||
title: 'Error',
|
||||
message: error.message,
|
||||
position: 'bottom-right',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onTest = async () => {
|
||||
try {
|
||||
const url = await ssoStore.testSamlConfig();
|
||||
window.open(url, '_blank');
|
||||
} catch (error) {
|
||||
Notification.error({
|
||||
title: 'Error',
|
||||
message: error.message,
|
||||
position: 'bottom-right',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeMount(async () => {
|
||||
try {
|
||||
await getSamlConfig();
|
||||
} catch (error) {
|
||||
Notification.error({
|
||||
title: 'Error',
|
||||
message: error.message,
|
||||
position: 'bottom-right',
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n8n-heading size="2xlarge">{{ locale.baseText('settings.sso.title') }}</n8n-heading>
|
||||
<div :class="$style.top">
|
||||
<n8n-heading size="medium">{{ locale.baseText('settings.sso.subtitle') }}</n8n-heading>
|
||||
<n8n-tooltip :disabled="ssoStore.isSamlLoginEnabled">
|
||||
<template #content>
|
||||
<span>
|
||||
{{ locale.baseText('settings.sso.activation.tooltip') }}
|
||||
</span>
|
||||
</template>
|
||||
<el-switch
|
||||
v-model="ssoStore.isSamlLoginEnabled"
|
||||
:disabled="!ssoSettingsSaved"
|
||||
:class="$style.switch"
|
||||
:inactive-text="ssoActivatedLabel"
|
||||
/>
|
||||
</n8n-tooltip>
|
||||
</div>
|
||||
<n8n-info-tip>
|
||||
<i18n :class="$style.count" path="settings.sso.info">
|
||||
<template #link>
|
||||
<a href="https://docs.n8n.io/user-management/sso/" target="_blank">
|
||||
{{ locale.baseText('settings.sso.info.link') }}
|
||||
</a>
|
||||
</template>
|
||||
</i18n>
|
||||
</n8n-info-tip>
|
||||
<div :class="$style.group">
|
||||
<label>{{ locale.baseText('settings.sso.settings.redirectUrl.label') }}</label>
|
||||
<CopyInput
|
||||
:class="$style.copyInput"
|
||||
:value="redirectUrl"
|
||||
:copy-button-text="locale.baseText('generic.clickToCopy')"
|
||||
:toast-title="locale.baseText('settings.sso.settings.redirectUrl.copied')"
|
||||
/>
|
||||
<small>{{ locale.baseText('settings.sso.settings.redirectUrl.help') }}</small>
|
||||
</div>
|
||||
<div :class="$style.group">
|
||||
<label>{{ locale.baseText('settings.sso.settings.entityId.label') }}</label>
|
||||
<CopyInput
|
||||
:class="$style.copyInput"
|
||||
:value="entityId"
|
||||
:copy-button-text="locale.baseText('generic.clickToCopy')"
|
||||
:toast-title="locale.baseText('settings.sso.settings.entityId.copied')"
|
||||
/>
|
||||
<small>{{ locale.baseText('settings.sso.settings.entityId.help') }}</small>
|
||||
</div>
|
||||
<div :class="$style.group">
|
||||
<label>{{ locale.baseText('settings.sso.settings.ips.label') }}</label>
|
||||
<n8n-input v-model="metadata" type="textarea" />
|
||||
<small>{{ locale.baseText('settings.sso.settings.ips.help') }}</small>
|
||||
</div>
|
||||
<div :class="$style.buttons">
|
||||
<n8n-button :disabled="!ssoSettingsSaved" type="tertiary" @click="onTest">
|
||||
{{ locale.baseText('settings.sso.settings.test') }}
|
||||
</n8n-button>
|
||||
<n8n-button :disabled="!metadata" @click="onSave">
|
||||
{{ locale.baseText('settings.sso.settings.save') }}
|
||||
</n8n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-2xl) 0 var(--spacing-xl);
|
||||
}
|
||||
|
||||
.switch {
|
||||
span {
|
||||
font-size: var(--font-size-2xs);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding: var(--spacing-2xl) 0 var(--spacing-3xl);
|
||||
|
||||
button {
|
||||
margin: 0 var(--spacing-s) 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.group {
|
||||
padding: var(--spacing-xl) 0 0;
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
font-size: var(--font-size-s);
|
||||
font-weight: var(--font-weight-bold);
|
||||
padding: 0 0 var(--spacing-2xs);
|
||||
}
|
||||
|
||||
small {
|
||||
display: block;
|
||||
padding: var(--spacing-2xs) 0 0;
|
||||
font-size: var(--font-size-2xs);
|
||||
color: var(--color-text-base);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -47,13 +47,6 @@
|
|||
@copyInviteLink="onCopyInviteLink"
|
||||
/>
|
||||
</div>
|
||||
<feature-coming-soon
|
||||
v-for="fakeDoorFeature in fakeDoorFeatures"
|
||||
:key="fakeDoorFeature.id"
|
||||
:featureId="fakeDoorFeature.id"
|
||||
class="pb-3xl"
|
||||
showTitle
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -61,8 +54,7 @@
|
|||
import { EnterpriseEditionFeature, INVITE_USER_MODAL_KEY, VIEWS } from '@/constants';
|
||||
|
||||
import PageAlert from '../components/PageAlert.vue';
|
||||
import FeatureComingSoon from '@/components/FeatureComingSoon.vue';
|
||||
import { IFakeDoor, IUser, IUserListAction } from '@/Interface';
|
||||
import { IUser, IUserListAction } from '@/Interface';
|
||||
import mixins from 'vue-typed-mixins';
|
||||
import { showMessage } from '@/mixins/showMessage';
|
||||
import { copyPaste } from '@/mixins/copyPaste';
|
||||
|
@ -77,7 +69,6 @@ export default mixins(showMessage, copyPaste).extend({
|
|||
name: 'SettingsUsersView',
|
||||
components: {
|
||||
PageAlert,
|
||||
FeatureComingSoon,
|
||||
},
|
||||
async mounted() {
|
||||
if (!this.usersStore.showUMSetupWarning) {
|
||||
|
@ -107,9 +98,6 @@ export default mixins(showMessage, copyPaste).extend({
|
|||
},
|
||||
];
|
||||
},
|
||||
fakeDoorFeatures(): IFakeDoor[] {
|
||||
return this.uiStore.getFakeDoorByLocation('settings/users');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
redirectToSetup() {
|
||||
|
|
|
@ -4,6 +4,7 @@ import monacoEditorPlugin from 'vite-plugin-monaco-editor';
|
|||
import path, { resolve } from 'path';
|
||||
import { defineConfig, mergeConfig } from 'vite';
|
||||
import { defineConfig as defineVitestConfig } from 'vitest/config';
|
||||
import { sentryVitePlugin } from '@sentry/vite-plugin';
|
||||
|
||||
import packageJSON from './package.json';
|
||||
|
||||
|
@ -43,6 +44,72 @@ const publicPath = process.env.VUE_APP_PUBLIC_PATH || '/';
|
|||
|
||||
const { NODE_ENV } = process.env;
|
||||
|
||||
const alias = [
|
||||
{ find: '@', replacement: resolve(__dirname, 'src') },
|
||||
{ find: 'stream', replacement: 'stream-browserify' },
|
||||
{
|
||||
find: /^n8n-design-system\//,
|
||||
replacement: resolve(__dirname, '..', 'design-system', 'src') + '/',
|
||||
},
|
||||
...['orderBy', 'camelCase', 'cloneDeep', 'isEqual', 'startCase'].map((name) => ({
|
||||
find: new RegExp(`^lodash.${name}$`, 'i'),
|
||||
replacement: require.resolve(`lodash-es/${name}`),
|
||||
})),
|
||||
{
|
||||
find: /^lodash\.(.+)$/,
|
||||
replacement: 'lodash-es/$1',
|
||||
},
|
||||
{
|
||||
find: 'vue2-boring-avatars',
|
||||
replacement: require.resolve('vue2-boring-avatars'),
|
||||
},
|
||||
{
|
||||
find: /element-ui\/(packages|lib)\/button$/,
|
||||
replacement: path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'design-system/src/components/N8nButton/overrides/ElButton.ts',
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
// https://github.com/vitest-dev/vitest/discussions/1806
|
||||
if (NODE_ENV === 'test') {
|
||||
alias.push({
|
||||
find: /^monaco-editor$/,
|
||||
replacement: __dirname + '/node_modules/monaco-editor/esm/vs/editor/editor.api',
|
||||
});
|
||||
}
|
||||
|
||||
const plugins = [
|
||||
vue(),
|
||||
legacy({
|
||||
targets: ['defaults', 'not IE 11'],
|
||||
}),
|
||||
monacoEditorPlugin({
|
||||
publicPath: 'assets/monaco-editor',
|
||||
customDistPath: (root: string, buildOutDir: string, base: string) =>
|
||||
`${root}/${buildOutDir}/assets/monaco-editor`,
|
||||
}),
|
||||
];
|
||||
|
||||
const { SENTRY_AUTH_TOKEN: authToken, RELEASE: release } = process.env;
|
||||
if (release && authToken) {
|
||||
plugins.push(
|
||||
sentryVitePlugin({
|
||||
org: 'n8nio',
|
||||
project: 'instance-frontend',
|
||||
// Specify the directory containing build artifacts
|
||||
include: './dist',
|
||||
// Auth tokens can be obtained from https://sentry.io/settings/account/api/auth-tokens/
|
||||
// and needs the `project:releases` and `org:read` scopes
|
||||
authToken,
|
||||
telemetry: false,
|
||||
release,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export default mergeConfig(
|
||||
defineConfig({
|
||||
define: {
|
||||
|
@ -51,47 +118,8 @@ export default mergeConfig(
|
|||
...(NODE_ENV === 'development' ? { process: { env: {} } } : {}),
|
||||
BASE_PATH: `'${publicPath}'`,
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
legacy({
|
||||
targets: ['defaults', 'not IE 11'],
|
||||
}),
|
||||
monacoEditorPlugin({
|
||||
publicPath: 'assets/monaco-editor',
|
||||
customDistPath: (root: string, buildOutDir: string, base: string) =>
|
||||
`${root}/${buildOutDir}/assets/monaco-editor`,
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: [
|
||||
{ find: '@', replacement: resolve(__dirname, 'src') },
|
||||
{ find: 'stream', replacement: 'stream-browserify' },
|
||||
{
|
||||
find: /^n8n-design-system\//,
|
||||
replacement: resolve(__dirname, '..', 'design-system', 'src') + '/',
|
||||
},
|
||||
...['orderBy', 'camelCase', 'cloneDeep', 'isEqual', 'startCase'].map((name) => ({
|
||||
find: new RegExp(`^lodash.${name}$`, 'i'),
|
||||
replacement: require.resolve(`lodash-es/${name}`),
|
||||
})),
|
||||
{
|
||||
find: /^lodash\.(.+)$/,
|
||||
replacement: 'lodash-es/$1',
|
||||
},
|
||||
{
|
||||
find: 'vue2-boring-avatars',
|
||||
replacement: require.resolve('vue2-boring-avatars'),
|
||||
},
|
||||
{
|
||||
find: /element-ui\/(packages|lib)\/button$/,
|
||||
replacement: path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'design-system/src/components/N8nButton/overrides/ElButton.ts',
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins,
|
||||
resolve: { alias },
|
||||
base: publicPath,
|
||||
envPrefix: 'VUE_APP',
|
||||
css: {
|
||||
|
@ -103,8 +131,10 @@ export default mergeConfig(
|
|||
},
|
||||
build: {
|
||||
assetsInlineLimit: 0,
|
||||
sourcemap: false,
|
||||
minify: !!release,
|
||||
sourcemap: !!release,
|
||||
rollupOptions: {
|
||||
treeshake: !!release,
|
||||
output: {
|
||||
manualChunks: {
|
||||
vendor: vendorChunks,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-node-dev",
|
||||
"version": "0.100.0",
|
||||
"version": "0.101.0",
|
||||
"description": "CLI to simplify n8n credentials/node development",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
|
|
@ -93,6 +93,42 @@ export class CompareDatasets implements INodeType {
|
|||
description: 'Output contains all data (but structure more complex)',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [1, 2],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'When There Are Differences',
|
||||
name: 'resolve',
|
||||
type: 'options',
|
||||
default: 'includeBoth',
|
||||
options: [
|
||||
{
|
||||
name: 'Use Input A Version',
|
||||
value: 'preferInput1',
|
||||
},
|
||||
{
|
||||
name: 'Use Input B Version',
|
||||
value: 'preferInput2',
|
||||
},
|
||||
{
|
||||
name: 'Use a Mix of Versions',
|
||||
value: 'mix',
|
||||
description: 'Output uses different inputs for different fields',
|
||||
},
|
||||
{
|
||||
name: 'Include Both Versions',
|
||||
value: 'includeBoth',
|
||||
description: 'Output contains all data (but structure more complex)',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
hide: {
|
||||
'@version': [1, 2],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Fuzzy Compare',
|
||||
|
|
|
@ -5,6 +5,8 @@ import get from 'lodash.get';
|
|||
import intersection from 'lodash.intersection';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import omit from 'lodash.omit';
|
||||
import unset from 'lodash.unset';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import set from 'lodash.set';
|
||||
import union from 'lodash.union';
|
||||
import { fuzzyCompare } from '../../utils/utilities';
|
||||
|
@ -65,7 +67,7 @@ function compareItems(
|
|||
const different: IDataObject = {};
|
||||
const skipped: IDataObject = {};
|
||||
|
||||
differentKeys.forEach((key) => {
|
||||
differentKeys.forEach((key, i) => {
|
||||
const processNullishValue = processNullishValueFunction(options.nodeVersion as number);
|
||||
|
||||
switch (options.resolve) {
|
||||
|
@ -76,18 +78,63 @@ function compareItems(
|
|||
different[key] = processNullishValue(item2.json[key]);
|
||||
break;
|
||||
default:
|
||||
const input1 = processNullishValue(item1.json[key]);
|
||||
const input2 = processNullishValue(item2.json[key]);
|
||||
let input1 = processNullishValue(item1.json[key]);
|
||||
let input2 = processNullishValue(item2.json[key]);
|
||||
|
||||
let [firstInputName, secondInputName] = ['input1', 'input2'];
|
||||
if ((options.nodeVersion as number) >= 2) {
|
||||
[firstInputName, secondInputName] = ['inputA', 'inputB'];
|
||||
}
|
||||
|
||||
if (skipFields.includes(key)) {
|
||||
skipped[key] = { [firstInputName]: input1, [secondInputName]: input2 };
|
||||
} else {
|
||||
if (
|
||||
(options.nodeVersion as number) >= 2.1 &&
|
||||
!options.disableDotNotation &&
|
||||
!skipFields.some((field) => field === key)
|
||||
) {
|
||||
const skippedFieldsWithDotNotation = skipFields.filter(
|
||||
(field) => field.startsWith(key) && field.includes('.'),
|
||||
);
|
||||
|
||||
input1 = cloneDeep(input1);
|
||||
input2 = cloneDeep(input2);
|
||||
|
||||
if (
|
||||
skippedFieldsWithDotNotation.length &&
|
||||
(typeof input1 !== 'object' || typeof input2 !== 'object')
|
||||
) {
|
||||
throw new Error(
|
||||
`The field \'${key}\' in item ${i} is not an object. It is not possible to use dot notation.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (skipped[key] === undefined && skippedFieldsWithDotNotation.length) {
|
||||
skipped[key] = { [firstInputName]: {}, [secondInputName]: {} };
|
||||
}
|
||||
|
||||
for (const skippedField of skippedFieldsWithDotNotation) {
|
||||
const nestedField = skippedField.replace(`${key}.`, '');
|
||||
set(
|
||||
(skipped[key] as IDataObject)[firstInputName] as IDataObject,
|
||||
nestedField,
|
||||
get(input1, nestedField),
|
||||
);
|
||||
set(
|
||||
(skipped[key] as IDataObject)[secondInputName] as IDataObject,
|
||||
nestedField,
|
||||
get(input2, nestedField),
|
||||
);
|
||||
|
||||
unset(input1, nestedField);
|
||||
unset(input2, nestedField);
|
||||
}
|
||||
|
||||
different[key] = { [firstInputName]: input1, [secondInputName]: input2 };
|
||||
} else {
|
||||
if (skipFields.includes(key)) {
|
||||
skipped[key] = { [firstInputName]: input1, [secondInputName]: input2 };
|
||||
} else {
|
||||
different[key] = { [firstInputName]: input1, [secondInputName]: input2 };
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -205,6 +252,15 @@ export function findMatches(
|
|||
const multipleMatches = (options.multipleMatches as string) || 'first';
|
||||
const skipFields = ((options.skipFields as string) || '').split(',').map((field) => field.trim());
|
||||
|
||||
if (disableDotNotation && skipFields.some((field) => field.includes('.'))) {
|
||||
const fieldToSkip = skipFields.find((field) => field.includes('.'));
|
||||
throw new Error(
|
||||
`Dot notation is disabled, but field to skip comparing '${
|
||||
fieldToSkip as string
|
||||
}' contains dot`,
|
||||
);
|
||||
}
|
||||
|
||||
const filteredData = {
|
||||
matched: [] as EntryMatches[],
|
||||
unmatched1: [] as INodeExecutionData[],
|
||||
|
@ -265,8 +321,18 @@ export function findMatches(
|
|||
let entryFromInput2 = match.json;
|
||||
|
||||
if (skipFields.length) {
|
||||
entryFromInput1 = omit(entryFromInput1, skipFields);
|
||||
entryFromInput2 = omit(entryFromInput2, skipFields);
|
||||
if (disableDotNotation || !skipFields.some((field) => field.includes('.'))) {
|
||||
entryFromInput1 = omit(entryFromInput1, skipFields);
|
||||
entryFromInput2 = omit(entryFromInput2, skipFields);
|
||||
} else {
|
||||
entryFromInput1 = cloneDeep(entryFromInput1);
|
||||
entryFromInput2 = cloneDeep(entryFromInput2);
|
||||
|
||||
skipFields.forEach((field) => {
|
||||
unset(entryFromInput1, field);
|
||||
unset(entryFromInput2, field);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let isItemsEqual = true;
|
||||
|
|
|
@ -0,0 +1,897 @@
|
|||
{
|
||||
"name": "skip fields dot test",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "eb18dcd4-3251-493f-ae3f-d4a89b16cee8",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 800]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "return [\n\t{\n\t\tid: 1,\n\t\tdata: {\n\t\t\tname: 'John',\n\t\t\tage: 31,\n active: true,\n\t\t},\n\t\taddress: {\n\t\t\tcity: 'New York',\n\t\t\tcountry: 'USA',\n\t\t},\n\t},\n\t{\n\t\tid: 2,\n\t\tdata: {\n\t\t\tname: 'Jane',\n\t\t\tage: 26,\n active: true,\n\t\t},\n\t\taddress: {\n\t\t\tcity: 'London',\n\t\t\tcountry: 'UK',\n\t\t},\n\t},\n\t{\n\t\tid: 3,\n\t\tdata: {\n\t\t\tname: 'Jack',\n\t\t\tage: 36,\n active: true,\n\t\t},\n\t\taddress: {\n\t\t\tcity: 'Paris',\n\t\t\tcountry: 'France',\n\t\t},\n\t},\n]"
|
||||
},
|
||||
"id": "77327bfe-a4a8-49b8-a237-2f264536e5e3",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 1,
|
||||
"position": [660, 640]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "return [\n\t{\n\t\tid: 1,\n\t\tdata: {\n\t\t\tname: 'John',\n\t\t\tage: 30,\n active: false,\n\t\t},\n\t\taddress: {\n\t\t\tcity: 'New York',\n\t\t\tcountry: 'us',\n \n\t\t},\n\t},\n\t{\n\t\tid: 2,\n\t\tdata: {\n\t\t\tname: 'Jane',\n\t\t\tage: 25,\n active: false,\n\t\t},\n\t\taddress: {\n\t\t\tcity: 'London',\n\t\t\tcountry: 'uk',\n\t\t},\n\t},\n\t{\n\t\tid: 3,\n\t\tdata: {\n\t\t\tname: 'Jack',\n\t\t\tage: 35,\n active: false,\n\t\t},\n\t\taddress: {\n\t\t\tcity: 'Paris',\n\t\t\tcountry: 'fr',\n\t\t},\n\t},\n]"
|
||||
},
|
||||
"id": "8ea09201-1ed6-4f9c-afb4-1261142a74a3",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 1,
|
||||
"position": [660, 920]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "750c4213-42e0-4e66-96b0-24dcb82d3d67",
|
||||
"name": "Any skipped",
|
||||
"type": "n8n-nodes-base.compareDatasets",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1020, 220]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"skipFields": "data, address"
|
||||
}
|
||||
},
|
||||
"id": "2912ebdb-ddb8-4c53-a357-66e8b20ff947",
|
||||
"name": "skip whole object",
|
||||
"type": "n8n-nodes-base.compareDatasets",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1020, 420]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"skipFields": "data.age, data.active, address.country"
|
||||
}
|
||||
},
|
||||
"id": "9ea72e2e-f062-4ae7-bfb1-9266848d0f3c",
|
||||
"name": "skip each key",
|
||||
"type": "n8n-nodes-base.compareDatasets",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1020, 620]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"skipFields": "address.country"
|
||||
}
|
||||
},
|
||||
"id": "fca0048b-a493-4ca4-861b-2ca8216fbfba",
|
||||
"name": "skipped contain contry",
|
||||
"type": "n8n-nodes-base.compareDatasets",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1040, 1060]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"skipFields": "data, address.country"
|
||||
}
|
||||
},
|
||||
"id": "498b2f65-1117-4ff2-acde-a51049fd9b89",
|
||||
"name": "skip object and key",
|
||||
"type": "n8n-nodes-base.compareDatasets",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1040, 860],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"mergeByFields": {
|
||||
"values": [
|
||||
{
|
||||
"field1": "id",
|
||||
"field2": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"skipFields": "data.age, address.country"
|
||||
}
|
||||
},
|
||||
"id": "77ad3a73-8683-41ed-9040-cef1a2895704",
|
||||
"name": "skip includes age and contry",
|
||||
"type": "n8n-nodes-base.compareDatasets",
|
||||
"typeVersion": 2.1,
|
||||
"position": [1040, 1260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "be74368b-6d04-43ae-ad01-e2efbd2758c3",
|
||||
"name": "Set",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [1340, 240]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "4cb1a37a-ce93-4a3e-b613-8ae9e8265718",
|
||||
"name": "Set1",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [1340, 380]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "ab00a0a0-34be-4a65-a1de-8f69db895b76",
|
||||
"name": "Set2",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [1340, 520]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "b23e2bf9-9a4b-4f87-a21f-cd24c56af3c1",
|
||||
"name": "Set3",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [1340, 660]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "2cf57ec3-72c4-43e2-ad28-6755b19cc8f3",
|
||||
"name": "Set4",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [1340, 800]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "fdf62bd3-5e54-43cd-b70e-be159aa1722f",
|
||||
"name": "Set5",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [1340, 940]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Set": [
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 1
|
||||
},
|
||||
"same": {
|
||||
"id": 1
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "John",
|
||||
"age": 31,
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "New York",
|
||||
"country": "USA"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "New York",
|
||||
"country": "us"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 2
|
||||
},
|
||||
"same": {
|
||||
"id": 2
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "Jane",
|
||||
"age": 26,
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "Jane",
|
||||
"age": 25,
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "London",
|
||||
"country": "UK"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "London",
|
||||
"country": "uk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 3
|
||||
},
|
||||
"same": {
|
||||
"id": 3
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "Jack",
|
||||
"age": 36,
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "Jack",
|
||||
"age": 35,
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "Paris",
|
||||
"country": "France"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "Paris",
|
||||
"country": "fr"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Set1": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": {
|
||||
"name": "John",
|
||||
"age": 31,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "New York",
|
||||
"country": "USA"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": {
|
||||
"name": "Jane",
|
||||
"age": 26,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "London",
|
||||
"country": "UK"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"data": {
|
||||
"name": "Jack",
|
||||
"age": 36,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "Paris",
|
||||
"country": "France"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Set2": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": {
|
||||
"name": "John",
|
||||
"age": 31,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "New York",
|
||||
"country": "USA"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": {
|
||||
"name": "Jane",
|
||||
"age": 26,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "London",
|
||||
"country": "UK"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"data": {
|
||||
"name": "Jack",
|
||||
"age": 36,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "Paris",
|
||||
"country": "France"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Set3": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"data": {
|
||||
"name": "John",
|
||||
"age": 31,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "New York",
|
||||
"country": "USA"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"data": {
|
||||
"name": "Jane",
|
||||
"age": 26,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "London",
|
||||
"country": "UK"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"data": {
|
||||
"name": "Jack",
|
||||
"age": 36,
|
||||
"active": true
|
||||
},
|
||||
"address": {
|
||||
"city": "Paris",
|
||||
"country": "France"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Set5": [
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 1
|
||||
},
|
||||
"same": {
|
||||
"id": 1
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "John",
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "John",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "New York"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "New York"
|
||||
}
|
||||
}
|
||||
},
|
||||
"skipped": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"age": 31
|
||||
},
|
||||
"inputB": {
|
||||
"age": 30
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"country": "USA"
|
||||
},
|
||||
"inputB": {
|
||||
"country": "us"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 2
|
||||
},
|
||||
"same": {
|
||||
"id": 2
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "Jane",
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "Jane",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "London"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "London"
|
||||
}
|
||||
}
|
||||
},
|
||||
"skipped": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"age": 26
|
||||
},
|
||||
"inputB": {
|
||||
"age": 25
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"country": "UK"
|
||||
},
|
||||
"inputB": {
|
||||
"country": "uk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 3
|
||||
},
|
||||
"same": {
|
||||
"id": 3
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "Jack",
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "Jack",
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "Paris"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "Paris"
|
||||
}
|
||||
}
|
||||
},
|
||||
"skipped": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"age": 36
|
||||
},
|
||||
"inputB": {
|
||||
"age": 35
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"country": "France"
|
||||
},
|
||||
"inputB": {
|
||||
"country": "fr"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Set4": [
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 1
|
||||
},
|
||||
"same": {
|
||||
"id": 1
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "John",
|
||||
"age": 31,
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "John",
|
||||
"age": 30,
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "New York"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "New York"
|
||||
}
|
||||
}
|
||||
},
|
||||
"skipped": {
|
||||
"address": {
|
||||
"inputA": {
|
||||
"country": "USA"
|
||||
},
|
||||
"inputB": {
|
||||
"country": "us"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 2
|
||||
},
|
||||
"same": {
|
||||
"id": 2
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "Jane",
|
||||
"age": 26,
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "Jane",
|
||||
"age": 25,
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "London"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "London"
|
||||
}
|
||||
}
|
||||
},
|
||||
"skipped": {
|
||||
"address": {
|
||||
"inputA": {
|
||||
"country": "UK"
|
||||
},
|
||||
"inputB": {
|
||||
"country": "uk"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"keys": {
|
||||
"id": 3
|
||||
},
|
||||
"same": {
|
||||
"id": 3
|
||||
},
|
||||
"different": {
|
||||
"data": {
|
||||
"inputA": {
|
||||
"name": "Jack",
|
||||
"age": 36,
|
||||
"active": true
|
||||
},
|
||||
"inputB": {
|
||||
"name": "Jack",
|
||||
"age": 35,
|
||||
"active": false
|
||||
}
|
||||
},
|
||||
"address": {
|
||||
"inputA": {
|
||||
"city": "Paris"
|
||||
},
|
||||
"inputB": {
|
||||
"city": "Paris"
|
||||
}
|
||||
}
|
||||
},
|
||||
"skipped": {
|
||||
"address": {
|
||||
"inputA": {
|
||||
"country": "France"
|
||||
},
|
||||
"inputB": {
|
||||
"country": "fr"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Any skipped",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "skip whole object",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "skip each key",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "skip object and key",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "skipped contain contry",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "skip includes age and contry",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Any skipped",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "skip whole object",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "skip each key",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "skip object and key",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "skipped contain contry",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
},
|
||||
{
|
||||
"node": "skip includes age and contry",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Any skipped": {
|
||||
"main": [
|
||||
[],
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Set",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"skip whole object": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Set1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"skip each key": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Set2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"skip object and key": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Set3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"skipped contain contry": {
|
||||
"main": [
|
||||
[],
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Set4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"skip includes age and contry": {
|
||||
"main": [
|
||||
[],
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Set5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "853bfbd2-7a94-4859-98e2-ad4df10bf922",
|
||||
"id": "146",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
{
|
||||
"name": "My workflow 10",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "4d3b2f7b-1ae9-432d-b765-c6134cd11afc",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-60, 2060]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"value": "={{ DateTime.fromISO($json.testData) }}",
|
||||
"toFormat": "X",
|
||||
"options": {
|
||||
"toTimezone": "UTC"
|
||||
}
|
||||
},
|
||||
"id": "31ad53db-5cfd-44e5-917b-b90105ae687d",
|
||||
"name": "Date & Time",
|
||||
"type": "n8n-nodes-base.dateTime",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 1960]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"value": "={{ DateTime.fromISO($json.testData).minus({days:1}) }}",
|
||||
"toFormat": "X",
|
||||
"options": {
|
||||
"toTimezone": "UTC"
|
||||
}
|
||||
},
|
||||
"id": "f959c146-2cd3-4bc5-aa55-5b974466ba90",
|
||||
"name": "Date & Time1",
|
||||
"type": "n8n-nodes-base.dateTime",
|
||||
"typeVersion": 1,
|
||||
"position": [400, 2180]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"value": "={{ Number($json.data) }}",
|
||||
"toFormat": "YYYY-MM-DD",
|
||||
"options": {
|
||||
"toTimezone": "UTC"
|
||||
}
|
||||
},
|
||||
"id": "8bf5b635-607e-4207-8b0e-66f0540b103c",
|
||||
"name": "Date & Time2",
|
||||
"type": "n8n-nodes-base.dateTime",
|
||||
"typeVersion": 1,
|
||||
"position": [620, 1960]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"value": "={{ Number($json.data) }}",
|
||||
"toFormat": "YYYY-MM-DD",
|
||||
"options": {
|
||||
"toTimezone": "UTC"
|
||||
}
|
||||
},
|
||||
"id": "350ce053-d415-4a7d-8447-6d7c73758f35",
|
||||
"name": "Date & Time3",
|
||||
"type": "n8n-nodes-base.dateTime",
|
||||
"typeVersion": 1,
|
||||
"position": [620, 2180]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"keepOnlySet": true,
|
||||
"values": {
|
||||
"string": [
|
||||
{
|
||||
"name": "testData",
|
||||
"value": "2023-03-31T06:31:01.000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "b57950a6-3a6a-4e23-8c3b-16abe12bfe16",
|
||||
"name": "Set",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 2,
|
||||
"position": [140, 2060]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Date & Time2": [
|
||||
{
|
||||
"json": {
|
||||
"testData": "2023-03-31T06:31:01.000",
|
||||
"data": "2023-03-31"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Date & Time3": [
|
||||
{
|
||||
"json": {
|
||||
"testData": "2023-03-31T06:31:01.000",
|
||||
"data": "2023-03-30"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Date & Time": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Date & Time2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Date & Time1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Date & Time3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Date & Time",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Date & Time1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "ff99618e-6e8e-49d8-b899-021caff6d01d",
|
||||
"id": "151",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
|
@ -748,9 +748,7 @@ export class HttpRequestV3 implements INodeType {
|
|||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
default: {
|
||||
redirect: {},
|
||||
},
|
||||
default: { redirect: {} },
|
||||
options: [
|
||||
{
|
||||
displayName: 'Redirect',
|
||||
|
@ -779,6 +777,56 @@ export class HttpRequestV3 implements INodeType {
|
|||
],
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [1, 2, 3],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Redirects',
|
||||
name: 'redirect',
|
||||
placeholder: 'Add Redirect',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
default: {
|
||||
redirect: {},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Redirect',
|
||||
name: 'redirect',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Follow Redirects',
|
||||
name: 'followRedirects',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
noDataExpression: true,
|
||||
description: 'Whether to follow all redirects',
|
||||
},
|
||||
{
|
||||
displayName: 'Max Redirects',
|
||||
name: 'maxRedirects',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
followRedirects: [true],
|
||||
},
|
||||
},
|
||||
default: 21,
|
||||
description: 'Max number of redirects to follow',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
hide: {
|
||||
'@version': [1, 2, 3],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Response',
|
||||
|
@ -984,7 +1032,6 @@ export class HttpRequestV3 implements INodeType {
|
|||
'keypair',
|
||||
) as string;
|
||||
const jsonHeadersParameter = this.getNodeParameter('jsonHeaders', itemIndex, '') as string;
|
||||
|
||||
const {
|
||||
redirect,
|
||||
batching,
|
||||
|
@ -1038,13 +1085,14 @@ export class HttpRequestV3 implements INodeType {
|
|||
if (autoDetectResponseFormat || fullResponse) {
|
||||
requestOptions.resolveWithFullResponse = true;
|
||||
}
|
||||
const defaultRedirect = nodeVersion >= 4 && redirect === undefined;
|
||||
|
||||
if (redirect?.redirect?.followRedirects) {
|
||||
if (redirect?.redirect?.followRedirects || defaultRedirect) {
|
||||
requestOptions.followRedirect = true;
|
||||
requestOptions.followAllRedirects = true;
|
||||
}
|
||||
|
||||
if (redirect?.redirect?.maxRedirects) {
|
||||
if (redirect?.redirect?.maxRedirects || defaultRedirect) {
|
||||
requestOptions.maxRedirects = redirect?.redirect?.maxRedirects;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-nodes-base",
|
||||
"version": "0.220.0",
|
||||
"version": "0.221.0",
|
||||
"description": "Base nodes of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-workflow",
|
||||
"version": "0.143.0",
|
||||
"version": "0.144.0",
|
||||
"description": "Workflow base code of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
|
|
@ -7,6 +7,7 @@ const augmentedObjects = new WeakSet<object>();
|
|||
function augment<T>(value: T): T {
|
||||
if (typeof value !== 'object' || value === null || value instanceof RegExp) return value;
|
||||
if (value instanceof Date) return new Date(value.valueOf()) as T;
|
||||
if (value instanceof Uint8Array) return value.slice() as T;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
if (Array.isArray(value)) return augmentArray(value) as T;
|
||||
|
|
|
@ -1677,6 +1677,7 @@ export interface IWorkflowExecuteAdditionalData {
|
|||
) => Promise<any>;
|
||||
// hooks?: IWorkflowExecuteHooks;
|
||||
executionId?: string;
|
||||
restartExecutionId?: string;
|
||||
hooks?: WorkflowHooks;
|
||||
httpResponse?: express.Response;
|
||||
httpRequest?: express.Request;
|
||||
|
|
|
@ -33,7 +33,8 @@ export class WorkflowHooks {
|
|||
this.executionId = executionId;
|
||||
this.workflowData = workflowData;
|
||||
this.sessionId = optionalParameters.sessionId;
|
||||
this.retryOf = optionalParameters.retryOf;
|
||||
// retryOf might be `null` from TypeORM
|
||||
this.retryOf = optionalParameters.retryOf ?? undefined;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
|
||||
|
|
12
patches/@sentry__cli@2.17.0.patch
Normal file
12
patches/@sentry__cli@2.17.0.patch
Normal file
|
@ -0,0 +1,12 @@
|
|||
diff --git a/js/helper.js b/js/helper.js
|
||||
index a4a7a61f0e226d7cb45f8d5db34c35cc9e62cf96..38fd3ec2214970b1c5dd22fdff902ff3b5eeddde 100644
|
||||
--- a/js/helper.js
|
||||
+++ b/js/helper.js
|
||||
@@ -15,6 +15,7 @@ function getBinaryPath() {
|
||||
const parts = [];
|
||||
parts.push(__dirname);
|
||||
parts.push('..');
|
||||
+ parts.push('bin');
|
||||
parts.push(`sentry-cli${process.platform === 'win32' ? '.exe' : ''}`);
|
||||
return path.resolve(...parts);
|
||||
}
|
310
pnpm-lock.yaml
310
pnpm-lock.yaml
|
@ -16,11 +16,14 @@ overrides:
|
|||
prettier: ^2.8.3
|
||||
tslib: ^2.5.0
|
||||
ts-node: ^10.9.1
|
||||
typescript: ^5.0.2
|
||||
typescript: ^5.0.3
|
||||
cpy@8>globby: ^11.1.0
|
||||
qqjs>globby: ^11.1.0
|
||||
|
||||
patchedDependencies:
|
||||
'@sentry/cli@2.17.0':
|
||||
hash: nchnoezkq6p37qaiku3vrpwraq
|
||||
path: patches/@sentry__cli@2.17.0.patch
|
||||
element-ui@2.15.12:
|
||||
hash: prckukfdop5sl2her6de25cod4
|
||||
path: patches/element-ui@2.15.12.patch
|
||||
|
@ -67,8 +70,8 @@ importers:
|
|||
specifier: ^29.5.0
|
||||
version: 29.5.0
|
||||
jest-mock-extended:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3(jest@29.5.0)(typescript@5.0.2)
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4(jest@29.5.0)(typescript@5.0.3)
|
||||
nock:
|
||||
specifier: ^13.2.9
|
||||
version: 13.2.9
|
||||
|
@ -94,17 +97,17 @@ importers:
|
|||
specifier: ^6.3.3
|
||||
version: 6.3.3
|
||||
ts-jest:
|
||||
specifier: ^29.0.5
|
||||
version: 29.0.5(@babel/core@7.20.12)(jest@29.5.0)(typescript@5.0.2)
|
||||
specifier: ^29.1.0
|
||||
version: 29.1.0(@babel/core@7.20.12)(jest@29.5.0)(typescript@5.0.3)
|
||||
tsc-watch:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0(typescript@5.0.2)
|
||||
version: 6.0.0(typescript@5.0.3)
|
||||
turbo:
|
||||
specifier: 1.8.8
|
||||
version: 1.8.8
|
||||
typescript:
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3
|
||||
|
||||
packages/@n8n_io/eslint-config:
|
||||
devDependencies:
|
||||
|
@ -113,13 +116,13 @@ importers:
|
|||
version: 8.4.6
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ~5.45
|
||||
version: 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.0.2)
|
||||
version: 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.0.3)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ~5.45
|
||||
version: 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
version: 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
'@vue/eslint-config-typescript':
|
||||
specifier: ~8.0
|
||||
version: 8.0.0(@typescript-eslint/eslint-plugin@5.45.0)(@typescript-eslint/parser@5.45.0)(eslint-plugin-vue@7.17.0)(eslint@8.28.0)(typescript@5.0.2)
|
||||
version: 8.0.0(@typescript-eslint/eslint-plugin@5.45.0)(@typescript-eslint/parser@5.45.0)(eslint-plugin-vue@7.17.0)(eslint@8.28.0)(typescript@5.0.3)
|
||||
eslint:
|
||||
specifier: ~8.28
|
||||
version: 8.28.0
|
||||
|
@ -573,7 +576,7 @@ importers:
|
|||
version: 1.1.6
|
||||
ts-essentials:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3(typescript@5.0.2)
|
||||
version: 7.0.3(typescript@5.0.3)
|
||||
tsc-alias:
|
||||
specifier: ^1.8.2
|
||||
version: 1.8.2
|
||||
|
@ -731,7 +734,7 @@ importers:
|
|||
version: 7.0.0-beta.46(@babel/core@7.20.12)(babel-loader@9.1.2)(css-loader@6.7.3)(vue@2.7.14)
|
||||
'@storybook/vue-webpack5':
|
||||
specifier: ^7.0.0-beta.46
|
||||
version: 7.0.0-beta.46(@babel/core@7.20.12)(@babel/preset-env@7.20.2)(babel-loader@9.1.2)(css-loader@6.7.3)(esbuild@0.16.17)(react-dom@18.2.0)(react@17.0.2)(typescript@5.0.2)(vue-loader@15.10.1)(vue-template-compiler@2.7.14)(vue@2.7.14)
|
||||
version: 7.0.0-beta.46(@babel/core@7.20.12)(@babel/preset-env@7.20.2)(babel-loader@9.1.2)(css-loader@6.7.3)(esbuild@0.16.17)(react-dom@18.2.0)(react@17.0.2)(typescript@5.0.3)(vue-loader@15.10.1)(vue-template-compiler@2.7.14)(vue@2.7.14)
|
||||
'@testing-library/jest-dom':
|
||||
specifier: ^5.16.5
|
||||
version: 5.16.5
|
||||
|
@ -809,7 +812,7 @@ importers:
|
|||
version: 2.7.14
|
||||
vue-tsc:
|
||||
specifier: ^1.0.24
|
||||
version: 1.0.24(typescript@5.0.2)
|
||||
version: 1.0.24(typescript@5.0.3)
|
||||
|
||||
packages/editor-ui:
|
||||
dependencies:
|
||||
|
@ -917,7 +920,7 @@ importers:
|
|||
version: 1.0.1
|
||||
pinia:
|
||||
specifier: ^2.0.22
|
||||
version: 2.0.23(typescript@5.0.2)(vue@2.7.14)
|
||||
version: 2.0.23(typescript@5.0.3)(vue@2.7.14)
|
||||
prettier:
|
||||
specifier: ^2.8.3
|
||||
version: 2.8.3
|
||||
|
@ -985,6 +988,9 @@ importers:
|
|||
'@pinia/testing':
|
||||
specifier: ^0.0.14
|
||||
version: 0.0.14(pinia@2.0.23)(vue@2.7.14)
|
||||
'@sentry/vite-plugin':
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0
|
||||
'@testing-library/jest-dom':
|
||||
specifier: ^5.16.5
|
||||
version: 5.16.5
|
||||
|
@ -1065,7 +1071,7 @@ importers:
|
|||
version: 0.28.5(sass@1.55.0)(terser@5.16.1)
|
||||
vue-tsc:
|
||||
specifier: ^1.0.24
|
||||
version: 1.0.24(typescript@5.0.2)
|
||||
version: 1.0.24(typescript@5.0.3)
|
||||
|
||||
packages/node-dev:
|
||||
dependencies:
|
||||
|
@ -1613,7 +1619,7 @@ importers:
|
|||
version: 0.4.11
|
||||
eslint-plugin-n8n-nodes-base:
|
||||
specifier: ^1.12.0
|
||||
version: 1.12.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
version: 1.12.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
gulp:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.2
|
||||
|
@ -4031,18 +4037,6 @@ packages:
|
|||
chalk: 4.1.2
|
||||
dev: true
|
||||
|
||||
/@jest/types@29.4.2:
|
||||
resolution: {integrity: sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@jest/schemas': 29.4.2
|
||||
'@types/istanbul-lib-coverage': 2.0.4
|
||||
'@types/istanbul-reports': 3.0.1
|
||||
'@types/node': 16.18.12
|
||||
'@types/yargs': 17.0.19
|
||||
chalk: 4.1.2
|
||||
dev: true
|
||||
|
||||
/@jest/types@29.5.0:
|
||||
resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
@ -4529,7 +4523,7 @@ packages:
|
|||
peerDependencies:
|
||||
pinia: '>=2.0.19'
|
||||
dependencies:
|
||||
pinia: 2.0.23(typescript@5.0.2)(vue@2.7.14)
|
||||
pinia: 2.0.23(typescript@5.0.3)(vue@2.7.14)
|
||||
vue-demi: 0.13.11(vue@2.7.14)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
|
@ -4585,6 +4579,46 @@ packages:
|
|||
selderee: 0.6.0
|
||||
dev: false
|
||||
|
||||
/@sentry-internal/tracing@7.47.0:
|
||||
resolution: {integrity: sha512-udpHnCzF8DQsWf0gQwd0XFGp6Y8MOiwnl8vGt2ohqZGS3m1+IxoRLXsSkD8qmvN6KKDnwbaAvYnK0z0L+AW95g==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
'@sentry/core': 7.47.0
|
||||
'@sentry/types': 7.47.0
|
||||
'@sentry/utils': 7.47.0
|
||||
tslib: 2.5.0
|
||||
dev: true
|
||||
|
||||
/@sentry/bundler-plugin-core@0.4.0:
|
||||
resolution: {integrity: sha512-Xi+dqaSOoxbdmxegX7f66FVOxm2dVJLmrMXUpoNyuV6ASoccRWzouGaFekP059SUTTD05ytk1mHqwgVuBCA0Dw==}
|
||||
engines: {node: '>= 10'}
|
||||
dependencies:
|
||||
'@sentry/cli': 2.17.0(patch_hash=nchnoezkq6p37qaiku3vrpwraq)
|
||||
'@sentry/node': 7.28.1
|
||||
'@sentry/tracing': 7.47.0
|
||||
magic-string: 0.27.0
|
||||
unplugin: 1.0.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sentry/cli@2.17.0(patch_hash=nchnoezkq6p37qaiku3vrpwraq):
|
||||
resolution: {integrity: sha512-CHIMEg8+YNCpEBDgUctu+DvG3S8+g8Zn9jTE5MMGINNmGkQTMG179LuDE04B/inaCYixLVNpFPTe6Iow3tXjnQ==}
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
https-proxy-agent: 5.0.1
|
||||
node-fetch: 2.6.8
|
||||
progress: 2.0.3
|
||||
proxy-from-env: 1.1.0
|
||||
which: 2.0.2
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: true
|
||||
patched: true
|
||||
|
||||
/@sentry/core@7.28.1:
|
||||
resolution: {integrity: sha512-7wvnuvn/mrAfcugWoCG/3pqDIrUgH5t+HisMJMGw0h9Tc33KqrmqMDCQVvjlrr2pWrw/vuUCFdm8CbUHJ832oQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -4592,7 +4626,15 @@ packages:
|
|||
'@sentry/types': 7.28.1
|
||||
'@sentry/utils': 7.28.1
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/@sentry/core@7.47.0:
|
||||
resolution: {integrity: sha512-EFhZhKdMu7wKmWYZwbgTi8FNZ7Fq+HdlXiZWNz51Bqe3pHmfAkdHtAEs0Buo0v623MKA0CA4EjXIazGUM34XTg==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
'@sentry/types': 7.47.0
|
||||
'@sentry/utils': 7.47.0
|
||||
tslib: 2.5.0
|
||||
dev: true
|
||||
|
||||
/@sentry/integrations@7.28.1:
|
||||
resolution: {integrity: sha512-opeXVR1L9mZmZcpAs9kX+4JPY7pXhVupy17Sbz+43zd5CshYTveIcttGNPp+EPT3j7mMU+1TMAYZspKqJXtEBQ==}
|
||||
|
@ -4617,12 +4659,22 @@ packages:
|
|||
tslib: 2.5.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@sentry/tracing@7.47.0:
|
||||
resolution: {integrity: sha512-hJCpKdekwaFNbCVXxfCz5IxfSEJIKnkPmRSVHITOm5VhKwq2e5kmy4Rn6bzSETwJFSDE8LGbR/3eSfGTqw37XA==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
'@sentry-internal/tracing': 7.47.0
|
||||
dev: true
|
||||
|
||||
/@sentry/types@7.28.1:
|
||||
resolution: {integrity: sha512-DvSplMVrVEmOzR2M161V5+B8Up3vR71xMqJOpWTzE9TqtFJRGPtqT/5OBsNJJw1+/j2ssMcnKwbEo9Q2EGeS6g==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/@sentry/types@7.47.0:
|
||||
resolution: {integrity: sha512-GxXocplN0j1+uczovHrfkykl9wvkamDtWxlPUQgyGlbLGZn+UH1Y79D4D58COaFWGEZdSNKr62gZAjfEYu9nQA==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/@sentry/utils@7.28.1:
|
||||
resolution: {integrity: sha512-75/jzLUO9HH09iC9TslNimGbxOP3jgn89P+q7uR+rp2fJfRExHVeKJZQdK0Ij4/SmE7TJ3Uh2r154N0INZEx1g==}
|
||||
|
@ -4630,7 +4682,24 @@ packages:
|
|||
dependencies:
|
||||
'@sentry/types': 7.28.1
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/@sentry/utils@7.47.0:
|
||||
resolution: {integrity: sha512-A89SaOLp6XeZfByeYo2C8Ecye/YAtk/gENuyOUhQEdMulI6mZdjqtHAp7pTMVgkBc/YNARVuoa+kR/IdRrTPkQ==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
'@sentry/types': 7.47.0
|
||||
tslib: 2.5.0
|
||||
dev: true
|
||||
|
||||
/@sentry/vite-plugin@0.4.0:
|
||||
resolution: {integrity: sha512-dBxM00MCLzO/idzAqj33ZfbIBKZxP+FzpxtS2WaV0yzad9yLBAFZ/VGDIGHQJC0Qo3fsFi/CZpmN39wJkJoWFA==}
|
||||
engines: {node: '>= 10'}
|
||||
dependencies:
|
||||
'@sentry/bundler-plugin-core': 0.4.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@servie/events@1.0.0:
|
||||
resolution: {integrity: sha512-sBSO19KzdrJCM3gdx6eIxV8M9Gxfgg6iDQmH5TIAGaUu+X9VDdsINXJOnoiZ1Kx3TrHdH4bt5UVglkjsEGBcvw==}
|
||||
|
@ -5099,7 +5168,7 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@storybook/builder-webpack5@7.0.0-beta.46(esbuild@0.16.17)(react-dom@18.2.0)(react@17.0.2)(typescript@5.0.2)(vue-template-compiler@2.7.14):
|
||||
/@storybook/builder-webpack5@7.0.0-beta.46(esbuild@0.16.17)(react-dom@18.2.0)(react@17.0.2)(typescript@5.0.3)(vue-template-compiler@2.7.14):
|
||||
resolution: {integrity: sha512-1pvo9IDlz836hiDFmALVttQxw4ehSd3UEhE6Yz0B+TMl4cAQuSRWze8w2sI474tiHtGeKGRoIIWmH5hSVQvFrQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
@ -5137,7 +5206,7 @@ packages:
|
|||
case-sensitive-paths-webpack-plugin: 2.4.0
|
||||
css-loader: 6.7.3(webpack@5.75.0)
|
||||
express: 4.18.2
|
||||
fork-ts-checker-webpack-plugin: 6.5.2(typescript@5.0.2)(vue-template-compiler@2.7.14)(webpack@5.75.0)
|
||||
fork-ts-checker-webpack-plugin: 6.5.2(typescript@5.0.3)(vue-template-compiler@2.7.14)(webpack@5.75.0)
|
||||
fs-extra: 11.1.0
|
||||
html-webpack-plugin: 5.5.0(webpack@5.75.0)
|
||||
path-browserify: 1.0.1
|
||||
|
@ -5149,7 +5218,7 @@ packages:
|
|||
style-loader: 3.3.1(webpack@5.75.0)
|
||||
terser-webpack-plugin: 5.3.6(esbuild@0.16.17)(webpack@5.75.0)
|
||||
ts-dedent: 2.2.0
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
util: 0.12.5
|
||||
util-deprecate: 1.0.2
|
||||
webpack: 5.75.0(esbuild@0.16.17)
|
||||
|
@ -5554,7 +5623,7 @@ packages:
|
|||
resolution: {integrity: sha512-tQ6hv57SPVxyOYPQzhlrhkuKs3Nk4Efa1DN9bzYg5jEzXbeZ9uK4jmV9TDQdWv0QeAvK81SD1YNI2OtzbLPVgA==}
|
||||
dev: true
|
||||
|
||||
/@storybook/preset-vue-webpack@7.0.0-beta.46(@babel/core@7.20.12)(@babel/preset-env@7.20.2)(babel-loader@9.1.2)(css-loader@6.7.3)(esbuild@0.16.17)(typescript@5.0.2)(vue-loader@15.10.1)(vue-template-compiler@2.7.14)(vue@2.7.14):
|
||||
/@storybook/preset-vue-webpack@7.0.0-beta.46(@babel/core@7.20.12)(@babel/preset-env@7.20.2)(babel-loader@9.1.2)(css-loader@6.7.3)(esbuild@0.16.17)(typescript@5.0.3)(vue-loader@15.10.1)(vue-template-compiler@2.7.14)(vue@2.7.14):
|
||||
resolution: {integrity: sha512-X40UKuFlXV5lQuVrdP4jBEkPwf+9whXqJwNnBMlQICU/SEwC5oE2sqi7cFU5sZ8DBVzqDunxCE0rfYETeliyyw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -5571,7 +5640,7 @@ packages:
|
|||
'@types/node': 16.18.12
|
||||
babel-loader: 9.1.2(@babel/core@7.20.12)(webpack@5.75.0)
|
||||
css-loader: 6.7.3(webpack@5.75.0)
|
||||
ts-loader: 9.4.2(typescript@5.0.2)(webpack@5.75.0)
|
||||
ts-loader: 9.4.2(typescript@5.0.3)(webpack@5.75.0)
|
||||
vue: 2.7.14
|
||||
vue-docgen-api: 4.56.4(vue@2.7.14)
|
||||
vue-docgen-loader: 1.5.1(@babel/preset-env@7.20.2)(vue-docgen-api@4.56.4)(webpack@5.75.0)
|
||||
|
@ -5719,7 +5788,7 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@storybook/vue-webpack5@7.0.0-beta.46(@babel/core@7.20.12)(@babel/preset-env@7.20.2)(babel-loader@9.1.2)(css-loader@6.7.3)(esbuild@0.16.17)(react-dom@18.2.0)(react@17.0.2)(typescript@5.0.2)(vue-loader@15.10.1)(vue-template-compiler@2.7.14)(vue@2.7.14):
|
||||
/@storybook/vue-webpack5@7.0.0-beta.46(@babel/core@7.20.12)(@babel/preset-env@7.20.2)(babel-loader@9.1.2)(css-loader@6.7.3)(esbuild@0.16.17)(react-dom@18.2.0)(react@17.0.2)(typescript@5.0.3)(vue-loader@15.10.1)(vue-template-compiler@2.7.14)(vue@2.7.14):
|
||||
resolution: {integrity: sha512-PJJo/NGiWL1raOfHpJTb3X6LmLAC2TgI3EV2nDlkPigxj29O+zeMkyvfDky7eWpgOIbVqqtNVUYGH1GwbL7jqw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -5733,9 +5802,9 @@ packages:
|
|||
vue-template-compiler: ^2.6.8
|
||||
dependencies:
|
||||
'@babel/core': 7.20.12
|
||||
'@storybook/builder-webpack5': 7.0.0-beta.46(esbuild@0.16.17)(react-dom@18.2.0)(react@17.0.2)(typescript@5.0.2)(vue-template-compiler@2.7.14)
|
||||
'@storybook/builder-webpack5': 7.0.0-beta.46(esbuild@0.16.17)(react-dom@18.2.0)(react@17.0.2)(typescript@5.0.3)(vue-template-compiler@2.7.14)
|
||||
'@storybook/core-common': 7.0.0-beta.46
|
||||
'@storybook/preset-vue-webpack': 7.0.0-beta.46(@babel/core@7.20.12)(@babel/preset-env@7.20.2)(babel-loader@9.1.2)(css-loader@6.7.3)(esbuild@0.16.17)(typescript@5.0.2)(vue-loader@15.10.1)(vue-template-compiler@2.7.14)(vue@2.7.14)
|
||||
'@storybook/preset-vue-webpack': 7.0.0-beta.46(@babel/core@7.20.12)(@babel/preset-env@7.20.2)(babel-loader@9.1.2)(css-loader@6.7.3)(esbuild@0.16.17)(typescript@5.0.3)(vue-loader@15.10.1)(vue-template-compiler@2.7.14)(vue@2.7.14)
|
||||
'@storybook/vue': 7.0.0-beta.46(@babel/core@7.20.12)(babel-loader@9.1.2)(css-loader@6.7.3)(vue@2.7.14)
|
||||
'@types/node': 16.18.12
|
||||
babel-loader: 9.1.2(@babel/core@7.20.12)(webpack@5.75.0)
|
||||
|
@ -6944,7 +7013,7 @@ packages:
|
|||
dev: true
|
||||
optional: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin@5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.0.2):
|
||||
/@typescript-eslint/eslint-plugin@5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -6955,23 +7024,23 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
'@typescript-eslint/scope-manager': 5.45.0
|
||||
'@typescript-eslint/type-utils': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/utils': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/type-utils': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
'@typescript-eslint/utils': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
eslint: 8.28.0
|
||||
ignore: 5.2.0
|
||||
natural-compare-lite: 1.4.0
|
||||
regexpp: 3.2.0
|
||||
semver: 7.3.8
|
||||
tsutils: 3.21.0(typescript@5.0.2)
|
||||
typescript: 5.0.2
|
||||
tsutils: 3.21.0(typescript@5.0.3)
|
||||
typescript: 5.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser@5.45.0(eslint@8.28.0)(typescript@5.0.2):
|
||||
/@typescript-eslint/parser@5.45.0(eslint@8.28.0)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -6983,10 +7052,10 @@ packages:
|
|||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 5.45.0
|
||||
'@typescript-eslint/types': 5.45.0
|
||||
'@typescript-eslint/typescript-estree': 5.45.0(typescript@5.0.2)
|
||||
'@typescript-eslint/typescript-estree': 5.45.0(typescript@5.0.3)
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
eslint: 8.28.0
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
@ -6999,7 +7068,7 @@ packages:
|
|||
'@typescript-eslint/visitor-keys': 5.45.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/type-utils@5.45.0(eslint@8.28.0)(typescript@5.0.2):
|
||||
/@typescript-eslint/type-utils@5.45.0(eslint@8.28.0)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -7009,12 +7078,12 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 5.45.0(typescript@5.0.2)
|
||||
'@typescript-eslint/utils': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/typescript-estree': 5.45.0(typescript@5.0.3)
|
||||
'@typescript-eslint/utils': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
eslint: 8.28.0
|
||||
tsutils: 3.21.0(typescript@5.0.2)
|
||||
typescript: 5.0.2
|
||||
tsutils: 3.21.0(typescript@5.0.3)
|
||||
typescript: 5.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
@ -7024,7 +7093,7 @@ packages:
|
|||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree@5.45.0(typescript@5.0.2):
|
||||
/@typescript-eslint/typescript-estree@5.45.0(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -7039,13 +7108,13 @@ packages:
|
|||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
semver: 7.3.8
|
||||
tsutils: 3.21.0(typescript@5.0.2)
|
||||
typescript: 5.0.2
|
||||
tsutils: 3.21.0(typescript@5.0.3)
|
||||
typescript: 5.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/utils@5.45.0(eslint@8.28.0)(typescript@5.0.2):
|
||||
/@typescript-eslint/utils@5.45.0(eslint@8.28.0)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
|
@ -7055,7 +7124,7 @@ packages:
|
|||
'@types/semver': 7.3.13
|
||||
'@typescript-eslint/scope-manager': 5.45.0
|
||||
'@typescript-eslint/types': 5.45.0
|
||||
'@typescript-eslint/typescript-estree': 5.45.0(typescript@5.0.2)
|
||||
'@typescript-eslint/typescript-estree': 5.45.0(typescript@5.0.3)
|
||||
eslint: 8.28.0
|
||||
eslint-scope: 5.1.1
|
||||
eslint-utils: 3.0.0(eslint@8.28.0)
|
||||
|
@ -7330,7 +7399,7 @@ packages:
|
|||
/@vue/devtools-api@6.4.5:
|
||||
resolution: {integrity: sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==}
|
||||
|
||||
/@vue/eslint-config-typescript@8.0.0(@typescript-eslint/eslint-plugin@5.45.0)(@typescript-eslint/parser@5.45.0)(eslint-plugin-vue@7.17.0)(eslint@8.28.0)(typescript@5.0.2):
|
||||
/@vue/eslint-config-typescript@8.0.0(@typescript-eslint/eslint-plugin@5.45.0)(@typescript-eslint/parser@5.45.0)(eslint-plugin-vue@7.17.0)(eslint@8.28.0)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-8u8Qpg4qfjJoNeRMdHlxif9BcGy4iYSSK4YYW5AFPPRtkBJiCqtoyT72l4F3ZeZII09ax2N6yQeHbQ0CXQi1bA==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
peerDependencies:
|
||||
|
@ -7343,11 +7412,11 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/eslint-plugin': 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.0.3)
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
eslint: 8.28.0
|
||||
eslint-plugin-vue: 7.17.0(eslint@8.28.0)
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
vue-eslint-parser: 7.11.0(eslint@8.28.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -9730,7 +9799,6 @@ packages:
|
|||
/cookie@0.4.2:
|
||||
resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/cookie@0.5.0:
|
||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||
|
@ -11033,8 +11101,8 @@ packages:
|
|||
eslint: ^7.32.0 || ^8.2.0
|
||||
eslint-plugin-import: ^2.25.3
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/eslint-plugin': 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.0.3)
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
eslint: 8.28.0
|
||||
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.26.0)(eslint@8.28.0)
|
||||
eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.45.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.28.0)
|
||||
|
@ -11103,7 +11171,7 @@ packages:
|
|||
eslint-import-resolver-webpack:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
debug: 3.2.7(supports-color@8.1.1)
|
||||
eslint: 8.28.0
|
||||
eslint-import-resolver-node: 0.3.6
|
||||
|
@ -11131,7 +11199,7 @@ packages:
|
|||
'@typescript-eslint/parser':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
array-includes: 3.1.5
|
||||
array.prototype.flat: 1.3.0
|
||||
debug: 2.6.9
|
||||
|
@ -11156,10 +11224,10 @@ packages:
|
|||
resolution: {integrity: sha512-qe6sVFDP1Vj5eXlqZxYZpIjwYvhuqXlI0P8OfPyhiPOhMkFtr0TpFphD8/6WCzkm7LJCvG1eJEzURCtMIsFTAg==}
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-n8n-nodes-base@1.12.0(eslint@8.28.0)(typescript@5.0.2):
|
||||
/eslint-plugin-n8n-nodes-base@1.12.0(eslint@8.28.0)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-AotXR6IsxLNnxp4OxhD33xcmRFwVq7ZImBd0mTgpirV3VX8pCJDdiDlI2zCAICcICZxtOdbVtHOMhhnMjTh71A==}
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 5.45.0(eslint@8.28.0)(typescript@5.0.2)
|
||||
'@typescript-eslint/utils': 5.45.0(eslint@8.28.0)(typescript@5.0.3)
|
||||
camel-case: 4.1.2
|
||||
indefinite: 2.4.1
|
||||
pascal-case: 3.1.2
|
||||
|
@ -11490,7 +11558,7 @@ packages:
|
|||
jest-get-type: 29.2.0
|
||||
jest-matcher-utils: 29.3.1
|
||||
jest-message-util: 29.3.1
|
||||
jest-util: 29.4.2
|
||||
jest-util: 29.5.0
|
||||
dev: true
|
||||
|
||||
/expect@29.4.2:
|
||||
|
@ -11501,7 +11569,7 @@ packages:
|
|||
jest-get-type: 29.4.2
|
||||
jest-matcher-utils: 29.4.2
|
||||
jest-message-util: 29.4.2
|
||||
jest-util: 29.4.2
|
||||
jest-util: 29.5.0
|
||||
dev: true
|
||||
|
||||
/expect@29.5.0:
|
||||
|
@ -12010,7 +12078,7 @@ packages:
|
|||
/forever-agent@0.6.1:
|
||||
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
|
||||
|
||||
/fork-ts-checker-webpack-plugin@6.5.2(typescript@5.0.2)(vue-template-compiler@2.7.14)(webpack@5.75.0):
|
||||
/fork-ts-checker-webpack-plugin@6.5.2(typescript@5.0.3)(vue-template-compiler@2.7.14)(webpack@5.75.0):
|
||||
resolution: {integrity: sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==}
|
||||
engines: {node: '>=10', yarn: '>=1.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -12037,7 +12105,7 @@ packages:
|
|||
schema-utils: 2.7.0
|
||||
semver: 7.3.8
|
||||
tapable: 1.1.3
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
vue-template-compiler: 2.7.14
|
||||
webpack: 5.75.0(esbuild@0.16.17)
|
||||
dev: true
|
||||
|
@ -13983,7 +14051,7 @@ packages:
|
|||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.18.6
|
||||
'@jest/types': 29.4.2
|
||||
'@jest/types': 29.5.0
|
||||
'@types/stack-utils': 2.0.1
|
||||
chalk: 4.1.2
|
||||
graceful-fs: 4.2.10
|
||||
|
@ -13998,7 +14066,7 @@ packages:
|
|||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.18.6
|
||||
'@jest/types': 29.4.2
|
||||
'@jest/types': 29.5.0
|
||||
'@types/stack-utils': 2.0.1
|
||||
chalk: 4.1.2
|
||||
graceful-fs: 4.2.10
|
||||
|
@ -14023,15 +14091,15 @@ packages:
|
|||
stack-utils: 2.0.6
|
||||
dev: true
|
||||
|
||||
/jest-mock-extended@3.0.3(jest@29.5.0)(typescript@5.0.2):
|
||||
resolution: {integrity: sha512-yqpzvwFr2JWFArj4sPco4hPSanYH3erFgdkv7ahP8EMiAbVH+Rgjc8/cpAHJVG7+sZnQgl0AuTkxofD7eLJN+g==}
|
||||
/jest-mock-extended@3.0.4(jest@29.5.0)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-2ynEZ7IEJNrhrgshklDMhrOdnmW4Nt+PhkyRqZxRgpwMo7JjmFWMzyp0+eSyk+H9KK1QjXI5xTZIw6x7cVDcRg==}
|
||||
peerDependencies:
|
||||
jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0
|
||||
typescript: ^3.0.0 || ^4.0.0
|
||||
typescript: ^3.0.0 || ^4.0.0 || ^5.0.0
|
||||
dependencies:
|
||||
jest: 29.5.0
|
||||
ts-essentials: 7.0.3(typescript@5.0.2)
|
||||
typescript: 5.0.2
|
||||
ts-essentials: 7.0.3(typescript@5.0.3)
|
||||
typescript: 5.0.3
|
||||
dev: true
|
||||
|
||||
/jest-mock@29.5.0:
|
||||
|
@ -14175,30 +14243,6 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/jest-util@29.3.1:
|
||||
resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@jest/types': 29.5.0
|
||||
'@types/node': 16.18.12
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.7.1
|
||||
graceful-fs: 4.2.10
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/jest-util@29.4.2:
|
||||
resolution: {integrity: sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@jest/types': 29.4.2
|
||||
'@types/node': 16.18.12
|
||||
chalk: 4.1.2
|
||||
ci-info: 3.7.1
|
||||
graceful-fs: 4.2.10
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/jest-util@29.5.0:
|
||||
resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
@ -15381,7 +15425,6 @@ packages:
|
|||
|
||||
/lru_map@0.3.3:
|
||||
resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==}
|
||||
dev: false
|
||||
|
||||
/luxon@3.3.0:
|
||||
resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==}
|
||||
|
@ -17172,7 +17215,7 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/pinia@2.0.23(typescript@5.0.2)(vue@2.7.14):
|
||||
/pinia@2.0.23(typescript@5.0.3)(vue@2.7.14):
|
||||
resolution: {integrity: sha512-N15hFf4o5STrxpNrib1IEb1GOArvPYf1zPvQVRGOO1G1d74Ak0J0lVyalX/SmrzdT4Q0nlEFjbURsmBmIGUR5Q==}
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.4.0
|
||||
|
@ -17185,7 +17228,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.4.5
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
vue: 2.7.14
|
||||
vue-demi: 0.13.11(vue@2.7.14)
|
||||
|
||||
|
@ -20327,20 +20370,20 @@ packages:
|
|||
engines: {node: '>=6.10'}
|
||||
dev: true
|
||||
|
||||
/ts-essentials@7.0.3(typescript@5.0.2):
|
||||
/ts-essentials@7.0.3(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==}
|
||||
peerDependencies:
|
||||
typescript: '>=3.7.0'
|
||||
dependencies:
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
dev: true
|
||||
|
||||
/ts-expect@1.3.0:
|
||||
resolution: {integrity: sha512-e4g0EJtAjk64xgnFPD6kTBUtpnMVzDrMb12N1YZV0VvSlhnVT3SGxiYTLdGy8Q5cYHOIC/FAHmZ10eGrAguicQ==}
|
||||
dev: false
|
||||
|
||||
/ts-jest@29.0.5(@babel/core@7.20.12)(jest@29.5.0)(typescript@5.0.2):
|
||||
resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==}
|
||||
/ts-jest@29.1.0(@babel/core@7.20.12)(jest@29.5.0)(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
@ -20349,7 +20392,7 @@ packages:
|
|||
babel-jest: ^29.0.0
|
||||
esbuild: '*'
|
||||
jest: ^29.0.0
|
||||
typescript: '>=4.3'
|
||||
typescript: '>=4.3 <6'
|
||||
peerDependenciesMeta:
|
||||
'@babel/core':
|
||||
optional: true
|
||||
|
@ -20364,16 +20407,16 @@ packages:
|
|||
bs-logger: 0.2.6
|
||||
fast-json-stable-stringify: 2.1.0
|
||||
jest: 29.5.0
|
||||
jest-util: 29.3.1
|
||||
jest-util: 29.5.0
|
||||
json5: 2.2.3
|
||||
lodash.memoize: 4.1.2
|
||||
make-error: 1.3.6
|
||||
semver: 7.3.8
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
yargs-parser: 21.1.1
|
||||
dev: true
|
||||
|
||||
/ts-loader@9.4.2(typescript@5.0.2)(webpack@5.75.0):
|
||||
/ts-loader@9.4.2(typescript@5.0.3)(webpack@5.75.0):
|
||||
resolution: {integrity: sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
|
@ -20384,7 +20427,7 @@ packages:
|
|||
enhanced-resolve: 5.10.0
|
||||
micromatch: 4.0.5
|
||||
semver: 7.3.8
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
webpack: 5.75.0(esbuild@0.16.17)
|
||||
dev: true
|
||||
|
||||
|
@ -20404,7 +20447,7 @@ packages:
|
|||
plimit-lit: 1.4.1
|
||||
dev: true
|
||||
|
||||
/tsc-watch@6.0.0(typescript@5.0.2):
|
||||
/tsc-watch@6.0.0(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-zgpju+/z5z29/kK5V28Nz16CMkX2voFOUxkTlCim/R25hxzbyUqu2NfTnmJBQfESBSPbEQUGqDdB9A8opAcB4A==}
|
||||
engines: {node: '>=12.12.0'}
|
||||
hasBin: true
|
||||
|
@ -20415,7 +20458,7 @@ packages:
|
|||
node-cleanup: 2.1.2
|
||||
ps-tree: 1.2.0
|
||||
string-argv: 0.3.1
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
dev: true
|
||||
|
||||
/tsconfig-paths@3.14.1:
|
||||
|
@ -20444,14 +20487,14 @@ packages:
|
|||
engines: {node: '>=0.6.x'}
|
||||
dev: false
|
||||
|
||||
/tsutils@3.21.0(typescript@5.0.2):
|
||||
/tsutils@3.21.0(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||
engines: {node: '>= 6'}
|
||||
peerDependencies:
|
||||
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
|
||||
dependencies:
|
||||
tslib: 2.5.0
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
dev: true
|
||||
|
||||
/tunnel-agent@0.6.0:
|
||||
|
@ -20687,8 +20730,8 @@ packages:
|
|||
- supports-color
|
||||
dev: false
|
||||
|
||||
/typescript@5.0.2:
|
||||
resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==}
|
||||
/typescript@5.0.3:
|
||||
resolution: {integrity: sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==}
|
||||
engines: {node: '>=12.20'}
|
||||
hasBin: true
|
||||
|
||||
|
@ -20876,6 +20919,15 @@ packages:
|
|||
webpack-virtual-modules: 0.4.6
|
||||
dev: true
|
||||
|
||||
/unplugin@1.0.1:
|
||||
resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==}
|
||||
dependencies:
|
||||
acorn: 8.8.1
|
||||
chokidar: 3.5.2
|
||||
webpack-sources: 3.2.3
|
||||
webpack-virtual-modules: 0.5.0
|
||||
dev: true
|
||||
|
||||
/unset-value@1.0.0:
|
||||
resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -21621,7 +21673,7 @@ packages:
|
|||
resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==}
|
||||
dev: true
|
||||
|
||||
/vue-tsc@1.0.24(typescript@5.0.2):
|
||||
/vue-tsc@1.0.24(typescript@5.0.3):
|
||||
resolution: {integrity: sha512-mmU1s5SAqE1nByQAiQnao9oU4vX+mSdsgI8H57SfKH6UVzq/jP9+Dbi2GaV+0b4Cn361d2ln8m6xeU60ApiEXg==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
@ -21629,7 +21681,7 @@ packages:
|
|||
dependencies:
|
||||
'@volar/vue-language-core': 1.0.24
|
||||
'@volar/vue-typescript': 1.0.24
|
||||
typescript: 5.0.2
|
||||
typescript: 5.0.3
|
||||
dev: true
|
||||
|
||||
/vue-typed-mixins@0.2.0:
|
||||
|
@ -21750,6 +21802,10 @@ packages:
|
|||
resolution: {integrity: sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==}
|
||||
dev: true
|
||||
|
||||
/webpack-virtual-modules@0.5.0:
|
||||
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
|
||||
dev: true
|
||||
|
||||
/webpack@5.75.0(esbuild@0.16.17):
|
||||
resolution: {integrity: sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
|
Loading…
Reference in a new issue