├── .devcontainer ├── devcontainer.json └── post-create-command.sh ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .husky ├── pre-commit └── pre-merge-commit ├── .ncurc.json ├── .nvmrc ├── .prettierignore ├── .vscode ├── launch.json └── settings.json ├── CONTRIBUTING.md ├── Dockerfile.dev ├── LICENSE ├── README.md ├── gulpfile.js ├── package-lock.json ├── package.json ├── scripts └── bundle-declaration-references.js ├── src ├── index.spec.ts └── nodes │ └── DataValidation │ ├── DataValidation.node.json │ └── DataValidation.node.ts ├── tsconfig.base.json ├── tsconfig.json ├── tsconfig.release.json ├── tsconfig.watch.json └── update-project-boilerplate.sh /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.194.3/containers/docker-existing-dockerfile 3 | { 4 | "name": "Existing Dockerfile", 5 | 6 | // Sets the run context to one level up instead of the .devcontainer folder. 7 | "context": "..", 8 | 9 | // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. 10 | "dockerFile": "../Dockerfile.dev", 11 | 12 | // Set *default* container specific settings.json values on container create. 13 | "settings": {}, 14 | 15 | // Add the IDs of extensions you want installed when the container is created. 16 | "extensions": ["dbaeumer.vscode-eslint"], 17 | 18 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 19 | // "forwardPorts": [], 20 | 21 | // Uncomment the next line to run commands after the container is created - for example installing curl. 22 | "postCreateCommand": "/workspaces/n8n-nodes-data-validation/.devcontainer/post-create-command.sh", 23 | 24 | // Uncomment when using a ptrace-based debugger like C++, Go, and Rust 25 | "runArgs": ["--network", "host"], 26 | 27 | // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. 28 | // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], 29 | 30 | // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. 31 | "remoteUser": "node" 32 | } 33 | -------------------------------------------------------------------------------- /.devcontainer/post-create-command.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd /workspaces/n8n-nodes-data-validation 6 | npm link 7 | 8 | cd ~/.npm-global/lib/node_modules/n8n 9 | npm link n8n-nodes-data-validation 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | 4 | /lib/ 5 | 6 | /coverage 7 | 8 | *.json 9 | *.js 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", 3 | parserOptions: { 4 | project: "tsconfig.json", 5 | sourceType: "module", 6 | }, 7 | plugins: [ 8 | "@typescript-eslint/eslint-plugin", 9 | "node", 10 | "eslint-plugin-n8n-nodes-base", 11 | ], 12 | extends: [ 13 | "eslint:recommended", 14 | "plugin:@typescript-eslint/recommended", 15 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 16 | "prettier", 17 | "plugin:n8n-nodes-base/nodes", 18 | ], 19 | root: true, 20 | env: { 21 | node: true, 22 | jest: true, 23 | }, 24 | settings: { 25 | "import/parsers": { 26 | "@typescript-eslint/parser": [".ts", ".tsx"], 27 | }, 28 | }, 29 | rules: { 30 | "@typescript-eslint/interface-name-prefix": "off", 31 | "@typescript-eslint/explicit-module-boundary-types": "error", 32 | "@typescript-eslint/require-await": "off", 33 | "@typescript-eslint/no-unused-vars": ["error", { args: "after-used" }], 34 | "@typescript-eslint/ban-types": "off", 35 | "@typescript-eslint/no-inferrable-types": "off", 36 | "@typescript-eslint/no-explicit-any": "error", 37 | "@typescript-eslint/no-unsafe-assignment": "error", 38 | "@typescript-eslint/no-unsafe-call": "error", 39 | "@typescript-eslint/no-unsafe-member-access": "error", 40 | "@typescript-eslint/no-unsafe-return": "error", 41 | "@typescript-eslint/no-unsafe-argument": "error", 42 | "@typescript-eslint/restrict-template-expressions": [ 43 | "error", 44 | { allowNumber: true, allowNullish: false }, 45 | ], 46 | "@typescript-eslint/no-floating-promises": [ 47 | "error", 48 | { ignoreVoid: false, ignoreIIFE: false }, 49 | ], 50 | "@typescript-eslint/no-misused-promises": "error", 51 | "@typescript-eslint/no-unnecessary-condition": "error", 52 | "@typescript-eslint/unbound-method": "error", 53 | "no-console": "error", 54 | "no-debugger": "error", 55 | "node/no-process-env": "error", 56 | }, 57 | }; 58 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: actions/setup-node@v3 11 | with: 12 | node-version: "14.x" 13 | registry-url: "https://registry.npmjs.org" 14 | - run: npm ci 15 | - run: npm run build 16 | - run: npm publish 17 | env: 18 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /lib/ 3 | 4 | /coverage 5 | 6 | .npmrc 7 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run precommit 5 | -------------------------------------------------------------------------------- /.husky/pre-merge-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run pre-merge-commit 5 | -------------------------------------------------------------------------------- /.ncurc.json: -------------------------------------------------------------------------------- 1 | { 2 | "dep": ["prod", "dev", "bundle", "optional", "peer"], 3 | "reject": ["@types/node"] 4 | } 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | 4 | /lib/ 5 | 6 | /coverage 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "attach", 7 | "name": "Attach to debugger", 8 | "port": 9229 9 | }, 10 | { 11 | "type": "node", 12 | "request": "launch", 13 | "name": "Debug n8n", 14 | "program": "n8n", 15 | "args": ["start"], 16 | "cwd": "/home/node/.npm-global/lib/node_modules/n8n/bin", 17 | "outFiles": ["${workspaceFolder}/lib/*.js"], 18 | "env": { 19 | "EXECUTIONS_PROCESS": "main" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this project 2 | 3 | - [Issues and Bugs](#issues-and-bugs) 4 | - [Suggesting or merging new Features](#new-features) 5 | 6 | ## Issues and Bugs 7 | 8 | If you found a bug and know how to fix it, just open a Pull Request and we'll gladly evaluate it and merge it. If you cannot fix it, just open an issue with a [Code Sandbox](https://codesandbox.io/) or similar reproducing the bug. 9 | 10 | ## New Features 11 | 12 | Right now, we're not expecting suggestions or Pull Requests for new features. 13 | -------------------------------------------------------------------------------- /Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM node:14-buster 2 | 3 | VOLUME /home/node 4 | 5 | ENV NPM_CONFIG_PREFIX=/home/node/.npm-global 6 | ENV PATH="${PATH}:/home/node/.npm-global/bin" 7 | 8 | RUN apt-get update \ 9 | && apt-get install -y nano 10 | 11 | USER node 12 | 13 | RUN npm install n8n -g 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bartolomeu Rodrigues 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## How to 2 | 3 | This n8n community node validates input data using JSON Schemas. 4 | 5 | Visit or to learn how to describe your validation rules in JSON Schemas. 6 | 7 | ## Library development 8 | 9 | ### Changing the supported Node version 10 | 11 | - Files to be changed 12 | - .nvmrc 13 | - Dockerfile.dev 14 | - package.json 15 | - `engine` field 16 | - `@types/node` version 17 | - tsconfig.base.json 18 | - .github/workflows/main.yml and other CI config files 19 | - delete all `node_modules` directories and `package-lock.json` files 20 | - run `npm run install` 21 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const { src, dest } = require("gulp"); 2 | 3 | function copyAssets() { 4 | return src("src/**/*.{png,svg,json}").pipe(dest("lib")); 5 | } 6 | 7 | exports.default = copyAssets; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "n8n-nodes-data-validation", 3 | "version": "1.0.1", 4 | "description": "", 5 | "keywords": [ 6 | "n8n-community-node-package" 7 | ], 8 | "main": "lib/index.js", 9 | "types": "lib/types.d.ts", 10 | "files": [ 11 | "lib", 12 | "assets" 13 | ], 14 | "n8n": { 15 | "n8nNodesApiVersion": 1, 16 | "credentials": [], 17 | "nodes": [ 18 | "lib/nodes/DataValidation/DataValidation.node.js" 19 | ] 20 | }, 21 | "engines": { 22 | "node": ">=14" 23 | }, 24 | "scripts": { 25 | "build": "npm run clean && gulp && tsc --project tsconfig.release.json && node scripts/bundle-declaration-references.js", 26 | "build:watch": "gulp && tsc --project tsconfig.watch.json --watch", 27 | "clean": "rimraf lib", 28 | "typecheck": "tsc --noEmit", 29 | "test": "jest", 30 | "test:watch": "jest --watch", 31 | "test:cov": "jest --coverage", 32 | "test:cov:changed": "npm run test:cov -- --onlyChanged", 33 | "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --testTimeout=86400000", 34 | "test:debug:watch": "npm run test:debug -- --watch", 35 | "format": "prettier --write .", 36 | "lint": "eslint --ext .ts,.tsx --max-warnings 0 .", 37 | "lint:fix": "eslint --ext .ts,.tsx --max-warnings 0 --fix .", 38 | "integrity-check": "npm run typecheck && npm run lint && npm run test", 39 | "precommit": "lint-staged && npm run integrity-check", 40 | "update-all-dependencies": "npm-check-updates --upgrade && rimraf package-lock.json node_modules && npm install", 41 | "reset-repository": "git clean -Xdf", 42 | "prepare": "husky install", 43 | "pre-merge-commit": "npm install && npm run integrity-check" 44 | }, 45 | "author": "Bartolomeu Rodrigues", 46 | "license": "MIT", 47 | "publishConfig": { 48 | "access": "public" 49 | }, 50 | "dependencies": { 51 | "ajv": "^8.11.0" 52 | }, 53 | "devDependencies": { 54 | "@types/express": "^4.17.13", 55 | "@types/jest": "^27.4.1", 56 | "@types/request-promise-native": "^1.0.18", 57 | "@typescript-eslint/eslint-plugin": "^5.15.0", 58 | "@typescript-eslint/parser": "^5.15.0", 59 | "eslint": "^8.11.0", 60 | "eslint-config-prettier": "^8.5.0", 61 | "eslint-plugin-n8n-nodes-base": "^1.6.5", 62 | "eslint-plugin-node": "^11.1.0", 63 | "glob": "^7.2.0", 64 | "gulp": "^4.0.2", 65 | "husky": "^7.0.4", 66 | "jest": "^27.5.1", 67 | "lint-staged": "^12.3.7", 68 | "n8n-core": "^0.132.0", 69 | "n8n-workflow": "^0.114.0", 70 | "npm-check-updates": "^12.5.4", 71 | "prettier": "^2.6.0", 72 | "rimraf": "^3.0.2", 73 | "ts-jest": "^27.1.3", 74 | "typescript": "^4.6.2" 75 | }, 76 | "jest": { 77 | "moduleFileExtensions": [ 78 | "js", 79 | "json", 80 | "ts", 81 | "tsx" 82 | ], 83 | "rootDir": ".", 84 | "testMatch": [ 85 | "**/*.spec.ts", 86 | "**/*.spec.tsx" 87 | ], 88 | "transform": { 89 | "\\.tsx?$": "ts-jest" 90 | }, 91 | "globals": { 92 | "ts-jest": { 93 | "isolatedModules": true, 94 | "tsconfig": "tsconfig.json" 95 | } 96 | }, 97 | "testEnvironment": "node", 98 | "roots": [ 99 | "/src/" 100 | ], 101 | "moduleDirectories": [ 102 | "node_modules", 103 | "" 104 | ] 105 | }, 106 | "lint-staged": { 107 | "*.{js,jsx,json,ts,tsx,scss,sass,css,md,yml,yaml}": "prettier --write" 108 | }, 109 | "repository": { 110 | "type": "git", 111 | "url": "https://github.com/Bartmr/n8n-nodes-data-validation.git" 112 | }, 113 | "bugs": { 114 | "url": "https://github.com/Bartmr/n8n-nodes-data-validation/issues" 115 | }, 116 | "homepage": "https://github.com/Bartmr/n8n-nodes-data-validation" 117 | } 118 | -------------------------------------------------------------------------------- /scripts/bundle-declaration-references.js: -------------------------------------------------------------------------------- 1 | const glob = require("glob"); 2 | const fs = require("fs"); 3 | 4 | const declarationFiles = glob.sync("lib/**/*.d.ts"); 5 | 6 | const typesFile = "lib/types.d.ts"; 7 | 8 | const declarationReferences = declarationFiles.reduce((content, current) => { 9 | if (current === typesFile) { 10 | return content; 11 | } 12 | 13 | const relativePath = current.substring(4); 14 | 15 | return content + `/// ` + "\n"; 16 | }, ""); 17 | 18 | fs.writeFileSync(typesFile, declarationReferences); 19 | -------------------------------------------------------------------------------- /src/index.spec.ts: -------------------------------------------------------------------------------- 1 | describe("Sample test. REMOVE IT", () => { 2 | it("Sample test. REMOVE IT", () => { 3 | expect("world").toBe("world"); 4 | }); 5 | }); 6 | 7 | export {}; 8 | -------------------------------------------------------------------------------- /src/nodes/DataValidation/DataValidation.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "node": "n8n-nodes-base.data-validation", 3 | "nodeVersion": "1.0", 4 | "codexVersion": "1.0", 5 | "categories": ["Utility"] 6 | } 7 | -------------------------------------------------------------------------------- /src/nodes/DataValidation/DataValidation.node.ts: -------------------------------------------------------------------------------- 1 | import { IExecuteFunctions } from "n8n-core"; 2 | import { 3 | INodeExecutionData, 4 | INodeType, 5 | INodeTypeDescription, 6 | NodeApiError, 7 | NodeOperationError, 8 | } from "n8n-workflow"; 9 | import Ajv, { Schema } from "ajv"; 10 | 11 | export class DataValidation implements INodeType { 12 | description: INodeTypeDescription = { 13 | displayName: "Data Validation", 14 | name: "dataValidation", 15 | group: ["transform"], 16 | version: 1, 17 | description: "Validate input data before continuing the workflow", 18 | defaults: { 19 | name: "Data Validation", 20 | color: "#000000", 21 | }, 22 | inputs: ["main"], 23 | outputs: ["main"], 24 | properties: [ 25 | { 26 | displayName: "JSON Schema", 27 | name: "jsonSchema", 28 | type: "json", 29 | typeOptions: { 30 | alwaysOpenEditWindow: true, 31 | }, 32 | default: JSON.stringify( 33 | { 34 | type: "object", 35 | properties: { 36 | foo: { type: "integer" }, 37 | bar: { type: "string" }, 38 | }, 39 | required: ["foo"], 40 | additionalProperties: false, 41 | }, 42 | undefined, 43 | 2 44 | ), 45 | placeholder: "", 46 | // eslint-disable-next-line n8n-nodes-base/node-param-description-miscased-json 47 | description: 48 | "Visit https://ajv.js.org/ or https://json-schema.org/ to learn how to describe your validation rules in JSON Schemas", 49 | }, 50 | ], 51 | }; 52 | 53 | async execute(this: IExecuteFunctions): Promise { 54 | const items = this.getInputData(); 55 | const returnData: INodeExecutionData[] = []; 56 | 57 | const jsonSchemaString = this.getNodeParameter("jsonSchema", 0); 58 | 59 | if (typeof jsonSchemaString !== "string") { 60 | throw new NodeOperationError(this.getNode(), "Invalid JSON Schema"); 61 | } 62 | 63 | let jsonSchema: Schema; 64 | 65 | try { 66 | jsonSchema = JSON.parse(jsonSchemaString) as Schema; 67 | } catch (err) { 68 | throw new NodeOperationError(this.getNode(), "Invalid JSON Schema"); 69 | } 70 | 71 | const ajv = new Ajv(); 72 | let validate: ReturnType; 73 | 74 | try { 75 | validate = ajv.compile(jsonSchema); 76 | } catch (err) { 77 | throw new NodeOperationError(this.getNode(), "Invalid JSON Schema"); 78 | } 79 | 80 | for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { 81 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 82 | const item = items[itemIndex]!; 83 | 84 | const json = item["json"]; 85 | 86 | const valid = validate(json); 87 | 88 | if (!valid) { 89 | throw new NodeApiError( 90 | this.getNode(), 91 | { 92 | errors: JSON.stringify(validate.errors, undefined, 4), 93 | }, 94 | { 95 | itemIndex, 96 | message: "Invalid data", 97 | description: JSON.stringify(validate.errors, undefined, 4), 98 | httpCode: "400", 99 | } 100 | ); 101 | } 102 | 103 | returnData.push({ 104 | json, 105 | pairedItem: { 106 | item: itemIndex, 107 | }, 108 | }); 109 | } 110 | 111 | return this.prepareOutputData(returnData); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "compilerOptions": { 4 | "lib": ["es2020"], 5 | "module": "commonjs", 6 | "target": "es2020", 7 | "isolatedModules": true, 8 | "declaration": true, 9 | "removeComments": false, 10 | "sourceMap": true, 11 | "outDir": "./lib", 12 | "rootDir": "./src", 13 | "strict": true, 14 | "noUncheckedIndexedAccess": true, 15 | "noImplicitReturns": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "esModuleInterop": true, 18 | "experimentalDecorators": true, 19 | "emitDecoratorMetadata": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "incremental": true, 5 | "tsBuildInfoFile": "./lib/default.tsbuildinfo" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "exclude": [ 4 | "**/*.*spec.ts", 5 | "**/*.*spec.tsx", 6 | "**/spec/**/*.ts", 7 | "**/spec/**/*.tsx" 8 | ], 9 | "compilerOptions": { 10 | "incremental": false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.watch.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "exclude": [ 4 | "**/*.*spec.ts", 5 | "**/*.*spec.tsx", 6 | "**/spec/**/*.ts", 7 | "**/spec/**/*.tsx" 8 | ], 9 | "compilerOptions": { 10 | "incremental": true, 11 | "tsBuildInfoFile": "./lib/watch.tsbuildinfo" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /update-project-boilerplate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ ! -d "./.git/" ] && [ ! -f "./.git" ] 4 | then 5 | echo -e "This command is only to be used when the project is in the root directory of a Git repository" 6 | else 7 | echo "----- 8 | WARNING!!! This will erase any uncommited changes that you have right now. 9 | To cancel the update press Ctrl+C. Commit and push your changes first and then run this update command again. 10 | To continue with the update write yes and press Enter. 11 | -----" 12 | read user_has_continued 13 | 14 | if [ "$user_has_continued" != "yes" ]; then 15 | echo "Update canceled" 16 | exit 0 17 | fi 18 | 19 | echo "Paste the URL of the git repository where the boilerplate is" 20 | read git_url 21 | 22 | echo "What is the name of the git branch where the updates are?" 23 | read git_branch 24 | 25 | echo "Updating..." 26 | 27 | git add . 28 | git reset --hard 29 | 30 | git remote remove boilerplate 31 | git remote add boilerplate $git_url 32 | 33 | git fetch boilerplate $git_branch 34 | 35 | git merge "boilerplate/${git_branch}" --no-commit 36 | 37 | echo " 38 | ----- 39 | ----- 40 | ----- 41 | ----- FURTHER INSTRUCTIONS 42 | 43 | - Review and pick the updates you want to add to the project 44 | 45 | - Run 'npm install', in case any dependencies were changed or added 46 | 47 | - Check if any of the changes made by the updates require a new migration to be written 48 | 49 | - Run 'git add .' to stage all the accepted updates 50 | 51 | - IF YOU WANT TO ABORT the update, run 'git merge --abort' 52 | 53 | - To finalize the update, run 'git merge --continue'" 54 | fi 55 | --------------------------------------------------------------------------------