diff --git a/.circleci/config.yml b/.circleci/config.yml index bc4b58c6312..58256e0dd97 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,114 +1,215 @@ -# Javascript Node CircleCI 2.0 configuration file -# Check https://circleci.com/docs/2.0/language-javascript/ for more details -version: 2 -jobs: - common: - working_directory: ~/typeorm - docker: - - image: circleci/node:10.16.0 - - image: mysql:5.7.24 - environment: - MYSQL_ROOT_PASSWORD: "admin" - MYSQL_DATABASE: "test" - - image: mariadb:10.1.37 - name: mariadb - environment: - MYSQL_ROOT_PASSWORD: "admin" - MYSQL_DATABASE: "test" - - image: circleci/postgres:9.6.11-postgis - environment: - POSTGRES_USER: "test" - POSTGRES_PASSWORD: "test" - POSTGRES_DB: "test" - - image: circleci/mongo:3.4.18 - # - image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu - # environment: - # SA_PASSWORD: "Admin12345" - # ACCEPT_EULA: "Y" +version: 2.1 +orbs: + codecov: codecov/codecov@1.1.1 + +commands: + create-typeorm-config: + parameters: + databases: + type: string + default: "" + steps: + - when: + condition: + equal: [ << parameters.databases >>, "" ] + steps: + - run: + name: "Enabling Databases in ORM config" + command: cp ormconfig.circleci-common.json ./ormconfig.json + - unless: + condition: + equal: [ << parameters.databases >>, "" ] + steps: + - run: + name: "Enabling Databases in ORM config" + command: > + cat ormconfig.circleci-common.json \ + | jq 'map(.skip = if (.name | IN($ARGS.positional[])) then false else true end)' --args << parameters.databases >> \ + > ormconfig.json + - run: + name: Check ORMConfig + command: cat ormconfig.json + + install-packages: + parameters: + cache-key: + type: string + default: "" steps: - - checkout - - run: cp ormconfig.circleci-common.json ormconfig.json - # Download and cache dependencies - restore_cache: - keys: - - v1-dependencies-{{ checksum "package.json" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- - - run: npm install + name: Restore node_modules cache + key: node_modules-<< parameters.cache-key >>-{{ checksum "package-lock.json" }} + - run: + name: Verify `package.json` and `package-lock.json` are in sync + command: npx lock-verify + - run: + # This uses `npm install` instead of `npm ci` + # because of https://github.com/npm/cli/issues/558 + name: Install Node Packages + command: | + if [ ! -d node_modules ]; then + npm install + fi + - run: + # This is pretty terrible but OracleDB requires you to grab the binaries OOB + # from the normal installation, place them in the LD Path + # also - not super well documented - grab `libaio` as well + # Because this is technically the same image as the runner we'll snag + # the libaio1 and place them in the same instantclient directory. + name: Download Required OracleDB Binaries + command: | + if [ ! -d node_modules/oracledb/instantclient_19_8 ]; then + curl -sf -o node_modules/oracledb/instantclient.zip $BLOB_URL + unzip -qqo node_modules/oracledb/instantclient.zip -d node_modules/oracledb/ + rm node_modules/oracledb/instantclient.zip + + DEBIAN_FRONTEND=noninteractive sudo apt-get -qq -y install libaio1 + cp /lib/*/libaio.so.* node_modules/oracledb/instantclient_19_8/ + fi + environment: + BLOB_URL: https://download.oracle.com/otn_software/linux/instantclient/19800/instantclient-basiclite-linux.x64-19.8.0.0.0dbru.zip - save_cache: + name: Save node_modules cache + key: node_modules-{{ checksum "package-lock.json" }} paths: - node_modules - key: v1-dependencies-{{ checksum "package.json" }} + +jobs: + lint: + working_directory: ~/typeorm + docker: + - image: circleci/node:12 + steps: + - checkout + - install-packages: + cache-key: node12 - run: npm run lint - - run: npm test - cockroachdb: + build: working_directory: ~/typeorm docker: - - image: circleci/node:10.15.0 - - image: cockroachdb/cockroach:latest - command: start --insecure + - image: circleci/node:12 steps: - checkout - - run: cp ormconfig.circleci-cockroach.json ormconfig.json - # Download and cache dependencies - - restore_cache: - keys: - - v1-dependencies-{{ checksum "package.json" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- - - run: npm install - - save_cache: + - install-packages: + cache-key: node12 + - run: npm run compile + - persist_to_workspace: + root: ~/typeorm paths: - - node_modules - key: v1-dependencies-{{ checksum "package.json" }} - - run: npm run lint - - run: - npm test - oracle: + - build/ + + test: + parameters: + databases: + type: string + default: "" + node-version: + type: string + default: "10" working_directory: ~/typeorm docker: - - image: circleci/node:10.16.0 - - image: "store/oracle/database-enterprise:12.2.0.1-slim" - auth: - username: $DOCKER_USER - password: $DOCKER_PASSWORD - environment: - DB_SID: "sys" - SYS_PASSWORD: "ORCLCDB" + - image: circleci/node:<< parameters.node-version >> steps: - - run: if [ -z "$DOCKER_USER" ]; then echo "DOCKER_USER is unset"; circleci step halt; fi - checkout - - run: sudo npm install -g npm@latest - - run: cp ormconfig.circleci-oracle.json ormconfig.json + - setup_remote_docker + - attach_workspace: + at: ~/typeorm + - create-typeorm-config: + databases: << parameters.databases >> + - run: + name: Start all Relevant Services + command: | + SERVICES=$( + npx js-yaml ./docker-compose.yml \ + | jq -r '.services | keys | map(select(. | IN($ARGS.positional[]))) | join(" ")' --args << parameters.databases >> + ) + + docker-compose --project-name typeorm --no-ansi up --detach $SERVICES + - install-packages: + cache-key: node<< parameters.node-version >> + - run: + name: Set up TypeORM Test Runner + command: | + docker run \ + --volume /typeorm \ + --name typeorm-code \ + --workdir /typeorm \ + circleci/node:<< parameters.node-version >> \ + /bin/bash -c "sudo chmod 777 /typeorm && sudo chown circleci /typeorm" + docker cp ./ typeorm-code:/typeorm + - run: + name: Wait for Services to be Available + command: | + COMMANDS=$( + cat ormconfig.json \ + | jq -r ' + map(select(.skip == false) + | select(.host) + | select(.port) + | "nc -z " + .host + " " + (.port|tostring) + " && echo " + .host + " " + (.port|tostring) + " is up") + | join(" && ") + ' + ) + echo "Running '$COMMANDS'" + + docker run \ + --network typeorm_default \ + --tty \ + ubuntu:trusty \ + timeout 60 sh -c "until ($COMMANDS); do echo \"Waiting for Services to be Available ...\"; sleep 5; done" # Download and cache dependencies - - restore_cache: - keys: - - v1-dependencies-{{ checksum "package.json" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- - - run: npm install - - save_cache: - paths: - - node_modules - key: v1-dependencies-{{ checksum "package.json" }} - - run: wget https://download.oracle.com/otn_software/linux/instantclient/195000/instantclient-basiclite-linux.x64-19.5.0.0.0dbru.zip - - run: unzip instantclient-basiclite-linux.x64-19.5.0.0.0dbru.zip -d /tmp/oracle - - run: wget https://download.oracle.com/otn_software/linux/instantclient/195000/instantclient-sqlplus-linux.x64-19.5.0.0.0dbru.zip - - run: unzip instantclient-sqlplus-linux.x64-19.5.0.0.0dbru.zip -d /tmp/oracle - - run: echo "export LD_LIBRARY_PATH=/tmp/oracle/instantclient_19_5" >> $BASH_ENV - - run: sudo sh -c "echo /tmp/oracle/instantclient_19_5 > /etc/ld.so.conf.d/oracle-instantclient.conf";sudo ldconfig - - run: sh -c 'echo WHENEVER SQLERROR EXIT FAILURE > /tmp/user.sql; echo CREATE USER typeorm IDENTIFIED BY Passw0rd\; >> /tmp/user.sql; echo GRANT CONNECT TO typeorm\; >> /tmp/user.sql; echo GRANT UNLIMITED TABLESPACE TO typeorm\; >> /tmp/user.sql; echo exit >> /tmp/user.sql' - - run: sudo apt install libaio1 - - run: until /tmp/oracle/instantclient_19_5/sqlplus -L -S sys/Oradoc_db1@//localhost:1521/orclpdb1.localdomain as sysdba @/tmp/user.sql ; do echo waiting for oracle; sleep 10; done; - - run: npm install oracledb --no-save - - run: npm run lint - - run: npm test + - run: + name: "Run Tests with Coverage" + command: | + docker run \ + --env LD_LIBRARY_PATH='/typeorm/node_modules/oracledb/instantclient_19_8/:$LD_LIBRARY_PATH' \ + --volumes-from typeorm-code \ + --network typeorm_default \ + --tty \ + --workdir /typeorm \ + --name typeorm-testrunner \ + circleci/node:<< parameters.node-version >> \ + npx nyc npm run test-fast + + docker cp typeorm-testrunner:/typeorm/coverage/ ./ + - run: + name: Stop all Relevant Services + command: docker-compose down + - store_artifacts: + path: coverage + - codecov/upload + workflows: version: 2 test: jobs: - - common - - cockroachdb - # - oracle + - lint + - build + - test: + name: test (mysql mariadb postgres mssql mongodb sqlite better-sqlite3 sqljs) - Node v<< matrix.node-version >> + requires: + - lint + - build + databases: "mysql mariadb postgres mssql mongodb sqlite better-sqlite3 sqljs" + matrix: + parameters: + node-version: + - "10" + - "12" + - "13" + - test: + name: test (cockroachdb) - Node v12 + requires: + - lint + - build + databases: "cockroachdb" + node-version: "12" + - test: + name: test (oracle) - Node v12 + requires: + - lint + - build + databases: "oracle" + node-version: "12" diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000000..06473d20bf9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,54 @@ +module.exports = { + "env": { + "node": true + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/no-unused-vars": [ + "error", + { + "args": "none" + } + ], + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/quotes": [ + "error", + "double", + { + "avoidEscape": true, + "allowTemplateLiterals": true + } + ], + "@typescript-eslint/semi": [ + "error" + ], + "@typescript-eslint/type-annotation-spacing": "error", + "@typescript-eslint/no-redeclare": "error", + "eqeqeq": [ + "error", + "smart" + ], + "id-blacklist": [ + "error", + "any", + "Number", + "number", + "String", + "string", + "Boolean", + "boolean", + "Undefined", + "undefined" + ], + "id-match": "error", + "no-eval": "error", + "no-var": "error" + } +}; diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000000..aa3d4af1355 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,127 @@ +--- +name: "🐛 Bug Report" +about: Report a reproducible bug or regression. +title: '' +labels: requires triage, bug +assignees: '' + +--- + + + +## Issue Description + +### Expected Behavior + + + + +### Actual Behavior + + + +``` +// include the output in code tags like these! +``` + + +### Steps to Reproduce + + + +1. +2. + +```typescript +// insert code here +``` + +### My Environment + + + +| Dependency | Version | +| --- | --- | +| Operating System | | +| Node.js version | vX.Y.ZZZ | +| Typescript version | vX.Y.ZZZ | +| TypeORM version | vX.Y.ZZZ | + + +### Additional Context + + + + +### Relevant Database Driver(s) + + + +- [ ] `aurora-data-api` +- [ ] `aurora-data-api-pg` +- [ ] `better-sqlite3` +- [ ] `cockroachdb` +- [ ] `cordova` +- [ ] `expo` +- [ ] `mongodb` +- [ ] `mysql` +- [ ] `nativescript` +- [ ] `oracle` +- [ ] `postgres` +- [ ] `react-native` +- [ ] `sap` +- [ ] `sqlite` +- [ ] `sqlite-abstract` +- [ ] `sqljs` +- [ ] `sqlserver` + + +### Are you willing to resolve this issue by submitting a Pull Request? + + + +- [ ] Yes, I have the time, and I know how to start. +- [ ] Yes, I have the time, but I don't know how to start. I would need guidance. +- [ ] No, I don't have the time, although I believe I could do it if I had the time... +- [ ] No, I don't have the time and I wouldn't even know how to start. + + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..94d91af328b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: 🤔 Questions, General Support, and Help + url: https://github.com/typeorm/typeorm/blob/master/docs/support.md + about: This issue tracker is not for support questions. Please refer to the TypeORM community support documentation. diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/documentation-issue.md new file mode 100644 index 00000000000..cf314029803 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation-issue.md @@ -0,0 +1,89 @@ +--- +name: 📝 Documentation Issue +about: Documentation is unclear or otherwise insufficient. +title: '' +labels: documentation, requires triage +assignees: '' + +--- + + + +## Documentation Issue + +### What was unclear or otherwise insufficient? + + + + +### Recommended Fix + + + + +### Additional Context + + + + +### Relevant Database Driver(s) + + + +- [ ] `aurora-data-api` +- [ ] `aurora-data-api-pg` +- [ ] `better-sqlite3` +- [ ] `cockroachdb` +- [ ] `cordova` +- [ ] `expo` +- [ ] `mongodb` +- [ ] `mysql` +- [ ] `nativescript` +- [ ] `oracle` +- [ ] `postgres` +- [ ] `react-native` +- [ ] `sap` +- [ ] `sqlite` +- [ ] `sqlite-abstract` +- [ ] `sqljs` +- [ ] `sqlserver` + + +### Are you willing to resolve this issue by submitting a Pull Request? + + + +- [ ] Yes, I have the time, and I know how to start. +- [ ] Yes, I have the time, but I don't know how to start. I would need guidance. +- [ ] No, I don't have the time, although I believe I could do it if I had the time... +- [ ] No, I don't have the time and I wouldn't even know how to start. + + + diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 00000000000..c2b13944b72 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,99 @@ +--- +name: 🌈 Feature request +about: Suggest an amazing new idea for this project +title: '' +labels: new feature, requires triage +assignees: '' + +--- + + + +## Feature Description + +### The Problem + + + + +### The Solution + + + + +### Considered Alternatives + + + + +### Additional Context + + + + +### Relevant Database Driver(s) + + + +- [ ] `aurora-data-api` +- [ ] `aurora-data-api-pg` +- [ ] `better-sqlite3` +- [ ] `cockroachdb` +- [ ] `cordova` +- [ ] `expo` +- [ ] `mongodb` +- [ ] `mysql` +- [ ] `nativescript` +- [ ] `oracle` +- [ ] `postgres` +- [ ] `react-native` +- [ ] `sap` +- [ ] `sqlite` +- [ ] `sqlite-abstract` +- [ ] `sqljs` +- [ ] `sqlserver` + + +### Are you willing to resolve this issue by submitting a Pull Request? + + + +- [ ] Yes, I have the time, and I know how to start. +- [ ] Yes, I have the time, but I don't know how to start. I would need guidance. +- [ ] No, I don't have the time, although I believe I could do it if I had the time... +- [ ] No, I don't have the time and I wouldn't even know how to start. + + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..fd33e7cbf69 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,47 @@ + + +### Description of change + + + + +### Pull-Request Checklist + + + +- [ ] Code is up-to-date with the `master` branch +- [ ] `npm run lint` passes with this change +- [ ] `npm run test` passes with this change +- [ ] This pull request links relevant issues as `Fixes #0000` +- [ ] There are new or updated unit tests validating the change +- [ ] Documentation has been updated to reflect this change +- [ ] The new commits follow conventions explained in [CONTRIBUTING.md](https://github.com/typeorm/typeorm/blob/master/CONTRIBUTING.md) + + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3fa8b1987be..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -language: node_js -node_js: - - 13 - - 12 - - 10 - -services: - - docker - -before_script: - - sudo service mysql stop - - sudo service postgresql stop - - docker-compose up -d - - cp ormconfig.travis.json ormconfig.json - -script: - - npm run lint - - npx nyc npm test - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba22f7e6801..57220f04727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,164 @@ +## [0.2.30](https://github.com/typeorm/typeorm/compare/0.2.29...0.2.30) (2021-01-12) + +### Bug Fixes + +* add missing "comment" field to QB clone method ([#7205](https://github.com/typeorm/typeorm/issues/7205)) ([f019771](https://github.com/typeorm/typeorm/commit/f0197710ab986b474ce0b6c260d57e8234a5bb4f)), closes [#7203](https://github.com/typeorm/typeorm/issues/7203) +* avoid early release of PostgresQueryRunner ([#7109](https://github.com/typeorm/typeorm/issues/7109)) ([#7185](https://github.com/typeorm/typeorm/issues/7185)) ([9abe007](https://github.com/typeorm/typeorm/commit/9abe0076f65afba9034fb48ba3ebd43be7e7557a)) +* Error when sorting by an embedded entity while using join and skip/take ([#7082](https://github.com/typeorm/typeorm/issues/7082)) ([d27dd2a](https://github.com/typeorm/typeorm/commit/d27dd2af2ca320e74a17b3ab273cd3bf55d01923)), closes [#7079](https://github.com/typeorm/typeorm/issues/7079) +* Fix CLI query command TypeError ([#7043](https://github.com/typeorm/typeorm/issues/7043)) ([b35397e](https://github.com/typeorm/typeorm/commit/b35397ea07982a21d3b263cb0b7c04d5aa057d1a)) +* get length attribute of postgres array columns ([#7239](https://github.com/typeorm/typeorm/issues/7239)) ([eb82f78](https://github.com/typeorm/typeorm/commit/eb82f786cbe3244351d5860289dace3169cf473b)), closes [#6990](https://github.com/typeorm/typeorm/issues/6990) +* handle overlapping property / database names in querybuilder ([#7042](https://github.com/typeorm/typeorm/issues/7042)) ([b518fa1](https://github.com/typeorm/typeorm/commit/b518fa15f9b2183545b3c0daa2447ecd38ecc859)), closes [#7030](https://github.com/typeorm/typeorm/issues/7030) +* improve stack traces when using persist executor ([#7218](https://github.com/typeorm/typeorm/issues/7218)) ([0dfe5b8](https://github.com/typeorm/typeorm/commit/0dfe5b83f584c3960cdef28e53d2f0ded3f829ce)) +* order should allow only model fields, not methods ([#7188](https://github.com/typeorm/typeorm/issues/7188)) ([0194193](https://github.com/typeorm/typeorm/commit/01941937df11abd63fad9da082e1b5cf6a1300ce)), closes [#7178](https://github.com/typeorm/typeorm/issues/7178) +* resolve migration for UpdateDateColumn without ON UPDATE clause ([#7057](https://github.com/typeorm/typeorm/issues/7057)) ([ddd8cbc](https://github.com/typeorm/typeorm/commit/ddd8cbcdf6d67b6b1425de581c3da5d264a01167)), closes [#6995](https://github.com/typeorm/typeorm/issues/6995) +* resolves Postgres sequence identifier length error ([#7115](https://github.com/typeorm/typeorm/issues/7115)) ([568ef35](https://github.com/typeorm/typeorm/commit/568ef3546e6da6e73f68437fff418901d6232c51)), closes [#7106](https://github.com/typeorm/typeorm/issues/7106) +* return 'null' (instead of 'undefined') on lazy relations that have no results ([#7146](https://github.com/typeorm/typeorm/issues/7146)) ([#7147](https://github.com/typeorm/typeorm/issues/7147)) ([9b278c9](https://github.com/typeorm/typeorm/commit/9b278c99e52bbcdf0d36ece29168785ee8641687)) +* support MongoDB DNS seed list connection ([#7136](https://github.com/typeorm/typeorm/issues/7136)) ([f730bb9](https://github.com/typeorm/typeorm/commit/f730bb9fc1908a65edacc07e5e364648efb48768)), closes [#3347](https://github.com/typeorm/typeorm/issues/3347) [#3133](https://github.com/typeorm/typeorm/issues/3133) +* **data-api:** Fixed how data api driver uses and reuses a client ([#6869](https://github.com/typeorm/typeorm/issues/6869)) ([6ce65fb](https://github.com/typeorm/typeorm/commit/6ce65fbf6be5e696c3ae907d3f8e63b1e7332a1e)) +* use default import of yargs for --help ([#6986](https://github.com/typeorm/typeorm/issues/6986)) ([6ef8ffe](https://github.com/typeorm/typeorm/commit/6ef8ffe387980c51f9f20e9cc03d6199c7068ac5)) + + +### Features + +* add NOWAIT and SKIP LOCKED lock support for MySQL ([#7236](https://github.com/typeorm/typeorm/issues/7236)) ([9407507](https://github.com/typeorm/typeorm/commit/9407507a742a3fe0ea2a836417d6851cad72e74c)), closes [#6530](https://github.com/typeorm/typeorm/issues/6530) +* closure table custom naming ([#7120](https://github.com/typeorm/typeorm/issues/7120)) ([bcd998b](https://github.com/typeorm/typeorm/commit/bcd998b4f384893679e60914d3c52b3d68e7792e)) +* JavaScript file migrations output ([#7253](https://github.com/typeorm/typeorm/issues/7253)) ([ce9cb87](https://github.com/typeorm/typeorm/commit/ce9cb8732cb70458f29c0976d980d34b0f4fa3d7)) +* relations: Orphaned row action ([#7105](https://github.com/typeorm/typeorm/issues/7105)) ([efc2837](https://github.com/typeorm/typeorm/commit/efc283769ed972d022980e681e294d695087a807)) + +## [0.2.29](https://github.com/typeorm/typeorm/compare/0.2.28...0.2.29) (2020-11-02) + +### Bug Fixes + +* allow falsey discriminator values ([#6973](https://github.com/typeorm/typeorm/issues/6973)) ([f3ba242](https://github.com/typeorm/typeorm/commit/f3ba2420396341ad3b808ea8540ea6a2272ff916)), closes [#3891](https://github.com/typeorm/typeorm/issues/3891) +* allow for complex jsonb primary key columns ([#6834](https://github.com/typeorm/typeorm/issues/6834)) ([f95e9d8](https://github.com/typeorm/typeorm/commit/f95e9d8f9a6c7a1117564b3e3f65b5294f8d5ff5)), closes [#6833](https://github.com/typeorm/typeorm/issues/6833) +* Allows valid non-object JSON to be retrieved in simple-json columns ([#6574](https://github.com/typeorm/typeorm/issues/6574)) ([0aedf43](https://github.com/typeorm/typeorm/commit/0aedf43874a6f950614134967bf4b173e4513ba0)), closes [#5501](https://github.com/typeorm/typeorm/issues/5501) +* Cannot read property 'hasMetadata' of undefined ([#5659](https://github.com/typeorm/typeorm/issues/5659)) ([0280cdc](https://github.com/typeorm/typeorm/commit/0280cdc451c35ef73c830eb1191c95d34f6ce06e)), closes [#3685](https://github.com/typeorm/typeorm/issues/3685) +* check if the connection is closed before executing a query. This prevents SQLITE_MISUSE errors (https://sqlite.org/rescode.html#misuse) originating from sqlite itself ([#6975](https://github.com/typeorm/typeorm/issues/6975)) ([5f6bbec](https://github.com/typeorm/typeorm/commit/5f6bbecd6166f1e80ed87d7e6c2c181fe463bdef)) +* check mysql constraint schema on join ([#6851](https://github.com/typeorm/typeorm/issues/6851)) ([d2b914d](https://github.com/typeorm/typeorm/commit/d2b914da6a425d47916c72ac50bfa69bea4847fb)), closes [#6169](https://github.com/typeorm/typeorm/issues/6169) [#6169](https://github.com/typeorm/typeorm/issues/6169) +* correct reading of custom ormconfig.env files ([#6922](https://github.com/typeorm/typeorm/issues/6922)) ([a09fb7f](https://github.com/typeorm/typeorm/commit/a09fb7fb919e7ebb1c174ba4b0abe09b245e0442)) +* explicitly define `query` command's param ([#6899](https://github.com/typeorm/typeorm/issues/6899)) ([4475d80](https://github.com/typeorm/typeorm/commit/4475d8067592b91b857f2b456dc31c5850a21081)), closes [#6896](https://github.com/typeorm/typeorm/issues/6896) +* findRoots should get the defined primary key column ([#6982](https://github.com/typeorm/typeorm/issues/6982)) ([f2ba901](https://github.com/typeorm/typeorm/commit/f2ba9012fe4e851bc667dfdfedc3fd4af665d52b)), closes [#6948](https://github.com/typeorm/typeorm/issues/6948) [#6948](https://github.com/typeorm/typeorm/issues/6948) +* Fix Mongodb delete by ObjectId. Closes [#6552](https://github.com/typeorm/typeorm/issues/6552) ([#6553](https://github.com/typeorm/typeorm/issues/6553)) ([e37eb1e](https://github.com/typeorm/typeorm/commit/e37eb1e8e8544f91c3d0a44b55322966e121b3af)) +* fixes the typescript errors in EntityCreateCommand & SubscriberCreateCommand ([#6824](https://github.com/typeorm/typeorm/issues/6824)) ([0221a93](https://github.com/typeorm/typeorm/commit/0221a933d19125cc0703a7fdd2a243b494ac5e72)) +* handle count multiple PK & edge cases more gracefully ([#6870](https://github.com/typeorm/typeorm/issues/6870)) ([4abfb34](https://github.com/typeorm/typeorm/commit/4abfb342aa390ab4643a1133daaf90c0996b61c2)), closes [#5989](https://github.com/typeorm/typeorm/issues/5989) [#5314](https://github.com/typeorm/typeorm/issues/5314) [#4550](https://github.com/typeorm/typeorm/issues/4550) +* Handle undefined querysets in QueryCommand ([#6910](https://github.com/typeorm/typeorm/issues/6910)) ([6f285dc](https://github.com/typeorm/typeorm/commit/6f285dce1ac315707fe01a892c1c74521a98aae2)), closes [#6612](https://github.com/typeorm/typeorm/issues/6612) +* handle Undefined values in driver URL options ([#6925](https://github.com/typeorm/typeorm/issues/6925)) ([6fa2df5](https://github.com/typeorm/typeorm/commit/6fa2df5ade71a3fee550e3c8fb7bcd7cd02080a8)) +* ILike operator generally available for any driver ([#6945](https://github.com/typeorm/typeorm/issues/6945)) ([37f0d8f](https://github.com/typeorm/typeorm/commit/37f0d8f7938ee5dbcf899a7f2855ea6dc6dc604e)) +* Only check for discriminator conflicts on STI entities ([#2985](https://github.com/typeorm/typeorm/issues/2985)) ([06903d1](https://github.com/typeorm/typeorm/commit/06903d1c914e8082620dbf16551caa302862d328)), closes [#2984](https://github.com/typeorm/typeorm/issues/2984) +* postgresql connection URL can use an UNIX Socket ([#2614](https://github.com/typeorm/typeorm/issues/2614)) ([#6042](https://github.com/typeorm/typeorm/issues/6042)) ([21c4166](https://github.com/typeorm/typeorm/commit/21c41663ccecfa5f2d94f94424f1a9a53e5d817c)) +* prevent create-type commands edge-case TypeErrors ([#6836](https://github.com/typeorm/typeorm/issues/6836)) ([08ec0a8](https://github.com/typeorm/typeorm/commit/08ec0a8ed922225ff529790ad5ff19c0e463954e)), closes [#6831](https://github.com/typeorm/typeorm/issues/6831) +* redundant migration with decimal default ([#6879](https://github.com/typeorm/typeorm/issues/6879)) ([6ff67f7](https://github.com/typeorm/typeorm/commit/6ff67f71fa7ad2bcf8a89c01ead7f54386e35f3a)), closes [#6140](https://github.com/typeorm/typeorm/issues/6140) [#5407](https://github.com/typeorm/typeorm/issues/5407) +* remove @DiscriminatorValue from error message ([#5256](https://github.com/typeorm/typeorm/issues/5256)) ([2bf15ca](https://github.com/typeorm/typeorm/commit/2bf15ca913016ad07080c38c9fc3ee848b60ca4f)), closes [#5255](https://github.com/typeorm/typeorm/issues/5255) +* resolves issue proto-less object validation ([#6884](https://github.com/typeorm/typeorm/issues/6884)) ([e08d9c6](https://github.com/typeorm/typeorm/commit/e08d9c61aab72f16ecd8bd790cb32bf0d164a5af)), closes [#2065](https://github.com/typeorm/typeorm/issues/2065) +* return null for nullable RelationId() column ([#6848](https://github.com/typeorm/typeorm/issues/6848)) ([7147a0d](https://github.com/typeorm/typeorm/commit/7147a0dbe7f2622a21c51edefa3b921f42e04b49)), closes [#6815](https://github.com/typeorm/typeorm/issues/6815) +* subscribers should use the subscribersDir ([5ef9450](https://github.com/typeorm/typeorm/commit/5ef94509b89f11f8337e18046c3f9d9632d234df)) +* support changing comments in MySQL columns ([#6903](https://github.com/typeorm/typeorm/issues/6903)) ([c5143aa](https://github.com/typeorm/typeorm/commit/c5143aab08a04e96aebb55996ed7683d48542bbd)) +* support combination of many-to-one/cacade/composte PK ([#6417](https://github.com/typeorm/typeorm/issues/6417)) ([9a0497b](https://github.com/typeorm/typeorm/commit/9a0497b533b2f6896b8e7d189b36dd3892e58007)) +* support empty `IN` clause across all dialects ([#6887](https://github.com/typeorm/typeorm/issues/6887)) ([9635080](https://github.com/typeorm/typeorm/commit/96350805fb9f02b8fb2c90b5528a15d5cdb9faeb)), closes [#4865](https://github.com/typeorm/typeorm/issues/4865) [#2195](https://github.com/typeorm/typeorm/issues/2195) +* support multiple row insert on oracle ([#6927](https://github.com/typeorm/typeorm/issues/6927)) ([a5eb946](https://github.com/typeorm/typeorm/commit/a5eb946117a18d94c0157188b6a39542c8d50756)), closes [#2434](https://github.com/typeorm/typeorm/issues/2434) +* sync the typeorm-model-shim ([#6891](https://github.com/typeorm/typeorm/issues/6891)) ([c72e48b](https://github.com/typeorm/typeorm/commit/c72e48b9c7b893f8a2483ba1ddaa7ded039fe349)), closes [#6288](https://github.com/typeorm/typeorm/issues/6288) [#5920](https://github.com/typeorm/typeorm/issues/5920) +* TreeRepository based entities primary column supports custom name. ([#6942](https://github.com/typeorm/typeorm/issues/6942)) ([7ec1b75](https://github.com/typeorm/typeorm/commit/7ec1b75f12832e4d99e1ed0cef40755f2b6d650a)) +* use `require` in `ReactNativeDriver` ([#6814](https://github.com/typeorm/typeorm/issues/6814)) ([1a6383c](https://github.com/typeorm/typeorm/commit/1a6383cecd74ee90388db313a74432f7ba12cfdf)), closes [#6811](https://github.com/typeorm/typeorm/issues/6811) +* use correct type for MongoQueryRunner.databaseConnection ([#6906](https://github.com/typeorm/typeorm/issues/6906)) ([da70b40](https://github.com/typeorm/typeorm/commit/da70b405498b142ecc29f7ff01e7a37f88227360)), closes [#6453](https://github.com/typeorm/typeorm/issues/6453) +* use pg ^8 in `init` command ([6ed9906](https://github.com/typeorm/typeorm/commit/6ed990666604ca9b8c0029d4fe972a039ef28570)) +* wrong FK loaded in multi-database environment ([#6828](https://github.com/typeorm/typeorm/issues/6828)) ([c060f95](https://github.com/typeorm/typeorm/commit/c060f95db0e261b02c4b28b19541cabcb1ac4a75)), closes [#6168](https://github.com/typeorm/typeorm/issues/6168) + + +### Features + +* add ability for escaping for Raw() find operator ([#6850](https://github.com/typeorm/typeorm/issues/6850)) ([91b85bf](https://github.com/typeorm/typeorm/commit/91b85bfe6e73ff93db2684a13935b9bd6a9abcfd)) +* add absolute path support to other CLI commands ([#6807](https://github.com/typeorm/typeorm/issues/6807)) ([d9a76e9](https://github.com/typeorm/typeorm/commit/d9a76e91bed06037ff28ec132893f40c09004438)) +* Add SelectQueryBuilder.getOneOrFail() ([#6885](https://github.com/typeorm/typeorm/issues/6885)) ([920e781](https://github.com/typeorm/typeorm/commit/920e7812cd9d405df921f9ae9ce52ba0a9743bea)), closes [#6246](https://github.com/typeorm/typeorm/issues/6246) +* backport ilike from next ([#6862](https://github.com/typeorm/typeorm/issues/6862)) ([c8bf81e](https://github.com/typeorm/typeorm/commit/c8bf81ed2d47ba0822f8d6267ae1997180db2e31)) +* Exit with code 1 on empty migration:generate ([#6978](https://github.com/typeorm/typeorm/issues/6978)) ([8244ea1](https://github.com/typeorm/typeorm/commit/8244ea1371d5cf37e3f80e1b141f5945af38cb5e)) +* schema synchronization for partitioned tables with PostgreSQL 12+ ([#6780](https://github.com/typeorm/typeorm/issues/6780)) ([990442e](https://github.com/typeorm/typeorm/commit/990442e891e91cd829f9f34eff2114d4c623d24b)) +* support `autoEncryption` option for MongoDB ([#6865](https://github.com/typeorm/typeorm/issues/6865)) ([b22c27f](https://github.com/typeorm/typeorm/commit/b22c27feb2dd3892d47a9e82b0d7b11650d059b5)) +* Support column comments in Postgres and CockroachDB ([#6902](https://github.com/typeorm/typeorm/issues/6902)) ([bc623a4](https://github.com/typeorm/typeorm/commit/bc623a42a868eae7c988779abc4cdc0bbf775def)), closes [#3360](https://github.com/typeorm/typeorm/issues/3360) +* support ESM in ormconfig js & ts ([#6853](https://github.com/typeorm/typeorm/issues/6853)) ([7ebca2b](https://github.com/typeorm/typeorm/commit/7ebca2b9b1fd21e546b3a345a069637d6aab4b3e)), closes [#5003](https://github.com/typeorm/typeorm/issues/5003) +* support query comments in the query builder ([#6892](https://github.com/typeorm/typeorm/issues/6892)) ([84c18a9](https://github.com/typeorm/typeorm/commit/84c18a9cab2e87b28eb046b5688bfca4d3ce9da6)), closes [#3643](https://github.com/typeorm/typeorm/issues/3643) +* transactional events in subscriber interface + "transaction" option in FindOptions ([#6996](https://github.com/typeorm/typeorm/issues/6996)) ([0e4b239](https://github.com/typeorm/typeorm/commit/0e4b2397a6e62f5f2c35e5890bba53abe40a49ac)) + +### Performance Improvements + +* Improve MySQL LoadTables Performance ([#6886](https://github.com/typeorm/typeorm/issues/6886)) ([0f0e0b6](https://github.com/typeorm/typeorm/commit/0f0e0b660c83409bb59f806b9f6e099ca8dbc61c)), closes [#6800](https://github.com/typeorm/typeorm/issues/6800) +* Improve replacePropertyNames ([#4760](https://github.com/typeorm/typeorm/issues/4760)) ([d86671c](https://github.com/typeorm/typeorm/commit/d86671cb179751730d0324b23d9f4bcb21010728)) + +## [0.2.28](https://github.com/typeorm/typeorm/compare/0.2.27...0.2.28) (2020-09-30) + +### Bug Fixes + +* FindManyOptions order in parameter typing is important ([51608ae](https://github.com/typeorm/typeorm/commit/51608aebccd31570fc33ba0cd90c3147cdfc70b8)) +* lock Typescript to 3.6.0 ([#6810](https://github.com/typeorm/typeorm/issues/6810)) ([7f7e4d5](https://github.com/typeorm/typeorm/commit/7f7e4d53119506bdbb86999606707cd740859fe7)), closes [#6809](https://github.com/typeorm/typeorm/issues/6809) [#6805](https://github.com/typeorm/typeorm/issues/6805) + +## [0.2.27](https://github.com/typeorm/typeorm/compare/0.2.26...0.2.27) (2020-09-29) + +### Bug Fixes + +* add dummy for FileLogger, ConnectionOptionsReaders, and update gulpfile ([#6763](https://github.com/typeorm/typeorm/issues/6763)) ([180fbd4](https://github.com/typeorm/typeorm/commit/180fbd415da80ce383b426f6d38486aa3826296d)) +* backport FindOperator return types ([#6717](https://github.com/typeorm/typeorm/issues/6717)) ([2b37808](https://github.com/typeorm/typeorm/commit/2b3780836f5fd737fdc58fe4e0eb2ea4200cae66)) +* coerce port to number in ConnectionOptionsEnvReader ([#6786](https://github.com/typeorm/typeorm/issues/6786)) ([55fbb69](https://github.com/typeorm/typeorm/commit/55fbb696c6c2324a67a08061322dc5726844b7d1)), closes [#6781](https://github.com/typeorm/typeorm/issues/6781) +* count() method for multiple primary keys for cockroachdb ([#6745](https://github.com/typeorm/typeorm/issues/6745)) ([dfe8259](https://github.com/typeorm/typeorm/commit/dfe8259ef53a432f1c02607e6ffee662dd4fd8a9)) +* enforce name argument of migration generate command ([#2719](https://github.com/typeorm/typeorm/issues/2719)) ([#6690](https://github.com/typeorm/typeorm/issues/6690)) ([dfcb2db](https://github.com/typeorm/typeorm/commit/dfcb2db216d6ed33946dfa190e19eb14c0fed390)), closes [#4798](https://github.com/typeorm/typeorm/issues/4798) [#4805](https://github.com/typeorm/typeorm/issues/4805) [#4798](https://github.com/typeorm/typeorm/issues/4798) [#4805](https://github.com/typeorm/typeorm/issues/4805) +* ensure browser builds don't include any non-browser modules ([#6743](https://github.com/typeorm/typeorm/issues/6743)) ([c714867](https://github.com/typeorm/typeorm/commit/c714867d3d0c43ccbb7ca8fb3ce969207e4d5c04)), closes [#6739](https://github.com/typeorm/typeorm/issues/6739) +* hdb-pool is not namespaced under [@sap](https://github.com/sap) ([#6700](https://github.com/typeorm/typeorm/issues/6700)) ([9583430](https://github.com/typeorm/typeorm/commit/9583430e8282d1ad758724957971a5d5d9664f63)), closes [#6697](https://github.com/typeorm/typeorm/issues/6697) +* migration:generate issue with onUpdate using mariadb 10.4 ([#6714](https://github.com/typeorm/typeorm/issues/6714)) ([6e28322](https://github.com/typeorm/typeorm/commit/6e28322ca65ba739bf0d767075016bc0cae7a48c)) +* prevent multiple `release` listeners in PostgresQueryRunner ([#6708](https://github.com/typeorm/typeorm/issues/6708)) ([208cf6b](https://github.com/typeorm/typeorm/commit/208cf6b0511a2d565c7999837497bb6cf8f8e7c7)), closes [#6699](https://github.com/typeorm/typeorm/issues/6699) +* prevent wrong returned entity in ReturningResultsEntityUpdator ([#6440](https://github.com/typeorm/typeorm/issues/6440)) ([c1c8e88](https://github.com/typeorm/typeorm/commit/c1c8e88f8945bf6a03bde728de370f5c61c5bdb8)) +* resolve issues ora-00972:identifier is too long ([#6751](https://github.com/typeorm/typeorm/issues/6751)) ([b55a417](https://github.com/typeorm/typeorm/commit/b55a417ea4852ad2e66091cfa800534f7ccdd3c9)), closes [#5067](https://github.com/typeorm/typeorm/issues/5067) [#5067](https://github.com/typeorm/typeorm/issues/5067) +* sql.js v1.2+ don't support undefined parameters ([#6698](https://github.com/typeorm/typeorm/issues/6698)) ([ea59b8d](https://github.com/typeorm/typeorm/commit/ea59b8d46b2a36ac251f43c8a8fb98ff15ab4e2d)), closes [#5720](https://github.com/typeorm/typeorm/issues/5720) + +### Features + +* add option to pass postgres server notices to client logger ([#6215](https://github.com/typeorm/typeorm/issues/6215)) ([5084e47](https://github.com/typeorm/typeorm/commit/5084e47be4fd42316ad47e6102645534fae45d9f)), closes [#2216](https://github.com/typeorm/typeorm/issues/2216) +* backport SQLite Busy handler & WAL mode enable ([#6588](https://github.com/typeorm/typeorm/issues/6588)) ([7a52f18](https://github.com/typeorm/typeorm/commit/7a52f18c86613292c3503484eac332f59141a6e3)) +* Beautify generated SQL for migrations ([#6685](https://github.com/typeorm/typeorm/issues/6685)) ([370442c](https://github.com/typeorm/typeorm/commit/370442c27a0aecd67eeb44f6077922dda16bcef8)), closes [#4415](https://github.com/typeorm/typeorm/issues/4415) +* create EntityTarget and use instead of EntitySchema / ObjectType / etc ([#6701](https://github.com/typeorm/typeorm/issues/6701)) ([8b68f40](https://github.com/typeorm/typeorm/commit/8b68f40a01b6cdc0e8d21492d988fe21cbef64de)) + +### Reverts + +* Revert "fix: properly override database url properties (#6247)" (#6802) ([45b980c](https://github.com/typeorm/typeorm/commit/45b980cf7fd61b0ee2e9560d9aadb96ce331d5cb)), closes [#6247](https://github.com/typeorm/typeorm/issues/6247) [#6802](https://github.com/typeorm/typeorm/issues/6802) + +## [0.2.26](https://github.com/typeorm/typeorm/compare/0.2.25...0.2.26) (2020-09-10) + +### Bug Fixes + +* @JoinTable does not respect inverseJoinColumns referenced column width ([#6444](https://github.com/typeorm/typeorm/issues/6444)) ([f642a9e](https://github.com/typeorm/typeorm/commit/f642a9e)), closes [#6442](https://github.com/typeorm/typeorm/issues/6442) +* add missing schema for OracleDriver ([#6673](https://github.com/typeorm/typeorm/issues/6673)) ([8b8bc35](https://github.com/typeorm/typeorm/commit/8b8bc35)) +* change InsertQueryBuilder.values() with an empty array into a no-op ([#6584](https://github.com/typeorm/typeorm/issues/6584)) ([9d2df28](https://github.com/typeorm/typeorm/commit/9d2df28)), closes [#3111](https://github.com/typeorm/typeorm/issues/3111) +* Child entities not being saved correctly with cascade actions ([#6219](https://github.com/typeorm/typeorm/issues/6219)) ([16a2d80](https://github.com/typeorm/typeorm/commit/16a2d80)) +* correctly parse connection URI with query params ([#6390](https://github.com/typeorm/typeorm/issues/6390)) ([54a3a15](https://github.com/typeorm/typeorm/commit/54a3a15)), closes [#6389](https://github.com/typeorm/typeorm/issues/6389) +* decorators should implement the official TypeScript interface ([#6398](https://github.com/typeorm/typeorm/issues/6398)) ([c23c888](https://github.com/typeorm/typeorm/commit/c23c888)), closes [#5922](https://github.com/typeorm/typeorm/issues/5922) +* DeepPartial with any and {[k: string]: any} ([#6581](https://github.com/typeorm/typeorm/issues/6581)) ([8d90d40](https://github.com/typeorm/typeorm/commit/8d90d40)), closes [#6580](https://github.com/typeorm/typeorm/issues/6580) [#6580](https://github.com/typeorm/typeorm/issues/6580) +* exporting missing load event ([#6396](https://github.com/typeorm/typeorm/issues/6396)) ([c6336aa](https://github.com/typeorm/typeorm/commit/c6336aa)) +* get correct insert ids for multiple entities inserted ([#6668](https://github.com/typeorm/typeorm/issues/6668)) ([ef2011d](https://github.com/typeorm/typeorm/commit/ef2011d)), closes [#2131](https://github.com/typeorm/typeorm/issues/2131) [#5973](https://github.com/typeorm/typeorm/issues/5973) [#2131](https://github.com/typeorm/typeorm/issues/2131) +* getPendingMigrations isn't properly working ([#6372](https://github.com/typeorm/typeorm/issues/6372)) ([7c0da1c](https://github.com/typeorm/typeorm/commit/7c0da1c)) +* handle 'error' events from pool connection ([#6262](https://github.com/typeorm/typeorm/issues/6262)) ([ae3cf0e](https://github.com/typeorm/typeorm/commit/ae3cf0e)) +* insert IN(null) instead of IN() when In([]) empty array for mysqlDriver ([#6237](https://github.com/typeorm/typeorm/issues/6237)) ([6f6bdbd](https://github.com/typeorm/typeorm/commit/6f6bdbd)) +* make only a single SELECT to get inserted default and generated values of multiple entities ([#6669](https://github.com/typeorm/typeorm/issues/6669)) ([4fc4a1b](https://github.com/typeorm/typeorm/commit/4fc4a1b)), closes [#6266](https://github.com/typeorm/typeorm/issues/6266) [#6266](https://github.com/typeorm/typeorm/issues/6266) +* Migration issues with scale & precision in sqlite/sql.js ([#6638](https://github.com/typeorm/typeorm/issues/6638)) ([0397e44](https://github.com/typeorm/typeorm/commit/0397e44)), closes [#6636](https://github.com/typeorm/typeorm/issues/6636) +* mysql migration: make sure the indices sql which left-join be the same database ([#6426](https://github.com/typeorm/typeorm/issues/6426)) ([906d97f](https://github.com/typeorm/typeorm/commit/906d97f)) +* pass `ids_` to alias builder to prevent length overflow ([#6624](https://github.com/typeorm/typeorm/issues/6624)) ([cf3ad62](https://github.com/typeorm/typeorm/commit/cf3ad62)) +* pass formatOptions to Data API Client, fix extensions ([#6404](https://github.com/typeorm/typeorm/issues/6404)) ([9abab82](https://github.com/typeorm/typeorm/commit/9abab82)), closes [#1](https://github.com/typeorm/typeorm/issues/1) +* Query builder makes query with joins, without limit for inherited entities ([#6402](https://github.com/typeorm/typeorm/issues/6402)) ([874e573](https://github.com/typeorm/typeorm/commit/874e573)), closes [#6399](https://github.com/typeorm/typeorm/issues/6399) +* remove unnecessary optionality from Raw operator's columnAlias argument ([#6321](https://github.com/typeorm/typeorm/issues/6321)) ([0d99b46](https://github.com/typeorm/typeorm/commit/0d99b46)) +* resolve missing decorators on shim ([#6354](https://github.com/typeorm/typeorm/issues/6354)) ([8e2d97d](https://github.com/typeorm/typeorm/commit/8e2d97d)), closes [#6093](https://github.com/typeorm/typeorm/issues/6093) +* revert fix handle URL objects as column field values ([#6145](https://github.com/typeorm/typeorm/issues/6145)) ([e073e02](https://github.com/typeorm/typeorm/commit/e073e02)) +* SqlQueryRunner.hasColumn was not working ([#6146](https://github.com/typeorm/typeorm/issues/6146)) ([a595fed](https://github.com/typeorm/typeorm/commit/a595fed)), closes [#5718](https://github.com/typeorm/typeorm/issues/5718) +* support multiple `JoinColumn`s in EntitySchema ([#6397](https://github.com/typeorm/typeorm/issues/6397)) ([298a3b9](https://github.com/typeorm/typeorm/commit/298a3b9)), closes [#5444](https://github.com/typeorm/typeorm/issues/5444) +* Unnecessary migrations for fulltext indices ([#6634](https://github.com/typeorm/typeorm/issues/6634)) ([c81b405](https://github.com/typeorm/typeorm/commit/c81b405)), closes [#6633](https://github.com/typeorm/typeorm/issues/6633) +* unnecessary migrations for unsigned numeric types ([#6632](https://github.com/typeorm/typeorm/issues/6632)) ([7ddaf23](https://github.com/typeorm/typeorm/commit/7ddaf23)), closes [#2943](https://github.com/typeorm/typeorm/issues/2943) [/github.com/typeorm/typeorm/pull/6632#pullrequestreview-480932808](https://github.com//github.com/typeorm/typeorm/pull/6632/issues/pullrequestreview-480932808) +* update query deep partial TypeScript definition ([#6085](https://github.com/typeorm/typeorm/issues/6085)) ([23110d1](https://github.com/typeorm/typeorm/commit/23110d1)) + +### Features + +* add AWS configurationOptions to aurora-data-api-pg connector ([#6106](https://github.com/typeorm/typeorm/issues/6106)) ([203f51d](https://github.com/typeorm/typeorm/commit/203f51d)) +* add better-sqlite3 driver ([#6224](https://github.com/typeorm/typeorm/issues/6224)) ([2241451](https://github.com/typeorm/typeorm/commit/2241451)) +* add postgres connection timeout option ([#6160](https://github.com/typeorm/typeorm/issues/6160)) ([0072149](https://github.com/typeorm/typeorm/commit/0072149)) +* FileLogger accepts custom file path ([#6642](https://github.com/typeorm/typeorm/issues/6642)) ([c99ba40](https://github.com/typeorm/typeorm/commit/c99ba40)), closes [#4410](https://github.com/typeorm/typeorm/issues/4410) +* implement postgres ltree ([#6480](https://github.com/typeorm/typeorm/issues/6480)) ([43a7386](https://github.com/typeorm/typeorm/commit/43a7386)), closes [#4193](https://github.com/typeorm/typeorm/issues/4193) +* support absolute paths in migrationsDir for the CLI ([#6660](https://github.com/typeorm/typeorm/issues/6660)) ([2b5f139](https://github.com/typeorm/typeorm/commit/2b5f139)) +* support cjs extension for ormconfig ([#6285](https://github.com/typeorm/typeorm/issues/6285)) ([6eeb03a](https://github.com/typeorm/typeorm/commit/6eeb03a)) + ## [0.2.25](https://github.com/typeorm/typeorm/compare/0.2.24...0.2.25) (2020-05-19) ### Bug Fixes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c619c186f2..e92a577913e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ As a contributor, here are the guidelines we would like you to follow: There are several ways how you can ask your question: * You can create a question on [StackOverflow](https://stackoverflow.com/questions/tagged/typeorm) where the questions should be tagged with tag `typeorm`. -* You can ask on [Slack](https://join.slack.com/t/typeorm/shared_invite/enQtNDQ1MzA3MDA5MTExLTUxNTZhM2Q4NDNhMjMzNjQ2NGM1ZjI1ZGRkNjJjYzI4OTZjMGYyYTc0MzAxYTdjMWE3ZDIxOWUzZTdlM2QxNTY) +* You can ask on [Slack](https://join.slack.com/t/typeorm/shared_invite/zt-gej3gc00-hR~L~DqGUJ7qOpGy4SSq3g) * You can create issue on [github](https://github.com/typeorm/typeorm/issues) * If you have a Skype then try to find me there (`Umed Khudoiberdiev`) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 8e96c64ffa5..00000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,36 +0,0 @@ -**Issue type:** - -[ ] question -[ ] bug report -[ ] feature request -[ ] documentation issue - -**Database system/driver:** - -[ ] `cordova` -[ ] `mongodb` -[ ] `mssql` -[ ] `mysql` / `mariadb` -[ ] `oracle` -[ ] `postgres` -[ ] `cockroachdb` -[ ] `sqlite` -[ ] `sqljs` -[ ] `react-native` -[ ] `expo` - -**TypeORM version:** - -[ ] `latest` -[ ] `@next` -[ ] `0.x.x` (or put your version here) - -**Steps to reproduce or a small repository showing the problem:** - - diff --git a/README-zh_CN.md b/README-zh_CN.md index 2c6f2e28066..4b7dc796d99 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -4,8 +4,8 @@

- - + + @@ -22,13 +22,13 @@ TypeORM 是一个 [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) 框架,它可以运行在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、Expo 和 Electron 平台上,可以与 TypeScript 和 JavaScript (ES5,ES6,ES7,ES8)一起使用。 它的目标是始终支持最新的 JavaScript 特性并提供额外的特性以帮助你开发任何使用数据库的(不管是只有几张表的小型应用还是拥有多数据库的大型企业应用)应用程序。 -不同于现有的所有其他 JavaScript ORM 框架,TypeORM 支持 [Active Record](./docs/active-record-data-mapper.md#what-is-the-active-record-pattern) 和 [Data Mapper](./docs/active-record-data-mapper.md#what-is-the-data-mapper-pattern) 模式,这意味着你可以以最高效的方式编写高质量的、松耦合的、可扩展的、可维护的应用程序。 +不同于现有的所有其他 JavaScript ORM 框架,TypeORM 支持 [Data Mapper](./docs/zh_CN/active-record-data-mapper.md#什么是data-mapper模式) 和 [Active Record](./docs/zh_CN/active-record-data-mapper.md#什么是active-record模式) 模式,这意味着你可以以最高效的方式编写高质量的、松耦合的、可扩展的、可维护的应用程序。 TypeORM 参考了很多其他优秀 ORM 的实现, 比如 [Hibernate](http://hibernate.org/orm/), [Doctrine](http://www.doctrine-project.org/) 和 [Entity Framework](https://www.asp.net/entity-framework)。 TypeORM 的一些特性: -- 同时支持 [DataMapper](./docs/active-record-data-mapper.md#what-is-the-data-mapper-pattern) 和 [ActiveRecord](./docs/active-record-data-mapper.md#what-is-the-active-record-pattern) (随你选择) +- 同时支持 [DataMapper](./docs/zh_CN/active-record-data-mapper.md#什么是data-mapper模式) 和 [ActiveRecord](./docs/zh_CN/active-record-data-mapper.md#什么是active-record模式) (随你选择) - 实体和列 - 数据库特性列类型 - 实体管理 @@ -69,7 +69,7 @@ TypeORM 的一些特性: 通过使用 `TypeORM` 你的 `models` 看起来如下: -```typescript +```ts import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; @Entity() @@ -90,7 +90,7 @@ export class User { 逻辑操作如下: -```typescript +```ts const user = new User(); user.firstName = "Timber"; user.lastName = "Saw"; @@ -106,7 +106,7 @@ await repository.remove(timber); 或者,如果你更喜欢使用 `ActiveRecord` 模式,也可以这样用: -```typescript +```ts import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm"; @Entity() @@ -127,7 +127,7 @@ export class User extends BaseEntity { 逻辑操作如下所示: -```typescript +```ts const user = new User(); user.firstName = "Timber"; user.lastName = "Saw"; @@ -241,7 +241,7 @@ typeorm init --name MyProject --database mysql 此命令将在 `MyProject` 目录中生成一个包含以下文件的新项目: -``` +```sh MyProject ├── src // TypeScript 代码 │ ├── entity // 存储实体(数据库模型)的位置 @@ -259,9 +259,9 @@ MyProject 接下来安装项目依赖项: -``` -cd MyProject -npm install +```sh +$ cd MyProject +$ npm install ``` 在安装过程中,编辑 `ormconfig.json` 文件并在其中编辑自己的数据库连接配置选项: @@ -286,8 +286,8 @@ npm install 完成配置并安装所有 node modules 后,即可运行应用程序: -``` -npm start +```sh +$ npm start ``` 至此你的应用程序应该成功运行并将新用户插入数据库。你可以继续使用此项目并集成所需的其他模块并创建更多实体。 @@ -305,7 +305,7 @@ npm start 举个例子, 你有一个 `Photo` 模型: -```typescript +```ts export class Photo { id: number; name: string; @@ -323,7 +323,7 @@ export class Photo { 让我们将 `Photo` 模型作为一个实体 -```typescript +```ts import { Entity } from "typeorm"; @Entity() @@ -344,7 +344,7 @@ export class Photo { 要添加数据库列,你只需要将要生成的实体属性加上 `@Column` 装饰器。 -```typescript +```ts import { Entity, Column } from "typeorm"; @Entity() @@ -378,7 +378,7 @@ export class Photo { 每个**必须**至少有一个主键列。这是必须的,你无法避免。要使列成为主键,你需要使用 `@PrimaryColumn` 装饰器。 -```typescript +```ts import { Entity, Column, PrimaryColumn } from "typeorm"; @Entity() @@ -407,7 +407,7 @@ export class Photo { 假设你希望 id 列自动生成(这称为 auto-increment/sequence/serial/generated identity column)。为此你需要将`@PrimaryColumn` 装饰器更改为 `@PrimaryGeneratedColumn` 装饰器: -```typescript +```ts import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"; @Entity() @@ -437,7 +437,7 @@ export class Photo { 接下来,让我们修改数据类型。默认情况下,字符串被映射到一个 varchar(255) 类型(取决于数据库类型)。 数字被映射到一个类似 integer 类型(取决于数据库类型)。但是我们不希望所有的列都是有限的 varchars 或 integer,让我们修改下代码以设置想要的数据类型: -```typescript +```ts import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"; @Entity() @@ -464,13 +464,13 @@ export class Photo { } ``` -列类型是特定于数据库的。你可以设置数据库支持的任何列类型。有关支持的列类型的更多信息,请参见[此处](./docs/entities.md#column-types)。 +列类型是特定于数据库的。你可以设置数据库支持的任何列类型。有关支持的列类型的更多信息,请参见[此处](./docs/zh_CN/entities.md#列类型)。 ### 创建数据库的连接 当实体被创建后,让我们创建一个`index.ts`(或`app.ts`,无论你怎么命名)文件,并配置数据库连接:: -```typescript +```ts import "reflect-metadata"; import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; @@ -502,7 +502,7 @@ createConnection({ 之后当我们创建更多实体时,都需要一一将它们添加到配置中的实体中,但是这不是很方便,所以我们可以设置加载整个目录,从中连接所有实体并使用: -```typescript +```ts import { createConnection } from "typeorm"; createConnection({ @@ -547,7 +547,7 @@ createConnection({ 现在创建一个新的 photo 存到数据库: -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; @@ -573,7 +573,7 @@ createConnection(/*...*/) 我们可以使用ES8(ES2017)的新特性,并使用 async/await 语法代替: -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; @@ -598,7 +598,7 @@ createConnection(/*...*/) 例如,加载已经保存的实体: -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; @@ -613,13 +613,13 @@ createConnection(/*...*/) `savedPhotos` 是一个 Photo 对象数组,其中包含从数据库加载的数据。 -了解更多有关 [EntityManager](./docs/working-with-entity-manager.md) 的信息。 +了解更多有关 [EntityManager](./docs/zh_CN/working-with-entity-manager.md) 的信息。 ### 使用 Repositories 现在让我们重构之前的代码,并使用 `Repository` 替代 `EntityManager`。每个实体都有自己的repository,可以处理其实体的所有操作。当你经常处理实体时,Repositories 比 EntityManagers 更方便使用: -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; @@ -643,13 +643,13 @@ createConnection(/*...*/) .catch(error => console.log(error)); ``` -了解更多有关 [Repository](./docs/working-with-repository.md) 的信息。 +了解更多有关 [Repository](./docs/zh_CN/working-with-repository.md) 的信息。 ### 从数据库加载 让我们使用 Repository 尝试更多的加载操作: -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; @@ -682,7 +682,7 @@ createConnection(/*...*/) 让我们从数据库加载出 photo,更新并保存到数据库: -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; @@ -702,7 +702,7 @@ createConnection(/*...*/) 让我们从数据库中删除 Photo: -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; @@ -721,7 +721,7 @@ createConnection(/*...*/) 要与另一个类创建一对一的关系。先在 `PhotoMetadata.ts` 中创建一个新类。此 PhotoMetadata 类应包含 photo 的其他元信息: -```typescript +```ts import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn } from "typeorm"; import { Photo } from "./Photo"; @@ -777,7 +777,7 @@ export class PhotoMetadata { 现在让我们来创建一个 photo,它的元信息将它们互相连接起来。 -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; import { PhotoMetadata } from "./entity/PhotoMetadata"; @@ -820,7 +820,7 @@ createConnection(/*...*/) 关系可以是单向的或双向的。目前 PhotoMetadata 和 Photo 之间的关系是单向的。关系的所有者是 PhotoMetadata,而 Photo 对 PhotoMetadata 一无所知。这使得从 Photo 中访问 PhotoMetadata 变得很复杂。要解决这个问题,我们应该在 PhotoMetadata 和 Photo 之间建立双向关系。让我们来修改一下实体: -```typescript +```ts import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn } from "typeorm"; import { Photo } from "./Photo"; @@ -855,7 +855,7 @@ export class Photo { 在一个查询中加载 photo 及 photo metadata 有两种方法。使用 `find *` 或使用 `QueryBuilder`。我们先使用 `find *` 方法。 `find *` 方法允许你使用 `FindOneOptions` / `FindManyOptions` 接口指定对象。 -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; import { PhotoMetadata } from "./entity/PhotoMetadata"; @@ -869,11 +869,11 @@ createConnection(/*...*/) .catch(error => console.log(error)); ``` -photos 包含来自数据库的 photos 数组,每个 photo 包含其 photo metadata。详细了解本文档中的[Find 选项](./docs/find-options.md)。 +photos 包含来自数据库的 photos 数组,每个 photo 包含其 photo metadata。详细了解本文档中的[Find 选项](./docs/zh_CN/find-options.md)。 使用find选项很简单,但是如果你需要更复杂的查询,则应该使用 `QueryBuilder`。 `QueryBuilder` 使用更优雅的方式执行更复杂的查询: -```typescript +```ts import { createConnection } from "typeorm"; import { Photo } from "./entity/Photo"; import { PhotoMetadata } from "./entity/PhotoMetadata"; @@ -896,7 +896,7 @@ createConnection(/*...*/) 我们可以在关系中设置 `cascade` 选项,这时就可以在保存其他对象的同时保存相关对象。让我们更改一下的 photo 的 `@OneToOne` 装饰器: -```typescript +```ts export class Photo { /// ... 其他列 @@ -909,7 +909,7 @@ export class Photo { 使用 `cascade` 允许就不需要边存 photo 边存元数据对象。我们可以简单地保存一个 photo 对象,由于使用了 cascade,metadata 也将自动保存。 -```typescript +```ts createConnection(options) .then(async connection => { // 创建 photo 对象 @@ -966,7 +966,7 @@ export class Author { 现在让我们将关系的所有者方添加到 Photo 实体中: -```typescript +```ts import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from "typeorm"; import { PhotoMetadata } from "./PhotoMetadata"; import { Author } from "./Author"; @@ -1011,7 +1011,7 @@ export class Photo { 假设一个 photo 可以放在多个 albums 中,每个 albums 可以包含多个 photo。让我们创建一个`Album`类: -```typescript +```ts import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable } from "typeorm"; @Entity() @@ -1032,7 +1032,7 @@ export class Album { 现在添加反向关系到`Photo`类: -```typescript +```ts export class Photo { /// ... 其他列 @@ -1054,7 +1054,7 @@ export class Photo { 记得在 ORM 中使用 ConnectionOptions 注册`Album`类: -```typescript +```ts const options: ConnectionOptions = { // ... 其他选项 entities: [Photo, PhotoMetadata, Author, Album] @@ -1063,7 +1063,7 @@ const options: ConnectionOptions = { 现在让我们将 albums 和 photos 插入我们的数据库: -```typescript +```ts let connection = await createConnection(options); // 创建一些 albums @@ -1090,7 +1090,7 @@ const loadedPhoto = await connection.getRepository(Photo).findOne(1, { relations `loadedPhoto` 如下所示: -```typescript +```ts { id: 1, name: "Me and Bears", @@ -1110,7 +1110,7 @@ const loadedPhoto = await connection.getRepository(Photo).findOne(1, { relations 你可以使用 QueryBuilder 构建几乎任何复杂性的 SQL 查询。例如,可以这样做: -```typescript +```ts let photos = await connection .getRepository(Photo) .createQueryBuilder("photo") // 第一个参数是别名。即photos。 该参数必须指定。 @@ -1128,7 +1128,7 @@ let photos = await connection 此查询选择所有 published 的 name 等于"My"或"Mishka"的 photos。它将从结果中的第 5 个(分页偏移)开始,并且仅选择 10 个结果(分页限制)。得到的结果将按 ID 降序排序。photo 的 albums 将被 left-joined,其元数据将被 inner joined。 由于 QueryBuilder 的自由度更高,因此在项目中可能会大量的使用它。 -更多关于 QueryBuilder 的信息,[可查看](./docs/select-query-builder.md)。 +更多关于 QueryBuilder 的信息,[可查看](./docs/zh_CN/select-query-builder.md)。 ## 示例 diff --git a/README.md b/README.md index 516b37cdd50..0a1c9ed2a7d 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@

- - + + @@ -16,7 +16,7 @@ Codecov - +
@@ -39,7 +39,7 @@ maintainable applications the most productive way. TypeORM is highly influenced by other ORMs, such as [Hibernate](http://hibernate.org/orm/), [Doctrine](http://www.doctrine-project.org/) and [Entity Framework](https://www.asp.net/entity-framework). -Some TypeORM features: +## Features * supports both [DataMapper](./docs/active-record-data-mapper.md#what-is-the-data-mapper-pattern) and [ActiveRecord](./docs/active-record-data-mapper.md#what-is-the-active-record-pattern) (your choice) * entities and columns @@ -175,7 +175,7 @@ await timber.remove(); 3. You may need to install node typings: - `npm install @types/node --save` + `npm install @types/node --save-dev` 4. Install a database driver: @@ -209,11 +209,12 @@ await timber.remove(); * for **SAP Hana** ``` - npm config set @sap:registry https://npm.sap.com npm i @sap/hana-client npm i hdb-pool ``` + *SAP Hana support made possible by sponsorship of [Neptune Software](https://www.neptune-software.com/).* + * for **MongoDB** (experimental) `npm install mongodb --save` @@ -324,6 +325,9 @@ creating more entities. > You can generate an even more advanced project with express installed by running `typeorm init --name MyProject --database mysql --express` command. +> You can generate docker-compose file by running +`typeorm init --name MyProject --database postgres --docker` command. + ## Step-by-Step Guide What are you expecting from ORM? @@ -1031,7 +1035,7 @@ createConnection(options).then(async connection => { metadata.width = 480; metadata.compressed = true; metadata.comment = "cybershoot"; - metadata.orientation = "portait"; + metadata.orientation = "portrait"; photo.metadata = metadata; // this way we connect them @@ -1198,6 +1202,8 @@ let photo = new Photo(); photo.name = "Me and Bears"; photo.description = "I am near polar bears"; photo.filename = "photo-with-bears.jpg"; +photo.views = 1 +photo.isPublished = true photo.albums = [album1, album2]; await connection.manager.save(photo); @@ -1284,6 +1290,7 @@ There are several extensions that simplify working with TypeORM and integrating * [TypeORM integration](https://github.com/typeorm/typeorm-routing-controllers-extensions) with [routing-controllers](https://github.com/pleerock/routing-controllers) * Models generation from existing database - [typeorm-model-generator](https://github.com/Kononnable/typeorm-model-generator) * Fixtures loader - [typeorm-fixtures-cli](https://github.com/RobinCK/typeorm-fixtures) +* ER Diagram generator - [typeorm-uml](https://github.com/eugene-manuilov/typeorm-uml/) ## Contributing @@ -1295,7 +1302,7 @@ This project exists thanks to all the people who contribute: ## Sponsors -Open source is hard and time-consuming. If you want to invest into TypeORM's future you can become a sponsor and make our core team to spend more time on TypeORM's improvements and new features. [Become a sponsor](https://opencollective.com/typeorm) +Open source is hard and time-consuming. If you want to invest into TypeORM's future you can become a sponsor and allow our core team to spend more time on TypeORM's improvements and new features. [Become a sponsor](https://opencollective.com/typeorm) diff --git a/docker-compose.yml b/docker-compose.yml index 81a000efc5f..47415bce4e6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -62,12 +62,18 @@ services: # cockroachdb cockroachdb: - image: "cockroachdb/cockroach-unstable:v19.1.0-rc.2" + image: "cockroachdb/cockroach:v19.2.9" container_name: "typeorm-cockroachdb" command: start --insecure ports: - "26257:26257" + oracle: + image: imnotjames/oracle-xe:18 + container_name: "typeorm-oracle" + ports: + - "1521:1521" + # sap hana (works only on linux) # hanaexpress: # image: "store/saplabs/hanaexpress:2.00.040.00.20190729.1" diff --git a/docs/caching.md b/docs/caching.md index 85013484301..36393434f45 100644 --- a/docs/caching.md +++ b/docs/caching.md @@ -113,7 +113,7 @@ await connection.queryResultCache.remove(["users_admins"]); By default, TypeORM uses a separate table called `query-result-cache` and stores all queries and results there. -Table name is configurable, so you could change its by give the value in the tableName property. +Table name is configurable, so you could change it by specifying a different value in the tableName property. Example: ```typescript @@ -188,7 +188,7 @@ In case you want to connect to a redis-cluster using IORedis's cluster functiona } ``` -Note that, you can still use options as first argument of IORedis's cluster constructor. +Note that, you can still use options as the first argument of IORedis's cluster constructor. ```typescript { diff --git a/docs/connection-options.md b/docs/connection-options.md index 485b8d17fb0..734dfc3a76c 100644 --- a/docs/connection-options.md +++ b/docs/connection-options.md @@ -5,6 +5,7 @@ * [`mysql` / `mariadb` connection options](#mysql--mariadb-connection-options) * [`postgres` / `cockroachdb` connection options](#postgres--cockroachdb-connection-options) * [`sqlite` connection options](#sqlite-connection-options) +* [`better-sqlite3` connection options](#better-sqlite3-connection-options) * [`cordova` connection options](#cordova-connection-options) * [`react-native` connection options](#react-native-connection-options) * [`nativescript` connection options](#nativescript-connection-options) @@ -22,7 +23,7 @@ Connection options is a connection configuration you pass to `createConnection` ## Common connection options * `type` - Database type. You must specify what database engine you use. - Possible values are "mysql", "postgres", "cockroachdb", "mariadb", "sqlite", "cordova", "nativescript", + Possible values are "mysql", "postgres", "cockroachdb", "mariadb", "sqlite", "better-sqlite3", "cordova", "nativescript", "oracle", "mssql", "mongodb", "sqljs", "react-native". This option is **required**. @@ -179,10 +180,20 @@ See [SSL options](https://github.com/mysqljs/mysql#ssl-options). * `poolErrorHandler` - A function that get's called when underlying pool emits `'error'` event. Takes single parameter (error instance) and defaults to logging with `warn` level. +* `logNotifications` - A boolean to determine whether postgres server [notice messages](https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html) and [notification events](https://www.postgresql.org/docs/current/sql-notify.html) should be included in client's logs with `info` level (default: `false`). + ## `sqlite` connection options * `database` - Database path. For example "./mydb.sql" +## `better-sqlite3` connection options + +* `database` - Database path. For example "./mydb.sql" + +* `statementCacheSize` - Cache size of sqlite statement to speed up queries (default 100). + +* `prepareDatabase` - Function to run before a database is used in typeorm. You can access original better-sqlite3 Database object here. + ## `cordova` connection options * `database` - Database name @@ -357,6 +368,10 @@ See [SSL options](https://github.com/mysqljs/mysql#ssl-options). * `port` - Database host port. Default mongodb port is `27017`. +* `username` - Database username (replacement for `auth.user`). + +* `password` - Database password (replacement for `auth.password`). + * `database` - Database name. * `poolSize` - Set the maximum pool size for each individual server or proxy connection. diff --git a/docs/entities.md b/docs/entities.md index a5893b54acc..f8d12af430a 100644 --- a/docs/entities.md +++ b/docs/entities.md @@ -311,7 +311,7 @@ or `date`, `time`, `time without time zone`, `time with time zone`, `interval`, `bool`, `boolean`, `enum`, `point`, `line`, `lseg`, `box`, `path`, `polygon`, `circle`, `cidr`, `inet`, `macaddr`, `tsvector`, `tsquery`, `uuid`, `xml`, `json`, `jsonb`, `int4range`, `int8range`, `numrange`, -`tsrange`, `tstzrange`, `daterange`, `geometry`, `geography`, `cube` +`tsrange`, `tstzrange`, `daterange`, `geometry`, `geography`, `cube`, `ltree` ### Column types for `cockroachdb` @@ -480,7 +480,7 @@ Note you **MUST NOT** have any comma in values you write. There is a special column type called `simple-json` which can store any values which can be stored in database via JSON.stringify. Very useful when you do not have json type in your database and you want to store and load object -without any hustle. +without any hassle. For example: ```typescript @@ -705,11 +705,11 @@ export class Category { @Column() description: string; - @OneToMany(type => Category, category => category.children) + @ManyToOne(type => Category, category => category.children) parent: Category; - @ManyToOne(type => Category, category => category.parent) - children: Category; + @OneToMany(type => Category, category => category.parent) + children: Category[]; } ``` diff --git a/docs/find-options.md b/docs/find-options.md index f1df389d2e1..b506594de23 100644 --- a/docs/find-options.md +++ b/docs/find-options.md @@ -74,6 +74,14 @@ userRepository.find({ }); ``` +* `withDeleted` - include entities which have been soft deleted with `softDelete` or `softRemove`, e.g. have their `@DeleteDateColumn` column set. By default, soft deleted entities are not included. + +```typescript +userRepository.find({ + withDeleted: true +}); +``` + `find` methods which return multiple entities (`find`, `findAndCount`, `findByIds`) also accept following options: * `skip` - offset (paginated) from where entities should be taken. @@ -367,8 +375,39 @@ will execute following query: SELECT * FROM "post" WHERE "currentDate" > NOW() ``` -> Note: beware with `Raw` operator. It executes pure SQL from supplied expression and should not contain a user input, - otherwise it will lead to SQL-injection. +If you need to provide user input, you should not include the user input directly in your query as this may create a SQL injection vulnerability. Instead, you can use the second argument of the `Raw` function to provide a list of parameters to bind to the query. + +```ts +import {Raw} from "typeorm"; + +const loadedPosts = await connection.getRepository(Post).find({ + currentDate: Raw(alias =>`${alias} > ':date'`, { date: "2020-10-06" }) +}); +``` + +will execute following query: + +```sql +SELECT * FROM "post" WHERE "currentDate" > '2020-10-06' +``` + +If you need to provide user input that is an array, you can bind them as a list of values in the SQL statement by using the special expression syntax: + +```ts +import {Raw} from "typeorm"; + +const loadedPosts = await connection.getRepository(Post).find({ + title: Raw(alias =>`${alias} IN (:...titles)`, { titles: ["Go To Statement Considered Harmful", "Structured Programming"] }) +}); +``` + +will execute following query: + +```sql +SELECT * FROM "post" WHERE "titles" IN ('Go To Statement Considered Harmful', 'Structured Programming') +``` + +## Combining Advanced Options Also you can combine these operators with `Not` operator: diff --git a/docs/indices.md b/docs/indices.md index daa226b7d8a..3ce2ce12f35 100644 --- a/docs/indices.md +++ b/docs/indices.md @@ -127,6 +127,11 @@ export class Thing { To create a spatial index on a column in PostgreSQL, add an `Index` with `spatial: true` on a column that uses a spatial type (`geometry`, `geography`): ```typescript +export interface Geometry { + type: "Point"; + coordinates: [Number, Number]; +} + @Entity() export class Thing { @Column("geometry", { diff --git a/docs/listeners-and-subscribers.md b/docs/listeners-and-subscribers.md index 8ecde6f2852..ef164b4d45f 100644 --- a/docs/listeners-and-subscribers.md +++ b/docs/listeners-and-subscribers.md @@ -18,14 +18,14 @@ You must mark those methods with special decorators depending on what event you ### `@AfterLoad` You can define a method with any name in entity and mark it with `@AfterLoad` -and TypeORM will call it each time the entity +and TypeORM will call it each time the entity is loaded using `QueryBuilder` or repository/manager find methods. Example: ```typescript @Entity() export class Post { - + @AfterLoad() updateCounters() { if (this.likesCount === undefined) @@ -43,7 +43,7 @@ Example: ```typescript @Entity() export class Post { - + @BeforeInsert() updateDates() { this.createdDate = new Date(); @@ -60,7 +60,7 @@ Example: ```typescript @Entity() export class Post { - + @AfterInsert() resetCounters() { this.counters = 0; @@ -77,7 +77,7 @@ Example: ```typescript @Entity() export class Post { - + @BeforeUpdate() updateDates() { this.updatedDate = new Date(); @@ -94,7 +94,7 @@ Example: ```typescript @Entity() export class Post { - + @AfterUpdate() updateCounters() { this.counter = 0; @@ -111,7 +111,7 @@ Example: ```typescript @Entity() export class Post { - + @BeforeRemove() updateStatus() { this.status = "removed"; @@ -128,7 +128,7 @@ Example: ```typescript @Entity() export class Post { - + @AfterRemove() updateStatus() { this.status = "removed"; @@ -146,14 +146,14 @@ Example: @EventSubscriber() export class PostSubscriber implements EntitySubscriberInterface { - + /** * Indicates that this subscriber only listen to Post events. */ listenTo() { return Post; } - + /** * Called before post insertion. */ @@ -170,15 +170,99 @@ To listen to any entity you just omit `listenTo` method and use `any`: ```typescript @EventSubscriber() export class PostSubscriber implements EntitySubscriberInterface { - + /** - * Called before entity insertion. + * Called after entity is loaded. + */ + afterLoad(entity: any) { + console.log(`AFTER ENTITY LOADED: `, entity); + } + + /** + * Called before post insertion. */ beforeInsert(event: InsertEvent) { - console.log(`BEFORE ENTITY INSERTED: `, event.entity); + console.log(`BEFORE POST INSERTED: `, event.entity); + } + + /** + * Called after entity insertion. + */ + afterInsert(event: InsertEvent) { + console.log(`AFTER ENTITY INSERTED: `, event.entity); + } + + /** + * Called before entity update. + */ + beforeUpdate(event: UpdateEvent) { + console.log(`BEFORE ENTITY UPDATED: `, event.entity); + } + + /** + * Called after entity update. + */ + afterUpdate(event: UpdateEvent) { + console.log(`AFTER ENTITY UPDATED: `, event.entity); + } + + /** + * Called before entity removal. + */ + beforeRemove(event: RemoveEvent) { + console.log(`BEFORE ENTITY WITH ID ${event.entityId} REMOVED: `, event.entity); + } + + /** + * Called after entity removal. + */ + afterRemove(event: RemoveEvent) { + console.log(`AFTER ENTITY WITH ID ${event.entityId} REMOVED: `, event.entity); + } + + /** + * Called before transaction start. + */ + beforeTransactionStart(event: TransactionStartEvent) { + console.log(`BEFORE TRANSACTION STARTED: `, event); + } + + /** + * Called after transaction start. + */ + afterTransactionStart(event: TransactionStartEvent) { + console.log(`AFTER TRANSACTION STARTED: `, event); + } + + /** + * Called before transaction commit. + */ + beforeTransactionCommit(event: TransactionCommitEvent) { + console.log(`BEFORE TRANSACTION COMMITTED: `, event); + } + + /** + * Called after transaction commit. + */ + afterTransactionCommit(event: TransactionCommitEvent) { + console.log(`AFTER TRANSACTION COMMITTED: `, event); + } + + /** + * Called before transaction rollback. + */ + beforeTransactionRollback(event: TransactionRollbackEvent) { + console.log(`BEFORE TRANSACTION ROLLBACK: `, event); + } + + /** + * Called after transaction rollback. + */ + afterTransactionRollback(event: TransactionRollbackEvent) { + console.log(`AFTER TRANSACTION ROLLBACK: `, event); } } ``` -Make sure your `subscribers` property is set in your [Connection Options](./connection-options.md#common-connection-options) so TypeORM loads your subscriber. \ No newline at end of file +Make sure your `subscribers` property is set in your [Connection Options](./connection-options.md#common-connection-options) so TypeORM loads your subscriber. diff --git a/docs/many-to-many-relations.md b/docs/many-to-many-relations.md index 8a53a21e8db..1de5c9d0011 100644 --- a/docs/many-to-many-relations.md +++ b/docs/many-to-many-relations.md @@ -44,7 +44,7 @@ export class Question { @Column() text: string; - @ManyToMany(type => Category) + @ManyToMany(() => Category) @JoinTable() categories: Category[]; @@ -146,7 +146,7 @@ export class Question { @PrimaryGeneratedColumn() id: number; - @ManyToMany(type => Category, category => category.questions, { + @ManyToMany(() => Category, category => category.questions, { cascade: true }) @JoinTable() @@ -174,7 +174,7 @@ const questions = await connection .getMany(); ``` -With eager loading enabled on a relation you don't have to specify relation or join it - it will ALWAYS be loaded automatically. +When using `FindOptions` you don't need to specify eager relations - they are always automatically loaded. ## bi-directional relations @@ -197,7 +197,7 @@ export class Category { @Column() name: string; - @ManyToMany(type => Question, question => question.categories) + @ManyToMany(() => Question, question => question.categories) questions: Question[]; } @@ -219,7 +219,7 @@ export class Question { @Column() text: string; - @ManyToMany(type => Category, category => category.questions) + @ManyToMany(() => Category, category => category.questions) @JoinTable() categories: Category[]; @@ -263,10 +263,10 @@ export class PostToCategory { @Column() public order!: number; - @ManyToOne(type => Post, post => post.postToCategories) + @ManyToOne(() => Post, post => post.postToCategories) public post!: Post; - @ManyToOne(type => Category, category => category.postToCategories) + @ManyToOne(() => Category, category => category.postToCategories) public category!: Category; } ``` @@ -276,11 +276,11 @@ Additionally you will have to add a relationship like the following to `Post` an ```typescript // category.ts ... -@OneToMany(type => PostToCategory, postToCategory => postToCategory.category) +@OneToMany(() => PostToCategory, postToCategory => postToCategory.category) public postToCategories!: PostToCategory[]; // post.ts ... -@OneToMany(type => PostToCategory, postToCategory => postToCategory.post) +@OneToMany(() => PostToCategory, postToCategory => postToCategory.post) public postToCategories!: PostToCategory[]; ``` diff --git a/docs/many-to-one-one-to-many-relations.md b/docs/many-to-one-one-to-many-relations.md index 0a4e53d73e6..cf42f0cbd25 100644 --- a/docs/many-to-one-one-to-many-relations.md +++ b/docs/many-to-one-one-to-many-relations.md @@ -17,7 +17,7 @@ export class Photo { @Column() url: string; - @ManyToOne(type => User, user => user.photos) + @ManyToOne(() => User, user => user.photos) user: User; } @@ -36,7 +36,7 @@ export class User { @Column() name: string; - @OneToMany(type => Photo, photo => photo.user) + @OneToMany(() => Photo, photo => photo.user) photos: Photo[]; } @@ -102,7 +102,7 @@ photo2.user = user; await connection.manager.save(photo2); ``` -With cascades enabled you can save this relation with only one `save` call. +With [cascades](https://github.com/typeorm/typeorm/blob/master/docs/relations.md#cascades) enabled you can save this relation with only one `save` call. To load a user with photos inside you must specify the relation in `FindOptions`: diff --git a/docs/migrations.md b/docs/migrations.md index 3cbd03f8ecd..3e4799d2059 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -87,7 +87,7 @@ typeorm migration:create -n PostRefactoring ``` Here, `PostRefactoring` is the name of the migration - you can specify any name you want. -After you run the command you can see a new file generated in the "migration" directory +After you run the command you can see a new file generated in the "migration" directory named `{TIMESTAMP}-PostRefactoring.ts` where `{TIMESTAMP}` is the current timestamp when the migration was generated. Now you can open the file and add your migration sql queries there. @@ -144,7 +144,7 @@ Once you have a migration to run on production, you can run them using a CLI com typeorm migration:run ``` -**`typeorm migration:create` and `typeorm migration:generate` will create `.ts` files. The `migration:run` and `migration:revert` commands only work on `.js` files. Thus the typescript files need to be compiled before running the commands.** Alternatively you can use `ts-node` in conjunction with `typeorm` to run `.ts` migration files. +**`typeorm migration:create` and `typeorm migration:generate` will create `.ts` files. The `migration:run` and `migration:revert` commands only work on `.js` files. Thus the typescript files need to be compiled before running the commands.** Alternatively you can use `ts-node` in conjunction with `typeorm` to run `.ts` migration files. Example with `ts-node`: ``` @@ -161,8 +161,8 @@ If for some reason you want to revert the changes, you can run: typeorm migration:revert ``` -This command will execute `down` in the latest executed migration. -If you need to revert multiple migrations you must call this command multiple times. +This command will execute `down` in the latest executed migration. +If you need to revert multiple migrations you must call this command multiple times. ## Generating migrations @@ -195,7 +195,7 @@ export class PostRefactoringTIMESTAMP implements MigrationInterface { ``` See, you don't need to write the queries on your own. -The rule of thumb for generating migrations is that you generate them after "each" change you made to your models. +The rule of thumb for generating migrations is that you generate them after "each" change you made to your models. To apply multi-line formatting to your generated migration queries, use the `p` (alias for `--pretty`) flag. ## Connection option If you need to run/revert your migrations for another connection rather than the default, use the `-c` (alias for `--connection`) and pass the config name as an argument @@ -246,6 +246,11 @@ export class QuestionRefactoringTIMESTAMP implements MigrationInterface { { name: "name", type: "varchar", + }, + { + name: 'created_at', + type: 'timestamp', + default: 'now()' } ] }), true); @@ -383,7 +388,7 @@ Drops database. createSchema(schemaPath: string, ifNotExist?: boolean): Promise ``` -- `schemaPath` - schema name. For SqlServer can accept schema path (e.g. 'dbName.schemaName') as parameter. +- `schemaPath` - schema name. For SqlServer can accept schema path (e.g. 'dbName.schemaName') as parameter. If schema path passed, it will create schema in specified database - `ifNotExist` - skips creation if `true`, otherwise throws error if schema already exist @@ -395,7 +400,7 @@ Creates a new table schema. dropSchema(schemaPath: string, ifExist?: boolean, isCascade?: boolean): Promise ``` -- `schemaPath` - schema name. For SqlServer can accept schema path (e.g. 'dbName.schemaName') as parameter. +- `schemaPath` - schema name. For SqlServer can accept schema path (e.g. 'dbName.schemaName') as parameter. If schema path passed, it will drop schema in specified database - `ifExist` - skips deletion if `true`, otherwise throws error if schema was not found - `isCascade` - If `true`, automatically drop objects (tables, functions, etc.) that are contained in the schema. diff --git a/docs/multiple-connections.md b/docs/multiple-connections.md index 21d021eb2a6..d0c2cc44b22 100644 --- a/docs/multiple-connections.md +++ b/docs/multiple-connections.md @@ -275,7 +275,8 @@ Example of replication connection settings: ``` All schema update and write operations are performed using `master` server. -All simple queries performed by find methods or select query builder are using a random `slave` instance. +All simple queries performed by find methods or select query builder are using a random `slave` instance. +All queries performed by query method are performed using the `master` instance. If you want to explicitly use master in SELECT created by query builder, you can use the following code: @@ -291,6 +292,18 @@ try { ``` +If you want to use `slave` in raw queries, you also need to explicitly specify the query runner. + +```typescript + +const slaveQueryRunner = connection.createQueryRunner("slave"); +try { + const userFromSlave = await connection.query('SELECT * FROM users WHERE id = $1', [userId], slaveQueryRunner); +} finally { + return slaveQueryRunner.release(); +} +``` + Note that connection created by a `QueryRunner` need to be explicitly released. Replication is supported by mysql, postgres and sql server databases. diff --git a/docs/one-to-one-relations.md b/docs/one-to-one-relations.md index acb87e0bf68..4188e50e5a2 100644 --- a/docs/one-to-one-relations.md +++ b/docs/one-to-one-relations.md @@ -35,7 +35,7 @@ export class User { @Column() name: string; - @OneToOne(type => Profile) + @OneToOne(() => Profile) @JoinColumn() profile: Profile; @@ -82,7 +82,7 @@ user.profile = profile; await connection.manager.save(user); ``` -With cascades enabled you can save this relation with only one `save` call. +With [cascades](https://github.com/typeorm/typeorm/blob/master/docs/relations.md#cascades) enabled you can save this relation with only one `save` call. To load user with profile inside you must specify relation in `FindOptions`: @@ -125,7 +125,7 @@ export class Profile { @Column() photo: string; - @OneToOne(type => User, user => user.profile) // specify inverse side as a second parameter + @OneToOne(() => User, user => user.profile) // specify inverse side as a second parameter user: User; } @@ -144,7 +144,7 @@ export class User { @Column() name: string; - @OneToOne(type => Profile, profile => profile.user) // specify inverse side as a second parameter + @OneToOne(() => Profile, profile => profile.user) // specify inverse side as a second parameter @JoinColumn() profile: Profile; diff --git a/docs/relational-query-builder.md b/docs/relational-query-builder.md index 247c41a576b..0e2e69a98b5 100644 --- a/docs/relational-query-builder.md +++ b/docs/relational-query-builder.md @@ -3,7 +3,6 @@ `RelationQueryBuilder` is a special type of `QueryBuilder` which allows you to work with your relations. Using it, you can bind entities to each other in the database without the need to load any entities, or you can load related entities easily. -Examples: For example, we have a `Post` entity and it has a many-to-many relation to `Category` called `categories`. Let's add a new category to this many-to-many relation: diff --git a/docs/relations.md b/docs/relations.md index 0c30ff5aa23..0ebc646258b 100644 --- a/docs/relations.md +++ b/docs/relations.md @@ -96,7 +96,7 @@ Also, they provide a less explicit way of saving new objects into the database. ### Cascade Options -The `cascade` option can be set as a `boolean` or an array of cascade options `("insert", "update")[]`. +The `cascade` option can be set as a `boolean` or an array of cascade options `("insert" | "update" | "remove" | "soft-remove" | "recover")[]`. It will default to `false`, meaning no cascades. Setting `cascade: true` will enable full cascades. You can also specify options by providing an array. diff --git a/docs/repository-api.md b/docs/repository-api.md index 22c84794bee..e77bb252668 100644 --- a/docs/repository-api.md +++ b/docs/repository-api.md @@ -235,6 +235,8 @@ const user = await repository.findOneOrFail(1); const timber = await repository.findOneOrFail({ firstName: "Timber" }); ``` +>Note: It is strongly recommended to ensure that your `id` or `FindOptions` value is not `null` or `undefined` before calling `findOne` and `findOneOrFail`. When passed `null` or `undefined`, the query will match with every entity in the repository and return the first record. + * `query` - Executes a raw SQL query. ```typescript diff --git a/docs/roadmap.md b/docs/roadmap.md index e6a7a28c62c..997c7ac12f9 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -4,7 +4,7 @@ See what amazing new features we are expecting to land in the next TypeORM versi ## Note on 1.0.0 release -We are planning to release a final stable `1.0.0` version somewhere in Autumn 2018. +We are planning to release a final stable `1.0.0` version in near future. However, TypeORM is already actively used in a number of big production systems. The main API is already very stable. TypeORM follows a semantic versioning and until `1.0.0`, breaking changes may appear in `0.x.x` versions. diff --git a/docs/select-query-builder.md b/docs/select-query-builder.md index 7e2ca9ad991..5b2352d51b4 100644 --- a/docs/select-query-builder.md +++ b/docs/select-query-builder.md @@ -174,6 +174,16 @@ const timber = await getRepository(User) .getOne(); ``` +`getOneOrFail` will get a single result from the database, but if +no result exists it will throw an `EntityNotFoundError`: + +```typescript +const timber = await getRepository(User) + .createQueryBuilder("user") + .where("user.id = :id OR user.name = :name", { id: 1, name: "Timber" }) + .getOneOrFail(); +``` + To get multiple results from the database, for example, to get all users from the database, use `getMany`: @@ -342,7 +352,7 @@ createQueryBuilder("user") Which will produce the following SQL query: ```sql -SELECT ... FROM users user WHERE user.firstName IN (1, 2, 3, 4) +SELECT ... FROM users user WHERE user.id IN (1, 2, 3, 4) ``` diff --git a/docs/support.md b/docs/support.md index d69707290ac..aaf7229434a 100644 --- a/docs/support.md +++ b/docs/support.md @@ -6,11 +6,11 @@ If you found a bug, issue, or you just want to propose a new feature, create [an ### Have a question? -If you have a question, you can ask it on [StackOverflow](https://stackoverflow.com/questions/tagged/typeorm). +If you have a question, you can ask it on [StackOverflow](https://stackoverflow.com/questions/tagged/typeorm) or other community support channels. ### Want community support? -If you want community support, or simply want to chat with friendly TypeORM enthusiasts and users, you can do it on [Slack](https://join.slack.com/t/typeorm/shared_invite/enQtNDQ1MzA3MDA5MTExLTFiNDEyOGUxZGQyYWIwOTA0NDQxODdkOGQ0OTUxNzFjYjUwY2E0ZmFlODc5OTYyYzAzNGM3MGZjYzhjYTBiZTY). +If you want community support, or simply want to chat with friendly TypeORM enthusiasts and users, you can do it on [Slack](https://join.slack.com/t/typeorm/shared_invite/zt-gej3gc00-hR~L~DqGUJ7qOpGy4SSq3g). ### Want professional commercial support? diff --git a/docs/supported-platforms.md b/docs/supported-platforms.md index 344e63f5d7a..721db5859e8 100644 --- a/docs/supported-platforms.md +++ b/docs/supported-platforms.md @@ -13,7 +13,7 @@ TypeORM was tested on Node.js version 4 and above. ## Browser -You can use [sql.js](https://github.com/kripken/sql.js) in the browser. +You can use [sql.js](https://sql.js.org) in the browser. **Webpack configuration** @@ -61,7 +61,7 @@ You have the option to choose between module loaders just like in browser packag For an example how to use TypeORM in Cordova see [typeorm/cordova-example](https://github.com/typeorm/cordova-example) and for Ionic see [typeorm/ionic-example](https://github.com/typeorm/ionic-example). **Important**: For use with Ionic, a custom webpack config file is needed! Please checkout the example to see the needed changes. ## React Native -TypeORM is able to on React Native apps using the [react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) plugin. For an example see [typeom/react-native-example](https://github.com/typeorm/react-native-example). +TypeORM is able to run on React Native apps using the [react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) plugin. For an example see [typeorm/react-native-example](https://github.com/typeorm/react-native-example). ## Expo diff --git a/docs/tree-entities.md b/docs/tree-entities.md index a4127ff4c7d..a674723f273 100644 --- a/docs/tree-entities.md +++ b/docs/tree-entities.md @@ -11,8 +11,8 @@ To learn more about hierarchy table take a look at [this awesome presentation by ## Adjacency list -Adjacency list is a simple model with self-referencing. -The benefit of this approach is simplicity, +Adjacency list is a simple model with self-referencing. +The benefit of this approach is simplicity, drawback is that you can't load big trees in all at once because of join limitations. To learn more about the benefits and use of Adjacency Lists look at [this article by Matthew Schinckel](http://schinckel.net/2014/09/13/long-live-adjacency-lists/). Example: @@ -38,7 +38,7 @@ export class Category { @OneToMany(type => Category, category => category.parent) children: Category[]; } - + ``` ## Nested set @@ -98,7 +98,7 @@ export class Category { ## Closure table -Closure table stores relations between parent and child in a separate table in a special way. +Closure table stores relations between parent and child in a separate table in a special way. It's efficient in both reads and writes. Example: @@ -123,6 +123,16 @@ export class Category { } ``` +You can specify closure table name and / or closure table columns names by setting optional parameter `options` into `@Tree("closure-table", options)`. `ancestorColumnName` and `descandantColumnName` are callback functions, which receive primary column's metadata and return column's name. + +```ts +@Tree("closure-table", { + closureTableName: "category_closure", + ancestorColumnName: (column) => "ancestor_" + column.propertyName, + descendantColumnName: (column) => "descendant_" + column.propertyName, +}) +``` + ### Note: Updating or removing a component's parent has not been implemented yet ([see this issue](https://github.com/typeorm/typeorm/issues/2032)). The closure table will need to be explicitly updated to do either of these operations. diff --git a/docs/usage-with-javascript.md b/docs/usage-with-javascript.md index 6e883022f8a..7eacd79ee8a 100644 --- a/docs/usage-with-javascript.md +++ b/docs/usage-with-javascript.md @@ -16,7 +16,7 @@ typeorm.createConnection({ password: "admin", database: "test", synchronize: true, - entitySchemas: [ + entities: [ require("./entity/Post"), require("./entity/Category") ] @@ -58,8 +58,11 @@ typeorm.createConnection({ ##### entity/Category.js ```typescript -module.exports = { - name: "Category", +var EntitySchema = require("typeorm").EntitySchema; + +module.exports = new EntitySchema({ + name: "Category", // Will use table name `category` as default behaviour. + tableName: "categories", // Optional: Provide `tableName` property to override the default behaviour for table name. columns: { id: { primary: true, @@ -67,17 +70,20 @@ module.exports = { generated: true }, name: { - type: "string" + type: "varchar" } } -}; +}); ``` ##### entity/Post.js ```typescript -module.exports = { - name: "Post", +var EntitySchema = require("typeorm").EntitySchema; + +module.exports = new EntitySchema({ + name: "Post", // Will use table name `post` as default behaviour. + tableName: "posts", // Optional: Provide `tableName` property to override the default behaviour for table name. columns: { id: { primary: true, @@ -85,7 +91,7 @@ module.exports = { generated: true }, title: { - type: "string" + type: "varchar" }, text: { type: "text" @@ -99,7 +105,7 @@ module.exports = { cascade: true } } -}; +}); ``` -You can checkout this example [typeorm/javascript-example](https://github.com/typeorm/javascript-example) to learn more. \ No newline at end of file +You can checkout this example [typeorm/javascript-example](https://github.com/typeorm/javascript-example) to learn more. diff --git a/docs/using-cli.md b/docs/using-cli.md index 0abcc8f971a..35e193f5aca 100644 --- a/docs/using-cli.md +++ b/docs/using-cli.md @@ -5,7 +5,7 @@ * [Create a new entity](#create-a-new-entity) * [Create a new subscriber](#create-a-new-subscriber) * [Create a new migration](#create-a-new-migration) -* [Generate a migration from existing table schema](#generate-a-migration-from-exist-table-schema) +* [Generate a migration from existing table schema](#generate-a-migration-from-existing-table-schema) * [Run migrations](#run-migrations) * [Revert migrations](#revert-migrations) * [Show migrations](#show-migrations) @@ -203,6 +203,8 @@ Learn more about [Migrations](./migrations.md). Automatic migration generation creates a new migration file and writes all sql queries that must be executed to update the database. +If no there were no changes generated, the command will exit with code 1. + ``` typeorm migration:generate -n UserMigration ``` diff --git a/docs/using-ormconfig.md b/docs/using-ormconfig.md index 1f95b393545..6a1b816696c 100644 --- a/docs/using-ormconfig.md +++ b/docs/using-ormconfig.md @@ -24,7 +24,7 @@ import {createConnection} from "typeorm"; const connection = await createConnection(); ``` -Supported ormconfig file formats are: `.json`, `.js`, `.env`, `.yml` and `.xml`. +Supported ormconfig file formats are: `.json`, `.js`, `.ts`, `.env`, `.yml` and `.xml`. ## Using `ormconfig.json` @@ -80,6 +80,19 @@ module.exports = { } ``` +Alternatively, you may use the ECMAScript module format if your environment supports it: + +```javascript +export default { + "type": "mysql", + "host": "localhost", + "port": 3306, + "username": "test", + "password": "test", + "database": "test" +} +``` + You can specify any other options from [ConnectionOptions](./connection-options.md). If you want to create multiple connections then simply create multiple connections in a single array and return it. diff --git a/docs/zh_CN/README.md b/docs/zh_CN/README.md index 348588f0a3d..c8e24aeee85 100644 --- a/docs/zh_CN/README.md +++ b/docs/zh_CN/README.md @@ -4,8 +4,8 @@

- - + + diff --git a/docs/zh_CN/connection-options.md b/docs/zh_CN/connection-options.md index 3966d6c8e82..5523bc6afb6 100644 --- a/docs/zh_CN/connection-options.md +++ b/docs/zh_CN/connection-options.md @@ -5,6 +5,7 @@ - [`mysql`/`mariadb`](#mysql/mariadb) - [`postgres`/`cockroachdb`连接选项](#postgres/cockroachdb连接选项) - [`sqlite`](#sqlite) + - [`better-sqlite3`](#better-sqlite3) - [`cordova`](#cordova) - [`react-native`](#react-native) - [`nativescript`](#nativescript) @@ -20,7 +21,7 @@ ## 常用的连接选项 -- `type` - 数据库类型。你必须指定要使用的数据库引擎。该值可以是"mysql","postgres","mariadb","sqlite","cordova","nativescript","oracle","mssql","mongodb","sqljs","react-native"。此选项是**必需**的。 +- `type` - 数据库类型。你必须指定要使用的数据库引擎。该值可以是"mysql","postgres","mariadb","sqlite", "better-sqlite3","cordova","nativescript","oracle","mssql","mongodb","sqljs","react-native"。此选项是**必需**的。 - `name` - 连接名。 在使用 `getConnection(name: string)` 或 `ConnectionManager.get(name: string)`时候需要用到。不同连接的连接名称不能相同,它们都必须是唯一的。如果没有给出连接名称,那么它将被设置为"default"。 @@ -128,6 +129,14 @@ - `database` - 数据库路径。 例如 "./mydb.sql" +## `better-sqlite3` + +* `database` - 数据库路径。 例如 "./mydb.sql" + +* `statementCacheSize` - Sqlite 查询 Statement 缓存大小。默认100 + +* `prepareDatabase` - 在数据库投入使用前运行的函数。你可以在这里访问到better-sqlite3原始数据库对象。 + ## `cordova` - `database` - 数据库名 diff --git a/docs/zh_CN/index.md b/docs/zh_CN/index.md index ea48fa264c9..6d6893d0d47 100644 --- a/docs/zh_CN/index.md +++ b/docs/zh_CN/index.md @@ -4,8 +4,8 @@

- - + + diff --git a/docs/zh_CN/many-to-many-relations.md b/docs/zh_CN/many-to-many-relations.md index aacd8b87cd6..7f4a6d48cad 100644 --- a/docs/zh_CN/many-to-many-relations.md +++ b/docs/zh_CN/many-to-many-relations.md @@ -32,7 +32,7 @@ export class Question { @Column() text: string; - @ManyToMany(type => Category) + @ManyToMany(() => Category) @JoinTable() categories: Category[]; } @@ -121,7 +121,7 @@ export class Category { @Column() name: string; - @ManyToMany(type => Question, question => question.categories) + @ManyToMany(() => Question, question => question.categories) questions: Question[]; } ``` @@ -141,7 +141,7 @@ export class Question { @Column() text: string; - @ManyToMany(type => Category, category => category.questions) + @ManyToMany(() => Category, category => category.questions) @JoinTable() categories: Category[]; } diff --git a/docs/zh_CN/many-to-one-one-to-many-relations.md b/docs/zh_CN/many-to-one-one-to-many-relations.md index 248c5b5f591..3dcbfd23457 100644 --- a/docs/zh_CN/many-to-one-one-to-many-relations.md +++ b/docs/zh_CN/many-to-one-one-to-many-relations.md @@ -16,7 +16,7 @@ export class Photo { @Column() url: string; - @ManyToOne(type => User, user => user.photos) + @ManyToOne(() => User, user => user.photos) user: User; } ``` @@ -33,7 +33,7 @@ export class User { @Column() name: string; - @OneToMany(type => Photo, photo => photo.user) + @OneToMany(() => Photo, photo => photo.user) photos: Photo[]; } ``` diff --git a/docs/zh_CN/one-to-one-relations.md b/docs/zh_CN/one-to-one-relations.md index 1ec4065d0ae..de00087f337 100644 --- a/docs/zh_CN/one-to-one-relations.md +++ b/docs/zh_CN/one-to-one-relations.md @@ -33,7 +33,7 @@ export class User { @Column() name: string; - @OneToOne(type => Profile) + @OneToOne(() => Profile) @JoinColumn() profile: Profile; } @@ -121,7 +121,7 @@ export class Profile { @Column() photo: string; - @OneToOne(type => User, user => user.profile) // 将另一面指定为第二个参数 + @OneToOne(() => User, user => user.profile) // 将另一面指定为第二个参数 user: User; } ``` @@ -138,7 +138,7 @@ export class User { @Column() name: string; - @OneToOne(type => Profile, profile => profile.user) // 指定另一面作为第二个参数 + @OneToOne(() => Profile, profile => profile.user) // 指定另一面作为第二个参数 @JoinColumn() profile: Profile; } diff --git a/docs/zh_CN/usage-with-javascript.md b/docs/zh_CN/usage-with-javascript.md index c3d193d0187..4a8922dee22 100644 --- a/docs/zh_CN/usage-with-javascript.md +++ b/docs/zh_CN/usage-with-javascript.md @@ -17,7 +17,10 @@ typeorm password: "admin", database: "test", synchronize: true, - entitySchemas: [require("./entity/Post"), require("./entity/Category")] + entities: [ + require("./entity/Post"), + require("./entity/Category") + ] }) .then(function(connection) { var category1 = { @@ -54,7 +57,9 @@ typeorm ##### entity/Category.js ```typescript -module.exports = { +var EntitySchema = require("typeorm").EntitySchema; + +module.exports = new EntitySchema({ name: "Category", columns: { id: { @@ -63,16 +68,18 @@ module.exports = { generated: true }, name: { - type: "string" + type: "varchar" } } -}; +}); ``` ##### entity/Post.js ```typescript -module.exports = { +var EntitySchema = require("typeorm").EntitySchema; + +module.exports = new EntitySchema({ name: "Post", columns: { id: { @@ -81,7 +88,7 @@ module.exports = { generated: true }, title: { - type: "string" + type: "varchar" }, text: { type: "text" @@ -95,7 +102,7 @@ module.exports = { cascade: true } } -}; +}); ``` 您可以查看此示例[typeorm/javascript-example](https://github.com/typeorm/javascript-example)以了解更多信息。 diff --git a/extra/typeorm-model-shim.js b/extra/typeorm-model-shim.js index 6de41344d39..ff69bc08979 100644 --- a/extra/typeorm-model-shim.js +++ b/extra/typeorm-model-shim.js @@ -21,212 +21,247 @@ // } -// columns - -/* export */ function Column(typeOrOptions, options) { - return function (object, propertyName) { - }; +/* export */ function Column() { + return function () {}; } exports.Column = Column; -/* export */ function CreateDateColumn(options) { - return function (object, propertyName) { - }; +/* export */ function CreateDateColumn() { + return function () {}; } exports.CreateDateColumn = CreateDateColumn; -/* export */ function ObjectIdColumn(columnOptions) { - return function (object, propertyName) { - }; +/* export */ function DeleteDateColumn() { + return function () {}; } -exports.ObjectIdColumn = ObjectIdColumn; +exports.DeleteDateColumn = DeleteDateColumn; -/* export */ function PrimaryColumn(typeOrOptions, options) { - return function (object, propertyName) { - }; +/* export */ function PrimaryGeneratedColumn() { + return function () {}; } -exports.PrimaryColumn = PrimaryColumn; +exports.PrimaryGeneratedColumn = PrimaryGeneratedColumn; -/* export */ function PrimaryGeneratedColumn(options) { - return function (object, propertyName) { - }; +/* export */ function PrimaryColumn() { + return function () {}; } -exports.PrimaryGeneratedColumn = PrimaryGeneratedColumn; +exports.PrimaryColumn = PrimaryColumn; -/* export */ function UpdateDateColumn(options) { - return function (object, propertyName) { - }; +/* export */ function UpdateDateColumn() { + return function () {}; } exports.UpdateDateColumn = UpdateDateColumn; -/* export */ function VersionColumn(options) { - return function (object, propertyName) { - }; +/* export */ function VersionColumn() { + return function () {}; } exports.VersionColumn = VersionColumn; -// entities - -/* export */ function ChildEntity(tableName, options) { - return function (object) { - }; +/* export */ function ViewColumn() { + return function () {}; } -exports.ChildEntity = ChildEntity; - -/* export */ function Entity(name, options) { - return function (object) { - }; -} -exports.Entity = Entity; +exports.ViewColumn = ViewColumn; -/* export */ function TableInheritance(type) { - return function (object) { - }; +/* export */ function ObjectIdColumn() { + return function () {}; } -exports.TableInheritance = TableInheritance; - -// listeners +exports.ObjectIdColumn = ObjectIdColumn; /* export */ function AfterInsert() { - return function (object, propertyName) { - }; + return function () {}; } exports.AfterInsert = AfterInsert; /* export */ function AfterLoad() { - return function (object, propertyName) { - }; + return function () {}; } exports.AfterLoad = AfterLoad; /* export */ function AfterRemove() { - return function (object, propertyName) { - }; + return function () {}; } exports.AfterRemove = AfterRemove; /* export */ function AfterUpdate() { - return function (object, propertyName) { - }; + return function () {}; } exports.AfterUpdate = AfterUpdate; /* export */ function BeforeInsert() { - return function (object, propertyName) { - }; + return function () {}; } exports.BeforeInsert = BeforeInsert; /* export */ function BeforeRemove() { - return function (object, propertyName) { - }; + return function () {}; } exports.BeforeRemove = BeforeRemove; /* export */ function BeforeUpdate() { - return function (object, propertyName) { - }; + return function () {}; } exports.BeforeUpdate = BeforeUpdate; /* export */ function EventSubscriber() { - return function (object, propertyName) { - }; + return function () {}; } exports.EventSubscriber = EventSubscriber; -// relations +/* export */ function ColumnOptions() { + return function () {}; +} +exports.ColumnOptions = ColumnOptions; + +/* export */ function IndexOptions() { + return function () {}; +} +exports.IndexOptions = IndexOptions; + +/* export */ function JoinColumnOptions() { + return function () {}; +} +exports.JoinColumnOptions = JoinColumnOptions; + +/* export */ function JoinTableOptions() { + return function () {}; +} +exports.JoinTableOptions = JoinTableOptions; + +/* export */ function RelationOptions() { + return function () {}; +} +exports.RelationOptions = RelationOptions; + +/* export */ function EntityOptions() { + return function () {}; +} +exports.EntityOptions = EntityOptions; + +/* export */ function ValueTransformer() { + return function () {}; +} +exports.ValueTransformer = ValueTransformer; -/* export */ function JoinColumn(options) { - return function (object, propertyName) { - }; +/* export */ function JoinColumn() { + return function () {}; } exports.JoinColumn = JoinColumn; -/* export */ function JoinTable(options) { - return function (object, propertyName) { - }; +/* export */ function JoinTable() { + return function () {}; } exports.JoinTable = JoinTable; -/* export */ function ManyToMany(typeFunction, inverseSideOrOptions, options) { - return function (object, propertyName) { - }; +/* export */ function ManyToMany() { + return function () {}; } exports.ManyToMany = ManyToMany; -/* export */ function ManyToOne(typeFunction, inverseSideOrOptions, options) { - return function (object, propertyName) { - }; +/* export */ function ManyToOne() { + return function () {}; } exports.ManyToOne = ManyToOne; -/* export */ function OneToMany(typeFunction, inverseSideOrOptions, options) { - return function (object, propertyName) { - }; +/* export */ function OneToMany() { + return function () {}; } exports.OneToMany = OneToMany; -/* export */ function OneToOne(typeFunction, inverseSideOrOptions, options) { - return function (object, propertyName) { - }; +/* export */ function OneToOne() { + return function () {}; } exports.OneToOne = OneToOne; -/* export */ function RelationCount(relation) { - return function (object, propertyName) { - }; +/* export */ function RelationCount() { + return function () {}; } exports.RelationCount = RelationCount; -/* export */ function RelationId(relation) { - return function (object, propertyName) { - }; +/* export */ function RelationId() { + return function () {}; } exports.RelationId = RelationId; -// tree +/* export */ function Entity() { + return function () {}; +} +exports.Entity = Entity; -/* export */ function Tree(name, options) { - return function (object) { - }; +/* export */ function ChildEntity() { + return function () {}; } -exports.Tree = Tree; +exports.ChildEntity = ChildEntity; -/* export */ function TreeChildren(options) { - return function (object, propertyName) { - }; +/* export */ function TableInheritance() { + return function () {}; } -exports.TreeChildren = TreeChildren; +exports.TableInheritance = TableInheritance; + +/* export */ function ViewEntity() { + return function () {}; +} +exports.ViewEntity = ViewEntity; + +/* export */ function Transaction() { + return function () {}; +} +exports.Transaction = Transaction; -/* export */ function TreeChildrenCount(options) { - return function (object, propertyName) { - }; +/* export */ function TransactionManager() { + return function () {}; } -exports.TreeChildrenCount = TreeChildrenCount; +exports.TransactionManager = TransactionManager; + +/* export */ function TransactionRepository() { + return function () {}; +} +exports.TransactionRepository = TransactionRepository; /* export */ function TreeLevelColumn() { - return function (object, propertyName) { - }; + return function () {}; } exports.TreeLevelColumn = TreeLevelColumn; -/* export */ function TreeParent(options) { - return function (object, propertyName) { - }; +/* export */ function TreeParent() { + return function () {}; } exports.TreeParent = TreeParent; -// other +/* export */ function TreeChildren() { + return function () {}; +} +exports.TreeChildren = TreeChildren; -/* export */ function Generated(options) { - return function (object, propertyName) { - }; +/* export */ function Tree() { + return function () {}; } -exports.Generated = Generated; +exports.Tree = Tree; -/* export */ function Index(options) { - return function (object, propertyName) { - }; +/* export */ function Index() { + return function () {}; } exports.Index = Index; + +/* export */ function Unique() { + return function () {}; +} +exports.Unique = Unique; + +/* export */ function Check() { + return function () {}; +} +exports.Check = Check; + +/* export */ function Exclusion() { + return function () {}; +} +exports.Exclusion = Exclusion; + +/* export */ function Generated() { + return function () {}; +} +exports.Generated = Generated; + +/* export */ function EntityRepository() { + return function () {}; +} +exports.EntityRepository = EntityRepository; diff --git a/gulpfile.ts b/gulpfile.ts index de3dfcf1776..18edf52cb99 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -11,7 +11,7 @@ const replace = require("gulp-replace"); const rename = require("gulp-rename"); const mocha = require("gulp-mocha"); const chai = require("chai"); -const tslint = require("gulp-tslint"); +const eslint = require("gulp-eslint"); const sourcemaps = require("gulp-sourcemaps"); const istanbul = require("gulp-istanbul"); const remapIstanbul = require("remap-istanbul/lib/gulpRemapIstanbul"); @@ -64,29 +64,18 @@ export class Gulpfile { "!./src/commands/*.ts", "!./src/cli.ts", "!./src/typeorm.ts", - "!./src/typeorm-model-shim.ts", - "!./src/platform/PlatformTools.ts" + "!./src/typeorm-model-shim.ts" ]) .pipe(gulp.dest("./build/browser/src")); } /** - * Replaces PlatformTools with browser-specific implementation called BrowserPlatformTools. + * Copies templates for compilation */ @Task() - browserCopyPlatformTools() { - return gulp.src("./src/platform/BrowserPlatformTools.template") - .pipe(rename("PlatformTools.ts")) - .pipe(gulp.dest("./build/browser/src/platform")); - } - - /** - * Adds dummy classes for disabled drivers (replacement is done via browser entry point in package.json) - */ - @Task() - browserCopyDisabledDriversDummy() { - return gulp.src("./src/platform/BrowserDisabledDriversDummy.template") - .pipe(rename("BrowserDisabledDriversDummy.ts")) + browserCopyTemplates() { + return gulp.src("./src/platform/*.template") + .pipe(rename((p: any) => { p.extname = '.ts'; })) .pipe(gulp.dest("./build/browser/src/platform")); } @@ -97,7 +86,10 @@ export class Gulpfile { "lib": ["es5", "es6", "dom"], typescript: require("typescript") }); - const tsResult = gulp.src(["./build/browser/src/**/*.ts", "./node_modules/reflect-metadata/**/*.d.ts", "./node_modules/@types/**/*.ts"]) + const tsResult = gulp.src([ + "./build/browser/src/**/*.ts", + "./node_modules/reflect-metadata/**/*.d.ts" + ]) .pipe(sourcemaps.init()) .pipe(tsProject()); @@ -151,8 +143,7 @@ export class Gulpfile { typescript: require("typescript") }); const tsResult = gulp.src([ - "./src/**/*.ts", - "./node_modules/@types/**/*.ts", + "./src/**/*.ts" ]) .pipe(sourcemaps.init()) .pipe(tsProject()); @@ -231,7 +222,7 @@ export class Gulpfile { package() { return [ "clean", - ["browserCopySources", "browserCopyPlatformTools", "browserCopyDisabledDriversDummy"], + ["browserCopySources", "browserCopyTemplates"], ["packageCompile", "browserCompile"], "packageMoveCompiledFiles", [ @@ -269,18 +260,11 @@ export class Gulpfile { * Runs ts linting to validate source code. */ @Task() - tslint() { + eslint() { return gulp.src(["./src/**/*.ts", "./test/**/*.ts", "./sample/**/*.ts"]) - .pipe( - tslint({ - formatter: "stylish" - }) - ) - .pipe(tslint.report({ - emitError: true, - sort: true, - bell: true - })); + .pipe(eslint()) + .pipe(eslint.format('stylish')) + .pipe(eslint.failAfterError()) } /** diff --git a/ormconfig.circleci-cockroach.json b/ormconfig.circleci-cockroach.json deleted file mode 100644 index b91014a7813..00000000000 --- a/ormconfig.circleci-cockroach.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "skip": true, - "name": "mysql", - "type": "mysql", - "host": "localhost", - "port": 3306, - "username": "root", - "password": "admin", - "database": "test" - }, - { - "skip": true, - "name": "mariadb", - "type": "mariadb", - "host": "mariadb", - "port": 3306, - "username": "root", - "password": "admin", - "database": "test" - }, - { - "skip": true, - "name": "sqlite", - "type": "sqlite", - "database": "temp/sqlitedb.db" - }, - { - "skip": true, - "name": "postgres", - "type": "postgres", - "host": "localhost", - "port": 5432, - "username": "test", - "password": "test", - "database": "test" - }, - { - "skip": true, - "name": "sqljs", - "type": "sqljs" - }, - { - "skip": true, - "name": "mssql", - "type": "mssql", - "host": "localhost", - "username": "sa", - "password": "Admin12345", - "database": "tempdb" - }, - { - "skip": true, - "name": "sap", - "type": "sap", - "host": "localhost", - "port": 39015, - "username": "SYSTEM", - "password": "HXEHana1", - "database": "HXE", - "logging": false - }, - { - "skip": true, - "disabledIfNotEnabledImplicitly": true, - "name": "mongodb", - "type": "mongodb", - "database": "test", - "useNewUrlParser": true, - "useUnifiedTopology": true - }, - { - "skip": false, - "name": "cockroachdb", - "type": "cockroachdb", - "host": "localhost", - "port": 26257, - "username": "root", - "password": "", - "database": "defaultdb" - }, - { - "skip": true, - "name": "oracle", - "type": "oracle", - "host": "localhost", - "username": "typeorm", - "password": "Passw0rd", - "port": 1521, - "sid": "orclpdb1.localdomain" - } -] diff --git a/ormconfig.circleci-common.json b/ormconfig.circleci-common.json index 286289f0f67..d9f558a78a0 100644 --- a/ormconfig.circleci-common.json +++ b/ormconfig.circleci-common.json @@ -1,71 +1,89 @@ [ { - "skip": false, + "skip": true, "name": "mysql", "type": "mysql", - "host": "localhost", + "host": "typeorm-mysql", "port": 3306, "username": "root", "password": "admin", - "database": "test" + "database": "test", + "logging": false }, { - "skip": false, + "skip": true, "name": "mariadb", "type": "mariadb", - "host": "mariadb", + "host": "typeorm-mariadb", "port": 3306, "username": "root", "password": "admin", - "database": "test" + "database": "test", + "logging": false }, { - "skip": false, + "skip": true, "name": "sqlite", "type": "sqlite", - "database": "temp/sqlitedb.db" + "database": "temp/sqlitedb.db", + "logging": false }, { - "skip": false, + "skip": true, + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": "temp/better-sqlite3db.db", + "logging": false + }, + { + "skip": true, "name": "postgres", "type": "postgres", - "host": "localhost", + "host": "typeorm-postgres", "port": 5432, "username": "test", "password": "test", - "database": "test" + "database": "test", + "logging": false }, { "skip": false, "name": "sqljs", - "type": "sqljs" + "type": "sqljs", + "logging": false }, + { "skip": true, "name": "mssql", "type": "mssql", - "host": "localhost", + "host": "typeorm-mssql", + "port": 1433, "username": "sa", "password": "Admin12345", - "database": "tempdb" + "database": "tempdb", + "logging": false }, { "skip": true, "name": "sap", "type": "sap", - "host": "localhost", + "host": "typeorm-hana", "port": 39015, "username": "SYSTEM", - "password": "HXEHana1", + "password": "MySuperHanaPwd123!", "database": "HXE", "logging": false }, { - "skip": false, + "skip": true, "disabledIfNotEnabledImplicitly": true, "name": "mongodb", "type": "mongodb", + "host": "typeorm-mongodb", + "port": 27017, "database": "test", + "logging": false, "useNewUrlParser": true, "useUnifiedTopology": true }, @@ -73,7 +91,7 @@ "skip": true, "name": "cockroachdb", "type": "cockroachdb", - "host": "localhost", + "host": "typeorm-cockroachdb", "port": 26257, "username": "root", "password": "", @@ -83,10 +101,14 @@ "skip": true, "name": "oracle", "type": "oracle", - "host": "localhost", - "username": "typeorm", - "password": "Passw0rd", + "host": "typeorm-oracle", "port": 1521, - "sid": "orclpdb1.localdomain" + "sid": "XE", + "username": "system", + "password": "oracle", + "logging": false, + "extra": { + "connectString": "typeorm-oracle:1521/XE" + } } ] diff --git a/ormconfig.circleci-oracle.json b/ormconfig.circleci-oracle.json deleted file mode 100644 index 83bc38febe7..00000000000 --- a/ormconfig.circleci-oracle.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "skip": true, - "name": "mysql", - "type": "mysql", - "host": "localhost", - "port": 3306, - "username": "root", - "password": "admin", - "database": "test" - }, - { - "skip": true, - "name": "mariadb", - "type": "mariadb", - "host": "mariadb", - "port": 3306, - "username": "root", - "password": "admin", - "database": "test" - }, - { - "skip": true, - "name": "sqlite", - "type": "sqlite", - "database": "temp/sqlitedb.db" - }, - { - "skip": true, - "name": "postgres", - "type": "postgres", - "host": "localhost", - "port": 5432, - "username": "test", - "password": "test", - "database": "test" - }, - { - "skip": true, - "name": "sqljs", - "type": "sqljs" - }, - { - "skip": true, - "name": "mssql", - "type": "mssql", - "host": "localhost", - "username": "sa", - "password": "Admin12345", - "database": "tempdb" - }, - { - "skip": true, - "name": "sap", - "type": "sap", - "host": "localhost", - "port": 39015, - "username": "SYSTEM", - "password": "HXEHana1", - "database": "HXE", - "logging": false - }, - { - "skip": true, - "disabledIfNotEnabledImplicitly": true, - "name": "mongodb", - "type": "mongodb", - "database": "test", - "useNewUrlParser": true, - "useUnifiedTopology": true - }, - { - "skip": true, - "name": "cockroachdb", - "type": "cockroachdb", - "host": "localhost", - "port": 26257, - "username": "root", - "password": "", - "database": "defaultdb" - }, - { - "skip": false, - "name": "oracle", - "type": "oracle", - "host": "localhost", - "username": "typeorm", - "password": "Passw0rd", - "port": 1521, - "sid": "orclpdb1.localdomain" - } -] diff --git a/ormconfig.json.dist b/ormconfig.json.dist index ff780d352d3..9c045454be0 100644 --- a/ormconfig.json.dist +++ b/ormconfig.json.dist @@ -28,6 +28,13 @@ "database": "temp/sqlitedb.db", "logging": false }, + { + "skip": false, + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": "temp/better-sqlite3db.db", + "logging": false + }, { "skip": false, "name": "postgres", diff --git a/ormconfig.travis.json b/ormconfig.travis.json deleted file mode 100644 index f6a76e4c9c0..00000000000 --- a/ormconfig.travis.json +++ /dev/null @@ -1,93 +0,0 @@ -[ - { - "skip": false, - "name": "mysql", - "type": "mysql", - "host": "localhost", - "port": 3306, - "username": "root", - "password": "admin", - "database": "test" - }, - { - "skip": false, - "name": "mariadb", - "type": "mariadb", - "host": "localhost", - "port": 3307, - "username": "root", - "password": "admin", - "database": "test" - }, - { - "skip": false, - "name": "sqlite", - "type": "sqlite", - "database": "temp/sqlitedb.db" - }, - { - "skip": false, - "name": "postgres", - "type": "postgres", - "host": "localhost", - "port": 5432, - "username": "test", - "password": "test", - "database": "test", - "logging": false - }, - { - "skip": false, - "name": "sqljs", - "type": "sqljs" - }, - { - "skip": false, - "name": "mssql", - "type": "mssql", - "host": "localhost", - "username": "sa", - "password": "Admin12345", - "database": "tempdb" - }, - { - "skip": true, - "name": "oracle", - "type": "oracle", - "host": "localhost", - "username": "system", - "password": "oracle", - "port": 1521, - "sid": "xe.oracle.docker" - }, - { - "skip": false, - "name": "cockroachdb", - "type": "cockroachdb", - "host": "localhost", - "port": 26257, - "username": "root", - "password": "", - "database": "defaultdb" - }, - { - "skip": true, - "name": "sap", - "type": "sap", - "host": "localhost", - "port": 39015, - "username": "SYSTEM", - "password": "HXEHana1", - "database": "HXE", - "logging": false - }, - { - "skip": false, - "disabledIfNotEnabledImplicitly": true, - "name": "mongodb", - "type": "mongodb", - "database": "test", - "useNewUrlParser": true, - "useUnifiedTopology": true - } -] diff --git a/package-lock.json b/package-lock.json index 9af2c73c56c..914cb030990 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,152 @@ { "name": "typeorm", - "version": "0.2.26", + "version": "0.2.30", "lockfileVersion": 1, "requires": true, "dependencies": { - "@babel/runtime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.0.0.tgz", - "integrity": "sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==", + "@azure/ms-rest-azure-env": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-1.1.2.tgz", + "integrity": "sha512-l7z0DPCi2Hp88w12JhDTtx5d0Y3+vhfE7JKJb9O7sEz71Cwp053N8piTtTnnk/tUor9oZHgEKi/p3tQQmLPjvA==", + "dev": true + }, + "@azure/ms-rest-js": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.8.15.tgz", + "integrity": "sha512-kIB71V3DcrA4iysBbOsYcxd4WWlOE7OFtCUYNfflPODM0lbIR23A236QeTn5iAeYwcHmMjR/TAKp5KQQh/WqoQ==", + "dev": true, + "requires": { + "@types/tunnel": "0.0.0", + "axios": "^0.19.0", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "tunnel": "0.0.6", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "@azure/ms-rest-nodeauth": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-2.0.2.tgz", + "integrity": "sha512-KmNNICOxt3EwViAJI3iu2VH8t8BQg5J2rSAyO4IUYLF9ZwlyYsP419pdvl4NBUhluAP2cgN7dfD2V6E6NOMZlQ==", + "dev": true, + "requires": { + "@azure/ms-rest-azure-env": "^1.1.2", + "@azure/ms-rest-js": "^1.8.7", + "adal-node": "^0.1.28" + } + }, + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { - "regenerator-runtime": "^0.12.0" + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@eslint/eslintrc": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", + "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } } }, "@gulp-sourcemaps/identity-map": { @@ -44,110 +180,69 @@ "through2": "^2.0.3" } }, - "@iamstarkov/listr-update-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz", - "integrity": "sha512-IJyxQWsYDEkf8C8QthBn5N8tIUR9V9je6j3sMIpAkonaadjbvxmRC6RAhpa3RKxndhNnU2M6iNbtJwd7usQYIA==", + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", "dev": true, "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" } }, - "@samverschueren/stream-to-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", - "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", "dev": true, "requires": { - "any-observable": "^0.3.0" + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" } }, "@sinonjs/commons": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.3.1.tgz", - "integrity": "sha512-rgmZk5CrBGAMATk0HlHOFvo8V44/r+On6cKS80tqid0Eljd+fFBWBOXZp9H2/EB3faxdNdzXTx6QZIKLkbJ7mA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", "dev": true, "requires": { "type-detect": "4.0.8" } }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, "@sinonjs/formatio": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.0.tgz", - "integrity": "sha512-hskkZG4qB0HgsxrPUlnk2EiIyBwntM+ETIxCha/gidl172MCfdosNezB5706ciS5P2VhueM7MoACWwMc4A4gMQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", + "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", "dev": true, "requires": { "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "@sinonjs/samsam": "^5.0.2" } }, "@sinonjs/samsam": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.2.0.tgz", - "integrity": "sha512-j5F1rScewLtx6pbTK0UAjA3jJj4RYiSKOix53YWv+Jzy/AZ69qHxUpU8fwVLjyKbEEud9QrLpv6Ggs7WqTimYw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.1.0.tgz", + "integrity": "sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg==", "dev": true, "requires": { - "@sinonjs/commons": "^1.0.2", - "array-from": "^2.1.1", - "lodash": "^4.17.11" + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" } }, "@sinonjs/text-encoding": { @@ -156,16 +251,27 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@sqltools/formatter": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.2.tgz", + "integrity": "sha512-/5O7Fq6Vnv8L6ucmPjaWbVG1XkP4FO+w5glqfkIsq3Xw4oyNAdJddbnYodNDAfjVUvo/rrSCTom4kAND7T1o5Q==" + }, + "@types/app-root-path": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/app-root-path/-/app-root-path-1.2.4.tgz", + "integrity": "sha1-p4twMoKzKsVN52j1US7MNWmRncc=", + "dev": true + }, "@types/chai": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz", - "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz", + "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==", "dev": true }, "@types/chai-as-promised": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.0.tgz", - "integrity": "sha512-MFiW54UOSt+f2bRw8J7LgQeIvE/9b4oGvwU7XW30S9QGAiHGnU/fmiOprsyMkdmH2rl8xSPc0/yrQw8juXU6bQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.3.tgz", + "integrity": "sha512-FQnh1ohPXJELpKhzjuDkPLR2BZCAqed+a6xV4MI/T3XzHfd2FlarfUGUdZYgqYe8oxkYn0fchHEeHfHqdZ96sg==", "dev": true, "requires": { "@types/chai": "*" @@ -181,24 +287,32 @@ "@types/node": "*" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, "@types/debug": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.2.tgz", - "integrity": "sha512-jkf6UiWUjcOqdQbatbvOm54/YbCdjt3JjiAzT/9KS2XtMmOkYHdKsI5u8fulhbuTUuiqNBfa6J5GSDiwjK+zLA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", "dev": true }, + "@types/dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-ylSC9GhfRH7m1EUXBXofhgx4lUWmFeQDINW5oLuS+gxWdfUeW4zJdeVTYVkexEW+e2VUvlZR2kGnGGipAWR7kw==", + "dev": true, + "requires": { + "dotenv": "*" + } + }, "@types/events": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", "dev": true }, - "@types/fancy-log": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-1.3.0.tgz", - "integrity": "sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw==", - "dev": true - }, "@types/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", @@ -231,6 +345,18 @@ "@types/vinyl-fs": "*" } }, + "@types/js-yaml": { + "version": "3.12.5", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.5.tgz", + "integrity": "sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, "@types/merge2": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/merge2/-/merge2-1.1.4.tgz", @@ -246,22 +372,59 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "dev": true + }, + "@types/mkdirp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz", + "integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/mocha": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.6.tgz", - "integrity": "sha512-1axi39YdtBI7z957vdqXI4Ac25e7YihYQtJa+Clnxg1zTJEaIRbndt71O3sP4GAMgiAm0pY26/b9BrY4MR/PMw==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz", + "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==", "dev": true }, "@types/node": { - "version": "9.6.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.42.tgz", - "integrity": "sha512-SpeVQJFekfnEaZZO1yl4je/36upII36L7gOT4DBx51B1GeAB45mmDb3a5OBQB+ZeFxVVOP37r8Owsl940G/fBg==", + "version": "14.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz", + "integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/readable-stream": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz", + "integrity": "sha512-sqsgQqFT7HmQz/V5jH1O0fvQQnXAJO46Gg9LRO/JPfjmVmGUlcx831TZZO3Y3HtWhIkzf3kTsNT0Z0kzIhIvZw==", + "dev": true, + "requires": { + "@types/node": "*", + "safe-buffer": "*" + } + }, "@types/rimraf": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.2.tgz", - "integrity": "sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-7WhJ0MdpFgYQPXlF4Dx+DhgvlPCfz/x5mHaeDQAKhcenvQP1KCpLQ18JklAqeGMYSAT2PxLpzd0g2/HE7fj7hQ==", "dev": true, "requires": { "@types/glob": "*", @@ -278,15 +441,41 @@ } }, "@types/sinon": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.0.8.tgz", - "integrity": "sha512-v8HKmpYANbS3y0cWji16uk/CV9v2CqpI+wxeW7WVZPU2E8MAcvBYbCBpeukhaWPRHbTO3tu8m6vb2IXWCbEUOQ==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.5.tgz", + "integrity": "sha512-4CnkGdM/5/FXDGqL32JQ1ttVrGvhOoesLLF7VnTh4KdjK5N5VQOtxaylFqqTjnHx55MnD9O02Nbk5c1ELC8wlQ==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz", + "integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==", "dev": true }, "@types/source-map-support": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.4.2.tgz", - "integrity": "sha512-GbGWx39O8NdUHSChdrU0XeigBAgu1Teg3llwE0slSVcH2qISaQT70ftAiH+h4HIt3VIObFU34PSpXIKJuXCybQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.3.tgz", + "integrity": "sha512-fvjMjVH8Rmokw2dWh1dkj90iX5R8FPjeZzjNH+6eFXReh0QnHFf1YBl3B0CF0RohIAA3SDRJsGeeUWKl6d7HqA==", + "dev": true, + "requires": { + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@types/tunnel": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.0.tgz", + "integrity": "sha512-FGDp0iBRiBdPjOgjJmn1NH0KDLN+Z8fRmo+9J7XGBhubq1DPrGrbmG4UTlGzrpbCpesMqD0sWkzi27EYkOMHyg==", "dev": true, "requires": { "@types/node": "*" @@ -329,12 +518,172 @@ "@types/vinyl": "*" } }, + "@types/xml2js": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.5.tgz", + "integrity": "sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { - "version": "12.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.9.tgz", - "integrity": "sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", + "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.1.0.tgz", + "integrity": "sha512-U+nRJx8XDUqJxYF0FCXbpmD9nWt/xHDDG0zsw1vrVYAmEAuD/r49iowfurjSL2uTA2JsgtpsyG7mjO7PHf2dYw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.1.0", + "@typescript-eslint/scope-manager": "4.1.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.1.0.tgz", + "integrity": "sha512-paEYLA37iqRIDPeQwAmoYSiZ3PiHsaAc3igFeBTeqRHgPnHjHLJ9OGdmP6nwAkF65p2QzEsEBtpjNUBWByNWzA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.1.0", + "@typescript-eslint/types": "4.1.0", + "@typescript-eslint/typescript-estree": "4.1.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.1.0.tgz", + "integrity": "sha512-hM/WNCQTzDHgS0Ke3cR9zPndL3OTKr9OoN9CL3UqulsAjYDrglSwIIgswSmHBcSbOzLmgaMARwrQEbIumIglvQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.1.0", + "@typescript-eslint/types": "4.1.0", + "@typescript-eslint/typescript-estree": "4.1.0", + "debug": "^4.1.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.1.0.tgz", + "integrity": "sha512-HD1/u8vFNnxwiHqlWKC/Pigdn0Mvxi84Y6GzbZ5f5sbLrFKu0al02573Er+D63Sw67IffVUXR0uR8rpdfdk+vA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.1.0", + "@typescript-eslint/visitor-keys": "4.1.0" + } + }, + "@typescript-eslint/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.1.0.tgz", + "integrity": "sha512-rkBqWsO7m01XckP9R2YHVN8mySOKKY2cophGM8K5uDK89ArCgahItQYdbg/3n8xMxzu2elss+an1TphlUpDuJw==", "dev": true }, + "@typescript-eslint/typescript-estree": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.1.0.tgz", + "integrity": "sha512-r6et57qqKAWU173nWyw31x7OfgmKfMEcjJl9vlJEzS+kf9uKNRr4AVTRXfTCwebr7bdiVEkfRY5xGnpPaNPe4Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.1.0", + "@typescript-eslint/visitor-keys": "4.1.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.1.0.tgz", + "integrity": "sha512-+taO0IZGCtCEsuNTTF2Q/5o8+fHrlml8i9YsZt2AiDCdYEJzYlsmRY991l/6f3jNXFyAWepdQj7n8Na6URiDRQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.1.0", + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + } + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -357,12 +706,61 @@ "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", "dev": true }, - "add-stream": { + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "adal-node": { + "version": "0.1.28", + "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", + "integrity": "sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU=", + "dev": true, + "requires": { + "@types/node": "^8.0.47", + "async": ">=0.6.0", + "date-utils": "*", + "jws": "3.x.x", + "request": ">= 2.52.0", + "underscore": ">= 1.3.1", + "uuid": "^3.1.0", + "xmldom": ">= 0.1.x", + "xpath.js": "~1.1.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.63.tgz", + "integrity": "sha512-g+nSkeHFDd2WOQChfmy9SAXLywT47WZBrGS/NC5ym5PJ8c8RC6l4pbGaUW/X0+eZJnXw6/AVNEouXWhV4iz72Q==", + "dev": true + } + } + }, + "add-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", "integrity": "sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=", "dev": true }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", @@ -401,10 +799,21 @@ } }, "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } }, "ansi-gray": { "version": "0.1.1", @@ -427,12 +836,14 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -443,10 +854,10 @@ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", "dev": true }, - "any-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", - "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", "dev": true }, "any-promise": { @@ -462,6 +873,111 @@ "requires": { "micromatch": "^3.1.4", "normalize-path": "^2.1.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } } }, "app-root-path": { @@ -501,9 +1017,9 @@ } }, "arg": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", - "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, "argparse": { @@ -562,12 +1078,6 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -635,18 +1145,9 @@ } }, "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, "array-unique": { @@ -655,6 +1156,18 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", + "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.4" + } + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -688,6 +1201,12 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -695,21 +1214,21 @@ "dev": true }, "async-done": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.1.tgz", - "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.2", - "process-nextick-args": "^1.0.7", + "process-nextick-args": "^2.0.0", "stream-exhaust": "^1.0.1" } }, "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, "async-settle": { @@ -740,80 +1259,18 @@ "dev": true }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", "dev": true }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - } + "follow-redirects": "1.5.10" } }, "bach": { @@ -907,22 +1364,27 @@ "tweetnacl": "^0.14.3" } }, - "big-number": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/big-number/-/big-number-0.3.1.tgz", - "integrity": "sha1-rHMCDApZu3nrF8LOLbd/d9l04BM=", - "dev": true + "better-sqlite3": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-7.1.0.tgz", + "integrity": "sha512-FV/snQ8F/kyqhdxsevzbojVtMowDWOfe1A5N3lYu1KJwoho2t7JgITmdlSc7DkOh3Zq65I+ZyeNWXQrkLEDFTg==", + "dev": true, + "requires": { + "bindings": "^1.5.0", + "prebuild-install": "^5.3.3", + "tar": "4.4.10" + } }, "bignumber.js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", - "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", "dev": true }, "binary-extensions": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", - "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, "binaryextensions": { @@ -931,16 +1393,35 @@ "integrity": "sha512-xVNN69YGDghOqCCtA6FI7avYrr02mTJjOgB0/f1VPD3pJC8QEvjTKWc4epDx8AqxxA75NI0QpVM2gPJXUbE4Tg==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npm.taobao.org/bindings/download/bindings-1.5.0.tgz", + "integrity": "sha1-EDU8npRTNLwFEabZCzj7x8nFBN8=", + "dev": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", "dev": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" } }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "optional": true, + "requires": { + "inherits": "~2.0.0" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -951,32 +1432,12 @@ } }, "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "fill-range": "^7.0.1" } }, "browser-stdout": { @@ -986,15 +1447,15 @@ "dev": true }, "bson": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.0.tgz", - "integrity": "sha512-9Aeai9TacfNtWXOYarkFJRW2CWo+dRon+fuLZYJmvLV3+MiUp0bEI6IAZfXEIg7/Pl/7IWlLaDnhzTsD81etQA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==", "dev": true }, "buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", - "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -1006,6 +1467,12 @@ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", "dev": true }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "dev": true + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -1041,44 +1508,31 @@ "unset-value": "^1.0.0" } }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", "dev": true, "requires": { - "caller-callsite": "^2.0.0" + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" } }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", "dev": true, "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" } }, "caseless": { @@ -1111,15 +1565,44 @@ } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } } }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -1127,50 +1610,124 @@ "dev": true }, "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", "glob-parent": "^3.1.0", - "inherits": "^2.0.1", + "inherits": "^2.0.3", "is-binary-path": "^1.0.0", "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", + "normalize-path": "^3.0.0", "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - } - }, - "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "class-transformer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.2.3.tgz", - "integrity": "sha512-qsP+0xoavpOlJHuYsQJsN58HXSl8Jvveo+T37rEvCEeRfMWoytAyR0Ua/YsFgpM6AZYZ/og2PJwArwzJl1aXtQ==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "class-transformer": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.3.1.tgz", + "integrity": "sha512-cKFwohpJbuMovS8xVLmn8N2AUbAuc8pVo4zEfsUVo8qgECOogns1WVk/FkOZoxhOPTyTYFckuoH+13FO+MQ8GA==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", "isobject": "^3.0.0", @@ -1188,137 +1745,89 @@ } } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "^3.1.0" } }, "cli-highlight": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.0.0.tgz", - "integrity": "sha512-cW9HBA7Z7YETTwncdScUBUUDj8AnBU4rq6qQt6NbSXG2sFLcQ1LHEAGadRWydVtNXnH6StuN4GDCX5yddJDgew==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.10.tgz", + "integrity": "sha512-CcPFD3JwdQ2oSzy+AMG6j3LRTkNjM82kzcSKzoVw6cLanDCJNlsLjeqVTOTfOfucnWv5F0rmBemVf1m9JiIasw==", "requires": { - "chalk": "^2.3.0", - "highlight.js": "^9.6.0", + "chalk": "^4.0.0", + "highlight.js": "^10.0.0", "mz": "^2.4.0", - "parse5": "^4.0.0", - "yargs": "^11.0.0" - }, - "dependencies": { - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "requires": { - "invert-kv": "^2.0.0" - } - }, - "yargs": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.1.tgz", - "integrity": "sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==", - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" - }, - "dependencies": { - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", - "requires": { - "camelcase": "^4.1.0" - } - } - } - } + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" } }, "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, "requires": { - "slice-ansi": "0.0.4", - "string-width": "^1.0.1" + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" }, "dependencies": { "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.0" } } } }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true }, "clone": { "version": "2.1.2", @@ -1360,7 +1869,8 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true }, "collection-map": { "version": "1.0.0", @@ -1387,6 +1897,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -1394,7 +1905,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "color-support": { "version": "1.1.3", @@ -1412,25 +1924,31 @@ } }, "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", + "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", "dev": true }, "compare-func": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", - "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "requires": { "array-ify": "^1.0.0", - "dot-prop": "^3.0.0" + "dot-prop": "^5.1.0" } }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "concat-map": { @@ -1457,96 +1975,114 @@ "dev": true }, "conventional-changelog": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.8.tgz", - "integrity": "sha512-fb3/DOLLrQdNqN0yYn/lT6HcNsAa9A+VTDBqlZBMQcEPPIeJIMI+DBs3yu+eiYOLi22w9oShq3nn/zN6qm1Hmw==", + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.23.tgz", + "integrity": "sha512-sScUu2NHusjRC1dPc5p8/b3kT78OYr95/Bx7Vl8CPB8tF2mG1xei5iylDTRjONV5hTlzt+Cn/tBWrKdd299b7A==", "dev": true, "requires": { - "conventional-changelog-angular": "^5.0.3", - "conventional-changelog-atom": "^2.0.1", - "conventional-changelog-codemirror": "^2.0.1", - "conventional-changelog-conventionalcommits": "^3.0.2", - "conventional-changelog-core": "^3.2.2", - "conventional-changelog-ember": "^2.0.2", - "conventional-changelog-eslint": "^3.0.2", - "conventional-changelog-express": "^2.0.1", - "conventional-changelog-jquery": "^3.0.4", - "conventional-changelog-jshint": "^2.0.1", - "conventional-changelog-preset-loader": "^2.1.1" + "conventional-changelog-angular": "^5.0.11", + "conventional-changelog-atom": "^2.0.7", + "conventional-changelog-codemirror": "^2.0.7", + "conventional-changelog-conventionalcommits": "^4.4.0", + "conventional-changelog-core": "^4.2.0", + "conventional-changelog-ember": "^2.0.8", + "conventional-changelog-eslint": "^3.0.8", + "conventional-changelog-express": "^2.0.5", + "conventional-changelog-jquery": "^3.0.10", + "conventional-changelog-jshint": "^2.0.8", + "conventional-changelog-preset-loader": "^2.3.4" } }, "conventional-changelog-angular": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz", - "integrity": "sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz", + "integrity": "sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw==", "dev": true, "requires": { - "compare-func": "^1.3.1", + "compare-func": "^2.0.0", "q": "^1.5.1" } }, "conventional-changelog-atom": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.1.tgz", - "integrity": "sha512-9BniJa4gLwL20Sm7HWSNXd0gd9c5qo49gCi8nylLFpqAHhkFTj7NQfROq3f1VpffRtzfTQp4VKU5nxbe2v+eZQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.7.tgz", + "integrity": "sha512-7dOREZwzB+tCEMjRTDfen0OHwd7vPUdmU0llTy1eloZgtOP4iSLVzYIQqfmdRZEty+3w5Jz+AbhfTJKoKw1JeQ==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-cli": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.0.21.tgz", - "integrity": "sha512-gMT1XvSVmo9Np1WUXz8Mvt3K+OtzR+Xu13z0jq/3qsXBbLuYc2/oaUXVr68r3fYOL8E9dN2uvX7Hc7RkeWvRVA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-2.1.0.tgz", + "integrity": "sha512-hZ8EcpxV4LcGOZwH+U5LJQDnyA4o/uyUdmIGzmFZMB4caujavvDBo/iTgVihk0m1QKkEhJgulagrILSm1JCakA==", "dev": true, "requires": { "add-stream": "^1.0.0", - "conventional-changelog": "^3.1.8", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "tempfile": "^1.1.1" + "conventional-changelog": "^3.1.23", + "lodash": "^4.17.15", + "meow": "^7.0.0", + "tempfile": "^3.0.0" } }, "conventional-changelog-codemirror": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.1.tgz", - "integrity": "sha512-23kT5IZWa+oNoUaDUzVXMYn60MCdOygTA2I+UjnOMiYVhZgmVwNd6ri/yDlmQGXHqbKhNR5NoXdBzSOSGxsgIQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.7.tgz", + "integrity": "sha512-Oralk1kiagn3Gb5cR5BffenWjVu59t/viE6UMD/mQa1hISMPkMYhJIqX+CMeA1zXgVBO+YHQhhokEj99GP5xcg==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-conventionalcommits": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-3.0.2.tgz", - "integrity": "sha512-w1+fQSDnm/7+sPKIYC5nfRVYDszt+6HdWizrigSqWFVIiiBVzkHGeqDLMSHc+Qq9qssHVAxAak5206epZyK87A==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.4.0.tgz", + "integrity": "sha512-ybvx76jTh08tpaYrYn/yd0uJNLt5yMrb1BphDe4WBredMlvPisvMghfpnJb6RmRNcqXeuhR6LfGZGewbkRm9yA==", "dev": true, "requires": { - "compare-func": "^1.3.1", + "compare-func": "^2.0.0", + "lodash": "^4.17.15", "q": "^1.5.1" } }, "conventional-changelog-core": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.2.tgz", - "integrity": "sha512-cssjAKajxaOX5LNAJLB+UOcoWjAIBvXtDMedv/58G+YEmAXMNfC16mmPl0JDOuVJVfIqM0nqQiZ8UCm8IXbE0g==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.0.tgz", + "integrity": "sha512-8+xMvN6JvdDtPbGBqA7oRNyZD4od1h/SIzrWqHcKZjitbVXrFpozEeyn4iI4af1UwdrabQpiZMaV07fPUTGd4w==", "dev": true, "requires": { - "conventional-changelog-writer": "^4.0.5", - "conventional-commits-parser": "^3.0.2", + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^4.0.17", + "conventional-commits-parser": "^3.1.0", "dateformat": "^3.0.0", "get-pkg-repo": "^1.0.0", "git-raw-commits": "2.0.0", "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^2.0.2", - "lodash": "^4.2.1", + "git-semver-tags": "^4.1.0", + "lodash": "^4.17.15", "normalize-package-data": "^2.3.5", "q": "^1.5.1", "read-pkg": "^3.0.0", "read-pkg-up": "^3.0.0", + "shelljs": "^0.8.3", "through2": "^3.0.0" }, "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -1559,6 +2095,40 @@ "strip-bom": "^3.0.0" } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -1606,101 +2176,140 @@ "dev": true }, "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { + "inherits": "^2.0.4", "readable-stream": "2 || 3" } } } }, "conventional-changelog-ember": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.2.tgz", - "integrity": "sha512-qtZbA3XefO/n6DDmkYywDYi6wDKNNc98MMl2F9PKSaheJ25Trpi3336W8fDlBhq0X+EJRuseceAdKLEMmuX2tg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.8.tgz", + "integrity": "sha512-JEMEcUAMg4Q9yxD341OgWlESQ4gLqMWMXIWWUqoQU8yvTJlKnrvcui3wk9JvnZQyONwM2g1MKRZuAjKxr8hAXA==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-eslint": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.2.tgz", - "integrity": "sha512-Yi7tOnxjZLXlCYBHArbIAm8vZ68QUSygFS7PgumPRiEk+9NPUeucy5Wg9AAyKoBprSV3o6P7Oghh4IZSLtKCvQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.8.tgz", + "integrity": "sha512-5rTRltgWG7TpU1PqgKHMA/2ivjhrB+E+S7OCTvj0zM/QGg4vmnVH67Vq/EzvSNYtejhWC+OwzvDrLk3tqPry8A==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-express": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.1.tgz", - "integrity": "sha512-G6uCuCaQhLxdb4eEfAIHpcfcJ2+ao3hJkbLrw/jSK/eROeNfnxCJasaWdDAfFkxsbpzvQT4W01iSynU3OoPLIw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.5.tgz", + "integrity": "sha512-pW2hsjKG+xNx/Qjof8wYlAX/P61hT5gQ/2rZ2NsTpG+PgV7Rc8RCfITvC/zN9K8fj0QmV6dWmUefCteD9baEAw==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-jquery": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.4.tgz", - "integrity": "sha512-IVJGI3MseYoY6eybknnTf9WzeQIKZv7aNTm2KQsiFVJH21bfP2q7XVjfoMibdCg95GmgeFlaygMdeoDDa+ZbEQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.10.tgz", + "integrity": "sha512-QCW6wF8QgPkq2ruPaxc83jZxoWQxLkt/pNxIDn/oYjMiVgrtqNdd7lWe3vsl0hw5ENHNf/ejXuzDHk6suKsRpg==", "dev": true, "requires": { "q": "^1.5.1" } }, "conventional-changelog-jshint": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.1.tgz", - "integrity": "sha512-kRFJsCOZzPFm2tzRHULWP4tauGMvccOlXYf3zGeuSW4U0mZhk5NsjnRZ7xFWrTFPlCLV+PNmHMuXp5atdoZmEg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.8.tgz", + "integrity": "sha512-hB/iI0IiZwnZ+seYI+qEQ4b+EMQSEC8jGIvhO2Vpz1E5p8FgLz75OX8oB1xJWl+s4xBMB6f8zJr0tC/BL7YOjw==", "dev": true, "requires": { - "compare-func": "^1.3.1", + "compare-func": "^2.0.0", "q": "^1.5.1" } }, "conventional-changelog-preset-loader": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.1.1.tgz", - "integrity": "sha512-K4avzGMLm5Xw0Ek/6eE3vdOXkqnpf9ydb68XYmCc16cJ99XMMbc2oaNMuPwAsxVK6CC1yA4/I90EhmWNj0Q6HA==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", "dev": true }, "conventional-changelog-writer": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.5.tgz", - "integrity": "sha512-g/Myp4MaJ1A+f7Ai+SnVhkcWtaHk6flw0SYN7A+vQ+MTu0+gSovQWs4Pg4NtcNUcIztYQ9YHsoxHP+GGQplI7Q==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz", + "integrity": "sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==", "dev": true, "requires": { - "compare-func": "^1.3.1", - "conventional-commits-filter": "^2.0.2", + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.6", "dateformat": "^3.0.0", - "handlebars": "^4.1.0", + "handlebars": "^4.7.6", "json-stringify-safe": "^5.0.1", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "semver": "^5.5.0", + "lodash": "^4.17.15", + "meow": "^7.0.0", + "semver": "^6.0.0", "split": "^1.0.0", "through2": "^3.0.0" }, "dependencies": { + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { + "inherits": "^2.0.4", "readable-stream": "2 || 3" } } } }, "conventional-commits-filter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz", - "integrity": "sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.6.tgz", + "integrity": "sha512-4g+sw8+KA50/Qwzfr0hL5k5NWxqtrOVw4DDk3/h6L85a9Gz0/Eqp3oP+CWCNfesBvZZZEFHF7OTEbRe+yYSyKw==", "dev": true, "requires": { "lodash.ismatch": "^4.4.0", @@ -1708,26 +2317,33 @@ } }, "conventional-commits-parser": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.2.tgz", - "integrity": "sha512-y5eqgaKR0F6xsBNVSQ/5cI5qIF3MojddSUi1vKIggRkqUTbkqFKH9P5YX/AT1BVZp9DtSzBTIkvjyVLotLsVog==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.1.0.tgz", + "integrity": "sha512-RSo5S0WIwXZiRxUGTPuYFbqvrR4vpJ1BDdTlthFgvHt5kEdnd1+pdvwWphWn57/oIl4V72NMmOocFqqJ8mFFhA==", "dev": true, "requires": { "JSONStream": "^1.0.4", - "is-text-path": "^1.0.0", - "lodash": "^4.2.1", - "meow": "^4.0.0", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^7.0.0", "split2": "^2.0.0", "through2": "^3.0.0", "trim-off-newlines": "^1.0.0" }, "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { + "inherits": "^2.0.4", "readable-stream": "2 || 3" } } @@ -1758,12 +2374,6 @@ "is-plain-object": "^2.0.1" } }, - "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", - "dev": true - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1771,27 +2381,35 @@ "dev": true }, "cosmiconfig": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.1.0.tgz", - "integrity": "sha512-kCNPvthka8gvLtzAxQXvWo4FxqRB+ftRZyPZNuab5ngvM9Y7yw7hbEysglptLgpkGX9nAOKTBVkHUAe8xtYR6Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", "dev": true, "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "lodash.get": "^4.4.2", - "parse-json": "^4.0.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "dependencies": { "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "dev": true, "requires": { + "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true } } }, @@ -1799,6 +2417,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -1846,9 +2465,9 @@ } }, "dargs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", - "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", "dev": true }, "dashdash": { @@ -1861,17 +2480,18 @@ } }, "data-api-client": { - "version": "github:ArsenyYankovsky/data-api-client#42a4a26b5d7de0939b748d6d22a67022a9955a6f", - "from": "github:ArsenyYankovsky/data-api-client#support-date", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-api-client/-/data-api-client-1.1.0.tgz", + "integrity": "sha512-ZO6i4g55afRYbDwpVuF3pa6KI0pNee5VcAGAIQRHhCFAJytFCwazzSQ3iwZU8ONcRumXoHDKTp/dyIQOub1ysg==", "dev": true, "requires": { "sqlstring": "^2.3.1" } }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "date-utils": { + "version": "1.2.21", + "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", + "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=", "dev": true }, "dateformat": { @@ -1913,7 +2533,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "decamelize-keys": { "version": "1.1.0", @@ -1939,6 +2560,15 @@ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -2040,17 +2670,36 @@ } }, "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", + "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", + "dev": true, + "requires": { + "globby": "^10.0.1", + "graceful-fs": "^4.2.2", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.1", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + } } }, "delayed-stream": { @@ -2066,15 +2715,15 @@ "dev": true }, "denque": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.0.tgz", - "integrity": "sha512-gh513ac7aiKrAgjiIBWZG0EASyDF9p4JMWwKA8YU5s9figrL5SRNEMT6FDynsegakuhWd1wVqTvqvqAoDxw7wQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", + "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==", "dev": true }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true }, "detect-file": { @@ -2101,25 +2750,53 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "dot-prop": { + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, + "doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", - "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "esutils": "^2.0.2" } }, - "dotenv": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", - "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + }, + "dependencies": { + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + } + } }, - "double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", - "dev": true + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, "duplexify": { "version": "3.6.1", @@ -2153,31 +2830,53 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "editions": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", "dev": true }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", - "dev": true - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, "requires": { "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + } + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2188,23 +2887,77 @@ } }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } } }, "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -2256,6 +3009,11 @@ "es6-symbol": "^3.1.1" } }, + "escalade": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", + "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -2292,11 +3050,306 @@ } } }, + "eslint": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz", + "integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.1.3", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.0", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^1.3.0", + "espree": "^7.3.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + }, + "dependencies": { + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + } + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", + "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", + "dev": true + } + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + } + } + }, "estraverse": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", @@ -2320,34 +3373,61 @@ } }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz", + "integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^3.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" }, "dependencies": { - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "requires": { - "pump": "^3.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "pump": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "isexe": "^2.0.0" } } } @@ -2402,6 +3482,12 @@ } } }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true + }, "expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -2438,10 +3524,21 @@ } } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -2527,6 +3624,46 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "dev": true }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + } + } + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -2539,85 +3676,201 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastq": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", + "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "figlet": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.2.1.tgz", "integrity": "sha512-qc8gycfnnfOmfvPl7Fi3JeTbcvdmbZkckyUVGGAM02je7Ookvu+bBfKy1I4FKqTsQHCs3ARJ76ip/k98r+OQuQ==" }, "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "flat-cache": "^2.0.1" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/file-uri-to-path/download/file-uri-to-path-1.0.0.tgz", + "integrity": "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90=", + "dev": true + }, "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true } } }, - "find-parent-dir": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", - "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, "requires": { - "locate-path": "^2.0.0" + "semver-regex": "^2.0.0" } }, "findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", "dev": true, "requires": { "detect-file": "^1.0.0", - "is-glob": "^3.1.0", + "is-glob": "^4.0.0", "micromatch": "^3.0.4", "resolve-dir": "^1.0.1" }, "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" } } } }, "fined": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", - "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -2643,13 +3896,41 @@ }, "dependencies": { "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", "dev": true } } }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "flush-write-stream": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", @@ -2660,11 +3941,31 @@ "readable-stream": "^2.0.4" } }, - "fn-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fn-name/-/fn-name-2.0.1.tgz", - "integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=", - "dev": true + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } }, "for-in": { "version": "1.0.2", @@ -2688,9 +3989,9 @@ "dev": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", "dev": true, "requires": { "asynckit": "^0.4.0", @@ -2707,6 +4008,12 @@ "map-cache": "^0.2.2" } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-minipass": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", @@ -2732,647 +4039,111 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" }, "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true, "optional": true }, - "aproba": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, - "optional": true + "optional": true, + "requires": { + "minimist": "^1.2.5" + } }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": false, - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "glob": "^7.1.3" } + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, - "balanced-match": { + "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": false, - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "number-is-nan": "^1.0.0" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true - }, - "core-util-is": { + "string-width": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "resolved": false, - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.5.1", - "resolved": false, - "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true, - "optional": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "resolved": false, - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": false, - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.21", - "resolved": false, - "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", - "dev": true, - "optional": true, - "requires": { - "safer-buffer": "^2.1.0" - } - }, - "ignore-walk": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "resolved": false, - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": false, - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": false, - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": false, - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.0", - "resolved": false, - "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.0", - "resolved": false, - "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": false, - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.1.10", - "resolved": false, - "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": false, - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": false, - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "resolved": false, - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.7", - "resolved": false, - "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": false, - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "optional": true, - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": false, - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": false, - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "resolved": false, - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "optional": true - }, - "semver": { - "version": "5.5.0", - "resolved": false, - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", - "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.2", - "resolved": false, - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "g-status": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/g-status/-/g-status-2.0.2.tgz", - "integrity": "sha512-kQoE9qH+T1AHKgSSD0Hkv98bobE90ILQcXAF4wvGgsr7uFqNvwmh8j+Lq3l0RVt3E3HjSbv2B9biEGcEtpHLCA==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "matcher": "^1.0.0", - "simple-git": "^1.85.0" - } - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "strip-ansi": { @@ -3395,16 +4166,11 @@ "is-property": "^1.0.2" } }, - "generic-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.6.1.tgz", - "integrity": "sha512-iMmD/pY4q0+V+f8o4twE9JPeqfNuX+gJAaIPB3B0W1lFkBOtTxBo6B0HxHPgGhzQA8jego7EWopcYq/UDJO2KA==", - "dev": true - }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true }, "get-func-name": { "version": "2.0.0", @@ -3413,9 +4179,9 @@ "dev": true }, "get-own-enumerable-property-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", - "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", "dev": true }, "get-pkg-repo": { @@ -3447,12 +4213,6 @@ "map-obj": "^1.0.0" } }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", @@ -3474,99 +4234,305 @@ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + } + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-raw-commits": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", + "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==", + "dev": true, + "requires": { + "dargs": "^4.0.1", + "lodash.template": "^4.0.2", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "dargs": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", + "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "meow": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", + "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist": "^1.1.3", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", "dev": true }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" } }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", "dev": true, "requires": { - "get-stdin": "^4.0.1" + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" } }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - } - } - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "git-raw-commits": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.0.tgz", - "integrity": "sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg==", - "dev": true, - "requires": { - "dargs": "^4.0.1", - "lodash.template": "^4.0.2", - "meow": "^4.0.0", - "split2": "^2.0.0", - "through2": "^2.0.0" - }, - "dependencies": { - "dargs": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", - "integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=", + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true } } }, @@ -3589,13 +4555,21 @@ } }, "git-semver-tags": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.2.tgz", - "integrity": "sha512-34lMF7Yo1xEmsK2EkbArdoU79umpvm0MfzaDkSNYSJqtM5QLAVTPWgpiXSVI5o/O9EvZPSrP4Zvnec/CqhSd5w==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.0.tgz", + "integrity": "sha512-TcxAGeo03HdErzKzi4fDD+xEL7gi8r2Y5YSxH6N2XYdVSV5UkBwfrt7Gqo1b+uSHCjy/sa9Y6BBBxxFLxfbhTg==", "dev": true, "requires": { - "meow": "^4.0.0", - "semver": "^5.5.0" + "meow": "^7.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "gitconfiglocal": { @@ -3607,10 +4581,16 @@ "ini": "^1.3.2" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "dev": true + }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3660,9 +4640,9 @@ } }, "glob-watcher": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", - "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -3670,7 +4650,16 @@ "chokidar": "^2.0.0", "is-negated-glob": "^1.0.0", "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", "object.defaults": "^1.1.0" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + } } }, "global-modules": { @@ -3697,23 +4686,35 @@ "which": "^1.2.14" } }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", "dev": true, "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "type-fest": "^0.8.1" + } + }, + "globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" }, "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true } } @@ -3740,14 +4741,14 @@ "dev": true }, "gulp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.0.tgz", - "integrity": "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", "dev": true, "requires": { - "glob-watcher": "^5.0.0", - "gulp-cli": "^2.0.0", - "undertaker": "^1.0.0", + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", "vinyl-fs": "^3.0.0" }, "dependencies": { @@ -3775,9 +4776,9 @@ } }, "gulp-cli": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.0.1.tgz", - "integrity": "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -3788,15 +4789,15 @@ "copy-props": "^2.0.1", "fancy-log": "^1.3.2", "gulplog": "^1.0.0", - "interpret": "^1.1.0", + "interpret": "^1.4.0", "isobject": "^3.0.1", - "liftoff": "^2.5.0", + "liftoff": "^3.1.0", "matchdep": "^2.0.0", "mute-stdout": "^1.0.0", "pretty-hrtime": "^1.0.0", "replace-homedir": "^1.0.0", "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.0.1", + "v8flags": "^3.2.0", "yargs": "^7.1.0" } }, @@ -3809,15 +4810,6 @@ "number-is-nan": "^1.0.0" } }, - "os-locale": { - "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -3845,9 +4837,9 @@ "dev": true }, "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", + "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", "dev": true, "requires": { "camelcase": "^3.0.0", @@ -3862,16 +4854,239 @@ "string-width": "^1.0.2", "which-module": "^1.0.0", "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" + "yargs-parser": "5.0.0-security.0" } }, "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "version": "5.0.0-security.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", + "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + } + } + }, + "gulp-eslint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-6.0.0.tgz", + "integrity": "sha512-dCVPSh1sA+UVhn7JSQt7KEb4An2sQNbOdB3PA8UCfxsoPlAKjJHxYHGXdXC7eb+V1FAnilSFFqslPrq037l1ig==", + "dev": true, + "requires": { + "eslint": "^6.0.0", + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.1" + }, + "dependencies": { + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + }, + "dependencies": { + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "camelcase": "^3.0.0" + "has-flag": "^3.0.0" } } } @@ -3891,67 +5106,76 @@ } }, "gulp-mocha": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gulp-mocha/-/gulp-mocha-6.0.0.tgz", - "integrity": "sha512-FfBldW5ttnDpKf4Sg6/BLOOKCCbr5mbixDGK1t02/8oSrTCwNhgN/mdszG3cuQuYNzuouUdw4EH/mlYtgUscPg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/gulp-mocha/-/gulp-mocha-7.0.2.tgz", + "integrity": "sha512-ZXBGN60TXYnFhttr19mfZBOtlHYGx9SvCSc+Kr/m2cMIGloUe176HBPwvPqlakPuQgeTGVRS47NmcdZUereKMQ==", "dev": true, "requires": { - "dargs": "^5.1.0", - "execa": "^0.10.0", - "mocha": "^5.2.0", - "npm-run-path": "^2.0.2", + "dargs": "^7.0.0", + "execa": "^2.0.4", + "mocha": "^6.2.0", "plugin-error": "^1.0.1", - "supports-color": "^5.4.0", - "through2": "^2.0.3" + "supports-color": "^7.0.0", + "through2": "^3.0.1" }, "dependencies": { - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", "dev": true }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "locate-path": "^3.0.0" } }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -3962,38 +5186,82 @@ "path-is-absolute": "^1.0.0" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", + "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", + "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", "dev": true, "requires": { + "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", + "debug": "3.2.6", "diff": "3.5.0", "escape-string-regexp": "1.0.5", - "glob": "7.1.2", + "find-up": "3.0.0", + "glob": "7.1.3", "growl": "1.10.5", - "he": "1.1.1", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" + "mkdirp": "0.5.4", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" }, "dependencies": { - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "minimist": "0.0.8" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -4001,10 +5269,28 @@ } } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "plugin-error": { @@ -4017,14 +5303,114 @@ "arr-diff": "^4.0.0", "arr-union": "^3.1.0", "extend-shallow": "^3.0.2" + }, + "dependencies": { + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + } + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, "gulp-rename": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz", - "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", + "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", "dev": true }, "gulp-replace": { @@ -4039,28 +5425,87 @@ } }, "gulp-shell": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.6.5.tgz", - "integrity": "sha512-f3m1WcS0o2B72/PGj1Jbv9zYR9rynBh/EQJv64n01xQUo7j7anols0eww9GG/WtDTzGVQLrupVDYkifRFnj5Zg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.8.0.tgz", + "integrity": "sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ==", "dev": true, "requires": { - "async": "^2.1.5", - "chalk": "^2.3.0", - "fancy-log": "^1.3.2", - "lodash": "^4.17.4", - "lodash.template": "^4.4.0", - "plugin-error": "^0.1.2", - "through2": "^2.0.3" + "chalk": "^3.0.0", + "fancy-log": "^1.3.3", + "lodash.template": "^4.5.0", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "tslib": "^1.10.0" }, "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { - "lodash": "^4.17.10" + "inherits": "^2.0.4", + "readable-stream": "2 || 3" } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true } } }, @@ -4091,52 +5536,30 @@ } } }, - "gulp-tslint": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/gulp-tslint/-/gulp-tslint-8.1.4.tgz", - "integrity": "sha512-wBoZIEMJRz9urHwolsvQpngA9l931p6g/Liwz1b/KrsVP6jEBFZv/o0NS1TFCQZi/l8mXxz8+v3twhf4HOXxPQ==", - "dev": true, - "requires": { - "@types/fancy-log": "1.3.0", - "ansi-colors": "^1.0.1", - "fancy-log": "1.3.3", - "map-stream": "~0.0.7", - "plugin-error": "1.0.1", - "through": "~2.3.8" - }, - "dependencies": { - "plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dev": true, - "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - } - } - } - }, "gulp-typescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-5.0.0.tgz", - "integrity": "sha512-lMj2U+Ni6HyFaY2nr1sSQ6D014eHil5L1i52XWBaAQUR9UAUUp9btnm4yRBT2Jb8xhrwqmhMssZf/g2B7cinCA==", + "version": "6.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-6.0.0-alpha.1.tgz", + "integrity": "sha512-KoT0TTfjfT7w3JItHkgFH1T/zK4oXWC+a8xxKfniRfVcA0Fa1bKrIhztYelYmb+95RB80OLMBreknYkdwzdi2Q==", "dev": true, "requires": { - "ansi-colors": "^3.0.5", + "ansi-colors": "^4.1.1", "plugin-error": "^1.0.1", "source-map": "^0.7.3", - "through2": "^3.0.0", - "vinyl": "^2.1.0", + "through2": "^3.0.1", + "vinyl": "^2.2.0", "vinyl-fs": "^3.0.3" }, "dependencies": { "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "plugin-error": { @@ -4169,11 +5592,12 @@ "dev": true }, "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", "dev": true, "requires": { + "inherits": "^2.0.4", "readable-stream": "2 || 3" } } @@ -4228,15 +5652,41 @@ "dev": true }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "dev": true, "requires": { - "ajv": "^6.5.5", + "ajv": "^6.12.3", "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + } } }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4262,9 +5712,9 @@ } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "has-symbols": { "version": "1.0.0", @@ -4299,6 +5749,26 @@ "kind-of": "^4.0.0" }, "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -4311,20 +5781,20 @@ } }, "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, "highlight.js": { - "version": "9.15.6", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.6.tgz", - "integrity": "sha512-zozTAWM1D6sozHo8kqhfYgsac+B+q0PmsjXeyDrYIHHcBN0zTVT66+s2GW1GZv7DbyaROdLXKdabwS/WqPyIdQ==" + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.5.0.tgz", + "integrity": "sha512-xTmvd9HiIHR6L53TMC7TKolEj65zG1XU+Onr8oi86mYa+nLcIbxTTWkpW7CsEwv/vK7u1zb8alZIMLDqqN6KTw==" }, "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "requires": { "parse-passwd": "^1.0.0" @@ -4347,134 +5817,64 @@ "sshpk": "^1.7.0" } }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, "husky": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/husky/-/husky-1.3.1.tgz", - "integrity": "sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.7", - "execa": "^1.0.0", - "find-up": "^3.0.0", - "get-stdin": "^6.0.0", - "is-ci": "^2.0.0", - "pkg-dir": "^3.0.0", - "please-upgrade-node": "^3.1.1", - "read-pkg": "^4.0.1", - "run-node": "^1.0.0", - "slash": "^2.0.0" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz", + "integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" }, "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "get-stream": { + "chalk": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "p-try": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "color-name": "~1.1.4" } }, - "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "read-pkg": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", - "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", - "dev": true, - "requires": { - "normalize-package-data": "^2.3.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0" - } } } }, @@ -4492,29 +5892,41 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "requires": { "minimatch": "^3.0.4" } }, "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", "dev": true, "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" } }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, "inflight": { @@ -4532,15 +5944,201 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", + "dev": true + }, + "inquirer": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.2.tgz", + "integrity": "sha512-DF4osh1FM6l0RJc5YWYhSDB6TawiBRlbV9Cox8MWlidU218Tb7fm3lQTULyUJDfJ0tjbzl0W4q651mrCCEM55w==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.16", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rxjs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } }, "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, "invert-kv": { @@ -4561,7 +6159,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -4579,6 +6177,12 @@ } } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -4610,23 +6214,14 @@ } }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.1.tgz", + "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==", "dev": true }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -4645,9 +6240,9 @@ } }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, "is-descriptor": { @@ -4669,12 +6264,6 @@ } } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -4688,18 +6277,16 @@ "dev": true }, "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "is-glob": { "version": "4.0.0", @@ -4710,6 +6297,12 @@ "is-extglob": "^2.1.1" } }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, "is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", @@ -4717,24 +6310,10 @@ "dev": true }, "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-obj": { "version": "1.0.1", @@ -4742,38 +6321,17 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "dev": true, - "requires": { - "symbol-observable": "^1.1.0" - } - }, "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true }, "is-plain-obj": { "version": "1.1.0", @@ -4803,12 +6361,20 @@ "dev": true }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "dev": true, "requires": { - "has": "^1.0.1" + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + } } }, "is-regexp": { @@ -4826,18 +6392,39 @@ "is-unc-path": "^1.0.0" } }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + } } }, "is-text-path": { @@ -4891,7 +6478,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "3.0.1", @@ -5005,21 +6593,37 @@ "textextensions": "2" } }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "iterate-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", + "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", "dev": true }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "requires": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + } + }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, + "jsbi": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", + "integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg==", + "dev": true + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -5032,6 +6636,12 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -5081,15 +6691,36 @@ "dev": true }, "just-extend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", - "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", + "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", "dev": true }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "last-run": { @@ -5140,13 +6771,13 @@ } }, "liftoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", - "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", "dev": true, "requires": { "extend": "^3.0.0", - "findup-sync": "^2.0.0", + "findup-sync": "^3.0.0", "fined": "^1.0.1", "flagged-respawn": "^1.0.0", "is-plain-object": "^2.0.4", @@ -5155,221 +6786,225 @@ "resolve": "^1.1.7" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, "lint-staged": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.4.tgz", - "integrity": "sha512-oFbbhB/VzN8B3i/sIdb9gMfngGArI6jIfxSn+WPdQb2Ni3GJeS6T4j5VriSbQfxfMuYoQlMHOoFt+lfcWV0HfA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.3.0.tgz", + "integrity": "sha512-an3VgjHqmJk0TORB/sdQl0CTkRg4E5ybYCXTTCSJ5h9jFwZbcgKIx5oVma5e7wp/uKt17s1QYFmYqT9MGVosGw==", "dev": true, "requires": { - "@iamstarkov/listr-update-renderer": "0.4.1", - "chalk": "^2.3.1", - "commander": "^2.14.1", - "cosmiconfig": "^5.0.2", - "debug": "^3.1.0", + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.0.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.1.1", "dedent": "^0.7.0", - "del": "^3.0.0", - "execa": "^1.0.0", - "find-parent-dir": "^0.3.0", - "g-status": "^2.0.2", - "is-glob": "^4.0.0", - "is-windows": "^1.0.2", - "listr": "^0.14.2", - "lodash": "^4.17.11", - "log-symbols": "^2.2.0", - "micromatch": "^3.1.8", - "npm-which": "^3.0.1", - "p-map": "^1.1.1", - "path-is-inside": "^1.0.2", - "pify": "^3.0.0", - "please-upgrade-node": "^3.0.2", - "staged-git-files": "1.1.2", - "string-argv": "^0.0.2", - "stringify-object": "^3.2.2", - "yup": "^0.26.10" + "enquirer": "^2.3.6", + "execa": "^4.0.3", + "listr2": "^2.6.0", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" }, "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "ms": "^2.1.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" } }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { - "pump": "^3.0.0" + "chalk": "^4.0.0" } }, - "pump": { + "normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "path-key": "^3.0.0" } - } - } - }, - "listr": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", - "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", - "dev": true, - "requires": { - "@samverschueren/stream-to-observable": "^0.3.0", - "is-observable": "^1.1.0", - "is-promise": "^2.1.0", - "is-stream": "^1.1.0", - "listr-silent-renderer": "^1.1.1", - "listr-update-renderer": "^0.5.0", - "listr-verbose-renderer": "^0.5.0", - "p-map": "^2.0.0", - "rxjs": "^6.3.3" - }, - "dependencies": { - "p-map": { + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.0.0.tgz", - "integrity": "sha512-GO107XdrSUmtHxVoi60qc9tUl/KkNKm+X2CF4P9amalpGxv5YqVPJNfSb0wcA+syCopkZvYYIzW8OVTQW59x/w==", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", - "dev": true - }, - "listr-update-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", - "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "listr2": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.2.tgz", + "integrity": "sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==", "dev": true, "requires": { - "chalk": "^1.1.3", - "cli-truncate": "^0.2.1", - "elegant-spinner": "^1.0.1", - "figures": "^1.7.0", - "indent-string": "^3.0.0", - "log-symbols": "^1.0.2", - "log-update": "^2.3.0", - "strip-ansi": "^3.0.1" + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.2", + "through": "^2.3.8" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { - "chalk": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "color-name": "~1.1.4" } }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true - } - } - }, - "listr-verbose-renderer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", - "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "cli-cursor": "^2.1.0", - "date-fns": "^1.27.2", - "figures": "^2.0.0" - }, - "dependencies": { - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "aggregate-error": "^3.0.0" } } } }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -5389,18 +7024,18 @@ } }, "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash._reinterpolate": { @@ -5409,12 +7044,6 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -5453,37 +7082,141 @@ "dev": true, "requires": { "chalk": "^2.0.1" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } } } }, - "lolex": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-3.1.0.tgz", - "integrity": "sha512-zFo5MgCJ0rZ7gQg69S4pqBsLURbFw11X68C18OcJjJQbqaXm2NoTrGl1IMM3TIz0/BnN1tIs2tzmmqvCsOMMjw==", - "dev": true - }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", @@ -5501,13 +7234,12 @@ } }, "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "yallist": "^3.0.2" } }, "lru-queue": { @@ -5520,9 +7252,9 @@ } }, "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "make-iterator": { @@ -5534,14 +7266,6 @@ "kind-of": "^6.0.2" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "requires": { - "p-defer": "^1.0.0" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -5549,15 +7273,9 @@ "dev": true }, "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", - "dev": true - }, - "map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", "dev": true }, "map-visit": { @@ -5579,31 +7297,131 @@ "micromatch": "^3.0.4", "resolve": "^1.4.0", "stack-trace": "0.0.10" - } - }, - "matcher": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-1.1.1.tgz", - "integrity": "sha512-+BmqxWIubKTRKNWx/ahnCkk3mG8m7OturVlqq6HiojGJTd5hVYbgZm6WzcYPCoB+KBT4Vd6R7WSRG2OADNaCjg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.4" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" }, "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } } } }, @@ -5631,88 +7449,101 @@ "optional": true }, "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", - "dev": true, - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0" + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz", + "integrity": "sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" }, "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "dev": true, "requires": { + "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" } }, "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } } }, "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true } } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "merge2": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", @@ -5720,45 +7551,47 @@ "dev": true }, "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", "dev": true }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "dev": true, "requires": { - "mime-db": "1.40.0" + "mime-db": "1.44.0" } }, "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true }, "minimatch": { "version": "3.0.4", @@ -5775,13 +7608,14 @@ "dev": true }, "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, "requires": { "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" } }, "minipass": { @@ -5833,45 +7667,53 @@ } }, "mkdirp": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz", - "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true }, "mocha": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", - "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz", + "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==", "dev": true, "requires": { - "ansi-colors": "3.2.3", + "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", + "chokidar": "3.4.2", + "debug": "4.1.1", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "2.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "ms": "2.1.1", - "node-environment-flags": "1.0.5", + "ms": "2.1.2", "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", + "promise.allsettled": "1.0.2", + "serialize-javascript": "4.0.0", + "strip-json-comments": "3.0.1", + "supports-color": "7.1.0", + "which": "2.0.2", "wide-align": "1.1.3", - "yargs": "13.2.2", - "yargs-parser": "13.0.0", - "yargs-unparser": "1.5.0" + "workerpool": "6.0.0", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.1" }, "dependencies": { "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-regex": { @@ -5880,173 +7722,209 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", "dev": true }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "chokidar": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", + "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" } }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "ms": "^2.1.1" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "color-name": "~1.1.4" } }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, "requires": { - "pump": "^3.0.0" + "is-glob": "^4.0.1" } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { - "invert-kv": "^2.0.0" + "binary-extensions": "^2.0.0" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "is-extglob": "^2.1.1" } }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" + "p-locate": "^5.0.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { - "minimist": "0.0.8" + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } } }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", "dev": true, "requires": { "p-try": "^2.0.0" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^3.0.2" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "picomatch": "^2.2.1" } }, "require-main-filename": { @@ -6075,13 +7953,39 @@ "ansi-regex": "^4.1.0" } }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" } }, "y18n": { @@ -6091,33 +7995,164 @@ "dev": true }, "yargs": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", - "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { - "cliui": "^4.0.0", + "cliui": "^5.0.0", "find-up": "^3.0.0", "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } } }, "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yargs-unparser": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", + "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "decamelize": "^1.2.0", + "flat": "^4.1.0", + "is-plain-obj": "^1.1.0", + "yargs": "^14.2.3" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } } } }, @@ -6128,22 +8163,14 @@ "dev": true }, "mongodb": { - "version": "3.1.13", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.13.tgz", - "integrity": "sha512-sz2dhvBZQWf3LRNDhbd30KHVzdjZx9IKC0L+kSZ/gzYquCF5zPOgGqRz6sSCqYZtKP2ekB4nfLxhGtzGHnIKxA==", - "dev": true, - "requires": { - "mongodb-core": "3.1.11", - "safe-buffer": "^5.1.2" - } - }, - "mongodb-core": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.11.tgz", - "integrity": "sha512-rD2US2s5qk/ckbiiGFHeu+yKYDXdJ1G87F6CG3YdaZpzdOm5zpoAZd/EKbPmFO6cQZ+XVXBXBJ660sSI0gc6qg==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.1.tgz", + "integrity": "sha512-uH76Zzr5wPptnjEKJRQnwTsomtFOU/kQEU8a9hKHr2M7y9qVk7Q4Pkv0EQVp88742z9+RwvsdTw6dRjDZCNu1g==", "dev": true, "requires": { - "bson": "^1.1.0", + "bl": "^2.2.0", + "bson": "^1.1.4", + "denque": "^1.4.1", "require_optional": "^1.0.1", "safe-buffer": "^5.1.2", "saslprep": "^1.0.0" @@ -6155,25 +8182,14 @@ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "mssql": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-4.3.2.tgz", - "integrity": "sha512-FMC/nvEjR+yMkkyPsHCiswckT7EExoKuTj9uC1yohtJuySgodzPqkJX7q+tsZsZyK8mFYo7JfX+li6/Xf+Irbg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-6.2.1.tgz", + "integrity": "sha512-erINJ9EUPvPuWXifZfhum0CVEVrdvnFYlpgU6WKkQW69W4W7DWqJS2FHdedHnuJWlJ8x1WW1NcD8GFfF15O2aA==", "dev": true, "requires": { - "debug": "^3.2.6", - "generic-pool": "^3.6", - "tedious": "^2.7.1" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } + "debug": "^4", + "tarn": "^1.1.5", + "tedious": "^6.6.2" } }, "mute-stdout": { @@ -6182,32 +8198,67 @@ "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", "dev": true }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, "mysql": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.16.0.tgz", - "integrity": "sha512-dPbN2LHonQp7D5ja5DJXNbCLe/HRdu+f3v61aguzNRQIrmZLOeRoymBYyeThrR6ug+FqzDL95Gc9maqZUJS+Gw==", + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", "dev": true, "requires": { - "bignumber.js": "4.1.0", - "readable-stream": "2.3.6", + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", "safe-buffer": "5.1.2", "sqlstring": "2.3.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } } }, "mysql2": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-1.6.5.tgz", - "integrity": "sha512-zedaOOyb3msuuZcJJnxIX/EGOpmljDG7B+UevRH5lqcv+yhy9eCwkArBz8/AO+/rlY3/oCsOdG8R5oD6k0hNfg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.1.0.tgz", + "integrity": "sha512-9kGVyi930rG2KaHrz3sHwtc6K+GY9d8wWk1XRSYxQiunvGcn4DwuZxOwmK11ftuhhwrYDwGx9Ta4VBwznJn36A==", "dev": true, "requires": { - "denque": "^1.4.0", + "cardinal": "^2.1.1", + "denque": "^1.4.1", "generate-function": "^2.3.1", - "iconv-lite": "^0.4.24", + "iconv-lite": "^0.5.0", "long": "^4.0.0", - "lru-cache": "^4.1.3", + "lru-cache": "^5.1.1", "named-placeholders": "^1.1.2", "seq-queue": "^0.0.5", "sqlstring": "^2.3.1" + }, + "dependencies": { + "iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } } }, "mz": { @@ -6227,12 +8278,30 @@ "dev": true, "requires": { "lru-cache": "^4.1.3" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } } }, "nan": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.0.tgz", - "integrity": "sha512-zT5nC0JhbljmyEf+Z456nvm7iO7XgRV2hYxoBtPpnyp+0Q4aCoP6uWNn76v/I6k2kCYNLWqWbwBWQcjsNI/bjw==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", "dev": true, "optional": true }, @@ -6255,16 +8324,28 @@ "to-regex": "^3.0.1" } }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, "native-duplexpair": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", "integrity": "sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A=", "dev": true }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "needle": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", - "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", + "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", "dev": true, "requires": { "debug": "^3.2.6", @@ -6298,29 +8379,37 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "nise": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.10.tgz", - "integrity": "sha512-sa0RRbj53dovjc7wombHmVli9ZihXbXCQ2uH3TNm03DyvOSIQbxg+pbqDKrk2oxMK1rtLGVlKxcB9rrc6X5YjA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz", + "integrity": "sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==", "dev": true, "requires": { - "@sinonjs/formatio": "^3.1.0", + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", - "lolex": "^2.3.2", "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "lolex": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", - "dev": true - } } }, + "node-abi": { + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.19.1.tgz", + "integrity": "sha512-HbtmIuByq44yhAzK7b9j/FelKlHYISKQn0mtvcBrU5QBkhoCMp5bu8Hv5AI34DcKfOAcJBcOEMwLlwO62FFu9A==", + "dev": true, + "requires": { + "semver": "^5.4.1" + } + }, + "node-addon-api": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz", + "integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA==", + "dev": true + }, "node-environment-flags": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", @@ -6332,13 +8421,82 @@ }, "dependencies": { "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "optional": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true, + "optional": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "dev": true, + "optional": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + } + } + }, "node-pre-gyp": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", @@ -6364,26 +8522,41 @@ "dev": true }, "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { "minimist": "^1.2.5" } }, "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, "requires": { "abbrev": "1", "osenv": "^0.1.4" } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } } } }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", + "dev": true + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -6424,47 +8597,46 @@ } }, "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", - "dev": true - }, - "npm-packlist": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", - "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "dev": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-normalize-package-bin": "^1.0.1" } }, - "npm-path": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/npm-path/-/npm-path-2.0.4.tgz", - "integrity": "sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==", + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "dev": true, "requires": { - "which": "^1.2.10" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "npm-which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz", - "integrity": "sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", + "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", "dev": true, "requires": { - "commander": "^2.9.0", - "npm-path": "^2.0.2", - "which": "^1.2.10" + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + } } }, "npmlog": { @@ -6482,7 +8654,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true }, "oauth-sign": { "version": "0.9.0", @@ -6526,6 +8699,12 @@ } } }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, "object-keys": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", @@ -6566,13 +8745,13 @@ } }, "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" } }, "object.map": { @@ -6613,14 +8792,20 @@ } }, "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "^2.1.0" } }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true + }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -6653,6 +8838,12 @@ "wordwrap": "~1.0.0" } }, + "oracledb": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/oracledb/-/oracledb-5.0.0.tgz", + "integrity": "sha512-NLE3t6KiAkpBHA1/zgjNiKaa9Z4Nnp4PuB3d0b3Kz4C8klrIrMKfHIGUySlwqgDW588m7/2hnPxU7PH6wjBd6g==", + "dev": true + }, "ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", @@ -6668,6 +8859,15 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -6682,49 +8882,46 @@ "requires": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + } }, "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-is-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", - "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true }, "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "^2.2.0" } }, "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } }, "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true }, "packet-reader": { "version": "1.0.0", @@ -6732,6 +8929,23 @@ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", "dev": true }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, "parent-require": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", @@ -6776,9 +8990,24 @@ "dev": true }, "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + } + } }, "pascalcase": { "version": "0.1.1", @@ -6795,23 +9024,19 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true }, "path-parse": { "version": "1.0.6", @@ -6835,9 +9060,9 @@ "dev": true }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, "requires": { "isarray": "0.0.1" @@ -6883,16 +9108,17 @@ "dev": true }, "pg": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-7.8.1.tgz", - "integrity": "sha512-m9aIrOV4mgfo+1Ze+eNoJwaWZDvpeBz8Kzwi0zzqLC+tQBsQgIuu+FGPqzyRv9HFlS7tHO1I33LKp9gP5g7U4Q==", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.3.3.tgz", + "integrity": "sha512-wmUyoQM/Xzmo62wgOdQAn5tl7u+IA1ZYK7qbuppi+3E+Gj4hlUxVHjInulieWrd0SfHi/ADriTb5ILJ/lsJrSg==", "dev": true, "requires": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", - "pg-connection-string": "0.1.3", - "pg-pool": "^2.0.4", - "pg-types": "~2.0.0", + "pg-connection-string": "^2.3.0", + "pg-pool": "^3.2.1", + "pg-protocol": "^1.2.5", + "pg-types": "^2.1.0", "pgpass": "1.x", "semver": "4.3.2" }, @@ -6906,9 +9132,15 @@ } }, "pg-connection-string": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz", - "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.3.0.tgz", + "integrity": "sha512-ukMTJXLI7/hZIwTW7hGMZJ0Lj0S2XQBCJ4Shv4y1zgQ/vqVea+FLhzywvPj0ujSuofu+yA4MYHGZPTsgjBgJ+w==", + "dev": true + }, + "pg-cursor": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.5.2.tgz", + "integrity": "sha512-yS0lxXA5WoIVK7BUgJr1uOJDJe5JxVezItTLvqnTXj6bF3di4UtQOrPx8RW3GpFmom2NTQfpEc2N6vvdpopQSw==", "dev": true }, "pg-int8": { @@ -6918,21 +9150,36 @@ "dev": true }, "pg-pool": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.6.tgz", - "integrity": "sha512-hod2zYQxM8Gt482q+qONGTYcg/qVcV32VHVPtktbBJs0us3Dj7xibISw0BAAXVMCzt8A/jhfJvpZaxUlqtqs0g==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.1.tgz", + "integrity": "sha512-BQDPWUeKenVrMMDN9opfns/kZo4lxmSWhIqo+cSAF7+lfi9ZclQbr9vfnlNaPr8wYF3UYjm5X0yPAhbcgqNOdA==", + "dev": true + }, + "pg-protocol": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.5.tgz", + "integrity": "sha512-1uYCckkuTfzz/FCefvavRywkowa6M5FohNMF5OjKrqo9PSR8gYc8poVmwwYQaBxhmQdBjhtP514eXy9/Us2xKg==", "dev": true }, + "pg-query-stream": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pg-query-stream/-/pg-query-stream-4.0.0.tgz", + "integrity": "sha512-Jftit2EUBn+ilh4JtAgw0JXKR54vATmYHlZ4fmIlbZgL1qT/GKUuwMzLFT8QQm+qJHZwlRIIfkMUOP7soY38ag==", + "dev": true, + "requires": { + "pg-cursor": "^2.5.2" + } + }, "pg-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.0.0.tgz", - "integrity": "sha512-THUD7gQll5tys+5eQ8Rvs7DjHiIC3bLqixk3gMN9Hu8UrCBAOjf35FoI39rTGGc3lM2HU/R+Knpxvd11mCwOMA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", "dev": true, "requires": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.0", + "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, @@ -6945,6 +9192,12 @@ "split": "^1.0.0" } }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -6967,63 +9220,69 @@ } }, "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" }, "dependencies": { "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^2.2.0" } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true } } }, "please-upgrade-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz", - "integrity": "sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", "dev": true, "requires": { "semver-compare": "^1.0.0" @@ -7100,9 +9359,9 @@ "dev": true }, "postgres-date": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz", - "integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", "dev": true }, "postgres-interval": { @@ -7114,6 +9373,56 @@ "xtend": "^4.0.0" } }, + "prebuild-install": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.5.tgz", + "integrity": "sha512-YmMO7dph9CYKi5IR/BzjOJlRzpxGGVo1EsLSUZ0mt/Mq0HWZIHOKHHcHdT69yG54C9m6i45GpItwRHpk0Py7Uw==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp": "^0.5.1", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -7127,17 +9436,30 @@ "dev": true }, "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "property-expr": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-1.5.1.tgz", - "integrity": "sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g==", + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "promise.allsettled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", + "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", + "dev": true, + "requires": { + "array.prototype.map": "^1.0.1", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "iterate-value": "^1.0.0" + } + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -7145,9 +9467,9 @@ "dev": true }, "psl": { - "version": "1.1.32", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", - "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", "dev": true }, "pump": { @@ -7190,11 +9512,20 @@ "dev": true }, "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -7289,6 +9620,111 @@ "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.2" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } } }, "rechoir": { @@ -7301,49 +9737,62 @@ } }, "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", "dev": true, "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" + "esprima": "~4.0.0" } }, "redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", + "integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", "dev": true, "requires": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" + "denque": "^1.4.1", + "redis-commands": "^1.5.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" } }, "redis-commands": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz", - "integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.6.0.tgz", + "integrity": "sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ==", "dev": true }, - "redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=", + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", "dev": true }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dev": true, + "requires": { + "redis-errors": "^1.0.0" + } + }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, - "regenerator-runtime": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", - "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", - "dev": true - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -7354,6 +9803,12 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, "remap-istanbul": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/remap-istanbul/-/remap-istanbul-0.13.0.tgz", @@ -7474,9 +9929,9 @@ } }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -7486,7 +9941,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -7496,9 +9951,22 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } } }, "require-directory": { @@ -7509,7 +9977,8 @@ "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true }, "require_optional": { "version": "1.0.1", @@ -7530,9 +9999,9 @@ } }, "resolve": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", - "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -7549,9 +10018,9 @@ } }, "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "resolve-options": { @@ -7570,12 +10039,12 @@ "dev": true }, "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "requires": { - "onetime": "^2.0.0", + "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, @@ -7585,28 +10054,48 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" } }, - "run-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz", - "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==", + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", "dev": true }, "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", "dev": true, "requires": { "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } } }, "safe-buffer": { @@ -7630,9 +10119,9 @@ "dev": true }, "saslprep": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.2.tgz", - "integrity": "sha512-4cDsYuAjXssUSjxHKRe4DTZC0agDwsCqcMqtJAQPzC74nJ7LfAJflAtC1Zed5hMzEQKj82d3tuzqdGNRsLJ4Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", "dev": true, "optional": true, "requires": { @@ -7647,7 +10136,8 @@ "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true }, "semver-compare": { "version": "1.0.0", @@ -7664,16 +10154,32 @@ "sver-compat": "^1.5.0" } }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, "seq-queue": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=", "dev": true }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true }, "set-value": { "version": "2.0.1", @@ -7711,6 +10217,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -7718,54 +10225,127 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true }, - "simple-git": { - "version": "1.107.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.107.0.tgz", - "integrity": "sha512-t4OK1JRlp4ayKRfcW6owrWcRVLyHRUlhGd0uN6ZZTqfDq8a5XpcUdOKiGRNobHEuMtNqzp0vcJNvhYWwh5PsQA==", + "simple-get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", + "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", "dev": true, "requires": { - "debug": "^4.0.1" + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" } }, "sinon": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.2.5.tgz", - "integrity": "sha512-1c2KK6g5NQr9XNYCEcUbeFtBpKZD1FXEw0VX7gNhWUBtkchguT2lNdS7XmS7y64OpQWfSNeeV/f8py3NNcQ63Q==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.3.0", - "@sinonjs/formatio": "^3.1.0", - "@sinonjs/samsam": "^3.2.0", - "diff": "^3.5.0", - "lolex": "^3.1.0", - "nise": "^1.4.10", - "supports-color": "^5.5.0" + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.3.tgz", + "integrity": "sha512-IKo9MIM111+smz9JGwLmw5U1075n1YXeAq8YeSFlndCLhAL5KGn6bLgu7b/4AYHTV/LcEMcRm2wU2YiL55/6Pg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.2", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/formatio": "^5.0.1", + "@sinonjs/samsam": "^5.1.0", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } } }, "sinon-chai": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.3.0.tgz", - "integrity": "sha512-r2JhDY7gbbmh5z3Q62pNbrjxZdOAjpsqW/8yxAZRSqLZqowmfGZPGUZPFf3UX36NLis0cv8VEM5IJh9HgkSOAA==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.5.0.tgz", + "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==", "dev": true }, "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } + } }, "snapdragon": { "version": "0.8.2", @@ -7909,9 +10489,9 @@ } }, "source-map-support": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", - "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -8013,28 +10593,20 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sql.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.0.0.tgz", - "integrity": "sha512-fl/cQu8hDbu9bXNXTrmwwtS2wvHttWrXS9fzINVtedOBOJJWMjwSADR1aQ+DKmCdGNF4jrg/15AedJ5kcrWRJA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.3.2.tgz", + "integrity": "sha512-vRdzoj4TCrCX8yI2mv0OVVEuOOz2IlhEfw1x1Q65BhpmLep46iu+M04zxln4u2mHRk+wj7avFq2L3/1gQS1orQ==", "dev": true }, "sqlite3": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.9.tgz", - "integrity": "sha512-IkvzjmsWQl9BuBiM4xKpl5X8WCR4w0AeJHRdobCdXZ8dT/lNc1XS6WqvY35N6+YzIIgzSBeY5prdFObID9F9tA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.0.tgz", + "integrity": "sha512-rjvqHFUaSGnzxDy2AHCwhHy6Zp6MNJzCPGYju4kD8yi6bze4d1/zMTg6C7JI49b7/EM7jKMTvyfN/4ylBKdwfw==", "dev": true, "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.11.0", - "request": "^2.87.0" - }, - "dependencies": { - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true - } + "node-addon-api": "2.0.0", + "node-gyp": "3.x", + "node-pre-gyp": "^0.11.0" } }, "sqlstring": { @@ -8066,12 +10638,6 @@ "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", "dev": true }, - "staged-git-files": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/staged-git-files/-/staged-git-files-1.1.2.tgz", - "integrity": "sha512-0Eyrk6uXW6tg9PYkhi/V/J4zHp33aNyi2hOCmhFLqLTIhbgqWn5jlSzI+IU0VqrZq6+DbHcabQl/WP6P3BG0QA==", - "dev": true - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -8106,23 +10672,44 @@ "dev": true }, "string-argv": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.0.2.tgz", - "integrity": "sha1-2sMECGkMIfPDYwo/86BYd73L1zY=", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", "dev": true }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8144,6 +10731,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, "requires": { "ansi-regex": "^3.0.0" } @@ -8163,17 +10751,21 @@ "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", "dev": true }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-indent": { + "strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -8181,11 +10773,11 @@ "dev": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } }, "sver-compat": { @@ -8198,17 +10790,74 @@ "es6-symbol": "^3.1.1" } }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true - }, - "synchronous-promise": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.6.tgz", - "integrity": "sha512-TyOuWLwkmtPL49LHCX1caIwHjRzcVd62+GF6h8W/jHOeZUFHpnd2XJDVuUlaTaLPH1nuu2M69mfHr5XbQJnf/g==", - "dev": true + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } }, "tar": { "version": "4.4.10", @@ -8248,23 +10897,135 @@ } } }, - "tedious": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/tedious/-/tedious-2.7.1.tgz", - "integrity": "sha512-u3ciATGm5byim91b3+c3MVTvY1zKjDmhUhnBQZXKymT2Vb9w322dziPQY6MhBNyBEcNONPsAMR+7/Uub7NYABQ==", + "tar-fs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", + "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz", + "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "big-number": "0.3.1", - "bl": "^1.2.2", - "depd": "^1.1.2", - "iconv-lite": "^0.4.23", + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "bl": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "tarn": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-1.1.5.tgz", + "integrity": "sha512-PMtJ3HCLAZeedWjJPgGnCvcphbCOMbtZpjKgLq3qM5Qq9aQud+XHrL0WlrlgnTyS8U+jrjGbEXprFcQrxPy52g==", + "dev": true + }, + "tedious": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-6.7.0.tgz", + "integrity": "sha512-8qr7+sB0h4SZVQBRWUgHmYuOEflAOl2eihvxk0fVNvpvGJV4V5UC/YmSvebyfgyfwWcPO22/AnSbYVZZqf9wuQ==", + "dev": true, + "requires": { + "@azure/ms-rest-nodeauth": "2.0.2", + "@types/node": "^12.12.17", + "@types/readable-stream": "^2.3.5", + "bl": "^3.0.0", + "depd": "^2.0.0", + "iconv-lite": "^0.5.0", + "jsbi": "^3.1.1", "native-duplexpair": "^1.0.0", "punycode": "^2.1.0", - "readable-stream": "^2.3.6", - "sprintf-js": "^1.1.1" + "readable-stream": "^3.4.0", + "sprintf-js": "^1.1.2" }, "dependencies": { + "@types/node": { + "version": "12.12.56", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.56.tgz", + "integrity": "sha512-8OdIupOIZtmObR13fvGyTvpcuzKmMugkATeVcfNwCjGtHxhjEKmOvLqXwR8U9VOtNnZ4EXaSfNiLVsPinaCXkQ==", + "dev": true + }, + "bl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz", + "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==", + "dev": true, + "requires": { + "readable-stream": "^3.0.1" + } + }, + "iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", @@ -8273,22 +11034,20 @@ } } }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true + }, "tempfile": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz", - "integrity": "sha1-W8xOrsxKsscH2LwR2ZzMmiyyh/I=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-3.0.0.tgz", + "integrity": "sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw==", "dev": true, "requires": { - "os-tmpdir": "^1.0.0", - "uuid": "^2.0.1" - }, - "dependencies": { - "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", - "dev": true - } + "temp-dir": "^2.0.0", + "uuid": "^3.3.2" } }, "text-extensions": { @@ -8297,6 +11056,12 @@ "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", "dev": true }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "textextensions": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.4.0.tgz", @@ -8304,9 +11069,9 @@ "dev": true }, "thenify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", - "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "requires": { "any-promise": "^1.0.0" } @@ -8361,6 +11126,15 @@ "next-tick": "1" } }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -8404,13 +11178,12 @@ } }, "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } }, "to-through": { @@ -8422,34 +11195,20 @@ "through2": "^2.0.3" } }, - "toposort": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=", - "dev": true - }, "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } + "psl": "^1.1.28", + "punycode": "^2.1.1" } }, "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", "dev": true }, "trim-off-newlines": { @@ -8459,69 +11218,53 @@ "dev": true }, "ts-node": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.0.2.tgz", - "integrity": "sha512-MosTrinKmaAcWgO8tqMjMJB22h+sp3Rd1i4fdoWY4mhBDekOwIAKI/bzmRi7IcbCmjquccYg2gcF6NBkLgr0Tw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", "dev": true, "requires": { "arg": "^4.1.0", - "diff": "^3.1.0", + "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } } }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" }, - "tslint": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.13.1.tgz", - "integrity": "sha512-fplQqb2miLbcPhyHoMV4FU9PtNRbgmm/zI5d3SZwwmJQM6V0eodju+hplpyfhLWpmwrDNfNYU57uYRb8s0zZoQ==", + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^3.2.0", - "glob": "^7.1.1", - "js-yaml": "^3.7.0", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.27.2" + "tslib": "^1.8.1" }, "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", "dev": true - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } } } }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true }, "tunnel-agent": { "version": "0.6.0", @@ -8553,6 +11296,12 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -8560,18 +11309,18 @@ "dev": true }, "typeorm-aurora-data-api-driver": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/typeorm-aurora-data-api-driver/-/typeorm-aurora-data-api-driver-1.2.0.tgz", - "integrity": "sha512-PT/y49qFk0Kub+HWITgtJ2oOQCEET9UYcpZ3LA7kgFvnrGrrbiCsDwbuJpFCXt1boqca5nkGsVCUcbzLcCaO9w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/typeorm-aurora-data-api-driver/-/typeorm-aurora-data-api-driver-1.4.0.tgz", + "integrity": "sha512-uGoVZvuStFknrhQuSzpdhdm7TG3XcOWrkHH47oMY8qNcNrYGEGkiK5Msqpoqu9nfEEY+HG6sDSx0ssmfmkF31g==", "dev": true, "requires": { - "data-api-client": "github:ArsenyYankovsky/data-api-client#support-date" + "data-api-client": "^1.1.0" } }, "typescript": { - "version": "3.3.3333", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz", - "integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.5.tgz", + "integrity": "sha512-BEjlc0Z06ORZKbtcxGrIvvwYs5hAnuo6TKdNFL55frVDlB+na3z5bsLhFaIxmT+dPWgBIjMo6aNnTOgHHmHgiQ==", "dev": true }, "uglify-js": { @@ -8607,10 +11356,16 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, + "underscore": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz", + "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw==", + "dev": true + }, "undertaker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz", - "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", "dev": true, "requires": { "arr-flatten": "^1.0.1", @@ -8618,10 +11373,19 @@ "bach": "^1.0.0", "collection-map": "^1.0.0", "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", "last-run": "^1.1.0", "object.defaults": "^1.0.0", "object.reduce": "^1.0.0", "undertaker-registry": "^1.0.0" + }, + "dependencies": { + "fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", + "dev": true + } } }, "undertaker-registry": { @@ -8693,9 +11457,9 @@ } }, "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, "uri-js": { @@ -8731,10 +11495,16 @@ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "dev": true }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, "v8flags": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz", - "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", "dev": true, "requires": { "homedir-polyfill": "^1.0.1" @@ -8834,6 +11604,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -8841,7 +11612,14 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true }, "wide-align": { "version": "1.1.3", @@ -8852,16 +11630,29 @@ "string-width": "^1.0.2 || 2" } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "workerpool": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", + "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "dev": true + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, "requires": { "string-width": "^1.0.1", "strip-ansi": "^3.0.1" @@ -8870,12 +11661,14 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8884,6 +11677,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8894,6 +11688,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8905,19 +11700,57 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } + } + }, "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", "requires": { "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" + "xmlbuilder": "~11.0.0" } }, "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, + "xmldom": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz", + "integrity": "sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g==", + "dev": true + }, + "xpath.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz", + "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==", + "dev": true }, "xtend": { "version": "4.0.1", @@ -8928,12 +11761,19 @@ "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", "dev": true }, "yargonaut": { @@ -8984,234 +11824,161 @@ } }, "yargs": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.1.tgz", - "integrity": "sha512-HgY0xHGmPPakg6kEDufqxZuXVtvPZcipORC8O7S44iEnwsUmP+qnhReHc6d1dyeIZkrPmYFblh45Z2oeDn++fQ==", - "requires": { - "cliui": "^4.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.0.3.tgz", + "integrity": "sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA==", + "requires": { + "cliui": "^7.0.0", + "escalade": "^3.0.2", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" + "string-width": "^4.2.0", + "y18n": "^5.0.1", + "yargs-parser": "^20.0.0" }, "dependencies": { "ansi-regex": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", - "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==" - }, - "camelcase": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", - "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "cliui": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.1.tgz", + "integrity": "sha512-rcvHOWyGyid6I1WjT/3NatKj2kDt9OdSHSXpyLXaMWFbKpGACNW8pRhhdPUq9MWUOdwn8Rz9AVETjF4105rZZQ==", "requires": { - "locate-path": "^3.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "get-caller-file": { + "color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.1.tgz", - "integrity": "sha512-SpOZHfz845AH0wJYVuZk2jWDqFmu7Xubsx+ldIpwzy5pDUpu7OJHK7QYNSA2NPlDSKQwM1GFaAkciOWjjW92Sg==" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "color-name": "~1.1.4" } }, - "mem": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", - "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^1.0.0", - "p-is-promise": "^2.0.0" - } + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", - "requires": { - "p-try": "^2.0.0" - } + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, - "p-locate": { + "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "string-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.0.0.tgz", - "integrity": "sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew==", + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.0.0" + "ansi-regex": "^5.0.0" } }, - "strip-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", - "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "requires": { - "ansi-regex": "^4.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.1.tgz", + "integrity": "sha512-/jJ831jEs4vGDbYPQp4yGKDYPSCCEQ45uZWJHE1AoYBzqdZi8+LDWas0z4HrmJXmKdpFsTiowSHXdxyFhpmdMg==" }, "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.0.0.tgz", + "integrity": "sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==" + } + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true } } }, "yargs-unparser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", - "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", "dev": true, "requires": { "flat": "^4.1.0", - "lodash": "^4.17.11", - "yargs": "^12.0.5" + "lodash": "^4.17.15", + "yargs": "^13.3.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } }, "find-up": { @@ -9223,30 +11990,12 @@ "locate-path": "^3.0.0" } }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -9257,38 +12006,10 @@ "path-exists": "^3.0.0" } }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -9309,40 +12030,71 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" } }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", + "cliui": "^5.0.0", "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^3.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" } }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -9352,24 +12104,10 @@ } }, "yn": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.0.0.tgz", - "integrity": "sha512-+Wo/p5VRfxUgBUGy2j/6KX2mj9AYJWOHuhMjMcbBFc3y54o9/4buK1ksBvuiK01C3kby8DH9lSmJdSxw+4G/2Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true - }, - "yup": { - "version": "0.26.10", - "resolved": "https://registry.npmjs.org/yup/-/yup-0.26.10.tgz", - "integrity": "sha512-keuNEbNSnsOTOuGCt3UJW69jDE3O4P+UHAakO7vSeFMnjaitcmlbij/a3oNb9g1Y1KvSKH/7O1R2PQ4m4TRylw==", - "dev": true, - "requires": { - "@babel/runtime": "7.0.0", - "fn-name": "~2.0.1", - "lodash": "^4.17.10", - "property-expr": "^1.5.0", - "synchronous-promise": "^2.0.5", - "toposort": "^2.0.2" - } } } } diff --git a/package.json b/package.json index 7f4c19f2041..eefc3ef79b9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typeorm-disable-constraint-option", "private": false, - "version": "0.2.25.4", + "version": "0.2.30", "description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, MongoDB databases.", "license": "MIT", "readmeFilename": "README.md", @@ -11,6 +11,8 @@ }, "main": "./index.js", "browser": { + "./browser/driver/aurora-data-api/AuroraDataApiDriver.js": "./browser/platform/BrowserDisabledDriversDummy.js", + "./browser/driver/cockroachdb/CockroachDriver.js": "./browser/platform/BrowserDisabledDriversDummy.js", "./browser/driver/postgres/PostgresDriver.js": "./browser/platform/BrowserDisabledDriversDummy.js", "./browser/driver/oracle/OracleDriver.ts": "./browser/platform/BrowserDisabledDriversDummy.js", "./browser/driver/sap/SapDriver.ts": "./browser/platform/BrowserDisabledDriversDummy.js", @@ -20,6 +22,14 @@ "./browser/driver/mongodb/MongoQueryRunner.js": "./browser/platform/BrowserDisabledDriversDummy.js", "./browser/entity-manager/MongoEntityManager.js": "./browser/platform/BrowserDisabledDriversDummy.js", "./browser/repository/MongoRepository.js": "./browser/platform/BrowserDisabledDriversDummy.js", + "./browser/driver/sqlite/SqliteDriver.js": "./browser/platform/BrowserDisabledDriversDummy.js", + "./browser/driver/better-sqlite3/BetterSqlite3Driver.js": "./browser/platform/BrowserDisabledDriversDummy.js", + "./browser/util/DirectoryExportedClassesLoader.js": "./browser/platform/BrowserDirectoryExportedClassesLoader.js", + "./browser/logger/FileLogger.js": "./browser/platform/BrowserFileLoggerDummy.js", + "./browser/connection/ConnectionOptionsReader.js": "./browser/platform/BrowserConnectionOptionsReaderDummy.js", + "./browser/connection/options-reader/ConnectionOptionsXmlReader.js": "./browser/platform/BrowserConnectionOptionsReaderDummy.js", + "./browser/connection/options-reader/ConnectionOptionsYmlReader.js": "./browser/platform/BrowserConnectionOptionsReaderDummy.js", + "./browser/platform/PlatformTools.js": "./browser/platform/BrowserPlatformTools.js", "./index.js": "./browser/index.js" }, "repository": { @@ -47,73 +57,84 @@ "oracle-orm" ], "devDependencies": { - "@types/chai": "^4.1.2", - "@types/chai-as-promised": "7.1.0", - "@types/debug": "4.1.2", - "@types/mocha": "^5.2.6", - "@types/node": "^9.6.0", - "@types/rimraf": "^2.0.2", + "@types/app-root-path": "^1.2.4", + "@types/chai": "^4.2.0", + "@types/chai-as-promised": "^7.1.3", + "@types/debug": "^4.1.5", + "@types/dotenv": "^8.2.0", + "@types/js-yaml": "^3.12.5", + "@types/mkdirp": "^1.0.1", + "@types/mocha": "^8.0.3", + "@types/node": "^14.6.4", + "@types/rimraf": "^3.0.0", "@types/sha.js": "^2.4.0", - "@types/sinon": "^7.0.8", - "@types/source-map-support": "^0.4.2", - "@types/yargs": "^12.0.9", + "@types/sinon": "^9.0.5", + "@types/source-map-support": "^0.5.1", + "@types/xml2js": "^0.4.5", + "@types/yargs": "^15.0.4", + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "better-sqlite3": "^7.1.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", - "class-transformer": "^0.2.3", - "conventional-changelog-angular": "^5.0.3", - "conventional-changelog-cli": "^2.0.21", - "del": "^3.0.0", - "gulp": "^4.0.0", + "class-transformer": "^0.3.1", + "conventional-changelog-angular": "^5.0.11", + "conventional-changelog-cli": "^2.1.0", + "del": "^5.1.0", + "eslint": "^7.8.1", + "gulp": "^4.0.2", + "gulp-eslint": "^6.0.0", "gulp-istanbul": "^1.1.3", - "gulp-mocha": "^6.0.0", - "gulp-rename": "^1.2.2", + "gulp-mocha": "^7.0.2", + "gulp-rename": "^2.0.0", "gulp-replace": "^1.0.0", - "gulp-shell": "^0.6.5", + "gulp-shell": "^0.8.0", "gulp-sourcemaps": "^2.6.5", - "gulp-tslint": "^8.1.4", - "gulp-typescript": "^5.0.0", + "gulp-typescript": "^6.0.0-alpha.1", "gulpclass": "^0.2.0", - "husky": "^1.3.1", - "lint-staged": "^8.1.4", - "mocha": "^6.1.4", - "mongodb": "^3.1.13", - "mssql": "^4.3.2", - "mysql": "^2.15.0", - "mysql2": "^1.6.5", - "pg": "^7.8.1", - "redis": "^2.8.0", + "husky": "^4.2.3", + "lint-staged": "^10.0.8", + "mocha": "^8.1.3", + "mongodb": "^3.5.4", + "mssql": "^6.1.0", + "mysql": "^2.18.1", + "mysql2": "^2.1.0", + "oracledb": "^5.0.0", + "pg": "^8.3.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.0.2", "remap-istanbul": "^0.13.0", - "rimraf": "^2.6.3", - "sinon": "^7.2.5", - "sinon-chai": "^3.3.0", - "source-map-support": "^0.5.10", - "sql.js": "^1.0.0", - "sqlite3": "^4.0.9", - "ts-node": "^8.0.2", - "tslint": "^5.13.1", - "typeorm-aurora-data-api-driver": "^1.2.0", - "typescript": "^3.3.3333" + "rimraf": "^3.0.2", + "sinon": "^9.0.0", + "sinon-chai": "^3.5.0", + "source-map-support": "^0.5.16", + "sql.js": "^1.3.2", + "sqlite3": "^5.0.0", + "ts-node": "^9.0.0", + "typeorm-aurora-data-api-driver": "^1.4.0", + "typescript": "~3.6.0" }, "dependencies": { + "@sqltools/formatter": "1.2.2", "app-root-path": "^3.0.0", - "buffer": "^5.1.0", - "chalk": "^2.4.2", - "cli-highlight": "^2.0.0", + "buffer": "^5.5.0", + "chalk": "^4.1.0", + "cli-highlight": "^2.1.10", "debug": "^4.1.1", - "dotenv": "^6.2.0", - "glob": "^7.1.2", - "js-yaml": "^3.13.1", - "mkdirp": "^1.0.3", + "dotenv": "^8.2.0", + "glob": "^7.1.6", + "js-yaml": "^3.14.0", + "mkdirp": "^1.0.4", "reflect-metadata": "^0.1.13", "sha.js": "^2.4.11", - "tslib": "^1.9.0", - "xml2js": "^0.4.17", + "tslib": "^1.13.0", + "xml2js": "^0.4.23", "yargonaut": "^1.1.2", - "yargs": "^13.2.1" + "yargs": "^16.0.3" }, "lint-staged": { "*.ts": [ - "tslint --fix", + "eslint --fix", "git add" ] }, @@ -124,12 +145,13 @@ "compile": "rimraf ./build && tsc", "watch": "./node_modules/.bin/tsc -w", "package": "gulp package", - "lint": "tslint -p .", + "lint": "eslint -c ./.eslintrc.js src/**/*.ts test/**/*.ts sample/**/*.ts", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 2" }, "bin": { "typeorm": "./cli.js" }, + "funding": "https://opencollective.com/typeorm", "collective": { "type": "opencollective", "url": "https://opencollective.com/typeorm", diff --git a/src/cache/DbQueryResultCache.ts b/src/cache/DbQueryResultCache.ts index 34dc8e44429..be243ce6acb 100644 --- a/src/cache/DbQueryResultCache.ts +++ b/src/cache/DbQueryResultCache.ts @@ -229,7 +229,7 @@ export class DbQueryResultCache implements QueryResultCache { if (queryRunner) return queryRunner; - return this.connection.createQueryRunner("master"); + return this.connection.createQueryRunner(); } } diff --git a/src/cache/RedisQueryResultCache.ts b/src/cache/RedisQueryResultCache.ts index 735a4279800..afda734a262 100644 --- a/src/cache/RedisQueryResultCache.ts +++ b/src/cache/RedisQueryResultCache.ts @@ -185,8 +185,11 @@ export class RedisQueryResultCache implements QueryResultCache { */ protected loadRedis(): any { try { - return PlatformTools.load(this.clientType); - + if (this.clientType === "ioredis/cluster") { + return PlatformTools.load("ioredis"); + } else { + return PlatformTools.load(this.clientType); + } } catch (e) { throw new Error(`Cannot use cache because ${this.clientType} is not installed. Please run "npm i ${this.clientType} --save".`); } diff --git a/src/cli.ts b/src/cli.ts index 26b9364c8fe..ebca7a4c8df 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node import "reflect-metadata"; -import * as yargs from "yargs"; +import yargs from "yargs"; import {SchemaSyncCommand} from "./commands/SchemaSyncCommand"; import {SchemaDropCommand} from "./commands/SchemaDropCommand"; import {QueryCommand} from "./commands/QueryCommand"; diff --git a/src/commands/CacheClearCommand.ts b/src/commands/CacheClearCommand.ts index 7c635611c81..79dba0e87a1 100644 --- a/src/commands/CacheClearCommand.ts +++ b/src/commands/CacheClearCommand.ts @@ -2,7 +2,7 @@ import {createConnection} from "../index"; import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {Connection} from "../connection/Connection"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Clear cache command. diff --git a/src/commands/CommandUtils.ts b/src/commands/CommandUtils.ts index 75f43b41aeb..bd42c4ba5c8 100644 --- a/src/commands/CommandUtils.ts +++ b/src/commands/CommandUtils.ts @@ -1,6 +1,6 @@ import * as fs from "fs"; import * as path from "path"; -const mkdirp = require("mkdirp"); +import mkdirp from "mkdirp"; /** * Command line utils functions. diff --git a/src/commands/EntityCreateCommand.ts b/src/commands/EntityCreateCommand.ts index 024aecb2ddc..13918d891c7 100644 --- a/src/commands/EntityCreateCommand.ts +++ b/src/commands/EntityCreateCommand.ts @@ -1,7 +1,7 @@ import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {CommandUtils} from "./CommandUtils"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Generates a new entity. @@ -37,7 +37,7 @@ export class EntityCreateCommand implements yargs.CommandModule { try { const fileContent = EntityCreateCommand.getTemplate(args.name as any); const filename = args.name + ".ts"; - let directory = args.dir; + let directory = args.dir as string | undefined; // if directory is not set then try to open tsconfig and find default path there if (!directory) { @@ -47,11 +47,14 @@ export class EntityCreateCommand implements yargs.CommandModule { configName: args.config as any }); const connectionOptions = await connectionOptionsReader.get(args.connection as any); - directory = connectionOptions.cli ? connectionOptions.cli.entitiesDir : undefined; + directory = connectionOptions.cli ? (connectionOptions.cli.entitiesDir || "") : ""; } catch (err) { } } - const path = process.cwd() + "/" + (directory ? (directory + "/") : "") + filename; + if (directory && !directory.startsWith("/")) { + directory = process.cwd() + "/" + directory; + } + const path = (directory ? (directory + "/") : "") + filename; const fileExists = await CommandUtils.fileExists(path); if (fileExists) { throw `File ${chalk.blue(path)} already exists`; diff --git a/src/commands/InitCommand.ts b/src/commands/InitCommand.ts index b8e553ecb3f..62922a30df5 100644 --- a/src/commands/InitCommand.ts +++ b/src/commands/InitCommand.ts @@ -2,7 +2,7 @@ import {CommandUtils} from "./CommandUtils"; import {ObjectLiteral} from "../common/ObjectLiteral"; import * as path from "path"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Generates a new project with TypeORM. @@ -113,6 +113,12 @@ export class InitCommand implements yargs.CommandModule { "database": "database.sqlite", }); break; + case "better-sqlite3": + Object.assign(options, { + type: "better-sqlite3", + "database": "database.sqlite", + }); + break; case "postgres": Object.assign(options, { "type": "postgres", @@ -457,6 +463,7 @@ services: `; case "sqlite": + case "better-sqlite3": return `version: '3' services: `; @@ -541,11 +548,14 @@ Steps to run this project: break; case "postgres": case "cockroachdb": - packageJson.dependencies["pg"] = "^7.3.0"; + packageJson.dependencies["pg"] = "^8.4.0"; break; case "sqlite": packageJson.dependencies["sqlite3"] = "^4.0.3"; break; + case "better-sqlite3": + packageJson.dependencies["better-sqlite3"] = "^7.0.0"; + break; case "oracle": packageJson.dependencies["oracledb"] = "^1.13.1"; break; diff --git a/src/commands/MigrationCreateCommand.ts b/src/commands/MigrationCreateCommand.ts index 8e56855c821..d4cb7f3fc66 100644 --- a/src/commands/MigrationCreateCommand.ts +++ b/src/commands/MigrationCreateCommand.ts @@ -2,7 +2,7 @@ import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {CommandUtils} from "./CommandUtils"; import {camelCase} from "../util/StringUtils"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Creates a new migration file. @@ -33,6 +33,12 @@ export class MigrationCreateCommand implements yargs.CommandModule { alias: "config", default: "ormconfig", describe: "Name of the file with connection configuration." + }) + .option("o", { + alias: "outputJs", + type: "boolean", + default: false, + describe: "Generate a migration file on Javascript instead of Typescript", }); } @@ -43,9 +49,12 @@ export class MigrationCreateCommand implements yargs.CommandModule { try { const timestamp = new Date().getTime(); - const fileContent = MigrationCreateCommand.getTemplate(args.name as any, timestamp); - const filename = timestamp + "-" + args.name + ".ts"; - let directory = args.dir; + const fileContent = args.outputJs ? + MigrationCreateCommand.getJavascriptTemplate(args.name as any, timestamp) + : MigrationCreateCommand.getTemplate(args.name as any, timestamp); + const extension = args.outputJs ? ".js" : ".ts"; + const filename = timestamp + "-" + args.name + extension; + let directory = args.dir as string | undefined; // if directory is not set then try to open tsconfig and find default path there if (!directory) { @@ -55,11 +64,14 @@ export class MigrationCreateCommand implements yargs.CommandModule { configName: args.config as any }); const connectionOptions = await connectionOptionsReader.get(args.connection as any); - directory = connectionOptions.cli ? connectionOptions.cli.migrationsDir : undefined; + directory = connectionOptions.cli ? (connectionOptions.cli.migrationsDir || "") : ""; } catch (err) { } } - const path = process.cwd() + "/" + (directory ? (directory + "/") : "") + filename; + if (directory && !directory.startsWith("/")) { + directory = process.cwd() + "/" + directory; + } + const path = (directory ? (directory + "/") : "") + filename; await CommandUtils.createFile(path, fileContent); console.log(`Migration ${chalk.blue(path)} has been generated successfully.`); @@ -92,4 +104,20 @@ export class ${camelCase(name, true)}${timestamp} implements MigrationInterface `; } + /** + * Gets contents of the migration file in Javascript. + */ + protected static getJavascriptTemplate(name: string, timestamp: number): string { + return `const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class ${camelCase(name, true)}${timestamp} { + + async up(queryRunner) { + } + + async down(queryRunner) { + } +} + `; + } } diff --git a/src/commands/MigrationGenerateCommand.ts b/src/commands/MigrationGenerateCommand.ts index faf6edd382c..f10d198497e 100644 --- a/src/commands/MigrationGenerateCommand.ts +++ b/src/commands/MigrationGenerateCommand.ts @@ -1,12 +1,12 @@ import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {CommandUtils} from "./CommandUtils"; -import {Connection} from "../connection/Connection"; import {createConnection} from "../index"; import {MysqlDriver} from "../driver/mysql/MysqlDriver"; import {camelCase} from "../util/StringUtils"; import * as yargs from "yargs"; import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; -const chalk = require("chalk"); +import chalk from "chalk"; +import { format } from "@sqltools/formatter/lib/sqlFormatter"; /** * Generates a new migration file with sql needs to be executed to update schema. @@ -27,16 +27,29 @@ export class MigrationGenerateCommand implements yargs.CommandModule { .option("n", { alias: "name", describe: "Name of the migration class.", - demand: true + demand: true, + type: "string" }) .option("d", { alias: "dir", describe: "Directory where migration should be created." }) + .option("p", { + alias: "pretty", + type: "boolean", + default: false, + describe: "Pretty-print generated SQL", + }) .option("f", { alias: "config", default: "ormconfig", describe: "Name of the file with connection configuration." + }) + .option("o", { + alias: "outputJs", + type: "boolean", + default: false, + describe: "Generate a migration file on Javascript instead of Typescript", }); } @@ -46,7 +59,8 @@ export class MigrationGenerateCommand implements yargs.CommandModule { } const timestamp = new Date().getTime(); - const filename = timestamp + "-" + args.name + ".ts"; + const extension = args.outputJs ? ".js" : ".ts"; + const filename = timestamp + "-" + args.name + extension; let directory = args.dir; // if directory is not set then try to open tsconfig and find default path there @@ -61,7 +75,6 @@ export class MigrationGenerateCommand implements yargs.CommandModule { } catch (err) { } } - let connection: Connection|undefined = undefined; try { const connectionOptionsReader = new ConnectionOptionsReader({ root: process.cwd(), @@ -74,46 +87,60 @@ export class MigrationGenerateCommand implements yargs.CommandModule { dropSchema: false, logging: false }); - connection = await createConnection(connectionOptions); - const sqlInMemory = await connection.driver.createSchemaBuilder().log(); + const upSqls: string[] = [], downSqls: string[] = []; - // mysql is exceptional here because it uses ` character in to escape names in queries, that's why for mysql - // we are using simple quoted string instead of template string syntax - if (connection.driver instanceof MysqlDriver || connection.driver instanceof AuroraDataApiDriver) { - sqlInMemory.upQueries.forEach(upQuery => { - upSqls.push(" await queryRunner.query(\"" + upQuery.query.replace(new RegExp(`"`, "g"), `\\"`) + "\"" + MigrationGenerateCommand.queryParams(upQuery.parameters) + ");"); - }); - sqlInMemory.downQueries.forEach(downQuery => { - downSqls.push(" await queryRunner.query(\"" + downQuery.query.replace(new RegExp(`"`, "g"), `\\"`) + "\"" + MigrationGenerateCommand.queryParams(downQuery.parameters) + ");"); - }); - } else { - sqlInMemory.upQueries.forEach(upQuery => { - upSqls.push(" await queryRunner.query(`" + upQuery.query.replace(new RegExp("`", "g"), "\\`") + "`" + MigrationGenerateCommand.queryParams(upQuery.parameters) + ");"); - }); - sqlInMemory.downQueries.forEach(downQuery => { - downSqls.push(" await queryRunner.query(`" + downQuery.query.replace(new RegExp("`", "g"), "\\`") + "`" + MigrationGenerateCommand.queryParams(downQuery.parameters) + ");"); - }); + const connection = await createConnection(connectionOptions); + try { + const sqlInMemory = await connection.driver.createSchemaBuilder().log(); + + if (args.pretty) { + sqlInMemory.upQueries.forEach(upQuery => { + upQuery.query = MigrationGenerateCommand.prettifyQuery(upQuery.query); + }); + sqlInMemory.downQueries.forEach(downQuery => { + downQuery.query = MigrationGenerateCommand.prettifyQuery(downQuery.query); + }); + } + + // mysql is exceptional here because it uses ` character in to escape names in queries, that's why for mysql + // we are using simple quoted string instead of template string syntax + if (connection.driver instanceof MysqlDriver || connection.driver instanceof AuroraDataApiDriver) { + sqlInMemory.upQueries.forEach(upQuery => { + upSqls.push(" await queryRunner.query(\"" + upQuery.query.replace(new RegExp(`"`, "g"), `\\"`) + "\"" + MigrationGenerateCommand.queryParams(upQuery.parameters) + ");"); + }); + sqlInMemory.downQueries.forEach(downQuery => { + downSqls.push(" await queryRunner.query(\"" + downQuery.query.replace(new RegExp(`"`, "g"), `\\"`) + "\"" + MigrationGenerateCommand.queryParams(downQuery.parameters) + ");"); + }); + } else { + sqlInMemory.upQueries.forEach(upQuery => { + upSqls.push(" await queryRunner.query(`" + upQuery.query.replace(new RegExp("`", "g"), "\\`") + "`" + MigrationGenerateCommand.queryParams(upQuery.parameters) + ");"); + }); + sqlInMemory.downQueries.forEach(downQuery => { + downSqls.push(" await queryRunner.query(`" + downQuery.query.replace(new RegExp("`", "g"), "\\`") + "`" + MigrationGenerateCommand.queryParams(downQuery.parameters) + ");"); + }); + } + } finally { + await connection.close(); } if (upSqls.length) { if (args.name) { - const fileContent = MigrationGenerateCommand.getTemplate(args.name as any, timestamp, upSqls, downSqls.reverse()); + const fileContent = args.outputJs ? + MigrationGenerateCommand.getJavascriptTemplate(args.name as any, timestamp, upSqls, downSqls.reverse()) : + MigrationGenerateCommand.getTemplate(args.name as any, timestamp, upSqls, downSqls.reverse()); const path = process.cwd() + "/" + (directory ? (directory + "/") : "") + filename; await CommandUtils.createFile(path, fileContent); console.log(chalk.green(`Migration ${chalk.blue(path)} has been generated successfully.`)); } else { - console.log(chalk.yellow("Please specify migration name")); + console.log(chalk.yellow("Please specify a migration name using the `-n` argument")); } } else { console.log(chalk.yellow(`No changes in database schema were found - cannot generate a migration. To create a new empty migration use "typeorm migration:create" command`)); + process.exit(1); } - await connection.close(); - } catch (err) { - if (connection) await (connection as Connection).close(); - console.log(chalk.black.bgRed("Error during migration generation:")); console.error(err); process.exit(1); @@ -160,4 +187,35 @@ ${downSqls.join(` `; } + /** + * Gets contents of the migration file in Javascript. + */ + protected static getJavascriptTemplate(name: string, timestamp: number, upSqls: string[], downSqls: string[]): string { + const migrationName = `${camelCase(name, true)}${timestamp}`; + + return `const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class ${migrationName} { + name = '${migrationName}' + + async up(queryRunner) { +${upSqls.join(` +`)} + } + + async down(queryRunner) { +${downSqls.join(` +`)} + } +} +`; + } + + /** + * + */ + protected static prettifyQuery(query: string) { + const formattedQuery = format(query, { indent: " " }); + return "\n" + formattedQuery.replace(/^/gm, " ") + "\n "; + } } diff --git a/src/commands/MigrationRevertCommand.ts b/src/commands/MigrationRevertCommand.ts index 5a7605ac188..1d70326c939 100644 --- a/src/commands/MigrationRevertCommand.ts +++ b/src/commands/MigrationRevertCommand.ts @@ -2,7 +2,7 @@ import {createConnection} from "../index"; import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {Connection} from "../connection/Connection"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Reverts last migration command. diff --git a/src/commands/MigrationRunCommand.ts b/src/commands/MigrationRunCommand.ts index e27915fe179..f953d5b551d 100644 --- a/src/commands/MigrationRunCommand.ts +++ b/src/commands/MigrationRunCommand.ts @@ -3,7 +3,7 @@ import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {Connection} from "../connection/Connection"; import * as process from "process"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Runs migration command. diff --git a/src/commands/MigrationShowCommand.ts b/src/commands/MigrationShowCommand.ts index d72b62c9ae3..8995faa6b0f 100644 --- a/src/commands/MigrationShowCommand.ts +++ b/src/commands/MigrationShowCommand.ts @@ -3,7 +3,7 @@ import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {Connection} from "../connection/Connection"; import * as process from "process"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Runs migration command. diff --git a/src/commands/QueryCommand.ts b/src/commands/QueryCommand.ts index 49bf0078e31..215bf9a442e 100644 --- a/src/commands/QueryCommand.ts +++ b/src/commands/QueryCommand.ts @@ -4,17 +4,21 @@ import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {Connection} from "../connection/Connection"; import {PlatformTools} from "../platform/PlatformTools"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Executes an sql query on the given connection. */ export class QueryCommand implements yargs.CommandModule { - command = "query"; + command = "query [query]"; describe = "Executes given SQL query on a default connection. Specify connection name to run query on a specific connection."; builder(args: yargs.Argv) { return args + .positional("query", { + describe: "The SQL Query to run", + type: "string" + }) .option("c", { alias: "connection", default: "default", @@ -48,11 +52,17 @@ export class QueryCommand implements yargs.CommandModule { connection = await createConnection(connectionOptions); // create a query runner and execute query using it - queryRunner = connection.createQueryRunner("master"); - console.log(chalk.green("Running query: ") + PlatformTools.highlightSql(args._[1])); - const queryResult = await queryRunner.query(args._[1]); - console.log(chalk.green("Query has been executed. Result: ")); - console.log(PlatformTools.highlightJson(JSON.stringify(queryResult, undefined, 2))); + queryRunner = connection.createQueryRunner(); + const query = args.query as string; + console.log(chalk.green("Running query: ") + PlatformTools.highlightSql(query)); + const queryResult = await queryRunner.query(query); + + if (typeof queryResult === "undefined") { + console.log(chalk.green("Query has been executed. No result was returned.")); + } else { + console.log(chalk.green("Query has been executed. Result: ")); + console.log(PlatformTools.highlightJson(JSON.stringify(queryResult, undefined, 2))); + } await queryRunner.release(); await connection.close(); diff --git a/src/commands/SchemaDropCommand.ts b/src/commands/SchemaDropCommand.ts index 2ce3caafd06..fcabb029c41 100644 --- a/src/commands/SchemaDropCommand.ts +++ b/src/commands/SchemaDropCommand.ts @@ -2,7 +2,7 @@ import {createConnection} from "../index"; import {Connection} from "../connection/Connection"; import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Drops all tables of the database from the given connection. diff --git a/src/commands/SchemaLogCommand.ts b/src/commands/SchemaLogCommand.ts index 86d99a0aac0..73c1320934f 100644 --- a/src/commands/SchemaLogCommand.ts +++ b/src/commands/SchemaLogCommand.ts @@ -3,8 +3,7 @@ import {Connection} from "../connection/Connection"; import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {highlight} from "cli-highlight"; import * as yargs from "yargs"; - -const chalk = require("chalk"); +import chalk from "chalk"; /** * Shows sql to be executed by schema:sync command. @@ -53,7 +52,7 @@ export class SchemaLogCommand implements yargs.CommandModule { } else { const lengthSeparators = String(sqlInMemory.upQueries.length).split("").map(char => "-").join(""); console.log(chalk.yellow("---------------------------------------------------------------" + lengthSeparators)); - console.log(chalk.yellow.bold(`-- Schema syncronization will execute following sql queries (${chalk.white(sqlInMemory.upQueries.length)}):`)); + console.log(chalk.yellow.bold(`-- Schema syncronization will execute following sql queries (${chalk.white(sqlInMemory.upQueries.length.toString())}):`)); console.log(chalk.yellow("---------------------------------------------------------------" + lengthSeparators)); sqlInMemory.upQueries.forEach(upQuery => { diff --git a/src/commands/SchemaSyncCommand.ts b/src/commands/SchemaSyncCommand.ts index 5ac12aab7df..3a1653344b4 100644 --- a/src/commands/SchemaSyncCommand.ts +++ b/src/commands/SchemaSyncCommand.ts @@ -2,7 +2,7 @@ import {createConnection} from "../index"; import {Connection} from "../connection/Connection"; import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Synchronizes database schema with entities. diff --git a/src/commands/SubscriberCreateCommand.ts b/src/commands/SubscriberCreateCommand.ts index b91f40d0141..48e40642295 100644 --- a/src/commands/SubscriberCreateCommand.ts +++ b/src/commands/SubscriberCreateCommand.ts @@ -1,7 +1,7 @@ import {ConnectionOptionsReader} from "../connection/ConnectionOptionsReader"; import {CommandUtils} from "./CommandUtils"; import * as yargs from "yargs"; -const chalk = require("chalk"); +import chalk from "chalk"; /** * Generates a new subscriber. @@ -38,7 +38,7 @@ export class SubscriberCreateCommand implements yargs.CommandModule { try { const fileContent = SubscriberCreateCommand.getTemplate(args.name as any); const filename = args.name + ".ts"; - let directory = args.dir; + let directory = args.dir as string | undefined; // if directory is not set then try to open tsconfig and find default path there if (!directory) { @@ -48,11 +48,14 @@ export class SubscriberCreateCommand implements yargs.CommandModule { configName: args.config as any }); const connectionOptions = await connectionOptionsReader.get(args.connection as any); - directory = connectionOptions.cli ? connectionOptions.cli.subscribersDir : undefined; + directory = connectionOptions.cli ? (connectionOptions.cli.subscribersDir || "") : ""; } catch (err) { } } - const path = process.cwd() + "/" + (directory ? (directory + "/") : "") + filename; + if (directory && !directory.startsWith("/")) { + directory = process.cwd() + "/" + directory; + } + const path = (directory ? (directory + "/") : "") + filename; await CommandUtils.createFile(path, fileContent); console.log(chalk.green(`Subscriber ${chalk.blue(path)} has been created successfully.`)); diff --git a/src/commands/VersionCommand.ts b/src/commands/VersionCommand.ts index 3f2daaebaa9..09361ea831e 100644 --- a/src/commands/VersionCommand.ts +++ b/src/commands/VersionCommand.ts @@ -1,5 +1,5 @@ import * as yargs from "yargs"; -const exec = require("child_process").exec; +import {exec} from "child_process"; /** * Shows typeorm version. diff --git a/src/common/DeepPartial.ts b/src/common/DeepPartial.ts index c318088d541..370c1f5f2d9 100644 --- a/src/common/DeepPartial.ts +++ b/src/common/DeepPartial.ts @@ -5,5 +5,5 @@ export type DeepPartial = { [P in keyof T]?: T[P] extends Array ? Array> : T[P] extends ReadonlyArray ? ReadonlyArray> : - DeepPartial + DeepPartial | T[P] }; diff --git a/src/common/EntityFieldsNames.ts b/src/common/EntityFieldsNames.ts new file mode 100644 index 00000000000..9af3f4d8a50 --- /dev/null +++ b/src/common/EntityFieldsNames.ts @@ -0,0 +1,6 @@ +/** + * Interface of the entity fields names only (without functions) + */ +export type EntityFieldsNames = { + [P in keyof Entity]: Entity[P] extends Function ? never : P; +}[keyof Entity]; diff --git a/src/common/EntityTarget.ts b/src/common/EntityTarget.ts new file mode 100644 index 00000000000..6ee00dd1be0 --- /dev/null +++ b/src/common/EntityTarget.ts @@ -0,0 +1,11 @@ +import {ObjectType} from "./ObjectType"; +import {EntitySchema} from ".."; + +/** + * Entity target. + */ +export type EntityTarget = + | ObjectType + | EntitySchema + | string + | { type: Entity, name: string }; diff --git a/src/connection/Connection.ts b/src/connection/Connection.ts index 1ddb8ff7b58..9745cb70069 100644 --- a/src/connection/Connection.ts +++ b/src/connection/Connection.ts @@ -1,6 +1,7 @@ import {Driver} from "../driver/Driver"; import {Repository} from "../repository/Repository"; import {EntitySubscriberInterface} from "../subscriber/EntitySubscriberInterface"; +import {EntityTarget} from "../common/EntityTarget"; import {ObjectType} from "../common/ObjectType"; import {EntityManager} from "../entity-manager/EntityManager"; import {DefaultNamingStrategy} from "../naming-strategy/DefaultNamingStrategy"; @@ -35,10 +36,10 @@ import {EntitySchema} from "../"; import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; import {MysqlDriver} from "../driver/mysql/MysqlDriver"; import {ObjectUtils} from "../util/ObjectUtils"; -import {PromiseUtils} from "../"; import {IsolationLevel} from "../driver/types/IsolationLevel"; import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; import {DriverUtils} from "../driver/DriverUtils"; +import {ReplicationMode} from "../driver/types/ReplicationMode"; /** * Connection is a single database ORM connection to a specific database. @@ -258,7 +259,7 @@ export class Connection { */ // TODO rename async dropDatabase(): Promise { - const queryRunner = this.createQueryRunner("master"); + const queryRunner = this.createQueryRunner(); try { if (this.driver instanceof SqlServerDriver || this.driver instanceof MysqlDriver || this.driver instanceof AuroraDataApiDriver) { const databases: string[] = this.driver.database ? [this.driver.database] : []; @@ -266,7 +267,10 @@ export class Connection { if (metadata.database && databases.indexOf(metadata.database) === -1) databases.push(metadata.database); }); - await PromiseUtils.runInSequence(databases, database => queryRunner.clearDatabase(database)); + + for (const database of databases) { + await queryRunner.clearDatabase(database); + } } else { await queryRunner.clearDatabase(); } @@ -320,14 +324,14 @@ export class Connection { /** * Checks if entity metadata exist for the given entity class, target name or table name. */ - hasMetadata(target: Function|EntitySchema|string): boolean { + hasMetadata(target: EntityTarget): boolean { return !!this.findMetadata(target); } /** * Gets entity metadata for the given entity class or schema name. */ - getMetadata(target: Function|EntitySchema|string): EntityMetadata { + getMetadata(target: EntityTarget): EntityMetadata { const metadata = this.findMetadata(target); if (!metadata) throw new EntityMetadataNotFoundError(target); @@ -338,7 +342,7 @@ export class Connection { /** * Gets repository for the given entity. */ - getRepository(target: ObjectType|EntitySchema|string): Repository { + getRepository(target: EntityTarget): Repository { return this.manager.getRepository(target); } @@ -346,7 +350,7 @@ export class Connection { * Gets tree repository for the given entity class or name. * Only tree-type entities can have a TreeRepository, like ones decorated with @Tree decorator. */ - getTreeRepository(target: ObjectType|EntitySchema|string): TreeRepository { + getTreeRepository(target: EntityTarget): TreeRepository { return this.manager.getTreeRepository(target); } @@ -354,7 +358,7 @@ export class Connection { * Gets mongodb-specific repository for the given entity class or name. * Works only if connection is mongodb-specific. */ - getMongoRepository(target: ObjectType|EntitySchema|string): MongoRepository { + getMongoRepository(target: EntityTarget): MongoRepository { if (!(this.driver instanceof MongoDriver)) throw new Error(`You can use getMongoRepository only for MongoDB connections.`); @@ -394,7 +398,7 @@ export class Connection { if (queryRunner && queryRunner.isReleased) throw new QueryRunnerProviderAlreadyReleasedError(); - const usedQueryRunner = queryRunner || this.createQueryRunner("master"); + const usedQueryRunner = queryRunner || this.createQueryRunner(); try { return await usedQueryRunner.query(query, parameters); // await is needed here because we are using finally @@ -408,7 +412,7 @@ export class Connection { /** * Creates a new query builder that can be used to build a sql query. */ - createQueryBuilder(entityClass: ObjectType|EntitySchema|Function|string, alias: string, queryRunner?: QueryRunner): SelectQueryBuilder; + createQueryBuilder(entityClass: EntityTarget, alias: string, queryRunner?: QueryRunner): SelectQueryBuilder; /** * Creates a new query builder that can be used to build a sql query. @@ -418,12 +422,12 @@ export class Connection { /** * Creates a new query builder that can be used to build a sql query. */ - createQueryBuilder(entityOrRunner?: ObjectType|EntitySchema|Function|string|QueryRunner, alias?: string, queryRunner?: QueryRunner): SelectQueryBuilder { + createQueryBuilder(entityOrRunner?: EntityTarget|QueryRunner, alias?: string, queryRunner?: QueryRunner): SelectQueryBuilder { if (this instanceof MongoEntityManager) throw new Error(`Query Builder is not supported by MongoDB.`); if (alias) { - const metadata = this.getMetadata(entityOrRunner as Function|EntitySchema|string); + const metadata = this.getMetadata(entityOrRunner as EntityTarget); return new SelectQueryBuilder(this, queryRunner) .select(alias) .from(metadata.target, alias); @@ -443,7 +447,7 @@ export class Connection { * If you perform writes you must use master database, * if you perform reads you can use slave databases. */ - createQueryRunner(mode: "master"|"slave" = "master"): QueryRunner { + createQueryRunner(mode: ReplicationMode = "master"): QueryRunner { const queryRunner = this.driver.createQueryRunner(mode); const manager = this.createEntityManager(queryRunner); Object.assign(queryRunner, { manager: manager }); @@ -453,7 +457,7 @@ export class Connection { /** * Gets entity metadata of the junction table (many-to-many table). */ - getManyToManyMetadata(entityTarget: Function|string, relationPropertyPath: string) { + getManyToManyMetadata(entityTarget: EntityTarget, relationPropertyPath: string) { const relationMetadata = this.getMetadata(entityTarget).findRelationWithPropertyPath(relationPropertyPath); if (!relationMetadata) throw new Error(`Relation "${relationPropertyPath}" was not found in ${entityTarget} entity.`); @@ -478,7 +482,7 @@ export class Connection { /** * Finds exist entity metadata by the given entity class, target name or table name. */ - protected findMetadata(target: Function|EntitySchema|string): EntityMetadata|undefined { + protected findMetadata(target: EntityTarget): EntityMetadata|undefined { return this.entityMetadatas.find(metadata => { if (metadata.target === target) return true; diff --git a/src/connection/ConnectionOptions.ts b/src/connection/ConnectionOptions.ts index 1c37c7ce843..40fd6080874 100644 --- a/src/connection/ConnectionOptions.ts +++ b/src/connection/ConnectionOptions.ts @@ -13,6 +13,7 @@ import {ExpoConnectionOptions} from "../driver/expo/ExpoConnectionOptions"; import {AuroraDataApiConnectionOptions} from "../driver/aurora-data-api/AuroraDataApiConnectionOptions"; import {SapConnectionOptions} from "../driver/sap/SapConnectionOptions"; import {AuroraDataApiPostgresConnectionOptions} from "../driver/aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions"; +import {BetterSqlite3ConnectionOptions} from "../driver/better-sqlite3/BetterSqlite3ConnectionOptions"; /** @@ -35,4 +36,5 @@ export type ConnectionOptions = MongoConnectionOptions| AuroraDataApiConnectionOptions| AuroraDataApiPostgresConnectionOptions| - ExpoConnectionOptions; + ExpoConnectionOptions| + BetterSqlite3ConnectionOptions; diff --git a/src/connection/ConnectionOptionsReader.ts b/src/connection/ConnectionOptionsReader.ts index f5dc6a8d724..a30da5d3cac 100644 --- a/src/connection/ConnectionOptionsReader.ts +++ b/src/connection/ConnectionOptionsReader.ts @@ -1,3 +1,4 @@ +import appRootPath from "app-root-path"; import {ConnectionOptions} from "./ConnectionOptions"; import {PlatformTools} from "../platform/PlatformTools"; import {ConnectionOptionsEnvReader} from "./options-reader/ConnectionOptionsEnvReader"; @@ -91,36 +92,37 @@ export class ConnectionOptionsReader { return PlatformTools.fileExist(this.baseFilePath + "." + format); }); + // Determine config file name + const configFile = fileExtension ? this.baseFilePath : this.baseFilePath + "." + foundFileFormat; + // if .env file found then load all its variables into process.env using dotenv package if (foundFileFormat === "env") { - const dotenv = PlatformTools.load("dotenv"); - dotenv.config({ path: this.baseFilePath }); - } else if (PlatformTools.fileExist(".env")) { - const dotenv = PlatformTools.load("dotenv"); - dotenv.config({ path: ".env" }); + PlatformTools.dotenv(configFile); + } else if (PlatformTools.fileExist(this.baseDirectory + "/.env")) { + PlatformTools.dotenv(this.baseDirectory + "/.env"); } - // Determine config file name - const configFile = fileExtension ? this.baseFilePath : this.baseFilePath + "." + foundFileFormat; - // try to find connection options from any of available sources of configuration - if (PlatformTools.getEnvVariable("TYPEORM_CONNECTION") || PlatformTools.getEnvVariable("TYPEORM_URL")) { - connectionOptions = new ConnectionOptionsEnvReader().read(); + if (PlatformTools.getEnvVariable("TYPEORM_CONNECTION") || PlatformTools.getEnvVariable("TYPEORM_URL")) { + connectionOptions = await new ConnectionOptionsEnvReader().read(); - } else if (foundFileFormat === "js") { - connectionOptions = await PlatformTools.load(configFile); + } else if (foundFileFormat === "js" || foundFileFormat === "cjs" || foundFileFormat === "ts") { + const configModule = await require(configFile); - } else if (foundFileFormat === "ts") { - connectionOptions = await PlatformTools.load(configFile); + if (configModule && "__esModule" in configModule && "default" in configModule) { + connectionOptions = configModule.default; + } else { + connectionOptions = configModule; + } } else if (foundFileFormat === "json") { - connectionOptions = PlatformTools.load(configFile); + connectionOptions = require(configFile); } else if (foundFileFormat === "yml") { - connectionOptions = new ConnectionOptionsYmlReader().read(configFile); + connectionOptions = await new ConnectionOptionsYmlReader().read(configFile); } else if (foundFileFormat === "yaml") { - connectionOptions = new ConnectionOptionsYmlReader().read(configFile); + connectionOptions = await new ConnectionOptionsYmlReader().read(configFile); } else if (foundFileFormat === "xml") { connectionOptions = await new ConnectionOptionsXmlReader().read(configFile); @@ -171,7 +173,7 @@ export class ConnectionOptionsReader { } // make database path file in sqlite relative to package.json - if (options.type === "sqlite") { + if (options.type === "sqlite" || options.type === "better-sqlite3") { if (typeof options.database === "string" && options.database.substr(0, 1) !== "/" && // unix absolute options.database.substr(1, 2) !== ":\\" && // windows absolute @@ -200,7 +202,7 @@ export class ConnectionOptionsReader { if (this.options && this.options.root) return this.options.root; - return PlatformTools.load("app-root-path").path; + return appRootPath.path; } /** diff --git a/src/connection/options-reader/ConnectionOptionsEnvReader.ts b/src/connection/options-reader/ConnectionOptionsEnvReader.ts index a0d00cb50b0..9ad12910048 100644 --- a/src/connection/options-reader/ConnectionOptionsEnvReader.ts +++ b/src/connection/options-reader/ConnectionOptionsEnvReader.ts @@ -16,12 +16,12 @@ export class ConnectionOptionsEnvReader { /** * Reads connection options from environment variables. */ - read(): ConnectionOptions { - return { + async read(): Promise { + return [{ type: PlatformTools.getEnvVariable("TYPEORM_CONNECTION") || (PlatformTools.getEnvVariable("TYPEORM_URL") ? PlatformTools.getEnvVariable("TYPEORM_URL").split("://")[0] : undefined), url: PlatformTools.getEnvVariable("TYPEORM_URL"), host: PlatformTools.getEnvVariable("TYPEORM_HOST"), - port: PlatformTools.getEnvVariable("TYPEORM_PORT"), + port: this.stringToNumber(PlatformTools.getEnvVariable("TYPEORM_PORT")), username: PlatformTools.getEnvVariable("TYPEORM_USERNAME"), password: PlatformTools.getEnvVariable("TYPEORM_PASSWORD"), database: PlatformTools.getEnvVariable("TYPEORM_DATABASE"), @@ -47,7 +47,7 @@ export class ConnectionOptionsEnvReader { }, cache: this.transformCaching(), uuidExtension: PlatformTools.getEnvVariable("TYPEORM_UUID_EXTENSION") - }; + }]; } // ------------------------------------------------------------------------- @@ -95,4 +95,14 @@ export class ConnectionOptionsEnvReader { return variable.split(",").map(str => str.trim()); } + /** + * Converts a string which contains a number into a javascript number + */ + private stringToNumber(value: any): number|undefined { + if (!value) { + return undefined; + } + + return parseInt(value); + } } diff --git a/src/connection/options-reader/ConnectionOptionsXmlReader.ts b/src/connection/options-reader/ConnectionOptionsXmlReader.ts index d43e9e76443..17f5cf347ef 100644 --- a/src/connection/options-reader/ConnectionOptionsXmlReader.ts +++ b/src/connection/options-reader/ConnectionOptionsXmlReader.ts @@ -1,3 +1,4 @@ +import {parseString as xmlParser} from 'xml2js'; import {PlatformTools} from "../../platform/PlatformTools"; import {ConnectionOptions} from "../ConnectionOptions"; @@ -43,11 +44,10 @@ export class ConnectionOptionsXmlReader { * Reads xml file contents and returns them in a promise. */ protected readXml(path: string): Promise { - const xmlParser = PlatformTools.load("xml2js").parseString; const xmlOptions = { trim: true, explicitRoot: false }; return new Promise((ok, fail) => { xmlParser(PlatformTools.readFileSync(path), xmlOptions, (err: any, result: any) => err ? fail(err) : ok(result)); }); } -} \ No newline at end of file +} diff --git a/src/connection/options-reader/ConnectionOptionsYmlReader.ts b/src/connection/options-reader/ConnectionOptionsYmlReader.ts index 8d90e6a519f..f43dd8867ec 100644 --- a/src/connection/options-reader/ConnectionOptionsYmlReader.ts +++ b/src/connection/options-reader/ConnectionOptionsYmlReader.ts @@ -1,3 +1,4 @@ +import ymlParser from 'js-yaml'; import {PlatformTools} from "../../platform/PlatformTools"; import {ConnectionOptions} from "../ConnectionOptions"; @@ -13,12 +14,19 @@ export class ConnectionOptionsYmlReader { /** * Reads connection options from given yml file. */ - read(path: string): ConnectionOptions[] { - const ymlParser = PlatformTools.load("js-yaml"); - const config = ymlParser.safeLoad(PlatformTools.readFileSync(path)); + async read(path: string): Promise { + const contentsBuffer = PlatformTools.readFileSync(path); + const contents = contentsBuffer.toString(); + + const config: undefined | string | {[key: string]: any} = ymlParser.safeLoad(contents); + + if (typeof config !== 'object') { + return []; + } + return Object.keys(config).map(connectionName => { return Object.assign({ name: connectionName }, config[connectionName]); }); } -} \ No newline at end of file +} diff --git a/src/decorator/Check.ts b/src/decorator/Check.ts index 138eea1174a..305fd1e5b80 100644 --- a/src/decorator/Check.ts +++ b/src/decorator/Check.ts @@ -6,21 +6,21 @@ import {CheckMetadataArgs} from "../metadata-args/CheckMetadataArgs"; * Can be used on entity property or on entity. * Can create checks with composite columns when used on entity. */ -export function Check(expression: string): Function; +export function Check(expression: string): ClassDecorator & PropertyDecorator; /** * Creates a database check. * Can be used on entity property or on entity. * Can create checks with composite columns when used on entity. */ -export function Check(name: string, expression: string): Function; +export function Check(name: string, expression: string): ClassDecorator & PropertyDecorator; /** * Creates a database check. * Can be used on entity property or on entity. * Can create checks with composite columns when used on entity. */ -export function Check(nameOrExpression: string, maybeExpression?: string): Function { +export function Check(nameOrExpression: string, maybeExpression?: string): ClassDecorator & PropertyDecorator { const name = maybeExpression ? nameOrExpression : undefined; const expression = maybeExpression ? maybeExpression : nameOrExpression; @@ -28,7 +28,7 @@ export function Check(nameOrExpression: string, maybeExpression?: string): Funct if (!expression) throw new Error(`Check expression is required`); - return function (clsOrObject: Function|Object, propertyName?: string) { + return function (clsOrObject: Function|Object, propertyName?: string | symbol) { getMetadataArgsStorage().checks.push({ target: propertyName ? clsOrObject.constructor : clsOrObject as Function, diff --git a/src/decorator/EntityRepository.ts b/src/decorator/EntityRepository.ts index 996acb395ac..3a25adcdd0d 100644 --- a/src/decorator/EntityRepository.ts +++ b/src/decorator/EntityRepository.ts @@ -1,13 +1,13 @@ -import { getMetadataArgsStorage } from "../"; -import { EntityRepositoryMetadataArgs } from "../metadata-args/EntityRepositoryMetadataArgs"; -import { EntitySchema } from "../entity-schema/EntitySchema"; +import {getMetadataArgsStorage} from "../"; +import {EntityRepositoryMetadataArgs} from "../metadata-args/EntityRepositoryMetadataArgs"; +import {EntitySchema} from "../entity-schema/EntitySchema"; /** * Used to declare a class as a custom repository. * Custom repository can manage some specific entity or just be generic. * Custom repository optionally can extend AbstractRepository, Repository or TreeRepository. */ -export function EntityRepository(entity?: Function | EntitySchema): Function { +export function EntityRepository(entity?: Function | EntitySchema): ClassDecorator { return function (target: Function) { getMetadataArgsStorage().entityRepositories.push({ diff --git a/src/decorator/Exclusion.ts b/src/decorator/Exclusion.ts index 75f3434a94a..d05394cab88 100644 --- a/src/decorator/Exclusion.ts +++ b/src/decorator/Exclusion.ts @@ -6,21 +6,21 @@ import {ExclusionMetadataArgs} from "../metadata-args/ExclusionMetadataArgs"; * Can be used on entity. * Can create exclusions with composite columns when used on entity. */ -export function Exclusion(expression: string): Function; +export function Exclusion(expression: string): ClassDecorator & PropertyDecorator; /** * Creates a database exclusion. * Can be used on entity. * Can create exclusions with composite columns when used on entity. */ -export function Exclusion(name: string, expression: string): Function; +export function Exclusion(name: string, expression: string): ClassDecorator & PropertyDecorator; /** * Creates a database exclusion. * Can be used on entity. * Can create exclusions with composite columns when used on entity. */ -export function Exclusion(nameOrExpression: string, maybeExpression?: string): Function { +export function Exclusion(nameOrExpression: string, maybeExpression?: string): ClassDecorator & PropertyDecorator { const name = maybeExpression ? nameOrExpression : undefined; const expression = maybeExpression ? maybeExpression : nameOrExpression; @@ -28,7 +28,7 @@ export function Exclusion(nameOrExpression: string, maybeExpression?: string): F if (!expression) throw new Error(`Exclusion expression is required`); - return function (clsOrObject: Function|Object, propertyName?: string) { + return function (clsOrObject: Function|Object, propertyName?: string | symbol) { getMetadataArgsStorage().exclusions.push({ target: propertyName ? clsOrObject.constructor : clsOrObject as Function, diff --git a/src/decorator/Generated.ts b/src/decorator/Generated.ts index 9857c95e497..934ccd4b6c4 100644 --- a/src/decorator/Generated.ts +++ b/src/decorator/Generated.ts @@ -10,7 +10,7 @@ import {GeneratedMetadataArgs} from "../metadata-args/GeneratedMetadataArgs"; * * Note, some databases do not support non-primary generation columns. */ -export function Generated(strategy: "increment"|"uuid"|"rowid" = "increment"): Function { +export function Generated(strategy: "increment"|"uuid"|"rowid" = "increment"): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().generations.push({ diff --git a/src/decorator/Index.ts b/src/decorator/Index.ts index 17e57ea5c0a..2bc63d68269 100644 --- a/src/decorator/Index.ts +++ b/src/decorator/Index.ts @@ -6,49 +6,49 @@ import {IndexMetadataArgs} from "../metadata-args/IndexMetadataArgs"; * Can be used on entity property or on entity. * Can create indices with composite columns when used on entity. */ -export function Index(options?: IndexOptions): Function; +export function Index(options?: IndexOptions): ClassDecorator & PropertyDecorator; /** * Creates a database index. * Can be used on entity property or on entity. * Can create indices with composite columns when used on entity. */ -export function Index(name: string, options?: IndexOptions): Function; +export function Index(name: string, options?: IndexOptions): ClassDecorator & PropertyDecorator; /** * Creates a database index. * Can be used on entity property or on entity. * Can create indices with composite columns when used on entity. */ -export function Index(name: string, options: { synchronize: false }): Function; +export function Index(name: string, options: { synchronize: false }): ClassDecorator & PropertyDecorator; /** * Creates a database index. * Can be used on entity property or on entity. * Can create indices with composite columns when used on entity. */ -export function Index(name: string, fields: string[], options?: IndexOptions): Function; +export function Index(name: string, fields: string[], options?: IndexOptions): ClassDecorator & PropertyDecorator; /** * Creates a database index. * Can be used on entity property or on entity. * Can create indices with composite columns when used on entity. */ -export function Index(fields: string[], options?: IndexOptions): Function; +export function Index(fields: string[], options?: IndexOptions): ClassDecorator & PropertyDecorator; /** * Creates a database index. * Can be used on entity property or on entity. * Can create indices with composite columns when used on entity. */ -export function Index(fields: (object?: any) => (any[]|{ [key: string]: number }), options?: IndexOptions): Function; +export function Index(fields: (object?: any) => (any[]|{ [key: string]: number }), options?: IndexOptions): ClassDecorator & PropertyDecorator; /** * Creates a database index. * Can be used on entity property or on entity. * Can create indices with composite columns when used on entity. */ -export function Index(name: string, fields: (object?: any) => (any[]|{ [key: string]: number }), options?: IndexOptions): Function; +export function Index(name: string, fields: (object?: any) => (any[]|{ [key: string]: number }), options?: IndexOptions): ClassDecorator & PropertyDecorator; /** * Creates a database index. @@ -57,7 +57,7 @@ export function Index(name: string, fields: (object?: any) => (any[]|{ [key: str */ export function Index(nameOrFieldsOrOptions?: string|string[]|((object: any) => (any[]|{ [key: string]: number }))|IndexOptions, maybeFieldsOrOptions?: ((object?: any) => (any[]|{ [key: string]: number }))|IndexOptions|string[]|{ synchronize: false }, - maybeOptions?: IndexOptions): Function { + maybeOptions?: IndexOptions): ClassDecorator & PropertyDecorator { // normalize parameters const name = typeof nameOrFieldsOrOptions === "string" ? nameOrFieldsOrOptions : undefined; @@ -66,7 +66,7 @@ export function Index(nameOrFieldsOrOptions?: string|string[]|((object: any) => if (!options) options = (typeof maybeFieldsOrOptions === "object" && !Array.isArray(maybeFieldsOrOptions)) ? maybeFieldsOrOptions as IndexOptions : maybeOptions; - return function (clsOrObject: Function|Object, propertyName?: string) { + return function (clsOrObject: Function|Object, propertyName?: string | symbol) { getMetadataArgsStorage().indices.push({ target: propertyName ? clsOrObject.constructor : clsOrObject as Function, diff --git a/src/decorator/Unique.ts b/src/decorator/Unique.ts index 871e25c58a1..1923af52ad1 100644 --- a/src/decorator/Unique.ts +++ b/src/decorator/Unique.ts @@ -1,39 +1,54 @@ -import {getMetadataArgsStorage} from "../index"; -import {UniqueMetadataArgs} from "../metadata-args/UniqueMetadataArgs"; +import { getMetadataArgsStorage } from "../index"; +import { UniqueMetadataArgs } from "../metadata-args/UniqueMetadataArgs"; /** * Composite unique constraint must be set on entity classes and must specify entity's fields to be unique. */ -export function Unique(name: string, fields: string[]): Function; +export function Unique(name: string, fields: string[]): ClassDecorator & PropertyDecorator; /** * Composite unique constraint must be set on entity classes and must specify entity's fields to be unique. */ -export function Unique(fields: string[]): Function; +export function Unique(fields: string[]): ClassDecorator & PropertyDecorator; /** * Composite unique constraint must be set on entity classes and must specify entity's fields to be unique. */ -export function Unique(fields: (object?: any) => (any[]|{ [key: string]: number })): Function; +export function Unique(fields: (object?: any) => (any[] | { [key: string]: number })): ClassDecorator & PropertyDecorator; /** * Composite unique constraint must be set on entity classes and must specify entity's fields to be unique. */ -export function Unique(name: string, fields: (object?: any) => (any[]|{ [key: string]: number })): Function; +export function Unique(name: string, fields: (object?: any) => (any[] | { [key: string]: number })): ClassDecorator & PropertyDecorator; /** * Composite unique constraint must be set on entity classes and must specify entity's fields to be unique. */ -export function Unique(nameOrFields?: string|string[]|((object: any) => (any[]|{ [key: string]: number })), - maybeFields?: ((object?: any) => (any[]|{ [key: string]: number }))|string[]): Function { +export function Unique(nameOrFields?: string | string[] | ((object: any) => (any[] | { [key: string]: number })), + maybeFields?: ((object?: any) => (any[] | { [key: string]: number })) | string[]): ClassDecorator & PropertyDecorator { const name = typeof nameOrFields === "string" ? nameOrFields : undefined; - const fields = typeof nameOrFields === "string" ? <((object?: any) => (any[]|{ [key: string]: number }))|string[]> maybeFields : nameOrFields as string[]; + const fields = typeof nameOrFields === "string" ? <((object?: any) => (any[] | { [key: string]: number })) | string[]>maybeFields : nameOrFields as string[]; + + return function (clsOrObject: Function | Object, propertyName?: string | symbol) { + + let columns = fields; + + if (propertyName !== undefined) { + switch (typeof (propertyName)) { + case "string": + columns = [propertyName]; + break; + + case "symbol": + columns = [propertyName.toString()]; + break; + } + } - return function (clsOrObject: Function|Object, propertyName?: string) { const args: UniqueMetadataArgs = { target: propertyName ? clsOrObject.constructor : clsOrObject as Function, name: name, - columns: propertyName ? [propertyName] : fields + columns, }; getMetadataArgsStorage().uniques.push(args); }; diff --git a/src/decorator/columns/Column.ts b/src/decorator/columns/Column.ts index 834c33119ea..ac5467980cd 100644 --- a/src/decorator/columns/Column.ts +++ b/src/decorator/columns/Column.ts @@ -1,7 +1,11 @@ import {ColumnOptions, getMetadataArgsStorage} from "../../"; import { - ColumnType, SimpleColumnType, SpatialColumnType, WithLengthColumnType, - WithPrecisionColumnType, WithWidthColumnType + ColumnType, + SimpleColumnType, + SpatialColumnType, + WithLengthColumnType, + WithPrecisionColumnType, + WithWidthColumnType } from "../../driver/types/ColumnTypes"; import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs"; import {ColumnCommonOptions} from "../options/ColumnCommonOptions"; @@ -14,73 +18,73 @@ import {EmbeddedMetadataArgs} from "../../metadata-args/EmbeddedMetadataArgs"; import {ColumnTypeUndefinedError} from "../../error/ColumnTypeUndefinedError"; import {ColumnHstoreOptions} from "../options/ColumnHstoreOptions"; import {ColumnWithWidthOptions} from "../options/ColumnWithWidthOptions"; -import { GeneratedMetadataArgs } from "../../metadata-args/GeneratedMetadataArgs"; +import {GeneratedMetadataArgs} from "../../metadata-args/GeneratedMetadataArgs"; /** * Column decorator is used to mark a specific class property as a table column. Only properties decorated with this * decorator will be persisted to the database when entity be saved. */ -export function Column(): Function; +export function Column(): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(options: ColumnOptions): Function; +export function Column(options: ColumnOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: SimpleColumnType, options?: ColumnCommonOptions): Function; +export function Column(type: SimpleColumnType, options?: ColumnCommonOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: SpatialColumnType, options?: ColumnCommonOptions & SpatialColumnOptions): Function; +export function Column(type: SpatialColumnType, options?: ColumnCommonOptions & SpatialColumnOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: WithLengthColumnType, options?: ColumnCommonOptions & ColumnWithLengthOptions): Function; +export function Column(type: WithLengthColumnType, options?: ColumnCommonOptions & ColumnWithLengthOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: WithWidthColumnType, options?: ColumnCommonOptions & ColumnWithWidthOptions): Function; +export function Column(type: WithWidthColumnType, options?: ColumnCommonOptions & ColumnWithWidthOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: WithPrecisionColumnType, options?: ColumnCommonOptions & ColumnNumericOptions): Function; +export function Column(type: WithPrecisionColumnType, options?: ColumnCommonOptions & ColumnNumericOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: "enum", options?: ColumnCommonOptions & ColumnEnumOptions): Function; +export function Column(type: "enum", options?: ColumnCommonOptions & ColumnEnumOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: "simple-enum", options?: ColumnCommonOptions & ColumnEnumOptions): Function; +export function Column(type: "simple-enum", options?: ColumnCommonOptions & ColumnEnumOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: "set", options?: ColumnCommonOptions & ColumnEnumOptions): Function; +export function Column(type: "set", options?: ColumnCommonOptions & ColumnEnumOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(type: "hstore", options?: ColumnCommonOptions & ColumnHstoreOptions): Function; +export function Column(type: "hstore", options?: ColumnCommonOptions & ColumnHstoreOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. @@ -90,13 +94,13 @@ export function Column(type: "hstore", options?: ColumnCommonOptions & ColumnHst * single table of the entity where Embedded is used. And on hydration all columns which supposed to be in the * embedded will be mapped to it from the single table. */ -export function Column(type: (type?: any) => Function, options?: ColumnEmbeddedOptions): Function; +export function Column(type: (type?: any) => Function, options?: ColumnEmbeddedOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. */ -export function Column(typeOrOptions?: ((type?: any) => Function)|ColumnType|(ColumnOptions&ColumnEmbeddedOptions), options?: (ColumnOptions&ColumnEmbeddedOptions)): Function { +export function Column(typeOrOptions?: ((type?: any) => Function)|ColumnType|(ColumnOptions&ColumnEmbeddedOptions), options?: (ColumnOptions&ColumnEmbeddedOptions)): PropertyDecorator { return function (object: Object, propertyName: string) { // normalize parameters diff --git a/src/decorator/columns/CreateDateColumn.ts b/src/decorator/columns/CreateDateColumn.ts index 6d9acd802d0..b14bf5c4f3a 100644 --- a/src/decorator/columns/CreateDateColumn.ts +++ b/src/decorator/columns/CreateDateColumn.ts @@ -6,7 +6,7 @@ import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs"; * Creation date is generated and inserted only once, * at the first time when you create an object, the value is inserted into the table, and is never touched again. */ -export function CreateDateColumn(options?: ColumnOptions): Function { +export function CreateDateColumn(options?: ColumnOptions): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().columns.push({ target: object.constructor, diff --git a/src/decorator/columns/DeleteDateColumn.ts b/src/decorator/columns/DeleteDateColumn.ts index 35ea43b8480..deece8bba07 100644 --- a/src/decorator/columns/DeleteDateColumn.ts +++ b/src/decorator/columns/DeleteDateColumn.ts @@ -5,7 +5,7 @@ import { ColumnMetadataArgs } from "../../metadata-args/ColumnMetadataArgs"; * This column will store a delete date of the soft-deleted object. * This date is being updated each time you soft-delete the object. */ -export function DeleteDateColumn(options?: ColumnOptions): Function { +export function DeleteDateColumn(options?: ColumnOptions): PropertyDecorator { return function(object: Object, propertyName: string) { getMetadataArgsStorage().columns.push({ target: object.constructor, diff --git a/src/decorator/columns/ObjectIdColumn.ts b/src/decorator/columns/ObjectIdColumn.ts index 257aebf347e..5feeb201dec 100644 --- a/src/decorator/columns/ObjectIdColumn.ts +++ b/src/decorator/columns/ObjectIdColumn.ts @@ -5,7 +5,7 @@ import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs"; * Special type of column that is available only for MongoDB database. * Marks your entity's column to be an object id. */ -export function ObjectIdColumn(options?: ColumnOptions): Function { +export function ObjectIdColumn(options?: ColumnOptions): PropertyDecorator { return function (object: Object, propertyName: string) { // if column options are not given then create a new empty options diff --git a/src/decorator/columns/PrimaryColumn.ts b/src/decorator/columns/PrimaryColumn.ts index 9c56828bf6c..e84b8d12bac 100644 --- a/src/decorator/columns/PrimaryColumn.ts +++ b/src/decorator/columns/PrimaryColumn.ts @@ -2,28 +2,28 @@ import {ColumnOptions, ColumnType, getMetadataArgsStorage} from "../../"; import {ColumnTypeUndefinedError} from "../../error/ColumnTypeUndefinedError"; import {PrimaryColumnCannotBeNullableError} from "../../error/PrimaryColumnCannotBeNullableError"; import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs"; -import { GeneratedMetadataArgs } from "../../metadata-args/GeneratedMetadataArgs"; +import {GeneratedMetadataArgs} from "../../metadata-args/GeneratedMetadataArgs"; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. * Primary columns also creates a PRIMARY KEY for this column in a db. */ -export function PrimaryColumn(options?: ColumnOptions): Function; +export function PrimaryColumn(options?: ColumnOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. * Primary columns also creates a PRIMARY KEY for this column in a db. */ -export function PrimaryColumn(type?: ColumnType, options?: ColumnOptions): Function; +export function PrimaryColumn(type?: ColumnType, options?: ColumnOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. * Only properties decorated with this decorator will be persisted to the database when entity be saved. * Primary columns also creates a PRIMARY KEY for this column in a db. */ -export function PrimaryColumn(typeOrOptions?: ColumnType|ColumnOptions, options?: ColumnOptions): Function { +export function PrimaryColumn(typeOrOptions?: ColumnType|ColumnOptions, options?: ColumnOptions): PropertyDecorator { return function (object: Object, propertyName: string) { // normalize parameters diff --git a/src/decorator/columns/PrimaryGeneratedColumn.ts b/src/decorator/columns/PrimaryGeneratedColumn.ts index afa6c81336e..14fbffa55a1 100644 --- a/src/decorator/columns/PrimaryGeneratedColumn.ts +++ b/src/decorator/columns/PrimaryGeneratedColumn.ts @@ -6,27 +6,27 @@ import {GeneratedMetadataArgs} from "../../metadata-args/GeneratedMetadataArgs"; /** * Column decorator is used to mark a specific class property as a table column. */ -export function PrimaryGeneratedColumn(): Function; +export function PrimaryGeneratedColumn(): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. */ -export function PrimaryGeneratedColumn(options: PrimaryGeneratedColumnNumericOptions): Function; +export function PrimaryGeneratedColumn(options: PrimaryGeneratedColumnNumericOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. */ -export function PrimaryGeneratedColumn(strategy: "increment", options?: PrimaryGeneratedColumnNumericOptions): Function; +export function PrimaryGeneratedColumn(strategy: "increment", options?: PrimaryGeneratedColumnNumericOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. */ -export function PrimaryGeneratedColumn(strategy: "uuid", options?: PrimaryGeneratedColumnUUIDOptions): Function; +export function PrimaryGeneratedColumn(strategy: "uuid", options?: PrimaryGeneratedColumnUUIDOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. */ -export function PrimaryGeneratedColumn(strategy: "rowid", options?: PrimaryGeneratedColumnUUIDOptions): Function; +export function PrimaryGeneratedColumn(strategy: "rowid", options?: PrimaryGeneratedColumnUUIDOptions): PropertyDecorator; /** * Column decorator is used to mark a specific class property as a table column. @@ -34,7 +34,7 @@ export function PrimaryGeneratedColumn(strategy: "rowid", options?: PrimaryGener * This column creates an integer PRIMARY COLUMN with generated set to true. */ export function PrimaryGeneratedColumn(strategyOrOptions?: "increment"|"uuid"|"rowid"|PrimaryGeneratedColumnNumericOptions|PrimaryGeneratedColumnUUIDOptions, - maybeOptions?: PrimaryGeneratedColumnNumericOptions|PrimaryGeneratedColumnUUIDOptions): Function { + maybeOptions?: PrimaryGeneratedColumnNumericOptions|PrimaryGeneratedColumnUUIDOptions): PropertyDecorator { // normalize parameters const options: ColumnOptions = {}; diff --git a/src/decorator/columns/UpdateDateColumn.ts b/src/decorator/columns/UpdateDateColumn.ts index f4895e76b42..f4627a0df4c 100644 --- a/src/decorator/columns/UpdateDateColumn.ts +++ b/src/decorator/columns/UpdateDateColumn.ts @@ -5,7 +5,7 @@ import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs"; * This column will store an update date of the updated object. * This date is being updated each time you persist the object. */ -export function UpdateDateColumn(options?: ColumnOptions): Function { +export function UpdateDateColumn(options?: ColumnOptions): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().columns.push({ diff --git a/src/decorator/columns/VersionColumn.ts b/src/decorator/columns/VersionColumn.ts index f1f14984a3b..17798697836 100644 --- a/src/decorator/columns/VersionColumn.ts +++ b/src/decorator/columns/VersionColumn.ts @@ -6,7 +6,7 @@ import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs"; * Every time your entity will be persisted, this number will be increased by one - * so you can organize visioning and update strategies of your entity. */ -export function VersionColumn(options?: ColumnOptions): Function { +export function VersionColumn(options?: ColumnOptions): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().columns.push({ diff --git a/src/decorator/columns/ViewColumn.ts b/src/decorator/columns/ViewColumn.ts index e3df7bcd4d1..11356f66e3e 100644 --- a/src/decorator/columns/ViewColumn.ts +++ b/src/decorator/columns/ViewColumn.ts @@ -5,7 +5,7 @@ import { ViewColumnOptions } from "../options/ViewColumnOptions"; /** * ViewColumn decorator is used to mark a specific class property as a view column. */ -export function ViewColumn(options?: ViewColumnOptions): Function { +export function ViewColumn(options?: ViewColumnOptions): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().columns.push({ target: object.constructor, diff --git a/src/decorator/entity-view/ViewEntity.ts b/src/decorator/entity-view/ViewEntity.ts index 50cb0f96a43..7223ed8ce58 100644 --- a/src/decorator/entity-view/ViewEntity.ts +++ b/src/decorator/entity-view/ViewEntity.ts @@ -6,19 +6,19 @@ import {ViewEntityOptions} from "../options/ViewEntityOptions"; * This decorator is used to mark classes that will be an entity view. * Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it. */ -export function ViewEntity(options?: ViewEntityOptions): Function; +export function ViewEntity(options?: ViewEntityOptions): ClassDecorator; /** * This decorator is used to mark classes that will be an entity view. * Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it. */ -export function ViewEntity(name?: string, options?: ViewEntityOptions): Function; +export function ViewEntity(name?: string, options?: ViewEntityOptions): ClassDecorator; /** * This decorator is used to mark classes that will be an entity view. * Database schema will be created for all classes decorated with it, and Repository can be retrieved and used for it. */ -export function ViewEntity(nameOrOptions?: string|ViewEntityOptions, maybeOptions?: ViewEntityOptions): Function { +export function ViewEntity(nameOrOptions?: string|ViewEntityOptions, maybeOptions?: ViewEntityOptions): ClassDecorator { const options = (typeof nameOrOptions === "object" ? nameOrOptions as ViewEntityOptions : maybeOptions) || {}; const name = typeof nameOrOptions === "string" ? nameOrOptions : options.name; diff --git a/src/decorator/entity/ChildEntity.ts b/src/decorator/entity/ChildEntity.ts index 644b7d5babb..2405e4305b0 100644 --- a/src/decorator/entity/ChildEntity.ts +++ b/src/decorator/entity/ChildEntity.ts @@ -5,7 +5,7 @@ import {DiscriminatorValueMetadataArgs} from "../../metadata-args/DiscriminatorV /** * Special type of the table used in the single-table inherited tables. */ -export function ChildEntity(discriminatorValue?: any) { +export function ChildEntity(discriminatorValue?: any): ClassDecorator { return function (target: Function) { // register a table metadata @@ -15,7 +15,7 @@ export function ChildEntity(discriminatorValue?: any) { } as TableMetadataArgs); // register discriminator value if it was provided - if (discriminatorValue) { + if (typeof discriminatorValue !== 'undefined') { getMetadataArgsStorage().discriminatorValues.push({ target: target, value: discriminatorValue diff --git a/src/decorator/entity/TableInheritance.ts b/src/decorator/entity/TableInheritance.ts index 264743713e5..f716b5c227b 100644 --- a/src/decorator/entity/TableInheritance.ts +++ b/src/decorator/entity/TableInheritance.ts @@ -4,7 +4,7 @@ import {InheritanceMetadataArgs} from "../../metadata-args/InheritanceMetadataAr /** * Sets for entity to use table inheritance pattern. */ -export function TableInheritance(options?: { pattern?: "STI"/*|"CTI"*/, column?: string|ColumnOptions }) { +export function TableInheritance(options?: { pattern?: "STI"/*|"CTI"*/, column?: string|ColumnOptions }): ClassDecorator { return function (target: Function) { getMetadataArgsStorage().inheritances.push({ diff --git a/src/decorator/listeners/AfterInsert.ts b/src/decorator/listeners/AfterInsert.ts index 68c2c1a11c2..48be17d67d1 100644 --- a/src/decorator/listeners/AfterInsert.ts +++ b/src/decorator/listeners/AfterInsert.ts @@ -5,7 +5,7 @@ import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMeta /** * Calls a method on which this decorator is applied after this entity insertion. */ -export function AfterInsert() { +export function AfterInsert(): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().entityListeners.push({ diff --git a/src/decorator/listeners/AfterLoad.ts b/src/decorator/listeners/AfterLoad.ts index 8b810feed79..02e2fe81c67 100644 --- a/src/decorator/listeners/AfterLoad.ts +++ b/src/decorator/listeners/AfterLoad.ts @@ -5,7 +5,7 @@ import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMeta /** * Calls a method on which this decorator is applied after entity is loaded. */ -export function AfterLoad() { +export function AfterLoad(): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().entityListeners.push({ diff --git a/src/decorator/listeners/AfterRemove.ts b/src/decorator/listeners/AfterRemove.ts index e9e7d2549cd..a2f00f63f4d 100644 --- a/src/decorator/listeners/AfterRemove.ts +++ b/src/decorator/listeners/AfterRemove.ts @@ -5,7 +5,7 @@ import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMeta /** * Calls a method on which this decorator is applied after this entity removal. */ -export function AfterRemove() { +export function AfterRemove(): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().entityListeners.push({ diff --git a/src/decorator/listeners/AfterUpdate.ts b/src/decorator/listeners/AfterUpdate.ts index fadb31815f6..f77241e0954 100644 --- a/src/decorator/listeners/AfterUpdate.ts +++ b/src/decorator/listeners/AfterUpdate.ts @@ -5,7 +5,7 @@ import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMeta /** * Calls a method on which this decorator is applied after this entity update. */ -export function AfterUpdate() { +export function AfterUpdate(): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().entityListeners.push({ diff --git a/src/decorator/listeners/BeforeInsert.ts b/src/decorator/listeners/BeforeInsert.ts index 10db63ad141..6f2d4c7d98a 100644 --- a/src/decorator/listeners/BeforeInsert.ts +++ b/src/decorator/listeners/BeforeInsert.ts @@ -5,7 +5,7 @@ import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMeta /** * Calls a method on which this decorator is applied before this entity insertion. */ -export function BeforeInsert() { +export function BeforeInsert(): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().entityListeners.push({ diff --git a/src/decorator/listeners/BeforeRemove.ts b/src/decorator/listeners/BeforeRemove.ts index e108e2e9f66..0fcadd1fa4e 100644 --- a/src/decorator/listeners/BeforeRemove.ts +++ b/src/decorator/listeners/BeforeRemove.ts @@ -5,7 +5,7 @@ import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMeta /** * Calls a method on which this decorator is applied before this entity removal. */ -export function BeforeRemove() { +export function BeforeRemove(): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().entityListeners.push({ diff --git a/src/decorator/listeners/BeforeUpdate.ts b/src/decorator/listeners/BeforeUpdate.ts index 753e3a15328..d4a15d36d2e 100644 --- a/src/decorator/listeners/BeforeUpdate.ts +++ b/src/decorator/listeners/BeforeUpdate.ts @@ -5,7 +5,7 @@ import {EntityListenerMetadataArgs} from "../../metadata-args/EntityListenerMeta /** * Calls a method on which this decorator is applied before this entity update. */ -export function BeforeUpdate() { +export function BeforeUpdate(): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().entityListeners.push({ diff --git a/src/decorator/listeners/EventSubscriber.ts b/src/decorator/listeners/EventSubscriber.ts index 99741d0b9ad..ea13dc44a8a 100644 --- a/src/decorator/listeners/EventSubscriber.ts +++ b/src/decorator/listeners/EventSubscriber.ts @@ -5,7 +5,7 @@ import {EntitySubscriberMetadataArgs} from "../../metadata-args/EntitySubscriber * Classes decorated with this decorator will listen to ORM events and their methods will be triggered when event * occurs. Those classes must implement EventSubscriberInterface interface. */ -export function EventSubscriber() { +export function EventSubscriber(): ClassDecorator { return function (target: Function) { getMetadataArgsStorage().entitySubscribers.push({ diff --git a/src/decorator/options/ColumnOptions.ts b/src/decorator/options/ColumnOptions.ts index 52a1afe75e7..931042c6ed5 100644 --- a/src/decorator/options/ColumnOptions.ts +++ b/src/decorator/options/ColumnOptions.ts @@ -1,6 +1,6 @@ import {ColumnType} from "../../driver/types/ColumnTypes"; import {ValueTransformer} from "./ValueTransformer"; -import { ColumnCommonOptions } from "./ColumnCommonOptions"; +import {ColumnCommonOptions} from "./ColumnCommonOptions"; /** * Describes all column's options. diff --git a/src/decorator/options/RelationOptions.ts b/src/decorator/options/RelationOptions.ts index 21183e29a8f..31911857116 100644 --- a/src/decorator/options/RelationOptions.ts +++ b/src/decorator/options/RelationOptions.ts @@ -64,4 +64,10 @@ export interface RelationOptions { * This is useful for performance optimization since its disabling avoid multiple extra queries during entity save. */ persistence?: boolean; + + /** + * When a child row is removed from its parent, determines if the child row should be orphaned (default) or deleted. + */ + orphanedRowAction?: "nullify" | "delete"; + } diff --git a/src/decorator/options/TransactionOptions.ts b/src/decorator/options/TransactionOptions.ts index 1570f4a2460..564c6d6ba26 100644 --- a/src/decorator/options/TransactionOptions.ts +++ b/src/decorator/options/TransactionOptions.ts @@ -1,6 +1,6 @@ -import { IsolationLevel } from "../../driver/types/IsolationLevel"; +import {IsolationLevel} from "../../driver/types/IsolationLevel"; export interface TransactionOptions { connectionName?: string; isolation?: IsolationLevel; -} \ No newline at end of file +} diff --git a/src/decorator/relations/JoinColumn.ts b/src/decorator/relations/JoinColumn.ts index 3bded3a0800..f619e374ad4 100644 --- a/src/decorator/relations/JoinColumn.ts +++ b/src/decorator/relations/JoinColumn.ts @@ -6,28 +6,28 @@ import {JoinColumnMetadataArgs} from "../../metadata-args/JoinColumnMetadataArgs * It also can be used on both one-to-one and many-to-one relations to specify custom column name * or custom referenced column. */ -export function JoinColumn(): Function; +export function JoinColumn(): PropertyDecorator; /** * JoinColumn decorator used on one-to-one relations to specify owner side of relationship. * It also can be used on both one-to-one and many-to-one relations to specify custom column name * or custom referenced column. */ -export function JoinColumn(options: JoinColumnOptions): Function; +export function JoinColumn(options: JoinColumnOptions): PropertyDecorator; /** * JoinColumn decorator used on one-to-one relations to specify owner side of relationship. * It also can be used on both one-to-one and many-to-one relations to specify custom column name * or custom referenced column. */ -export function JoinColumn(options: JoinColumnOptions[]): Function; +export function JoinColumn(options: JoinColumnOptions[]): PropertyDecorator; /** * JoinColumn decorator used on one-to-one relations to specify owner side of relationship. * It also can be used on both one-to-one and many-to-one relations to specify custom column name * or custom referenced column. */ -export function JoinColumn(optionsOrOptionsArray?: JoinColumnOptions|JoinColumnOptions[]): Function { +export function JoinColumn(optionsOrOptionsArray?: JoinColumnOptions|JoinColumnOptions[]): PropertyDecorator { return function (object: Object, propertyName: string) { const options = Array.isArray(optionsOrOptionsArray) ? optionsOrOptionsArray : [optionsOrOptionsArray || {}]; options.forEach(options => { diff --git a/src/decorator/relations/JoinTable.ts b/src/decorator/relations/JoinTable.ts index ed7ddab587f..c9d95b09095 100644 --- a/src/decorator/relations/JoinTable.ts +++ b/src/decorator/relations/JoinTable.ts @@ -6,25 +6,25 @@ import {JoinTableMultipleColumnsOptions} from "../options/JoinTableMultipleColum * JoinTable decorator is used in many-to-many relationship to specify owner side of relationship. * Its also used to set a custom junction table's name, column names and referenced columns. */ -export function JoinTable(): Function; +export function JoinTable(): PropertyDecorator; /** * JoinTable decorator is used in many-to-many relationship to specify owner side of relationship. * Its also used to set a custom junction table's name, column names and referenced columns. */ -export function JoinTable(options: JoinTableOptions): Function; +export function JoinTable(options: JoinTableOptions): PropertyDecorator; /** * JoinTable decorator is used in many-to-many relationship to specify owner side of relationship. * Its also used to set a custom junction table's name, column names and referenced columns. */ -export function JoinTable(options: JoinTableMultipleColumnsOptions): Function; +export function JoinTable(options: JoinTableMultipleColumnsOptions): PropertyDecorator; /** * JoinTable decorator is used in many-to-many relationship to specify owner side of relationship. * Its also used to set a custom junction table's name, column names and referenced columns. */ -export function JoinTable(options?: JoinTableOptions|JoinTableMultipleColumnsOptions): Function { +export function JoinTable(options?: JoinTableOptions|JoinTableMultipleColumnsOptions): PropertyDecorator { return function (object: Object, propertyName: string) { options = options || {} as JoinTableOptions|JoinTableMultipleColumnsOptions; getMetadataArgsStorage().joinTables.push({ diff --git a/src/decorator/relations/ManyToMany.ts b/src/decorator/relations/ManyToMany.ts index f287b434f59..e07a124f04a 100644 --- a/src/decorator/relations/ManyToMany.ts +++ b/src/decorator/relations/ManyToMany.ts @@ -7,7 +7,7 @@ import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs"; * entity1 and entity2 ids. This is owner side of the relationship. */ export function ManyToMany(typeFunctionOrTarget: string|((type?: any) => ObjectType), - options?: RelationOptions): Function; + options?: RelationOptions): PropertyDecorator; /** * Many-to-many is a type of relationship when Entity1 can have multiple instances of Entity2, and Entity2 can have @@ -16,7 +16,7 @@ export function ManyToMany(typeFunctionOrTarget: string|((type?: any) => Obje */ export function ManyToMany(typeFunctionOrTarget: string|((type?: any) => ObjectType), inverseSide?: string|((object: T) => any), - options?: RelationOptions): Function; + options?: RelationOptions): PropertyDecorator; /** * Many-to-many is a type of relationship when Entity1 can have multiple instances of Entity2, and Entity2 can have @@ -25,7 +25,7 @@ export function ManyToMany(typeFunctionOrTarget: string|((type?: any) => Obje */ export function ManyToMany(typeFunctionOrTarget: string|((type?: any) => ObjectType), inverseSideOrOptions?: string|((object: T) => any)|RelationOptions, - options?: RelationOptions): Function { + options?: RelationOptions): PropertyDecorator { // normalize parameters let inverseSideProperty: string|((object: T) => any); diff --git a/src/decorator/relations/ManyToOne.ts b/src/decorator/relations/ManyToOne.ts index 7c0031e6c09..0c60ba0bf6a 100644 --- a/src/decorator/relations/ManyToOne.ts +++ b/src/decorator/relations/ManyToOne.ts @@ -6,7 +6,7 @@ import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs"; * Entity2 can have a multiple instances of Entity1. Entity1 is an owner of the relationship, and storages Entity2 id * on its own side. */ -export function ManyToOne(typeFunctionOrTarget: string|((type?: any) => ObjectType), options?: RelationOptions): Function; +export function ManyToOne(typeFunctionOrTarget: string|((type?: any) => ObjectType), options?: RelationOptions): PropertyDecorator; /** * Many-to-one relation allows to create type of relation when Entity1 can have single instance of Entity2, but @@ -15,7 +15,7 @@ export function ManyToOne(typeFunctionOrTarget: string|((type?: any) => Objec */ export function ManyToOne(typeFunctionOrTarget: string|((type?: any) => ObjectType), inverseSide?: string|((object: T) => any), - options?: RelationOptions): Function; + options?: RelationOptions): PropertyDecorator; /** * Many-to-one relation allows to create type of relation when Entity1 can have single instance of Entity2, but @@ -24,7 +24,7 @@ export function ManyToOne(typeFunctionOrTarget: string|((type?: any) => Objec */ export function ManyToOne(typeFunctionOrTarget: string|((type?: any) => ObjectType), inverseSideOrOptions?: string|((object: T) => any)|RelationOptions, - options?: RelationOptions): Function { + options?: RelationOptions): PropertyDecorator { // normalize parameters let inverseSideProperty: string|((object: T) => any); diff --git a/src/decorator/relations/OneToMany.ts b/src/decorator/relations/OneToMany.ts index 80e1f7175cf..ae32f1cf3b4 100644 --- a/src/decorator/relations/OneToMany.ts +++ b/src/decorator/relations/OneToMany.ts @@ -2,10 +2,10 @@ import {getMetadataArgsStorage, ObjectType, RelationOptions} from "../../"; import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs"; /** - * One-to-many relation allows to create type of relation when Entity2 can have multiple instances of Entity1. - * Entity1 have only one Entity2. Entity1 is an owner of the relationship, and storages Entity2 id on its own side. + * One-to-many relation allows us to create a type of relation where Entity1 can have multiple instances of Entity2. + * Entity2 has only one Entity1. Entity2 is the owner of the relationship and stores Entity1's id on its own side. */ -export function OneToMany(typeFunctionOrTarget: string|((type?: any) => ObjectType), inverseSide: string|((object: T) => any), options?: RelationOptions): Function { +export function OneToMany(typeFunctionOrTarget: string|((type?: any) => ObjectType), inverseSide: string|((object: T) => any), options?: RelationOptions): PropertyDecorator { return function (object: Object, propertyName: string) { if (!options) options = {} as RelationOptions; diff --git a/src/decorator/relations/OneToOne.ts b/src/decorator/relations/OneToOne.ts index 85f196b2270..e1d8c04982d 100644 --- a/src/decorator/relations/OneToOne.ts +++ b/src/decorator/relations/OneToOne.ts @@ -6,7 +6,7 @@ import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs"; * Entity1 is an owner of the relationship, and storages Entity1 id on its own side. */ export function OneToOne(typeFunctionOrTarget: string|((type?: any) => ObjectType), - options?: RelationOptions): Function; + options?: RelationOptions): PropertyDecorator; /** * One-to-one relation allows to create direct relation between two entities. Entity1 have only one Entity2. @@ -14,7 +14,7 @@ export function OneToOne(typeFunctionOrTarget: string|((type?: any) => Object */ export function OneToOne(typeFunctionOrTarget: string|((type?: any) => ObjectType), inverseSide?: string|((object: T) => any), - options?: RelationOptions): Function; + options?: RelationOptions): PropertyDecorator; /** * One-to-one relation allows to create direct relation between two entities. Entity1 have only one Entity2. @@ -22,7 +22,7 @@ export function OneToOne(typeFunctionOrTarget: string|((type?: any) => Object */ export function OneToOne(typeFunctionOrTarget: string|((type?: any) => ObjectType), inverseSideOrOptions?: string|((object: T) => any)|RelationOptions, - options?: RelationOptions): Function { + options?: RelationOptions): PropertyDecorator { // normalize parameters let inverseSideProperty: string|((object: T) => any); diff --git a/src/decorator/relations/RelationCount.ts b/src/decorator/relations/RelationCount.ts index abfb76c2e46..9670c5c8953 100644 --- a/src/decorator/relations/RelationCount.ts +++ b/src/decorator/relations/RelationCount.ts @@ -6,7 +6,7 @@ import {RelationCountMetadataArgs} from "../../metadata-args/RelationCountMetada * * @deprecated Do not use this decorator, it may be removed in the future versions */ -export function RelationCount(relation: string|((object: T) => any), alias?: string, queryBuilderFactory?: (qb: SelectQueryBuilder) => SelectQueryBuilder): Function { +export function RelationCount(relation: string|((object: T) => any), alias?: string, queryBuilderFactory?: (qb: SelectQueryBuilder) => SelectQueryBuilder): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().relationCounts.push({ diff --git a/src/decorator/relations/RelationId.ts b/src/decorator/relations/RelationId.ts index c7880e17a69..78855aeb4f0 100644 --- a/src/decorator/relations/RelationId.ts +++ b/src/decorator/relations/RelationId.ts @@ -6,7 +6,7 @@ import {RelationIdMetadataArgs} from "../../metadata-args/RelationIdMetadataArgs * * @experimental */ -export function RelationId(relation: string|((object: T) => any), alias?: string, queryBuilderFactory?: (qb: SelectQueryBuilder) => SelectQueryBuilder): Function { +export function RelationId(relation: string|((object: T) => any), alias?: string, queryBuilderFactory?: (qb: SelectQueryBuilder) => SelectQueryBuilder): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().relationIds.push({ diff --git a/src/decorator/transaction/TransactionManager.ts b/src/decorator/transaction/TransactionManager.ts index 31addbcc769..083dd8d42d8 100644 --- a/src/decorator/transaction/TransactionManager.ts +++ b/src/decorator/transaction/TransactionManager.ts @@ -4,7 +4,7 @@ import {TransactionEntityMetadataArgs} from "../../metadata-args/TransactionEnti /** * Injects transaction's entity manager into the method wrapped with @Transaction decorator. */ -export function TransactionManager(): Function { +export function TransactionManager(): ParameterDecorator { return function (object: Object, methodName: string, index: number) { getMetadataArgsStorage().transactionEntityManagers.push({ diff --git a/src/decorator/tree/Tree.ts b/src/decorator/tree/Tree.ts index e93ab0be9cb..8e39a04cfd7 100644 --- a/src/decorator/tree/Tree.ts +++ b/src/decorator/tree/Tree.ts @@ -1,6 +1,7 @@ import {getMetadataArgsStorage} from "../../"; import {TreeMetadataArgs} from "../../metadata-args/TreeMetadataArgs"; import {TreeType} from "../../metadata/types/TreeTypes"; +import {ClosureTreeOptions} from "../../metadata/types/ClosureTreeOptions"; /** * Marks entity to work like a tree. @@ -8,12 +9,13 @@ import {TreeType} from "../../metadata/types/TreeTypes"; * @TreeParent decorator must be used in tree entities. * TreeRepository can be used to manipulate with tree entities. */ -export function Tree(type: TreeType): Function { +export function Tree(type: TreeType, options?: ClosureTreeOptions): ClassDecorator { return function (target: Function) { getMetadataArgsStorage().trees.push({ target: target, - type: type + type: type, + options: type === "closure-table" ? options : undefined } as TreeMetadataArgs); }; } diff --git a/src/decorator/tree/TreeChildren.ts b/src/decorator/tree/TreeChildren.ts index 70de9e59956..2ef0935e0a2 100644 --- a/src/decorator/tree/TreeChildren.ts +++ b/src/decorator/tree/TreeChildren.ts @@ -5,7 +5,7 @@ import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs"; * Marks a entity property as a children of the tree. * "Tree children" will contain all children (bind) of this entity. */ -export function TreeChildren(options?: { cascade?: boolean|("insert"|"update"|"remove"|"soft-remove"|"recover")[] }): Function { +export function TreeChildren(options?: { cascade?: boolean|("insert"|"update"|"remove"|"soft-remove"|"recover")[] }): PropertyDecorator { return function (object: Object, propertyName: string) { if (!options) options = {} as RelationOptions; diff --git a/src/decorator/tree/TreeLevelColumn.ts b/src/decorator/tree/TreeLevelColumn.ts index f34a4173edd..24d9b5f2b16 100644 --- a/src/decorator/tree/TreeLevelColumn.ts +++ b/src/decorator/tree/TreeLevelColumn.ts @@ -4,7 +4,7 @@ import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs"; /** * Creates a "level"/"length" column to the table that holds a closure table. */ -export function TreeLevelColumn(): Function { +export function TreeLevelColumn(): PropertyDecorator { return function (object: Object, propertyName: string) { getMetadataArgsStorage().columns.push({ diff --git a/src/decorator/tree/TreeParent.ts b/src/decorator/tree/TreeParent.ts index 4b0d38b9ef8..931b78206e1 100644 --- a/src/decorator/tree/TreeParent.ts +++ b/src/decorator/tree/TreeParent.ts @@ -5,7 +5,7 @@ import {RelationMetadataArgs} from "../../metadata-args/RelationMetadataArgs"; * Marks a entity property as a parent of the tree. * "Tree parent" indicates who owns (is a parent) of this entity in tree structure. */ -export function TreeParent(): Function { +export function TreeParent(): PropertyDecorator { return function (object: Object, propertyName: string) { // now try to determine it its lazy relation diff --git a/src/driver/Driver.ts b/src/driver/Driver.ts index 4041f586932..61a19ed16d1 100644 --- a/src/driver/Driver.ts +++ b/src/driver/Driver.ts @@ -8,6 +8,7 @@ import {DataTypeDefaults} from "./types/DataTypeDefaults"; import {BaseConnectionOptions} from "../connection/BaseConnectionOptions"; import {TableColumn} from "../schema-builder/table/TableColumn"; import {EntityMetadata} from "../metadata/EntityMetadata"; +import {ReplicationMode} from "./types/ReplicationMode"; /** * Driver organizes TypeORM communication with specific database management system. @@ -102,7 +103,7 @@ export interface Driver { /** * Creates a query runner used for common queries. */ - createQueryRunner(mode: "master"|"slave"): QueryRunner; + createQueryRunner(mode: ReplicationMode): QueryRunner; /** * Replaces parameters in the given sql with special escaping character @@ -175,7 +176,7 @@ export interface Driver { /** * Creates generated map of values generated or returned by database after INSERT query. */ - createGeneratedMap(metadata: EntityMetadata, insertResult: any): ObjectLiteral|undefined; + createGeneratedMap(metadata: EntityMetadata, insertResult: any, entityIndex?: number, entityNum?: number): ObjectLiteral|undefined; /** * Differentiate columns of this table and columns from the given column metadatas columns @@ -193,6 +194,11 @@ export interface Driver { */ isUUIDGenerationSupported(): boolean; + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean; + /** * Creates an escaped parameter. */ diff --git a/src/driver/DriverFactory.ts b/src/driver/DriverFactory.ts index 80f58df4efc..df4955a69bc 100644 --- a/src/driver/DriverFactory.ts +++ b/src/driver/DriverFactory.ts @@ -12,10 +12,11 @@ import {MysqlDriver} from "./mysql/MysqlDriver"; import {PostgresDriver} from "./postgres/PostgresDriver"; import {ExpoDriver} from "./expo/ExpoDriver"; import {AuroraDataApiDriver} from "./aurora-data-api/AuroraDataApiDriver"; +import {AuroraDataApiPostgresDriver} from "./aurora-data-api-pg/AuroraDataApiPostgresDriver"; import {Driver} from "./Driver"; import {Connection} from "../connection/Connection"; import {SapDriver} from "./sap/SapDriver"; -import {AuroraDataApiPostgresDriver} from "./postgres/PostgresDriver"; +import {BetterSqlite3Driver} from "./better-sqlite3/BetterSqlite3Driver"; /** * Helps to create drivers. @@ -40,6 +41,8 @@ export class DriverFactory { return new MysqlDriver(connection); case "sqlite": return new SqliteDriver(connection); + case "better-sqlite3": + return new BetterSqlite3Driver(connection); case "cordova": return new CordovaDriver(connection); case "nativescript": diff --git a/src/driver/DriverUtils.ts b/src/driver/DriverUtils.ts index b410b84a3f9..162c882dc8a 100644 --- a/src/driver/DriverUtils.ts +++ b/src/driver/DriverUtils.ts @@ -1,7 +1,7 @@ -import { Driver } from "./Driver"; +import {Driver} from "./Driver"; import { hash } from "../util/StringUtils"; - /** +/** * Common driver utility functions. */ export class DriverUtils { @@ -16,18 +16,18 @@ export class DriverUtils { */ static buildDriverOptions(options: any, buildOptions?: { useSid: boolean }): any { if (options.url) { - const parsedUrl = this.parseConnectionUrl(options.url); - let urlDriverOptions: any = { - type: parsedUrl.type, - host: parsedUrl.host, - username: parsedUrl.username, - password: parsedUrl.password, - port: parsedUrl.port, - database: parsedUrl.database - }; - if (buildOptions && buildOptions.useSid) { - urlDriverOptions.sid = parsedUrl.database; + const urlDriverOptions = this.parseConnectionUrl(options.url) as { [key: string]: any }; + + if (buildOptions && buildOptions.useSid && urlDriverOptions.database) { + urlDriverOptions.sid = urlDriverOptions.database; + } + + for (const key of Object.keys(urlDriverOptions)) { + if (typeof urlDriverOptions[key] === "undefined") { + delete urlDriverOptions[key]; + } } + return Object.assign({}, options, urlDriverOptions); } return Object.assign({}, options); @@ -35,7 +35,7 @@ export class DriverUtils { /** * Builds column alias from given alias name and column name. - * + * * If alias length is greater than the limit (if any) allowed by the current * driver, replaces it with a hashed string. * @@ -68,7 +68,11 @@ export class DriverUtils { const preBase = url.substr(firstSlashes + 2); const secondSlash = preBase.indexOf("/"); const base = (secondSlash !== -1) ? preBase.substr(0, secondSlash) : preBase; - const afterBase = (secondSlash !== -1) ? preBase.substr(secondSlash + 1) : undefined; + let afterBase = (secondSlash !== -1) ? preBase.substr(secondSlash + 1) : undefined; + // remove mongodb query params + if (afterBase && afterBase.indexOf("?") !== -1) { + afterBase = afterBase.substr(0, afterBase.indexOf("?")); + } const lastAtSign = base.lastIndexOf("@"); const usernameAndPassword = base.substr(0, lastAtSign); diff --git a/src/driver/aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions.ts b/src/driver/aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions.ts index c0f110f2ca5..18078aed828 100644 --- a/src/driver/aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions.ts +++ b/src/driver/aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions.ts @@ -31,4 +31,8 @@ export interface AuroraDataApiPostgresConnectionOptions extends BaseConnectionOp * Defaults to logging error with `warn` level. */ readonly poolErrorHandler?: (err: any) => any; + + readonly serviceConfigOptions?: { [key: string]: any }; + + readonly formatOptions?: { [key: string]: any }; } diff --git a/src/driver/aurora-data-api-pg/AuroraDataApiPostgresDriver.ts b/src/driver/aurora-data-api-pg/AuroraDataApiPostgresDriver.ts new file mode 100644 index 00000000000..be827d60602 --- /dev/null +++ b/src/driver/aurora-data-api-pg/AuroraDataApiPostgresDriver.ts @@ -0,0 +1,128 @@ +import {Driver} from "../Driver"; +import {PostgresDriver} from "../postgres/PostgresDriver"; +import {PlatformTools} from "../../platform/PlatformTools"; +import {Connection} from "../../connection/Connection"; +import {AuroraDataApiPostgresConnectionOptions} from "../aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions"; +import {AuroraDataApiPostgresQueryRunner} from "../aurora-data-api-pg/AuroraDataApiPostgresQueryRunner"; +import {ReplicationMode} from "../types/ReplicationMode"; + +abstract class PostgresWrapper extends PostgresDriver { + options: any; + + abstract createQueryRunner(mode: ReplicationMode): any; +} + +export class AuroraDataApiPostgresDriver extends PostgresWrapper implements Driver { + + // ------------------------------------------------------------------------- + // Public Properties + // ------------------------------------------------------------------------- + + /** + * Connection used by driver. + */ + connection: Connection; + + /** + * Aurora Data API underlying library. + */ + DataApiDriver: any; + + // ------------------------------------------------------------------------- + // Public Implemented Properties + // ------------------------------------------------------------------------- + + /** + * Connection options. + */ + options: AuroraDataApiPostgresConnectionOptions; + + /** + * Master database used to perform all write queries. + */ + database?: string; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(connection: Connection) { + super(); + this.connection = connection; + this.options = connection.options as AuroraDataApiPostgresConnectionOptions; + this.isReplicated = false; + + // load data-api package + this.loadDependencies(); + } + + // ------------------------------------------------------------------------- + // Public Implemented Methods + // ------------------------------------------------------------------------- + + /** + * Performs connection to the database. + * Based on pooling options, it can either create connection immediately, + * either create a pool and create connection when needed. + */ + async connect(): Promise { + } + + /** + * Closes connection with database. + */ + async disconnect(): Promise { + } + + /** + * Creates a query runner used to execute database queries. + */ + createQueryRunner(mode: ReplicationMode) { + return new AuroraDataApiPostgresQueryRunner( + this, + new this.DataApiDriver( + this.options.region, + this.options.secretArn, + this.options.resourceArn, + this.options.database, + (query: string, parameters?: any[]) => this.connection.logger.logQuery(query, parameters), + this.options.serviceConfigOptions, + this.options.formatOptions, + ), + mode + ); + } + + // ------------------------------------------------------------------------- + // Protected Methods + // ------------------------------------------------------------------------- + + /** + * If driver dependency is not given explicitly, then try to load it via "require". + */ + protected loadDependencies(): void { + const { pg } = PlatformTools.load("typeorm-aurora-data-api-driver"); + + this.DataApiDriver = pg; + } + + /** + * Executes given query. + */ + protected executeQuery(connection: any, query: string) { + return this.connection.query(query); + } + + /** + * Makes any action after connection (e.g. create extensions in Postgres driver). + */ + async afterConnect(): Promise { + const extensionsMetadata = await this.checkMetadataForExtensions(); + + if (extensionsMetadata.hasExtensions) { + await this.enableExtensions(extensionsMetadata, this.connection); + } + + return Promise.resolve(); + } +} diff --git a/src/driver/aurora-data-api-pg/AuroraDataApiPostgresQueryRunner.ts b/src/driver/aurora-data-api-pg/AuroraDataApiPostgresQueryRunner.ts index 2a553c134c1..f6950a4ef28 100644 --- a/src/driver/aurora-data-api-pg/AuroraDataApiPostgresQueryRunner.ts +++ b/src/driver/aurora-data-api-pg/AuroraDataApiPostgresQueryRunner.ts @@ -3,13 +3,15 @@ import {TransactionAlreadyStartedError} from "../../error/TransactionAlreadyStar import {TransactionNotStartedError} from "../../error/TransactionNotStartedError"; import {QueryRunner} from "../../query-runner/QueryRunner"; import {IsolationLevel} from "../types/IsolationLevel"; -import {AuroraDataApiPostgresDriver} from "../postgres/PostgresDriver"; +import {AuroraDataApiPostgresDriver} from "./AuroraDataApiPostgresDriver"; import {PostgresQueryRunner} from "../postgres/PostgresQueryRunner"; +import {ReplicationMode} from "../types/ReplicationMode"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; class PostgresQueryRunnerWrapper extends PostgresQueryRunner { driver: any; - constructor(driver: any, mode: "master"|"slave") { + constructor(driver: any, mode: ReplicationMode) { super(driver, mode); } } @@ -28,6 +30,8 @@ export class AuroraDataApiPostgresQueryRunner extends PostgresQueryRunnerWrapper */ driver: AuroraDataApiPostgresDriver; + protected client: any; + // ------------------------------------------------------------------------- // Protected Properties // ------------------------------------------------------------------------- @@ -46,8 +50,10 @@ export class AuroraDataApiPostgresQueryRunner extends PostgresQueryRunnerWrapper // Constructor // ------------------------------------------------------------------------- - constructor(driver: AuroraDataApiPostgresDriver, mode: "master"|"slave" = "master") { + constructor(driver: AuroraDataApiPostgresDriver, client: any, mode: ReplicationMode) { super(driver, mode); + + this.client = client } // ------------------------------------------------------------------------- @@ -92,8 +98,17 @@ export class AuroraDataApiPostgresQueryRunner extends PostgresQueryRunnerWrapper if (this.isTransactionActive) throw new TransactionAlreadyStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = true; - await this.driver.client.startTransaction(); + + await this.client.startTransaction(); + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -103,9 +118,18 @@ export class AuroraDataApiPostgresQueryRunner extends PostgresQueryRunnerWrapper async commitTransaction(): Promise { if (!this.isTransactionActive) throw new TransactionNotStartedError(); + + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + + await this.client.commitTransaction(); - await this.driver.client.commitTransaction(); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -116,8 +140,15 @@ export class AuroraDataApiPostgresQueryRunner extends PostgresQueryRunnerWrapper if (!this.isTransactionActive) throw new TransactionNotStartedError(); - await this.driver.client.rollbackTransaction(); - this.isTransactionActive = false; + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + + await this.client.rollbackTransaction(); + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -127,7 +158,7 @@ export class AuroraDataApiPostgresQueryRunner extends PostgresQueryRunnerWrapper if (this.isReleased) throw new QueryRunnerAlreadyReleasedError(); - const result = await this.driver.client.query(query, parameters); + const result = await this.client.query(query, parameters); if (result.records) { return result.records; diff --git a/src/driver/aurora-data-api/AuroraDataApiConnection.ts b/src/driver/aurora-data-api/AuroraDataApiConnection.ts index 386248809fb..e79bc30dc9e 100644 --- a/src/driver/aurora-data-api/AuroraDataApiConnection.ts +++ b/src/driver/aurora-data-api/AuroraDataApiConnection.ts @@ -1,6 +1,7 @@ import {AuroraDataApiQueryRunner} from "./AuroraDataApiQueryRunner"; import {Connection} from "../../connection/Connection"; import {ConnectionOptions, QueryRunner} from "../.."; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with MySQL DBMS. @@ -13,7 +14,7 @@ export class AuroraDataApiConnection extends Connection { this.queryRunnter = queryRunner; } - public createQueryRunner(mode: "master" | "slave" = "master"): QueryRunner { + public createQueryRunner(mode: ReplicationMode): QueryRunner { return this.queryRunnter; } diff --git a/src/driver/aurora-data-api/AuroraDataApiConnectionOptions.ts b/src/driver/aurora-data-api/AuroraDataApiConnectionOptions.ts index d9468d1e366..5e5722c2272 100644 --- a/src/driver/aurora-data-api/AuroraDataApiConnectionOptions.ts +++ b/src/driver/aurora-data-api/AuroraDataApiConnectionOptions.ts @@ -23,6 +23,8 @@ export interface AuroraDataApiConnectionOptions extends BaseConnectionOptions, A readonly serviceConfigOptions?: { [key: string]: any }; // pass optional AWS.ConfigurationOptions here + readonly formatOptions?: { [key: string]: any }; + /** * Use spatial functions like GeomFromText and AsText which are removed in MySQL 8. * (Default: true) diff --git a/src/driver/aurora-data-api/AuroraDataApiDriver.ts b/src/driver/aurora-data-api/AuroraDataApiDriver.ts index 89b93ccc039..bdbb14d7cd8 100644 --- a/src/driver/aurora-data-api/AuroraDataApiDriver.ts +++ b/src/driver/aurora-data-api/AuroraDataApiDriver.ts @@ -16,6 +16,7 @@ import {AuroraDataApiConnectionCredentialsOptions} from "./AuroraDataApiConnecti import {EntityMetadata} from "../../metadata/EntityMetadata"; import {OrmUtils} from "../../util/OrmUtils"; import {ApplyValueTransformers} from "../../util/ApplyValueTransformers"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with MySQL DBMS. @@ -32,8 +33,6 @@ export class AuroraDataApiDriver implements Driver { */ DataApiDriver: any; - client: any; - /** * Connection pool. * Used in non-replication mode. @@ -300,15 +299,6 @@ export class AuroraDataApiDriver implements Driver { // load mysql package this.loadDependencies(); - this.client = new this.DataApiDriver( - this.options.region, - this.options.secretArn, - this.options.resourceArn, - this.options.database, - (query: string, parameters?: any[]) => this.connection.logger.logQuery(query, parameters), - this.options.serviceConfigOptions - ); - // validate options to make sure everything is set // todo: revisit validation with replication in mind // if (!(this.options.host || (this.options.extra && this.options.extra.socketPath)) && !this.options.socketPath) @@ -354,8 +344,16 @@ export class AuroraDataApiDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master") { - return new AuroraDataApiQueryRunner(this); + createQueryRunner(mode: ReplicationMode) { + return new AuroraDataApiQueryRunner(this, new this.DataApiDriver( + this.options.region, + this.options.secretArn, + this.options.resourceArn, + this.options.database, + (query: string, parameters?: any[]) => this.connection.logger.logQuery(query, parameters), + this.options.serviceConfigOptions, + this.options.formatOptions, + )); } /** @@ -749,6 +747,13 @@ export class AuroraDataApiDriver implements Driver { return false; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return true; + } + /** * Creates an escaped parameter. */ diff --git a/src/driver/aurora-data-api/AuroraDataApiQueryRunner.ts b/src/driver/aurora-data-api/AuroraDataApiQueryRunner.ts index a9df895eba7..87099ddcb5b 100644 --- a/src/driver/aurora-data-api/AuroraDataApiQueryRunner.ts +++ b/src/driver/aurora-data-api/AuroraDataApiQueryRunner.ts @@ -16,10 +16,11 @@ import {TableIndexOptions} from "../../schema-builder/options/TableIndexOptions" import {TableUnique} from "../../schema-builder/table/TableUnique"; import {BaseQueryRunner} from "../../query-runner/BaseQueryRunner"; import {Broadcaster} from "../../subscriber/Broadcaster"; -import {ColumnType, PromiseUtils} from "../../index"; +import {ColumnType} from "../../index"; import {TableCheck} from "../../schema-builder/table/TableCheck"; import {IsolationLevel} from "../types/IsolationLevel"; import {TableExclusion} from "../../schema-builder/table/TableExclusion"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; /** * Runs queries on a single mysql database connection. @@ -36,6 +37,8 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu driver: AuroraDataApiDriver; + protected client: any + // ------------------------------------------------------------------------- // Protected Properties // ------------------------------------------------------------------------- @@ -49,10 +52,11 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu // Constructor // ------------------------------------------------------------------------- - constructor(driver: AuroraDataApiDriver) { + constructor(driver: AuroraDataApiDriver, client: any) { super(); this.driver = driver; this.connection = driver.connection; + this.client = client; this.broadcaster = new Broadcaster(this); } @@ -86,8 +90,17 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu if (this.isTransactionActive) throw new TransactionAlreadyStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = true; - await this.driver.client.startTransaction(); + await this.client.startTransaction(); + + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -98,8 +111,16 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu if (!this.isTransactionActive) throw new TransactionNotStartedError(); - await this.driver.client.commitTransaction(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + + await this.client.commitTransaction(); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -110,8 +131,17 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu if (!this.isTransactionActive) throw new TransactionNotStartedError(); - await this.driver.client.rollbackTransaction(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + + await this.client.rollbackTransaction(); + this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -121,7 +151,7 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu if (this.isReleased) throw new QueryRunnerAlreadyReleasedError(); - const result = await this.driver.client.query(query, parameters); + const result = await this.client.query(query, parameters); if (result.records) { return result.records; @@ -469,7 +499,9 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu * Creates a new columns from the column in the table. */ async addColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.addColumn(tableOrName, column)); + for (const column of columns) { + await this.addColumn(tableOrName, column); + } } /** @@ -682,7 +714,9 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu * Changes a column in the table. */ async changeColumns(tableOrName: Table|string, changedColumns: { newColumn: TableColumn, oldColumn: TableColumn }[]): Promise { - await PromiseUtils.runInSequence(changedColumns, changedColumn => this.changeColumn(tableOrName, changedColumn.oldColumn, changedColumn.newColumn)); + for (const {oldColumn, newColumn} of changedColumns) { + await this.changeColumn(tableOrName, oldColumn, newColumn) + } } /** @@ -774,7 +808,9 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu * Drops the columns in the table. */ async dropColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.dropColumn(tableOrName, column)); + for (const column of columns) { + await this.dropColumn(tableOrName, column); + } } /** @@ -1623,4 +1659,26 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu return c; } + /** + * Checks if column display width is by default. + */ + protected isDefaultColumnWidth(table: Table, column: TableColumn, width: number): boolean { + // if table have metadata, we check if length is specified in column metadata + if (this.connection.hasMetadata(table.name)) { + const metadata = this.connection.getMetadata(table.name); + const columnMetadata = metadata.findColumnWithDatabaseName(column.name); + if (columnMetadata && columnMetadata.width) + return false; + } + + const defaultWidthForType = this.connection.driver.dataTypeDefaults + && this.connection.driver.dataTypeDefaults[column.type] + && this.connection.driver.dataTypeDefaults[column.type].width; + + if (defaultWidthForType) { + return defaultWidthForType === width; + } + return false; + } + } diff --git a/src/driver/better-sqlite3/BetterSqlite3ConnectionOptions.ts b/src/driver/better-sqlite3/BetterSqlite3ConnectionOptions.ts new file mode 100644 index 00000000000..09422cbfc00 --- /dev/null +++ b/src/driver/better-sqlite3/BetterSqlite3ConnectionOptions.ts @@ -0,0 +1,60 @@ +import {BaseConnectionOptions} from "../../connection/BaseConnectionOptions"; + +/** + * Sqlite-specific connection options. + */ +export interface BetterSqlite3ConnectionOptions extends BaseConnectionOptions { + + /** + * Database type. + */ + readonly type: "better-sqlite3"; + + /** + * Storage type or path to the storage. + */ + readonly database: string; + + /** + * Encryption key for for SQLCipher. + */ + readonly key?: string; + + /** + * Cache size of sqlite statement to speed up queries. + * Default: 100. + */ + readonly statementCacheSize?: number; + + /** + * Function to run before a database is used in typeorm. + * You can set pragmas, register plugins or register + * functions or aggregates in this function. + */ + readonly prepareDatabase?: (db: any) => void | Promise; + + /** + * Open the database connection in readonly mode. + * Default: false. + */ + readonly readonly?: boolean; + + /** + * If the database does not exist, an Error will be thrown instead of creating a new file. + * This option does not affect in-memory or readonly database connections. + * Default: false. + */ + readonly fileMustExist?: boolean; + + /** + * The number of milliseconds to wait when executing queries + * on a locked database, before throwing a SQLITE_BUSY error. + * Default: 5000. + */ + readonly timeout?: number; + + /** + * Provide a function that gets called with every SQL string executed by the database connection. + */ + readonly verbose?: Function; +} \ No newline at end of file diff --git a/src/driver/better-sqlite3/BetterSqlite3Driver.ts b/src/driver/better-sqlite3/BetterSqlite3Driver.ts new file mode 100644 index 00000000000..f5d9480759e --- /dev/null +++ b/src/driver/better-sqlite3/BetterSqlite3Driver.ts @@ -0,0 +1,142 @@ +import mkdirp from 'mkdirp'; +import path from 'path'; +import { DriverPackageNotInstalledError } from "../../error/DriverPackageNotInstalledError"; +import { DriverOptionNotSetError } from "../../error/DriverOptionNotSetError"; +import { PlatformTools } from "../../platform/PlatformTools"; +import { Connection } from "../../connection/Connection"; +import { ColumnType } from "../types/ColumnTypes"; +import { QueryRunner } from "../../query-runner/QueryRunner"; +import { AbstractSqliteDriver } from "../sqlite-abstract/AbstractSqliteDriver"; +import { BetterSqlite3ConnectionOptions } from "./BetterSqlite3ConnectionOptions"; +import { BetterSqlite3QueryRunner } from "./BetterSqlite3QueryRunner"; +import {ReplicationMode} from "../types/ReplicationMode"; + +/** + * Organizes communication with sqlite DBMS. + */ +export class BetterSqlite3Driver extends AbstractSqliteDriver { + + // ------------------------------------------------------------------------- + // Public Implemented Properties + // ------------------------------------------------------------------------- + + /** + * Connection options. + */ + options: BetterSqlite3ConnectionOptions; + + /** + * SQLite underlying library. + */ + sqlite: any; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(connection: Connection) { + super(connection); + + this.connection = connection; + this.options = connection.options as BetterSqlite3ConnectionOptions; + this.database = this.options.database; + + // validate options to make sure everything is set + if (!this.options.database) + throw new DriverOptionNotSetError("database"); + + // load sqlite package + this.loadDependencies(); + } + + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- + + /** + * Closes connection with database. + */ + async disconnect(): Promise { + this.queryRunner = undefined; + this.databaseConnection.close(); + } + + /** + * Creates a query runner used to execute database queries. + */ + createQueryRunner(mode: ReplicationMode): QueryRunner { + if (!this.queryRunner) + this.queryRunner = new BetterSqlite3QueryRunner(this); + + return this.queryRunner; + } + + normalizeType(column: { type?: ColumnType, length?: number | string, precision?: number | null, scale?: number }): string { + if ((column.type as any) === Buffer) { + return "blob"; + } + + return super.normalizeType(column); + } + + // ------------------------------------------------------------------------- + // Protected Methods + // ------------------------------------------------------------------------- + + /** + * Creates connection with the database. + */ + protected async createDatabaseConnection() { + // not to create database directory if is in memory + if (this.options.database !== ":memory:") + await this.createDatabaseDirectory(this.options.database); + + const { + database, + readonly = false, + fileMustExist = false, + timeout = 5000, + verbose = null, + prepareDatabase + } = this.options; + const databaseConnection = this.sqlite(database, { readonly, fileMustExist, timeout, verbose }); + + // we need to enable foreign keys in sqlite to make sure all foreign key related features + // working properly. this also makes onDelete to work with sqlite. + databaseConnection.exec(`PRAGMA foreign_keys = ON`); + + // turn on WAL mode to enhance performance + databaseConnection.exec(`PRAGMA journal_mode = WAL`); + + // in the options, if encryption key for SQLCipher is setted. + if (this.options.key) { + databaseConnection.exec(`PRAGMA key = ${JSON.stringify(this.options.key)}`); + } + + if (typeof prepareDatabase === "function") { + prepareDatabase(databaseConnection); + } + + return databaseConnection; + } + + /** + * If driver dependency is not given explicitly, then try to load it via "require". + */ + protected loadDependencies(): void { + try { + this.sqlite = PlatformTools.load("better-sqlite3"); + + } catch (e) { + throw new DriverPackageNotInstalledError("SQLite", "better-sqlite3"); + } + } + + /** + * Auto creates database directory if it does not exist. + */ + protected async createDatabaseDirectory(fullPath: string): Promise { + await mkdirp(path.dirname(fullPath)); + } + +} diff --git a/src/driver/better-sqlite3/BetterSqlite3QueryRunner.ts b/src/driver/better-sqlite3/BetterSqlite3QueryRunner.ts new file mode 100644 index 00000000000..7df796ade48 --- /dev/null +++ b/src/driver/better-sqlite3/BetterSqlite3QueryRunner.ts @@ -0,0 +1,106 @@ +import { QueryRunnerAlreadyReleasedError } from "../../error/QueryRunnerAlreadyReleasedError"; +import { QueryFailedError } from "../../error/QueryFailedError"; +import { AbstractSqliteQueryRunner } from "../sqlite-abstract/AbstractSqliteQueryRunner"; +import { Broadcaster } from "../../subscriber/Broadcaster"; +import { BetterSqlite3Driver } from "./BetterSqlite3Driver"; + +/** + * Runs queries on a single sqlite database connection. + * + * Does not support compose primary keys with autoincrement field. + * todo: need to throw exception for this case. + */ +export class BetterSqlite3QueryRunner extends AbstractSqliteQueryRunner { + + /** + * Database driver used by connection. + */ + driver: BetterSqlite3Driver; + + // ------------------------------------------------------------------------- + // Constructor + // ------------------------------------------------------------------------- + + constructor(driver: BetterSqlite3Driver) { + super(); + this.driver = driver; + this.connection = driver.connection; + this.broadcaster = new Broadcaster(this); + if (typeof this.driver.options.statementCacheSize === "number") { + this.cacheSize = this.driver.options.statementCacheSize; + } else { + this.cacheSize = 100; + } + } + + private cacheSize: number; + private stmtCache = new Map(); + + private async getStmt(query: string) { + if (this.cacheSize > 0) { + let stmt = this.stmtCache.get(query); + if (!stmt) { + const databaseConnection = await this.connect(); + stmt = databaseConnection.prepare(query); + this.stmtCache.set(query, stmt); + while (this.stmtCache.size > this.cacheSize) { + // since es6 map keeps the insertion order, + // it comes to be FIFO cache + const key = this.stmtCache.keys().next().value; + this.stmtCache.delete(key); + } + } + return stmt; + } else { + const databaseConnection = await this.connect(); + return databaseConnection.prepare(query); + } + } + + /** + * Executes a given SQL query. + */ + async query(query: string, parameters?: any[]): Promise { + if (this.isReleased) + throw new QueryRunnerAlreadyReleasedError(); + + const connection = this.driver.connection; + + parameters = parameters || []; + for (let i = 0; i < parameters.length; i++) { + // in "where" clauses the parameters are not escaped by the driver + if (typeof parameters[i] === "boolean") + parameters[i] = +parameters[i]; + } + + this.driver.connection.logger.logQuery(query, parameters, this); + const queryStartTime = +new Date(); + + const stmt = await this.getStmt(query); + + try { + + let result: any; + if (stmt.reader) { + result = stmt.all.apply(stmt, parameters); + } else { + result = stmt.run.apply(stmt, parameters); + if (query.substr(0, 6) === "INSERT") { + result = result.lastInsertRowid; + } + } + + // log slow queries if maxQueryExecution time is set + const maxQueryExecutionTime = connection.options.maxQueryExecutionTime; + const queryEndTime = +new Date(); + const queryExecutionTime = queryEndTime - queryStartTime; + if (maxQueryExecutionTime && queryExecutionTime > maxQueryExecutionTime) + connection.logger.logQuerySlow(queryExecutionTime, query, parameters, this); + + return result; + } catch (err) { + connection.logger.logQueryError(err, query, parameters, this); + throw new QueryFailedError(query, parameters, err); + } + } +} \ No newline at end of file diff --git a/src/driver/cockroachdb/CockroachConnectionCredentialsOptions.ts b/src/driver/cockroachdb/CockroachConnectionCredentialsOptions.ts index ba8fd9b968a..06a30cf66aa 100644 --- a/src/driver/cockroachdb/CockroachConnectionCredentialsOptions.ts +++ b/src/driver/cockroachdb/CockroachConnectionCredentialsOptions.ts @@ -1,4 +1,4 @@ -import { TlsOptions } from "tls"; +import {TlsOptions} from "tls"; /** * Cockroachdb specific connection credential options. diff --git a/src/driver/cockroachdb/CockroachDriver.ts b/src/driver/cockroachdb/CockroachDriver.ts index 33472503616..8cb82185d68 100644 --- a/src/driver/cockroachdb/CockroachDriver.ts +++ b/src/driver/cockroachdb/CockroachDriver.ts @@ -19,6 +19,7 @@ import {EntityMetadata} from "../../metadata/EntityMetadata"; import {OrmUtils} from "../../util/OrmUtils"; import {CockroachQueryRunner} from "./CockroachQueryRunner"; import {ApplyValueTransformers} from "../../util/ApplyValueTransformers"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with Cockroach DBMS. @@ -284,7 +285,7 @@ export class CockroachDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master") { + createQueryRunner(mode: ReplicationMode) { return new CockroachQueryRunner(this, mode); } @@ -483,7 +484,7 @@ export class CockroachDriver implements Driver { const arrayCast = columnMetadata.isArray ? `::${columnMetadata.type}[]` : ""; if (typeof defaultValue === "number") { - return "" + defaultValue; + return `(${defaultValue})`; } else if (typeof defaultValue === "boolean") { return defaultValue === true ? "true" : "false"; @@ -617,8 +618,8 @@ export class CockroachDriver implements Driver { || tableColumn.type !== this.normalizeType(columnMetadata) || tableColumn.length !== columnMetadata.length || tableColumn.precision !== columnMetadata.precision - || tableColumn.scale !== columnMetadata.scale - // || tableColumn.comment !== columnMetadata.comment // todo + || (columnMetadata.scale !== undefined && tableColumn.scale !== columnMetadata.scale) + || (tableColumn.comment || "") !== columnMetadata.comment || (!tableColumn.isGenerated && this.lowerDefaultValueIfNecessary(this.normalizeDefault(columnMetadata)) !== tableColumn.default) // we included check for generated here, because generated columns already can have default values || tableColumn.isPrimary !== columnMetadata.isPrimary || tableColumn.isNullable !== columnMetadata.isNullable @@ -649,6 +650,13 @@ export class CockroachDriver implements Driver { return true; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return false; + } + /** * Creates an escaped parameter. */ diff --git a/src/driver/cockroachdb/CockroachQueryRunner.ts b/src/driver/cockroachdb/CockroachQueryRunner.ts index 018b721f72f..2c3e4d3b7a1 100644 --- a/src/driver/cockroachdb/CockroachQueryRunner.ts +++ b/src/driver/cockroachdb/CockroachQueryRunner.ts @@ -17,11 +17,12 @@ import {TableIndexOptions} from "../../schema-builder/options/TableIndexOptions" import {TableUnique} from "../../schema-builder/table/TableUnique"; import {BaseQueryRunner} from "../../query-runner/BaseQueryRunner"; import {OrmUtils} from "../../util/OrmUtils"; -import {PromiseUtils} from "../../"; import {TableCheck} from "../../schema-builder/table/TableCheck"; import {ColumnType} from "../../index"; import {IsolationLevel} from "../types/IsolationLevel"; import {TableExclusion} from "../../schema-builder/table/TableExclusion"; +import {ReplicationMode} from "../types/ReplicationMode"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; /** * Runs queries on a single postgres database connection. @@ -65,7 +66,7 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner // Constructor // ------------------------------------------------------------------------- - constructor(driver: CockroachDriver, mode: "master"|"slave" = "master") { + constructor(driver: CockroachDriver, mode: ReplicationMode) { super(); this.driver = driver; this.connection = driver.connection; @@ -130,6 +131,10 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner if (this.isTransactionActive) throw new TransactionAlreadyStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = true; await this.query("START TRANSACTION"); await this.query("SAVEPOINT cockroach_restart"); @@ -137,6 +142,10 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner await this.query("SET TRANSACTION ISOLATION LEVEL " + isolationLevel); } this.storeQueries = true; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -147,6 +156,10 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.storeQueries = false; try { @@ -158,10 +171,16 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner } catch (e) { if (e.code === "40001") { await this.query("ROLLBACK TO SAVEPOINT cockroach_restart"); - await PromiseUtils.runInSequence(this.queries, q => this.query(q.query, q.parameters)); + for (const q of this.queries) { + await this.query(q.query, q.parameters); + } await this.commitTransaction(); } } + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -172,10 +191,18 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.storeQueries = false; await this.query("ROLLBACK"); this.queries = []; this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -583,6 +610,12 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner downQueries.push(this.dropIndexSql(table, uniqueConstraint.name!)); // CockroachDB creates indices for unique constraints } + // create column's comment + if (column.comment) { + upQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${column.name}" IS ${this.escapeComment(column.comment)}`)); + downQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${column.name}" IS ${this.escapeComment(column.comment)}`)); + } + await this.executeQueries(upQueries, downQueries); clonedTable.addColumn(column); @@ -593,7 +626,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Creates a new columns from the column in the table. */ async addColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.addColumn(tableOrName, column)); + for (const column of columns) { + await this.addColumn(tableOrName, column); + } } /** @@ -734,8 +769,8 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner } if (oldColumn.comment !== newColumn.comment) { - upQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${oldColumn.name}" IS '${newColumn.comment}'`)); - downQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${newColumn.name}" IS '${oldColumn.comment}'`)); + upQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${oldColumn.name}" IS ${this.escapeComment(newColumn.comment)}`)); + downQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${newColumn.name}" IS ${this.escapeComment(oldColumn.comment)}`)); } if (newColumn.isPrimary !== oldColumn.isPrimary) { @@ -843,7 +878,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Changes a column in the table. */ async changeColumns(tableOrName: Table|string, changedColumns: { newColumn: TableColumn, oldColumn: TableColumn }[]): Promise { - await PromiseUtils.runInSequence(changedColumns, changedColumn => this.changeColumn(tableOrName, changedColumn.oldColumn, changedColumn.newColumn)); + for (const {oldColumn, newColumn} of changedColumns) { + await this.changeColumn(tableOrName, oldColumn, newColumn); + } } /** @@ -922,7 +959,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Drops the columns in the table. */ async dropColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.dropColumn(tableOrName, column)); + for (const column of columns) { + await this.dropColumn(tableOrName, column); + } } /** @@ -1013,7 +1052,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Creates new unique constraints. */ async createUniqueConstraints(tableOrName: Table|string, uniqueConstraints: TableUnique[]): Promise { - await PromiseUtils.runInSequence(uniqueConstraints, uniqueConstraint => this.createUniqueConstraint(tableOrName, uniqueConstraint)); + for (const uniqueConstraint of uniqueConstraints) { + await this.createUniqueConstraint(tableOrName, uniqueConstraint); + } } /** @@ -1037,7 +1078,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Drops unique constraints. */ async dropUniqueConstraints(tableOrName: Table|string, uniqueConstraints: TableUnique[]): Promise { - await PromiseUtils.runInSequence(uniqueConstraints, uniqueConstraint => this.dropUniqueConstraint(tableOrName, uniqueConstraint)); + for (const uniqueConstraint of uniqueConstraints) { + await this.dropUniqueConstraint(tableOrName, uniqueConstraint); + } } /** @@ -1135,7 +1178,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Creates a new foreign keys. */ async createForeignKeys(tableOrName: Table|string, foreignKeys: TableForeignKey[]): Promise { - await PromiseUtils.runInSequence(foreignKeys, foreignKey => this.createForeignKey(tableOrName, foreignKey)); + for (const foreignKey of foreignKeys) { + await this.createForeignKey(tableOrName, foreignKey); + } } /** @@ -1157,7 +1202,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Drops a foreign keys from the table. */ async dropForeignKeys(tableOrName: Table|string, foreignKeys: TableForeignKey[]): Promise { - await PromiseUtils.runInSequence(foreignKeys, foreignKey => this.dropForeignKey(tableOrName, foreignKey)); + for (const foreignKey of foreignKeys) { + await this.dropForeignKey(tableOrName, foreignKey); + } } /** @@ -1195,7 +1242,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Creates a new indices */ async createIndices(tableOrName: Table|string, indices: TableIndex[]): Promise { - await PromiseUtils.runInSequence(indices, index => this.createIndex(tableOrName, index)); + for (const index of indices) { + await this.createIndex(tableOrName, index); + } } /** @@ -1217,7 +1266,9 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner * Drops an indices from the table. */ async dropIndices(tableOrName: Table|string, indices: TableIndex[]): Promise { - await PromiseUtils.runInSequence(indices, index => this.dropIndex(tableOrName, index)); + for (const index of indices) { + await this.dropIndex(tableOrName, index); + } } /** @@ -1324,7 +1375,13 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner return `("table_schema" = '${schema}' AND "table_name" = '${name}')`; }).join(" OR "); const tablesSql = `SELECT * FROM "information_schema"."tables" WHERE ` + tablesCondition; - const columnsSql = `SELECT * FROM "information_schema"."columns" WHERE "is_hidden" = 'NO' AND ` + tablesCondition; + + const columnsSql = ` + SELECT + *, + pg_catalog.col_description(('"' || table_catalog || '"."' || table_schema || '"."' || table_name || '"')::regclass::oid, ordinal_position) as description + FROM "information_schema"."columns" + WHERE "is_hidden" = 'NO' AND ` + tablesCondition; const constraintsCondition = tableNames.map(tableName => { let [schema, name] = tableName.split("."); @@ -1472,10 +1529,11 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner } else { tableColumn.default = dbColumn["column_default"].replace(/:::.*/, ""); + tableColumn.default = tableColumn.default.replace(/^(-?[\d\.]+)$/, "($1)"); } } - tableColumn.comment = ""; // dbColumn["COLUMN_COMMENT"]; + tableColumn.comment = dbColumn["description"] == null ? undefined : dbColumn["description"]; if (dbColumn["character_set_name"]) tableColumn.charset = dbColumn["character_set_name"]; @@ -1642,6 +1700,11 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner sql += `)`; + table.columns + .filter(it => it.comment) + .forEach(it => sql += `; COMMENT ON COLUMN ${this.escapePath(table)}."${it.name}" IS ${this.escapeComment(it.comment)}`); + + return new Query(sql); } @@ -1820,6 +1883,21 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner return disableEscape ? `${table.name}_${columnName}_seq` : `"${table.name}_${columnName}_seq"`; } + /** + * Escapes a given comment so it's safe to include in a query. + */ + protected escapeComment(comment?: string) { + if (comment === undefined || comment.length === 0) { + return 'NULL'; + } + + comment = comment + .replace("'", "''") + .replace("\0", ""); // Null bytes aren't allowed in comments + + return `'${comment}'`; + } + /** * Escapes given table or view path. */ diff --git a/src/driver/cordova/CordovaDriver.ts b/src/driver/cordova/CordovaDriver.ts index bec6b6b63ff..6f6eb3a463f 100644 --- a/src/driver/cordova/CordovaDriver.ts +++ b/src/driver/cordova/CordovaDriver.ts @@ -5,6 +5,7 @@ import {QueryRunner} from "../../query-runner/QueryRunner"; import {Connection} from "../../connection/Connection"; import {DriverOptionNotSetError} from "../../error/DriverOptionNotSetError"; import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError"; +import {ReplicationMode} from "../types/ReplicationMode"; // needed for typescript compiler interface Window { @@ -15,7 +16,7 @@ declare var window: Window; export class CordovaDriver extends AbstractSqliteDriver { options: CordovaConnectionOptions; - + // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- @@ -37,7 +38,7 @@ export class CordovaDriver extends AbstractSqliteDriver { // load sqlite package this.loadDependencies(); } - + // ------------------------------------------------------------------------- // Public Methods @@ -52,17 +53,17 @@ export class CordovaDriver extends AbstractSqliteDriver { this.databaseConnection.close(ok, fail); }); } - + /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master"): QueryRunner { + createQueryRunner(mode: ReplicationMode): QueryRunner { if (!this.queryRunner) this.queryRunner = new CordovaQueryRunner(this); return this.queryRunner; } - + // ------------------------------------------------------------------------- // Protected Methods // ------------------------------------------------------------------------- @@ -104,4 +105,4 @@ export class CordovaDriver extends AbstractSqliteDriver { throw new DriverPackageNotInstalledError("Cordova-SQLite", "cordova-sqlite-storage"); } } -} \ No newline at end of file +} diff --git a/src/driver/expo/ExpoDriver.ts b/src/driver/expo/ExpoDriver.ts index 280b7e08b7c..1086e42e719 100644 --- a/src/driver/expo/ExpoDriver.ts +++ b/src/driver/expo/ExpoDriver.ts @@ -4,10 +4,11 @@ import {ExpoQueryRunner} from "./ExpoQueryRunner"; import {QueryRunner} from "../../query-runner/QueryRunner"; import {Connection} from "../../connection/Connection"; import {DriverOptionNotSetError} from "../../error/DriverOptionNotSetError"; +import {ReplicationMode} from "../types/ReplicationMode"; export class ExpoDriver extends AbstractSqliteDriver { options: ExpoConnectionOptions; - + // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- @@ -20,14 +21,14 @@ export class ExpoDriver extends AbstractSqliteDriver { // validate options to make sure everything is set if (!this.options.database) throw new DriverOptionNotSetError("database"); - + if (!this.options.driver) throw new DriverOptionNotSetError("driver"); // load sqlite package this.sqlite = this.options.driver; } - + // ------------------------------------------------------------------------- // Public Methods @@ -48,17 +49,17 @@ export class ExpoDriver extends AbstractSqliteDriver { } }); } - + /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master"): QueryRunner { + createQueryRunner(mode: ReplicationMode): QueryRunner { if (!this.queryRunner) this.queryRunner = new ExpoQueryRunner(this); return this.queryRunner; } - + // ------------------------------------------------------------------------- // Protected Methods // ------------------------------------------------------------------------- diff --git a/src/driver/expo/ExpoQueryRunner.ts b/src/driver/expo/ExpoQueryRunner.ts index 3931e49a81b..2169f629a20 100644 --- a/src/driver/expo/ExpoQueryRunner.ts +++ b/src/driver/expo/ExpoQueryRunner.ts @@ -5,6 +5,7 @@ import {TransactionAlreadyStartedError} from "../../error/TransactionAlreadyStar import {TransactionNotStartedError} from "../../error/TransactionNotStartedError"; import {ExpoDriver} from "./ExpoDriver"; import {Broadcaster} from "../../subscriber/Broadcaster"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; // Needed to satisfy the Typescript compiler interface IResultSet { @@ -29,7 +30,7 @@ interface ITransaction { * Runs queries on a single sqlite database connection. */ export class ExpoQueryRunner extends AbstractSqliteQueryRunner { - + /** * Database driver used by connection. */ @@ -39,7 +40,7 @@ export class ExpoQueryRunner extends AbstractSqliteQueryRunner { * Database transaction object */ private transaction?: ITransaction; - + // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- @@ -55,9 +56,9 @@ export class ExpoQueryRunner extends AbstractSqliteQueryRunner { * Starts transaction. Within Expo, all database operations happen in a * transaction context, so issuing a `BEGIN TRANSACTION` command is * redundant and will result in the following error: - * + * * `Error: Error code 1: cannot start a transaction within a transaction` - * + * * Instead, we keep track of a `Transaction` object in `this.transaction` * and continue using the same object until we wish to commit the * transaction. @@ -66,7 +67,15 @@ export class ExpoQueryRunner extends AbstractSqliteQueryRunner { if (this.isTransactionActive && typeof this.transaction !== "undefined") throw new TransactionAlreadyStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = true; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -81,8 +90,16 @@ export class ExpoQueryRunner extends AbstractSqliteQueryRunner { if (!this.isTransactionActive && typeof this.transaction === "undefined") throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = false; this.transaction = undefined; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -96,8 +113,16 @@ export class ExpoQueryRunner extends AbstractSqliteQueryRunner { if (!this.isTransactionActive && typeof this.transaction === "undefined") throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = false; this.transaction = undefined; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -125,7 +150,7 @@ export class ExpoQueryRunner extends AbstractSqliteQueryRunner { if (maxQueryExecutionTime && queryExecutionTime > maxQueryExecutionTime) { this.driver.connection.logger.logQuerySlow(queryExecutionTime, query, parameters, this); } - + // return id of inserted row, if query was insert statement. if (query.substr(0, 11) === "INSERT INTO") { ok(result.insertId); diff --git a/src/driver/mongodb/MongoConnectionOptions.ts b/src/driver/mongodb/MongoConnectionOptions.ts index 845f3f718c7..7cbb1aac82e 100644 --- a/src/driver/mongodb/MongoConnectionOptions.ts +++ b/src/driver/mongodb/MongoConnectionOptions.ts @@ -327,4 +327,9 @@ export interface MongoConnectionOptions extends BaseConnectionOptions { * https://github.com/mongodb/node-mongodb-native/releases/tag/v3.2.1 */ readonly useUnifiedTopology?: boolean; + + /** + * Automatic Client-Side Field Level Encryption configuration. + */ + readonly autoEncryption?: any; } diff --git a/src/driver/mongodb/MongoDriver.ts b/src/driver/mongodb/MongoDriver.ts index 71065324678..ebf1a9b6487 100644 --- a/src/driver/mongodb/MongoDriver.ts +++ b/src/driver/mongodb/MongoDriver.ts @@ -16,6 +16,8 @@ import {ConnectionOptions} from "../../connection/ConnectionOptions"; import {EntityMetadata} from "../../metadata/EntityMetadata"; import {ObjectUtils} from "../../util/ObjectUtils"; import {ApplyValueTransformers} from "../../util/ApplyValueTransformers"; +import {ReplicationMode} from "../types/ReplicationMode"; +import {DriverUtils} from "../DriverUtils"; /** * Organizes communication with MongoDB. @@ -134,7 +136,7 @@ export class MongoDriver implements Driver { /** * Valid mongo connection options * NOTE: Keep sync with MongoConnectionOptions - * Sync with http://mongodb.github.io/node-mongodb-native/3.1/api/MongoClient.html + * Sync with http://mongodb.github.io/node-mongodb-native/3.5/api/MongoClient.html */ protected validOptionNames: string[] = [ "poolSize", @@ -195,7 +197,8 @@ export class MongoDriver implements Driver { "minSize", "monitorCommands", "useNewUrlParser", - "useUnifiedTopology" + "useUnifiedTopology", + "autoEncryption" ]; // ------------------------------------------------------------------------- @@ -221,9 +224,11 @@ export class MongoDriver implements Driver { */ connect(): Promise { return new Promise((ok, fail) => { + const options = DriverUtils.buildDriverOptions(this.options); + this.mongodb.MongoClient.connect( - this.buildConnectionUrl(), - this.buildConnectionOptions(), + this.buildConnectionUrl(options), + this.buildConnectionOptions(options), (err: any, client: any) => { if (err) return fail(err); @@ -262,7 +267,7 @@ export class MongoDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master") { + createQueryRunner(mode: ReplicationMode) { return this.queryRunner!; } @@ -389,6 +394,13 @@ export class MongoDriver implements Driver { return false; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return false; + } + /** * Creates an escaped parameter. */ @@ -425,30 +437,31 @@ export class MongoDriver implements Driver { /** * Builds connection url that is passed to underlying driver to perform connection to the mongodb database. */ - protected buildConnectionUrl(): string { - if (this.options.url) - return this.options.url; - - const credentialsUrlPart = (this.options.username && this.options.password) - ? `${this.options.username}:${this.options.password}@` + protected buildConnectionUrl(options: { [key: string]: any }): string { + const schemaUrlPart = options.type.toLowerCase(); + const credentialsUrlPart = (options.username && options.password) + ? `${options.username}:${options.password}@` : ""; + const portUrlPart = (schemaUrlPart === "mongodb+srv") + ? "" + : `:${options.port || "27017"}`; - return `mongodb://${credentialsUrlPart}${this.options.host || "127.0.0.1"}:${this.options.port || "27017"}/${this.options.database}`; + return `${schemaUrlPart}://${credentialsUrlPart}${options.host || "127.0.0.1"}${portUrlPart}/${options.database || ""}`; } /** * Build connection options from MongoConnectionOptions */ - protected buildConnectionOptions(): any { + protected buildConnectionOptions(options: { [key: string]: any }): any { const mongoOptions: any = {}; for (let index = 0; index < this.validOptionNames.length; index++) { const optionName = this.validOptionNames[index]; - if (this.options.extra && optionName in this.options.extra) { - mongoOptions[optionName] = this.options.extra[optionName]; - } else if (optionName in this.options) { - mongoOptions[optionName] = (this.options as any)[optionName]; + if (options.extra && optionName in options.extra) { + mongoOptions[optionName] = options.extra[optionName]; + } else if (optionName in options) { + mongoOptions[optionName] = options[optionName]; } } diff --git a/src/driver/mongodb/MongoQueryRunner.ts b/src/driver/mongodb/MongoQueryRunner.ts index 5bdf54171ef..edfcb17c8e7 100644 --- a/src/driver/mongodb/MongoQueryRunner.ts +++ b/src/driver/mongodb/MongoQueryRunner.ts @@ -1,9 +1,9 @@ -import { QueryRunner } from "../../query-runner/QueryRunner"; -import { ObjectLiteral } from "../../common/ObjectLiteral"; -import { TableColumn } from "../../schema-builder/table/TableColumn"; -import { Table } from "../../schema-builder/table/Table"; -import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"; -import { TableIndex } from "../../schema-builder/table/TableIndex"; +import {QueryRunner} from "../../query-runner/QueryRunner"; +import {ObjectLiteral} from "../../common/ObjectLiteral"; +import {TableColumn} from "../../schema-builder/table/TableColumn"; +import {Table} from "../../schema-builder/table/Table"; +import {TableForeignKey} from "../../schema-builder/table/TableForeignKey"; +import {TableIndex} from "../../schema-builder/table/TableIndex"; import {View} from "../../schema-builder/view/View"; import { AggregationCursor, @@ -20,7 +20,6 @@ import { CollStats, CommandCursor, Cursor, - Db, DeleteWriteOpResultObject, FindAndModifyWriteOpResultObject, FindOneAndReplaceOption, @@ -29,6 +28,7 @@ import { InsertOneWriteOpResult, InsertWriteOpResult, MapReduceOptions, + MongoClient, MongoCountPreferences, MongodbIndexOptions, OrderedBulkOperation, @@ -38,14 +38,14 @@ import { UnorderedBulkOperation, UpdateWriteOpResult } from "./typings"; -import { Connection } from "../../connection/Connection"; -import { ReadStream } from "../../platform/PlatformTools"; -import { MongoEntityManager } from "../../entity-manager/MongoEntityManager"; -import { SqlInMemory } from "../SqlInMemory"; -import { TableUnique } from "../../schema-builder/table/TableUnique"; -import { Broadcaster } from "../../subscriber/Broadcaster"; -import { TableCheck } from "../../schema-builder/table/TableCheck"; -import { TableExclusion } from "../../schema-builder/table/TableExclusion"; +import {Connection} from "../../connection/Connection"; +import {ReadStream} from "../../platform/PlatformTools"; +import {MongoEntityManager} from "../../entity-manager/MongoEntityManager"; +import {SqlInMemory} from "../SqlInMemory"; +import {TableUnique} from "../../schema-builder/table/TableUnique"; +import {Broadcaster} from "../../subscriber/Broadcaster"; +import {TableCheck} from "../../schema-builder/table/TableCheck"; +import {TableExclusion} from "../../schema-builder/table/TableExclusion"; /** * Runs queries on a single MongoDB connection. @@ -103,13 +103,13 @@ export class MongoQueryRunner implements QueryRunner { /** * Real database connection from a connection pool used to perform queries. */ - databaseConnection: Db; + databaseConnection: MongoClient; // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- - constructor(connection: Connection, databaseConnection: Db) { + constructor(connection: Connection, databaseConnection: MongoClient) { this.connection = connection; this.databaseConnection = databaseConnection; this.broadcaster = new Broadcaster(this); diff --git a/src/driver/mongodb/typings.ts b/src/driver/mongodb/typings.ts index 9ec1f61d6f4..f0605311353 100644 --- a/src/driver/mongodb/typings.ts +++ b/src/driver/mongodb/typings.ts @@ -1,4 +1,4 @@ -import { EventEmitter, Readable, Writable } from "../../platform/PlatformTools"; +import {EventEmitter, Readable, Writable} from "../../platform/PlatformTools"; /** * Creates a new MongoClient instance. @@ -5014,7 +5014,7 @@ export declare class Cursor extends Readable { * @see http://mongodb.github.io/node-mongodb-native/3.1/api/Cursor.html#forEach */ forEach(iterator: IteratorCallback, callback: EndCallback): void; - forEach(iterator: IteratorCallback): Promise; + forEach(iterator: IteratorCallback): Promise; /** * Check if there is any document still available in the cursor. diff --git a/src/driver/mysql/MysqlDriver.ts b/src/driver/mysql/MysqlDriver.ts index 291beaa68fc..cdf3deb7c37 100644 --- a/src/driver/mysql/MysqlDriver.ts +++ b/src/driver/mysql/MysqlDriver.ts @@ -18,6 +18,7 @@ import {MysqlConnectionCredentialsOptions} from "./MysqlConnectionCredentialsOpt import {EntityMetadata} from "../../metadata/EntityMetadata"; import {OrmUtils} from "../../util/OrmUtils"; import {ApplyValueTransformers} from "../../util/ApplyValueTransformers"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with MySQL DBMS. @@ -385,7 +386,7 @@ export class MysqlDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master") { + createQueryRunner(mode: ReplicationMode) { return new MysqlQueryRunner(this, mode); } @@ -590,7 +591,7 @@ export class MysqlDriver implements Driver { } if (typeof defaultValue === "number") { - return "" + defaultValue; + return `'${defaultValue.toFixed(columnMetadata.scale)}'`; } else if (typeof defaultValue === "boolean") { return defaultValue === true ? "1" : "0"; @@ -709,11 +710,13 @@ export class MysqlDriver implements Driver { /** * Creates generated map of values generated or returned by database after INSERT query. */ - createGeneratedMap(metadata: EntityMetadata, insertResult: any) { + createGeneratedMap(metadata: EntityMetadata, insertResult: any, entityIndex: number) { const generatedMap = metadata.generatedColumns.reduce((map, generatedColumn) => { let value: any; if (generatedColumn.generationStrategy === "increment" && insertResult.insertId) { - value = insertResult.insertId; + // NOTE: When multiple rows is inserted by a single INSERT statement, + // `insertId` is the value generated for the first inserted row only. + value = insertResult.insertId + entityIndex; // } else if (generatedColumn.generationStrategy === "uuid") { // console.log("getting db value:", generatedColumn.databaseName); // value = generatedColumn.getEntityValue(uuidMap); @@ -767,13 +770,13 @@ export class MysqlDriver implements Driver { || tableColumn.type !== this.normalizeType(columnMetadata) || tableColumn.length !== columnMetadataLength || tableColumn.width !== columnMetadata.width - || tableColumn.precision !== columnMetadata.precision - || tableColumn.scale !== columnMetadata.scale + || (columnMetadata.precision !== undefined && tableColumn.precision !== columnMetadata.precision) + || (columnMetadata.scale !== undefined && tableColumn.scale !== columnMetadata.scale) || tableColumn.zerofill !== columnMetadata.zerofill || tableColumn.unsigned !== columnMetadata.unsigned || tableColumn.asExpression !== columnMetadata.asExpression || tableColumn.generatedType !== columnMetadata.generatedType - // || tableColumn.comment !== columnMetadata.comment // todo + || (tableColumn.comment || "") !== columnMetadata.comment || !this.compareDefaultValues(this.normalizeDefault(columnMetadata), tableColumn.default) || (tableColumn.enum && columnMetadata.enum && !OrmUtils.isArraysEqual(tableColumn.enum, columnMetadata.enum.map(val => val + ""))) || tableColumn.onUpdate !== columnMetadata.onUpdate @@ -798,6 +801,13 @@ export class MysqlDriver implements Driver { return false; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return true; + } + /** * Creates an escaped parameter. */ diff --git a/src/driver/mysql/MysqlQueryRunner.ts b/src/driver/mysql/MysqlQueryRunner.ts index f501c899d8e..53b7b4eb1e6 100644 --- a/src/driver/mysql/MysqlQueryRunner.ts +++ b/src/driver/mysql/MysqlQueryRunner.ts @@ -17,11 +17,13 @@ import {TableIndexOptions} from "../../schema-builder/options/TableIndexOptions" import {TableUnique} from "../../schema-builder/table/TableUnique"; import {BaseQueryRunner} from "../../query-runner/BaseQueryRunner"; import {Broadcaster} from "../../subscriber/Broadcaster"; -import {ColumnType, PromiseUtils} from "../../index"; +import {ColumnType} from "../../index"; import {TableCheck} from "../../schema-builder/table/TableCheck"; import {IsolationLevel} from "../types/IsolationLevel"; import {TableExclusion} from "../../schema-builder/table/TableExclusion"; -import { VersionUtils } from "../../util/VersionUtils"; +import {VersionUtils} from "../../util/VersionUtils"; +import {ReplicationMode} from "../types/ReplicationMode"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; /** * Runs queries on a single mysql database connection. @@ -50,7 +52,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { // Constructor // ------------------------------------------------------------------------- - constructor(driver: MysqlDriver, mode: "master"|"slave" = "master") { + constructor(driver: MysqlDriver, mode: ReplicationMode) { super(); this.driver = driver; this.connection = driver.connection; @@ -108,6 +110,10 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { if (this.isTransactionActive) throw new TransactionAlreadyStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = true; if (isolationLevel) { await this.query("SET TRANSACTION ISOLATION LEVEL " + isolationLevel); @@ -115,6 +121,10 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { } else { await this.query("START TRANSACTION"); } + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -125,8 +135,16 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("COMMIT"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -137,8 +155,16 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("ROLLBACK"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -518,7 +544,9 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { * Creates a new columns from the column in the table. */ async addColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.addColumn(tableOrName, column)); + for (const column of columns) { + await this.addColumn(tableOrName, column); + } } /** @@ -634,7 +662,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { oldColumn.name = newColumn.name; } - if (this.isColumnChanged(oldColumn, newColumn, true)) { + if (this.isColumnChanged(oldColumn, newColumn, true, true)) { upQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} CHANGE \`${oldColumn.name}\` ${this.buildCreateColumnSql(newColumn, true)}`)); downQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} CHANGE \`${newColumn.name}\` ${this.buildCreateColumnSql(oldColumn, true)}`)); } @@ -733,7 +761,9 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { * Changes a column in the table. */ async changeColumns(tableOrName: Table|string, changedColumns: { newColumn: TableColumn, oldColumn: TableColumn }[]): Promise { - await PromiseUtils.runInSequence(changedColumns, changedColumn => this.changeColumn(tableOrName, changedColumn.oldColumn, changedColumn.newColumn)); + for (const {oldColumn, newColumn} of changedColumns) { + await this.changeColumn(tableOrName, oldColumn, newColumn); + } } /** @@ -825,7 +855,9 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { * Drops the columns in the table. */ async dropColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.dropColumn(tableOrName, column)); + for (const column of columns) { + await this.dropColumn(tableOrName, column); + } } /** @@ -1193,47 +1225,183 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { return []; const currentDatabase = await this.getCurrentDatabase(); - const tablesCondition = tableNames.map(tableName => { + + // The following SQL brought to you by: + // A terrible understanding of https://dev.mysql.com/doc/refman/8.0/en/information-schema-optimization.html + // + // Short Version: + // INFORMATION_SCHEMA is a weird metadata virtual table and follows VERY FEW of the normal + // query optimization rules. Depending on the columns you query against & the columns you're SELECTing + // there can be a drastically different query performance - this is because the tables map to + // data on the disk and some pieces of data require a scan of the data directory, the database files, etc + + // With most of these, you'll want to do an `EXPLAIN` when making changes to make sure + // the changes you're making aren't changing the query performance profile negatively + // When you do the explain you'll want to look at the `Extra` field - + // It will look something like: "Using where; {FILE_OPENING}; Scanned {DB_NUM} databases" + // FILE_OPENING will commonly be OPEN_FRM_ONLY or OPEN_FULL_TABLE - you want to aim to NOT do + // an OPEN_FULL_TABLE unless necessary. DB_NUM may be a number or "all" - you really want to + // keep this to 0 or 1. Ideally 0. "All" means you've scanned all databases - not good. + // + // For more info, see the above link to the MySQL docs. + // + // Something not noted in the docs is that complex `WHERE` clauses - such as `OR` expressions - + // will cause the query to not hit the optimizations & do full scans. This is why + // a number of queries below do `UNION`s of single `WHERE` clauses. + + // Avoid data directory scan: TABLE_SCHEMA + // Avoid database directory scan: TABLE_NAME + // Full columns: CARDINALITY & INDEX_TYPE - everything else is FRM only + const statsSubquerySql = tableNames.map(tableName => { let [database, name] = tableName.split("."); if (!name) { name = database; database = this.driver.database || currentDatabase; } - return `(\`TABLE_SCHEMA\` = '${database}' AND \`TABLE_NAME\` = '${name}')`; - }).join(" OR "); - const tablesSql = `SELECT * FROM \`INFORMATION_SCHEMA\`.\`TABLES\` WHERE ` + tablesCondition; - - const columnsSql = `SELECT * FROM \`INFORMATION_SCHEMA\`.\`COLUMNS\` WHERE ` + tablesCondition; - - const primaryKeySql = `SELECT * FROM \`INFORMATION_SCHEMA\`.\`KEY_COLUMN_USAGE\` WHERE \`CONSTRAINT_NAME\` = 'PRIMARY' AND (${tablesCondition})`; - - const collationsSql = `SELECT \`SCHEMA_NAME\`, \`DEFAULT_CHARACTER_SET_NAME\` as \`CHARSET\`, \`DEFAULT_COLLATION_NAME\` AS \`COLLATION\` FROM \`INFORMATION_SCHEMA\`.\`SCHEMATA\``; - - const indicesCondition = tableNames.map(tableName => { + return ` + SELECT + * + FROM \`INFORMATION_SCHEMA\`.\`STATISTICS\` + WHERE + \`TABLE_SCHEMA\` = '${database}' + AND + \`TABLE_NAME\` = '${name}' + `; + }).join(" UNION "); + + // Avoid data directory scan: TABLE_SCHEMA + // Avoid database directory scan: TABLE_NAME + // All columns will hit the full table. + const kcuSubquerySql = tableNames.map(tableName => { let [database, name] = tableName.split("."); if (!name) { name = database; database = this.driver.database || currentDatabase; } - return `(\`s\`.\`TABLE_SCHEMA\` = '${database}' AND \`s\`.\`TABLE_NAME\` = '${name}')`; - }).join(" OR "); - const indicesSql = `SELECT \`s\`.* FROM \`INFORMATION_SCHEMA\`.\`STATISTICS\` \`s\` ` + - `LEFT JOIN \`INFORMATION_SCHEMA\`.\`REFERENTIAL_CONSTRAINTS\` \`rc\` ON \`s\`.\`INDEX_NAME\` = \`rc\`.\`CONSTRAINT_NAME\` ` + - `WHERE (${indicesCondition}) AND \`s\`.\`INDEX_NAME\` != 'PRIMARY' AND \`rc\`.\`CONSTRAINT_NAME\` IS NULL`; - - const foreignKeysCondition = tableNames.map(tableName => { + return ` + SELECT + * + FROM \`INFORMATION_SCHEMA\`.\`KEY_COLUMN_USAGE\` \`kcu\` + WHERE + \`kcu\`.\`TABLE_SCHEMA\` = '${database}' + AND + \`kcu\`.\`TABLE_NAME\` = '${name}' + `; + }).join(" UNION "); + + // Avoid data directory scan: CONSTRAINT_SCHEMA + // Avoid database directory scan: TABLE_NAME + // All columns will hit the full table. + const rcSubquerySql = tableNames.map(tableName => { let [database, name] = tableName.split("."); if (!name) { name = database; database = this.driver.database || currentDatabase; } - return `(\`kcu\`.\`TABLE_SCHEMA\` = '${database}' AND \`kcu\`.\`TABLE_NAME\` = '${name}')`; - }).join(" OR "); - const foreignKeysSql = `SELECT \`kcu\`.\`TABLE_SCHEMA\`, \`kcu\`.\`TABLE_NAME\`, \`kcu\`.\`CONSTRAINT_NAME\`, \`kcu\`.\`COLUMN_NAME\`, \`kcu\`.\`REFERENCED_TABLE_SCHEMA\`, ` + - `\`kcu\`.\`REFERENCED_TABLE_NAME\`, \`kcu\`.\`REFERENCED_COLUMN_NAME\`, \`rc\`.\`DELETE_RULE\` \`ON_DELETE\`, \`rc\`.\`UPDATE_RULE\` \`ON_UPDATE\` ` + - `FROM \`INFORMATION_SCHEMA\`.\`KEY_COLUMN_USAGE\` \`kcu\` ` + - `INNER JOIN \`INFORMATION_SCHEMA\`.\`REFERENTIAL_CONSTRAINTS\` \`rc\` ON \`rc\`.\`constraint_name\` = \`kcu\`.\`constraint_name\` ` + - `WHERE ` + foreignKeysCondition; + return ` + SELECT + * + FROM \`INFORMATION_SCHEMA\`.\`REFERENTIAL_CONSTRAINTS\` + WHERE + \`CONSTRAINT_SCHEMA\` = '${database}' + AND + \`TABLE_NAME\` = '${name}' + `; + }).join(" UNION "); + + // Avoid data directory scan: TABLE_SCHEMA + // Avoid database directory scan: TABLE_NAME + // We only use `TABLE_SCHEMA` and `TABLE_NAME` which is `SKIP_OPEN_TABLE` + const tablesSql = tableNames.map(tableName => { + let [database, name] = tableName.split("."); + if (!name) { + name = database; + database = this.driver.database || currentDatabase; + } + return ` + SELECT + \`TABLE_SCHEMA\`, + \`TABLE_NAME\` + FROM + \`INFORMATION_SCHEMA\`.\`TABLES\` + WHERE + \`TABLE_SCHEMA\` = '${database}' + AND + \`TABLE_NAME\` = '${name}' + `; + }).join(" UNION "); + + // Avoid data directory scan: TABLE_SCHEMA + // Avoid database directory scan: TABLE_NAME + // OPEN_FRM_ONLY applies to all columns + const columnsSql = tableNames.map(tableName => { + let [database, name] = tableName.split("."); + if (!name) { + name = database; + database = this.driver.database || currentDatabase; + } + return ` + SELECT + * + FROM + \`INFORMATION_SCHEMA\`.\`COLUMNS\` + WHERE + \`TABLE_SCHEMA\` = '${database}' + AND + \`TABLE_NAME\` = '${name}' + `; + }).join(" UNION "); + + // No Optimizations are available for COLLATIONS + const collationsSql = ` + SELECT + \`SCHEMA_NAME\`, + \`DEFAULT_CHARACTER_SET_NAME\` as \`CHARSET\`, + \`DEFAULT_COLLATION_NAME\` AS \`COLLATION\` + FROM \`INFORMATION_SCHEMA\`.\`SCHEMATA\` + `; + + // Key Column Usage but only for PKs + const primaryKeySql = `SELECT * FROM (${kcuSubquerySql}) \`kcu\` WHERE \`CONSTRAINT_NAME\` = 'PRIMARY'`; + + // Combine stats & referential constraints + const indicesSql = ` + SELECT + \`s\`.* + FROM (${statsSubquerySql}) \`s\` + LEFT JOIN (${rcSubquerySql}) \`rc\` + ON + \`s\`.\`INDEX_NAME\` = \`rc\`.\`CONSTRAINT_NAME\` + AND + \`s\`.\`TABLE_SCHEMA\` = \`rc\`.\`CONSTRAINT_SCHEMA\` + WHERE + \`s\`.\`INDEX_NAME\` != 'PRIMARY' + AND + \`rc\`.\`CONSTRAINT_NAME\` IS NULL + `; + + // Combine Key Column Usage & Referential Constraints + const foreignKeysSql = ` + SELECT + \`kcu\`.\`TABLE_SCHEMA\`, + \`kcu\`.\`TABLE_NAME\`, + \`kcu\`.\`CONSTRAINT_NAME\`, + \`kcu\`.\`COLUMN_NAME\`, + \`kcu\`.\`REFERENCED_TABLE_SCHEMA\`, + \`kcu\`.\`REFERENCED_TABLE_NAME\`, + \`kcu\`.\`REFERENCED_COLUMN_NAME\`, + \`rc\`.\`DELETE_RULE\` \`ON_DELETE\`, + \`rc\`.\`UPDATE_RULE\` \`ON_UPDATE\` + FROM (${kcuSubquerySql}) \`kcu\` + INNER JOIN (${rcSubquerySql}) \`rc\` + ON + \`rc\`.\`CONSTRAINT_SCHEMA\` = \`kcu\`.\`CONSTRAINT_SCHEMA\` + AND + \`rc\`.\`TABLE_NAME\` = \`kcu\`.\`TABLE_NAME\` + AND + \`rc\`.\`CONSTRAINT_NAME\` = \`kcu\`.\`CONSTRAINT_NAME\` + `; + const [dbTables, dbColumns, dbPrimaryKeys, dbCollations, dbIndices, dbForeignKeys]: ObjectLiteral[][] = await Promise.all([ this.query(tablesSql), this.query(columnsSql), @@ -1296,6 +1464,8 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { tableColumn.name = dbColumn["COLUMN_NAME"]; tableColumn.type = dbColumn["DATA_TYPE"].toLowerCase(); + tableColumn.zerofill = dbColumn["COLUMN_TYPE"].indexOf("zerofill") !== -1; + tableColumn.unsigned = tableColumn.zerofill ? true : dbColumn["COLUMN_TYPE"].indexOf("unsigned") !== -1; if (this.driver.withWidthColumnTypes.indexOf(tableColumn.type as ColumnType) !== -1) { const width = dbColumn["COLUMN_TYPE"].substring(dbColumn["COLUMN_TYPE"].indexOf("(") + 1, dbColumn["COLUMN_TYPE"].indexOf(")")); tableColumn.width = width && !this.isDefaultColumnWidth(table, tableColumn, parseInt(width)) ? parseInt(width) : undefined; @@ -1319,7 +1489,9 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { } if (dbColumn["EXTRA"].indexOf("on update") !== -1) { - tableColumn.onUpdate = dbColumn["EXTRA"].substring(dbColumn["EXTRA"].indexOf("on update") + 10); + // New versions of MariaDB return expressions in lowercase. We need to set it in + // uppercase so the comparison in MysqlDriver#compareExtraValues does not fail. + tableColumn.onUpdate = dbColumn["EXTRA"].substring(dbColumn["EXTRA"].indexOf("on update") + 10).toUpperCase(); } if (dbColumn["GENERATION_EXPRESSION"]) { @@ -1332,13 +1504,11 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { tableColumn.isPrimary = dbPrimaryKeys.some(dbPrimaryKey => { return this.driver.buildTableName(dbPrimaryKey["TABLE_NAME"], undefined, dbPrimaryKey["TABLE_SCHEMA"]) === tableFullName && dbPrimaryKey["COLUMN_NAME"] === tableColumn.name; }); - tableColumn.zerofill = dbColumn["COLUMN_TYPE"].indexOf("zerofill") !== -1; - tableColumn.unsigned = tableColumn.zerofill ? true : dbColumn["COLUMN_TYPE"].indexOf("unsigned") !== -1; tableColumn.isGenerated = dbColumn["EXTRA"].indexOf("auto_increment") !== -1; if (tableColumn.isGenerated) tableColumn.generationStrategy = "increment"; - tableColumn.comment = dbColumn["COLUMN_COMMENT"]; + tableColumn.comment = (typeof dbColumn["COLUMN_COMMENT"] === "string" && dbColumn["COLUMN_COMMENT"].length === 0) ? undefined : dbColumn["COLUMN_COMMENT"]; if (dbColumn["CHARACTER_SET_NAME"]) tableColumn.charset = dbColumn["CHARACTER_SET_NAME"] === defaultCharset ? undefined : dbColumn["CHARACTER_SET_NAME"]; if (dbColumn["COLLATION_NAME"]) @@ -1638,6 +1808,22 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { }; } + /** + * Escapes a given comment so it's safe to include in a query. + */ + protected escapeComment(comment?: string) { + if (comment === undefined || comment.length === 0) { + return `''`; + } + + comment = comment + .replace("\\", "\\\\") // MySQL allows escaping characters via backslashes + .replace("'", "''") + .replace("\0", ""); // Null bytes aren't allowed in comments + + return `'${comment}'`; + } + /** * Escapes given table or view path. */ @@ -1679,8 +1865,8 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { c += " PRIMARY KEY"; if (column.isGenerated && column.generationStrategy === "increment") // don't use skipPrimary here since updates can update already exist primary without auto inc. c += " AUTO_INCREMENT"; - if (column.comment) - c += ` COMMENT '${column.comment.replace("'", "''")}'`; + if (column.comment !== undefined && column.comment.length > 0) + c += ` COMMENT ${this.escapeComment(column.comment)}`; if (column.default !== undefined && column.default !== null) c += ` DEFAULT ${column.default}`; if (column.onUpdate) @@ -1694,4 +1880,35 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner { return result[0]["version"]; } + /** + * Checks if column display width is by default. + */ + protected isDefaultColumnWidth(table: Table, column: TableColumn, width: number): boolean { + // if table have metadata, we check if length is specified in column metadata + if (this.connection.hasMetadata(table.name)) { + const metadata = this.connection.getMetadata(table.name); + const columnMetadata = metadata.findColumnWithDatabaseName(column.name); + if (columnMetadata && columnMetadata.width) + return false; + } + + const defaultWidthForType = this.connection.driver.dataTypeDefaults + && this.connection.driver.dataTypeDefaults[column.type] + && this.connection.driver.dataTypeDefaults[column.type].width; + + if (defaultWidthForType) { + // In MariaDB & MySQL 5.7, the default widths of certain numeric types are 1 less than + // the usual defaults when the column is unsigned. + const typesWithReducedUnsignedDefault = ["int", "tinyint", "smallint", "mediumint"]; + const needsAdjustment = typesWithReducedUnsignedDefault.indexOf(column.type) !== -1; + if (column.unsigned && needsAdjustment) { + return (defaultWidthForType - 1) === width; + } else { + return defaultWidthForType === width; + } + } + + return false; + } + } diff --git a/src/driver/nativescript/NativescriptDriver.ts b/src/driver/nativescript/NativescriptDriver.ts index 0d6e8d9daca..b6384f59daf 100644 --- a/src/driver/nativescript/NativescriptDriver.ts +++ b/src/driver/nativescript/NativescriptDriver.ts @@ -6,6 +6,7 @@ import {Connection} from "../../connection/Connection"; import {DriverOptionNotSetError} from "../../error/DriverOptionNotSetError"; import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError"; import {ColumnType} from "../types/ColumnTypes"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with sqlite DBMS within Nativescript. @@ -66,7 +67,7 @@ export class NativescriptDriver extends AbstractSqliteDriver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master"): QueryRunner { + createQueryRunner(mode: ReplicationMode): QueryRunner { if (!this.queryRunner) { this.queryRunner = new NativescriptQueryRunner(this); } diff --git a/src/driver/oracle/OracleDriver.ts b/src/driver/oracle/OracleDriver.ts index 134ed6bba7e..52a2bf46f97 100644 --- a/src/driver/oracle/OracleDriver.ts +++ b/src/driver/oracle/OracleDriver.ts @@ -18,6 +18,7 @@ import {DriverUtils} from "../DriverUtils"; import {EntityMetadata} from "../../metadata/EntityMetadata"; import {OrmUtils} from "../../util/OrmUtils"; import {ApplyValueTransformers} from "../../util/ApplyValueTransformers"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with Oracle RDBMS. @@ -287,7 +288,7 @@ export class OracleDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master") { + createQueryRunner(mode: ReplicationMode) { return new OracleQueryRunner(this, mode); } @@ -392,7 +393,7 @@ export class OracleDriver implements Driver { return columnMetadata.transformer ? ApplyValueTransformers.transformFrom(columnMetadata.transformer, value) : value; if (columnMetadata.type === Boolean) { - value = value ? true : false; + value = !!value; } else if (columnMetadata.type === "date") { value = DateUtils.mixedDateToDateString(value); @@ -623,11 +624,18 @@ export class OracleDriver implements Driver { return false; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return false; + } + /** * Creates an escaped parameter. */ createParameter(parameterName: string, index: number): string { - return ":" + parameterName; + return ":" + (index + 1); } /** diff --git a/src/driver/oracle/OracleQueryRunner.ts b/src/driver/oracle/OracleQueryRunner.ts index 15696da5b0b..508ef434952 100644 --- a/src/driver/oracle/OracleQueryRunner.ts +++ b/src/driver/oracle/OracleQueryRunner.ts @@ -17,9 +17,11 @@ import {Broadcaster} from "../../subscriber/Broadcaster"; import {BaseQueryRunner} from "../../query-runner/BaseQueryRunner"; import {OrmUtils} from "../../util/OrmUtils"; import {TableCheck} from "../../schema-builder/table/TableCheck"; -import {ColumnType, PromiseUtils} from "../../index"; +import {ColumnType} from "../../index"; import {IsolationLevel} from "../types/IsolationLevel"; import {TableExclusion} from "../../schema-builder/table/TableExclusion"; +import {ReplicationMode} from "../types/ReplicationMode"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; /** * Runs queries on a single oracle database connection. @@ -48,7 +50,7 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { // Constructor // ------------------------------------------------------------------------- - constructor(driver: OracleDriver, mode: "master"|"slave" = "master") { + constructor(driver: OracleDriver, mode: ReplicationMode) { super(); this.driver = driver; this.connection = driver.connection; @@ -121,8 +123,17 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { if (isolationLevel !== "SERIALIZABLE" && isolationLevel !== "READ COMMITTED") { throw new Error(`Oracle only supports SERIALIZABLE and READ COMMITTED isolation`); } + + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("SET TRANSACTION ISOLATION LEVEL " + isolationLevel); this.isTransactionActive = true; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -133,8 +144,16 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("COMMIT"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -145,8 +164,16 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("ROLLBACK"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -506,7 +533,9 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { * Creates a new columns from the column in the table. */ async addColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.addColumn(tableOrName, column)); + for (const column of columns) { + await this.addColumn(tableOrName, column); + } } /** @@ -733,7 +762,9 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { * Changes a column in the table. */ async changeColumns(tableOrName: Table|string, changedColumns: { newColumn: TableColumn, oldColumn: TableColumn }[]): Promise { - await PromiseUtils.runInSequence(changedColumns, changedColumn => this.changeColumn(tableOrName, changedColumn.oldColumn, changedColumn.newColumn)); + for (const {oldColumn, newColumn} of changedColumns) { + await this.changeColumn(tableOrName, oldColumn, newColumn); + } } /** @@ -805,7 +836,9 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner { * Drops the columns in the table. */ async dropColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.dropColumn(tableOrName, column)); + for (const column of columns) { + await this.dropColumn(tableOrName, column); + } } /** diff --git a/src/driver/postgres/PostgresConnectionCredentialsOptions.ts b/src/driver/postgres/PostgresConnectionCredentialsOptions.ts index 98f56027ce0..3601670bce3 100644 --- a/src/driver/postgres/PostgresConnectionCredentialsOptions.ts +++ b/src/driver/postgres/PostgresConnectionCredentialsOptions.ts @@ -1,4 +1,4 @@ -import { TlsOptions } from "tls"; +import {TlsOptions} from "tls"; /** * Postgres specific connection credential options. diff --git a/src/driver/postgres/PostgresConnectionOptions.ts b/src/driver/postgres/PostgresConnectionOptions.ts index aa704112178..d37b1377e91 100644 --- a/src/driver/postgres/PostgresConnectionOptions.ts +++ b/src/driver/postgres/PostgresConnectionOptions.ts @@ -46,4 +46,9 @@ export interface PostgresConnectionOptions extends BaseConnectionOptions, Postgr * Defaults to logging error with `warn` level. */ readonly poolErrorHandler?: (err: any) => any; + + /** + * Include notification messages from Postgres server in client logs + */ + readonly logNotifications?: boolean; } diff --git a/src/driver/postgres/PostgresDriver.ts b/src/driver/postgres/PostgresDriver.ts index 15ac6624936..05fe5c7ea1a 100644 --- a/src/driver/postgres/PostgresDriver.ts +++ b/src/driver/postgres/PostgresDriver.ts @@ -2,7 +2,6 @@ import {Driver} from "../Driver"; import {ConnectionIsNotSetError} from "../../error/ConnectionIsNotSetError"; import {ObjectLiteral} from "../../common/ObjectLiteral"; import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError"; -import {DriverUtils} from "../DriverUtils"; import {ColumnMetadata} from "../../metadata/ColumnMetadata"; import {PostgresQueryRunner} from "./PostgresQueryRunner"; import {DateUtils} from "../../util/DateUtils"; @@ -19,8 +18,7 @@ import {PostgresConnectionCredentialsOptions} from "./PostgresConnectionCredenti import {EntityMetadata} from "../../metadata/EntityMetadata"; import {OrmUtils} from "../../util/OrmUtils"; import {ApplyValueTransformers} from "../../util/ApplyValueTransformers"; -import {AuroraDataApiPostgresConnectionOptions} from "../aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions"; -import {AuroraDataApiPostgresQueryRunner} from "../aurora-data-api-pg/AuroraDataApiPostgresQueryRunner"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with PostgreSQL DBMS. @@ -151,7 +149,8 @@ export class PostgresDriver implements Driver { "daterange", "geometry", "geography", - "cube" + "cube", + "ltree" ]; /** @@ -301,6 +300,83 @@ export class PostgresDriver implements Driver { * Makes any action after connection (e.g. create extensions in Postgres driver). */ async afterConnect(): Promise { + const extensionsMetadata = await this.checkMetadataForExtensions(); + + if (extensionsMetadata.hasExtensions) { + await Promise.all([this.master, ...this.slaves].map(pool => { + return new Promise((ok, fail) => { + pool.connect(async (err: any, connection: any, release: Function) => { + await this.enableExtensions(extensionsMetadata, connection); + if (err) return fail(err); + release(); + ok(); + }); + }); + })); + } + + return Promise.resolve(); + } + + protected async enableExtensions(extensionsMetadata: any, connection: any) { + const { logger } = this.connection; + + const { + hasUuidColumns, + hasCitextColumns, + hasHstoreColumns, + hasCubeColumns, + hasGeometryColumns, + hasLtreeColumns, + hasExclusionConstraints, + } = extensionsMetadata; + + if (hasUuidColumns) + try { + await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "${this.options.uuidExtension || "uuid-ossp"}"`); + } catch (_) { + logger.log("warn", `At least one of the entities has uuid column, but the '${this.options.uuidExtension || "uuid-ossp"}' extension cannot be installed automatically. Please install it manually using superuser rights, or select another uuid extension.`); + } + if (hasCitextColumns) + try { + await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "citext"`); + } catch (_) { + logger.log("warn", "At least one of the entities has citext column, but the 'citext' extension cannot be installed automatically. Please install it manually using superuser rights"); + } + if (hasHstoreColumns) + try { + await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "hstore"`); + } catch (_) { + logger.log("warn", "At least one of the entities has hstore column, but the 'hstore' extension cannot be installed automatically. Please install it manually using superuser rights"); + } + if (hasGeometryColumns) + try { + await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "postgis"`); + } catch (_) { + logger.log("warn", "At least one of the entities has a geometry column, but the 'postgis' extension cannot be installed automatically. Please install it manually using superuser rights"); + } + if (hasCubeColumns) + try { + await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "cube"`); + } catch (_) { + logger.log("warn", "At least one of the entities has a cube column, but the 'cube' extension cannot be installed automatically. Please install it manually using superuser rights"); + } + if (hasLtreeColumns) + try { + await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "ltree"`); + } catch (_) { + logger.log("warn", "At least one of the entities has a cube column, but the 'ltree' extension cannot be installed automatically. Please install it manually using superuser rights"); + } + if (hasExclusionConstraints) + try { + // The btree_gist extension provides operator support in PostgreSQL exclusion constraints + await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "btree_gist"`); + } catch (_) { + logger.log("warn", "At least one of the entities has an exclusion constraint, but the 'btree_gist' extension cannot be installed automatically. Please install it manually using superuser rights"); + } + } + + protected async checkMetadataForExtensions() { const hasUuidColumns = this.connection.entityMetadatas.some(metadata => { return metadata.generatedColumns.filter(column => column.generationStrategy === "uuid").length > 0; }); @@ -316,60 +392,23 @@ export class PostgresDriver implements Driver { const hasGeometryColumns = this.connection.entityMetadatas.some(metadata => { return metadata.columns.filter(column => this.spatialTypes.indexOf(column.type) >= 0).length > 0; }); + const hasLtreeColumns = this.connection.entityMetadatas.some(metadata => { + return metadata.columns.filter(column => column.type === "ltree").length > 0; + }); const hasExclusionConstraints = this.connection.entityMetadatas.some(metadata => { return metadata.exclusions.length > 0; }); - if (hasUuidColumns || hasCitextColumns || hasHstoreColumns || hasGeometryColumns || hasCubeColumns || hasExclusionConstraints) { - await Promise.all([this.master, ...this.slaves].map(pool => { - return new Promise((ok, fail) => { - pool.connect(async (err: any, connection: any, release: Function) => { - const { logger } = this.connection; - if (err) return fail(err); - if (hasUuidColumns) - try { - await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "${this.options.uuidExtension || "uuid-ossp"}"`); - } catch (_) { - logger.log("warn", `At least one of the entities has uuid column, but the '${this.options.uuidExtension || "uuid-ossp"}' extension cannot be installed automatically. Please install it manually using superuser rights, or select another uuid extension.`); - } - if (hasCitextColumns) - try { - await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "citext"`); - } catch (_) { - logger.log("warn", "At least one of the entities has citext column, but the 'citext' extension cannot be installed automatically. Please install it manually using superuser rights"); - } - if (hasHstoreColumns) - try { - await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "hstore"`); - } catch (_) { - logger.log("warn", "At least one of the entities has hstore column, but the 'hstore' extension cannot be installed automatically. Please install it manually using superuser rights"); - } - if (hasGeometryColumns) - try { - await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "postgis"`); - } catch (_) { - logger.log("warn", "At least one of the entities has a geometry column, but the 'postgis' extension cannot be installed automatically. Please install it manually using superuser rights"); - } - if (hasCubeColumns) - try { - await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "cube"`); - } catch (_) { - logger.log("warn", "At least one of the entities has a cube column, but the 'cube' extension cannot be installed automatically. Please install it manually using superuser rights"); - } - if (hasExclusionConstraints) - try { - // The btree_gist extension provides operator support in PostgreSQL exclusion constraints - await this.executeQuery(connection, `CREATE EXTENSION IF NOT EXISTS "btree_gist"`); - } catch (_) { - logger.log("warn", "At least one of the entities has an exclusion constraint, but the 'btree_gist' extension cannot be installed automatically. Please install it manually using superuser rights"); - } - release(); - ok(); - }); - }); - })); - } - return Promise.resolve(); + return { + hasUuidColumns, + hasCitextColumns, + hasHstoreColumns, + hasCubeColumns, + hasGeometryColumns, + hasLtreeColumns, + hasExclusionConstraints, + hasExtensions: hasUuidColumns || hasCitextColumns || hasHstoreColumns || hasGeometryColumns || hasCubeColumns || hasLtreeColumns || hasExclusionConstraints, + }; } /** @@ -395,7 +434,7 @@ export class PostgresDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master") { + createQueryRunner(mode: ReplicationMode): QueryRunner { return new PostgresQueryRunner(this, mode); } @@ -458,6 +497,8 @@ export class PostgresDriver implements Driver { } return `(${value.join(",")})`; + } else if (columnMetadata.type === "ltree") { + return value.split(".").filter(Boolean).join(".").replace(/[\s]+/g, "_"); } else if ( ( columnMetadata.type === "enum" @@ -693,7 +734,7 @@ export class PostgresDriver implements Driver { } if (typeof defaultValue === "number") { - return "" + defaultValue; + return `'${defaultValue}'`; } else if (typeof defaultValue === "boolean") { return defaultValue === true ? "true" : "false"; @@ -833,8 +874,8 @@ export class PostgresDriver implements Driver { || tableColumn.type !== this.normalizeType(columnMetadata) || tableColumn.length !== columnMetadata.length || tableColumn.precision !== columnMetadata.precision - || tableColumn.scale !== columnMetadata.scale - // || tableColumn.comment !== columnMetadata.comment // todo + || (columnMetadata.scale !== undefined && tableColumn.scale !== columnMetadata.scale) + || (tableColumn.comment || "") !== columnMetadata.comment || (!tableColumn.isGenerated && this.lowerDefaultValueIfNecessary(this.normalizeDefault(columnMetadata)) !== tableColumn.default) // we included check for generated here, because generated columns already can have default values || tableColumn.isPrimary !== columnMetadata.isPrimary || tableColumn.isNullable !== columnMetadata.isNullable @@ -869,6 +910,13 @@ export class PostgresDriver implements Driver { return true; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return false; + } + get uuidGenerator(): string { return this.options.uuidExtension === "pgcrypto" ? "gen_random_uuid()" : "uuid_generate_v4()"; } @@ -922,10 +970,11 @@ export class PostgresDriver implements Driver { */ protected async createPool(options: PostgresConnectionOptions, credentials: PostgresConnectionCredentialsOptions): Promise { - credentials = Object.assign({}, credentials, DriverUtils.buildDriverOptions(credentials)); // todo: do it better way + credentials = Object.assign({}, credentials); // build connection options for the driver const connectionOptions = Object.assign({}, { + connectionString: credentials.url, host: credentials.host, user: credentials.username, password: credentials.password, @@ -949,6 +998,15 @@ export class PostgresDriver implements Driver { return new Promise((ok, fail) => { pool.connect((err: any, connection: any, release: Function) => { if (err) return fail(err); + + if (options.logNotifications) { + connection.on("notice", (msg: any) => { + msg && this.connection.logger.log("info", msg.message); + }); + connection.on("notification", (msg: any) => { + msg && this.connection.logger.log("info", `Received NOTIFY on channel ${msg.channel}: ${msg.payload}.`); + }); + } release(); ok(pool); }); @@ -978,113 +1036,3 @@ export class PostgresDriver implements Driver { } } - -abstract class PostgresWrapper extends PostgresDriver { - options: any; - - abstract createQueryRunner(mode: "master"|"slave"): any; -} - -/** - * Organizes communication with PostgreSQL DBMS. - */ -export class AuroraDataApiPostgresDriver extends PostgresWrapper { - - // ------------------------------------------------------------------------- - // Public Properties - // ------------------------------------------------------------------------- - - /** - * Connection used by driver. - */ - connection: Connection; - - /** - * Aurora Data API underlying library. - */ - DataApiDriver: any; - - client: any; - - // ------------------------------------------------------------------------- - // Public Implemented Properties - // ------------------------------------------------------------------------- - - /** - * Connection options. - */ - options: AuroraDataApiPostgresConnectionOptions; - - /** - * Master database used to perform all write queries. - */ - database?: string; - - // ------------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------------- - - constructor(connection: Connection) { - super(); - this.connection = connection; - this.options = connection.options as AuroraDataApiPostgresConnectionOptions; - this.isReplicated = false; - - // load data-api package - this.loadDependencies(); - - this.client = new this.DataApiDriver( - this.options.region, - this.options.secretArn, - this.options.resourceArn, - this.options.database, - (query: string, parameters?: any[]) => this.connection.logger.logQuery(query, parameters), - ); - } - - // ------------------------------------------------------------------------- - // Public Implemented Methods - // ------------------------------------------------------------------------- - - /** - * Performs connection to the database. - * Based on pooling options, it can either create connection immediately, - * either create a pool and create connection when needed. - */ - async connect(): Promise { - } - - /** - * Closes connection with database. - */ - async disconnect(): Promise { - } - - /** - * Creates a query runner used to execute database queries. - */ - createQueryRunner(mode: "master"|"slave" = "master") { - return new AuroraDataApiPostgresQueryRunner(this, mode); - } - - // ------------------------------------------------------------------------- - // Protected Methods - // ------------------------------------------------------------------------- - - /** - * If driver dependency is not given explicitly, then try to load it via "require". - */ - protected loadDependencies(): void { - const { pg } = PlatformTools.load("typeorm-aurora-data-api-driver"); - - this.DataApiDriver = pg; - } - - /** - * Executes given query. - */ - protected executeQuery(connection: any, query: string) { - return this.client.query(query); - } - -} diff --git a/src/driver/postgres/PostgresQueryRunner.ts b/src/driver/postgres/PostgresQueryRunner.ts index e4ce86a8b2e..8245d09f798 100644 --- a/src/driver/postgres/PostgresQueryRunner.ts +++ b/src/driver/postgres/PostgresQueryRunner.ts @@ -1,4 +1,3 @@ -import {PromiseUtils} from "../../"; import {ObjectLiteral} from "../../common/ObjectLiteral"; import {QueryFailedError} from "../../error/QueryFailedError"; import {QueryRunnerAlreadyReleasedError} from "../../error/QueryRunnerAlreadyReleasedError"; @@ -22,6 +21,8 @@ import {OrmUtils} from "../../util/OrmUtils"; import {Query} from "../Query"; import {IsolationLevel} from "../types/IsolationLevel"; import {PostgresDriver} from "./PostgresDriver"; +import {ReplicationMode} from "../types/ReplicationMode"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; /** * Runs queries on a single postgres database connection. @@ -55,7 +56,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner // Constructor // ------------------------------------------------------------------------- - constructor(driver: PostgresDriver, mode: "master"|"slave" = "master") { + constructor(driver: PostgresDriver, mode: ReplicationMode) { super(); this.driver = driver; this.connection = driver.connection; @@ -79,10 +80,17 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner return this.databaseConnectionPromise; if (this.mode === "slave" && this.driver.isReplicated) { - this.databaseConnectionPromise = this.driver.obtainSlaveConnection().then(([ connection, release]: any[]) => { + this.databaseConnectionPromise = this.driver.obtainSlaveConnection().then(([connection, release]: any[]) => { this.driver.connectedQueryRunners.push(this); this.databaseConnection = connection; - this.releaseCallback = release; + + const onErrorCallback = () => this.release(); + this.releaseCallback = () => { + this.databaseConnection.removeListener("error", onErrorCallback); + release(); + }; + this.databaseConnection.on("error", onErrorCallback); + return this.databaseConnection; }); @@ -90,7 +98,14 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner this.databaseConnectionPromise = this.driver.obtainMasterConnection().then(([connection, release]: any[]) => { this.driver.connectedQueryRunners.push(this); this.databaseConnection = connection; - this.releaseCallback = release; + + const onErrorCallback = () => this.release(); + this.releaseCallback = () => { + this.databaseConnection.removeListener("error", onErrorCallback); + release(); + }; + this.databaseConnection.on("error", onErrorCallback); + return this.databaseConnection; }); } @@ -103,6 +118,10 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * You cannot use query runner methods once its released. */ release(): Promise { + if (this.isReleased) { + return Promise.resolve(); + } + this.isReleased = true; if (this.releaseCallback) this.releaseCallback(); @@ -120,11 +139,19 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner if (this.isTransactionActive) throw new TransactionAlreadyStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = true; await this.query("START TRANSACTION"); if (isolationLevel) { await this.query("SET TRANSACTION ISOLATION LEVEL " + isolationLevel); } + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -135,8 +162,16 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("COMMIT"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -147,52 +182,51 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("ROLLBACK"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** * Executes a given SQL query. */ - query(query: string, parameters?: any[]): Promise { + async query(query: string, parameters?: any[]): Promise { if (this.isReleased) throw new QueryRunnerAlreadyReleasedError(); - return new Promise(async (ok, fail) => { - try { - const databaseConnection = await this.connect(); - this.driver.connection.logger.logQuery(query, parameters, this); - const queryStartTime = +new Date(); - - databaseConnection.query(query, parameters, (err: any, result: any) => { - - // log slow queries if maxQueryExecution time is set - const maxQueryExecutionTime = this.driver.connection.options.maxQueryExecutionTime; - const queryEndTime = +new Date(); - const queryExecutionTime = queryEndTime - queryStartTime; - if (maxQueryExecutionTime && queryExecutionTime > maxQueryExecutionTime) - this.driver.connection.logger.logQuerySlow(queryExecutionTime, query, parameters, this); - - if (err) { - this.driver.connection.logger.logQueryError(err, query, parameters, this); - fail(new QueryFailedError(query, parameters, err)); - } else { - switch (result.command) { - case "DELETE": - case "UPDATE": - // for UPDATE and DELETE query additionally return number of affected rows - ok([result.rows, result.rowCount]); - break; - default: - ok(result.rows); - } - } - }); + const databaseConnection = await this.connect(); - } catch (err) { - fail(err); + this.driver.connection.logger.logQuery(query, parameters, this); + try { + const queryStartTime = +new Date(); + const result = await databaseConnection.query(query, parameters); + // log slow queries if maxQueryExecution time is set + const maxQueryExecutionTime = this.driver.connection.options.maxQueryExecutionTime; + const queryEndTime = +new Date(); + const queryExecutionTime = queryEndTime - queryStartTime; + if (maxQueryExecutionTime && queryExecutionTime > maxQueryExecutionTime) + this.driver.connection.logger.logQuerySlow(queryExecutionTime, query, parameters, this); + + switch (result.command) { + case "DELETE": + case "UPDATE": + // for UPDATE and DELETE query additionally return number of affected rows + return [result.rows, result.rowCount]; + break; + default: + return result.rows; } - }); + } catch (err) { + this.driver.connection.logger.logQueryError(err, query, parameters, this); + throw new QueryFailedError(query, parameters, err); + } } /** @@ -546,6 +580,12 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner downQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} DROP CONSTRAINT "${uniqueConstraint.name}"`)); } + // create column's comment + if (column.comment) { + upQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${column.name}" IS ${this.escapeComment(column.comment)}`)); + downQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${column.name}" IS ${this.escapeComment(column.comment)}`)); + } + await this.executeQueries(upQueries, downQueries); clonedTable.addColumn(column); @@ -556,7 +596,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Creates a new columns from the column in the table. */ async addColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.addColumn(tableOrName, column)); + for (const column of columns) { + await this.addColumn(tableOrName, column); + } } /** @@ -763,8 +805,8 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner } if (oldColumn.comment !== newColumn.comment) { - upQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${oldColumn.name}" IS '${newColumn.comment}'`)); - downQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${newColumn.name}" IS '${oldColumn.comment}'`)); + upQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${oldColumn.name}" IS ${this.escapeComment(newColumn.comment)}`)); + downQueries.push(new Query(`COMMENT ON COLUMN ${this.escapePath(table)}."${newColumn.name}" IS ${this.escapeComment(oldColumn.comment)}`)); } if (newColumn.isPrimary !== oldColumn.isPrimary) { @@ -874,7 +916,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Changes a column in the table. */ async changeColumns(tableOrName: Table|string, changedColumns: { newColumn: TableColumn, oldColumn: TableColumn }[]): Promise { - await PromiseUtils.runInSequence(changedColumns, changedColumn => this.changeColumn(tableOrName, changedColumn.oldColumn, changedColumn.newColumn)); + for (const {oldColumn, newColumn} of changedColumns) { + await this.changeColumn(tableOrName, oldColumn, newColumn); + } } /** @@ -958,7 +1002,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Drops the columns in the table. */ async dropColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.dropColumn(tableOrName, column)); + for (const column of columns) { + await this.dropColumn(tableOrName, column); + } } /** @@ -1047,7 +1093,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Creates new unique constraints. */ async createUniqueConstraints(tableOrName: Table|string, uniqueConstraints: TableUnique[]): Promise { - await PromiseUtils.runInSequence(uniqueConstraints, uniqueConstraint => this.createUniqueConstraint(tableOrName, uniqueConstraint)); + for (const uniqueConstraint of uniqueConstraints) { + await this.createUniqueConstraint(tableOrName, uniqueConstraint); + } } /** @@ -1069,7 +1117,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Drops unique constraints. */ async dropUniqueConstraints(tableOrName: Table|string, uniqueConstraints: TableUnique[]): Promise { - await PromiseUtils.runInSequence(uniqueConstraints, uniqueConstraint => this.dropUniqueConstraint(tableOrName, uniqueConstraint)); + for (const uniqueConstraint of uniqueConstraints) { + await this.dropUniqueConstraint(tableOrName, uniqueConstraint); + } } /** @@ -1186,7 +1236,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Creates a new foreign keys. */ async createForeignKeys(tableOrName: Table|string, foreignKeys: TableForeignKey[]): Promise { - await PromiseUtils.runInSequence(foreignKeys, foreignKey => this.createForeignKey(tableOrName, foreignKey)); + for (const foreignKey of foreignKeys) { + await this.createForeignKey(tableOrName, foreignKey); + } } /** @@ -1208,7 +1260,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Drops a foreign keys from the table. */ async dropForeignKeys(tableOrName: Table|string, foreignKeys: TableForeignKey[]): Promise { - await PromiseUtils.runInSequence(foreignKeys, foreignKey => this.dropForeignKey(tableOrName, foreignKey)); + for (const foreignKey of foreignKeys) { + await this.dropForeignKey(tableOrName, foreignKey); + } } /** @@ -1231,7 +1285,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Creates a new indices */ async createIndices(tableOrName: Table|string, indices: TableIndex[]): Promise { - await PromiseUtils.runInSequence(indices, index => this.createIndex(tableOrName, index)); + for (const index of indices) { + await this.createIndex(tableOrName, index); + } } /** @@ -1253,7 +1309,9 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner * Drops an indices from the table. */ async dropIndices(tableOrName: Table|string, indices: TableIndex[]): Promise { - await PromiseUtils.runInSequence(indices, index => this.dropIndex(tableOrName, index)); + for (const index of indices) { + await this.dropIndex(tableOrName, index); + } } /** @@ -1359,7 +1417,30 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner return `("table_schema" = '${schema}' AND "table_name" = '${name}')`; }).join(" OR "); const tablesSql = `SELECT * FROM "information_schema"."tables" WHERE ` + tablesCondition; - const columnsSql = `SELECT *, ('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype" AS "regtype" FROM "information_schema"."columns" WHERE ` + tablesCondition; + + /** + * Uses standard SQL information_schema.columns table and postgres-specific + * pg_catalog.pg_attribute table to get column information. + * @see https://stackoverflow.com/a/19541865 + */ + const columnsSql = ` + SELECT columns.*, + pg_catalog.col_description(('"' || table_catalog || '"."' || table_schema || '"."' || table_name || '"')::regclass::oid, ordinal_position) AS description, + ('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype" AS "regtype", + pg_catalog.format_type("col_attr"."atttypid", "col_attr"."atttypmod") AS "format_type" + FROM "information_schema"."columns" + LEFT JOIN "pg_catalog"."pg_attribute" AS "col_attr" + ON "col_attr"."attname" = "columns"."column_name" + AND "col_attr"."attrelid" = ( + SELECT + "cls"."oid" FROM "pg_catalog"."pg_class" AS "cls" + LEFT JOIN "pg_catalog"."pg_namespace" AS "ns" + ON "ns"."oid" = "cls"."relnamespace" + WHERE "cls"."relname" = "columns"."table_name" + AND "ns"."nspname" = "columns"."table_schema" + ) + WHERE + ` + tablesCondition; const constraintsCondition = tableNames.map(tableName => { let [schema, name] = tableName.split("."); @@ -1377,7 +1458,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner `INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" ` + `INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" ` + `LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") ` + - `WHERE "t"."relkind" = 'r' AND (${constraintsCondition})`; + `WHERE "t"."relkind" IN ('r', 'p') AND (${constraintsCondition})`; const indicesSql = `SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", ` + `CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", ` + @@ -1389,7 +1470,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner `INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" ` + `INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" ` + `LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" ` + - `WHERE "t"."relkind" = 'r' AND "cnst"."contype" IS NULL AND (${constraintsCondition})`; + `WHERE "t"."relkind" IN ('r', 'p') AND "cnst"."contype" IS NULL AND (${constraintsCondition})`; const foreignKeysCondition = tableNames.map(tableName => { let [schema, name] = tableName.split("."); @@ -1399,6 +1480,10 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner } return `("ns"."nspname" = '${schema}' AND "cl"."relname" = '${name}')`; }).join(" OR "); + + const hasRelispartitionColumn = await this.hasSupportForPartitionedTables(); + const isPartitionCondition = hasRelispartitionColumn ? ` AND "cl"."relispartition" = 'f'` : ""; + const foreignKeysSql = `SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", ` + `"ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", ` + `"con"."confupdtype" AS "on_update", "con"."condeferrable" AS "deferrable", "con"."condeferred" AS "deferred" ` + @@ -1414,7 +1499,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner `WHERE "con1"."contype" = 'f' AND (${foreignKeysCondition}) ` + `) "con" ` + `INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" ` + - `INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" ` + + `INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" ${isPartitionCondition}` + `INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" ` + `INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent"`; const [dbTables, dbColumns, dbConstraints, dbIndices, dbForeignKeys]: ObjectLiteral[][] = await Promise.all([ @@ -1526,9 +1611,17 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner } // check only columns that have length property - if (this.driver.withLengthColumnTypes.indexOf(tableColumn.type as ColumnType) !== -1 && dbColumn["character_maximum_length"]) { - const length = dbColumn["character_maximum_length"].toString(); + if (this.driver.withLengthColumnTypes.indexOf(tableColumn.type as ColumnType) !== -1) { + let length; + if (tableColumn.isArray) { + const match = /\((\d+)\)/.exec(dbColumn["format_type"]); + length = match ? match[1] : undefined; + } else if (dbColumn["character_maximum_length"]) { + length = dbColumn["character_maximum_length"].toString(); + } + if (length) { tableColumn.length = !this.isDefaultColumnLength(table, tableColumn, length) ? length : ""; + } } tableColumn.isNullable = dbColumn["is_nullable"] === "YES"; tableColumn.isPrimary = !!columnConstraints.find(constraint => constraint["constraint_type"] === "PRIMARY"); @@ -1550,10 +1643,11 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner tableColumn.generationStrategy = "uuid"; } else { tableColumn.default = dbColumn["column_default"].replace(/::.*/, ""); + tableColumn.default = tableColumn.default.replace(/^(-?\d+)$/, "'$1'"); } } - tableColumn.comment = ""; // dbColumn["COLUMN_COMMENT"]; + tableColumn.comment = dbColumn["description"] == null ? undefined : dbColumn["description"]; if (dbColumn["character_set_name"]) tableColumn.charset = dbColumn["character_set_name"]; if (dbColumn["collation_name"]) @@ -1728,6 +1822,10 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner sql += `)`; + table.columns + .filter(it => it.comment) + .forEach(it => sql += `; COMMENT ON COLUMN ${this.escapePath(table)}."${it.name}" IS ${this.escapeComment(it.comment)}`); + return new Query(sql); } @@ -1977,11 +2075,14 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner tableName = table.name.split(".")[1]; } + let seqName = `${tableName}_${columnName}_seq`; + if (seqName.length > this.connection.driver.maxAliasLength!) // note doesn't yet handle corner cases where .length differs from number of UTF-8 bytes + seqName=`${tableName.substring(0,29)}_${columnName.substring(0,Math.max(29,63-tableName.length-5))}_seq`; + if (schema && schema !== currentSchema && !skipSchema) { - return disableEscape ? `${schema}.${tableName}_${columnName}_seq` : `"${schema}"."${tableName}_${columnName}_seq"`; - + return disableEscape ? `${schema}.${seqName}` : `"${schema}"."${seqName}"`; } else { - return disableEscape ? `${tableName}_${columnName}_seq` : `"${tableName}_${columnName}_seq"`; + return disableEscape ? `${seqName}` : `"${seqName}"`; } } @@ -2025,6 +2126,21 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner }; } + /** + * Escapes a given comment so it's safe to include in a query. + */ + protected escapeComment(comment?: string) { + if (comment === undefined || comment.length === 0) { + return "NULL"; + } + + comment = comment + .replace("'", "''") + .replace("\0", ""); // Null bytes aren't allowed in comments + + return `'${comment}'`; + } + /** * Escapes given table or view path. */ @@ -2090,4 +2206,11 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner return c; } + /** + * Checks if the PostgreSQL server has support for partitioned tables + */ + protected async hasSupportForPartitionedTables() { + const result = await this.query(`SELECT TRUE FROM information_schema.columns WHERE table_name = 'pg_class' and column_name = 'relispartition'`); + return result.length ? true : false; + } } diff --git a/src/driver/react-native/ReactNativeDriver.ts b/src/driver/react-native/ReactNativeDriver.ts index 803ac93b794..232fa676426 100644 --- a/src/driver/react-native/ReactNativeDriver.ts +++ b/src/driver/react-native/ReactNativeDriver.ts @@ -5,10 +5,11 @@ import {QueryRunner} from "../../query-runner/QueryRunner"; import {Connection} from "../../connection/Connection"; import {DriverOptionNotSetError} from "../../error/DriverOptionNotSetError"; import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError"; +import {ReplicationMode} from "../types/ReplicationMode"; export class ReactNativeDriver extends AbstractSqliteDriver { options: ReactNativeConnectionOptions; - + // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- @@ -28,7 +29,7 @@ export class ReactNativeDriver extends AbstractSqliteDriver { // load sqlite package this.loadDependencies(); } - + // ------------------------------------------------------------------------- // Public Methods @@ -43,17 +44,17 @@ export class ReactNativeDriver extends AbstractSqliteDriver { this.databaseConnection.close(ok, fail); }); } - + /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master"): QueryRunner { + createQueryRunner(mode: ReplicationMode): QueryRunner { if (!this.queryRunner) this.queryRunner = new ReactNativeQueryRunner(this); return this.queryRunner; } - + // ------------------------------------------------------------------------- // Protected Methods // ------------------------------------------------------------------------- @@ -95,4 +96,4 @@ export class ReactNativeDriver extends AbstractSqliteDriver { throw new DriverPackageNotInstalledError("React-Native", "react-native-sqlite-storage"); } } -} \ No newline at end of file +} diff --git a/src/driver/sap/SapDriver.ts b/src/driver/sap/SapDriver.ts index 9479277503d..958e199981c 100644 --- a/src/driver/sap/SapDriver.ts +++ b/src/driver/sap/SapDriver.ts @@ -11,6 +11,7 @@ import {DataTypeDefaults} from "../types/DataTypeDefaults"; import {MappedColumnTypes} from "../types/MappedColumnTypes"; import {SapConnectionOptions} from "./SapConnectionOptions"; import {SapQueryRunner} from "./SapQueryRunner"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with SAP Hana DBMS. @@ -273,7 +274,7 @@ export class SapDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master") { + createQueryRunner(mode: ReplicationMode) { return new SapQueryRunner(this, mode); } @@ -624,6 +625,13 @@ export class SapDriver implements Driver { return false; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return true; + } + /** * Creates an escaped parameter. */ diff --git a/src/driver/sap/SapQueryRunner.ts b/src/driver/sap/SapQueryRunner.ts index 9e839a44909..c3dc3485b1b 100644 --- a/src/driver/sap/SapQueryRunner.ts +++ b/src/driver/sap/SapQueryRunner.ts @@ -2,7 +2,7 @@ import {ObjectLiteral} from "../../common/ObjectLiteral"; import {QueryRunnerAlreadyReleasedError} from "../../error/QueryRunnerAlreadyReleasedError"; import {TransactionAlreadyStartedError} from "../../error/TransactionAlreadyStartedError"; import {TransactionNotStartedError} from "../../error/TransactionNotStartedError"; -import {ColumnType, PromiseUtils, QueryFailedError} from "../../index"; +import {ColumnType, QueryFailedError} from "../../index"; import {ReadStream} from "../../platform/PlatformTools"; import {BaseQueryRunner} from "../../query-runner/BaseQueryRunner"; import {QueryRunner} from "../../query-runner/QueryRunner"; @@ -20,6 +20,8 @@ import {OrmUtils} from "../../util/OrmUtils"; import {Query} from "../Query"; import {IsolationLevel} from "../types/IsolationLevel"; import {SapDriver} from "./SapDriver"; +import {ReplicationMode} from "../types/ReplicationMode"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; /** * Runs queries on a single SQL Server database connection. @@ -55,7 +57,7 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { // Constructor // ------------------------------------------------------------------------- - constructor(driver: SapDriver, mode: "master"|"slave" = "master") { + constructor(driver: SapDriver, mode: ReplicationMode) { super(); this.driver = driver; this.connection = driver.connection; @@ -91,7 +93,7 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { return this.driver.master.release(this.databaseConnection); } - return Promise.resolve(); + return Promise.resolve(); } /** @@ -104,10 +106,18 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { if (this.isTransactionActive) throw new TransactionAlreadyStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + this.isTransactionActive = true; if (isolationLevel) { await this.query(`SET TRANSACTION ISOLATION LEVEL ${isolationLevel || ""}`); } + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -121,8 +131,16 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("COMMIT"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -136,8 +154,16 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("ROLLBACK"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -633,7 +659,9 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { * Creates a new columns from the column in the table. */ async addColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.addColumn(tableOrName, column)); + for (const column of columns) { + await this.addColumn(tableOrName, column); + } } /** @@ -869,7 +897,9 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { * Changes a column in the table. */ async changeColumns(tableOrName: Table|string, changedColumns: { newColumn: TableColumn, oldColumn: TableColumn }[]): Promise { - await PromiseUtils.runInSequence(changedColumns, changedColumn => this.changeColumn(tableOrName, changedColumn.oldColumn, changedColumn.newColumn)); + for (const {oldColumn, newColumn} of changedColumns) { + await this.changeColumn(tableOrName, oldColumn, newColumn) + } } /** @@ -989,7 +1019,9 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { * Drops the columns in the table. */ async dropColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { - await PromiseUtils.runInSequence(columns, column => this.dropColumn(tableOrName, column)); + for (const column of columns) { + await this.dropColumn(tableOrName, column); + } } /** @@ -1823,7 +1855,7 @@ export class SapQueryRunner extends BaseQueryRunner implements QueryRunner { let indexType = ""; if (index.isUnique) { indexType += "UNIQUE "; - } + } if (index.isFulltext) { indexType += "FULLTEXT "; } diff --git a/src/driver/sqlite-abstract/AbstractSqliteDriver.ts b/src/driver/sqlite-abstract/AbstractSqliteDriver.ts index 09cb044e178..22d28cc35ec 100644 --- a/src/driver/sqlite-abstract/AbstractSqliteDriver.ts +++ b/src/driver/sqlite-abstract/AbstractSqliteDriver.ts @@ -13,6 +13,7 @@ import {BaseConnectionOptions} from "../../connection/BaseConnectionOptions"; import {EntityMetadata} from "../../metadata/EntityMetadata"; import {OrmUtils} from "../../util/OrmUtils"; import {ApplyValueTransformers} from "../../util/ApplyValueTransformers"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with sqlite DBMS. @@ -130,12 +131,31 @@ export abstract class AbstractSqliteDriver implements Driver { /** * Gets list of column data types that support precision by a driver. */ - withPrecisionColumnTypes: ColumnType[] = []; + withPrecisionColumnTypes: ColumnType[] = [ + "real", + "double", + "double precision", + "float", + "real", + "numeric", + "decimal", + "date", + "time", + "datetime" + ]; /** * Gets list of column data types that support scale by a driver. */ - withScaleColumnTypes: ColumnType[] = []; + withScaleColumnTypes: ColumnType[] = [ + "real", + "double", + "double precision", + "float", + "real", + "numeric", + "decimal", + ]; /** * Orm has special columns and we need to know what database column types should be for those types. @@ -195,7 +215,7 @@ export abstract class AbstractSqliteDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - abstract createQueryRunner(mode: "master"|"slave"): QueryRunner; + abstract createQueryRunner(mode: ReplicationMode): QueryRunner; // ------------------------------------------------------------------------- // Public Methods @@ -507,11 +527,13 @@ export abstract class AbstractSqliteDriver implements Driver { /** * Creates generated map of values generated or returned by database after INSERT query. */ - createGeneratedMap(metadata: EntityMetadata, insertResult: any) { + createGeneratedMap(metadata: EntityMetadata, insertResult: any, entityIndex: number, entityNum: number) { const generatedMap = metadata.generatedColumns.reduce((map, generatedColumn) => { let value: any; if (generatedColumn.generationStrategy === "increment" && insertResult) { - value = insertResult; + // NOTE: When INSERT statement is successfully completed, the last inserted row ID is returned. + // see also: SqliteQueryRunner.query() + value = insertResult - entityNum + entityIndex + 1; // } else if (generatedColumn.generationStrategy === "uuid") { // value = insertValue[generatedColumn.databaseName]; } @@ -575,6 +597,13 @@ export abstract class AbstractSqliteDriver implements Driver { return false; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return false; + } + /** * Creates an escaped parameter. */ diff --git a/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts b/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts index c361695c2cf..ce9eb957962 100644 --- a/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts +++ b/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts @@ -8,6 +8,7 @@ import {Table} from "../../schema-builder/table/Table"; import {TableIndex} from "../../schema-builder/table/TableIndex"; import {TableForeignKey} from "../../schema-builder/table/TableForeignKey"; import {View} from "../../schema-builder/view/View"; +import { BroadcasterResult } from "../../subscriber/BroadcasterResult"; import {Query} from "../Query"; import {AbstractSqliteDriver} from "./AbstractSqliteDriver"; import {ReadStream} from "../../platform/PlatformTools"; @@ -70,8 +71,6 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen if (this.isTransactionActive) throw new TransactionAlreadyStartedError(); - this.isTransactionActive = true; - if (isolationLevel) { if (isolationLevel !== "READ UNCOMMITTED" && isolationLevel !== "SERIALIZABLE") { throw new Error(`SQLite only supports SERIALIZABLE and READ UNCOMMITTED isolation`); @@ -84,7 +83,17 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen } } + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + + this.isTransactionActive = true; + await this.query("BEGIN TRANSACTION"); + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -95,8 +104,16 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("COMMIT"); this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -107,8 +124,17 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + await this.query("ROLLBACK"); + this.isTransactionActive = false; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); } /** @@ -825,17 +851,31 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen } } - // parse datatype and attempt to retrieve length + // parse datatype and attempt to retrieve length, precision and scale let pos = tableColumn.type.indexOf("("); if (pos !== -1) { - let dataType = tableColumn.type.substr(0, pos); + const fullType = tableColumn.type; + let dataType = fullType.substr(0, pos); if (!!this.driver.withLengthColumnTypes.find(col => col === dataType)) { - let len = parseInt(tableColumn.type.substring(pos + 1, tableColumn.type.length - 1)); + let len = parseInt(fullType.substring(pos + 1, fullType.length - 1)); if (len) { tableColumn.length = len.toString(); tableColumn.type = dataType; // remove the length part from the datatype } } + if (!!this.driver.withPrecisionColumnTypes.find(col => col === dataType)) { + const re = new RegExp(`^${dataType}\\((\\d+),?\\s?(\\d+)?\\)`); + const matches = fullType.match(re); + if (matches && matches[1]) { + tableColumn.precision = +matches[1]; + } + if (!!this.driver.withScaleColumnTypes.find(col => col === dataType)) { + if (matches && matches[2]) { + tableColumn.scale = +matches[2]; + } + } + tableColumn.type = dataType; // remove the precision/scale part from the datatype + } } return tableColumn; diff --git a/src/driver/sqlite/SqliteConnectionOptions.ts b/src/driver/sqlite/SqliteConnectionOptions.ts index 9155047e4fc..8e092b9c697 100644 --- a/src/driver/sqlite/SqliteConnectionOptions.ts +++ b/src/driver/sqlite/SqliteConnectionOptions.ts @@ -20,4 +20,24 @@ export interface SqliteConnectionOptions extends BaseConnectionOptions { */ readonly key?: string; -} \ No newline at end of file + /** + * In your SQLite application when you perform parallel writes its common to face SQLITE_BUSY error. + * This error indicates that SQLite failed to write to the database file since someone else already writes into it. + * Since SQLite cannot handle parallel saves this error cannot be avoided. + * + * To simplify life's of those who have this error this particular option sets a timeout within which ORM will try + * to perform requested write operation again and again until it receives SQLITE_BUSY error. + * + * Enabling WAL can improve your app performance and face less SQLITE_BUSY issues. + * Time in milliseconds. + */ + readonly busyErrorRetry?: number; + + /** + * Enables WAL mode. By default its disabled. + * + * @see https://www.sqlite.org/wal.html + */ + readonly enableWAL?: boolean; + +} diff --git a/src/driver/sqlite/SqliteDriver.ts b/src/driver/sqlite/SqliteDriver.ts index cdd62261279..caab6aeebab 100644 --- a/src/driver/sqlite/SqliteDriver.ts +++ b/src/driver/sqlite/SqliteDriver.ts @@ -1,3 +1,5 @@ +import mkdirp from 'mkdirp'; +import path from 'path'; import { DriverPackageNotInstalledError } from "../../error/DriverPackageNotInstalledError"; import { SqliteQueryRunner } from "./SqliteQueryRunner"; import { DriverOptionNotSetError } from "../../error/DriverOptionNotSetError"; @@ -7,6 +9,7 @@ import { SqliteConnectionOptions } from "./SqliteConnectionOptions"; import { ColumnType } from "../types/ColumnTypes"; import { QueryRunner } from "../../query-runner/QueryRunner"; import { AbstractSqliteDriver } from "../sqlite-abstract/AbstractSqliteDriver"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with sqlite DBMS. @@ -63,7 +66,7 @@ export class SqliteDriver extends AbstractSqliteDriver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master" | "slave" = "master"): QueryRunner { + createQueryRunner(mode: ReplicationMode): QueryRunner { if (!this.queryRunner) this.queryRunner = new SqliteQueryRunner(this); @@ -105,6 +108,10 @@ export class SqliteDriver extends AbstractSqliteDriver { }); } + if (this.options.enableWAL) { + await run(`PRAGMA journal_mode = WAL;`); + } + // we need to enable foreign keys in sqlite to make sure all foreign key related features // working properly. this also makes onDelete to work with sqlite. await run(`PRAGMA foreign_keys = ON;`); @@ -132,10 +139,8 @@ export class SqliteDriver extends AbstractSqliteDriver { /** * Auto creates database directory if it does not exist. */ - protected createDatabaseDirectory(fullPath: string): Promise { - const mkdirp = PlatformTools.load("mkdirp"); - const path = PlatformTools.load("path"); - return mkdirp(path.dirname(fullPath)); + protected async createDatabaseDirectory(fullPath: string): Promise { + await mkdirp(path.dirname(fullPath)); } } diff --git a/src/driver/sqlite/SqliteQueryRunner.ts b/src/driver/sqlite/SqliteQueryRunner.ts index 6e5a55d8851..9e617866597 100644 --- a/src/driver/sqlite/SqliteQueryRunner.ts +++ b/src/driver/sqlite/SqliteQueryRunner.ts @@ -1,8 +1,10 @@ import {QueryRunnerAlreadyReleasedError} from "../../error/QueryRunnerAlreadyReleasedError"; import {QueryFailedError} from "../../error/QueryFailedError"; import {AbstractSqliteQueryRunner} from "../sqlite-abstract/AbstractSqliteQueryRunner"; +import {SqliteConnectionOptions} from "./SqliteConnectionOptions"; import {SqliteDriver} from "./SqliteDriver"; import {Broadcaster} from "../../subscriber/Broadcaster"; +import { ConnectionIsNotSetError } from '../../error/ConnectionIsNotSetError'; /** * Runs queries on a single sqlite database connection. @@ -36,10 +38,34 @@ export class SqliteQueryRunner extends AbstractSqliteQueryRunner { throw new QueryRunnerAlreadyReleasedError(); const connection = this.driver.connection; + const options = connection.options as SqliteConnectionOptions; + + if (!connection.isConnected){ + throw new ConnectionIsNotSetError('sqlite') + } return new Promise(async (ok, fail) => { + const databaseConnection = await this.connect(); + this.driver.connection.logger.logQuery(query, parameters, this); + const queryStartTime = +new Date(); + const isInsertQuery = query.substr(0, 11) === "INSERT INTO"; + + const execute = async () => { + if (isInsertQuery) { + databaseConnection.run(query, parameters, handler); + } else { + databaseConnection.all(query, parameters, handler); + } + }; + const handler = function (err: any, result: any) { + if (err && err.toString().indexOf("SQLITE_BUSY:") !== -1) { + if (typeof options.busyErrorRetry === "number" && options.busyErrorRetry > 0) { + setTimeout(execute, options.busyErrorRetry); + return; + } + } // log slow queries if maxQueryExecution time is set const maxQueryExecutionTime = connection.options.maxQueryExecutionTime; @@ -56,15 +82,7 @@ export class SqliteQueryRunner extends AbstractSqliteQueryRunner { } }; - const databaseConnection = await this.connect(); - this.driver.connection.logger.logQuery(query, parameters, this); - const queryStartTime = +new Date(); - const isInsertQuery = query.substr(0, 11) === "INSERT INTO"; - if (isInsertQuery) { - databaseConnection.run(query, parameters, handler); - } else { - databaseConnection.all(query, parameters, handler); - } + await execute(); }); } -} \ No newline at end of file +} diff --git a/src/driver/sqljs/SqljsDriver.ts b/src/driver/sqljs/SqljsDriver.ts index 1953bb6d158..ef06346ca81 100644 --- a/src/driver/sqljs/SqljsDriver.ts +++ b/src/driver/sqljs/SqljsDriver.ts @@ -9,6 +9,7 @@ import {PlatformTools} from "../../platform/PlatformTools"; import {EntityMetadata} from "../../metadata/EntityMetadata"; import {OrmUtils} from "../../util/OrmUtils"; import {ObjectLiteral} from "../../common/ObjectLiteral"; +import {ReplicationMode} from "../types/ReplicationMode"; // This is needed to satisfy the typescript compiler. interface Window { @@ -69,7 +70,7 @@ export class SqljsDriver extends AbstractSqliteDriver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master" | "slave" = "master"): QueryRunner { + createQueryRunner(mode: ReplicationMode): QueryRunner { if (!this.queryRunner) this.queryRunner = new SqljsQueryRunner(this); diff --git a/src/driver/sqljs/SqljsQueryRunner.ts b/src/driver/sqljs/SqljsQueryRunner.ts index 51a38ea0dcb..69a6349c476 100644 --- a/src/driver/sqljs/SqljsQueryRunner.ts +++ b/src/driver/sqljs/SqljsQueryRunner.ts @@ -53,6 +53,8 @@ export class SqljsQueryRunner extends AbstractSqliteQueryRunner { try { statement = databaseConnection.prepare(query); if (parameters) { + parameters = parameters.map(p => typeof p !== 'undefined' ? p : null); + statement.bind(parameters); } diff --git a/src/driver/sqlserver/SqlServerDriver.ts b/src/driver/sqlserver/SqlServerDriver.ts index 20e7f9e11d3..e6f1735f1fc 100644 --- a/src/driver/sqlserver/SqlServerDriver.ts +++ b/src/driver/sqlserver/SqlServerDriver.ts @@ -19,6 +19,7 @@ import {SqlServerConnectionCredentialsOptions} from "./SqlServerConnectionCreden import {EntityMetadata} from "../../metadata/EntityMetadata"; import {OrmUtils} from "../../util/OrmUtils"; import {ApplyValueTransformers} from "../../util/ApplyValueTransformers"; +import {ReplicationMode} from "../types/ReplicationMode"; /** * Organizes communication with SQL Server DBMS. @@ -296,7 +297,7 @@ export class SqlServerDriver implements Driver { /** * Creates a query runner used to execute database queries. */ - createQueryRunner(mode: "master"|"slave" = "master") { + createQueryRunner(mode: ReplicationMode) { return new SqlServerQueryRunner(this, mode); } @@ -640,6 +641,13 @@ export class SqlServerDriver implements Driver { return true; } + /** + * Returns true if driver supports fulltext indices. + */ + isFullTextColumnTypeSupported(): boolean { + return false; + } + /** * Creates an escaped parameter. */ diff --git a/src/driver/sqlserver/SqlServerQueryRunner.ts b/src/driver/sqlserver/SqlServerQueryRunner.ts index 84f00318cb1..b705517f0ab 100644 --- a/src/driver/sqlserver/SqlServerQueryRunner.ts +++ b/src/driver/sqlserver/SqlServerQueryRunner.ts @@ -1,27 +1,29 @@ -import { ObjectLiteral } from "../../common/ObjectLiteral"; -import { QueryFailedError } from "../../error/QueryFailedError"; -import { QueryRunnerAlreadyReleasedError } from "../../error/QueryRunnerAlreadyReleasedError"; -import { TransactionAlreadyStartedError } from "../../error/TransactionAlreadyStartedError"; -import { TransactionNotStartedError } from "../../error/TransactionNotStartedError"; -import { ColumnType, PromiseUtils } from "../../index"; -import { ReadStream } from "../../platform/PlatformTools"; -import { BaseQueryRunner } from "../../query-runner/BaseQueryRunner"; -import { QueryRunner } from "../../query-runner/QueryRunner"; -import { TableIndexOptions } from "../../schema-builder/options/TableIndexOptions"; -import { Table } from "../../schema-builder/table/Table"; -import { TableCheck } from "../../schema-builder/table/TableCheck"; -import { TableColumn } from "../../schema-builder/table/TableColumn"; -import { TableExclusion } from "../../schema-builder/table/TableExclusion"; -import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"; -import { TableIndex } from "../../schema-builder/table/TableIndex"; -import { TableUnique } from "../../schema-builder/table/TableUnique"; -import { View } from "../../schema-builder/view/View"; -import { Broadcaster } from "../../subscriber/Broadcaster"; -import { OrmUtils } from "../../util/OrmUtils"; -import { Query } from "../Query"; -import { IsolationLevel } from "../types/IsolationLevel"; -import { MssqlParameter } from "./MssqlParameter"; -import { SqlServerDriver } from "./SqlServerDriver"; +import {ObjectLiteral} from "../../common/ObjectLiteral"; +import {QueryFailedError} from "../../error/QueryFailedError"; +import {QueryRunnerAlreadyReleasedError} from "../../error/QueryRunnerAlreadyReleasedError"; +import {TransactionAlreadyStartedError} from "../../error/TransactionAlreadyStartedError"; +import {TransactionNotStartedError} from "../../error/TransactionNotStartedError"; +import {ColumnType} from "../../index"; +import {ReadStream} from "../../platform/PlatformTools"; +import {BaseQueryRunner} from "../../query-runner/BaseQueryRunner"; +import {QueryRunner} from "../../query-runner/QueryRunner"; +import {TableIndexOptions} from "../../schema-builder/options/TableIndexOptions"; +import {Table} from "../../schema-builder/table/Table"; +import {TableCheck} from "../../schema-builder/table/TableCheck"; +import {TableColumn} from "../../schema-builder/table/TableColumn"; +import {TableExclusion} from "../../schema-builder/table/TableExclusion"; +import {TableForeignKey} from "../../schema-builder/table/TableForeignKey"; +import {TableIndex} from "../../schema-builder/table/TableIndex"; +import {TableUnique} from "../../schema-builder/table/TableUnique"; +import {View} from "../../schema-builder/view/View"; +import {Broadcaster} from "../../subscriber/Broadcaster"; +import {OrmUtils} from "../../util/OrmUtils"; +import {Query} from "../Query"; +import {IsolationLevel} from "../types/IsolationLevel"; +import {MssqlParameter} from "./MssqlParameter"; +import {SqlServerDriver} from "./SqlServerDriver"; +import {ReplicationMode} from "../types/ReplicationMode"; +import {BroadcasterResult} from "../../subscriber/BroadcasterResult"; /** * Runs queries on a single SQL Server database connection. @@ -54,7 +56,7 @@ export class SqlServerQueryRunner extends BaseQueryRunner // Constructor // ------------------------------------------------------------------------- - constructor(driver: SqlServerDriver, mode: "master" | "slave" = "master") { + constructor(driver: SqlServerDriver, mode: ReplicationMode) { super(); this.driver = driver; this.connection = driver.connection; @@ -92,6 +94,10 @@ export class SqlServerQueryRunner extends BaseQueryRunner if (this.isTransactionActive) throw new TransactionAlreadyStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionStartEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + return new Promise(async (ok, fail) => { this.isTransactionActive = true; @@ -122,6 +128,10 @@ export class SqlServerQueryRunner extends BaseQueryRunner } else { this.databaseConnection.begin(transactionCallback); } + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionStartEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); }); } @@ -134,11 +144,20 @@ export class SqlServerQueryRunner extends BaseQueryRunner if (!this.isTransactionActive) throw new TransactionNotStartedError(); + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionCommitEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + return new Promise((ok, fail) => { - this.databaseConnection.commit((err: any) => { + this.databaseConnection.commit(async (err: any) => { if (err) return fail(err); this.isTransactionActive = false; this.databaseConnection = null; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionCommitEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); + ok(); this.connection.logger.logQuery("COMMIT"); }); @@ -154,11 +173,20 @@ export class SqlServerQueryRunner extends BaseQueryRunner if (!this.isTransactionActive) throw new TransactionNotStartedError(); - return new Promise((ok, fail) => { - this.databaseConnection.rollback((err: any) => { + const beforeBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastBeforeTransactionRollbackEvent(beforeBroadcastResult); + if (beforeBroadcastResult.promises.length > 0) await Promise.all(beforeBroadcastResult.promises); + + return new Promise( (ok, fail) => { + this.databaseConnection.rollback(async (err: any) => { if (err) return fail(err); this.isTransactionActive = false; this.databaseConnection = null; + + const afterBroadcastResult = new BroadcasterResult(); + this.broadcaster.broadcastAfterTransactionRollbackEvent(afterBroadcastResult); + if (afterBroadcastResult.promises.length > 0) await Promise.all(afterBroadcastResult.promises); + ok(); this.connection.logger.logQuery("ROLLBACK"); }); @@ -994,13 +1022,10 @@ export class SqlServerQueryRunner extends BaseQueryRunner /** * Creates a new columns from the column in the table. */ - async addColumns( - tableOrName: Table | string, - columns: TableColumn[] - ): Promise { - await PromiseUtils.runInSequence(columns, (column) => - this.addColumn(tableOrName, column) - ); + async addColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { + for (const column of columns) { + await this.addColumn(tableOrName, column); + } } /** @@ -1614,17 +1639,10 @@ export class SqlServerQueryRunner extends BaseQueryRunner /** * Changes a column in the table. */ - async changeColumns( - tableOrName: Table | string, - changedColumns: { newColumn: TableColumn; oldColumn: TableColumn }[] - ): Promise { - await PromiseUtils.runInSequence(changedColumns, (changedColumn) => - this.changeColumn( - tableOrName, - changedColumn.oldColumn, - changedColumn.newColumn - ) - ); + async changeColumns(tableOrName: Table|string, changedColumns: { newColumn: TableColumn, oldColumn: TableColumn }[]): Promise { + for (const {oldColumn, newColumn} of changedColumns) { + await this.changeColumn(tableOrName, oldColumn, newColumn); + } } /** @@ -1806,13 +1824,10 @@ export class SqlServerQueryRunner extends BaseQueryRunner /** * Drops the columns in the table. */ - async dropColumns( - tableOrName: Table | string, - columns: TableColumn[] - ): Promise { - await PromiseUtils.runInSequence(columns, (column) => - this.dropColumn(tableOrName, column) - ); + async dropColumns(tableOrName: Table|string, columns: TableColumn[]): Promise { + for (const column of columns) { + await this.dropColumn(tableOrName, column); + } } /** diff --git a/src/driver/types/ColumnTypes.ts b/src/driver/types/ColumnTypes.ts index 3569bedda3c..44c3ab2b852 100644 --- a/src/driver/types/ColumnTypes.ts +++ b/src/driver/types/ColumnTypes.ts @@ -193,7 +193,8 @@ export type SimpleColumnType = |"uniqueidentifier" // mssql |"rowversion" // mssql |"array" // cockroachdb, sap - |"cube"; // postgres + |"cube" // postgres + |"ltree"; // postgres /** * Any column type column can be. diff --git a/src/driver/types/DatabaseType.ts b/src/driver/types/DatabaseType.ts index 4426c9829da..5ceaece0ccc 100644 --- a/src/driver/types/DatabaseType.ts +++ b/src/driver/types/DatabaseType.ts @@ -17,4 +17,5 @@ export type DatabaseType = "mongodb"| "aurora-data-api"| "aurora-data-api-pg"| - "expo"; + "expo"| + "better-sqlite3"; diff --git a/src/driver/types/ReplicationMode.ts b/src/driver/types/ReplicationMode.ts new file mode 100644 index 00000000000..53aca0d9978 --- /dev/null +++ b/src/driver/types/ReplicationMode.ts @@ -0,0 +1 @@ +export type ReplicationMode = "master" | "slave"; diff --git a/src/entity-manager/EntityManager.ts b/src/entity-manager/EntityManager.ts index bb9979fa7b6..cc5616b48a3 100644 --- a/src/entity-manager/EntityManager.ts +++ b/src/entity-manager/EntityManager.ts @@ -1,5 +1,6 @@ import {Connection} from "../connection/Connection"; import {FindManyOptions} from "../find-options/FindManyOptions"; +import {EntityTarget} from "../common/EntityTarget"; import {ObjectType} from "../common/ObjectType"; import {EntityNotFoundError} from "../error/EntityNotFoundError"; import {QueryRunnerProviderAlreadyReleasedError} from "../error/QueryRunnerProviderAlreadyReleasedError"; @@ -31,7 +32,6 @@ import {ObjectID} from "../driver/mongodb/typings"; import {InsertResult} from "../query-builder/result/InsertResult"; import {UpdateResult} from "../query-builder/result/UpdateResult"; import {DeleteResult} from "../query-builder/result/DeleteResult"; -import {OracleDriver} from "../driver/oracle/OracleDriver"; import {FindConditions} from "../find-options/FindConditions"; import {IsolationLevel} from "../driver/types/IsolationLevel"; import {ObjectUtils} from "../util/ObjectUtils"; @@ -127,7 +127,7 @@ export class EntityManager { // if query runner is already defined in this class, it means this entity manager was already created for a single connection // if its not defined we create a new query runner - single connection where we'll execute all our operations - const queryRunner = this.queryRunner || this.connection.createQueryRunner("master"); + const queryRunner = this.queryRunner || this.connection.createQueryRunner(); try { if (isolation) { @@ -161,17 +161,7 @@ export class EntityManager { /** * Creates a new query builder that can be used to build a sql query. */ - createQueryBuilder(entityClass: ObjectType, alias: string, queryRunner?: QueryRunner): SelectQueryBuilder; - - /** - * Creates a new query builder that can be used to build a sql query. - */ - createQueryBuilder(entityClass: EntitySchema, alias: string, queryRunner?: QueryRunner): SelectQueryBuilder; - - /** - * Creates a new query builder that can be used to build a sql query. - */ - createQueryBuilder(entityName: string, alias: string, queryRunner?: QueryRunner): SelectQueryBuilder; + createQueryBuilder(entityClass: EntityTarget, alias: string, queryRunner?: QueryRunner): SelectQueryBuilder; /** * Creates a new query builder that can be used to build a sql query. @@ -181,9 +171,9 @@ export class EntityManager { /** * Creates a new query builder that can be used to build a sql query. */ - createQueryBuilder(entityClass?: ObjectType|EntitySchema|string|QueryRunner, alias?: string, queryRunner?: QueryRunner): SelectQueryBuilder { + createQueryBuilder(entityClass?: EntityTarget|QueryRunner, alias?: string, queryRunner?: QueryRunner): SelectQueryBuilder { if (alias) { - return this.connection.createQueryBuilder(entityClass as EntitySchema|string, alias, queryRunner || this.queryRunner); + return this.connection.createQueryBuilder(entityClass as EntityTarget, alias, queryRunner || this.queryRunner); } else { return this.connection.createQueryBuilder(entityClass as QueryRunner|undefined || queryRunner || this.queryRunner); @@ -218,12 +208,12 @@ export class EntityManager { /** * Gets entity mixed id. */ - getId(target: Function|string, entity: any): any; + getId(target: EntityTarget, entity: any): any; /** * Gets entity mixed id. */ - getId(targetOrEntity: any|Function|string, maybeEntity?: any): any { + getId(targetOrEntity: any|EntityTarget, maybeEntity?: any): any { const target = arguments.length === 2 ? targetOrEntity : targetOrEntity.constructor; const entity = arguments.length === 2 ? maybeEntity : targetOrEntity; const metadata = this.connection.getMetadata(target); @@ -234,43 +224,19 @@ export class EntityManager { * Creates a new entity instance and copies all entity properties from this object into a new entity. * Note that it copies only properties that present in entity schema. */ - create(entityClass: ObjectType, plainObject?: DeepPartial): Entity; + create(entityClass: EntityTarget, plainObject?: DeepPartial): Entity; /** * Creates a new entities and copies all entity properties from given objects into their new entities. * Note that it copies only properties that present in entity schema. */ - create(entityClass: ObjectType, plainObjects?: DeepPartial[]): Entity[]; - - /** - * Creates a new entity instance and copies all entity properties from this object into a new entity. - * Note that it copies only properties that present in entity schema. - */ - create(entitySchema: EntitySchema, plainObject?: DeepPartial): Entity; - - /** - * Creates a new entities and copies all entity properties from given objects into their new entities. - * Note that it copies only properties that present in entity schema. - */ - create(entitySchema: EntitySchema, plainObjects?: DeepPartial[]): Entity[]; - - /** - * Creates a new entity instance and copies all entity properties from this object into a new entity. - * Note that it copies only properties that present in entity schema. - */ - create(entityName: string, plainObject?: DeepPartial): Entity; - - /** - * Creates a new entities and copies all entity properties from given objects into their new entities. - * Note that it copies only properties that present in entity schema. - */ - create(entityName: string, plainObjects?: DeepPartial[]): Entity[]; + create(entityClass: EntityTarget, plainObjects?: DeepPartial[]): Entity[]; /** * Creates a new entity instance or instances. * Can copy properties from the given object into new entities. */ - create(entityClass: ObjectType|EntitySchema|string, plainObjectOrObjects?: DeepPartial|DeepPartial[]): Entity|Entity[] { + create(entityClass: EntityTarget, plainObjectOrObjects?: DeepPartial|DeepPartial[]): Entity|Entity[] { const metadata = this.connection.getMetadata(entityClass); if (!plainObjectOrObjects) @@ -287,22 +253,7 @@ export class EntityManager { /** * Merges two entities into one new entity. */ - merge(entityClass: ObjectType, mergeIntoEntity: Entity, ...entityLikes: DeepPartial[]): Entity; - - /** - * Merges two entities into one new entity. - */ - merge(entitySchema: EntitySchema, mergeIntoEntity: Entity, ...entityLikes: DeepPartial[]): Entity; - - /** - * Merges two entities into one new entity. - */ - merge(entityName: string, mergeIntoEntity: Entity, ...entityLikes: DeepPartial[]): Entity; - - /** - * Merges two entities into one new entity. - */ - merge(entityClass: ObjectType|EntitySchema|string, mergeIntoEntity: Entity, ...entityLikes: DeepPartial[]): Entity { // todo: throw exception if entity manager is released + merge(entityClass: EntityTarget, mergeIntoEntity: Entity, ...entityLikes: DeepPartial[]): Entity { // todo: throw exception if entity manager is released const metadata = this.connection.getMetadata(entityClass); entityLikes.forEach(object => this.plainObjectToEntityTransformer.transform(mergeIntoEntity, object, metadata)); return mergeIntoEntity; @@ -314,31 +265,7 @@ export class EntityManager { * and returns this new entity. This new entity is actually a loaded from the db entity with all properties * replaced from the new object. */ - preload(entityClass: ObjectType, entityLike: DeepPartial): Promise; - - /** - * Creates a new entity from the given plain javascript object. If entity already exist in the database, then - * it loads it (and everything related to it), replaces all values with the new ones from the given object - * and returns this new entity. This new entity is actually a loaded from the db entity with all properties - * replaced from the new object. - */ - preload(entitySchema: EntitySchema, entityLike: DeepPartial): Promise; - - /** - * Creates a new entity from the given plain javascript object. If entity already exist in the database, then - * it loads it (and everything related to it), replaces all values with the new ones from the given object - * and returns this new entity. This new entity is actually a loaded from the db entity with all properties - * replaced from the new object. - */ - preload(entityName: string, entityLike: DeepPartial): Promise; - - /** - * Creates a new entity from the given plain javascript object. If entity already exist in the database, then - * it loads it (and everything related to it), replaces all values with the new ones from the given object - * and returns this new entity. This new entity is actually a loaded from the db entity with all properties - * replaced from the new object. - */ - async preload(entityClass: ObjectType|EntitySchema|string, entityLike: DeepPartial): Promise { + async preload(entityClass: EntityTarget, entityLike: DeepPartial): Promise { const metadata = this.connection.getMetadata(entityClass); const plainObjectToDatabaseEntityTransformer = new PlainObjectToDatabaseEntityTransformer(this.connection.manager); const transformedEntity = await plainObjectToDatabaseEntityTransformer.transform(entityLike, metadata); @@ -364,30 +291,18 @@ export class EntityManager { * Saves all given entities in the database. * If entities do not exist in the database then inserts, otherwise updates. */ - save>(targetOrEntity: ObjectType|EntitySchema, entities: T[], options?: SaveOptions): Promise; - - /** - * Saves all given entities in the database. - * If entities do not exist in the database then inserts, otherwise updates. - */ - save>(targetOrEntity: ObjectType|EntitySchema, entity: T, options?: SaveOptions): Promise; + save>(targetOrEntity: EntityTarget, entities: T[], options?: SaveOptions): Promise; /** * Saves all given entities in the database. * If entities do not exist in the database then inserts, otherwise updates. */ - save(targetOrEntity: string, entities: T[], options?: SaveOptions): Promise; - - /** - * Saves all given entities in the database. - * If entities do not exist in the database then inserts, otherwise updates. - */ - save(targetOrEntity: string, entity: T, options?: SaveOptions): Promise; + save>(targetOrEntity: EntityTarget, entity: T, options?: SaveOptions): Promise; /** * Saves a given entity in the database. */ - save>(targetOrEntity: (T|T[])|ObjectType|EntitySchema|string, maybeEntityOrOptions?: T|T[], maybeOptions?: SaveOptions): Promise { + save>(targetOrEntity: (T|T[])|EntityTarget, maybeEntityOrOptions?: T|T[], maybeOptions?: SaveOptions): Promise { // normalize mixed parameters let target = (arguments.length > 1 && (targetOrEntity instanceof Function || targetOrEntity instanceof EntitySchema || typeof targetOrEntity === "string")) ? targetOrEntity as Function|string : undefined; @@ -415,17 +330,7 @@ export class EntityManager { /** * Removes a given entity from the database. */ - remove(targetOrEntity: ObjectType, entity: Entity, options?: RemoveOptions): Promise; - - /** - * Removes a given entity from the database. - */ - remove(targetOrEntity: EntitySchema, entity: Entity, options?: RemoveOptions): Promise; - - /** - * Removes a given entity from the database. - */ - remove(targetOrEntity: string, entity: Entity, options?: RemoveOptions): Promise; + remove(targetOrEntity: EntityTarget, entity: Entity, options?: RemoveOptions): Promise; /** * Removes a given entity from the database. @@ -435,22 +340,12 @@ export class EntityManager { /** * Removes a given entity from the database. */ - remove(targetOrEntity: ObjectType, entity: Entity[], options?: RemoveOptions): Promise; - - /** - * Removes a given entity from the database. - */ - remove(targetOrEntity: EntitySchema, entity: Entity[], options?: RemoveOptions): Promise; - - /** - * Removes a given entity from the database. - */ - remove(targetOrEntity: string, entity: Entity[], options?: RemoveOptions): Promise; + remove(targetOrEntity: EntityTarget, entity: Entity[], options?: RemoveOptions): Promise; /** * Removes a given entity from the database. */ - remove(targetOrEntity: (Entity|Entity[])|Function|string, maybeEntityOrOptions?: Entity|Entity[], maybeOptions?: RemoveOptions): Promise { + remove(targetOrEntity: (Entity|Entity[])|EntityTarget, maybeEntityOrOptions?: Entity|Entity[], maybeOptions?: RemoveOptions): Promise { // normalize mixed parameters const target = (arguments.length > 1 && (targetOrEntity instanceof Function || typeof targetOrEntity === "string")) ? targetOrEntity as Function|string : undefined; @@ -480,27 +375,17 @@ export class EntityManager { /** * Records the delete date of all given entities. */ - softRemove>(targetOrEntity: ObjectType|EntitySchema, entities: T[], options?: SaveOptions): Promise; + softRemove>(targetOrEntity: EntityTarget, entities: T[], options?: SaveOptions): Promise; /** * Records the delete date of a given entity. */ - softRemove>(targetOrEntity: ObjectType|EntitySchema, entity: T, options?: SaveOptions): Promise; - - /** - * Records the delete date of all given entities. - */ - softRemove(targetOrEntity: string, entities: T[], options?: SaveOptions): Promise; - - /** - * Records the delete date of a given entity. - */ - softRemove(targetOrEntity: string, entity: T, options?: SaveOptions): Promise; + softRemove>(targetOrEntity: EntityTarget, entity: T, options?: SaveOptions): Promise; /** * Records the delete date of one or many given entities. */ - softRemove>(targetOrEntity: (T|T[])|ObjectType|EntitySchema|string, maybeEntityOrOptions?: T|T[], maybeOptions?: SaveOptions): Promise { + softRemove>(targetOrEntity: (T|T[])|EntityTarget, maybeEntityOrOptions?: T|T[], maybeOptions?: SaveOptions): Promise { // normalize mixed parameters let target = (arguments.length > 1 && (targetOrEntity instanceof Function || targetOrEntity instanceof EntitySchema || typeof targetOrEntity === "string")) ? targetOrEntity as Function|string : undefined; @@ -533,27 +418,17 @@ export class EntityManager { /** * Recovers all given entities. */ - recover>(targetOrEntity: ObjectType|EntitySchema, entities: T[], options?: SaveOptions): Promise; - - /** - * Recovers a given entity. - */ - recover>(targetOrEntity: ObjectType|EntitySchema, entity: T, options?: SaveOptions): Promise; - - /** - * Recovers all given entities. - */ - recover(targetOrEntity: string, entities: T[], options?: SaveOptions): Promise; + recover>(targetOrEntity: EntityTarget, entities: T[], options?: SaveOptions): Promise; /** * Recovers a given entity. */ - recover(targetOrEntity: string, entity: T, options?: SaveOptions): Promise; + recover>(targetOrEntity: EntityTarget, entity: T, options?: SaveOptions): Promise; /** * Recovers one or many given entities. */ - recover>(targetOrEntity: (T|T[])|ObjectType|EntitySchema|string, maybeEntityOrOptions?: T|T[], maybeOptions?: SaveOptions): Promise { + recover>(targetOrEntity: (T|T[])|EntityTarget, maybeEntityOrOptions?: T|T[], maybeOptions?: SaveOptions): Promise { // normalize mixed parameters let target = (arguments.length > 1 && (targetOrEntity instanceof Function || targetOrEntity instanceof EntitySchema || typeof targetOrEntity === "string")) ? targetOrEntity as Function|string : undefined; @@ -580,23 +455,7 @@ export class EntityManager { * Does not check if entity exist in the database, so query will fail if duplicate entity is being inserted. * You can execute bulk inserts using this method. */ - async insert(target: ObjectType|EntitySchema|string, entity: QueryDeepPartialEntity|(QueryDeepPartialEntity[])): Promise { - - // If user passed empty array of entities then we don't need to do - // anything. - // - // Fixes GitHub issue #5734. If we were to let this through we would - // run into problems downstream, like subscribers getting invoked with - // the empty array where they expect an entity, and SQL queries with an - // empty VALUES clause. - if (Array.isArray(entity) && entity.length === 0) - return Promise.resolve(new InsertResult()); - - // TODO: Oracle does not support multiple values. Need to create another nice solution. - if (this.connection.driver instanceof OracleDriver && Array.isArray(entity)) { - const results = await Promise.all(entity.map(entity => this.insert(target, entity))); - return results.reduce((mergedResult, result) => Object.assign(mergedResult, result), {} as InsertResult); - } + async insert(target: EntityTarget, entity: QueryDeepPartialEntity|(QueryDeepPartialEntity[])): Promise { return this.createQueryBuilder() .insert() .into(target) @@ -611,7 +470,7 @@ export class EntityManager { * Does not check if entity exist in the database. * Condition(s) cannot be empty. */ - update(target: ObjectType|EntitySchema|string, criteria: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|any, partialEntity: QueryDeepPartialEntity): Promise { + update(target: EntityTarget, criteria: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|any, partialEntity: QueryDeepPartialEntity): Promise { // if user passed empty criteria or empty list of criterias, then throw an error if (criteria === undefined || @@ -649,7 +508,7 @@ export class EntityManager { * Does not check if entity exist in the database. * Condition(s) cannot be empty. */ - delete(targetOrEntity: ObjectType|EntitySchema|string, criteria: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|any): Promise { + delete(targetOrEntity: EntityTarget, criteria: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|any): Promise { // if user passed empty criteria or empty list of criterias, then throw an error if (criteria === undefined || @@ -687,7 +546,7 @@ export class EntityManager { * Does not check if entity exist in the database. * Condition(s) cannot be empty. */ - softDelete(targetOrEntity: ObjectType|EntitySchema|string, criteria: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|any): Promise { + softDelete(targetOrEntity: EntityTarget, criteria: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|any): Promise { // if user passed empty criteria or empty list of criterias, then throw an error if (criteria === undefined || @@ -725,7 +584,7 @@ export class EntityManager { * Does not check if entity exist in the database. * Condition(s) cannot be empty. */ - restore(targetOrEntity: ObjectType|EntitySchema|string, criteria: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|any): Promise { + restore(targetOrEntity: EntityTarget, criteria: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|any): Promise { // if user passed empty criteria or empty list of criterias, then throw an error if (criteria === undefined || @@ -760,82 +619,44 @@ export class EntityManager { * Counts entities that match given options. * Useful for pagination. */ - count(entityClass: ObjectType, options?: FindOneOptions): Promise; - - /** - * Counts entities that match given options. - * Useful for pagination. - */ - count(entityClass: EntitySchema, options?: FindOneOptions): Promise; + count(entityClass: EntityTarget, options?: FindOneOptions): Promise; /** * Counts entities that match given options. * Useful for pagination. */ - count(entityClass: string, options?: FindOneOptions): Promise; - - /** - * Counts entities that match given conditions. - * Useful for pagination. - */ - count(entityClass: ObjectType, conditions?: FindConditions): Promise; - - /** - * Counts entities that match given conditions. - * Useful for pagination. - */ - count(entityClass: EntitySchema, conditions?: FindConditions): Promise; + count(entityClass: EntityTarget, options?: FindManyOptions): Promise; /** * Counts entities that match given conditions. * Useful for pagination. */ - count(entityClass: string, conditions?: FindConditions): Promise; + count(entityClass: EntityTarget, conditions?: FindConditions): Promise; /** * Counts entities that match given find options or conditions. * Useful for pagination. */ - async count(entityClass: ObjectType|EntitySchema|string, optionsOrConditions?: FindManyOptions|any): Promise { + async count(entityClass: EntityTarget, optionsOrConditions?: FindConditions|FindOneOptions|FindManyOptions): Promise { const metadata = this.connection.getMetadata(entityClass); - const qb = this.createQueryBuilder(entityClass as any, FindOptionsUtils.extractFindManyOptionsAlias(optionsOrConditions) || metadata.name); + const qb = this.createQueryBuilder(entityClass, FindOptionsUtils.extractFindManyOptionsAlias(optionsOrConditions) || metadata.name); return FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, optionsOrConditions).getCount(); } /** * Finds entities that match given options. */ - find(entityClass: ObjectType, options?: FindManyOptions): Promise; - - /** - * Finds entities that match given conditions. - */ - find(entityClass: ObjectType, conditions?: FindConditions): Promise; - - /** - * Finds entities that match given options. - */ - find(entitySchema: EntitySchema, options?: FindManyOptions): Promise; + find(entityClass: EntityTarget, options?: FindManyOptions): Promise; /** * Finds entities that match given conditions. */ - find(entitySchema: EntitySchema, conditions?: FindConditions): Promise; - - /** - * Finds entities that match given conditions. - */ - find(entityClass: string, options?: FindManyOptions): Promise; - - /** - * Finds entities that match given conditions. - */ - find(entityClass: string, conditions?: FindConditions): Promise; + find(entityClass: EntityTarget, conditions?: FindConditions): Promise; /** * Finds entities that match given find options or conditions. */ - async find(entityClass: ObjectType|EntitySchema|string, optionsOrConditions?: FindManyOptions|any): Promise { + async find(entityClass: EntityTarget, optionsOrConditions?: FindManyOptions|FindConditions): Promise { const metadata = this.connection.getMetadata(entityClass); const qb = this.createQueryBuilder(entityClass as any, FindOptionsUtils.extractFindManyOptionsAlias(optionsOrConditions) || metadata.name); @@ -850,49 +671,21 @@ export class EntityManager { * Also counts all entities that match given conditions, * but ignores pagination settings (from and take options). */ - findAndCount(entityClass: ObjectType, options?: FindManyOptions): Promise<[Entity[], number]>; - - /** - * Finds entities that match given find options. - * Also counts all entities that match given conditions, - * but ignores pagination settings (from and take options). - */ - findAndCount(entityClass: EntitySchema, options?: FindManyOptions): Promise<[Entity[], number]>; - - /** - * Finds entities that match given find options. - * Also counts all entities that match given conditions, - * but ignores pagination settings (from and take options). - */ - findAndCount(entityClass: string, options?: FindManyOptions): Promise<[Entity[], number]>; + findAndCount(entityClass: EntityTarget, options?: FindManyOptions): Promise<[Entity[], number]>; /** * Finds entities that match given conditions. * Also counts all entities that match given conditions, * but ignores pagination settings (from and take options). */ - findAndCount(entityClass: ObjectType, conditions?: FindConditions): Promise<[Entity[], number]>; - - /** - * Finds entities that match given conditions. - * Also counts all entities that match given conditions, - * but ignores pagination settings (from and take options). - */ - findAndCount(entityClass: EntitySchema, conditions?: FindConditions): Promise<[Entity[], number]>; - - /** - * Finds entities that match given conditions. - * Also counts all entities that match given conditions, - * but ignores pagination settings (from and take options). - */ - findAndCount(entityClass: string, conditions?: FindConditions): Promise<[Entity[], number]>; + findAndCount(entityClass: EntityTarget, conditions?: FindConditions): Promise<[Entity[], number]>; /** * Finds entities that match given find options and conditions. * Also counts all entities that match given conditions, * but ignores pagination settings (from and take options). */ - async findAndCount(entityClass: ObjectType|EntitySchema|string, optionsOrConditions?: FindManyOptions|any): Promise<[Entity[], number]> { + async findAndCount(entityClass: EntityTarget, optionsOrConditions?: FindConditions|FindManyOptions): Promise<[Entity[], number]> { const metadata = this.connection.getMetadata(entityClass); const qb = this.createQueryBuilder(entityClass as any, FindOptionsUtils.extractFindManyOptionsAlias(optionsOrConditions) || metadata.name); @@ -906,43 +699,19 @@ export class EntityManager { * Finds entities with ids. * Optionally find options can be applied. */ - findByIds(entityClass: ObjectType, ids: any[], options?: FindManyOptions): Promise; - - /** - * Finds entities with ids. - * Optionally find options can be applied. - */ - findByIds(entityClass: EntitySchema, ids: any[], options?: FindManyOptions): Promise; - - /** - * Finds entities with ids. - * Optionally find options can be applied. - */ - findByIds(entityClass: string, ids: any[], options?: FindManyOptions): Promise; + findByIds(entityClass: EntityTarget, ids: any[], options?: FindManyOptions): Promise; /** * Finds entities with ids. * Optionally conditions can be applied. */ - findByIds(entityClass: ObjectType, ids: any[], conditions?: FindConditions): Promise; - - /** - * Finds entities with ids. - * Optionally conditions can be applied. - */ - findByIds(entityClass: EntitySchema, ids: any[], conditions?: FindConditions): Promise; - - /** - * Finds entities with ids. - * Optionally conditions can be applied. - */ - findByIds(entityClass: string, ids: any[], conditions?: FindConditions): Promise; + findByIds(entityClass: EntityTarget, ids: any[], conditions?: FindConditions): Promise; /** * Finds entities with ids. * Optionally find options or conditions can be applied. */ - async findByIds(entityClass: ObjectType|EntitySchema|string, ids: any[], optionsOrConditions?: FindManyOptions|any): Promise { + async findByIds(entityClass: EntityTarget, ids: any[], optionsOrConditions?: FindConditions|FindManyOptions): Promise { // if no ids passed, no need to execute a query - just return an empty array of values if (!ids.length) @@ -960,52 +729,22 @@ export class EntityManager { /** * Finds first entity that matches given find options. */ - findOne(entityClass: ObjectType, id?: string|number|Date|ObjectID, options?: FindOneOptions): Promise; + findOne(entityClass: EntityTarget, id?: string|number|Date|ObjectID, options?: FindOneOptions): Promise; /** * Finds first entity that matches given find options. */ - findOne(entityClass: EntitySchema, id?: string|number|Date|ObjectID, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given find options. - */ - findOne(entityClass: string, id?: string|number|Date|ObjectID, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given find options. - */ - findOne(entityClass: ObjectType, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given find options. - */ - findOne(entityClass: EntitySchema, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given find options. - */ - findOne(entityClass: string, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given conditions. - */ - findOne(entityClass: ObjectType, conditions?: FindConditions, options?: FindOneOptions): Promise; + findOne(entityClass: EntityTarget, options?: FindOneOptions): Promise; /** * Finds first entity that matches given conditions. */ - findOne(entityClass: EntitySchema, conditions?: FindConditions, options?: FindOneOptions): Promise; + findOne(entityClass: EntityTarget, conditions?: FindConditions, options?: FindOneOptions): Promise; /** * Finds first entity that matches given conditions. */ - findOne(entityClass: string, conditions?: FindConditions, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given conditions. - */ - async findOne(entityClass: ObjectType|EntitySchema|string, idOrOptionsOrConditions?: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|FindOneOptions|any, maybeOptions?: FindOneOptions): Promise { + async findOne(entityClass: EntityTarget, idOrOptionsOrConditions?: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|FindOneOptions|FindConditions, maybeOptions?: FindOneOptions): Promise { let findOptions: FindManyOptions|FindOneOptions|undefined = undefined; if (FindOptionsUtils.isFindOneOptions(idOrOptionsOrConditions)) { @@ -1055,52 +794,22 @@ export class EntityManager { /** * Finds first entity that matches given find options or rejects the returned promise on error. */ - findOneOrFail(entityClass: ObjectType, id?: string|number|Date|ObjectID, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given find options or rejects the returned promise on error. - */ - findOneOrFail(entityClass: EntitySchema, id?: string|number|Date|ObjectID, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given find options or rejects the returned promise on error. - */ - findOneOrFail(entityClass: string, id?: string|number|Date|ObjectID, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given find options or rejects the returned promise on error. - */ - findOneOrFail(entityClass: ObjectType, options?: FindOneOptions): Promise; + findOneOrFail(entityClass: EntityTarget, id?: string|number|Date|ObjectID, options?: FindOneOptions): Promise; /** * Finds first entity that matches given find options or rejects the returned promise on error. */ - findOneOrFail(entityClass: EntitySchema, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given find options or rejects the returned promise on error. - */ - findOneOrFail(entityClass: string, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given conditions or rejects the returned promise on error. - */ - findOneOrFail(entityClass: ObjectType, conditions?: FindConditions, options?: FindOneOptions): Promise; - - /** - * Finds first entity that matches given conditions or rejects the returned promise on error. - */ - findOneOrFail(entityClass: EntitySchema, conditions?: FindConditions, options?: FindOneOptions): Promise; + findOneOrFail(entityClass: EntityTarget, options?: FindOneOptions): Promise; /** * Finds first entity that matches given conditions or rejects the returned promise on error. */ - findOneOrFail(entityClass: string, conditions?: FindConditions, options?: FindOneOptions): Promise; + findOneOrFail(entityClass: EntityTarget, conditions?: FindConditions, options?: FindOneOptions): Promise; /** * Finds first entity that matches given conditions or rejects the returned promise on error. */ - async findOneOrFail(entityClass: ObjectType|EntitySchema|string, idOrOptionsOrConditions?: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|FindOneOptions|any, maybeOptions?: FindOneOptions): Promise { + async findOneOrFail(entityClass: EntityTarget, idOrOptionsOrConditions?: string|string[]|number|number[]|Date|Date[]|ObjectID|ObjectID[]|FindOneOptions|FindConditions, maybeOptions?: FindOneOptions): Promise { return this.findOne(entityClass as any, idOrOptionsOrConditions as any, maybeOptions).then((value) => { if (value === undefined) { return Promise.reject(new EntityNotFoundError(entityClass, idOrOptionsOrConditions)); @@ -1115,9 +824,9 @@ export class EntityManager { * Note: this method uses TRUNCATE and may not work as you expect in transactions on some platforms. * @see https://stackoverflow.com/a/5972738/925151 */ - async clear(entityClass: ObjectType|EntitySchema|string): Promise { + async clear(entityClass: EntityTarget): Promise { const metadata = this.connection.getMetadata(entityClass); - const queryRunner = this.queryRunner || this.connection.createQueryRunner("master"); + const queryRunner = this.queryRunner || this.connection.createQueryRunner(); try { return await queryRunner.clearTable(metadata.tablePath); // await is needed here because we are using finally @@ -1130,7 +839,7 @@ export class EntityManager { /** * Increments some column by provided value of the entities matched given conditions. */ - async increment(entityClass: ObjectType|EntitySchema|string, + async increment(entityClass: EntityTarget, conditions: any, propertyPath: string, value: number | string): Promise { @@ -1162,7 +871,7 @@ export class EntityManager { /** * Decrements some column by provided value of the entities matched given conditions. */ - async decrement(entityClass: ObjectType|EntitySchema|string, + async decrement(entityClass: EntityTarget, conditions: any, propertyPath: string, value: number | string): Promise { @@ -1197,7 +906,7 @@ export class EntityManager { * repository aggregator, where each repository is individually created for this entity manager. * When single database connection is not used, repository is being obtained from the connection. */ - getRepository(target: ObjectType|EntitySchema|string): Repository { + getRepository(target: EntityTarget): Repository { // throw exception if there is no repository with this target registered if (!this.connection.hasMetadata(target)) @@ -1221,7 +930,7 @@ export class EntityManager { * repository aggregator, where each repository is individually created for this entity manager. * When single database connection is not used, repository is being obtained from the connection. */ - getTreeRepository(target: ObjectType|EntitySchema|string): TreeRepository { + getTreeRepository(target: EntityTarget): TreeRepository { // tree tables aren't supported by some drivers (mongodb) if (this.connection.driver.treeSupport === false) @@ -1238,7 +947,7 @@ export class EntityManager { /** * Gets mongodb repository for the given entity class. */ - getMongoRepository(target: ObjectType|EntitySchema|string): MongoRepository { + getMongoRepository(target: EntityTarget): MongoRepository { return this.connection.getMongoRepository(target); } diff --git a/src/entity-manager/MongoEntityManager.ts b/src/entity-manager/MongoEntityManager.ts index 346cf1eee42..fec1d5a2e70 100644 --- a/src/entity-manager/MongoEntityManager.ts +++ b/src/entity-manager/MongoEntityManager.ts @@ -1,6 +1,6 @@ import { Connection } from "../connection/Connection"; import { EntityManager } from "./EntityManager"; -import { ObjectType } from "../common/ObjectType"; +import { EntityTarget } from "../common/EntityTarget"; import { AggregationCursor, BulkWriteOpResultObject, @@ -51,7 +51,7 @@ import { InsertResult } from "../query-builder/result/InsertResult"; import { UpdateResult } from "../query-builder/result/UpdateResult"; import { DeleteResult } from "../query-builder/result/DeleteResult"; import { EntityMetadata } from "../metadata/EntityMetadata"; -import { EntitySchema, FindConditions } from "../index"; +import { FindConditions } from "../index"; import { BroadcasterResult } from "../subscriber/BroadcasterResult"; /** @@ -88,7 +88,7 @@ export class MongoEntityManager extends EntityManager { /** * Finds entities that match given find options or conditions. */ - async find(entityClassOrName: ObjectType | EntitySchema | string, optionsOrConditions?: FindManyOptions | Partial): Promise { + async find(entityClassOrName: EntityTarget, optionsOrConditions?: FindManyOptions | Partial): Promise { const query = this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions); const cursor = await this.createEntityCursor(entityClassOrName, query); if (FindOptionsUtils.isFindManyOptions(optionsOrConditions)) { @@ -109,7 +109,7 @@ export class MongoEntityManager extends EntityManager { * Also counts all entities that match given conditions, * but ignores pagination settings (from and take options). */ - async findAndCount(entityClassOrName: ObjectType | EntitySchema | string, optionsOrConditions?: FindManyOptions | Partial): Promise<[Entity[], number]> { + async findAndCount(entityClassOrName: EntityTarget, optionsOrConditions?: FindManyOptions | Partial): Promise<[Entity[], number]> { const query = this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions); const cursor = await this.createEntityCursor(entityClassOrName, query); if (FindOptionsUtils.isFindManyOptions(optionsOrConditions)) { @@ -134,7 +134,7 @@ export class MongoEntityManager extends EntityManager { * Finds entities by ids. * Optionally find options can be applied. */ - async findByIds(entityClassOrName: ObjectType | EntitySchema | string, ids: any[], optionsOrConditions?: FindManyOptions | Partial): Promise { + async findByIds(entityClassOrName: EntityTarget, ids: any[], optionsOrConditions?: FindManyOptions | Partial): Promise { const metadata = this.connection.getMetadata(entityClassOrName); const query = this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions) || {}; const objectIdInstance = PlatformTools.load("mongodb").ObjectID; @@ -164,7 +164,7 @@ export class MongoEntityManager extends EntityManager { /** * Finds first entity that matches given conditions and/or find options. */ - async findOne(entityClassOrName: ObjectType | EntitySchema | string, + async findOne(entityClassOrName: EntityTarget, optionsOrConditions?: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindOneOptions | DeepPartial, maybeOptions?: FindOneOptions): Promise { const objectIdInstance = PlatformTools.load("mongodb").ObjectID; @@ -194,7 +194,7 @@ export class MongoEntityManager extends EntityManager { * Does not check if entity exist in the database, so query will fail if duplicate entity is being inserted. * You can execute bulk inserts using this method. */ - async insert(target: ObjectType | EntitySchema | string, entity: QueryDeepPartialEntity | QueryDeepPartialEntity[]): Promise { + async insert(target: EntityTarget, entity: QueryDeepPartialEntity | QueryDeepPartialEntity[]): Promise { // todo: convert entity to its database name const result = new InsertResult(); if (Array.isArray(entity)) { @@ -220,7 +220,7 @@ export class MongoEntityManager extends EntityManager { * Executes fast and efficient UPDATE query. * Does not check if entity exist in the database. */ - async update(target: ObjectType | EntitySchema | string, criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindConditions, partialEntity: QueryDeepPartialEntity): Promise { + async update(target: EntityTarget, criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindConditions, partialEntity: QueryDeepPartialEntity): Promise { if (Array.isArray(criteria)) { await Promise.all((criteria as any[]).map(criteriaItem => { return this.update(target, criteriaItem, partialEntity); @@ -240,7 +240,7 @@ export class MongoEntityManager extends EntityManager { * Executes fast and efficient DELETE query. * Does not check if entity exist in the database. */ - async delete(target: ObjectType | EntitySchema | string, criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindConditions): Promise { + async delete(target: EntityTarget, criteria: string | string[] | number | number[] | Date | Date[] | ObjectID | ObjectID[] | FindConditions): Promise { if (Array.isArray(criteria)) { await Promise.all((criteria as any[]).map(criteriaItem => { return this.delete(target, criteriaItem); @@ -260,7 +260,7 @@ export class MongoEntityManager extends EntityManager { /** * Creates a cursor for a query that can be used to iterate over results from MongoDB. */ - createCursor(entityClassOrName: ObjectType | EntitySchema | string, query?: ObjectLiteral): Cursor { + createCursor(entityClassOrName: EntityTarget, query?: ObjectLiteral): Cursor { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.cursor(metadata.tableName, query); } @@ -269,7 +269,7 @@ export class MongoEntityManager extends EntityManager { * Creates a cursor for a query that can be used to iterate over results from MongoDB. * This returns modified version of cursor that transforms each result into Entity model. */ - createEntityCursor(entityClassOrName: ObjectType | EntitySchema | string, query?: ObjectLiteral): Cursor { + createEntityCursor(entityClassOrName: EntityTarget, query?: ObjectLiteral): Cursor { const metadata = this.connection.getMetadata(entityClassOrName); const cursor = this.createCursor(entityClassOrName, query); this.applyEntityTransformationToCursor(metadata, cursor); @@ -279,7 +279,7 @@ export class MongoEntityManager extends EntityManager { /** * Execute an aggregation framework pipeline against the collection. */ - aggregate(entityClassOrName: ObjectType | EntitySchema | string, pipeline: ObjectLiteral[], options?: CollectionAggregationOptions): AggregationCursor { + aggregate(entityClassOrName: EntityTarget, pipeline: ObjectLiteral[], options?: CollectionAggregationOptions): AggregationCursor { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.aggregate(metadata.tableName, pipeline, options); } @@ -288,7 +288,7 @@ export class MongoEntityManager extends EntityManager { * Execute an aggregation framework pipeline against the collection. * This returns modified version of cursor that transforms each result into Entity model. */ - aggregateEntity(entityClassOrName: ObjectType | EntitySchema | string, pipeline: ObjectLiteral[], options?: CollectionAggregationOptions): AggregationCursor { + aggregateEntity(entityClassOrName: EntityTarget, pipeline: ObjectLiteral[], options?: CollectionAggregationOptions): AggregationCursor { const metadata = this.connection.getMetadata(entityClassOrName); const cursor = this.queryRunner.aggregate(metadata.tableName, pipeline, options); this.applyEntityTransformationToCursor(metadata, cursor); @@ -298,7 +298,7 @@ export class MongoEntityManager extends EntityManager { /** * Perform a bulkWrite operation without a fluent API. */ - bulkWrite(entityClassOrName: ObjectType | EntitySchema | string, operations: ObjectLiteral[], options?: CollectionBulkWriteOptions): Promise { + bulkWrite(entityClassOrName: EntityTarget, operations: ObjectLiteral[], options?: CollectionBulkWriteOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.bulkWrite(metadata.tableName, operations, options); } @@ -306,7 +306,7 @@ export class MongoEntityManager extends EntityManager { /** * Count number of matching documents in the db to a query. */ - count(entityClassOrName: ObjectType | EntitySchema | string, query?: ObjectLiteral, options?: MongoCountPreferences): Promise { + count(entityClassOrName: EntityTarget, query?: ObjectLiteral, options?: MongoCountPreferences): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.count(metadata.tableName, query, options); } @@ -314,7 +314,7 @@ export class MongoEntityManager extends EntityManager { /** * Creates an index on the db and collection. */ - createCollectionIndex(entityClassOrName: ObjectType | EntitySchema | string, fieldOrSpec: string | any, options?: MongodbIndexOptions): Promise { + createCollectionIndex(entityClassOrName: EntityTarget, fieldOrSpec: string | any, options?: MongodbIndexOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.createCollectionIndex(metadata.tableName, fieldOrSpec, options); } @@ -324,7 +324,7 @@ export class MongoEntityManager extends EntityManager { * Earlier version of MongoDB will throw a command not supported error. * Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/. */ - createCollectionIndexes(entityClassOrName: ObjectType | EntitySchema | string, indexSpecs: ObjectLiteral[]): Promise { + createCollectionIndexes(entityClassOrName: EntityTarget, indexSpecs: ObjectLiteral[]): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.createCollectionIndexes(metadata.tableName, indexSpecs); } @@ -332,7 +332,7 @@ export class MongoEntityManager extends EntityManager { /** * Delete multiple documents on MongoDB. */ - deleteMany(entityClassOrName: ObjectType | EntitySchema | string, query: ObjectLiteral, options?: CollectionOptions): Promise { + deleteMany(entityClassOrName: EntityTarget, query: ObjectLiteral, options?: CollectionOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.deleteMany(metadata.tableName, query, options); } @@ -340,7 +340,7 @@ export class MongoEntityManager extends EntityManager { /** * Delete a document on MongoDB. */ - deleteOne(entityClassOrName: ObjectType | EntitySchema | string, query: ObjectLiteral, options?: CollectionOptions): Promise { + deleteOne(entityClassOrName: EntityTarget, query: ObjectLiteral, options?: CollectionOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.deleteOne(metadata.tableName, query, options); } @@ -348,7 +348,7 @@ export class MongoEntityManager extends EntityManager { /** * The distinct command returns returns a list of distinct values for the given key across a collection. */ - distinct(entityClassOrName: ObjectType | EntitySchema | string, key: string, query: ObjectLiteral, options?: { readPreference?: ReadPreference | string }): Promise { + distinct(entityClassOrName: EntityTarget, key: string, query: ObjectLiteral, options?: { readPreference?: ReadPreference | string }): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.distinct(metadata.tableName, key, query, options); } @@ -356,7 +356,7 @@ export class MongoEntityManager extends EntityManager { /** * Drops an index from this collection. */ - dropCollectionIndex(entityClassOrName: ObjectType | EntitySchema | string, indexName: string, options?: CollectionOptions): Promise { + dropCollectionIndex(entityClassOrName: EntityTarget, indexName: string, options?: CollectionOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.dropCollectionIndex(metadata.tableName, indexName, options); } @@ -364,7 +364,7 @@ export class MongoEntityManager extends EntityManager { /** * Drops all indexes from the collection. */ - dropCollectionIndexes(entityClassOrName: ObjectType | EntitySchema | string): Promise { + dropCollectionIndexes(entityClassOrName: EntityTarget): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.dropCollectionIndexes(metadata.tableName); } @@ -372,7 +372,7 @@ export class MongoEntityManager extends EntityManager { /** * Find a document and delete it in one atomic operation, requires a write lock for the duration of the operation. */ - findOneAndDelete(entityClassOrName: ObjectType | EntitySchema | string, query: ObjectLiteral, options?: { projection?: Object, sort?: Object, maxTimeMS?: number }): Promise { + findOneAndDelete(entityClassOrName: EntityTarget, query: ObjectLiteral, options?: { projection?: Object, sort?: Object, maxTimeMS?: number }): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.findOneAndDelete(metadata.tableName, query, options); } @@ -380,7 +380,7 @@ export class MongoEntityManager extends EntityManager { /** * Find a document and replace it in one atomic operation, requires a write lock for the duration of the operation. */ - findOneAndReplace(entityClassOrName: ObjectType | EntitySchema | string, query: ObjectLiteral, replacement: Object, options?: FindOneAndReplaceOption): Promise { + findOneAndReplace(entityClassOrName: EntityTarget, query: ObjectLiteral, replacement: Object, options?: FindOneAndReplaceOption): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.findOneAndReplace(metadata.tableName, query, replacement, options); } @@ -388,7 +388,7 @@ export class MongoEntityManager extends EntityManager { /** * Find a document and update it in one atomic operation, requires a write lock for the duration of the operation. */ - findOneAndUpdate(entityClassOrName: ObjectType | EntitySchema | string, query: ObjectLiteral, update: Object, options?: FindOneAndReplaceOption): Promise { + findOneAndUpdate(entityClassOrName: EntityTarget, query: ObjectLiteral, update: Object, options?: FindOneAndReplaceOption): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.findOneAndUpdate(metadata.tableName, query, update, options); } @@ -396,7 +396,7 @@ export class MongoEntityManager extends EntityManager { /** * Execute a geo search using a geo haystack index on a collection. */ - geoHaystackSearch(entityClassOrName: ObjectType | EntitySchema | string, x: number, y: number, options?: GeoHaystackSearchOptions): Promise { + geoHaystackSearch(entityClassOrName: EntityTarget, x: number, y: number, options?: GeoHaystackSearchOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.geoHaystackSearch(metadata.tableName, x, y, options); } @@ -404,7 +404,7 @@ export class MongoEntityManager extends EntityManager { /** * Execute the geoNear command to search for items in the collection. */ - geoNear(entityClassOrName: ObjectType | EntitySchema | string, x: number, y: number, options?: GeoNearOptions): Promise { + geoNear(entityClassOrName: EntityTarget, x: number, y: number, options?: GeoNearOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.geoNear(metadata.tableName, x, y, options); } @@ -412,7 +412,7 @@ export class MongoEntityManager extends EntityManager { /** * Run a group command across a collection. */ - group(entityClassOrName: ObjectType | EntitySchema | string, keys: Object | Array | Function | Code, condition: Object, initial: Object, reduce: Function | Code, finalize: Function | Code, command: boolean, options?: { readPreference?: ReadPreference | string }): Promise { + group(entityClassOrName: EntityTarget, keys: Object | Array | Function | Code, condition: Object, initial: Object, reduce: Function | Code, finalize: Function | Code, command: boolean, options?: { readPreference?: ReadPreference | string }): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.group(metadata.tableName, keys, condition, initial, reduce, finalize, command, options); } @@ -420,7 +420,7 @@ export class MongoEntityManager extends EntityManager { /** * Retrieve all the indexes on the collection. */ - collectionIndexes(entityClassOrName: ObjectType | EntitySchema | string): Promise { + collectionIndexes(entityClassOrName: EntityTarget): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.collectionIndexes(metadata.tableName); } @@ -428,7 +428,7 @@ export class MongoEntityManager extends EntityManager { /** * Retrieve all the indexes on the collection. */ - collectionIndexExists(entityClassOrName: ObjectType | EntitySchema | string, indexes: string | string[]): Promise { + collectionIndexExists(entityClassOrName: EntityTarget, indexes: string | string[]): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.collectionIndexExists(metadata.tableName, indexes); } @@ -436,7 +436,7 @@ export class MongoEntityManager extends EntityManager { /** * Retrieves this collections index info. */ - collectionIndexInformation(entityClassOrName: ObjectType | EntitySchema | string, options?: { full: boolean }): Promise { + collectionIndexInformation(entityClassOrName: EntityTarget, options?: { full: boolean }): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.collectionIndexInformation(metadata.tableName, options); } @@ -444,7 +444,7 @@ export class MongoEntityManager extends EntityManager { /** * Initiate an In order bulk write operation, operations will be serially executed in the order they are added, creating a new operation for each switch in types. */ - initializeOrderedBulkOp(entityClassOrName: ObjectType | EntitySchema | string, options?: CollectionOptions): OrderedBulkOperation { + initializeOrderedBulkOp(entityClassOrName: EntityTarget, options?: CollectionOptions): OrderedBulkOperation { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.initializeOrderedBulkOp(metadata.tableName, options); } @@ -452,7 +452,7 @@ export class MongoEntityManager extends EntityManager { /** * Initiate a Out of order batch write operation. All operations will be buffered into insert/update/remove commands executed out of order. */ - initializeUnorderedBulkOp(entityClassOrName: ObjectType | EntitySchema | string, options?: CollectionOptions): UnorderedBulkOperation { + initializeUnorderedBulkOp(entityClassOrName: EntityTarget, options?: CollectionOptions): UnorderedBulkOperation { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.initializeUnorderedBulkOp(metadata.tableName, options); } @@ -460,7 +460,7 @@ export class MongoEntityManager extends EntityManager { /** * Inserts an array of documents into MongoDB. */ - insertMany(entityClassOrName: ObjectType | EntitySchema | string, docs: ObjectLiteral[], options?: CollectionInsertManyOptions): Promise { + insertMany(entityClassOrName: EntityTarget, docs: ObjectLiteral[], options?: CollectionInsertManyOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.insertMany(metadata.tableName, docs, options); } @@ -468,7 +468,7 @@ export class MongoEntityManager extends EntityManager { /** * Inserts a single document into MongoDB. */ - insertOne(entityClassOrName: ObjectType | EntitySchema | string, doc: ObjectLiteral, options?: CollectionInsertOneOptions): Promise { + insertOne(entityClassOrName: EntityTarget, doc: ObjectLiteral, options?: CollectionInsertOneOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.insertOne(metadata.tableName, doc, options); } @@ -476,7 +476,7 @@ export class MongoEntityManager extends EntityManager { /** * Returns if the collection is a capped collection. */ - isCapped(entityClassOrName: ObjectType | EntitySchema | string): Promise { + isCapped(entityClassOrName: EntityTarget): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.isCapped(metadata.tableName); } @@ -484,7 +484,7 @@ export class MongoEntityManager extends EntityManager { /** * Get the list of all indexes information for the collection. */ - listCollectionIndexes(entityClassOrName: ObjectType | EntitySchema | string, options?: { batchSize?: number, readPreference?: ReadPreference | string }): CommandCursor { + listCollectionIndexes(entityClassOrName: EntityTarget, options?: { batchSize?: number, readPreference?: ReadPreference | string }): CommandCursor { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.listCollectionIndexes(metadata.tableName, options); } @@ -492,7 +492,7 @@ export class MongoEntityManager extends EntityManager { /** * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection. */ - mapReduce(entityClassOrName: ObjectType | EntitySchema | string, map: Function | string, reduce: Function | string, options?: MapReduceOptions): Promise { + mapReduce(entityClassOrName: EntityTarget, map: Function | string, reduce: Function | string, options?: MapReduceOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.mapReduce(metadata.tableName, map, reduce, options); } @@ -501,7 +501,7 @@ export class MongoEntityManager extends EntityManager { * Return N number of parallel cursors for a collection allowing parallel reading of entire collection. * There are no ordering guarantees for returned results. */ - parallelCollectionScan(entityClassOrName: ObjectType | EntitySchema | string, options?: ParallelCollectionScanOptions): Promise[]> { + parallelCollectionScan(entityClassOrName: EntityTarget, options?: ParallelCollectionScanOptions): Promise[]> { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.parallelCollectionScan(metadata.tableName, options); } @@ -509,7 +509,7 @@ export class MongoEntityManager extends EntityManager { /** * Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections. */ - reIndex(entityClassOrName: ObjectType | EntitySchema | string): Promise { + reIndex(entityClassOrName: EntityTarget): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.reIndex(metadata.tableName); } @@ -517,7 +517,7 @@ export class MongoEntityManager extends EntityManager { /** * Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections. */ - rename(entityClassOrName: ObjectType | EntitySchema | string, newName: string, options?: { dropTarget?: boolean }): Promise> { + rename(entityClassOrName: EntityTarget, newName: string, options?: { dropTarget?: boolean }): Promise> { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.rename(metadata.tableName, newName, options); } @@ -525,7 +525,7 @@ export class MongoEntityManager extends EntityManager { /** * Replace a document on MongoDB. */ - replaceOne(entityClassOrName: ObjectType | EntitySchema | string, query: ObjectLiteral, doc: ObjectLiteral, options?: ReplaceOneOptions): Promise { + replaceOne(entityClassOrName: EntityTarget, query: ObjectLiteral, doc: ObjectLiteral, options?: ReplaceOneOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.replaceOne(metadata.tableName, query, doc, options); } @@ -533,12 +533,12 @@ export class MongoEntityManager extends EntityManager { /** * Get all the collection statistics. */ - stats(entityClassOrName: ObjectType | EntitySchema | string, options?: { scale: number }): Promise { + stats(entityClassOrName: EntityTarget, options?: { scale: number }): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.stats(metadata.tableName, options); } - watch(entityClassOrName: ObjectType | EntitySchema | string, pipeline?: Object[], options?: ChangeStreamOptions): ChangeStream { + watch(entityClassOrName: EntityTarget, pipeline?: Object[], options?: ChangeStreamOptions): ChangeStream { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.watch(metadata.tableName, pipeline, options); } @@ -546,7 +546,7 @@ export class MongoEntityManager extends EntityManager { /** * Update multiple documents on MongoDB. */ - updateMany(entityClassOrName: ObjectType | EntitySchema | string, query: ObjectLiteral, update: ObjectLiteral, options?: { upsert?: boolean, w?: any, wtimeout?: number, j?: boolean }): Promise { + updateMany(entityClassOrName: EntityTarget, query: ObjectLiteral, update: ObjectLiteral, options?: { upsert?: boolean, w?: any, wtimeout?: number, j?: boolean }): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.updateMany(metadata.tableName, query, update, options); } @@ -554,7 +554,7 @@ export class MongoEntityManager extends EntityManager { /** * Update a single document on MongoDB. */ - updateOne(entityClassOrName: ObjectType | EntitySchema | string, query: ObjectLiteral, update: ObjectLiteral, options?: ReplaceOneOptions): Promise { + updateOne(entityClassOrName: EntityTarget, query: ObjectLiteral, update: ObjectLiteral, options?: ReplaceOneOptions): Promise { const metadata = this.connection.getMetadata(entityClassOrName); return this.queryRunner.updateOne(metadata.tableName, query, update, options); } @@ -570,7 +570,7 @@ export class MongoEntityManager extends EntityManager { if (!optionsOrConditions) return undefined; - if (FindOptionsUtils.isFindManyOptions(optionsOrConditions)) + if (FindOptionsUtils.isFindManyOptions(optionsOrConditions)) // If where condition is passed as a string which contains sql we have to ignore // as mongo is not a sql database return typeof optionsOrConditions.where === "string" @@ -587,7 +587,7 @@ export class MongoEntityManager extends EntityManager { if (!optionsOrConditions) return undefined; - if (FindOptionsUtils.isFindOneOptions(optionsOrConditions)) + if (FindOptionsUtils.isFindOneOptions(optionsOrConditions)) // If where condition is passed as a string which contains sql we have to ignore // as mongo is not a sql database return typeof optionsOrConditions.where === "string" @@ -630,6 +630,18 @@ export class MongoEntityManager extends EntityManager { * Ensures given id is an id for query. */ protected convertMixedCriteria(metadata: EntityMetadata, idMap: any): ObjectLiteral { + const objectIdInstance = PlatformTools.load("mongodb").ObjectID; + + // check first if it's ObjectId compatible: + // string, number, Buffer, ObjectId or ObjectId-like + if (objectIdInstance.isValid(idMap)) { + return { + "_id": new objectIdInstance(idMap) + }; + } + + // if it's some other type of object build a query from the columns + // this check needs to be after the ObjectId check, because a valid ObjectId is also an Object instance if (idMap instanceof Object) { return metadata.columns.reduce((query, column) => { const columnValue = column.getEntityValue(idMap); @@ -639,10 +651,11 @@ export class MongoEntityManager extends EntityManager { }, {} as any); } - // means idMap is just object id - const objectIdInstance = PlatformTools.load("mongodb").ObjectID; + // last resort: try to convert it to an ObjectID anyway + // most likely it will fail, but we want to be backwards compatible and keep the same thrown Errors. + // it can still pass with null/undefined return { - "_id": (idMap instanceof objectIdInstance) ? idMap : new objectIdInstance(idMap) + "_id": new objectIdInstance(idMap) }; } diff --git a/src/entity-schema/EntitySchemaColumnOptions.ts b/src/entity-schema/EntitySchemaColumnOptions.ts index 80e75363fa3..9d4a50b055b 100644 --- a/src/entity-schema/EntitySchemaColumnOptions.ts +++ b/src/entity-schema/EntitySchemaColumnOptions.ts @@ -1,6 +1,6 @@ import {ColumnType} from "../driver/types/ColumnTypes"; import {ValueTransformer} from "../decorator/options/ValueTransformer"; -import { SpatialColumnOptions } from "../decorator/options/SpatialColumnOptions"; +import {SpatialColumnOptions} from "../decorator/options/SpatialColumnOptions"; export interface EntitySchemaColumnOptions extends SpatialColumnOptions { diff --git a/src/entity-schema/EntitySchemaRelationOptions.ts b/src/entity-schema/EntitySchemaRelationOptions.ts index 953caaccd6e..0502fc67e66 100644 --- a/src/entity-schema/EntitySchemaRelationOptions.ts +++ b/src/entity-schema/EntitySchemaRelationOptions.ts @@ -55,7 +55,7 @@ export interface EntitySchemaRelationOptions { /** * Join column options of this column. If set to true then it simply means that it has a join column. */ - joinColumn?: boolean|JoinColumnOptions; + joinColumn?: boolean|JoinColumnOptions|JoinColumnOptions[]; /** * Indicates if this is a parent (can be only many-to-one relation) relation in the tree tables. diff --git a/src/entity-schema/EntitySchemaTransformer.ts b/src/entity-schema/EntitySchemaTransformer.ts index 81808816139..e53eb2c981b 100644 --- a/src/entity-schema/EntitySchemaTransformer.ts +++ b/src/entity-schema/EntitySchemaTransformer.ts @@ -151,13 +151,17 @@ export class EntitySchemaTransformer { }; metadataArgsStorage.joinColumns.push(joinColumn); } else { - const joinColumn: JoinColumnMetadataArgs = { - target: options.target || options.name, - propertyName: relationName, - name: relationSchema.joinColumn.name, - referencedColumnName: relationSchema.joinColumn.referencedColumnName - }; - metadataArgsStorage.joinColumns.push(joinColumn); + const joinColumnsOptions = Array.isArray(relationSchema.joinColumn) ? relationSchema.joinColumn : [relationSchema.joinColumn]; + + for (const joinColumnOption of joinColumnsOptions) { + const joinColumn: JoinColumnMetadataArgs = { + target: options.target || options.name, + propertyName: relationName, + name: joinColumnOption.name, + referencedColumnName: joinColumnOption.referencedColumnName + }; + metadataArgsStorage.joinColumns.push(joinColumn); + } } } diff --git a/src/error/EntityMetadataNotFoundError.ts b/src/error/EntityMetadataNotFoundError.ts index 3365617f86f..ecb43fd7cf7 100644 --- a/src/error/EntityMetadataNotFoundError.ts +++ b/src/error/EntityMetadataNotFoundError.ts @@ -1,3 +1,4 @@ +import {EntityTarget} from "../common/EntityTarget"; import {EntitySchema} from "../index"; /** @@ -5,7 +6,7 @@ import {EntitySchema} from "../index"; export class EntityMetadataNotFoundError extends Error { name = "EntityMetadataNotFound"; - constructor(target: Function|EntitySchema|string) { + constructor(target: EntityTarget) { super(); Object.setPrototypeOf(this, EntityMetadataNotFoundError.prototype); let targetName: string; @@ -13,10 +14,12 @@ export class EntityMetadataNotFoundError extends Error { targetName = target.options.name; } else if (typeof target === "function") { targetName = target.name; + } else if (typeof target === "object" && "name" in target) { + targetName = target.name; } else { targetName = target; } this.message = `No metadata for "${targetName}" was found.`; } -} \ No newline at end of file +} diff --git a/src/error/EntityNotFoundError.ts b/src/error/EntityNotFoundError.ts index df6d21404ff..446b79aaabe 100644 --- a/src/error/EntityNotFoundError.ts +++ b/src/error/EntityNotFoundError.ts @@ -1,4 +1,4 @@ -import {ObjectType} from "../common/ObjectType"; +import {EntityTarget} from "../common/EntityTarget"; import {EntitySchema} from "../index"; /** @@ -7,7 +7,7 @@ import {EntitySchema} from "../index"; export class EntityNotFoundError extends Error { name = "EntityNotFound"; - constructor(entityClass: ObjectType|EntitySchema|string, criteria: any) { + constructor(entityClass: EntityTarget, criteria: any) { super(); Object.setPrototypeOf(this, EntityNotFoundError.prototype); let targetName: string; @@ -15,6 +15,8 @@ export class EntityNotFoundError extends Error { targetName = entityClass.options.name; } else if (typeof entityClass === "function") { targetName = entityClass.name; + } else if (typeof entityClass === "object" && "name" in entityClass) { + targetName = entityClass.name; } else { targetName = entityClass; } diff --git a/src/error/FindRelationsNotFoundError.ts b/src/error/FindRelationsNotFoundError.ts index 8b44bfc620f..99a9e509f91 100644 --- a/src/error/FindRelationsNotFoundError.ts +++ b/src/error/FindRelationsNotFoundError.ts @@ -7,10 +7,10 @@ export class FindRelationsNotFoundError extends Error { super(); Object.setPrototypeOf(this, FindRelationsNotFoundError.prototype); if (notFoundRelations.length === 1) { - this.message = `Relation "${notFoundRelations[0]}" was not found, please check if it is correct and really exist in your entity.`; + this.message = `Relation "${notFoundRelations[0]}" was not found; please check if it is correct and really exists in your entity.`; } else { - this.message = `Relations ${notFoundRelations.map(relation => `"${relation}"`).join(", ")} were not found, please check if relations are correct and they exist in your entities.`; + this.message = `Relations ${notFoundRelations.map(relation => `"${relation}"`).join(", ")} were not found; please check if relations are correct and they exist in your entities.`; } } -} \ No newline at end of file +} diff --git a/src/error/MissingDriverError.ts b/src/error/MissingDriverError.ts index a5634f46791..5d2518fb553 100644 --- a/src/error/MissingDriverError.ts +++ b/src/error/MissingDriverError.ts @@ -7,7 +7,7 @@ export class MissingDriverError extends Error { constructor(driverType: string) { super(); Object.setPrototypeOf(this, MissingDriverError.prototype); - this.message = `Wrong driver: "${driverType}" given. Supported drivers are: "cordova", "expo", "mariadb", "mongodb", "mssql", "mysql", "oracle", "postgres", "sqlite", "sqljs", "react-native", "aurora-data-api", "aurora-data-api-pg".`; + this.message = `Wrong driver: "${driverType}" given. Supported drivers are: "cordova", "expo", "mariadb", "mongodb", "mssql", "mysql", "oracle", "postgres", "sqlite", "better-sqlite3", "sqljs", "react-native", "aurora-data-api", "aurora-data-api-pg".`; } } diff --git a/src/error/QueryFailedError.ts b/src/error/QueryFailedError.ts index 9e2a63d3e61..26446280a1c 100644 --- a/src/error/QueryFailedError.ts +++ b/src/error/QueryFailedError.ts @@ -1,4 +1,4 @@ -import { ObjectUtils } from "../util/ObjectUtils"; +import {ObjectUtils} from "../util/ObjectUtils"; /** * Thrown when query execution has failed. @@ -20,4 +20,4 @@ export class QueryFailedError extends Error { }); } -} \ No newline at end of file +} diff --git a/src/error/RepositoryNotFoundError.ts b/src/error/RepositoryNotFoundError.ts index 620078e751d..1a53bc06206 100644 --- a/src/error/RepositoryNotFoundError.ts +++ b/src/error/RepositoryNotFoundError.ts @@ -1,3 +1,4 @@ +import {EntityTarget} from "../common/EntityTarget"; import {EntitySchema} from "../index"; /** @@ -6,7 +7,7 @@ import {EntitySchema} from "../index"; export class RepositoryNotFoundError extends Error { name = "RepositoryNotFoundError"; - constructor(connectionName: string, entityClass: Function|EntitySchema|string) { + constructor(connectionName: string, entityClass: EntityTarget) { super(); Object.setPrototypeOf(this, RepositoryNotFoundError.prototype); let targetName: string; @@ -14,6 +15,8 @@ export class RepositoryNotFoundError extends Error { targetName = entityClass.options.name; } else if (typeof entityClass === "function") { targetName = entityClass.name; + } else if (typeof entityClass === "object" && "name" in entityClass) { + targetName = entityClass.name; } else { targetName = entityClass; } @@ -21,4 +24,4 @@ export class RepositoryNotFoundError extends Error { `current "${connectionName}" connection?`; } -} \ No newline at end of file +} diff --git a/src/error/RepositoryNotTreeError.ts b/src/error/RepositoryNotTreeError.ts index c1533ece2e6..082f01c7f34 100644 --- a/src/error/RepositoryNotTreeError.ts +++ b/src/error/RepositoryNotTreeError.ts @@ -1,3 +1,4 @@ +import {EntityTarget} from "../common/EntityTarget"; import {EntitySchema} from "../index"; /** @@ -6,18 +7,20 @@ import {EntitySchema} from "../index"; export class RepositoryNotTreeError extends Error { name = "RepositoryNotTreeError"; - constructor(target: Function|EntitySchema|string) { + constructor(entityClass: EntityTarget) { super(); Object.setPrototypeOf(this, RepositoryNotTreeError.prototype); let targetName: string; - if (target instanceof EntitySchema) { - targetName = target.options.name; - } else if (typeof target === "function") { - targetName = target.name; + if (entityClass instanceof EntitySchema) { + targetName = entityClass.options.name; + } else if (typeof entityClass === "function") { + targetName = entityClass.name; + } else if (typeof entityClass === "object" && "name" in entityClass) { + targetName = entityClass.name; } else { - targetName = target; + targetName = entityClass; } this.message = `Repository of the "${targetName}" class is not a TreeRepository. Try to apply @Tree decorator on your entity.`; } -} \ No newline at end of file +} diff --git a/src/find-options/FindOneOptions.ts b/src/find-options/FindOneOptions.ts index 309e262020d..dcb016a474d 100644 --- a/src/find-options/FindOneOptions.ts +++ b/src/find-options/FindOneOptions.ts @@ -1,3 +1,4 @@ +import {EntityFieldsNames} from "../common/EntityFieldsNames"; import {JoinOptions} from "./JoinOptions"; import {ObjectLiteral} from "../common/ObjectLiteral"; import {FindConditions} from "./FindConditions"; @@ -30,7 +31,7 @@ export interface FindOneOptions { /** * Order, in which entities should be ordered. */ - order?: { [P in keyof Entity]?: "ASC"|"DESC"|1|-1 }; + order?: { [P in EntityFieldsNames]?: "ASC"|"DESC"|1|-1 }; /** * Enables or disables query result caching. @@ -59,4 +60,9 @@ export interface FindOneOptions { */ loadEagerRelations?: boolean; + /** + * If this is set to true, SELECT query in a `find` method will be executed in a transaction. + */ + transaction?: boolean + } diff --git a/src/find-options/FindOperator.ts b/src/find-options/FindOperator.ts index a3b21c9f14f..6189e31fe17 100644 --- a/src/find-options/FindOperator.ts +++ b/src/find-options/FindOperator.ts @@ -1,6 +1,7 @@ +import {ObjectLiteral} from "../common/ObjectLiteral"; import {FindOperatorType} from "./FindOperatorType"; -import {Connection} from "../"; -import {OracleDriver} from "../driver/oracle/OracleDriver"; + +type SqlGeneratorType = (aliasPath: string) => string; /** * Find Operator used in Find Conditions. @@ -21,6 +22,11 @@ export class FindOperator { */ private _value: T|FindOperator; + /** + * ObjectLiteral parameters. + */ + private _objectLiteralParameters: ObjectLiteral|undefined; + /** * Indicates if parameter is used or not for this operator. */ @@ -31,15 +37,29 @@ export class FindOperator { */ private _multipleParameters: boolean; + /** + * SQL generator + */ + private _getSql: SqlGeneratorType|undefined; + // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- - constructor(type: FindOperatorType, value: T|FindOperator, useParameter: boolean = true, multipleParameters: boolean = false) { + constructor( + type: FindOperatorType, + value: T|FindOperator, + useParameter: boolean = true, + multipleParameters: boolean = false, + getSql?: SqlGeneratorType, + objectLiteralParameters?: ObjectLiteral, + ) { this._type = type; this._value = value; this._useParameter = useParameter; this._multipleParameters = multipleParameters; + this._getSql = getSql; + this._objectLiteralParameters = objectLiteralParameters; } // ------------------------------------------------------------------------- @@ -68,6 +88,13 @@ export class FindOperator { return this._multipleParameters; } + /** + * Gets the Type of this FindOperator + */ + get type(): string { + return this._type; + } + /** * Gets the final value needs to be used as parameter value. */ @@ -78,53 +105,34 @@ export class FindOperator { return this._value; } - // ------------------------------------------------------------------------- - // Public Methods - // ------------------------------------------------------------------------- + /** + * Gets ObjectLiteral parameters. + */ + get objectLiteralParameters(): ObjectLiteral|undefined { + if (this._value instanceof FindOperator) + return this._value.objectLiteralParameters; + + return this._objectLiteralParameters; + } + /** - * Gets SQL needs to be inserted into final query. + * Gets the child FindOperator if it exists */ - toSql(connection: Connection, aliasPath: string, parameters: string[]): string { - switch (this._type) { - case "not": - if (this._value instanceof FindOperator) { - return `NOT(${this._value.toSql(connection, aliasPath, parameters)})`; - } else { - return `${aliasPath} != ${parameters[0]}`; - } - case "lessThan": - return `${aliasPath} < ${parameters[0]}`; - case "lessThanOrEqual": - return `${aliasPath} <= ${parameters[0]}`; - case "moreThan": - return `${aliasPath} > ${parameters[0]}`; - case "moreThanOrEqual": - return `${aliasPath} >= ${parameters[0]}`; - case "equal": - return `${aliasPath} = ${parameters[0]}`; - case "like": - return `${aliasPath} LIKE ${parameters[0]}`; - case "between": - return `${aliasPath} BETWEEN ${parameters[0]} AND ${parameters[1]}`; - case "in": - if (connection.driver instanceof OracleDriver && parameters.length === 0) { - return `${aliasPath} IN (null)`; - } - return `${aliasPath} IN (${parameters.join(", ")})`; - case "any": - return `${aliasPath} = ANY(${parameters[0]})`; - case "isNull": - return `${aliasPath} IS NULL`; - case "raw": - if (this.value instanceof Function) { - return this.value(aliasPath); - } else { - return `${aliasPath} = ${this.value}`; - } - } - - return ""; + get child(): FindOperator|undefined { + if (this._value instanceof FindOperator) + return this._value; + + return undefined; } -} \ No newline at end of file + /** + * Gets the SQL generator + */ + get getSql(): SqlGeneratorType|undefined { + if (this._value instanceof FindOperator) + return this._value.getSql; + + return this._getSql; + } +} diff --git a/src/find-options/FindOperatorType.ts b/src/find-options/FindOperatorType.ts index 3830dec8779..7b9e04923a0 100644 --- a/src/find-options/FindOperatorType.ts +++ b/src/find-options/FindOperatorType.ts @@ -11,5 +11,6 @@ export type FindOperatorType = "not" | "in" | "any" | "isNull" + | "ilike" | "like" | "raw"; diff --git a/src/find-options/FindOptionsUtils.ts b/src/find-options/FindOptionsUtils.ts index 4ecdcd7114f..210d75e23e9 100644 --- a/src/find-options/FindOptionsUtils.ts +++ b/src/find-options/FindOptionsUtils.ts @@ -17,8 +17,8 @@ export class FindOptionsUtils { /** * Checks if given object is really instance of FindOneOptions interface. */ - static isFindOneOptions(obj: any): obj is FindOneOptions { - const possibleOptions: FindOneOptions = obj; + static isFindOneOptions(obj: any): obj is FindOneOptions { + const possibleOptions: FindOneOptions = obj; return possibleOptions && ( Array.isArray(possibleOptions.select) || @@ -34,15 +34,16 @@ export class FindOptionsUtils { possibleOptions.loadRelationIds instanceof Object || typeof possibleOptions.loadRelationIds === "boolean" || typeof possibleOptions.loadEagerRelations === "boolean" || - typeof possibleOptions.withDeleted === "boolean" + typeof possibleOptions.withDeleted === "boolean" || + typeof possibleOptions.transaction === "boolean" ); } /** * Checks if given object is really instance of FindManyOptions interface. */ - static isFindManyOptions(obj: any): obj is FindManyOptions { - const possibleOptions: FindManyOptions = obj; + static isFindManyOptions(obj: any): obj is FindManyOptions { + const possibleOptions: FindManyOptions = obj; return possibleOptions && ( this.isFindOneOptions(possibleOptions) || typeof (possibleOptions as FindManyOptions).skip === "number" || @@ -84,6 +85,10 @@ export class FindOptionsUtils { if (!options || (!this.isFindOneOptions(options) && !this.isFindManyOptions(options))) return qb; + if (options.transaction === true) { + qb.expressionMap.useTransaction = true; + } + if (!qb.expressionMap.mainAlias || !qb.expressionMap.mainAlias.hasMetadata) return qb; diff --git a/src/find-options/operator/Any.ts b/src/find-options/operator/Any.ts index 4b39792c6b2..3d66e20887a 100644 --- a/src/find-options/operator/Any.ts +++ b/src/find-options/operator/Any.ts @@ -4,6 +4,6 @@ import {FindOperator} from "../FindOperator"; * Find Options Operator. * Example: { someField: Any([...]) } */ -export function Any(value: T[]|FindOperator) { +export function Any(value: T[]|FindOperator): FindOperator { return new FindOperator("any", value as any); -} \ No newline at end of file +} diff --git a/src/find-options/operator/Between.ts b/src/find-options/operator/Between.ts index 798b6560199..cbb07b63b5e 100644 --- a/src/find-options/operator/Between.ts +++ b/src/find-options/operator/Between.ts @@ -4,6 +4,6 @@ import {FindOperator} from "../FindOperator"; * Find Options Operator. * Example: { someField: Between(x, y) } */ -export function Between(from: T|FindOperator, to: T|FindOperator) { +export function Between(from: T|FindOperator, to: T|FindOperator): FindOperator { return new FindOperator("between", [from, to] as any, true, true); -} \ No newline at end of file +} diff --git a/src/find-options/operator/ILike.ts b/src/find-options/operator/ILike.ts new file mode 100644 index 00000000000..1d3cfaffe46 --- /dev/null +++ b/src/find-options/operator/ILike.ts @@ -0,0 +1,9 @@ +import {FindOperator} from "../FindOperator"; + +/** + * Find Options Operator. + * Example: { someField: ILike("%SOME string%") } + */ +export function ILike(value: T|FindOperator) { + return new FindOperator("ilike", value); +} \ No newline at end of file diff --git a/src/find-options/operator/In.ts b/src/find-options/operator/In.ts index 97875e9a063..d907f43f140 100644 --- a/src/find-options/operator/In.ts +++ b/src/find-options/operator/In.ts @@ -4,6 +4,6 @@ import {FindOperator} from "../FindOperator"; * Find Options Operator. * Example: { someField: In([...]) } */ -export function In(value: T[]|FindOperator) { +export function In(value: T[]|FindOperator): FindOperator { return new FindOperator("in", value as any, true, true); -} \ No newline at end of file +} diff --git a/src/find-options/operator/Raw.ts b/src/find-options/operator/Raw.ts index cb0c163eb90..dfe6859e13b 100644 --- a/src/find-options/operator/Raw.ts +++ b/src/find-options/operator/Raw.ts @@ -1,9 +1,32 @@ import {FindOperator} from "../FindOperator"; +import {ObjectLiteral} from "../../common/ObjectLiteral"; /** * Find Options Operator. - * Example: { someField: Raw([...]) } + * Example: { someField: Raw("12") } */ -export function Raw(value: string|((columnAlias?: string) => string)) { - return new FindOperator("raw", value as any, false); -} \ No newline at end of file +export function Raw(value: string): FindOperator; + +/** + * Find Options Operator. + * Example: { someField: Raw((columnAlias) => `${columnAlias} = 5`) } + */ +export function Raw(sqlGenerator: ((columnAlias: string) => string)): FindOperator; + +/** + * Find Options Operator. + * For escaping parameters use next syntax: + * Example: { someField: Raw((columnAlias) => `${columnAlias} = :value`, { value: 5 }) } + */ +export function Raw(sqlGenerator: ((columnAlias: string) => string), parameters: ObjectLiteral): FindOperator; + +export function Raw( + valueOrSqlGenerator: string | ((columnAlias: string) => string), + sqlGeneratorParameters?: ObjectLiteral, +): FindOperator { + if (typeof valueOrSqlGenerator !== 'function') { + return new FindOperator("raw", valueOrSqlGenerator, false); + } + + return new FindOperator("raw", [], true, true, valueOrSqlGenerator, sqlGeneratorParameters); +} diff --git a/src/index.ts b/src/index.ts index 1acbfe541c2..a3e6b7d0203 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,17 +13,17 @@ import {PlatformTools} from "./platform/PlatformTools"; import {TreeRepository} from "./repository/TreeRepository"; import {MongoRepository} from "./repository/MongoRepository"; import {ConnectionOptionsReader} from "./connection/ConnectionOptionsReader"; -import {PromiseUtils} from "./util/PromiseUtils"; import {MongoEntityManager} from "./entity-manager/MongoEntityManager"; import {SqljsEntityManager} from "./entity-manager/SqljsEntityManager"; import {SelectQueryBuilder} from "./query-builder/SelectQueryBuilder"; -import {EntitySchema} from "./entity-schema/EntitySchema"; +import {EntityTarget} from "./common/EntityTarget"; // ------------------------------------------------------------------------- // Commonly Used exports // ------------------------------------------------------------------------- export * from "./container"; +export * from "./common/EntityTarget"; export * from "./common/ObjectType"; export * from "./common/ObjectLiteral"; export * from "./common/DeepPartial"; @@ -84,6 +84,7 @@ export * from "./find-options/operator/In"; export * from "./find-options/operator/IsNull"; export * from "./find-options/operator/LessThan"; export * from "./find-options/operator/LessThanOrEqual"; +export * from "./find-options/operator/ILike"; export * from "./find-options/operator/Like"; export * from "./find-options/operator/MoreThan"; export * from "./find-options/operator/MoreThanOrEqual"; @@ -98,6 +99,7 @@ export * from "./find-options/JoinOptions"; export * from "./find-options/OrderByCondition"; export * from "./find-options/FindOptionsUtils"; export * from "./logger/Logger"; +export * from "./logger/LoggerOptions"; export * from "./logger/AdvancedConsoleLogger"; export * from "./logger/SimpleConsoleLogger"; export * from "./logger/FileLogger"; @@ -119,6 +121,7 @@ export * from "./schema-builder/table/TableUnique"; export * from "./schema-builder/table/Table"; export * from "./driver/mongodb/typings"; export * from "./driver/types/DatabaseType"; +export * from "./driver/types/ReplicationMode"; export * from "./driver/sqlserver/MssqlParameter"; export {ConnectionOptionsReader} from "./connection/ConnectionOptionsReader"; @@ -138,29 +141,24 @@ export {InsertResult} from "./query-builder/result/InsertResult"; export {UpdateResult} from "./query-builder/result/UpdateResult"; export {DeleteResult} from "./query-builder/result/DeleteResult"; export {QueryRunner} from "./query-runner/QueryRunner"; -export {EntityManager} from "./entity-manager/EntityManager"; export {MongoEntityManager} from "./entity-manager/MongoEntityManager"; export {Migration} from "./migration/Migration"; export {MigrationExecutor} from "./migration/MigrationExecutor"; export {MigrationInterface} from "./migration/MigrationInterface"; export {DefaultNamingStrategy} from "./naming-strategy/DefaultNamingStrategy"; export {NamingStrategyInterface} from "./naming-strategy/NamingStrategyInterface"; -export {Repository} from "./repository/Repository"; -export {TreeRepository} from "./repository/TreeRepository"; -export {MongoRepository} from "./repository/MongoRepository"; export {FindOneOptions} from "./find-options/FindOneOptions"; export {FindManyOptions} from "./find-options/FindManyOptions"; export {InsertEvent} from "./subscriber/event/InsertEvent"; +export {LoadEvent} from "./subscriber/event/LoadEvent"; export {UpdateEvent} from "./subscriber/event/UpdateEvent"; export {RemoveEvent} from "./subscriber/event/RemoveEvent"; export {EntitySubscriberInterface} from "./subscriber/EntitySubscriberInterface"; -export {BaseEntity} from "./repository/BaseEntity"; export {EntitySchema} from "./entity-schema/EntitySchema"; export {EntitySchemaColumnOptions} from "./entity-schema/EntitySchemaColumnOptions"; export {EntitySchemaIndexOptions} from "./entity-schema/EntitySchemaIndexOptions"; export {EntitySchemaRelationOptions} from "./entity-schema/EntitySchemaRelationOptions"; export {ColumnType} from "./driver/types/ColumnTypes"; -export {PromiseUtils} from "./util/PromiseUtils"; // ------------------------------------------------------------------------- // Deprecated @@ -243,7 +241,7 @@ export async function createConnections(options?: ConnectionOptions[]): Promise< if (!options) options = await new ConnectionOptionsReader().all(); const connections = options.map(options => getConnectionManager().create(options)); - return PromiseUtils.runInSequence(connections, connection => connection.connect()); + return Promise.all(connections.map(connection => connection.connect())); } /** @@ -282,14 +280,14 @@ export function getSqljsManager(connectionName: string = "default"): SqljsEntity /** * Gets repository for the given entity class. */ -export function getRepository(entityClass: ObjectType|EntitySchema|string, connectionName: string = "default"): Repository { +export function getRepository(entityClass: EntityTarget, connectionName: string = "default"): Repository { return getConnectionManager().get(connectionName).getRepository(entityClass); } /** * Gets tree repository for the given entity class. */ -export function getTreeRepository(entityClass: ObjectType|string, connectionName: string = "default"): TreeRepository { +export function getTreeRepository(entityClass: EntityTarget, connectionName: string = "default"): TreeRepository { return getConnectionManager().get(connectionName).getTreeRepository(entityClass); } @@ -303,14 +301,14 @@ export function getCustomRepository(customRepository: ObjectType, connecti /** * Gets mongodb repository for the given entity class or name. */ -export function getMongoRepository(entityClass: ObjectType|string, connectionName: string = "default"): MongoRepository { +export function getMongoRepository(entityClass: EntityTarget, connectionName: string = "default"): MongoRepository { return getConnectionManager().get(connectionName).getMongoRepository(entityClass); } /** * Creates a new query builder. */ -export function createQueryBuilder(entityClass?: ObjectType|string, alias?: string, connectionName: string = "default"): SelectQueryBuilder { +export function createQueryBuilder(entityClass?: EntityTarget, alias?: string, connectionName: string = "default"): SelectQueryBuilder { if (entityClass) { return getRepository(entityClass, connectionName).createQueryBuilder(alias); } diff --git a/src/logger/DebugLogger.ts b/src/logger/DebugLogger.ts index 8943a4523b4..36e901187a9 100644 --- a/src/logger/DebugLogger.ts +++ b/src/logger/DebugLogger.ts @@ -1,3 +1,4 @@ +import debug from "debug"; import {Logger} from "./Logger"; import {QueryRunner} from "../"; import {PlatformTools} from "../platform/PlatformTools"; @@ -6,18 +7,16 @@ import {PlatformTools} from "../platform/PlatformTools"; * Performs logging of the events in TypeORM via debug library. */ export class DebugLogger implements Logger { - private debug = PlatformTools.load("debug"); + private debugQueryLog = debug("typeorm:query:log"); + private debugQueryError = debug("typeorm:query:error"); + private debugQuerySlow = debug("typeorm:query:slow"); + private debugSchemaBuild = debug("typeorm:schema"); + private debugMigration = debug("typeorm:migration"); + + private debugLog = debug("typeorm:log"); + private debugInfo = debug("typeorm:info"); + private debugWarn = debug("typeorm:warn"); - private debugQueryLog = this.debug("typeorm:query:log"); - private debugQueryError = this.debug("typeorm:query:error"); - private debugQuerySlow = this.debug("typeorm:query:slow"); - private debugSchemaBuild = this.debug("typeorm:schema"); - private debugMigration = this.debug("typeorm:migration"); - - private debugLog = this.debug("typeorm:log"); - private debugInfo = this.debug("typeorm:info"); - private debugWarn = this.debug("typeorm:warn"); - /** * Logs query and parameters used in it. */ @@ -29,7 +28,7 @@ export class DebugLogger implements Logger { } } } - + /** * Logs query that failed. */ @@ -42,7 +41,7 @@ export class DebugLogger implements Logger { this.debugQueryError("error: ", error); } } - + /** * Logs query that is slow. */ @@ -55,7 +54,7 @@ export class DebugLogger implements Logger { this.debugQuerySlow("execution time:", time); } } - + /** * Logs events from the schema build process. */ @@ -64,7 +63,7 @@ export class DebugLogger implements Logger { this.debugSchemaBuild(message); } } - + /** * Logs events from the migration run process. */ @@ -73,7 +72,7 @@ export class DebugLogger implements Logger { this.debugMigration(message); } } - + /** * Perform logging using given logger. * Log has its own level and message. diff --git a/src/logger/FileLogger.ts b/src/logger/FileLogger.ts index 3bdccda7bdd..e96d142ad2c 100644 --- a/src/logger/FileLogger.ts +++ b/src/logger/FileLogger.ts @@ -1,4 +1,5 @@ -import {LoggerOptions} from "./LoggerOptions"; +import {LoggerOptions, FileLoggerOptions} from "./LoggerOptions"; +import appRootPath from "app-root-path"; import {QueryRunner} from "../query-runner/QueryRunner"; import {Logger} from "./Logger"; import {PlatformTools} from "../platform/PlatformTools"; @@ -13,7 +14,10 @@ export class FileLogger implements Logger { // Constructor // ------------------------------------------------------------------------- - constructor(private options?: LoggerOptions) { + constructor( + private options?: LoggerOptions, + private fileLoggerOptions?: FileLoggerOptions, + ) { } // ------------------------------------------------------------------------- @@ -97,9 +101,13 @@ export class FileLogger implements Logger { */ protected write(strings: string|string[]) { strings = Array.isArray(strings) ? strings : [strings]; - const basePath = PlatformTools.load("app-root-path").path; + const basePath = appRootPath.path + "/"; + let logPath = "ormlogs.log"; + if (this.fileLoggerOptions && this.fileLoggerOptions.logPath) { + logPath = PlatformTools.pathNormalize(this.fileLoggerOptions.logPath); + } strings = (strings as string[]).map(str => "[" + new Date().toISOString() + "]" + str); - PlatformTools.appendFileSync(basePath + "/ormlogs.log", strings.join("\r\n") + "\r\n"); // todo: use async or implement promises? + PlatformTools.appendFileSync(basePath + logPath, strings.join("\r\n") + "\r\n"); // todo: use async or implement promises? } /** diff --git a/src/logger/Logger.ts b/src/logger/Logger.ts index 3bb677fbea6..4925cfa83e0 100644 --- a/src/logger/Logger.ts +++ b/src/logger/Logger.ts @@ -13,7 +13,7 @@ export interface Logger { /** * Logs query that is failed. */ - logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner): any; + logQueryError(error: string | Error, query: string, parameters?: any[], queryRunner?: QueryRunner): any; /** * Logs query that is slow. @@ -36,4 +36,4 @@ export interface Logger { */ log(level: "log"|"info"|"warn", message: any, queryRunner?: QueryRunner): any; -} \ No newline at end of file +} diff --git a/src/logger/LoggerOptions.ts b/src/logger/LoggerOptions.ts index 67049d5a4f5..adfff93488b 100644 --- a/src/logger/LoggerOptions.ts +++ b/src/logger/LoggerOptions.ts @@ -2,3 +2,13 @@ * Logging options. */ export type LoggerOptions = boolean|"all"|("query"|"schema"|"error"|"warn"|"info"|"log"|"migration")[]; + +/** + * File logging option. + */ +export type FileLoggerOptions = { + /** + * Specify custom path for log file, relative to application root + */ + logPath: string; +}; diff --git a/src/metadata-args/EntityRepositoryMetadataArgs.ts b/src/metadata-args/EntityRepositoryMetadataArgs.ts index 1b7e3e0dbb3..fd55676630a 100644 --- a/src/metadata-args/EntityRepositoryMetadataArgs.ts +++ b/src/metadata-args/EntityRepositoryMetadataArgs.ts @@ -1,4 +1,4 @@ -import { EntitySchema } from "../entity-schema/EntitySchema"; +import { EntityTarget } from "../common/EntityTarget"; /** * Arguments for EntityRepositoryMetadata class, helps to construct an EntityRepositoryMetadata object. @@ -13,6 +13,6 @@ export interface EntityRepositoryMetadataArgs { /** * Entity managed by this custom repository. */ - readonly entity?: Function|string|EntitySchema; + readonly entity?: EntityTarget; } diff --git a/src/metadata-args/RelationCountMetadataArgs.ts b/src/metadata-args/RelationCountMetadataArgs.ts index 31f003adc10..14c1e8cf08d 100644 --- a/src/metadata-args/RelationCountMetadataArgs.ts +++ b/src/metadata-args/RelationCountMetadataArgs.ts @@ -1,4 +1,5 @@ import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder"; + /** * Arguments for RelationCountMetadata class. */ diff --git a/src/metadata-args/RelationIdMetadataArgs.ts b/src/metadata-args/RelationIdMetadataArgs.ts index 8abec551876..5b5f627b642 100644 --- a/src/metadata-args/RelationIdMetadataArgs.ts +++ b/src/metadata-args/RelationIdMetadataArgs.ts @@ -1,4 +1,5 @@ import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder"; + /** * Arguments for RelationIdMetadataArgs class. */ diff --git a/src/metadata-args/TreeMetadataArgs.ts b/src/metadata-args/TreeMetadataArgs.ts index 021cd48bdaa..bd1897db37d 100644 --- a/src/metadata-args/TreeMetadataArgs.ts +++ b/src/metadata-args/TreeMetadataArgs.ts @@ -1,4 +1,5 @@ import {TreeType} from "../metadata/types/TreeTypes"; +import {ClosureTreeOptions} from "../metadata/types/ClosureTreeOptions"; /** * Stores metadata collected for Tree entities. @@ -15,4 +16,8 @@ export interface TreeMetadataArgs { */ type: TreeType; + /** + * Tree options + */ + options?: ClosureTreeOptions; } diff --git a/src/metadata-builder/ClosureJunctionEntityMetadataBuilder.ts b/src/metadata-builder/ClosureJunctionEntityMetadataBuilder.ts index 36250479a09..6fedeb50916 100644 --- a/src/metadata-builder/ClosureJunctionEntityMetadataBuilder.ts +++ b/src/metadata-builder/ClosureJunctionEntityMetadataBuilder.ts @@ -32,7 +32,7 @@ export class ClosureJunctionEntityMetadataBuilder { connection: this.connection, args: { target: "", - name: parentClosureEntityMetadata.tableNameWithoutPrefix, + name: parentClosureEntityMetadata.treeOptions && parentClosureEntityMetadata.treeOptions.closureTableName ? parentClosureEntityMetadata.treeOptions.closureTableName : parentClosureEntityMetadata.tableNameWithoutPrefix, type: "closure-junction" } }); @@ -48,7 +48,7 @@ export class ClosureJunctionEntityMetadataBuilder { args: { target: "", mode: "virtual", - propertyName: primaryColumn.propertyName + "_ancestor", // todo: naming strategy + propertyName: parentClosureEntityMetadata.treeOptions && parentClosureEntityMetadata.treeOptions.ancestorColumnName ? parentClosureEntityMetadata.treeOptions.ancestorColumnName(primaryColumn) : primaryColumn.propertyName + "_ancestor", options: { primary: true, length: primaryColumn.length, @@ -64,7 +64,7 @@ export class ClosureJunctionEntityMetadataBuilder { args: { target: "", mode: "virtual", - propertyName: primaryColumn.propertyName + "_descendant", + propertyName: parentClosureEntityMetadata.treeOptions && parentClosureEntityMetadata.treeOptions.descendantColumnName ? parentClosureEntityMetadata.treeOptions.descendantColumnName(primaryColumn) : primaryColumn.propertyName + "_descendant", options: { primary: true, length: primaryColumn.length, diff --git a/src/metadata-builder/EntityMetadataBuilder.ts b/src/metadata-builder/EntityMetadataBuilder.ts index ab5cf5d1fe1..c3554add3fb 100644 --- a/src/metadata-builder/EntityMetadataBuilder.ts +++ b/src/metadata-builder/EntityMetadataBuilder.ts @@ -9,6 +9,7 @@ import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage"; import {EmbeddedMetadataArgs} from "../metadata-args/EmbeddedMetadataArgs"; import {RelationIdMetadata} from "../metadata/RelationIdMetadata"; import {RelationCountMetadata} from "../metadata/RelationCountMetadata"; +import { EventListenerTypes } from "../metadata/types/EventListenerTypes"; import {MetadataUtils} from "./MetadataUtils"; import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs"; import {JunctionEntityMetadataBuilder} from "./JunctionEntityMetadataBuilder"; @@ -335,7 +336,12 @@ export class EntityMetadataBuilder { const entityInheritance = this.metadataArgsStorage.findInheritanceType(entityMetadata.target); const discriminatorValue = this.metadataArgsStorage.findDiscriminatorValue(entityMetadata.target); - entityMetadata.discriminatorValue = discriminatorValue ? discriminatorValue.value : (entityMetadata.target as any).name; // todo: pass this to naming strategy to generate a name + + if (typeof discriminatorValue !== "undefined") { + entityMetadata.discriminatorValue = discriminatorValue.value; + } else { + entityMetadata.discriminatorValue = (entityMetadata.target as any).name; + } // if single table inheritance is used, we need to mark all embedded columns as nullable entityMetadata.embeddeds = this.createEmbeddedsRecursively(entityMetadata, this.metadataArgsStorage.filterEmbeddeds(entityMetadata.inheritanceTree)) @@ -610,13 +616,13 @@ export class EntityMetadataBuilder { entityMetadata.treeChildrenRelation = entityMetadata.relations.find(relation => relation.isTreeChildren); entityMetadata.columns = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.columnsFromTree), entityMetadata.ownColumns); entityMetadata.listeners = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.listenersFromTree), entityMetadata.ownListeners); - entityMetadata.afterLoadListeners = entityMetadata.listeners.filter(listener => listener.type === "after-load"); - entityMetadata.afterInsertListeners = entityMetadata.listeners.filter(listener => listener.type === "after-insert"); - entityMetadata.afterUpdateListeners = entityMetadata.listeners.filter(listener => listener.type === "after-update"); - entityMetadata.afterRemoveListeners = entityMetadata.listeners.filter(listener => listener.type === "after-remove"); - entityMetadata.beforeInsertListeners = entityMetadata.listeners.filter(listener => listener.type === "before-insert"); - entityMetadata.beforeUpdateListeners = entityMetadata.listeners.filter(listener => listener.type === "before-update"); - entityMetadata.beforeRemoveListeners = entityMetadata.listeners.filter(listener => listener.type === "before-remove"); + entityMetadata.afterLoadListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_LOAD); + entityMetadata.afterInsertListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_INSERT); + entityMetadata.afterUpdateListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_UPDATE); + entityMetadata.afterRemoveListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.AFTER_REMOVE); + entityMetadata.beforeInsertListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_INSERT); + entityMetadata.beforeUpdateListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_UPDATE); + entityMetadata.beforeRemoveListeners = entityMetadata.listeners.filter(listener => listener.type === EventListenerTypes.BEFORE_REMOVE); entityMetadata.indices = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.indicesFromTree), entityMetadata.ownIndices); entityMetadata.uniques = entityMetadata.embeddeds.reduce((columns, embedded) => columns.concat(embedded.uniquesFromTree), entityMetadata.ownUniques); entityMetadata.primaryColumns = entityMetadata.columns.filter(column => column.isPrimary); diff --git a/src/metadata-builder/EntityMetadataValidator.ts b/src/metadata-builder/EntityMetadataValidator.ts index 33329f0a8a7..9e3fe488d5b 100644 --- a/src/metadata-builder/EntityMetadataValidator.ts +++ b/src/metadata-builder/EntityMetadataValidator.ts @@ -58,18 +58,21 @@ export class EntityMetadataValidator { // validate if table is using inheritance it has a discriminator // also validate if discriminator values are not empty and not repeated - if (entityMetadata.inheritancePattern === "STI") { + if (entityMetadata.inheritancePattern === "STI" || entityMetadata.tableType === "entity-child") { if (!entityMetadata.discriminatorColumn) throw new Error(`Entity ${entityMetadata.name} using single-table inheritance, it should also have a discriminator column. Did you forget to put discriminator column options?`); - if (["", undefined, null].indexOf(entityMetadata.discriminatorValue) !== -1) - throw new Error(`Entity ${entityMetadata.name} has empty discriminator value. Discriminator value should not be empty.`); + if (typeof entityMetadata.discriminatorValue === "undefined") + throw new Error(`Entity ${entityMetadata.name} has an undefined discriminator value. Discriminator value should be defined.`); const sameDiscriminatorValueEntityMetadata = allEntityMetadatas.find(metadata => { - return metadata !== entityMetadata && metadata.discriminatorValue === entityMetadata.discriminatorValue; + return metadata !== entityMetadata + && (metadata.inheritancePattern === "STI" || metadata.tableType === "entity-child") + && metadata.discriminatorValue === entityMetadata.discriminatorValue + && metadata.inheritanceTree.some(parent => entityMetadata.inheritanceTree.indexOf(parent) !== -1); }); if (sameDiscriminatorValueEntityMetadata) - throw new Error(`Entities ${entityMetadata.name} and ${sameDiscriminatorValueEntityMetadata.name} as equal discriminator values. Make sure their discriminator values are not equal using @DiscriminatorValue decorator.`); + throw new Error(`Entities ${entityMetadata.name} and ${sameDiscriminatorValueEntityMetadata.name} have the same discriminator values. Make sure they are different while using the @ChildEntity decorator.`); } entityMetadata.relationCounts.forEach(relationCount => { diff --git a/src/metadata-builder/JunctionEntityMetadataBuilder.ts b/src/metadata-builder/JunctionEntityMetadataBuilder.ts index e5464329075..9fbb1ed7338 100644 --- a/src/metadata-builder/JunctionEntityMetadataBuilder.ts +++ b/src/metadata-builder/JunctionEntityMetadataBuilder.ts @@ -113,6 +113,7 @@ export class JunctionEntityMetadataBuilder { && (inverseReferencedColumn.generationStrategy === "uuid" || inverseReferencedColumn.type === "uuid") ? "36" : inverseReferencedColumn.length, // fix https://github.com/typeorm/typeorm/issues/3604 + width: inverseReferencedColumn.width, // fix https://github.com/typeorm/typeorm/issues/6442 type: inverseReferencedColumn.type, precision: inverseReferencedColumn.precision, scale: inverseReferencedColumn.scale, diff --git a/src/metadata/ColumnMetadata.ts b/src/metadata/ColumnMetadata.ts index 395c0f4faf7..86f3d742214 100644 --- a/src/metadata/ColumnMetadata.ts +++ b/src/metadata/ColumnMetadata.ts @@ -8,7 +8,6 @@ import {Connection} from "../connection/Connection"; import {OrmUtils} from "../util/OrmUtils"; import {ValueTransformer} from "../decorator/options/ValueTransformer"; import {MongoDriver} from "../driver/mongodb/MongoDriver"; -import {PromiseUtils} from "../util/PromiseUtils"; import {FindOperator} from "../find-options/FindOperator"; import {ApplyValueTransformers} from "../util/ApplyValueTransformers"; @@ -422,6 +421,8 @@ export class ColumnMetadata { this.type = options.connection.driver.mappedDataTypes.updateDate; if (!this.default) this.default = () => options.connection.driver.mappedDataTypes.updateDateDefault; + if (!this.onUpdate) + this.onUpdate = options.connection.driver.mappedDataTypes.updateDateDefault; if (this.precision === undefined && options.connection.driver.mappedDataTypes.updateDatePrecision) this.precision = options.connection.driver.mappedDataTypes.updateDatePrecision; } @@ -478,7 +479,7 @@ export class ColumnMetadata { } // this is bugfix for #720 when increment number is bigint we need to make sure its a string - if ((this.generationStrategy === "increment" || this.generationStrategy === "rowid") && this.type === "bigint") + if ((this.generationStrategy === "increment" || this.generationStrategy === "rowid") && this.type === "bigint" && value !== null) value = String(value); map[useDatabaseName ? this.databaseName : this.propertyName] = value; @@ -489,7 +490,7 @@ export class ColumnMetadata { } else { // no embeds - no problems. Simply return column property name and its value of the entity // this is bugfix for #720 when increment number is bigint we need to make sure its a string - if ((this.generationStrategy === "increment" || this.generationStrategy === "rowid") && this.type === "bigint") + if ((this.generationStrategy === "increment" || this.generationStrategy === "rowid") && this.type === "bigint" && value !== null) value = String(value); return { [useDatabaseName ? this.databaseName : this.propertyName]: value }; @@ -592,21 +593,21 @@ export class ColumnMetadata { if (this.relationMetadata && this.referencedColumn) { const relatedEntity = this.relationMetadata.getEntityValue(embeddedObject); if (relatedEntity && relatedEntity instanceof Object && !(relatedEntity instanceof FindOperator)) { - value = this.referencedColumn.getEntityValue(PromiseUtils.extractValue(relatedEntity)); + value = this.referencedColumn.getEntityValue(relatedEntity); } else if (embeddedObject[this.propertyName] && embeddedObject[this.propertyName] instanceof Object && !(embeddedObject[this.propertyName] instanceof FindOperator)) { - value = this.referencedColumn.getEntityValue(PromiseUtils.extractValue(embeddedObject[this.propertyName])); + value = this.referencedColumn.getEntityValue(embeddedObject[this.propertyName]); } else { - value = PromiseUtils.extractValue(embeddedObject[this.propertyName]); + value = embeddedObject[this.propertyName]; } } else if (this.referencedColumn) { - value = this.referencedColumn.getEntityValue(PromiseUtils.extractValue(embeddedObject[this.propertyName])); + value = this.referencedColumn.getEntityValue(embeddedObject[this.propertyName]); } else { - value = PromiseUtils.extractValue(embeddedObject[this.propertyName]); + value = embeddedObject[this.propertyName]; } } @@ -614,17 +615,17 @@ export class ColumnMetadata { if (this.relationMetadata && this.referencedColumn) { const relatedEntity = this.relationMetadata.getEntityValue(entity); if (relatedEntity && relatedEntity instanceof Object && !(relatedEntity instanceof FindOperator) && !(relatedEntity instanceof Function)) { - value = this.referencedColumn.getEntityValue(PromiseUtils.extractValue(relatedEntity)); + value = this.referencedColumn.getEntityValue(relatedEntity); } else if (entity[this.propertyName] && entity[this.propertyName] instanceof Object && !(entity[this.propertyName] instanceof FindOperator) && !(entity[this.propertyName] instanceof Function)) { - value = this.referencedColumn.getEntityValue(PromiseUtils.extractValue(entity[this.propertyName])); + value = this.referencedColumn.getEntityValue(entity[this.propertyName]); } else { value = entity[this.propertyName]; } } else if (this.referencedColumn) { - value = this.referencedColumn.getEntityValue(PromiseUtils.extractValue(entity[this.propertyName])); + value = this.referencedColumn.getEntityValue(entity[this.propertyName]); } else { value = entity[this.propertyName]; @@ -663,7 +664,18 @@ export class ColumnMetadata { return extractEmbeddedColumnValue([...this.embeddedMetadata.embeddedMetadataTree], entity); } else { - entity[this.propertyName] = value; + // we write a deep object in this entity only if the column is virtual + // because if its not virtual it means the user defined a real column for this relation + // also we don't do it if column is inside a junction table + if (!this.entityMetadata.isJunction && this.isVirtual && this.referencedColumn && this.referencedColumn.propertyName !== this.propertyName) { + if (!(this.propertyName in entity)) { + entity[this.propertyName] = {}; + } + + entity[this.propertyName][this.referencedColumn.propertyName] = value; + } else { + entity[this.propertyName] = value; + } } } diff --git a/src/metadata/EntityMetadata.ts b/src/metadata/EntityMetadata.ts index 20cb81ad807..a2246ada944 100644 --- a/src/metadata/EntityMetadata.ts +++ b/src/metadata/EntityMetadata.ts @@ -6,6 +6,7 @@ import {PostgresDriver} from "../driver/postgres/PostgresDriver"; import {SapDriver} from "../driver/sap/SapDriver"; import {SqlServerConnectionOptions} from "../driver/sqlserver/SqlServerConnectionOptions"; import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; +import {OracleDriver} from "../driver/oracle/OracleDriver"; import {CannotCreateEntityIdMapError} from "../error/CannotCreateEntityIdMapError"; import {OrderByCondition} from "../find-options/OrderByCondition"; import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs"; @@ -25,6 +26,7 @@ import {RelationMetadata} from "./RelationMetadata"; import {TableType} from "./types/TableTypes"; import {TreeType} from "./types/TreeTypes"; import {UniqueMetadata} from "./UniqueMetadata"; +import {ClosureTreeOptions} from "./types/ClosureTreeOptions"; /** * Contains all entity metadata. @@ -190,6 +192,11 @@ export class EntityMetadata { */ treeType?: TreeType; + /** + * Indicates if this entity is a tree, what options of tree it has. + */ + treeOptions?: ClosureTreeOptions; + /** * Checks if this table is a junction table of the closure table. * This type is for tables that contain junction metadata of the closure tables. @@ -502,6 +509,7 @@ export class EntityMetadata { this.inheritanceTree = options.inheritanceTree || []; this.inheritancePattern = options.inheritancePattern; this.treeType = options.tableTree ? options.tableTree.type : undefined; + this.treeOptions = options.tableTree ? options.tableTree.options : undefined; this.parentClosureEntityMetadata = options.parentClosureEntityMetadata!; this.tableMetadataArgs = options.args; this.target = this.tableMetadataArgs.target; @@ -700,14 +708,21 @@ export class EntityMetadata { relations.forEach(relation => { const value = relation.getEntityValue(entity); if (Array.isArray(value)) { - value.forEach(subValue => relationsAndValues.push([relation, subValue, relation.inverseEntityMetadata])); + value.forEach(subValue => relationsAndValues.push([relation, subValue, this.getInverseEntityMetadata(subValue, relation)])); } else if (value) { - relationsAndValues.push([relation, value, relation.inverseEntityMetadata]); + relationsAndValues.push([relation, value, this.getInverseEntityMetadata(value, relation)]); } }); return relationsAndValues; } + private getInverseEntityMetadata(value: any, relation: RelationMetadata): EntityMetadata { + const childEntityMetadata = relation.inverseEntityMetadata.childEntityMetadatas.find(metadata => + metadata.target === value.constructor + ); + return childEntityMetadata ? childEntityMetadata : relation.inverseEntityMetadata; + } + // ------------------------------------------------------------------------- // Public Static Methods // ------------------------------------------------------------------------- @@ -841,7 +856,7 @@ export class EntityMetadata { */ protected buildTablePath(): string { let tablePath = this.tableName; - if (this.schema && ((this.connection.driver instanceof PostgresDriver) || (this.connection.driver instanceof SqlServerDriver) || (this.connection.driver instanceof SapDriver))) { + if (this.schema && ((this.connection.driver instanceof OracleDriver) || (this.connection.driver instanceof PostgresDriver) || (this.connection.driver instanceof SqlServerDriver) || (this.connection.driver instanceof SapDriver))) { tablePath = this.schema + "." + tablePath; } diff --git a/src/metadata/RelationMetadata.ts b/src/metadata/RelationMetadata.ts index 654747b5a9c..e7a49cf3f4d 100644 --- a/src/metadata/RelationMetadata.ts +++ b/src/metadata/RelationMetadata.ts @@ -109,6 +109,11 @@ export class RelationMetadata { */ persistenceEnabled: boolean = true; + /** + * When a child row is removed from its parent, determines if the child row should be orphaned (default) or deleted. + */ + orphanedRowAction?: "nullify" | "delete"; + /** * If set to true then related objects are allowed to be inserted to the database. */ @@ -298,6 +303,7 @@ export class RelationMetadata { this.deferrable = args.options.deferrable; this.isEager = args.options.eager || false; this.persistenceEnabled = args.options.persistence === false ? false : true; + this.orphanedRowAction = args.options.orphanedRowAction || "nullify"; this.isTreeParent = args.isTreeParent || false; this.isTreeChildren = args.isTreeChildren || false; this.type = args.type instanceof Function ? (args.type as () => any)() : args.type; diff --git a/src/metadata/types/ClosureTreeOptions.ts b/src/metadata/types/ClosureTreeOptions.ts new file mode 100644 index 00000000000..444924c134f --- /dev/null +++ b/src/metadata/types/ClosureTreeOptions.ts @@ -0,0 +1,11 @@ +/** + * Tree type. + * Specifies what table pattern will be used for the tree entity. + */ +import {ColumnMetadata} from "../ColumnMetadata"; + +export interface ClosureTreeOptions { + closureTableName?: string, + ancestorColumnName?: (column: ColumnMetadata) => string, + descendantColumnName?: (column: ColumnMetadata) => string, +} diff --git a/src/metadata/types/EventListenerTypes.ts b/src/metadata/types/EventListenerTypes.ts index 8f65645353b..9a310b9aa18 100644 --- a/src/metadata/types/EventListenerTypes.ts +++ b/src/metadata/types/EventListenerTypes.ts @@ -1,17 +1,24 @@ /** * All types that entity listener can be. */ -export type EventListenerType = "after-load"|"before-insert"|"after-insert"|"before-update"|"after-update"|"before-remove"|"after-remove"; +export type EventListenerType = + | "after-load" + | "before-insert" + | "after-insert" + | "before-update" + | "after-update" + | "before-remove" + | "after-remove"; /** * Provides a constants for each entity listener type. */ export class EventListenerTypes { - static AFTER_LOAD: EventListenerType = "after-load"; - static BEFORE_INSERT: EventListenerType = "before-insert"; - static AFTER_INSERT: EventListenerType = "after-insert"; - static BEFORE_UPDATE: EventListenerType = "before-update"; - static AFTER_UPDATE: EventListenerType = "after-update"; - static BEFORE_REMOVE: EventListenerType = "before-remove"; - static AFTER_REMOVE: EventListenerType = "after-remove"; -} \ No newline at end of file + static AFTER_LOAD = "after-load" as const; + static BEFORE_INSERT = "before-insert" as const; + static AFTER_INSERT = "after-insert" as const; + static BEFORE_UPDATE = "before-update" as const; + static AFTER_UPDATE = "after-update" as const; + static BEFORE_REMOVE = "before-remove" as const; + static AFTER_REMOVE = "after-remove" as const; +} diff --git a/src/migration/MigrationExecutor.ts b/src/migration/MigrationExecutor.ts index 2cc380c8251..3ec38750398 100644 --- a/src/migration/MigrationExecutor.ts +++ b/src/migration/MigrationExecutor.ts @@ -2,14 +2,13 @@ import {Table} from "../schema-builder/table/Table"; import {Connection} from "../connection/Connection"; import {Migration} from "./Migration"; import {ObjectLiteral} from "../common/ObjectLiteral"; -import {PromiseUtils} from "../util/PromiseUtils"; import {QueryRunner} from "../query-runner/QueryRunner"; import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; import {MssqlParameter} from "../driver/sqlserver/MssqlParameter"; import {SqlServerConnectionOptions} from "../driver/sqlserver/SqlServerConnectionOptions"; import {PostgresConnectionOptions} from "../driver/postgres/PostgresConnectionOptions"; -import { MongoDriver } from "../driver/mongodb/MongoDriver"; -import { MongoQueryRunner } from "../driver/mongodb/MongoQueryRunner"; +import {MongoDriver} from "../driver/mongodb/MongoDriver"; +import {MongoQueryRunner} from "../driver/mongodb/MongoQueryRunner"; /** * Executes migrations: runs pending and reverts previously executed migrations. @@ -90,7 +89,7 @@ export class MigrationExecutor { const executedMigrations = await this.getExecutedMigrations(); return allMigrations.filter(migration => - executedMigrations.find( + !executedMigrations.find( executedMigration => executedMigration.name === migration.name ) @@ -129,7 +128,7 @@ export class MigrationExecutor { */ async showMigrations(): Promise { let hasUnappliedMigrations = false; - const queryRunner = this.queryRunner || this.connection.createQueryRunner("master"); + const queryRunner = this.queryRunner || this.connection.createQueryRunner(); // create migrations table if its not created yet await this.createMigrationsTableIfNotExist(queryRunner); // get all migrations that are executed and saved in the database @@ -163,7 +162,7 @@ export class MigrationExecutor { */ async executePendingMigrations(): Promise { - const queryRunner = this.queryRunner || this.connection.createQueryRunner("master"); + const queryRunner = this.queryRunner || this.connection.createQueryRunner(); // create migrations table if its not created yet await this.createMigrationsTableIfNotExist(queryRunner); // get all migrations that are executed and saved in the database @@ -218,13 +217,13 @@ export class MigrationExecutor { // run all pending migrations in a sequence try { - await PromiseUtils.runInSequence(pendingMigrations, async migration => { + for (const migration of pendingMigrations) { if (this.transaction === "each" && !queryRunner.isTransactionActive) { await queryRunner.startTransaction(); transactionStartedByUs = true; } - return migration.instance!.up(queryRunner) + await migration.instance!.up(queryRunner) .then(async () => { // now when migration is executed we need to insert record about it into the database await this.insertExecutedMigration(queryRunner, migration); // commit transaction if we started it @@ -235,7 +234,7 @@ export class MigrationExecutor { successMigrations.push(migration); this.connection.logger.logSchemaBuild(`Migration ${migration.name} has been executed successfully.`); }); - }); + } // commit transaction if we started it if (this.transaction === "all" && transactionStartedByUs) @@ -265,7 +264,7 @@ export class MigrationExecutor { */ async undoLastMigration(): Promise { - const queryRunner = this.queryRunner || this.connection.createQueryRunner("master"); + const queryRunner = this.queryRunner || this.connection.createQueryRunner(); // create migrations table if its not created yet await this.createMigrationsTableIfNotExist(queryRunner); @@ -496,7 +495,7 @@ export class MigrationExecutor { } protected async withQueryRunner(callback: (queryRunner: QueryRunner) => T) { - const queryRunner = this.queryRunner || this.connection.createQueryRunner("master"); + const queryRunner = this.queryRunner || this.connection.createQueryRunner(); try { return callback(queryRunner); diff --git a/src/persistence/EntityPersistExecutor.ts b/src/persistence/EntityPersistExecutor.ts index ec04e3459c1..b4a0bc23d3f 100644 --- a/src/persistence/EntityPersistExecutor.ts +++ b/src/persistence/EntityPersistExecutor.ts @@ -13,7 +13,6 @@ import {ManyToManySubjectBuilder} from "./subject-builder/ManyToManySubjectBuild import {SubjectDatabaseEntityLoader} from "./SubjectDatabaseEntityLoader"; import {CascadesSubjectBuilder} from "./subject-builder/CascadesSubjectBuilder"; import {OrmUtils} from "../util/OrmUtils"; -import {PromiseUtils} from "../util/PromiseUtils"; /** * Persists a single entity or multiple entities - saves or removes them. @@ -39,138 +38,139 @@ export class EntityPersistExecutor { /** * Executes persistence operation ob given entity or entities. */ - execute(): Promise { + async execute(): Promise { // check if entity we are going to save is valid and is an object - if (!this.entity || !(this.entity instanceof Object)) + if (!this.entity || typeof this.entity !== "object") return Promise.reject(new MustBeEntityError(this.mode, this.entity)); // we MUST call "fake" resolve here to make sure all properties of lazily loaded relations are resolved - return Promise.resolve().then(async () => { - - // if query runner is already defined in this class, it means this entity manager was already created for a single connection - // if its not defined we create a new query runner - single connection where we'll execute all our operations - const queryRunner = this.queryRunner || this.connection.createQueryRunner("master"); - - // save data in the query runner - this is useful functionality to share data from outside of the world - // with third classes - like subscribers and listener methods - if (this.options && this.options.data) - queryRunner.data = this.options.data; - - try { - - // collect all operate subjects - const entities: ObjectLiteral[] = Array.isArray(this.entity) ? this.entity : [this.entity]; - const entitiesInChunks = this.options && this.options.chunk && this.options.chunk > 0 ? OrmUtils.chunk(entities, this.options.chunk) : [entities]; - - // console.time("building subject executors..."); - const executors = await Promise.all(entitiesInChunks.map(async entities => { - const subjects: Subject[] = []; - - // create subjects for all entities we received for the persistence - entities.forEach(entity => { - const entityTarget = this.target ? this.target : entity.constructor; - if (entityTarget === Object) - throw new CannotDetermineEntityError(this.mode); - - subjects.push(new Subject({ - metadata: this.connection.getMetadata(entityTarget), - entity: entity, - canBeInserted: this.mode === "save", - canBeUpdated: this.mode === "save", - mustBeRemoved: this.mode === "remove", - canBeSoftRemoved: this.mode === "soft-remove", - canBeRecovered: this.mode === "recover" - })); - }); - - // console.time("building cascades..."); - // go through each entity with metadata and create subjects and subjects by cascades for them - const cascadesSubjectBuilder = new CascadesSubjectBuilder(subjects); + await Promise.resolve(); + + // if query runner is already defined in this class, it means this entity manager was already created for a single connection + // if its not defined we create a new query runner - single connection where we'll execute all our operations + const queryRunner = this.queryRunner || this.connection.createQueryRunner(); + + // save data in the query runner - this is useful functionality to share data from outside of the world + // with third classes - like subscribers and listener methods + if (this.options && this.options.data) + queryRunner.data = this.options.data; + + try { + + // collect all operate subjects + const entities: ObjectLiteral[] = Array.isArray(this.entity) ? this.entity : [this.entity]; + const entitiesInChunks = this.options && this.options.chunk && this.options.chunk > 0 ? OrmUtils.chunk(entities, this.options.chunk) : [entities]; + + // console.time("building subject executors..."); + const executors = await Promise.all(entitiesInChunks.map(async entities => { + const subjects: Subject[] = []; + + // create subjects for all entities we received for the persistence + entities.forEach(entity => { + const entityTarget = this.target ? this.target : entity.constructor; + if (entityTarget === Object) + throw new CannotDetermineEntityError(this.mode); + + subjects.push(new Subject({ + metadata: this.connection.getMetadata(entityTarget), + entity: entity, + canBeInserted: this.mode === "save", + canBeUpdated: this.mode === "save", + mustBeRemoved: this.mode === "remove", + canBeSoftRemoved: this.mode === "soft-remove", + canBeRecovered: this.mode === "recover" + })); + }); + + // console.time("building cascades..."); + // go through each entity with metadata and create subjects and subjects by cascades for them + const cascadesSubjectBuilder = new CascadesSubjectBuilder(subjects); + subjects.forEach(subject => { + // next step we build list of subjects we will operate with + // these subjects are subjects that we need to insert or update alongside with main persisted entity + cascadesSubjectBuilder.build(subject, this.mode); + }); + // console.timeEnd("building cascades..."); + + // load database entities for all subjects we have + // next step is to load database entities for all operate subjects + // console.time("loading..."); + await new SubjectDatabaseEntityLoader(queryRunner, subjects).load(this.mode); + // console.timeEnd("loading..."); + + // console.time("other subjects..."); + // build all related subjects and change maps + if (this.mode === "save" || this.mode === "soft-remove" || this.mode === "recover") { + new OneToManySubjectBuilder(subjects).build(); + new OneToOneInverseSideSubjectBuilder(subjects).build(); + new ManyToManySubjectBuilder(subjects).build(); + } else { subjects.forEach(subject => { - // next step we build list of subjects we will operate with - // these subjects are subjects that we need to insert or update alongside with main persisted entity - cascadesSubjectBuilder.build(subject, this.mode); - }); - // console.timeEnd("building cascades..."); - - // load database entities for all subjects we have - // next step is to load database entities for all operate subjects - // console.time("loading..."); - await new SubjectDatabaseEntityLoader(queryRunner, subjects).load(this.mode); - // console.timeEnd("loading..."); - - // console.time("other subjects..."); - // build all related subjects and change maps - if (this.mode === "save" || this.mode === "soft-remove" || this.mode === "recover") { - new OneToManySubjectBuilder(subjects).build(); - new OneToOneInverseSideSubjectBuilder(subjects).build(); - new ManyToManySubjectBuilder(subjects).build(); - } else { - subjects.forEach(subject => { - if (subject.mustBeRemoved) { - new ManyToManySubjectBuilder(subjects).buildForAllRemoval(subject); - } - }); - } - // console.timeEnd("other subjects..."); - // console.timeEnd("building subjects..."); - // console.log("subjects", subjects); - - // create a subject executor - return new SubjectExecutor(queryRunner, subjects, this.options); - })); - // console.timeEnd("building subject executors..."); - - // make sure we have at least one executable operation before we create a transaction and proceed - // if we don't have operations it means we don't really need to update or remove something - const executorsWithExecutableOperations = executors.filter(executor => executor.hasExecutableOperations); - if (executorsWithExecutableOperations.length === 0) - return; - - // start execute queries in a transaction - // if transaction is already opened in this query runner then we don't touch it - // if its not opened yet then we open it here, and once we finish - we close it - let isTransactionStartedByUs = false; - try { - - // open transaction if its not opened yet - if (!queryRunner.isTransactionActive) { - if (!this.options || this.options.transaction !== false) { // start transaction until it was not explicitly disabled - isTransactionStartedByUs = true; - await queryRunner.startTransaction(); + if (subject.mustBeRemoved) { + new ManyToManySubjectBuilder(subjects).buildForAllRemoval(subject); } + }); + } + // console.timeEnd("other subjects..."); + // console.timeEnd("building subjects..."); + // console.log("subjects", subjects); + + // create a subject executor + return new SubjectExecutor(queryRunner, subjects, this.options); + })); + // console.timeEnd("building subject executors..."); + + // make sure we have at least one executable operation before we create a transaction and proceed + // if we don't have operations it means we don't really need to update or remove something + const executorsWithExecutableOperations = executors.filter(executor => executor.hasExecutableOperations); + if (executorsWithExecutableOperations.length === 0) + return; + + // start execute queries in a transaction + // if transaction is already opened in this query runner then we don't touch it + // if its not opened yet then we open it here, and once we finish - we close it + let isTransactionStartedByUs = false; + try { + + // open transaction if its not opened yet + if (!queryRunner.isTransactionActive) { + if (!this.options || this.options.transaction !== false) { // start transaction until it was not explicitly disabled + isTransactionStartedByUs = true; + await queryRunner.startTransaction(); } + } - // execute all persistence operations for all entities we have - // console.time("executing subject executors..."); - await PromiseUtils.runInSequence(executorsWithExecutableOperations, executor => executor.execute()); - // console.timeEnd("executing subject executors..."); + // execute all persistence operations for all entities we have + // console.time("executing subject executors..."); + for (const executor of executorsWithExecutableOperations) { + await executor.execute(); + } + // console.timeEnd("executing subject executors..."); - // commit transaction if it was started by us - // console.time("commit"); - if (isTransactionStartedByUs === true) - await queryRunner.commitTransaction(); - // console.timeEnd("commit"); + // commit transaction if it was started by us + // console.time("commit"); + if (isTransactionStartedByUs === true) + await queryRunner.commitTransaction(); + // console.timeEnd("commit"); - } catch (error) { + } catch (error) { - // rollback transaction if it was started by us - if (isTransactionStartedByUs) { - try { - await queryRunner.rollbackTransaction(); - } catch (rollbackError) { } - } - throw error; + // rollback transaction if it was started by us + if (isTransactionStartedByUs) { + try { + await queryRunner.rollbackTransaction(); + } catch (rollbackError) { } } + throw error; + } - } finally { + } finally { - // release query runner only if its created by us - if (!this.queryRunner) - await queryRunner.release(); - } - }); + // release query runner only if its created by us + if (!this.queryRunner) + await queryRunner.release(); + } } } diff --git a/src/persistence/Subject.ts b/src/persistence/Subject.ts index 988509fbb7d..80405b1ffee 100644 --- a/src/persistence/Subject.ts +++ b/src/persistence/Subject.ts @@ -289,7 +289,8 @@ export class Subject { if (this.parentSubject) { this.metadata.primaryColumns.forEach(primaryColumn => { if (primaryColumn.relationMetadata && primaryColumn.relationMetadata.inverseEntityMetadata === this.parentSubject!.metadata) { - primaryColumn.setEntityValue(this.entityWithFulfilledIds!, this.parentSubject!.entity); + const value = primaryColumn.referencedColumn!.getEntityValue(this.parentSubject!.entity!); + primaryColumn.setEntityValue(this.entityWithFulfilledIds!, value); } }); } diff --git a/src/persistence/SubjectExecutor.ts b/src/persistence/SubjectExecutor.ts index 3995e4fd2f8..00823a7bacb 100644 --- a/src/persistence/SubjectExecutor.ts +++ b/src/persistence/SubjectExecutor.ts @@ -1,23 +1,22 @@ -import { SapDriver } from "../driver/sap/SapDriver"; -import { QueryRunner } from "../query-runner/QueryRunner"; -import { Subject } from "./Subject"; -import { PromiseUtils } from "../util/PromiseUtils"; -import { SubjectTopoligicalSorter } from "./SubjectTopoligicalSorter"; -import { SubjectChangedColumnsComputer } from "./SubjectChangedColumnsComputer"; -import { SubjectWithoutIdentifierError } from "../error/SubjectWithoutIdentifierError"; -import { SubjectRemovedAndUpdatedError } from "../error/SubjectRemovedAndUpdatedError"; -import { MongoQueryRunner } from "../driver/mongodb/MongoQueryRunner"; -import { MongoEntityManager } from "../entity-manager/MongoEntityManager"; -import { MongoDriver } from "../driver/mongodb/MongoDriver"; -import { ObjectLiteral } from "../common/ObjectLiteral"; -import { SaveOptions } from "../repository/SaveOptions"; -import { RemoveOptions } from "../repository/RemoveOptions"; -import { BroadcasterResult } from "../subscriber/BroadcasterResult"; -import { OracleDriver } from "../driver/oracle/OracleDriver"; -import { NestedSetSubjectExecutor } from "./tree/NestedSetSubjectExecutor"; -import { ClosureSubjectExecutor } from "./tree/ClosureSubjectExecutor"; -import { MaterializedPathSubjectExecutor } from "./tree/MaterializedPathSubjectExecutor"; -import { OrmUtils } from "../util/OrmUtils"; +import {SapDriver} from "../driver/sap/SapDriver"; +import {QueryRunner} from "../query-runner/QueryRunner"; +import {Subject} from "./Subject"; +import {SubjectTopoligicalSorter} from "./SubjectTopoligicalSorter"; +import {SubjectChangedColumnsComputer} from "./SubjectChangedColumnsComputer"; +import {SubjectWithoutIdentifierError} from "../error/SubjectWithoutIdentifierError"; +import {SubjectRemovedAndUpdatedError} from "../error/SubjectRemovedAndUpdatedError"; +import {MongoQueryRunner} from "../driver/mongodb/MongoQueryRunner"; +import {MongoEntityManager} from "../entity-manager/MongoEntityManager"; +import {MongoDriver} from "../driver/mongodb/MongoDriver"; +import {ObjectLiteral} from "../common/ObjectLiteral"; +import {SaveOptions} from "../repository/SaveOptions"; +import {RemoveOptions} from "../repository/RemoveOptions"; +import {BroadcasterResult} from "../subscriber/BroadcasterResult"; +import {OracleDriver} from "../driver/oracle/OracleDriver"; +import {NestedSetSubjectExecutor} from "./tree/NestedSetSubjectExecutor"; +import {ClosureSubjectExecutor} from "./tree/ClosureSubjectExecutor"; +import {MaterializedPathSubjectExecutor} from "./tree/MaterializedPathSubjectExecutor"; +import {OrmUtils} from "../util/OrmUtils"; /** * Executes all database operations (inserts, updated, deletes) that must be executed @@ -248,7 +247,7 @@ export class SubjectExecutor { const [groupedInsertSubjects, groupedInsertSubjectKeys] = this.groupBulkSubjects(this.insertSubjects, "insert"); // then we run insertion in the sequential order which is important since we have an ordered subjects - await PromiseUtils.runInSequence(groupedInsertSubjectKeys, async groupName => { + for (const groupName of groupedInsertSubjectKeys) { const subjects = groupedInsertSubjects[groupName]; // we must separately insert entities which does not have any values to insert @@ -331,7 +330,7 @@ export class SubjectExecutor { // insert subjects which must be inserted in separate requests (all default values) if (singleInsertSubjects.length > 0) { - await PromiseUtils.runInSequence(singleInsertSubjects, async subject => { + for (const subject of singleInsertSubjects) { subject.insertedValueSet = subject.createValueSetAndPopChangeMap(); // important to have because query builder sets inserted values into it // for nested set we execute additional queries @@ -359,7 +358,7 @@ export class SubjectExecutor { } else if (subject.metadata.treeType === "materialized-path") { await new MaterializedPathSubjectExecutor(this.queryRunner).insert(subject); } - }); + } } } @@ -374,7 +373,7 @@ export class SubjectExecutor { }); } }); - }); + } } /** @@ -468,7 +467,7 @@ export class SubjectExecutor { // group insertion subjects to make bulk insertions const [groupedRemoveSubjects, groupedRemoveSubjectKeys] = this.groupBulkSubjects(this.removeSubjects, "delete"); - await PromiseUtils.runInSequence(groupedRemoveSubjectKeys, async groupName => { + for (const groupName of groupedRemoveSubjectKeys) { const subjects = groupedRemoveSubjects[groupName]; const deleteMaps = subjects.map(subject => { if (!subject.identifier) @@ -497,7 +496,7 @@ export class SubjectExecutor { .callListeners(false) .execute(); } - }); + } } /** diff --git a/src/persistence/subject-builder/OneToManySubjectBuilder.ts b/src/persistence/subject-builder/OneToManySubjectBuilder.ts index 2578bf1cdbd..57f28d06482 100644 --- a/src/persistence/subject-builder/OneToManySubjectBuilder.ts +++ b/src/persistence/subject-builder/OneToManySubjectBuilder.ts @@ -165,15 +165,21 @@ export class OneToManySubjectBuilder { const removedRelatedEntitySubject = new Subject({ metadata: relation.inverseEntityMetadata, parentSubject: subject, - canBeUpdated: true, identifier: removedRelatedEntityRelationId, - changeMaps: [{ + }); + + if (!relation.inverseRelation || relation.inverseRelation.orphanedRowAction === "nullify") { + removedRelatedEntitySubject.canBeUpdated = true; + removedRelatedEntitySubject.changeMaps = [{ relation: relation.inverseRelation!, value: null - }] - }); + }]; + } else if (relation.inverseRelation.orphanedRowAction === "delete") { + removedRelatedEntitySubject.mustBeRemoved = true; + } + this.subjects.push(removedRelatedEntitySubject); }); } -} \ No newline at end of file +} diff --git a/src/persistence/tree/ClosureSubjectExecutor.ts b/src/persistence/tree/ClosureSubjectExecutor.ts index 0a88e34516b..2a9d7021f59 100644 --- a/src/persistence/tree/ClosureSubjectExecutor.ts +++ b/src/persistence/tree/ClosureSubjectExecutor.ts @@ -22,7 +22,7 @@ export class ClosureSubjectExecutor { /** * Removes all children of the given subject's entity. - async deleteChildrenOf(subject: Subject) { + async deleteChildrenOf(subject: Subject) { // const relationValue = subject.metadata.treeParentRelation.getEntityValue(subject.databaseEntity); // console.log("relationValue: ", relationValue); // this.queryRunner.manager @@ -75,14 +75,14 @@ export class ClosureSubjectExecutor { firstQueryParameters.push(childEntityIdValues[index]); return this.queryRunner.connection.driver.createParameter("child_entity_" + column.databaseName, firstQueryParameters.length - 1); }); - const whereCondition = subject.metadata.primaryColumns.map(column => { - const columnName = escape(column.databaseName + "_descendant"); - const parentId = column.getEntityValue(parent); + const whereCondition = subject.metadata.closureJunctionTable.descendantColumns.map(column => { + const columnName = escape(column.databaseName); + const parentId = column.referencedColumn!.getEntityValue(parent); if (!parentId) throw new CannotAttachTreeChildrenEntityError(subject.metadata.name); firstQueryParameters.push(parentId); - const parameterName = this.queryRunner.connection.driver.createParameter("parent_entity_" + column.databaseName, firstQueryParameters.length - 1); + const parameterName = this.queryRunner.connection.driver.createParameter("parent_entity_" + column.referencedColumn!.databaseName, firstQueryParameters.length - 1); return columnName + " = " + parameterName; }).join(", "); @@ -109,4 +109,4 @@ export class ClosureSubjectExecutor { } -} \ No newline at end of file +} diff --git a/src/platform/BrowserConnectionOptionsReaderDummy.template b/src/platform/BrowserConnectionOptionsReaderDummy.template new file mode 100644 index 00000000000..c3343dc50ab --- /dev/null +++ b/src/platform/BrowserConnectionOptionsReaderDummy.template @@ -0,0 +1,55 @@ +/** + * Dummy class for replacement via `package.json` in browser builds. + * + * If we don't include these functions typeorm will throw an error on runtime + * as well as during webpack builds. + */ +export class ConnectionOptionsEnvReader { + async read() { + throw new Error(`Cannot read connection options in a browser context.`); + } +} + +/** + * Dummy class for replacement via `package.json` in browser builds. + * + * If we don't include these functions typeorm will throw an error on runtime + * as well as during webpack builds. + */ +export class ConnectionOptionsXmlReader { + async read(path: string) { + throw new Error(`Cannot read connection options in a browser context.`); + } +} + +/** + * Dummy class for replacement via `package.json` in browser builds. + * + * If we don't include these functions typeorm will throw an error on runtime + * as well as during webpack builds. + */ +export class ConnectionOptionsYmlReader { + async read(path: string) { + throw new Error(`Cannot read connection options in a browser context.`); + } +} + +/** + * Dummy class for replacement via `package.json` in browser builds. + * + * If we don't include these functions typeorm will throw an error on runtime + * as well as during webpack builds. + */ +export class ConnectionOptionsReader { + async all() { + throw new Error(`Cannot read connection options in a browser context.`); + } + + async get() { + throw new Error(`Cannot read connection options in a browser context.`); + } + + async has() { + throw new Error(`Cannot read connection options in a browser context.`); + } +} diff --git a/src/platform/BrowserDirectoryExportedClassesLoader.template b/src/platform/BrowserDirectoryExportedClassesLoader.template new file mode 100644 index 00000000000..39fe932a7ba --- /dev/null +++ b/src/platform/BrowserDirectoryExportedClassesLoader.template @@ -0,0 +1,22 @@ +/** + * Dummy functions for replacement via `package.json` in browser builds. + * + * If we don't include these functions typeorm will throw an error on runtime + * as well as during webpack builds. + */ + +import {Logger} from "../logger/Logger"; + +/** + * Loads all exported classes from the given directory. + */ +export function importClassesFromDirectories(logger: Logger, directories: string[], formats = [".js", ".cjs", ".ts"]): Function[] { + return []; +} + +/** + * Loads all json files from the given directory. + */ +export function importJsonsFromDirectories(directories: string[], format = ".json"): any[] { + return []; +} diff --git a/src/platform/BrowserDisabledDriversDummy.template b/src/platform/BrowserDisabledDriversDummy.template index 95cba48bea5..9b34705886d 100644 --- a/src/platform/BrowserDisabledDriversDummy.template +++ b/src/platform/BrowserDisabledDriversDummy.template @@ -3,8 +3,8 @@ * Using those classes reduces the build size by one third. * * If we don't include those dummy classes (and just disable the driver import - * with `false` in `package.json`) typeorm will throw an error on runtime, - * even if those driver are not used. + * with `false` in `package.json`) typeorm will throw an error on runtime and + * during webpack builds even if those driver are not used. */ /** @@ -37,6 +37,24 @@ export class MongoRepository {} */ export class PostgresDriver {} +/** + * DO NOT IMPORT THIS CLASS - + * This is a dummy class for replacement via `package.json` in browser builds + */ +export class AuroraDataApiDriver {} + +/** + * DO NOT IMPORT THIS CLASS - + * This is a dummy class for replacement via `package.json` in browser builds + */ +export class CockroachDriver {} + +/** + * DO NOT IMPORT THIS CLASS - + * This is a dummy class for replacement via `package.json` in browser builds + */ +export class AuroraDataApiPostgresDriver {} + /** * DO NOT IMPORT THIS CLASS - * This is a dummy class for replacement via `package.json` in browser builds @@ -60,3 +78,15 @@ export class MysqlDriver {} * This is a dummy class for replacement via `package.json` in browser builds */ export class OracleDriver {} + +/** + * DO NOT IMPORT THIS CLASS - + * This is a dummy class for replacement via `package.json` in browser builds + */ +export class SqliteDriver {} + +/** + * DO NOT IMPORT THIS CLASS - + * This is a dummy class for replacement via `package.json` in browser builds + */ +export class BetterSqlite3Driver {} diff --git a/src/platform/BrowserFileLoggerDummy.template b/src/platform/BrowserFileLoggerDummy.template new file mode 100644 index 00000000000..7277fe47ade --- /dev/null +++ b/src/platform/BrowserFileLoggerDummy.template @@ -0,0 +1,50 @@ +/** + * Performs logging of the events in TypeORM. + * This version of logger logs everything into ormlogs.log file. + */ +export class DummyLogger { + /** + * Logs query and parameters used in it. + */ + logQuery() { + throw new Error('This logger is not applicable in a browser context'); + } + + /** + * Logs query that is failed. + */ + logQueryError() { + throw new Error('This logger is not applicable in a browser context'); + } + + /** + * Logs query that is slow. + */ + logQuerySlow() { + throw new Error('This logger is not applicable in a browser context'); + } + + /** + * Logs events from the schema build process. + */ + logSchemaBuild() { + throw new Error('This logger is not applicable in a browser context'); + } + + /** + * Logs events from the migrations run process. + */ + logMigration() { + throw new Error('This logger is not applicable in a browser context'); + } + + /** + * Perform logging using given logger, or by default to the console. + * Log has its own level and message. + */ + log() { + throw new Error('This logger is not applicable in a browser context'); + } +} + +export class FileLogger extends DummyLogger {} diff --git a/src/platform/BrowserPlatformTools.template b/src/platform/BrowserPlatformTools.template index 216150aaa0d..72295f27d32 100644 --- a/src/platform/BrowserPlatformTools.template +++ b/src/platform/BrowserPlatformTools.template @@ -75,6 +75,11 @@ export class PlatformTools { return false; } + static dotenv(pathStr: string): void { + if (this.type === "browser") + throw new Error(`This option/function is not supported in the browser environment. Failed operation: dotenv.config({ path: "${pathStr}" }).`); + } + /** * Gets environment variable. */ @@ -89,7 +94,7 @@ export class PlatformTools { throw new Error(`This option/function is not supported in the browser environment. Failed operation: fs.readFileSync("${filename}").`); return null; } - + static appendFileSync(filename: string, data: any) { if (this.type === "browser") throw new Error(`This option/function is not supported in the browser environment. Failed operation: fs.appendFileSync("${filename}").`); @@ -125,11 +130,11 @@ export class PlatformTools { static logError(prefix: string, error: any) { console.error(prefix + " ", error); } - + static logWarn(prefix: string, warning: any) { console.warn(prefix + " ", warning); } - + static log(message: string) { console.log(message); } @@ -165,4 +170,4 @@ if (typeof window !== "undefined") { // NativeScript uses global, not window if (typeof global !== "undefined") { global.Buffer = require("buffer/").Buffer; -} \ No newline at end of file +} diff --git a/src/platform/PlatformTools.ts b/src/platform/PlatformTools.ts index 5af551057cf..9d75d8a479b 100644 --- a/src/platform/PlatformTools.ts +++ b/src/platform/PlatformTools.ts @@ -1,12 +1,13 @@ import * as path from "path"; import * as fs from "fs"; +import dotenv from "dotenv"; +import chalk from "chalk"; import {highlight, Theme} from "cli-highlight"; + export {ReadStream} from "fs"; export {EventEmitter} from "events"; export {Readable, Writable} from "stream"; -const chalk = require("chalk"); - /** * Platform-specific tools. */ @@ -49,8 +50,11 @@ export class PlatformTools { /** * hana */ - case "@sap/hdbext": - return require("@sap/hdbext"); + case "@sap/hana-client": + return require("@sap/hana-client"); + + case "hdb-pool": + return require("hdb-pool"); /** * mysql @@ -79,19 +83,24 @@ export class PlatformTools { case "pg-query-stream": return require("pg-query-stream"); + case "typeorm-aurora-data-api-driver": + return require("typeorm-aurora-data-api-driver"); + /** * redis */ case "redis": return require("redis"); - /** - * ioredis - */ case "ioredis": - case "ioredis/cluster": return require("ioredis"); + /** + * better-sqlite3 + */ + case "better-sqlite3": + return require("better-sqlite3"); + /** * sqlite */ @@ -111,40 +120,21 @@ export class PlatformTools { return require("mssql"); /** - * other modules - */ - case "mkdirp": - return require("mkdirp"); - - case "path": - return require("path"); - - case "debug": - return require("debug"); - - case "app-root-path": - return require("app-root-path"); - - case "glob": - return require("glob"); - - case "typeorm-aurora-data-api-driver": - return require("typeorm-aurora-data-api-driver"); - /** - * default - */ - default: - return require(name); - + * react-native-sqlite + */ + case "react-native-sqlite-storage": + return require("react-native-sqlite-storage"); } } catch (err) { - if (!path.isAbsolute(name) && name.substr(0, 2) !== "./" && name.substr(0, 3) !== "../") { - return require(path.resolve(process.cwd() + "/node_modules/" + name)); - } - - throw err; + return require(path.resolve(process.cwd() + "/node_modules/" + name)); } + + // If nothing above matched and we get here, the package was not listed within PlatformTools + // and is an Invalid Package. To make it explicit that this is NOT the intended use case for + // PlatformTools.load - it's not just a way to replace `require` all willy-nilly - let's throw + // an error. + throw new TypeError(`Invalid Package for PlatformTools.load: ${name}`); } /** @@ -192,6 +182,15 @@ export class PlatformTools { }); } + /** + * Loads a dotenv file into the environment variables. + * + * @param path The file to load as a dotenv configuration + */ + static dotenv(pathStr: string): void { + dotenv.config({ path: pathStr }); + } + /** * Gets environment variable. */ diff --git a/src/query-builder/Alias.ts b/src/query-builder/Alias.ts index 0ad0c8b414e..5ba81a0eeb5 100644 --- a/src/query-builder/Alias.ts +++ b/src/query-builder/Alias.ts @@ -1,5 +1,5 @@ import {EntityMetadata} from "../metadata/EntityMetadata"; -import { ObjectUtils } from "../util/ObjectUtils"; +import {ObjectUtils} from "../util/ObjectUtils"; /** */ @@ -45,4 +45,4 @@ export class Alias { return this._metadata; } -} \ No newline at end of file +} diff --git a/src/query-builder/DeleteQueryBuilder.ts b/src/query-builder/DeleteQueryBuilder.ts index 4be20ba3776..1840a487280 100644 --- a/src/query-builder/DeleteQueryBuilder.ts +++ b/src/query-builder/DeleteQueryBuilder.ts @@ -2,7 +2,7 @@ import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver"; import {OracleDriver} from "../driver/oracle/OracleDriver"; import {QueryBuilder} from "./QueryBuilder"; import {ObjectLiteral} from "../common/ObjectLiteral"; -import {ObjectType} from "../common/ObjectType"; +import {EntityTarget} from "../common/EntityTarget"; import {Connection} from "../connection/Connection"; import {QueryRunner} from "../query-runner/QueryRunner"; import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; @@ -16,6 +16,7 @@ import {MysqlDriver} from "../driver/mysql/MysqlDriver"; import {BroadcasterResult} from "../subscriber/BroadcasterResult"; import {EntitySchema} from "../index"; import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; +import {BetterSqlite3Driver} from "../driver/better-sqlite3/BetterSqlite3Driver"; /** * Allows to build complex sql queries in a fashion way and execute those queries. @@ -39,7 +40,8 @@ export class DeleteQueryBuilder extends QueryBuilder implements * Gets generated sql query without parameters being replaced. */ getQuery(): string { - let sql = this.createDeleteExpression(); + let sql = this.createComment(); + sql += this.createDeleteExpression(); return sql.trim(); } @@ -83,6 +85,10 @@ export class DeleteQueryBuilder extends QueryBuilder implements } else if (driver instanceof OracleDriver) { deleteResult.affected = result; + } else if (driver instanceof BetterSqlite3Driver) { // only works for better-sqlite3 + deleteResult.raw = result; + deleteResult.affected = result.changes; + } else { deleteResult.raw = result; } @@ -128,7 +134,7 @@ export class DeleteQueryBuilder extends QueryBuilder implements * Specifies FROM which entity's table select/update/delete will be executed. * Also sets a main string alias of the selection data. */ - from(entityTarget: ObjectType|EntitySchema|string, aliasName?: string): DeleteQueryBuilder { + from(entityTarget: EntityTarget, aliasName?: string): DeleteQueryBuilder { entityTarget = entityTarget instanceof EntitySchema ? entityTarget.options.name : entityTarget; const mainAlias = this.createFromAlias(entityTarget, aliasName); this.expressionMap.setMainAlias(mainAlias); diff --git a/src/query-builder/InsertQueryBuilder.ts b/src/query-builder/InsertQueryBuilder.ts index 480a93ac044..49e435a8a90 100644 --- a/src/query-builder/InsertQueryBuilder.ts +++ b/src/query-builder/InsertQueryBuilder.ts @@ -2,7 +2,7 @@ import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver"; import {SapDriver} from "../driver/sap/SapDriver"; import {QueryBuilder} from "./QueryBuilder"; import {ObjectLiteral} from "../common/ObjectLiteral"; -import {ObjectType} from "../common/ObjectType"; +import {EntityTarget} from "../common/EntityTarget"; import {QueryDeepPartialEntity} from "./QueryPartialEntity"; import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; import {PostgresDriver} from "../driver/postgres/PostgresDriver"; @@ -33,7 +33,8 @@ export class InsertQueryBuilder extends QueryBuilder { * Gets generated sql query without parameters being replaced. */ getQuery(): string { - let sql = this.createInsertExpression(); + let sql = this.createComment(); + sql += this.createInsertExpression(); return sql.trim(); } @@ -41,6 +42,20 @@ export class InsertQueryBuilder extends QueryBuilder { * Executes sql generated by query builder and returns raw database results. */ async execute(): Promise { + // console.time(".value sets"); + const valueSets: ObjectLiteral[] = this.getValueSets(); + // console.timeEnd(".value sets"); + + // If user passed empty array of entities then we don't need to do + // anything. + // + // Fixes GitHub issues #3111 and #5734. If we were to let this through + // we would run into problems downstream, like subscribers getting + // invoked with the empty array where they expect an entity, and SQL + // queries with an empty VALUES clause. + if (valueSets.length === 0) + return new InsertResult(); + // console.time("QueryBuilder.execute"); // console.time(".database stuff"); const queryRunner = this.obtainQueryRunner(); @@ -55,9 +70,6 @@ export class InsertQueryBuilder extends QueryBuilder { } // console.timeEnd(".database stuff"); - // console.time(".value sets"); - const valueSets: ObjectLiteral[] = this.getValueSets(); - // console.timeEnd(".value sets"); // call before insertion methods in listeners and subscribers if (this.expressionMap.callListeners === true && this.expressionMap.mainAlias!.hasMetadata) { @@ -75,8 +87,9 @@ export class InsertQueryBuilder extends QueryBuilder { // console.time(".prepare returning statement"); const returningResultsEntityUpdator = new ReturningResultsEntityUpdator(queryRunner, this.expressionMap); if (this.expressionMap.updateEntity === true && this.expressionMap.mainAlias!.hasMetadata) { - this.expressionMap.extraReturningColumns = returningResultsEntityUpdator.getInsertionReturningColumns(); - + if (!(valueSets.length > 1 && this.connection.driver instanceof OracleDriver)) { + this.expressionMap.extraReturningColumns = returningResultsEntityUpdator.getInsertionReturningColumns(); + } if (this.expressionMap.extraReturningColumns.length > 0 && this.connection.driver instanceof SqlServerDriver) { declareSql = this.connection.driver.buildTableVariableDeclaration("@OutputTable", this.expressionMap.extraReturningColumns); selectOutputSql = `SELECT * FROM @OutputTable`; @@ -153,7 +166,7 @@ export class InsertQueryBuilder extends QueryBuilder { /** * Specifies INTO which entity's table insertion will be executed. */ - into(entityTarget: ObjectType|EntitySchema|string, columns?: string[]): InsertQueryBuilder { + into(entityTarget: EntityTarget, columns?: string[]): InsertQueryBuilder { entityTarget = entityTarget instanceof EntitySchema ? entityTarget.options.name : entityTarget; const mainAlias = this.createFromAlias(entityTarget); this.expressionMap.setMainAlias(mainAlias); @@ -281,7 +294,7 @@ export class InsertQueryBuilder extends QueryBuilder { protected createInsertExpression() { const tableName = this.getTableName(this.getMainTableName()); const valuesExpression = this.createValuesExpression(); // its important to get values before returning expression because oracle rely on native parameters and ordering of them is important - const returningExpression = this.createReturningExpression(); + const returningExpression = (this.connection.driver instanceof OracleDriver && this.getValueSets().length > 1) ? null : this.createReturningExpression(); // oracle doesnt support returning with multi-row insert const columnsExpression = this.createColumnNamesExpression(); let query = "INSERT "; @@ -306,7 +319,11 @@ export class InsertQueryBuilder extends QueryBuilder { // add VALUES expression if (valuesExpression) { - query += ` VALUES ${valuesExpression}`; + if (this.connection.driver instanceof OracleDriver && this.getValueSets().length > 1) { + query += ` ${valuesExpression}`; + } else { + query += ` VALUES ${valuesExpression}`; + } } else { if (this.connection.driver instanceof MysqlDriver || this.connection.driver instanceof AuroraDataApiDriver) { // special syntax for mysql DEFAULT VALUES insertion query += " VALUES ()"; @@ -401,7 +418,11 @@ export class InsertQueryBuilder extends QueryBuilder { valueSets.forEach((valueSet, valueSetIndex) => { columns.forEach((column, columnIndex) => { if (columnIndex === 0) { - expression += "("; + if (this.connection.driver instanceof OracleDriver && valueSets.length > 1) { + expression += " SELECT "; + } else { + expression += "("; + } } const paramName = "i" + valueSetIndex + "_" + column.databaseName; @@ -461,7 +482,7 @@ export class InsertQueryBuilder extends QueryBuilder { // if value for this column was not provided then insert default value } else if (value === undefined) { - if (this.connection.driver instanceof AbstractSqliteDriver || this.connection.driver instanceof SapDriver) { // unfortunately sqlite does not support DEFAULT expression in INSERT queries + if ((this.connection.driver instanceof OracleDriver && valueSets.length > 1) || this.connection.driver instanceof AbstractSqliteDriver || this.connection.driver instanceof SapDriver) { // unfortunately sqlite does not support DEFAULT expression in INSERT queries if (column.default !== undefined) { // try to use default defined in the column expression += this.connection.driver.normalizeDefault(column); } else { @@ -510,9 +531,17 @@ export class InsertQueryBuilder extends QueryBuilder { if (columnIndex === columns.length - 1) { if (valueSetIndex === valueSets.length - 1) { - expression += ")"; + if (this.connection.driver instanceof OracleDriver && valueSets.length > 1) { + expression += " FROM DUAL "; + } else { + expression += ")"; + } } else { - expression += "), "; + if (this.connection.driver instanceof OracleDriver && valueSets.length > 1) { + expression += " FROM DUAL UNION ALL "; + } else { + expression += "), "; + } } } else { expression += ", "; @@ -579,7 +608,7 @@ export class InsertQueryBuilder extends QueryBuilder { * Gets array of values need to be inserted into the target table. */ protected getValueSets(): ObjectLiteral[] { - if (Array.isArray(this.expressionMap.valuesSet) && this.expressionMap.valuesSet.length > 0) + if (Array.isArray(this.expressionMap.valuesSet)) return this.expressionMap.valuesSet; if (this.expressionMap.valuesSet instanceof Object) diff --git a/src/query-builder/QueryBuilder.ts b/src/query-builder/QueryBuilder.ts index f11ac46a98f..c3b340f276e 100644 --- a/src/query-builder/QueryBuilder.ts +++ b/src/query-builder/QueryBuilder.ts @@ -8,13 +8,15 @@ import {DeleteQueryBuilder} from "./DeleteQueryBuilder"; import {SoftDeleteQueryBuilder} from "./SoftDeleteQueryBuilder"; import {InsertQueryBuilder} from "./InsertQueryBuilder"; import {RelationQueryBuilder} from "./RelationQueryBuilder"; -import {ObjectType} from "../common/ObjectType"; +import {EntityTarget} from "../common/EntityTarget"; import {Alias} from "./Alias"; import {Brackets} from "./Brackets"; import {QueryDeepPartialEntity} from "./QueryPartialEntity"; import {EntityMetadata} from "../metadata/EntityMetadata"; import {ColumnMetadata} from "../metadata/ColumnMetadata"; import {SqljsDriver} from "../driver/sqljs/SqljsDriver"; +import {PostgresDriver} from "../driver/postgres/PostgresDriver"; +import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver"; import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; import {OracleDriver} from "../driver/oracle/OracleDriver"; import {EntitySchema} from "../"; @@ -187,17 +189,7 @@ export abstract class QueryBuilder { /** * Creates UPDATE query for the given entity and applies given update values. */ - update(entity: ObjectType, updateSet?: QueryDeepPartialEntity): UpdateQueryBuilder; - - /** - * Creates UPDATE query for the given entity and applies given update values. - */ - update(entity: EntitySchema, updateSet?: QueryDeepPartialEntity): UpdateQueryBuilder; - - /** - * Creates UPDATE query for the given entity and applies given update values. - */ - update(entity: Function|EntitySchema|string, updateSet?: QueryDeepPartialEntity): UpdateQueryBuilder; + update(entity: EntityTarget, updateSet?: QueryDeepPartialEntity): UpdateQueryBuilder; /** * Creates UPDATE query for the given table name and applies given update values. @@ -207,7 +199,7 @@ export abstract class QueryBuilder { /** * Creates UPDATE query and applies given update values. */ - update(entityOrTableNameUpdateSet?: string|Function|EntitySchema|ObjectLiteral, maybeUpdateSet?: ObjectLiteral): UpdateQueryBuilder { + update(entityOrTableNameUpdateSet?: EntityTarget|ObjectLiteral, maybeUpdateSet?: ObjectLiteral): UpdateQueryBuilder { const updateSet = maybeUpdateSet ? maybeUpdateSet : entityOrTableNameUpdateSet as ObjectLiteral|undefined; entityOrTableNameUpdateSet = entityOrTableNameUpdateSet instanceof EntitySchema ? entityOrTableNameUpdateSet.options.name : entityOrTableNameUpdateSet; @@ -271,7 +263,7 @@ export abstract class QueryBuilder { /** * Sets entity's relation with which this query builder gonna work. */ - relation(entityTarget: ObjectType|string, propertyPath: string): RelationQueryBuilder; + relation(entityTarget: EntityTarget, propertyPath: string): RelationQueryBuilder; /** * Sets entity's relation with which this query builder gonna work. @@ -303,7 +295,7 @@ export abstract class QueryBuilder { * * todo: move this method to manager? or create a shortcut? */ - hasRelation(target: ObjectType|string, relation: string): boolean; + hasRelation(target: EntityTarget, relation: string): boolean; /** * Checks if given relations exist in the entity. @@ -311,7 +303,7 @@ export abstract class QueryBuilder { * * todo: move this method to manager? or create a shortcut? */ - hasRelation(target: ObjectType|string, relation: string[]): boolean; + hasRelation(target: EntityTarget, relation: string[]): boolean; /** * Checks if given relation or relations exist in the entity. @@ -319,7 +311,7 @@ export abstract class QueryBuilder { * * todo: move this method to manager? or create a shortcut? */ - hasRelation(target: ObjectType|string, relation: string|string[]): boolean { + hasRelation(target: EntityTarget, relation: string|string[]): boolean { const entityMetadata = this.connection.getMetadata(target); const relations = Array.isArray(relation) ? relation : [relation]; return relations.every(relation => { @@ -455,6 +447,16 @@ export abstract class QueryBuilder { return new (this.constructor as any)(this); } + /** + * Includes a Query comment in the query builder. This is helpful for debugging purposes, + * such as finding a specific query in the database server's logs, or for categorization using + * an APM product. + */ + comment(comment: string): this { + this.expressionMap.comment = comment; + return this; + } + /** * Disables escaping. */ @@ -532,7 +534,7 @@ export abstract class QueryBuilder { * Specifies FROM which entity's table select/update/delete will be executed. * Also sets a main string alias of the selection data. */ - protected createFromAlias(entityTarget: Function|string|((qb: SelectQueryBuilder) => SelectQueryBuilder), aliasName?: string): Alias { + protected createFromAlias(entityTarget: EntityTarget|((qb: SelectQueryBuilder) => SelectQueryBuilder), aliasName?: string): Alias { // if table has a metadata then find it to properly escape its properties // const metadata = this.connection.entityMetadatas.find(metadata => metadata.tableName === tableName); @@ -547,21 +549,25 @@ export abstract class QueryBuilder { }); } else { - let subQuery: string = ""; - if (entityTarget instanceof Function) { - const subQueryBuilder: SelectQueryBuilder = (entityTarget as any)(((this as any) as SelectQueryBuilder).subQuery()); - this.setParameters(subQueryBuilder.getParameters()); - subQuery = subQueryBuilder.getQuery(); - - } else { - subQuery = entityTarget; + if (typeof entityTarget === "string") { + const isSubquery = entityTarget.substr(0, 1) === "(" && entityTarget.substr(-1) === ")"; + + return this.expressionMap.createAlias({ + type: "from", + name: aliasName, + tablePath: !isSubquery ? entityTarget as string : undefined, + subQuery: isSubquery ? entityTarget : undefined, + }); } - const isSubQuery = entityTarget instanceof Function || entityTarget.substr(0, 1) === "(" && entityTarget.substr(-1) === ")"; + + const subQueryBuilder: SelectQueryBuilder = (entityTarget as any)(((this as any) as SelectQueryBuilder).subQuery()); + this.setParameters(subQueryBuilder.getParameters()); + const subquery = subQueryBuilder.getQuery(); + return this.expressionMap.createAlias({ type: "from", name: aliasName, - tablePath: isSubQuery === false ? entityTarget as string : undefined, - subQuery: isSubQuery === true ? subQuery : undefined, + subQuery: subquery }); } } @@ -570,35 +576,88 @@ export abstract class QueryBuilder { * Replaces all entity's propertyName to name in the given statement. */ protected replacePropertyNames(statement: string) { - this.expressionMap.aliases.forEach(alias => { - if (!alias.hasMetadata) return; - const replaceAliasNamePrefix = this.expressionMap.aliasNamePrefixingEnabled ? alias.name + "\\." : ""; - const replacementAliasNamePrefix = this.expressionMap.aliasNamePrefixingEnabled ? this.escape(alias.name) + "." : ""; - alias.metadata.columns.forEach(column => { - const expression = "([ =\(]|^.{0})" + replaceAliasNamePrefix + column.propertyPath + "([ =\)\,]|.{0}$)"; - statement = statement.replace(new RegExp(expression, "gm"), "$1" + replacementAliasNamePrefix + this.escape(column.databaseName) + "$2"); - const expression2 = "([ =\(]|^.{0})" + replaceAliasNamePrefix + column.propertyName + "([ =\)\,]|.{0}$)"; - statement = statement.replace(new RegExp(expression2, "gm"), "$1" + replacementAliasNamePrefix + this.escape(column.databaseName) + "$2"); - }); - alias.metadata.relations.forEach(relation => { - [...relation.joinColumns, ...relation.inverseJoinColumns].forEach(joinColumn => { - const expression = "([ =\(]|^.{0})" + replaceAliasNamePrefix + relation.propertyPath + "\\." + joinColumn.referencedColumn!.propertyPath + "([ =\)\,]|.{0}$)"; - statement = statement.replace(new RegExp(expression, "gm"), "$1" + replacementAliasNamePrefix + this.escape(joinColumn.databaseName) + "$2"); // todo: fix relation.joinColumns[0], what if multiple columns - }); - if (relation.joinColumns.length > 0) { - const expression = "([ =\(]|^.{0})" + replaceAliasNamePrefix + relation.propertyPath + "([ =\)\,]|.{0}$)"; - statement = statement.replace(new RegExp(expression, "gm"), "$1" + replacementAliasNamePrefix + this.escape(relation.joinColumns[0].databaseName) + "$2"); // todo: fix relation.joinColumns[0], what if multiple columns + // Escape special characters in regular expressions + // Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping + const escapeRegExp = (s: String) => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); + + for (const alias of this.expressionMap.aliases) { + if (!alias.hasMetadata) continue; + const replaceAliasNamePrefix = this.expressionMap.aliasNamePrefixingEnabled ? `${alias.name}.` : ""; + const replacementAliasNamePrefix = this.expressionMap.aliasNamePrefixingEnabled ? `${this.escape(alias.name)}.` : ""; + + const replacements: { [key: string]: string } = {}; + + // Insert & overwrite the replacements from least to most relevant in our replacements object. + // To do this we iterate and overwrite in the order of relevance. + // Least to Most Relevant: + // * Relation Property Path to first join column key + // * Relation Property Path + Column Path + // * Column Database Name + // * Column Propety Name + // * Column Property Path + + for (const relation of alias.metadata.relations) { + if (relation.joinColumns.length > 0) + replacements[relation.propertyPath] = relation.joinColumns[0].databaseName; + } + + for (const relation of alias.metadata.relations) { + for (const joinColumn of [...relation.joinColumns, ...relation.inverseJoinColumns]) { + const propertyKey = `${relation.propertyPath}.${joinColumn.referencedColumn!.propertyPath}`; + replacements[propertyKey] = joinColumn.databaseName; } - }); - }); + } + + for (const column of alias.metadata.columns) { + replacements[column.databaseName] = column.databaseName; + } + + for (const column of alias.metadata.columns) { + replacements[column.propertyName] = column.databaseName; + } + + for (const column of alias.metadata.columns) { + replacements[column.propertyPath] = column.databaseName; + } + + const replacementKeys = Object.keys(replacements); + + if (replacementKeys.length) { + statement = statement.replace(new RegExp( + `(?<=[ =\(]|^.{0})` + + `${escapeRegExp(replaceAliasNamePrefix)}(${replacementKeys.map(escapeRegExp).join("|")})` + + `(?=[ =\)\,]|.{0}$)`, + "gm" + ), (_, p) => + `${replacementAliasNamePrefix}${this.escape(replacements[p])}` + ); + } + } + return statement; } + protected createComment(): string { + if (!this.expressionMap.comment) { + return ""; + } + + // ANSI SQL 2003 support C style comments - comments that start with `/*` and end with `*/` + // In some dialects query nesting is available - but not all. Because of this, we'll need + // to scrub "ending" characters from the SQL but otherwise we can leave everything else + // as-is and it should be valid. + + return `/* ${this.expressionMap.comment.replace("*/", "")} */ `; + } + /** * Creates "WHERE" expression. */ protected createWhereExpression() { - let conditions = this.createWhereExpressionString(); + const conditionsArray = []; + + const whereExpression = this.createWhereExpressionString(); + whereExpression.trim() && conditionsArray.push(this.createWhereExpressionString()); if (this.expressionMap.mainAlias!.hasMetadata) { const metadata = this.expressionMap.mainAlias!.metadata; @@ -609,7 +668,7 @@ export abstract class QueryBuilder { : metadata.deleteDateColumn.propertyName; const condition = `${this.replacePropertyNames(column)} IS NULL`; - conditions = `${ conditions.length ? "(" + conditions + ") AND" : "" } ${condition}`; + conditionsArray.push(condition); } if (metadata.discriminatorColumn && metadata.parentEntityMetadata) { @@ -618,17 +677,22 @@ export abstract class QueryBuilder { : metadata.discriminatorColumn.databaseName; const condition = `${this.replacePropertyNames(column)} IN (:...discriminatorColumnValues)`; - return ` WHERE ${ conditions.length ? "(" + conditions + ") AND" : "" } ${condition}`; + conditionsArray.push(condition); } } - if (!conditions.length) // TODO copy in to discriminator condition - return this.expressionMap.extraAppendedAndWhereCondition ? " WHERE " + this.replacePropertyNames(this.expressionMap.extraAppendedAndWhereCondition) : ""; - - if (this.expressionMap.extraAppendedAndWhereCondition) - return " WHERE (" + conditions + ") AND " + this.replacePropertyNames(this.expressionMap.extraAppendedAndWhereCondition); + if (this.expressionMap.extraAppendedAndWhereCondition) { + const condition = this.replacePropertyNames(this.expressionMap.extraAppendedAndWhereCondition); + conditionsArray.push(condition); + } - return " WHERE " + conditions; + if (!conditionsArray.length) { + return " "; + } else if (conditionsArray.length === 1) { + return ` WHERE ${conditionsArray[0]}`; + } else { + return ` WHERE ( ${conditionsArray.join(" ) AND ( ")} )`; + } } /** @@ -770,6 +834,9 @@ export abstract class QueryBuilder { if (where instanceof Brackets) { const whereQueryBuilder = this.createQueryBuilder(); + whereQueryBuilder.expressionMap.mainAlias = this.expressionMap.mainAlias; + whereQueryBuilder.expressionMap.aliasNamePrefixingEnabled = this.expressionMap.aliasNamePrefixingEnabled; + whereQueryBuilder.expressionMap.nativeParameters = this.expressionMap.nativeParameters; where.whereFactory(whereQueryBuilder as any); const whereString = whereQueryBuilder.createWhereExpressionString(); this.setParameters(whereQueryBuilder.getParameters()); @@ -807,15 +874,19 @@ export abstract class QueryBuilder { } else if (parameterValue instanceof FindOperator) { let parameters: any[] = []; if (parameterValue.useParameter) { - const realParameterValues: any[] = parameterValue.multipleParameters ? parameterValue.value : [parameterValue.value]; - realParameterValues.forEach((realParameterValue, realParameterValueIndex) => { - this.expressionMap.nativeParameters[parameterName + (parameterBaseCount + realParameterValueIndex)] = realParameterValue; - parameterIndex++; - parameters.push(this.connection.driver.createParameter(parameterName + (parameterBaseCount + realParameterValueIndex), parameterIndex - 1)); - }); + if (parameterValue.objectLiteralParameters) { + this.setParameters(parameterValue.objectLiteralParameters); + } else { + const realParameterValues: any[] = parameterValue.multipleParameters ? parameterValue.value : [parameterValue.value]; + realParameterValues.forEach((realParameterValue, realParameterValueIndex) => { + this.expressionMap.nativeParameters[parameterName + (parameterBaseCount + realParameterValueIndex)] = realParameterValue; + parameterIndex++; + parameters.push(this.connection.driver.createParameter(parameterName + (parameterBaseCount + realParameterValueIndex), parameterIndex - 1)); + }); + } } - return parameterValue.toSql(this.connection, aliasPath, parameters); + return this.computeFindOperatorExpression(parameterValue, aliasPath, parameters); } else { this.expressionMap.nativeParameters[parameterName] = parameterValue; parameterIndex++; @@ -854,11 +925,64 @@ export abstract class QueryBuilder { return ""; } + /** + * Gets SQL needs to be inserted into final query. + */ + protected computeFindOperatorExpression(operator: FindOperator, aliasPath: string, parameters: any[]): string { + const { driver } = this.connection; + + switch (operator.type) { + case "not": + if (operator.child) { + return `NOT(${this.computeFindOperatorExpression(operator.child, aliasPath, parameters)})`; + } else { + return `${aliasPath} != ${parameters[0]}`; + } + case "lessThan": + return `${aliasPath} < ${parameters[0]}`; + case "lessThanOrEqual": + return `${aliasPath} <= ${parameters[0]}`; + case "moreThan": + return `${aliasPath} > ${parameters[0]}`; + case "moreThanOrEqual": + return `${aliasPath} >= ${parameters[0]}`; + case "equal": + return `${aliasPath} = ${parameters[0]}`; + case "ilike": + if (driver instanceof PostgresDriver || driver instanceof CockroachDriver) { + return `${aliasPath} ILIKE ${parameters[0]}`; + } + + return `UPPER(${aliasPath}) LIKE UPPER(${parameters[0]})`; + case "like": + return `${aliasPath} LIKE ${parameters[0]}`; + case "between": + return `${aliasPath} BETWEEN ${parameters[0]} AND ${parameters[1]}`; + case "in": + if (parameters.length === 0) { + return "0=1"; + } + return `${aliasPath} IN (${parameters.join(", ")})`; + case "any": + return `${aliasPath} = ANY(${parameters[0]})`; + case "isNull": + return `${aliasPath} IS NULL`; + case "raw": + if (operator.getSql) { + return operator.getSql(aliasPath); + } else { + return `${aliasPath} = ${operator.value}`; + } + } + + throw new TypeError(`Unsupported FindOperator ${FindOperator.constructor.name}`); + } + /** * Creates a query builder used to execute sql queries inside this query builder. */ protected obtainQueryRunner() { - return this.queryRunner || this.connection.createQueryRunner("master"); + return this.queryRunner || this.connection.createQueryRunner(); } } diff --git a/src/query-builder/QueryExpressionMap.ts b/src/query-builder/QueryExpressionMap.ts index e4c638888a9..93df26a5b21 100644 --- a/src/query-builder/QueryExpressionMap.ts +++ b/src/query-builder/QueryExpressionMap.ts @@ -271,6 +271,11 @@ export class QueryExpressionMap { */ nativeParameters: ObjectLiteral = {}; + /** + * Query Comment to include extra information for debugging or other purposes. + */ + comment?: string; + // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- @@ -429,6 +434,7 @@ export class QueryExpressionMap { map.callListeners = this.callListeners; map.useTransaction = this.useTransaction; map.nativeParameters = Object.assign({}, this.nativeParameters); + map.comment = this.comment; return map; } diff --git a/src/query-builder/QueryPartialEntity.ts b/src/query-builder/QueryPartialEntity.ts index 1e6ccfa3017..6889c547e02 100644 --- a/src/query-builder/QueryPartialEntity.ts +++ b/src/query-builder/QueryPartialEntity.ts @@ -11,7 +11,9 @@ export type QueryPartialEntity = { */ export type QueryDeepPartialEntity = { [P in keyof T]?: - T[P] extends Array ? Array> : - T[P] extends ReadonlyArray ? ReadonlyArray> : - QueryDeepPartialEntity | (() => string); + ( + T[P] extends Array ? Array> : + T[P] extends ReadonlyArray ? ReadonlyArray> : + QueryDeepPartialEntity + ) | (() => string); }; diff --git a/src/query-builder/RelationLoader.ts b/src/query-builder/RelationLoader.ts index 04eb5dd72d0..6f0c58ab5de 100644 --- a/src/query-builder/RelationLoader.ts +++ b/src/query-builder/RelationLoader.ts @@ -216,7 +216,7 @@ export class RelationLoader { // nothing is loaded yet, load relation data and save it in the model once they are loaded const loader = relationLoader.load(relation, this, queryRunner).then( - result => relation.isOneToOne || relation.isManyToOne ? result[0] : result + result => relation.isOneToOne || relation.isManyToOne ? (result.length === 0 ? null : result[0]) : result ); return setPromise(this, loader); }, diff --git a/src/query-builder/ReturningResultsEntityUpdator.ts b/src/query-builder/ReturningResultsEntityUpdator.ts index 7dcd9f100da..c669dd9c10e 100644 --- a/src/query-builder/ReturningResultsEntityUpdator.ts +++ b/src/query-builder/ReturningResultsEntityUpdator.ts @@ -66,7 +66,7 @@ export class ReturningResultsEntityUpdator { .from(metadata.target, metadata.targetName) .where(entityId) .setOption("create-pojo") // use POJO because created object can contain default values, e.g. property = null and those properties maight be overridden by merge process - .getOne(); + .getOne() as any; if (loadedReturningColumns) { this.queryRunner.manager.merge(metadata.target as any, entity, loadedReturningColumns); @@ -93,7 +93,7 @@ export class ReturningResultsEntityUpdator { } // get all values generated by a database for us const result = Array.isArray(insertResult.raw) ? insertResult.raw[entityIndex] : insertResult.raw; - const generatedMap = this.queryRunner.connection.driver.createGeneratedMap(metadata, result) || {}; + const generatedMap = this.queryRunner.connection.driver.createGeneratedMap(metadata, result, entityIndex, entities.length) || {}; // if database does not support uuid generation we need to get uuid values // generated by orm and set them to the generatedMap @@ -117,26 +117,36 @@ export class ReturningResultsEntityUpdator { // for postgres and mssql we use returning/output statement to get values of inserted default and generated values // for other drivers we have to re-select this data from the database if (this.queryRunner.connection.driver.isReturningSqlSupported() === false && insertionColumns.length > 0) { - await Promise.all(entities.map(async (entity, entityIndex) => { + const entityIds = entities.map((entity) => { const entityId = metadata.getEntityIdMap(entity)!; - // to select just inserted entity we need a criteria to select by. - // for newly inserted entities in drivers which do not support returning statement - // row identifier can only be an increment column - // (since its the only thing that can be generated by those databases) - // or (and) other primary key which is defined by a user and inserted value has it - - const returningResult: any = await this.queryRunner.manager - .createQueryBuilder() - .select(metadata.primaryColumns.map(column => metadata.targetName + "." + column.propertyPath)) - .addSelect(insertionColumns.map(column => metadata.targetName + "." + column.propertyPath)) - .from(metadata.target, metadata.targetName) - .where(entityId) - .setOption("create-pojo") // use POJO because created object can contain default values, e.g. property = null and those properties maight be overridden by merge process - .getOne(); - - this.queryRunner.manager.merge(metadata.target as any, generatedMaps[entityIndex], returningResult); - })); + // We have to check for an empty `entityId` - if we don't, the query against the database + // effectively drops the `where` clause entirely and the first record will be returned - + // not what we want at all. + if (!entityId) + throw new Error(`Cannot update entity because entity id is not set in the entity.`); + + return entityId; + }); + + // to select just inserted entities we need a criteria to select by. + // for newly inserted entities in drivers which do not support returning statement + // row identifier can only be an increment column + // (since its the only thing that can be generated by those databases) + // or (and) other primary key which is defined by a user and inserted value has it + + const returningResult: any = await this.queryRunner.manager + .createQueryBuilder() + .select(metadata.primaryColumns.map(column => metadata.targetName + "." + column.propertyPath)) + .addSelect(insertionColumns.map(column => metadata.targetName + "." + column.propertyPath)) + .from(metadata.target, metadata.targetName) + .where(entityIds) + .setOption("create-pojo") // use POJO because created object can contain default values, e.g. property = null and those properties maight be overridden by merge process + .getMany(); + + entities.forEach((entity, entityIndex) => { + this.queryRunner.manager.merge(metadata.target as any, generatedMaps[entityIndex], returningResult[entityIndex]); + }); } entities.forEach((entity, entityIndex) => { diff --git a/src/query-builder/SelectQueryBuilder.ts b/src/query-builder/SelectQueryBuilder.ts index c1de0e2f25a..d4e1caa97b2 100644 --- a/src/query-builder/SelectQueryBuilder.ts +++ b/src/query-builder/SelectQueryBuilder.ts @@ -24,7 +24,7 @@ import {EntityMetadata} from "../metadata/EntityMetadata"; import {ColumnMetadata} from "../metadata/ColumnMetadata"; import {OrderByCondition} from "../find-options/OrderByCondition"; import {QueryExpressionMap} from "./QueryExpressionMap"; -import {ObjectType} from "../common/ObjectType"; +import {EntityTarget} from "../common/EntityTarget"; import {QueryRunner} from "../query-runner/QueryRunner"; import {WhereExpression} from "./WhereExpression"; import {Brackets} from "./Brackets"; @@ -36,6 +36,8 @@ import {SelectQueryBuilderOption} from "./SelectQueryBuilderOption"; import {ObjectUtils} from "../util/ObjectUtils"; import {DriverUtils} from "../driver/DriverUtils"; import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; +import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver"; +import {EntityNotFoundError} from "../error/EntityNotFoundError"; /** * Allows to build complex sql queries in a fashion way and execute those queries. @@ -50,7 +52,8 @@ export class SelectQueryBuilder extends QueryBuilder implements * Gets generated sql query without parameters being replaced. */ getQuery(): string { - let sql = this.createSelectExpression(); + let sql = this.createComment(); + sql += this.createSelectExpression(); sql += this.createJoinExpression(); sql += this.createWhereExpression(); sql += this.createGroupByExpression(); @@ -188,14 +191,14 @@ export class SelectQueryBuilder extends QueryBuilder implements * Also sets a main string alias of the selection data. * Removes all previously set from-s. */ - from(entityTarget: ObjectType|string, aliasName: string): SelectQueryBuilder; + from(entityTarget: EntityTarget, aliasName: string): SelectQueryBuilder; /** * Specifies FROM which entity's table select/update/delete will be executed. * Also sets a main string alias of the selection data. * Removes all previously set from-s. */ - from(entityTarget: ObjectType|string|((qb: SelectQueryBuilder) => SelectQueryBuilder), aliasName: string): SelectQueryBuilder { + from(entityTarget: EntityTarget|((qb: SelectQueryBuilder) => SelectQueryBuilder), aliasName: string): SelectQueryBuilder { const mainAlias = this.createFromAlias(entityTarget, aliasName); this.expressionMap.setMainAlias(mainAlias); return (this as any) as SelectQueryBuilder; @@ -211,13 +214,13 @@ export class SelectQueryBuilder extends QueryBuilder implements * Specifies FROM which entity's table select/update/delete will be executed. * Also sets a main string alias of the selection data. */ - addFrom(entityTarget: ObjectType|string, aliasName: string): SelectQueryBuilder; + addFrom(entityTarget: EntityTarget, aliasName: string): SelectQueryBuilder; /** * Specifies FROM which entity's table select/update/delete will be executed. * Also sets a main string alias of the selection data. */ - addFrom(entityTarget: ObjectType|string|((qb: SelectQueryBuilder) => SelectQueryBuilder), aliasName: string): SelectQueryBuilder { + addFrom(entityTarget: EntityTarget|((qb: SelectQueryBuilder) => SelectQueryBuilder), aliasName: string): SelectQueryBuilder { const alias = this.createFromAlias(entityTarget, aliasName); if (!this.expressionMap.mainAlias) this.expressionMap.setMainAlias(alias); @@ -1103,6 +1106,19 @@ export class SelectQueryBuilder extends QueryBuilder implements return result; } + /** + * Gets the first entity returned by execution of generated query builder sql or rejects the returned promise on error. + */ + async getOneOrFail(): Promise { + const entity = await this.getOne(); + + if (!entity) { + throw new EntityNotFoundError(this.expressionMap.mainAlias!.target, this); + } + + return entity; + } + /** * Gets entities returned by execution of generated query builder sql. */ @@ -1245,9 +1261,6 @@ export class SelectQueryBuilder extends QueryBuilder implements } throw error; - } finally { - if (queryRunner !== this.queryRunner) // means we created our own query runner - await queryRunner.release(); } } @@ -1673,14 +1686,14 @@ export class SelectQueryBuilder extends QueryBuilder implements throw new LockNotSupportedOnGivenDriverError(); } case "pessimistic_partial_write": - if (driver instanceof PostgresDriver) { + if (driver instanceof PostgresDriver || driver instanceof MysqlDriver) { return " FOR UPDATE SKIP LOCKED"; } else { throw new LockNotSupportedOnGivenDriverError(); } case "pessimistic_write_or_fail": - if (driver instanceof PostgresDriver) { + if (driver instanceof PostgresDriver || driver instanceof MysqlDriver) { return " FOR UPDATE NOWAIT"; } else { throw new LockNotSupportedOnGivenDriverError(); @@ -1773,34 +1786,75 @@ export class SelectQueryBuilder extends QueryBuilder implements }); } - protected async executeCountQuery(queryRunner: QueryRunner): Promise { - + private computeCountExpression() { const mainAlias = this.expressionMap.mainAlias!.name; // todo: will this work with "fromTableName"? const metadata = this.expressionMap.mainAlias!.metadata; + const primaryColumns = metadata.primaryColumns; const distinctAlias = this.escape(mainAlias); - let countSql: string = ""; - if (metadata.hasMultiplePrimaryKeys) { - if (this.connection.driver instanceof AbstractSqliteDriver) { - countSql = `COUNT(DISTINCT(` + metadata.primaryColumns.map((primaryColumn, index) => { - const propertyName = this.escape(primaryColumn.databaseName); - return `${distinctAlias}.${propertyName}`; - }).join(" || ") + ")) as \"cnt\""; - } else { - countSql = `COUNT(DISTINCT(CONCAT(` + metadata.primaryColumns.map((primaryColumn, index) => { - const propertyName = this.escape(primaryColumn.databaseName); - return `${distinctAlias}.${propertyName}`; - }).join(", ") + "))) as \"cnt\""; + // If we aren't doing anything that will create a join, we can use a simpler `COUNT` instead + // so we prevent poor query patterns in the most likely cases + if ( + this.expressionMap.joinAttributes.length === 0 && + this.expressionMap.relationIdAttributes.length === 0 && + this.expressionMap.relationCountAttributes.length === 0 + ) { + return "COUNT(1)"; + } + + // For everything else, we'll need to do some hackery to get the correct count values. + + if (this.connection.driver instanceof CockroachDriver || this.connection.driver instanceof PostgresDriver) { + // Postgres and CockroachDB can pass multiple parameters to the `DISTINCT` function + // https://www.postgresql.org/docs/9.5/sql-select.html#SQL-DISTINCT + return "COUNT(DISTINCT(" + + primaryColumns.map(c => `${distinctAlias}.${this.escape(c.databaseName)}`).join(", ") + + "))"; + } + + if (this.connection.driver instanceof MysqlDriver) { + // MySQL & MariaDB can pass multiple parameters to the `DISTINCT` language construct + // https://mariadb.com/kb/en/count-distinct/ + return "COUNT(DISTINCT " + + primaryColumns.map(c => `${distinctAlias}.${this.escape(c.databaseName)}`).join(", ") + + ")"; + } + + if (this.connection.driver instanceof SqlServerDriver) { + // SQL Server has gotta be different from everyone else. They don't support + // distinct counting multiple columns & they don't have the same operator + // characteristic for concatenating, so we gotta use the `CONCAT` function. + // However, If it's exactly 1 column we can omit the `CONCAT` for better performance. + + const columnsExpression = primaryColumns.map( + primaryColumn => `${distinctAlias}.${this.escape(primaryColumn.databaseName)}` + ).join(", '|;|', "); + + if (primaryColumns.length === 1) { + + return `COUNT(DISTINCT(${columnsExpression}))`; } - } else { - countSql = `COUNT(DISTINCT(` + metadata.primaryColumns.map((primaryColumn, index) => { - const propertyName = this.escape(primaryColumn.databaseName); - return `${distinctAlias}.${propertyName}`; - }).join(", ") + ")) as \"cnt\""; + return `COUNT(DISTINCT(CONCAT(${columnsExpression})))`; + } + // If all else fails, fall back to a `COUNT` and `DISTINCT` across all the primary columns concatenated. + // Per the SQL spec, this is the canonical string concatenation mechanism which is most + // likely to work across servers implementing the SQL standard. + + // Please note, if there is only one primary column that the concatenation does not occur in this + // query and the query is a standard `COUNT DISTINCT` in that case. + + return `COUNT(DISTINCT(` + + primaryColumns.map(c => `${distinctAlias}.${this.escape(c.databaseName)}`).join(" || '|;|' || ") + + "))"; + } + + protected async executeCountQuery(queryRunner: QueryRunner): Promise { + const countSql = this.computeCountExpression(); + const results = await this.clone() .orderBy() .groupBy() @@ -1808,7 +1862,7 @@ export class SelectQueryBuilder extends QueryBuilder implements .limit(undefined) .skip(undefined) .take(undefined) - .select(countSql) + .select(countSql, "cnt") .setOption("disable-global-order") .loadRawResults(queryRunner); @@ -1861,7 +1915,14 @@ export class SelectQueryBuilder extends QueryBuilder implements const columnAlias = this.escape(DriverUtils.buildColumnAlias(this.connection.driver, mainAliasName, primaryColumn.databaseName)); if (!orderBys[columnAlias]) // make sure we aren't overriding user-defined order in inverse direction orderBys[columnAlias] = "ASC"; - return `${distinctAlias}.${columnAlias} as "ids_${DriverUtils.buildColumnAlias(this.connection.driver, mainAliasName, primaryColumn.databaseName)}"`; + + const alias = DriverUtils.buildColumnAlias( + this.connection.driver, + "ids_" + mainAliasName, + primaryColumn.databaseName + ); + + return `${distinctAlias}.${columnAlias} as "${alias}"`; }); rawResults = await new SelectQueryBuilder(this.connection, queryRunner) @@ -1888,7 +1949,13 @@ export class SelectQueryBuilder extends QueryBuilder implements }).join(" AND "); }).join(" OR "); } else { - const ids = rawResults.map(result => result["ids_" + DriverUtils.buildColumnAlias(this.connection.driver, mainAliasName, metadata.primaryColumns[0].databaseName)]); + const alias = DriverUtils.buildColumnAlias( + this.connection.driver, + "ids_" + mainAliasName, + metadata.primaryColumns[0].databaseName + ); + + const ids = rawResults.map(result => result[alias]); const areAllNumbers = ids.every((id: any) => typeof id === "number"); if (areAllNumbers) { // fixes #190. if all numbers then its safe to perform query without parameter @@ -1937,9 +2004,11 @@ export class SelectQueryBuilder extends QueryBuilder implements const selectString = Object.keys(orderBys) .map(orderCriteria => { if (orderCriteria.indexOf(".") !== -1) { - const [aliasName, propertyPath] = orderCriteria.split("."); + const criteriaParts = orderCriteria.split("."); + const aliasName = criteriaParts[0]; + const propertyPath = criteriaParts.slice(1).join("."); const alias = this.expressionMap.findAliasByName(aliasName); - const column = alias.metadata.findColumnWithPropertyName(propertyPath); + const column = alias.metadata.findColumnWithPropertyPath(propertyPath); return this.escape(parentAlias) + "." + this.escape(DriverUtils.buildColumnAlias(this.connection.driver, aliasName, column!.databaseName)); } else { if (this.expressionMap.selects.find(select => select.selection === orderCriteria || select.aliasName === orderCriteria)) @@ -1953,9 +2022,11 @@ export class SelectQueryBuilder extends QueryBuilder implements const orderByObject: OrderByCondition = {}; Object.keys(orderBys).forEach(orderCriteria => { if (orderCriteria.indexOf(".") !== -1) { - const [aliasName, propertyPath] = orderCriteria.split("."); + const criteriaParts = orderCriteria.split("."); + const aliasName = criteriaParts[0]; + const propertyPath = criteriaParts.slice(1).join("."); const alias = this.expressionMap.findAliasByName(aliasName); - const column = alias.metadata.findColumnWithPropertyName(propertyPath); + const column = alias.metadata.findColumnWithPropertyPath(propertyPath); orderByObject[this.escape(parentAlias) + "." + this.escape(DriverUtils.buildColumnAlias(this.connection.driver, aliasName, column!.databaseName))] = orderBys[orderCriteria]; } else { if (this.expressionMap.selects.find(select => select.selection === orderCriteria || select.aliasName === orderCriteria)) { diff --git a/src/query-builder/SoftDeleteQueryBuilder.ts b/src/query-builder/SoftDeleteQueryBuilder.ts index 3f5b903f486..f4e54395cfa 100644 --- a/src/query-builder/SoftDeleteQueryBuilder.ts +++ b/src/query-builder/SoftDeleteQueryBuilder.ts @@ -1,7 +1,7 @@ import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver"; import {QueryBuilder} from "./QueryBuilder"; import {ObjectLiteral} from "../common/ObjectLiteral"; -import {ObjectType} from "../common/ObjectType"; +import {EntityTarget} from "../common/EntityTarget"; import {Connection} from "../connection/Connection"; import {QueryRunner} from "../query-runner/QueryRunner"; import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; @@ -142,7 +142,7 @@ export class SoftDeleteQueryBuilder extends QueryBuilder impleme * Specifies FROM which entity's table select/update/delete/soft-delete will be executed. * Also sets a main string alias of the selection data. */ - from(entityTarget: ObjectType|EntitySchema|string, aliasName?: string): SoftDeleteQueryBuilder { + from(entityTarget: EntityTarget, aliasName?: string): SoftDeleteQueryBuilder { entityTarget = entityTarget instanceof EntitySchema ? entityTarget.options.name : entityTarget; const mainAlias = this.createFromAlias(entityTarget, aliasName); this.expressionMap.setMainAlias(mainAlias); diff --git a/src/query-builder/UpdateQueryBuilder.ts b/src/query-builder/UpdateQueryBuilder.ts index 22ac5c5e127..19a091da615 100644 --- a/src/query-builder/UpdateQueryBuilder.ts +++ b/src/query-builder/UpdateQueryBuilder.ts @@ -24,6 +24,7 @@ import {UpdateValuesMissingError} from "../error/UpdateValuesMissingError"; import {EntityColumnNotFound} from "../error/EntityColumnNotFound"; import {QueryDeepPartialEntity} from "./QueryPartialEntity"; import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; +import {BetterSqlite3Driver} from "../driver/better-sqlite3/BetterSqlite3Driver"; /** * Allows to build complex sql queries in a fashion way and execute those queries. @@ -47,7 +48,8 @@ export class UpdateQueryBuilder extends QueryBuilder implements * Gets generated sql query without parameters being replaced. */ getQuery(): string { - let sql = this.createUpdateExpression(); + let sql = this.createComment(); + sql += this.createUpdateExpression(); sql += this.createOrderByExpression(); sql += this.createLimitExpression(); return sql.trim(); @@ -108,6 +110,10 @@ export class UpdateQueryBuilder extends QueryBuilder implements updateResult.raw = result; updateResult.affected = result.affectedRows; } + else if (this.connection.driver instanceof BetterSqlite3Driver) { // only works for better-sqlite3 + updateResult.raw = result; + updateResult.affected = result.changes; + } else { updateResult.raw = result; } diff --git a/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts b/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts index 89118a4adbd..7048429245a 100644 --- a/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts +++ b/src/query-builder/transformer/RawSqlResultsToEntityTransformer.ts @@ -70,6 +70,10 @@ export class RawSqlResultsToEntityTransformer { return keyValue.toString("hex"); } + if (typeof keyValue === "object") { + return JSON.stringify(keyValue); + } + return keyValue; }).join("_"); // todo: check partial @@ -93,7 +97,7 @@ export class RawSqlResultsToEntityTransformer { if (metadata.discriminatorColumn) { const discriminatorValues = rawResults.map(result => result[DriverUtils.buildColumnAlias(this.driver, alias.name, alias.metadata.discriminatorColumn!.databaseName)]); const discriminatorMetadata = metadata.childEntityMetadatas.find(childEntityMetadata => { - return !!discriminatorValues.find(value => value === childEntityMetadata.discriminatorValue); + return typeof discriminatorValues.find(value => value === childEntityMetadata.discriminatorValue) !== 'undefined'; }); if (discriminatorMetadata) metadata = discriminatorMetadata; @@ -233,7 +237,7 @@ export class RawSqlResultsToEntityTransformer { const idMap = columns.reduce((idMap, column) => { let value = result[column.databaseName]; if (relation.isOneToMany || relation.isOneToOneNotOwner) { - if (column.referencedColumn) // if column is a relation + if (column.isVirtual && column.referencedColumn && column.referencedColumn.propertyName !== column.propertyName) // if column is a relation value = column.referencedColumn.createValueMap(value); return OrmUtils.mergeDeep(idMap, column.createValueMap(value)); @@ -253,8 +257,7 @@ export class RawSqlResultsToEntityTransformer { } } return idMap; - }).filter(result => result); - + }).filter(result => result !== undefined); const properties = rawRelationIdResult.relationIdAttribute.mapToPropertyPropertyPath.split("."); const mapToProperty = (properties: string[], map: ObjectLiteral, value: any): any => { diff --git a/src/query-runner/BaseQueryRunner.ts b/src/query-runner/BaseQueryRunner.ts index fefb2e05a2e..6e73f226e54 100644 --- a/src/query-runner/BaseQueryRunner.ts +++ b/src/query-runner/BaseQueryRunner.ts @@ -3,12 +3,12 @@ import {Query} from "../driver/Query"; import {SqlInMemory} from "../driver/SqlInMemory"; import {SqlServerConnectionOptions} from "../driver/sqlserver/SqlServerConnectionOptions"; import {View} from "../schema-builder/view/View"; -import {PromiseUtils} from "../util/PromiseUtils"; import {Connection} from "../connection/Connection"; import {Table} from "../schema-builder/table/Table"; import {EntityManager} from "../entity-manager/EntityManager"; import {TableColumn} from "../schema-builder/table/TableColumn"; import {Broadcaster} from "../subscriber/Broadcaster"; +import {ReplicationMode} from "../driver/types/ReplicationMode"; export abstract class BaseQueryRunner { @@ -82,7 +82,7 @@ export abstract class BaseQueryRunner { * Used for replication. * If replication is not setup its value is ignored. */ - protected mode: "master"|"slave"; + protected mode: ReplicationMode; // ------------------------------------------------------------------------- // Public Abstract Methods @@ -176,14 +176,18 @@ export abstract class BaseQueryRunner { * Executes up sql queries. */ async executeMemoryUpSql(): Promise { - await PromiseUtils.runInSequence(this.sqlInMemory.upQueries, upQuery => this.query(upQuery.query, upQuery.parameters)); + for (const {query, parameters} of this.sqlInMemory.upQueries) { + await this.query(query, parameters); + } } /** * Executes down sql queries. */ async executeMemoryDownSql(): Promise { - await PromiseUtils.runInSequence(this.sqlInMemory.downQueries.reverse(), downQuery => this.query(downQuery.query, downQuery.parameters)); + for (const {query, parameters} of this.sqlInMemory.downQueries.reverse()) { + await this.query(query, parameters); + } } // ------------------------------------------------------------------------- @@ -312,27 +316,6 @@ export abstract class BaseQueryRunner { return false; } - /** - * Checks if column display width is by default. Used only for MySQL. - */ - protected isDefaultColumnWidth(table: Table, column: TableColumn, width: number): boolean { - // if table have metadata, we check if length is specified in column metadata - if (this.connection.hasMetadata(table.name)) { - const metadata = this.connection.getMetadata(table.name); - const columnMetadata = metadata.findColumnWithDatabaseName(column.name); - if (columnMetadata && columnMetadata.width) - return false; - } - - if (this.connection.driver.dataTypeDefaults - && this.connection.driver.dataTypeDefaults[column.type] - && this.connection.driver.dataTypeDefaults[column.type].width) { - return this.connection.driver.dataTypeDefaults[column.type].width === width; - } - - return false; - } - /** * Checks if column precision is by default. */ @@ -391,7 +374,9 @@ export abstract class BaseQueryRunner { if (this.sqlMemoryMode === true) return Promise.resolve() as Promise; - await PromiseUtils.runInSequence(upQueries, upQuery => this.query(upQuery.query, upQuery.parameters)); + for (const {query, parameters} of upQueries) { + await this.query(query, parameters); + } } } diff --git a/src/query-runner/QueryRunner.ts b/src/query-runner/QueryRunner.ts index 62c9f78424e..532fae39913 100644 --- a/src/query-runner/QueryRunner.ts +++ b/src/query-runner/QueryRunner.ts @@ -92,7 +92,7 @@ export interface QueryRunner { commitTransaction(): Promise; /** - * Ends transaction. + * Rollbacks transaction. * Error will be thrown if transaction was not started. */ rollbackTransaction(): Promise; diff --git a/src/repository/AbstractRepository.ts b/src/repository/AbstractRepository.ts index 87005cb0a93..789368f2b9b 100644 --- a/src/repository/AbstractRepository.ts +++ b/src/repository/AbstractRepository.ts @@ -2,12 +2,12 @@ import {ObjectLiteral} from "../common/ObjectLiteral"; import {EntityManager} from "../entity-manager/EntityManager"; import {Repository} from "./Repository"; import {TreeRepository} from "./TreeRepository"; +import {EntityTarget} from "../common/EntityTarget"; import {ObjectType} from "../common/ObjectType"; import {CustomRepositoryDoesNotHaveEntityError} from "../error/CustomRepositoryDoesNotHaveEntityError"; import {getMetadataArgsStorage} from "../index"; import {CustomRepositoryNotFoundError} from "../error/CustomRepositoryNotFoundError"; import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder"; -import { EntitySchema } from "../entity-schema/EntitySchema"; /** * Provides abstract class for custom repositories that do not inherit from original orm Repository. @@ -100,7 +100,7 @@ export class AbstractRepository { * Gets custom repository's managed entity. * If given custom repository does not manage any entity then undefined will be returned. */ - private getCustomRepositoryTarget(customRepository: any): Function|string|EntitySchema|undefined { + private getCustomRepositoryTarget(customRepository: any): EntityTarget|undefined { const entityRepositoryMetadataArgs = getMetadataArgsStorage().entityRepositories.find(repository => { return repository.target === (customRepository instanceof Function ? customRepository : (customRepository as any).constructor); }); @@ -110,4 +110,4 @@ export class AbstractRepository { return entityRepositoryMetadataArgs.entity; } -} \ No newline at end of file +} diff --git a/src/repository/MongoRepository.ts b/src/repository/MongoRepository.ts index c43b6e859c8..2d9ccf754fa 100644 --- a/src/repository/MongoRepository.ts +++ b/src/repository/MongoRepository.ts @@ -24,7 +24,8 @@ import { InsertWriteOpResult, MapReduceOptions, MongoCountPreferences, - MongodbIndexOptions, ObjectID, + MongodbIndexOptions, + ObjectID, OrderedBulkOperation, ParallelCollectionScanOptions, ReadPreference, @@ -357,4 +358,4 @@ export class MongoRepository extends Repository { create(): Entity; /** - * Creates a new entities and copies all entity properties from given objects into their new entities. - * Note that it copies only properties that present in entity schema. + * Creates new entities and copies all entity properties from given objects into their new entities. + * Note that it copies only properties that are present in entity schema. */ create(entityLikeArray: DeepPartial[]): Entity[]; /** * Creates a new entity instance and copies all entity properties from this object into a new entity. - * Note that it copies only properties that present in entity schema. + * Note that it copies only properties that are present in entity schema. */ create(entityLike: DeepPartial): Entity; @@ -147,7 +147,7 @@ export class Repository { * Saves one or many given entities. */ save>(entityOrEntities: T|T[], options?: SaveOptions): Promise { - return this.manager.save(this.metadata.target as any, entityOrEntities as any, options); + return this.manager.save(this.metadata.target as any, entityOrEntities as any, options); } /** @@ -191,7 +191,7 @@ export class Repository { * Records the delete date of one or many given entities. */ softRemove>(entityOrEntities: T|T[], options?: SaveOptions): Promise { - return this.manager.softRemove(this.metadata.target as any, entityOrEntities as any, options); + return this.manager.softRemove(this.metadata.target as any, entityOrEntities as any, options); } /** @@ -218,7 +218,7 @@ export class Repository { * Recovers one or many given entities. */ recover>(entityOrEntities: T|T[], options?: SaveOptions): Promise { - return this.manager.recover(this.metadata.target as any, entityOrEntities as any, options); + return this.manager.recover(this.metadata.target as any, entityOrEntities as any, options); } /** diff --git a/src/repository/TreeRepository.ts b/src/repository/TreeRepository.ts index 376a26693aa..725a947a3d2 100644 --- a/src/repository/TreeRepository.ts +++ b/src/repository/TreeRepository.ts @@ -33,7 +33,7 @@ export class TreeRepository extends Repository { const escapeAlias = (alias: string) => this.manager.connection.driver.escape(alias); const escapeColumn = (column: string) => this.manager.connection.driver.escape(column); const parentPropertyName = this.manager.connection.namingStrategy.joinColumnName( - this.metadata.treeParentRelation!.propertyName, "id" + this.metadata.treeParentRelation!.propertyName, this.metadata.primaryColumns[0].propertyName ); return this.createQueryBuilder("treeEntity") @@ -265,7 +265,7 @@ export class TreeRepository extends Repository { const parentEntityId = this.metadata.primaryColumns[0].getEntityValue(entity); const childRelationMaps = relationMaps.filter(relationMap => relationMap.parentId === parentEntityId); const childIds = new Set(childRelationMaps.map(relationMap => relationMap.id)); - entity[childProperty] = entities.filter(entity => childIds.has(entity.id)); + entity[childProperty] = entities.filter(entity => childIds.has(entity[this.metadata.primaryColumns[0].propertyName])); entity[childProperty].forEach((childEntity: any) => { this.buildChildrenEntityTree(childEntity, entities, relationMaps); }); diff --git a/src/schema-builder/MongoSchemaBuilder.ts b/src/schema-builder/MongoSchemaBuilder.ts index b6f58c4a184..5f580581f9a 100644 --- a/src/schema-builder/MongoSchemaBuilder.ts +++ b/src/schema-builder/MongoSchemaBuilder.ts @@ -1,8 +1,8 @@ -import { Connection } from "../connection/Connection"; -import { SchemaBuilder } from "./SchemaBuilder"; -import { MongoDriver } from "../driver/mongodb/MongoDriver"; -import { SqlInMemory } from "../driver/SqlInMemory"; -import { MongodbIndexOptions } from "../driver/mongodb/typings"; +import {Connection} from "../connection/Connection"; +import {SchemaBuilder} from "./SchemaBuilder"; +import {MongoQueryRunner} from "../driver/mongodb/MongoQueryRunner"; +import {SqlInMemory} from "../driver/SqlInMemory"; +import {MongodbIndexOptions} from "../driver/mongodb/typings"; /** * Creates complete tables schemas in the database based on the entity metadatas. @@ -35,7 +35,7 @@ export class MongoSchemaBuilder implements SchemaBuilder { * Creates complete schemas for the given entity metadatas. */ async build(): Promise { - const queryRunner = (this.connection.driver as MongoDriver).createQueryRunner(); + const queryRunner = this.connection.createQueryRunner() as MongoQueryRunner; const promises: Promise[] = []; this.connection.entityMetadatas.forEach(metadata => { metadata.indices.forEach(index => { diff --git a/src/schema-builder/RdbmsSchemaBuilder.ts b/src/schema-builder/RdbmsSchemaBuilder.ts index 60749e056b0..b565dacfd30 100644 --- a/src/schema-builder/RdbmsSchemaBuilder.ts +++ b/src/schema-builder/RdbmsSchemaBuilder.ts @@ -1,28 +1,27 @@ -import { CockroachDriver } from "../driver/cockroachdb/CockroachDriver"; -import { PostgresConnectionOptions } from "../driver/postgres/PostgresConnectionOptions"; -import { SapDriver } from "../driver/sap/SapDriver"; -import { SqlServerConnectionOptions } from "../driver/sqlserver/SqlServerConnectionOptions"; -import { Table } from "./table/Table"; -import { TableColumn } from "./table/TableColumn"; -import { TableForeignKey } from "./table/TableForeignKey"; -import { TableIndex } from "./table/TableIndex"; -import { QueryRunner } from "../query-runner/QueryRunner"; -import { ColumnMetadata } from "../metadata/ColumnMetadata"; -import { EntityMetadata } from "../metadata/EntityMetadata"; -import { PromiseUtils } from "../util/PromiseUtils"; -import { Connection } from "../connection/Connection"; -import { SchemaBuilder } from "./SchemaBuilder"; -import { SqlInMemory } from "../driver/SqlInMemory"; -import { TableUtils } from "./util/TableUtils"; -import { TableColumnOptions } from "./options/TableColumnOptions"; -import { PostgresDriver } from "../driver/postgres/PostgresDriver"; -import { SqlServerDriver } from "../driver/sqlserver/SqlServerDriver"; -import { MysqlDriver } from "../driver/mysql/MysqlDriver"; -import { TableUnique } from "./table/TableUnique"; -import { TableCheck } from "./table/TableCheck"; -import { TableExclusion } from "./table/TableExclusion"; -import { View } from "./view/View"; -import { AuroraDataApiDriver } from "../driver/aurora-data-api/AuroraDataApiDriver"; +import {CockroachDriver} from "../driver/cockroachdb/CockroachDriver"; +import {PostgresConnectionOptions} from "../driver/postgres/PostgresConnectionOptions"; +import {SapDriver} from "../driver/sap/SapDriver"; +import {SqlServerConnectionOptions} from "../driver/sqlserver/SqlServerConnectionOptions"; +import {Table} from "./table/Table"; +import {TableColumn} from "./table/TableColumn"; +import {TableForeignKey} from "./table/TableForeignKey"; +import {TableIndex} from "./table/TableIndex"; +import {QueryRunner} from "../query-runner/QueryRunner"; +import {ColumnMetadata} from "../metadata/ColumnMetadata"; +import {EntityMetadata} from "../metadata/EntityMetadata"; +import {Connection} from "../connection/Connection"; +import {SchemaBuilder} from "./SchemaBuilder"; +import {SqlInMemory} from "../driver/SqlInMemory"; +import {TableUtils} from "./util/TableUtils"; +import {TableColumnOptions} from "./options/TableColumnOptions"; +import {PostgresDriver} from "../driver/postgres/PostgresDriver"; +import {SqlServerDriver} from "../driver/sqlserver/SqlServerDriver"; +import {MysqlDriver} from "../driver/mysql/MysqlDriver"; +import {TableUnique} from "./table/TableUnique"; +import {TableCheck} from "./table/TableCheck"; +import {TableExclusion} from "./table/TableExclusion"; +import {View} from "./view/View"; +import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; import { ForeignKeyMetadata } from "../metadata/ForeignKeyMetadata"; /** @@ -63,7 +62,7 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { * Creates complete schemas for the given entity metadatas. */ async build(): Promise { - this.queryRunner = this.connection.createQueryRunner("master"); + this.queryRunner = this.connection.createQueryRunner(); // CockroachDB implements asynchronous schema sync operations which can not been executed in transaction. // E.g. if you try to DROP column and ADD it again in the same transaction, crdb throws error. if (!(this.connection.driver instanceof CockroachDriver)) @@ -104,7 +103,7 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { * Returns sql queries to be executed by schema builder. */ async log(): Promise { - this.queryRunner = this.connection.createQueryRunner("master"); + this.queryRunner = this.connection.createQueryRunner(); try { const tablePaths = this.entityToSyncMetadatas.map( (metadata) => metadata.tablePath @@ -190,64 +189,35 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { * Drops all (old) foreign keys that exist in the tables, but do not exist in the entity metadata. */ protected async dropOldForeignKeys(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; - - // find foreign keys that exist in the schemas but does not exist in the entity metadata - const tableForeignKeysToDrop = table.foreignKeys.filter( - (tableForeignKey) => { - const metadataFK = metadata.foreignKeys.find( - (metadataForeignKey) => - foreignKeysMatch( - tableForeignKey, - metadataForeignKey - ) - ); - return ( - !metadataFK || - (metadataFK.onDelete && - metadataFK.onDelete !== - tableForeignKey.onDelete) || - (metadataFK.onUpdate && - metadataFK.onUpdate !== - tableForeignKey.onUpdate) - ); - } - ); - if (tableForeignKeysToDrop.length === 0) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; + + // find foreign keys that exist in the schemas but does not exist in the entity metadata + const tableForeignKeysToDrop = table.foreignKeys.filter(tableForeignKey => { + const metadataFK = metadata.foreignKeys.find(metadataForeignKey => foreignKeysMatch(tableForeignKey, metadataForeignKey)); + return !metadataFK + || (metadataFK.onDelete && metadataFK.onDelete !== tableForeignKey.onDelete) + || (metadataFK.onUpdate && metadataFK.onUpdate !== tableForeignKey.onUpdate); + }); + if (tableForeignKeysToDrop.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `dropping old foreign keys of ${ - table.name - }: ${tableForeignKeysToDrop - .map((dbForeignKey) => dbForeignKey.name) - .join(", ")}` - ); + this.connection.logger.logSchemaBuild(`dropping old foreign keys of ${table.name}: ${tableForeignKeysToDrop.map(dbForeignKey => dbForeignKey.name).join(", ")}`); - // drop foreign keys from the database - await this.queryRunner.dropForeignKeys( - table, - tableForeignKeysToDrop - ); - } - ); + // drop foreign keys from the database + await this.queryRunner.dropForeignKeys(table, tableForeignKeysToDrop); + } } /** * Rename tables */ protected async renameTables(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - // const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); - } - ); + // for (const metadata of this.entityToSyncMetadatas) { + // const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + // } } /** @@ -256,15 +226,13 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { * Changes only column name. If something besides name was changed, these changes will be ignored. */ protected async renameColumns(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; - if (metadata.columns.length !== table.columns.length) return; + if (metadata.columns.length !== table.columns.length) + continue; const renamedMetadataColumns = metadata.columns.filter( (column) => { @@ -285,11 +253,8 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { } ); - if ( - renamedMetadataColumns.length === 0 || - renamedMetadataColumns.length > 1 - ) - return; + if (renamedMetadataColumns.length === 0 || renamedMetadataColumns.length > 1) + continue; const renamedTableColumns = table.columns.filter( (tableColumn) => { @@ -307,35 +272,22 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { } ); - if ( - renamedTableColumns.length === 0 || - renamedTableColumns.length > 1 - ) - return; + if (renamedTableColumns.length === 0 || renamedTableColumns.length > 1) + continue; const renamedColumn = renamedTableColumns[0].clone(); renamedColumn.name = renamedMetadataColumns[0].databaseName; - this.connection.logger.logSchemaBuild( - `renaming column "${renamedTableColumns[0].name}" in to "${renamedColumn.name}"` - ); - await this.queryRunner.renameColumn( - table, - renamedTableColumns[0], - renamedColumn - ); - } - ); + this.connection.logger.logSchemaBuild(`renaming column "${renamedTableColumns[0].name}" in to "${renamedColumn.name}"`); + await this.queryRunner.renameColumn(table, renamedTableColumns[0], renamedColumn); + } } protected async dropOldIndices(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const dropQueries = table.indices .filter((tableIndex) => { @@ -354,11 +306,8 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ) return true; - if ( - indexMetadata.isFulltext !== - tableIndex.isFulltext - ) - return true; + if (this.connection.driver.isFullTextColumnTypeSupported() && indexMetadata.isFulltext !== tableIndex.isFulltext) + return true; if ( indexMetadata.columns.length !== @@ -383,9 +332,8 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { await this.queryRunner.dropIndex(table, tableIndex); }); - await Promise.all(dropQueries); - } - ); + await Promise.all(dropQueries); + } } protected async dropOldChecks(): Promise { @@ -396,13 +344,10 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ) return; - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const oldChecks = table.checks.filter((tableCheck) => { return !metadata.checks.find( @@ -411,26 +356,19 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ); }); - if (oldChecks.length === 0) return; + if (oldChecks.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `dropping old check constraint: ${oldChecks - .map((check) => `"${check.name}"`) - .join(", ")} from table "${table.name}"` - ); - await this.queryRunner.dropCheckConstraints(table, oldChecks); - } - ); + this.connection.logger.logSchemaBuild(`dropping old check constraint: ${oldChecks.map(check => `"${check.name}"`).join(", ")} from table "${table.name}"`); + await this.queryRunner.dropCheckConstraints(table, oldChecks); + } } protected async dropCompositeUniqueConstraints(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const compositeUniques = table.uniques.filter((tableUnique) => { return ( @@ -442,32 +380,22 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ); }); - if (compositeUniques.length === 0) return; + if (compositeUniques.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `dropping old unique constraint: ${compositeUniques - .map((unique) => `"${unique.name}"`) - .join(", ")} from table "${table.name}"` - ); - await this.queryRunner.dropUniqueConstraints( - table, - compositeUniques - ); - } - ); + this.connection.logger.logSchemaBuild(`dropping old unique constraint: ${compositeUniques.map(unique => `"${unique.name}"`).join(", ")} from table "${table.name}"`); + await this.queryRunner.dropUniqueConstraints(table, compositeUniques); + } } protected async dropOldExclusions(): Promise { // Only PostgreSQL supports exclusion constraints if (!(this.connection.driver instanceof PostgresDriver)) return; - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const oldExclusions = table.exclusions.filter( (tableExclusion) => { @@ -478,19 +406,12 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { } ); - if (oldExclusions.length === 0) return; + if (oldExclusions.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `dropping old exclusion constraint: ${oldExclusions - .map((exclusion) => `"${exclusion.name}"`) - .join(", ")} from table "${table.name}"` - ); - await this.queryRunner.dropExclusionConstraints( - table, - oldExclusions - ); - } - ); + this.connection.logger.logSchemaBuild(`dropping old exclusion constraint: ${oldExclusions.map(exclusion => `"${exclusion.name}"`).join(", ")} from table "${table.name}"`); + await this.queryRunner.dropExclusionConstraints(table, oldExclusions); + } } /** @@ -499,147 +420,76 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { * Primary key only can be created in conclusion with auto generated column. */ protected async createNewTables(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - // check if table does not exist yet - const existTable = this.queryRunner.loadedTables.find( - (table) => { - const database = - metadata.database && - metadata.database !== - this.connection.driver.database - ? metadata.database - : undefined; - const schema = - metadata.schema || - (( - this.connection.driver - )).options.schema; - const fullTableName = this.connection.driver.buildTableName( - metadata.tableName, - schema, - database - ); - - return table.name === fullTableName; - } - ); - if (existTable) return; + for (const metadata of this.entityToSyncMetadatas) { + // check if table does not exist yet + const existTable = this.queryRunner.loadedTables.find(table => { + const database = metadata.database && metadata.database !== this.connection.driver.database ? metadata.database : undefined; + const schema = metadata.schema || (this.connection.driver).options.schema; + const fullTableName = this.connection.driver.buildTableName(metadata.tableName, schema, database); + + return table.name === fullTableName; + }); + if (existTable) + continue; this.connection.logger.logSchemaBuild( `creating a new table: ${metadata.tablePath}` ); - // create a new table and sync it in the database - const table = Table.create(metadata, this.connection.driver); - await this.queryRunner.createTable(table, false, false); - this.queryRunner.loadedTables.push(table); - } - ); + // create a new table and sync it in the database + const table = Table.create(metadata, this.connection.driver); + await this.queryRunner.createTable(table, false, false); + this.queryRunner.loadedTables.push(table); + } } protected async createViews(): Promise { - await PromiseUtils.runInSequence( - this.viewEntityToSyncMetadatas, - async (metadata) => { - // check if view does not exist yet - const existView = this.queryRunner.loadedViews.find((view) => { - const database = - metadata.database && - metadata.database !== this.connection.driver.database - ? metadata.database - : undefined; - const schema = - metadata.schema || - (( - this.connection.driver - )).options.schema; - const fullViewName = this.connection.driver.buildTableName( - metadata.tableName, - schema, - database - ); - const viewExpression = - typeof view.expression === "string" - ? view.expression.trim() - : view.expression(this.connection).getQuery(); - const metadataExpression = - typeof metadata.expression === "string" - ? metadata.expression.trim() - : metadata.expression!(this.connection).getQuery(); - return ( - view.name === fullViewName && - viewExpression === metadataExpression - ); - }); - if (existView) return; + for (const metadata of this.viewEntityToSyncMetadatas) { + // check if view does not exist yet + const existView = this.queryRunner.loadedViews.find(view => { + const database = metadata.database && metadata.database !== this.connection.driver.database ? metadata.database : undefined; + const schema = metadata.schema || (this.connection.driver).options.schema; + const fullViewName = this.connection.driver.buildTableName(metadata.tableName, schema, database); + const viewExpression = typeof view.expression === "string" ? view.expression.trim() : view.expression(this.connection).getQuery(); + const metadataExpression = typeof metadata.expression === "string" ? metadata.expression.trim() : metadata.expression!(this.connection).getQuery(); + return view.name === fullViewName && viewExpression === metadataExpression; + }); + if (existView) + continue; this.connection.logger.logSchemaBuild( `creating a new view: ${metadata.tablePath}` ); - // create a new view and sync it in the database - const view = View.create(metadata, this.connection.driver); - await this.queryRunner.createView(view); - this.queryRunner.loadedViews.push(view); - } - ); + // create a new view and sync it in the database + const view = View.create(metadata, this.connection.driver); + await this.queryRunner.createView(view); + this.queryRunner.loadedViews.push(view); + } } protected async dropOldViews(): Promise { - await PromiseUtils.runInSequence( - this.queryRunner.loadedViews, - async (view) => { - const existViewMetadata = this.viewEntityToSyncMetadatas.find( - (metadata) => { - const database = - metadata.database && - metadata.database !== - this.connection.driver.database - ? metadata.database - : undefined; - const schema = - metadata.schema || - (( - this.connection.driver - )).options.schema; - const fullViewName = this.connection.driver.buildTableName( - metadata.tableName, - schema, - database - ); - const viewExpression = - typeof view.expression === "string" - ? view.expression.trim() - : view.expression(this.connection).getQuery(); - const metadataExpression = - typeof metadata.expression === "string" - ? metadata.expression.trim() - : metadata.expression!( - this.connection - ).getQuery(); - return ( - view.name === fullViewName && - viewExpression === metadataExpression - ); - } - ); + for (const view of this.queryRunner.loadedViews) { + const existViewMetadata = this.viewEntityToSyncMetadatas.find(metadata => { + const database = metadata.database && metadata.database !== this.connection.driver.database ? metadata.database : undefined; + const schema = metadata.schema || (this.connection.driver).options.schema; + const fullViewName = this.connection.driver.buildTableName(metadata.tableName, schema, database); + const viewExpression = typeof view.expression === "string" ? view.expression.trim() : view.expression(this.connection).getQuery(); + const metadataExpression = typeof metadata.expression === "string" ? metadata.expression.trim() : metadata.expression!(this.connection).getQuery(); + return view.name === fullViewName && viewExpression === metadataExpression; + }); - if (existViewMetadata) return; + if (existViewMetadata) + continue; this.connection.logger.logSchemaBuild( `dropping an old view: ${view.name}` ); - // drop an old view - await this.queryRunner.dropView(view); - this.queryRunner.loadedViews.splice( - this.queryRunner.loadedViews.indexOf(view), - 1 - ); - } - ); + // drop an old view + await this.queryRunner.dropView(view); + this.queryRunner.loadedViews.splice(this.queryRunner.loadedViews.indexOf(view), 1); + } } /** @@ -647,24 +497,17 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { * We drop their keys too, since it should be safe. */ protected async dropRemovedColumns(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; - - // find columns that exist in the database but does not exist in the metadata - const droppedTableColumns = table.columns.filter( - (tableColumn) => { - return !metadata.columns.find( - (columnMetadata) => - columnMetadata.databaseName === tableColumn.name - ); - } - ); - if (droppedTableColumns.length === 0) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; + + // find columns that exist in the database but does not exist in the metadata + const droppedTableColumns = table.columns.filter(tableColumn => { + return !metadata.columns.find(columnMetadata => columnMetadata.databaseName === tableColumn.name); + }); + if (droppedTableColumns.length === 0) + continue; this.connection.logger.logSchemaBuild( `columns dropped in ${table.name}: ` + @@ -673,10 +516,9 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { .join(", ") ); - // drop columns from the database - await this.queryRunner.dropColumns(table, droppedTableColumns); - } - ); + // drop columns from the database + await this.queryRunner.dropColumns(table, droppedTableColumns); + } } /** @@ -684,24 +526,17 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { * Columns are created without keys. */ protected async addNewColumns(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; - - // find which columns are new - const newColumnMetadatas = metadata.columns.filter( - (columnMetadata) => { - return !table.columns.find( - (tableColumn) => - tableColumn.name === columnMetadata.databaseName - ); - } - ); - if (newColumnMetadatas.length === 0) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; + + // find which columns are new + const newColumnMetadatas = metadata.columns.filter(columnMetadata => { + return !table.columns.find(tableColumn => tableColumn.name === columnMetadata.databaseName); + }); + if (newColumnMetadatas.length === 0) + continue; // create columns in the database const newTableColumnOptions = this.metadataColumnsToTableColumnOptions( @@ -711,30 +546,22 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { (option) => new TableColumn(option) ); - if (newTableColumns.length === 0) return; + if (newTableColumns.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `new columns added: ` + - newColumnMetadatas - .map((column) => column.databaseName) - .join(", ") - ); - await this.queryRunner.addColumns(table, newTableColumns); - } - ); + this.connection.logger.logSchemaBuild(`new columns added: ` + newColumnMetadatas.map(column => column.databaseName).join(", ")); + await this.queryRunner.addColumns(table, newTableColumns); + } } /** * Updates composite primary keys. */ protected async updatePrimaryKeys(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const primaryMetadataColumns = metadata.columns.filter( (column) => column.isPrimary @@ -763,7 +590,7 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ); } } - ); + } } /** @@ -771,107 +598,61 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { * Still don't create keys. Also we don't touch foreign keys of the changed columns. */ protected async updateExistColumns(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; - - const changedColumns = this.connection.driver.findChangedColumns( - table.columns, - metadata.columns - ); - if (changedColumns.length === 0) return; - - // drop all foreign keys that point to this column - await PromiseUtils.runInSequence( - changedColumns, - (changedColumn) => - this.dropColumnReferencedForeignKeys( - metadata.tablePath, - changedColumn.databaseName - ) - ); + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; + + const changedColumns = this.connection.driver.findChangedColumns(table.columns, metadata.columns); + if (changedColumns.length === 0) + continue; + + // drop all foreign keys that point to this column + for (const changedColumn of changedColumns) { + await this.dropColumnReferencedForeignKeys(metadata.tablePath, changedColumn.databaseName); + } - // drop all composite indices related to this column - await PromiseUtils.runInSequence( - changedColumns, - (changedColumn) => - this.dropColumnCompositeIndices( - metadata.tablePath, - changedColumn.databaseName - ) - ); + // drop all composite indices related to this column + for (const changedColumn of changedColumns) { + await this.dropColumnCompositeIndices(metadata.tablePath, changedColumn.databaseName); + } - // drop all composite uniques related to this column - // Mysql does not support unique constraints. - if ( - !( - this.connection.driver instanceof MysqlDriver || - this.connection.driver instanceof AuroraDataApiDriver - ) - ) { - await PromiseUtils.runInSequence( - changedColumns, - (changedColumn) => - this.dropColumnCompositeUniques( - metadata.tablePath, - changedColumn.databaseName - ) - ); + // drop all composite uniques related to this column + // Mysql does not support unique constraints. + if (!(this.connection.driver instanceof MysqlDriver || this.connection.driver instanceof AuroraDataApiDriver)) { + for (const changedColumn of changedColumns) { + await this.dropColumnCompositeUniques(metadata.tablePath, changedColumn.databaseName); } + } - // generate a map of new/old columns - const newAndOldTableColumns = changedColumns.map( - (changedColumn) => { - const oldTableColumn = table.columns.find( - (column) => - column.name === changedColumn.databaseName - )!; - const newTableColumnOptions = TableUtils.createTableColumnOptions( - changedColumn, - this.connection.driver - ); - const newTableColumn = new TableColumn( - newTableColumnOptions - ); + // generate a map of new/old columns + const newAndOldTableColumns = changedColumns.map(changedColumn => { + const oldTableColumn = table.columns.find(column => column.name === changedColumn.databaseName)!; + const newTableColumnOptions = TableUtils.createTableColumnOptions(changedColumn, this.connection.driver); + const newTableColumn = new TableColumn(newTableColumnOptions); - return { - oldColumn: oldTableColumn, - newColumn: newTableColumn, - }; - } - ); + return { + oldColumn: oldTableColumn, + newColumn: newTableColumn + }; + }); - if (newAndOldTableColumns.length === 0) return; + if (newAndOldTableColumns.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `columns changed in "${table.name}". updating: ` + - changedColumns - .map((column) => column.databaseName) - .join(", ") - ); - await this.queryRunner.changeColumns( - table, - newAndOldTableColumns - ); - } - ); + this.connection.logger.logSchemaBuild(`columns changed in "${table.name}". updating: ` + changedColumns.map(column => column.databaseName).join(", ")); + await this.queryRunner.changeColumns(table, newAndOldTableColumns); + } } /** * Creates composite indices which are missing in db yet. */ protected async createNewIndices(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const newIndices = metadata.indices .filter( @@ -883,16 +664,12 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ) .map((indexMetadata) => TableIndex.create(indexMetadata)); - if (newIndices.length === 0) return; + if (newIndices.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `adding new indices ${newIndices - .map((index) => `"${index.name}"`) - .join(", ")} in table "${table.name}"` - ); - await this.queryRunner.createIndices(table, newIndices); - } - ); + this.connection.logger.logSchemaBuild(`adding new indices ${newIndices.map(index => `"${index.name}"`).join(", ")} in table "${table.name}"`); + await this.queryRunner.createIndices(table, newIndices); + } } protected async createNewChecks(): Promise { @@ -903,13 +680,10 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ) return; - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const newChecks = metadata.checks .filter( @@ -921,29 +695,22 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { ) .map((checkMetadata) => TableCheck.create(checkMetadata)); - if (newChecks.length === 0) return; + if (newChecks.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `adding new check constraints: ${newChecks - .map((index) => `"${index.name}"`) - .join(", ")} in table "${table.name}"` - ); - await this.queryRunner.createCheckConstraints(table, newChecks); - } - ); + this.connection.logger.logSchemaBuild(`adding new check constraints: ${newChecks.map(index => `"${index.name}"`).join(", ")} in table "${table.name}"`); + await this.queryRunner.createCheckConstraints(table, newChecks); + } } /** * Creates composite uniques which are missing in db yet. */ protected async createCompositeUniqueConstraints(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const compositeUniques = metadata.uniques .filter( @@ -958,19 +725,12 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { TableUnique.create(uniqueMetadata) ); - if (compositeUniques.length === 0) return; + if (compositeUniques.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `adding new unique constraints: ${compositeUniques - .map((unique) => `"${unique.name}"`) - .join(", ")} in table "${table.name}"` - ); - await this.queryRunner.createUniqueConstraints( - table, - compositeUniques - ); - } - ); + this.connection.logger.logSchemaBuild(`adding new unique constraints: ${compositeUniques.map(unique => `"${unique.name}"`).join(", ")} in table "${table.name}"`); + await this.queryRunner.createUniqueConstraints(table, compositeUniques); + } } /** @@ -980,13 +740,10 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { // Only PostgreSQL supports exclusion constraints if (!(this.connection.driver instanceof PostgresDriver)) return; - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; const newExclusions = metadata.exclusions .filter( @@ -1001,51 +758,33 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { TableExclusion.create(exclusionMetadata) ); - if (newExclusions.length === 0) return; + if (newExclusions.length === 0) + continue; - this.connection.logger.logSchemaBuild( - `adding new exclusion constraints: ${newExclusions - .map((exclusion) => `"${exclusion.name}"`) - .join(", ")} in table "${table.name}"` - ); - await this.queryRunner.createExclusionConstraints( - table, - newExclusions - ); - } - ); + this.connection.logger.logSchemaBuild(`adding new exclusion constraints: ${newExclusions.map(exclusion => `"${exclusion.name}"`).join(", ")} in table "${table.name}"`); + await this.queryRunner.createExclusionConstraints(table, newExclusions); + } } /** * Creates foreign keys which does not exist in the table yet. */ protected async createForeignKeys(): Promise { - await PromiseUtils.runInSequence( - this.entityToSyncMetadatas, - async (metadata) => { - const table = this.queryRunner.loadedTables.find( - (table) => table.name === metadata.tablePath - ); - if (!table) return; + for (const metadata of this.entityToSyncMetadatas) { + const table = this.queryRunner.loadedTables.find(table => table.name === metadata.tablePath); + if (!table) + continue; - const newKeys = metadata.foreignKeys.filter((foreignKey) => { - return !table.foreignKeys.find((dbForeignKey) => - foreignKeysMatch(dbForeignKey, foreignKey) - ); - }); - if (newKeys.length === 0) return; + const newKeys = metadata.foreignKeys.filter(foreignKey => { + return !table.foreignKeys.find(dbForeignKey => foreignKeysMatch(dbForeignKey, foreignKey)); + }); + if (newKeys.length === 0) + continue; - const dbForeignKeys = newKeys.map((foreignKeyMetadata) => - TableForeignKey.create(foreignKeyMetadata) - ); - this.connection.logger.logSchemaBuild( - `creating a foreign keys: ${newKeys - .map((key) => key.name) - .join(", ")} on table "${table.name}"` - ); - await this.queryRunner.createForeignKeys(table, dbForeignKeys); - } - ); + const dbForeignKeys = newKeys.map(foreignKeyMetadata => TableForeignKey.create(foreignKeyMetadata)); + this.connection.logger.logSchemaBuild(`creating a foreign keys: ${newKeys.map(key => key.name).join(", ")} on table "${table.name}"`); + await this.queryRunner.createForeignKeys(table, dbForeignKeys); + } } /** @@ -1093,19 +832,10 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { }); if (tablesWithFK.length > 0) { - await PromiseUtils.runInSequence(tablesWithFK, (tableWithFK) => { - this.connection.logger.logSchemaBuild( - `dropping related foreign keys of ${ - tableWithFK.name - }: ${tableWithFK.foreignKeys - .map((foreignKey) => foreignKey.name) - .join(", ")}` - ); - return this.queryRunner.dropForeignKeys( - tableWithFK, - tableWithFK.foreignKeys - ); - }); + for (const tableWithFK of tablesWithFK) { + this.connection.logger.logSchemaBuild(`dropping related foreign keys of ${tableWithFK.name}: ${tableWithFK.foreignKeys.map(foreignKey => foreignKey.name).join(", ")}`); + await this.queryRunner.dropForeignKeys(tableWithFK, tableWithFK.foreignKeys); + } } } diff --git a/src/schema-builder/options/TableIndexOptions.ts b/src/schema-builder/options/TableIndexOptions.ts index c75228e2fd3..b37be3d790d 100644 --- a/src/schema-builder/options/TableIndexOptions.ts +++ b/src/schema-builder/options/TableIndexOptions.ts @@ -30,10 +30,10 @@ export interface TableIndexOptions { /** * The FULLTEXT modifier indexes the entire column and does not allow prefixing. - * Works only in MySQL. + * Supported only in MySQL & SAP HANA. */ isFulltext?: boolean; - + /** * Fulltext parser. * Works only in MySQL. @@ -45,4 +45,4 @@ export interface TableIndexOptions { */ where?: string; -} \ No newline at end of file +} diff --git a/src/schema-builder/view/View.ts b/src/schema-builder/view/View.ts index 45ac7e501ef..51fa19e4c75 100644 --- a/src/schema-builder/view/View.ts +++ b/src/schema-builder/view/View.ts @@ -1,5 +1,4 @@ -import {Connection, Driver, SelectQueryBuilder} from "../.."; -import {EntityMetadata} from "../.."; +import {Connection, Driver, EntityMetadata, SelectQueryBuilder} from "../.."; import {ViewOptions} from "../options/ViewOptions"; /** diff --git a/src/subscriber/Broadcaster.ts b/src/subscriber/Broadcaster.ts index 0dc3aaf326f..2ac9986063d 100644 --- a/src/subscriber/Broadcaster.ts +++ b/src/subscriber/Broadcaster.ts @@ -181,6 +181,126 @@ export class Broadcaster { } } + /** + * Broadcasts "BEFORE_TRANSACTION_START" event. + */ + broadcastBeforeTransactionStartEvent(result: BroadcasterResult): void { + if (this.queryRunner.connection.subscribers.length) { + this.queryRunner.connection.subscribers.forEach(subscriber => { + if (subscriber.beforeTransactionStart) { + const executionResult = subscriber.beforeTransactionStart({ + connection: this.queryRunner.connection, + queryRunner: this.queryRunner, + manager: this.queryRunner.manager, + }); + if (executionResult instanceof Promise) + result.promises.push(executionResult); + result.count++; + } + }); + } + } + + /** + * Broadcasts "AFTER_TRANSACTION_START" event. + */ + broadcastAfterTransactionStartEvent(result: BroadcasterResult): void { + if (this.queryRunner.connection.subscribers.length) { + this.queryRunner.connection.subscribers.forEach(subscriber => { + if (subscriber.afterTransactionStart) { + const executionResult = subscriber.afterTransactionStart({ + connection: this.queryRunner.connection, + queryRunner: this.queryRunner, + manager: this.queryRunner.manager, + }); + if (executionResult instanceof Promise) + result.promises.push(executionResult); + result.count++; + } + }); + } + } + + /** + * Broadcasts "BEFORE_TRANSACTION_COMMIT" event. + */ + broadcastBeforeTransactionCommitEvent(result: BroadcasterResult): void { + if (this.queryRunner.connection.subscribers.length) { + this.queryRunner.connection.subscribers.forEach(subscriber => { + if (subscriber.beforeTransactionCommit) { + const executionResult = subscriber.beforeTransactionCommit({ + connection: this.queryRunner.connection, + queryRunner: this.queryRunner, + manager: this.queryRunner.manager, + }); + if (executionResult instanceof Promise) + result.promises.push(executionResult); + result.count++; + } + }); + } + } + + /** + * Broadcasts "AFTER_TRANSACTION_COMMIT" event. + */ + broadcastAfterTransactionCommitEvent(result: BroadcasterResult): void { + if (this.queryRunner.connection.subscribers.length) { + this.queryRunner.connection.subscribers.forEach(subscriber => { + if (subscriber.afterTransactionCommit) { + const executionResult = subscriber.afterTransactionCommit({ + connection: this.queryRunner.connection, + queryRunner: this.queryRunner, + manager: this.queryRunner.manager, + }); + if (executionResult instanceof Promise) + result.promises.push(executionResult); + result.count++; + } + }); + } + } + + /** + * Broadcasts "BEFORE_TRANSACTION_ROLLBACK" event. + */ + broadcastBeforeTransactionRollbackEvent(result: BroadcasterResult): void { + if (this.queryRunner.connection.subscribers.length) { + this.queryRunner.connection.subscribers.forEach(subscriber => { + if (subscriber.beforeTransactionRollback) { + const executionResult = subscriber.beforeTransactionRollback({ + connection: this.queryRunner.connection, + queryRunner: this.queryRunner, + manager: this.queryRunner.manager, + }); + if (executionResult instanceof Promise) + result.promises.push(executionResult); + result.count++; + } + }); + } + } + + /** + * Broadcasts "AFTER_TRANSACTION_ROLLBACK" event. + */ + broadcastAfterTransactionRollbackEvent(result: BroadcasterResult): void { + if (this.queryRunner.connection.subscribers.length) { + this.queryRunner.connection.subscribers.forEach(subscriber => { + if (subscriber.afterTransactionRollback) { + const executionResult = subscriber.afterTransactionRollback({ + connection: this.queryRunner.connection, + queryRunner: this.queryRunner, + manager: this.queryRunner.manager, + }); + if (executionResult instanceof Promise) + result.promises.push(executionResult); + result.count++; + } + }); + } + } + /** * Broadcasts "AFTER_UPDATE" event. * After update event is executed after entity is being updated in the database. diff --git a/src/subscriber/EntitySubscriberInterface.ts b/src/subscriber/EntitySubscriberInterface.ts index bc726d52792..3cfb5776640 100644 --- a/src/subscriber/EntitySubscriberInterface.ts +++ b/src/subscriber/EntitySubscriberInterface.ts @@ -1,3 +1,6 @@ +import { TransactionCommitEvent } from "./event/TransactionCommitEvent"; +import { TransactionRollbackEvent } from "./event/TransactionRollbackEvent"; +import { TransactionStartEvent } from "./event/TransactionStartEvent"; import {UpdateEvent} from "./event/UpdateEvent"; import {RemoveEvent} from "./event/RemoveEvent"; import {InsertEvent} from "./event/InsertEvent"; @@ -54,4 +57,34 @@ export interface EntitySubscriberInterface { */ afterRemove?(event: RemoveEvent): Promise|void; + /** + * Called before transaction is started. + */ + beforeTransactionStart?(event: TransactionStartEvent): Promise|void; + + /** + * Called after transaction is started. + */ + afterTransactionStart?(event: TransactionStartEvent): Promise|void; + + /** + * Called before transaction is committed. + */ + beforeTransactionCommit?(event: TransactionCommitEvent): Promise|void; + + /** + * Called after transaction is committed. + */ + afterTransactionCommit?(event: TransactionCommitEvent): Promise|void; + + /** + * Called before transaction rollback. + */ + beforeTransactionRollback?(event: TransactionRollbackEvent): Promise|void; + + /** + * Called after transaction rollback. + */ + afterTransactionRollback?(event: TransactionRollbackEvent): Promise|void; + } diff --git a/src/subscriber/event/TransactionCommitEvent.ts b/src/subscriber/event/TransactionCommitEvent.ts new file mode 100644 index 00000000000..2a4e82c2cf0 --- /dev/null +++ b/src/subscriber/event/TransactionCommitEvent.ts @@ -0,0 +1,27 @@ +import {EntityManager} from "../../entity-manager/EntityManager"; +import {Connection} from "../../connection/Connection"; +import {QueryRunner} from "../../query-runner/QueryRunner"; + +/** + * TransactionCommitEvent is an object that broadcaster sends to the entity subscriber when an transaction is committed. + */ +export interface TransactionCommitEvent { + + /** + * Connection used in the event. + */ + connection: Connection; + + /** + * QueryRunner used in the event transaction. + * All database operations in the subscribed event listener should be performed using this query runner instance. + */ + queryRunner: QueryRunner; + + /** + * EntityManager used in the event transaction. + * All database operations in the subscribed event listener should be performed using this entity manager instance. + */ + manager: EntityManager; + +} diff --git a/src/subscriber/event/TransactionRollbackEvent.ts b/src/subscriber/event/TransactionRollbackEvent.ts new file mode 100644 index 00000000000..ae7fb5b1831 --- /dev/null +++ b/src/subscriber/event/TransactionRollbackEvent.ts @@ -0,0 +1,27 @@ +import {EntityManager} from "../../entity-manager/EntityManager"; +import {Connection} from "../../connection/Connection"; +import {QueryRunner} from "../../query-runner/QueryRunner"; + +/** + * TransactionRollbackEvent is an object that broadcaster sends to the entity subscriber on transaction rollback. + */ +export interface TransactionRollbackEvent { + + /** + * Connection used in the event. + */ + connection: Connection; + + /** + * QueryRunner used in the event transaction. + * All database operations in the subscribed event listener should be performed using this query runner instance. + */ + queryRunner: QueryRunner; + + /** + * EntityManager used in the event transaction. + * All database operations in the subscribed event listener should be performed using this entity manager instance. + */ + manager: EntityManager; + +} diff --git a/src/subscriber/event/TransactionStartEvent.ts b/src/subscriber/event/TransactionStartEvent.ts new file mode 100644 index 00000000000..b6409160ffe --- /dev/null +++ b/src/subscriber/event/TransactionStartEvent.ts @@ -0,0 +1,27 @@ +import {EntityManager} from "../../entity-manager/EntityManager"; +import {Connection} from "../../connection/Connection"; +import {QueryRunner} from "../../query-runner/QueryRunner"; + +/** + * TransactionStartEvent is an object that broadcaster sends to the entity subscriber before transaction is started. + */ +export interface TransactionStartEvent { + + /** + * Connection used in the event. + */ + connection: Connection; + + /** + * QueryRunner used in the event transaction. + * All database operations in the subscribed event listener should be performed using this query runner instance. + */ + queryRunner: QueryRunner; + + /** + * EntityManager used in the event transaction. + * All database operations in the subscribed event listener should be performed using this entity manager instance. + */ + manager: EntityManager; + +} diff --git a/src/util/ApplyValueTransformers.ts b/src/util/ApplyValueTransformers.ts index 15b2bcf81a3..682ce01578f 100644 --- a/src/util/ApplyValueTransformers.ts +++ b/src/util/ApplyValueTransformers.ts @@ -1,4 +1,4 @@ -import { ValueTransformer } from "../decorator/options/ValueTransformer"; +import {ValueTransformer} from "../decorator/options/ValueTransformer"; export class ApplyValueTransformers { static transformFrom(transformer: ValueTransformer | ValueTransformer[], databaseValue: any) { @@ -18,4 +18,4 @@ export class ApplyValueTransformers { } return transformer.to(entityValue); } -} \ No newline at end of file +} diff --git a/src/util/DateUtils.ts b/src/util/DateUtils.ts index bd6ec2a24c3..8f3b99a02d5 100644 --- a/src/util/DateUtils.ts +++ b/src/util/DateUtils.ts @@ -175,12 +175,7 @@ export class DateUtils { } static stringToSimpleJson(value: any) { - try { - const simpleJSON = JSON.parse(value); - return (typeof simpleJSON === "object") ? simpleJSON : {}; - } catch (err) { - return {}; - } + return typeof value === "string" ? JSON.parse(value) : value; } static simpleEnumToString(value: any) { diff --git a/src/util/DirectoryExportedClassesLoader.ts b/src/util/DirectoryExportedClassesLoader.ts index 9aea39c5711..2932b603be2 100644 --- a/src/util/DirectoryExportedClassesLoader.ts +++ b/src/util/DirectoryExportedClassesLoader.ts @@ -1,3 +1,4 @@ +import glob from "glob"; import {PlatformTools} from "../platform/PlatformTools"; import {EntitySchema} from "../index"; import {Logger} from "../logger/Logger"; @@ -24,7 +25,7 @@ export function importClassesFromDirectories(logger: Logger, directories: string } const allFiles = directories.reduce((allDirs, dir) => { - return allDirs.concat(PlatformTools.load("glob").sync(PlatformTools.pathNormalize(dir))); + return allDirs.concat(glob.sync(PlatformTools.pathNormalize(dir))); }, [] as string[]); if (directories.length > 0 && allFiles.length === 0) { @@ -37,7 +38,7 @@ export function importClassesFromDirectories(logger: Logger, directories: string const dtsExtension = file.substring(file.length - 5, file.length); return formats.indexOf(PlatformTools.pathExtname(file)) !== -1 && dtsExtension !== ".d.ts"; }) - .map(file => PlatformTools.load(PlatformTools.pathResolve(file))); + .map(file => require(PlatformTools.pathResolve(file))); return loadFileClasses(dirs, []); } @@ -48,10 +49,10 @@ export function importClassesFromDirectories(logger: Logger, directories: string export function importJsonsFromDirectories(directories: string[], format = ".json"): any[] { const allFiles = directories.reduce((allDirs, dir) => { - return allDirs.concat(PlatformTools.load("glob").sync(PlatformTools.pathNormalize(dir))); + return allDirs.concat(glob.sync(PlatformTools.pathNormalize(dir))); }, [] as string[]); return allFiles .filter(file => PlatformTools.pathExtname(file) === format) - .map(file => PlatformTools.load(PlatformTools.pathResolve(file))); -} \ No newline at end of file + .map(file => require(PlatformTools.pathResolve(file))); +} diff --git a/src/util/OrmUtils.ts b/src/util/OrmUtils.ts index 73d1c458ce3..f63efa9942f 100644 --- a/src/util/OrmUtils.ts +++ b/src/util/OrmUtils.ts @@ -1,5 +1,4 @@ -import { ObjectLiteral } from "../common/ObjectLiteral"; -import { URL } from "url"; +import {ObjectLiteral} from "../common/ObjectLiteral"; export class OrmUtils { diff --git a/src/util/PromiseUtils.ts b/src/util/PromiseUtils.ts deleted file mode 100644 index 326d6c44032..00000000000 --- a/src/util/PromiseUtils.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Utils to help to work with Promise objects. - */ -export class PromiseUtils { - - /** - * Creates a new promise with resolved value used for lazy relations. - */ - static create(value: any) { - const promise = Promise.resolve(value); - (promise as any)["__value__"] = value; - return promise; - } - - /** - * If given value is a promise created by "create" method this method gets its value. - * If given value is not a promise then given value is returned back. - */ - static extractValue(object: any) { - if (object instanceof Promise && (object as any)["__value__"]) - return (object as any)["__value__"]; - - return object; - } - - /** - * Runs given callback that returns promise for each item in the given collection in order. - * Operations executed after each other, right after previous promise being resolved. - */ - static runInSequence(collection: T[], callback: (item: T) => Promise): Promise { - const results: U[] = []; - return collection.reduce((promise, item) => { - return promise.then(() => { - return callback(item); - }).then(result => { - results.push(result); - }); - }, Promise.resolve()).then(() => { - return results; - }); - } - - /** - * Returns a promise that is fulfilled with an array of promise state snapshots, - * but only after all the original promises have settled, i.e. become either fulfilled or rejected. - */ - static settle(promises: Promise[]) { - return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({ - state: "fulfilled", - value: v, - }), r => ({ - state: "rejected", - reason: r, - })))).then((results: any[]): any => { - const rejected = results.find(result => result.state === "rejected"); - if (rejected) - return Promise.reject(rejected.reason); - - return results.map(result => result.value); - }); - } - -} \ No newline at end of file diff --git a/test/functional/cache/provider/MockQueryResultCache.ts b/test/functional/cache/provider/MockQueryResultCache.ts index 1d2993911f8..de1335b0891 100644 --- a/test/functional/cache/provider/MockQueryResultCache.ts +++ b/test/functional/cache/provider/MockQueryResultCache.ts @@ -229,7 +229,7 @@ export class MockQueryResultCache implements QueryResultCache { if (queryRunner) return queryRunner; - return this.connection.createQueryRunner("master"); + return this.connection.createQueryRunner(); } } diff --git a/test/functional/columns/comments/columns-comments.ts b/test/functional/columns/comments/columns-comments.ts new file mode 100644 index 00000000000..2ad8a8ea7c2 --- /dev/null +++ b/test/functional/columns/comments/columns-comments.ts @@ -0,0 +1,34 @@ +import {expect} from "chai"; +import "reflect-metadata"; +import {Connection} from "../../../../src"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; +import {Test} from "./entity/Test"; + +describe("columns > comments", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [Test], + // Only supported on postgres, cockroachdb, and mysql + enabledDrivers: ["postgres", "cockroachdb", "mysql"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should persist comments of different types to the database", () => Promise.all(connections.map(async connection => { + const table = (await connection.createQueryRunner().getTable("test"))!; + + expect(table.findColumnByName("a")!.comment).to.be.equal("Hello World") + expect(table.findColumnByName("b")!.comment).to.be.equal("Hello\nWorld") + expect(table.findColumnByName("c")!.comment).to.be.equal("Hello World! It's going to be a beautiful day.") + expect(table.findColumnByName("d")!.comment).to.be.equal("Hello World! #@!$`") + expect(table.findColumnByName("e")!.comment).to.be.equal("Hello World. \r\n\t\b\f\v") + expect(table.findColumnByName("f")!.comment).to.be.equal("Hello World.\\") + expect(table.findColumnByName("g")!.comment).to.be.equal(" ") + expect(table.findColumnByName("h")!.comment).to.be.equal(undefined); + expect(table.findColumnByName("i")!.comment).to.be.equal(undefined); + + }))); + + +}); diff --git a/test/functional/columns/comments/entity/Test.ts b/test/functional/columns/comments/entity/Test.ts new file mode 100644 index 00000000000..1fdaea37cb6 --- /dev/null +++ b/test/functional/columns/comments/entity/Test.ts @@ -0,0 +1,46 @@ +import {Entity} from "../../../../../src/decorator/entity/Entity"; +import {Column} from "../../../../../src/decorator/columns/Column"; +import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn"; + +@Entity() +export class Test { + + @PrimaryGeneratedColumn() + id: number; + + // Standard Comment + @Column({ comment: "Hello World"}) + a: string; + + // Comment with a newline + @Column({ comment: "Hello\nWorld"}) + b: string; + + // Comment with a single quote + @Column({ comment: "Hello World! It's going to be a beautiful day."}) + c: string; + + // Comment with special characters + @Column({ comment: "Hello World! #@!$`"}) + d: string; + + // Comment with control characters + @Column({ comment: "Hello World. \r\n\t\b\f\v\0"}) + e: string; + + // Comment that ends with a backslash + @Column({ comment: "Hello World.\\"}) + f: string; + + // Comment that is only whitespace + @Column({ comment: " "}) + g: string; + + // Comment that is empty + @Column({ comment: ""}) + h: string; + + // No comment. + @Column() + i: string; +} diff --git a/test/functional/connection-options-reader/configs/.env b/test/functional/connection-options-reader/configs/.env index 1930f2b109c..2d7a79b5a0c 100644 --- a/test/functional/connection-options-reader/configs/.env +++ b/test/functional/connection-options-reader/configs/.env @@ -1,2 +1,2 @@ TYPEORM_CONNECTION = mysql -TYPEORM_DATABASE = test-js +TYPEORM_DATABASE = test-env \ No newline at end of file diff --git a/test/functional/connection-options-reader/configs/ormconfig.env b/test/functional/connection-options-reader/configs/ormconfig.env new file mode 100644 index 00000000000..2bf6d39e066 --- /dev/null +++ b/test/functional/connection-options-reader/configs/ormconfig.env @@ -0,0 +1,2 @@ +TYPEORM_CONNECTION = mysql +TYPEORM_DATABASE = test-ormconfig-env \ No newline at end of file diff --git a/test/functional/connection-options-reader/configs/sqlite-memory.ts b/test/functional/connection-options-reader/configs/sqlite-memory.ts index 243dd45cef5..81907c198e6 100644 --- a/test/functional/connection-options-reader/configs/sqlite-memory.ts +++ b/test/functional/connection-options-reader/configs/sqlite-memory.ts @@ -6,4 +6,8 @@ module.exports = [{ type: "sqlite", name: "memory", database: ":memory:", +}, { + type: "better-sqlite3", + name: "memory2", + database: ":memory:", }]; \ No newline at end of file diff --git a/test/functional/connection-options-reader/configs/test-path-config-esm.ts b/test/functional/connection-options-reader/configs/test-path-config-esm.ts new file mode 100644 index 00000000000..e51c1aa7826 --- /dev/null +++ b/test/functional/connection-options-reader/configs/test-path-config-esm.ts @@ -0,0 +1,5 @@ +export default [{ + type: "sqlite", + name: "file", + database: "test-js-esm" +}]; diff --git a/test/functional/connection-options-reader/connection-options-reader.ts b/test/functional/connection-options-reader/connection-options-reader.ts index fae1bf881d4..23302a65cac 100644 --- a/test/functional/connection-options-reader/connection-options-reader.ts +++ b/test/functional/connection-options-reader/connection-options-reader.ts @@ -1,8 +1,21 @@ +import {promises as fs} from "fs"; import {expect} from "chai"; import {ConnectionOptions} from "../../../src/connection/ConnectionOptions"; import {ConnectionOptionsReader} from "../../../src/connection/ConnectionOptionsReader"; +import path from "path"; + +async function createDotenvFiles() { + // These files may not always exist + await fs.writeFile(path.join(__dirname, "configs/.env"), "TYPEORM_CONNECTION = mysql\nTYPEORM_DATABASE = test-env"); + await fs.writeFile(path.join(__dirname, "configs/ormconfig.env"), "TYPEORM_CONNECTION = mysql\nTYPEORM_DATABASE = test-ormconfig-env"); +} describe("ConnectionOptionsReader", () => { + beforeEach(() => { + delete process.env['TYPEORM_CONNECTION']; + delete process.env['TYPEORM_DATABASE']; + }); + after(() => { delete process.env.TYPEORM_CONNECTION; delete process.env.TYPEORM_DATABASE; @@ -26,22 +39,47 @@ describe("ConnectionOptionsReader", () => { }); it("properly loads config with specified file path", async () => { - const connectionOptionsReader = new ConnectionOptionsReader({ root: __dirname, configName: "configs/test-path-config.js" }); + const connectionOptionsReader = new ConnectionOptionsReader({ root: __dirname, configName: "configs/test-path-config" }); const fileOptions: ConnectionOptions = await connectionOptionsReader.get("file"); expect(fileOptions.database).to.have.string("/test-js"); }); it("properly loads asynchronous config with specified file path", async () => { - const connectionOptionsReader = new ConnectionOptionsReader({ root: __dirname, configName: "configs/test-path-config-async.js" }); + const connectionOptionsReader = new ConnectionOptionsReader({ root: __dirname, configName: "configs/test-path-config-async" }); const fileOptions: ConnectionOptions = await connectionOptionsReader.get("file"); expect(fileOptions.database).to.have.string("/test-js-async"); }); - // TODO This test requires the configs/.env file be moved to the matching directory in build/compiled - it.skip("properly loads config from .env file", async () => { - const connectionOptionsReader = new ConnectionOptionsReader({ root: __dirname, configName: "configs/.env" }); + it("properly loads config with specified file path from esm in js", async () => { + const connectionOptionsReader = new ConnectionOptionsReader({ root: __dirname, configName: "configs/test-path-config-esm" }); + const fileOptions: ConnectionOptions = await connectionOptionsReader.get("file"); + expect(fileOptions.database).to.have.string("/test-js-esm"); + }); + + it("properly loads config from .env file", async () => { + await createDotenvFiles(); + + const connectionOptionsReader = new ConnectionOptionsReader({ root: __dirname, configName: "configs/.env" }); + const [ fileOptions ]: ConnectionOptions[] = await connectionOptionsReader.all(); + expect(fileOptions.database).to.have.string("test-env"); + expect(process.env.TYPEORM_DATABASE).to.equal("test-env"); + }); + + it("properly loads config from ormconfig.env file", async () => { + await createDotenvFiles(); + + const connectionOptionsReader = new ConnectionOptionsReader({ root: __dirname, configName: "configs/ormconfig.env" }); + const [ fileOptions ]: ConnectionOptions[] = await connectionOptionsReader.all(); + expect(fileOptions.database).to.have.string("test-ormconfig-env"); + expect(process.env.TYPEORM_DATABASE).to.equal("test-ormconfig-env"); + }); + + it("properly loads config ormconfig.env when given multiple choices", async () => { + await createDotenvFiles(); + + const connectionOptionsReader = new ConnectionOptionsReader({ root: path.join(__dirname, "configs") }); const [ fileOptions ]: ConnectionOptions[] = await connectionOptionsReader.all(); - expect(fileOptions.database).to.have.string("test-js"); - expect(process.env.TYPEORM_DATABASE).to.equal("test-js"); + expect(fileOptions.database).to.have.string("test-ormconfig-env"); + expect(process.env.TYPEORM_DATABASE).to.equal("test-ormconfig-env"); }); }); diff --git a/test/functional/connection/connection.ts b/test/functional/connection/connection.ts index 3f03b099aaf..d7b7d74fdc4 100644 --- a/test/functional/connection/connection.ts +++ b/test/functional/connection/connection.ts @@ -17,7 +17,6 @@ import {EntityManager} from "../../../src/entity-manager/EntityManager"; import {CannotGetEntityManagerNotConnectedError} from "../../../src/error/CannotGetEntityManagerNotConnectedError"; import {ConnectionOptions} from "../../../src/connection/ConnectionOptions"; import {PostgresConnectionOptions} from "../../../src/driver/postgres/PostgresConnectionOptions"; -import {PromiseUtils} from "../../../src/util/PromiseUtils"; describe("Connection", () => { // const resourceDir = __dirname + "/../../../../../test/functional/connection/"; @@ -274,7 +273,7 @@ describe("Connection", () => { after(() => closeTestingConnections(connections)); it("should not interfere with each other", async () => { - await PromiseUtils.runInSequence(connections, c => c.synchronize()); + await Promise.all(connections.map(c => c.synchronize())); await closeTestingConnections(connections); const connections1 = await createTestingConnections({ name: "test", diff --git a/test/functional/connection/modules/blog/subscriber/TestBlogSubscriber.ts b/test/functional/connection/modules/blog/subscriber/TestBlogSubscriber.ts index f6a40b238c6..db6553da7bc 100644 --- a/test/functional/connection/modules/blog/subscriber/TestBlogSubscriber.ts +++ b/test/functional/connection/modules/blog/subscriber/TestBlogSubscriber.ts @@ -4,12 +4,12 @@ import {InsertEvent} from "../../../../../../src/subscriber/event/InsertEvent"; @EventSubscriber() export class TestBlogSubscriber implements EntitySubscriberInterface { - + /** * Called after entity insertion. */ beforeInsert(event: InsertEvent) { - console.log(`BEFORE ENTITY INSERTED: `, event.entity); + // Do nothing } -} \ No newline at end of file +} diff --git a/test/functional/connection/modules/question/subscriber/TestQuestionSubscriber.ts b/test/functional/connection/modules/question/subscriber/TestQuestionSubscriber.ts index d22940cbd4c..d9682c3b8b0 100644 --- a/test/functional/connection/modules/question/subscriber/TestQuestionSubscriber.ts +++ b/test/functional/connection/modules/question/subscriber/TestQuestionSubscriber.ts @@ -4,12 +4,12 @@ import {InsertEvent} from "../../../../../../src/subscriber/event/InsertEvent"; @EventSubscriber() export class TestQuestionSubscriber implements EntitySubscriberInterface { - + /** * Called before entity insertion. */ beforeInsert(event: InsertEvent) { - console.log(`BEFORE ENTITY INSERTED: `, event.entity); + // Do nothing } -} \ No newline at end of file +} diff --git a/test/functional/connection/modules/video/subscriber/TestVideoSubscriber.ts b/test/functional/connection/modules/video/subscriber/TestVideoSubscriber.ts index 58f66b5eb3b..e745ae03af3 100644 --- a/test/functional/connection/modules/video/subscriber/TestVideoSubscriber.ts +++ b/test/functional/connection/modules/video/subscriber/TestVideoSubscriber.ts @@ -4,12 +4,12 @@ import {InsertEvent} from "../../../../../../src/subscriber/event/InsertEvent"; @EventSubscriber() export class TestVideoSubscriber implements EntitySubscriberInterface { - + /** * Called after entity insertion. */ beforeInsert(event: InsertEvent) { - console.log(`BEFORE ENTITY INSERTED: `, event.entity); + // Do nothing } -} \ No newline at end of file +} diff --git a/test/functional/connection/subscriber/FirstConnectionSubscriber.ts b/test/functional/connection/subscriber/FirstConnectionSubscriber.ts index 2aefd827413..d47c31d3917 100644 --- a/test/functional/connection/subscriber/FirstConnectionSubscriber.ts +++ b/test/functional/connection/subscriber/FirstConnectionSubscriber.ts @@ -4,12 +4,12 @@ import {InsertEvent} from "../../../../src/subscriber/event/InsertEvent"; @EventSubscriber() export class FirstConnectionSubscriber implements EntitySubscriberInterface { - + /** * Called after entity insertion. */ beforeInsert(event: InsertEvent) { - console.log(`BEFORE ENTITY INSERTED: `, event.entity); + // Do nothing } -} \ No newline at end of file +} diff --git a/test/functional/connection/subscriber/SecondConnectionSubscriber.ts b/test/functional/connection/subscriber/SecondConnectionSubscriber.ts index 2ef4427b3a5..8046e640ff2 100644 --- a/test/functional/connection/subscriber/SecondConnectionSubscriber.ts +++ b/test/functional/connection/subscriber/SecondConnectionSubscriber.ts @@ -4,12 +4,12 @@ import {InsertEvent} from "../../../../src/subscriber/event/InsertEvent"; @EventSubscriber() export class SecondConnectionSubscriber implements EntitySubscriberInterface { - + /** * Called after entity insertion. */ beforeInsert(event: InsertEvent) { - console.log(`BEFORE ENTITY INSERTED: `, event.entity); + // Do nothing } -} \ No newline at end of file +} diff --git a/test/functional/database-schema/column-collation/sqlite/column-collation-sqlite.ts b/test/functional/database-schema/column-collation/sqlite/column-collation-sqlite.ts index cfd415258ec..869aa9785ed 100644 --- a/test/functional/database-schema/column-collation/sqlite/column-collation-sqlite.ts +++ b/test/functional/database-schema/column-collation/sqlite/column-collation-sqlite.ts @@ -10,7 +10,7 @@ describe.skip("database schema > column collation > sqlite", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/database-schema/column-length/sqlite/column-length-sqlite..ts b/test/functional/database-schema/column-length/sqlite/column-length-sqlite..ts index deffdbd3aa3..64a9d7f8fe5 100644 --- a/test/functional/database-schema/column-length/sqlite/column-length-sqlite..ts +++ b/test/functional/database-schema/column-length/sqlite/column-length-sqlite..ts @@ -10,7 +10,7 @@ describe("database schema > column length > sqlite", () => { before(async () => { connections = await createTestingConnections({ entities: [Post], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/database-schema/column-types/sqlite/column-types-sqlite.ts b/test/functional/database-schema/column-types/sqlite/column-types-sqlite.ts index b76a3e5bcc6..64a2213f22e 100644 --- a/test/functional/database-schema/column-types/sqlite/column-types-sqlite.ts +++ b/test/functional/database-schema/column-types/sqlite/column-types-sqlite.ts @@ -11,7 +11,7 @@ describe("database schema > column types > sqlite", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/database-schema/simple-enums/enums.ts b/test/functional/database-schema/simple-enums/enums.ts index 61f84893428..dc355710420 100644 --- a/test/functional/database-schema/simple-enums/enums.ts +++ b/test/functional/database-schema/simple-enums/enums.ts @@ -9,7 +9,7 @@ describe("database schema > simple-enums", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "postgres", "sqlite", "mssql"] + enabledDrivers: ["mysql", "mariadb", "postgres", "sqlite", "better-sqlite3", "mssql"] }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/entity-metadata-validator/basic/entity/Post.ts b/test/functional/entity-metadata-validator/basic/entity/Post.ts index 0d0de660c39..8edece963e6 100644 --- a/test/functional/entity-metadata-validator/basic/entity/Post.ts +++ b/test/functional/entity-metadata-validator/basic/entity/Post.ts @@ -14,7 +14,7 @@ export class Post { @Column() title: string; - + @OneToOne(type => Category) category: Category; @@ -27,4 +27,4 @@ export class Post { @RelationCount((post: Post) => post.category2) categoryCount2: number; -} \ No newline at end of file +} diff --git a/test/functional/entity-model/entity-model.ts b/test/functional/entity-model/entity-model.ts index 5be6481c122..baf250b4732 100644 --- a/test/functional/entity-model/entity-model.ts +++ b/test/functional/entity-model/entity-model.ts @@ -3,7 +3,6 @@ import {Post} from "./entity/Post"; import {Category} from "./entity/Category"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; import {Connection} from "../../../src/connection/Connection"; -import {PromiseUtils} from "../../../src/util/PromiseUtils"; describe("entity-model", () => { @@ -14,60 +13,65 @@ describe("entity-model", () => { beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("should save successfully and use static methods successfully", () => PromiseUtils.runInSequence(connections, async connection => { - Post.useConnection(connection); // change connection each time because of AR specifics - - const post = Post.create(); - post.title = "About ActiveRecord"; - post.text = "Huge discussion how good or bad ActiveRecord is."; - await post.save(); - - const loadedPost = await Post.findOne(post.id); - - loadedPost!.should.be.instanceOf(Post); - loadedPost!.id.should.be.eql(post.id); - loadedPost!.title.should.be.eql("About ActiveRecord"); - loadedPost!.text.should.be.eql("Huge discussion how good or bad ActiveRecord is."); - })); - - it("should reload given entity successfully", () => PromiseUtils.runInSequence(connections, async connection => { - await connection.synchronize(true); - Post.useConnection(connection); - Category.useConnection(connection); - - const category = Category.create(); - category.id = 1; - category.name = "Persistence"; - await category.save(); - - const post = Post.create(); - post.title = "About ActiveRecord"; - post.categories = [category]; - await post.save(); - - await post.reload(); - - const assertCategory = Object.assign({}, post.categories[0]); - post!.should.be.instanceOf(Post); - post!.id.should.be.eql(post.id); - post!.title.should.be.eql("About ActiveRecord"); - post!.text.should.be.eql("This is default text."); - assertCategory.should.be.eql({ - id: 1, - name: "Persistence" - }); - - category.name = "Persistence and Entity"; - await category.save(); - - await post.reload(); - - const assertReloadedCategory = Object.assign({}, post.categories[0]); - assertReloadedCategory.should.be.eql({ - id: 1, - name: "Persistence and Entity" - }); - - })); + it("should save successfully and use static methods successfully", async () => { + // These must run sequentially as we have the global context of the `Post` ActiveRecord class + for (const connection of connections) { + Post.useConnection(connection); // change connection each time because of AR specifics + + const post = Post.create(); + post.title = "About ActiveRecord"; + post.text = "Huge discussion how good or bad ActiveRecord is."; + await post.save(); + + const loadedPost = await Post.findOne(post.id); + + loadedPost!.should.be.instanceOf(Post); + loadedPost!.id.should.be.eql(post.id); + loadedPost!.title.should.be.eql("About ActiveRecord"); + loadedPost!.text.should.be.eql("Huge discussion how good or bad ActiveRecord is."); + } + }); + + it("should reload given entity successfully", async () => { + // These must run sequentially as we have the global context of the `Post` ActiveRecord class + for (const connection of connections) { + await connection.synchronize(true); + Post.useConnection(connection); + Category.useConnection(connection); + + const category = Category.create(); + category.id = 1; + category.name = "Persistence"; + await category.save(); + + const post = Post.create(); + post.title = "About ActiveRecord"; + post.categories = [category]; + await post.save(); + + await post.reload(); + + const assertCategory = Object.assign({}, post.categories[0]); + post!.should.be.instanceOf(Post); + post!.id.should.be.eql(post.id); + post!.title.should.be.eql("About ActiveRecord"); + post!.text.should.be.eql("This is default text."); + assertCategory.should.be.eql({ + id: 1, + name: "Persistence" + }); + + category.name = "Persistence and Entity"; + await category.save(); + + await post.reload(); + + const assertReloadedCategory = Object.assign({}, post.categories[0]); + assertReloadedCategory.should.be.eql({ + id: 1, + name: "Persistence and Entity" + }); + } + }); }); diff --git a/test/functional/entity-subscriber-transaction-flow/entity-subscriber-transaction-flow.ts b/test/functional/entity-subscriber-transaction-flow/entity-subscriber-transaction-flow.ts new file mode 100644 index 00000000000..d9b404a6654 --- /dev/null +++ b/test/functional/entity-subscriber-transaction-flow/entity-subscriber-transaction-flow.ts @@ -0,0 +1,213 @@ +import { + Connection, + EntitySubscriberInterface, + EventSubscriber, +} from "../../../src"; +import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils"; +import sinon from "sinon"; +import {expect} from "chai"; +import {SapDriver} from "../../../src/driver/sap/SapDriver"; +import {OracleDriver} from "../../../src/driver/oracle/OracleDriver"; +import {AuroraDataApiPostgresDriver} from "../../../src/driver/aurora-data-api-pg/AuroraDataApiPostgresDriver"; +import {AuroraDataApiDriver} from "../../../src/driver/aurora-data-api/AuroraDataApiDriver"; +import {SqlServerDriver} from "../../../src/driver/sqlserver/SqlServerDriver"; + +describe("entity subscriber transaction flow", () => { + + let beforeTransactionStart = sinon.spy(); + let afterTransactionStart = sinon.spy(); + let beforeTransactionCommit = sinon.spy(); + let afterTransactionCommit = sinon.spy(); + let beforeTransactionRollback = sinon.spy(); + let afterTransactionRollback = sinon.spy(); + + @EventSubscriber() + class PostSubscriber implements EntitySubscriberInterface { + + beforeTransactionStart() { + if (beforeTransactionStart) + beforeTransactionStart(); + } + + afterTransactionStart() { + if (afterTransactionStart) + afterTransactionStart(); + } + + beforeTransactionCommit() { + if (beforeTransactionCommit) + beforeTransactionCommit(); + } + + afterTransactionCommit() { + if (afterTransactionCommit) + afterTransactionCommit(); + } + + beforeTransactionRollback() { + if (beforeTransactionRollback) + beforeTransactionRollback(); + } + + afterTransactionRollback() { + if (afterTransactionRollback) + afterTransactionRollback(); + } + } + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + subscribers: [PostSubscriber], + dropSchema: true, + schemaCreate: true, + })); + after(() => closeTestingConnections(connections)); + + it("transactionStart", async () => { + for (let connection of connections) { + if (connection.driver instanceof SqlServerDriver) return; + + beforeTransactionStart.resetHistory(); + afterTransactionStart.resetHistory(); + + let isolationLevel: any = undefined; + if (connection.driver instanceof SapDriver || connection.driver instanceof OracleDriver) { + isolationLevel = "READ COMMITTED"; + } + + const queryRunner = await connection.createQueryRunner(); + + if (connection.driver instanceof AuroraDataApiPostgresDriver || connection.driver instanceof AuroraDataApiDriver) { + const startTransactionFn = sinon.spy(queryRunner.startTransaction); + await queryRunner.startTransaction(); + + expect(beforeTransactionStart.calledBefore(startTransactionFn)).to.be.true; + expect(afterTransactionStart.calledAfter(startTransactionFn)).to.be.true; + + startTransactionFn.restore(); + await queryRunner.commitTransaction(); + + } else { + const startTransactionFn = sinon.spy(queryRunner, "query"); + + const queryCallBeforeTransactionStart = startTransactionFn.getCalls().find(call => { + return call.args[0] === "BEGIN TRANSACTION" + || call.args[0] === "START TRANSACTION" + || call.args[0] === "SET TRANSACTION ISOLATION LEVEL READ COMMITTED"; + }) + expect(queryCallBeforeTransactionStart).to.be.undefined; + + await queryRunner.startTransaction(isolationLevel); + + const queryCallAfterTransactionStart = startTransactionFn.getCalls().find(call => { + return call.args[0] === "BEGIN TRANSACTION" + || call.args[0] === "START TRANSACTION" + || call.args[0] === "SET TRANSACTION ISOLATION LEVEL READ COMMITTED"; + }); + expect(beforeTransactionStart.called).to.be.true; + expect(afterTransactionStart.called).to.be.true; + expect(queryCallAfterTransactionStart).to.be.not.undefined; + expect(beforeTransactionStart.getCall(0).calledBefore(queryCallAfterTransactionStart!)).to.be.true; + expect(afterTransactionStart.getCall(0).calledAfter(queryCallAfterTransactionStart!)).to.be.true; + + await queryRunner.commitTransaction(); + startTransactionFn.restore(); + } + + await queryRunner.release(); + } + }); + + it("transactionCommit", async () => { + + for (let connection of connections) { + if (connection.driver instanceof SqlServerDriver) return; + + beforeTransactionCommit.resetHistory(); + afterTransactionCommit.resetHistory(); + + const queryRunner = await connection.createQueryRunner(); + await queryRunner.startTransaction(); + + if (connection.driver instanceof AuroraDataApiPostgresDriver || connection.driver instanceof AuroraDataApiDriver) { + const commitTransactionFn = sinon.spy(queryRunner.commitTransaction); + await queryRunner.commitTransaction(); + + expect(beforeTransactionCommit.calledBefore(commitTransactionFn)).to.be.true; + expect(afterTransactionCommit.calledAfter(commitTransactionFn)).to.be.true; + + commitTransactionFn.restore(); + + } else { + const commitTransactionFn = sinon.spy(queryRunner, "query"); + + const queryCallBeforeTransactionCommit = commitTransactionFn.getCalls().find(call => { + return call.args[0] === "COMMIT"; + }); + expect(queryCallBeforeTransactionCommit).to.be.undefined; + + await queryRunner.commitTransaction(); + + const queryCallAfterTransactionCommit = commitTransactionFn.getCalls().find(call => { + return call.args[0] === "COMMIT"; + }); + expect(queryCallAfterTransactionCommit).to.be.not.undefined; + expect(beforeTransactionCommit.called).to.be.true; + expect(afterTransactionCommit.called).to.be.true; + expect(beforeTransactionCommit.getCall(0).calledBefore(queryCallAfterTransactionCommit!)).to.be.true; + expect(afterTransactionCommit.getCall(0).calledAfter(queryCallAfterTransactionCommit!)).to.be.true; + + commitTransactionFn.restore(); + } + + await queryRunner.release(); + } + }); + + it("transactionRollback", async () => { + + for (let connection of connections) { + if (connection.driver instanceof SqlServerDriver) return; + + beforeTransactionRollback.resetHistory(); + afterTransactionRollback.resetHistory(); + + const queryRunner = await connection.createQueryRunner(); + await queryRunner.startTransaction(); + + if (connection.driver instanceof AuroraDataApiPostgresDriver || connection.driver instanceof AuroraDataApiDriver) { + const rollbackTransactionFn = sinon.spy(queryRunner.rollbackTransaction); + await queryRunner.rollbackTransaction(); + + expect(beforeTransactionRollback.calledBefore(rollbackTransactionFn)).to.be.true; + expect(afterTransactionRollback.calledAfter(rollbackTransactionFn)).to.be.true; + + rollbackTransactionFn.restore(); + + } else { + const rollbackTransactionFn = sinon.spy(queryRunner, "query"); + + const queryCallBeforeTransactionRollback = rollbackTransactionFn.getCalls().find(call => { + return call.args[0] === "ROLLBACK"; + }); + expect(queryCallBeforeTransactionRollback).to.be.undefined; + + await queryRunner.rollbackTransaction(); + + const queryCallAfterTransactionRollback = rollbackTransactionFn.getCalls().find(call => { + return call.args[0] === "ROLLBACK"; + }); + expect(queryCallAfterTransactionRollback).to.be.not.undefined; + expect(beforeTransactionRollback.called).to.be.true; + expect(afterTransactionRollback.called).to.be.true; + expect(beforeTransactionRollback.getCall(0).calledBefore(queryCallAfterTransactionRollback!)).to.be.true; + expect(afterTransactionRollback.getCall(0).calledAfter(queryCallAfterTransactionRollback!)).to.be.true; + + rollbackTransactionFn.restore(); + } + + await queryRunner.release(); + } + }); + +}); diff --git a/test/functional/indices/conditional-index/conditional-index.ts b/test/functional/indices/conditional-index/conditional-index.ts index 67f6648c8c4..f195fe9b79f 100644 --- a/test/functional/indices/conditional-index/conditional-index.ts +++ b/test/functional/indices/conditional-index/conditional-index.ts @@ -9,7 +9,7 @@ describe("indices > conditional index", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mssql", "postgres", "sqlite"], // only these drivers supports conditional indices + enabledDrivers: ["mssql", "postgres", "sqlite", "better-sqlite3"], // only these drivers supports conditional indices schemaCreate: true, dropSchema: true, }); @@ -37,7 +37,7 @@ describe("indices > conditional index", () => { expect(table!.indices[0].where).to.be.not.empty; expect(table!.indices[1].where).to.be.not.empty; - await queryRunner.dropIndices(table!, table!.indices); + await queryRunner.dropIndices(table!, [...table!.indices]); table = await queryRunner.getTable("post"); table!.indices.length.should.be.equal(0); diff --git a/test/functional/ltree/postgres/entity/Post.ts b/test/functional/ltree/postgres/entity/Post.ts new file mode 100644 index 00000000000..8abbf39803f --- /dev/null +++ b/test/functional/ltree/postgres/entity/Post.ts @@ -0,0 +1,14 @@ +import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import {Entity} from "../../../../../src/decorator/entity/Entity"; +import {Column} from "../../../../../src/decorator/columns/Column"; + +@Entity() +export class Post { + @PrimaryGeneratedColumn() + id: number; + + @Column("ltree", { + nullable: false + }) + path: string; +} diff --git a/test/functional/ltree/postgres/ltree-postgres.ts b/test/functional/ltree/postgres/ltree-postgres.ts new file mode 100644 index 00000000000..7e19718d84a --- /dev/null +++ b/test/functional/ltree/postgres/ltree-postgres.ts @@ -0,0 +1,136 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import { Connection } from "../../../../src/connection/Connection"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases +} from "../../../utils/test-utils"; +import { Post } from "./entity/Post"; + +describe("ltree-postgres", () => { + let connections: Connection[]; + before(async () => { + connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["postgres"] + }); + }); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should create correct schema with Postgres' ltree type", () => + Promise.all( + connections.map(async connection => { + const queryRunner = connection.createQueryRunner(); + const schema = await queryRunner.getTable("post"); + await queryRunner.release(); + expect(schema).not.to.be.undefined; + const ltreeColumn = schema!.columns.find( + tableColumn => + tableColumn.name === "path" && + tableColumn.type === "ltree" && + !tableColumn.isArray + ); + expect(ltreeColumn).to.not.be.undefined; + }) + )); + + it("should persist ltree correctly", () => + Promise.all( + connections.map(async connection => { + const path = 'News.Featured.Opinion'; + const postRepo = connection.getRepository(Post); + const post = new Post(); + post.path = path; + const persistedPost = await postRepo.save(post); + const foundPost = await postRepo.findOne(persistedPost.id); + expect(foundPost).to.exist; + expect(foundPost!.path).to.deep.equal(path); + }) + )); + + it("should update ltree correctly", () => + Promise.all( + connections.map(async connection => { + const path = 'News.Featured.Opinion'; + const path2 = 'News.Featured.Gossip'; + const postRepo = connection.getRepository(Post); + const post = new Post(); + post.path = path; + const persistedPost = await postRepo.save(post); + + await postRepo.update( + { id: persistedPost.id }, + { path: path2 } + ); + + const foundPost = await postRepo.findOne(persistedPost.id); + expect(foundPost).to.exist; + expect(foundPost!.path).to.deep.equal(path2); + }) + )); + + it("should re-save ltree correctly", () => + Promise.all( + connections.map(async connection => { + const path = 'News.Featured.Opinion'; + const path2 = 'News.Featured.Gossip'; + const postRepo = connection.getRepository(Post); + const post = new Post(); + post.path = path; + const persistedPost = await postRepo.save(post); + + persistedPost.path = path2; + await postRepo.save(persistedPost); + + const foundPost = await postRepo.findOne(persistedPost.id); + expect(foundPost).to.exist; + expect(foundPost!.path).to.deep.equal(path2); + }) + )); + + it("should persist ltree correctly with trailing '.'", () => + Promise.all( + connections.map(async connection => { + const path = 'News.Featured.Opinion.'; + const postRepo = connection.getRepository(Post); + const post = new Post(); + post.path = path; + const persistedPost = await postRepo.save(post); + const foundPost = await postRepo.findOne(persistedPost.id); + expect(foundPost).to.exist; + expect(foundPost!.path).to.deep.equal('News.Featured.Opinion'); + }) + )); + + it("should persist ltree correctly when containing spaces", () => + Promise.all( + connections.map(async connection => { + const path = 'News.Featured Story.Opinion'; + const postRepo = connection.getRepository(Post); + const post = new Post(); + post.path = path; + const persistedPost = await postRepo.save(post); + const foundPost = await postRepo.findOne(persistedPost.id); + expect(foundPost).to.exist; + expect(foundPost!.path).to.deep.equal('News.Featured_Story.Opinion'); + }) + )); + + it("should be able to query ltree correctly", () => + Promise.all( + connections.map(async connection => { + const path = 'News.Featured.Opinion'; + const postRepo = connection.getRepository(Post); + const post = new Post(); + post.path = path; + await postRepo.save(post); + const foundPost = await postRepo.createQueryBuilder().where(`path ~ 'news@.*'`).getOne() + expect(foundPost).to.exist; + expect(foundPost!.path).to.deep.equal(path); + }) + )); +}); + + diff --git a/test/functional/migrations/generate-command/command.ts b/test/functional/migrations/generate-command/command.ts index 1c136334286..465ee27871b 100644 --- a/test/functional/migrations/generate-command/command.ts +++ b/test/functional/migrations/generate-command/command.ts @@ -7,11 +7,10 @@ describe("migrations > generate command", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ migrations: [], - enabledDrivers: ["postgres"], + enabledDrivers: ["postgres", "cockroachdb", "mysql"], schemaCreate: false, dropSchema: true, entities: [Post, Category], - logging: true, schema: "public", })); // beforeEach(() => reloadTestingDatabases(connections)); @@ -28,7 +27,6 @@ describe("migrations > generate command", () => { const sqlInMemory = await connection.driver.createSchemaBuilder().log(); - console.log(sqlInMemory.upQueries); sqlInMemory.upQueries.length.should.be.equal(0); sqlInMemory.downQueries.length.should.be.equal(0); diff --git a/test/functional/persistence/custom-column-names/persistence-custom-column-names.ts b/test/functional/persistence/custom-column-names/persistence-custom-column-names.ts index bc597c3e354..34805630878 100644 --- a/test/functional/persistence/custom-column-names/persistence-custom-column-names.ts +++ b/test/functional/persistence/custom-column-names/persistence-custom-column-names.ts @@ -34,7 +34,6 @@ describe("persistence > custom-column-names", function() { return connection .synchronize(true) .catch(e => { - console.log("Error during schema re-creation: ", e); throw e; }); } @@ -53,7 +52,7 @@ describe("persistence > custom-column-names", function() { // ------------------------------------------------------------------------- // Specifications // ------------------------------------------------------------------------- - + describe("attach exist entity to exist entity with many-to-one relation", function() { if (!connection) return; diff --git a/test/functional/persistence/delete-orphans/delete-orphans.ts b/test/functional/persistence/delete-orphans/delete-orphans.ts new file mode 100644 index 00000000000..e000d884a78 --- /dev/null +++ b/test/functional/persistence/delete-orphans/delete-orphans.ts @@ -0,0 +1,73 @@ +import "reflect-metadata"; +import { Connection, Repository } from "../../../../src/index"; +import { reloadTestingDatabases, createTestingConnections, closeTestingConnections } from "../../../utils/test-utils"; +import { expect } from "chai"; +import { Category } from "./entity/Category"; +import { Post } from "./entity/Post"; + +describe("persistence > delete orphans", () => { + + // ------------------------------------------------------------------------- + // Configuration + // ------------------------------------------------------------------------- + + // connect to db + let connections: Connection[] = []; + + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + // ------------------------------------------------------------------------- + // Specifications + // ------------------------------------------------------------------------- + + describe("when a Post is removed from a Category", () => { + let categoryRepository: Repository; + let postRepository: Repository; + let categoryId: number; + + beforeEach(async () => { + await Promise.all(connections.map(async connection => { + categoryRepository = connection.getRepository(Category); + postRepository = connection.getRepository(Post); + })); + + const categoryToInsert = await categoryRepository.save(new Category()); + categoryToInsert.posts = [ + new Post(), + new Post() + ]; + + await categoryRepository.save(categoryToInsert); + categoryId = categoryToInsert.id; + + const categoryToUpdate = (await categoryRepository.findOne(categoryId))!; + categoryToUpdate.posts = categoryToInsert.posts.filter(p => p.id === 1); // Keep the first post + + await categoryRepository.save(categoryToUpdate); + }); + + it("should retain a Post on the Category", async () => { + const category = await categoryRepository.findOne(categoryId); + expect(category).not.to.be.undefined; + expect(category!.posts).to.have.lengthOf(1); + expect(category!.posts[0].id).to.equal(1); + }); + + it("should delete the orphaned Post from the database", async () => { + const postCount = await postRepository.count(); + expect(postCount).to.equal(1); + }); + + it("should retain foreign keys on remaining Posts", async () => { + const postsWithoutForeignKeys = (await postRepository.find()) + .filter(p => !p.categoryId); + expect(postsWithoutForeignKeys).to.have.lengthOf(0); + }); + }); + +}); diff --git a/test/functional/persistence/delete-orphans/entity/Category.ts b/test/functional/persistence/delete-orphans/entity/Category.ts new file mode 100644 index 00000000000..477195b90aa --- /dev/null +++ b/test/functional/persistence/delete-orphans/entity/Category.ts @@ -0,0 +1,18 @@ +import {Entity} from "../../../../../src/decorator/entity/Entity"; +import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import {Post} from "./Post"; +import {OneToMany} from "../../../../../src/decorator/relations/OneToMany"; + +@Entity() +export class Category { + + @PrimaryGeneratedColumn() + id: number; + + @OneToMany(() => Post, post => post.category, { + cascade: ["insert"], + eager: true + }) + posts: Post[]; + +} diff --git a/test/functional/persistence/delete-orphans/entity/Post.ts b/test/functional/persistence/delete-orphans/entity/Post.ts new file mode 100644 index 00000000000..6587c8f06c7 --- /dev/null +++ b/test/functional/persistence/delete-orphans/entity/Post.ts @@ -0,0 +1,21 @@ +import {Category} from "./Category"; +import {Entity} from "../../../../../src/decorator/entity/Entity"; +import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import {Column} from "../../../../../src/decorator/columns/Column"; +import {ManyToOne} from "../../../../../src/decorator/relations/ManyToOne"; +import {JoinColumn} from "../../../../../src/decorator/relations/JoinColumn"; + +@Entity() +export class Post { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + categoryId: string; + + @ManyToOne(() => Category, category => category.posts, { orphanedRowAction: "delete" }) + @JoinColumn({ name: "categoryId" }) + category: Category; + +} diff --git a/test/functional/query-builder/comment/entity/Test.ts b/test/functional/query-builder/comment/entity/Test.ts new file mode 100644 index 00000000000..6312daa316c --- /dev/null +++ b/test/functional/query-builder/comment/entity/Test.ts @@ -0,0 +1,10 @@ +import {Entity} from "../../../../../src/decorator/entity/Entity"; +import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn"; + +@Entity() +export class Test { + + @PrimaryGeneratedColumn() + id: number; + +} diff --git a/test/functional/query-builder/comment/query-builder-comment.ts b/test/functional/query-builder/comment/query-builder-comment.ts new file mode 100644 index 00000000000..a84441075a3 --- /dev/null +++ b/test/functional/query-builder/comment/query-builder-comment.ts @@ -0,0 +1,85 @@ +import "reflect-metadata"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; +import {Connection} from "../../../../src/connection/Connection"; +import {Test} from "./entity/Test"; +import {expect} from "chai"; + +describe("query builder > comment", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [Test], + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should scrub end comment pattern from string", () => Promise.all(connections.map(async connection => { + const sql = connection.manager.createQueryBuilder(Test, "test") + .comment("Hello World */") + .getSql(); + + expect(sql).to.match(/^\/\* Hello World \*\/ /); + }))); + + it("should not allow an empty comment", () => Promise.all(connections.map(async connection => { + const sql = connection.manager.createQueryBuilder(Test, "test") + .comment("") + .getSql(); + + expect(sql).to.not.match(/^\/\* Hello World \*\/ /); + }))); + + it("should allow a comment with just whitespaces", () => Promise.all(connections.map(async connection => { + const sql = connection.manager.createQueryBuilder(Test, "test") + .comment(" ") + .getSql(); + + expect(sql).to.match(/^\/\* \*\/ /); + }))); + + it("should allow a multi-line comment", () => Promise.all(connections.map(async connection => { + const sql = connection.manager.createQueryBuilder(Test, "test") + .comment("Hello World\nIt's a beautiful day!") + .getSql(); + + expect(sql).to.match(/^\/\* Hello World\nIt's a beautiful day! \*\/ /); + }))); + + it("should include comment in select", () => Promise.all(connections.map(async connection => { + const sql = connection.manager.createQueryBuilder(Test, "test") + .comment("Hello World") + .getSql(); + + expect(sql).to.match(/^\/\* Hello World \*\/ /); + }))); + + it("should include comment in update", () => Promise.all(connections.map(async connection => { + const sql = connection.manager.createQueryBuilder(Test, "test") + .update() + .set({ id: 2 }) + .comment("Hello World") + .getSql(); + + expect(sql).to.match(/^\/\* Hello World \*\/ /); + }))); + + it("should include comment in insert", () => Promise.all(connections.map(async connection => { + const sql = connection.manager.createQueryBuilder(Test, "test") + .insert() + .values({ id: 1 }) + .comment("Hello World") + .getSql(); + + expect(sql).to.match(/^\/\* Hello World \*\/ /); + }))); + + it("should include comment in delete", () => Promise.all(connections.map(async connection => { + const sql = connection.manager.createQueryBuilder(Test, "test") + .delete() + .comment("Hello World") + .getSql(); + + expect(sql).to.match(/^\/\* Hello World \*\/ /); + }))); + +}); diff --git a/test/functional/query-builder/count/entity/AmbigiousPrimaryKey.ts b/test/functional/query-builder/count/entity/AmbigiousPrimaryKey.ts new file mode 100644 index 00000000000..3492003321f --- /dev/null +++ b/test/functional/query-builder/count/entity/AmbigiousPrimaryKey.ts @@ -0,0 +1,19 @@ +import {Entity, PrimaryColumn} from "../../../../../src"; + +@Entity("ambig_primary_key") +export class AmbigiousPrimaryKey { + @PrimaryColumn() + a: string; + + @PrimaryColumn() + b: string; + + static make({ a, b }: { a: string, b: string }): AmbigiousPrimaryKey { + const apk = new AmbigiousPrimaryKey(); + apk.a = a; + apk.b = b; + + return apk; + } + +} diff --git a/test/functional/query-builder/count/entity/Test.ts b/test/functional/query-builder/count/entity/Test.ts new file mode 100644 index 00000000000..360fce0ac07 --- /dev/null +++ b/test/functional/query-builder/count/entity/Test.ts @@ -0,0 +1,13 @@ +import {Entity, PrimaryColumn} from "../../../../../src"; + +@Entity("tests") +export class Test { + @PrimaryColumn() + varcharField: string; + + @PrimaryColumn("uuid") + uuidField: string; + + @PrimaryColumn() + intField: number; +} diff --git a/test/functional/query-builder/count/query-builder-count.ts b/test/functional/query-builder/count/query-builder-count.ts new file mode 100644 index 00000000000..59db9594e94 --- /dev/null +++ b/test/functional/query-builder/count/query-builder-count.ts @@ -0,0 +1,89 @@ +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; +import {Connection} from "../../../../src/connection/Connection"; +import {expect} from "chai"; +import {Test} from "./entity/Test"; +import {AmbigiousPrimaryKey} from "./entity/AmbigiousPrimaryKey"; + +describe("query builder > count", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [Test, AmbigiousPrimaryKey], + schemaCreate: true, + dropSchema: true, + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("Count query should of empty table should be 0", () => Promise.all(connections.map(async connection => { + const repo = connection.getRepository(Test); + + const count = await repo.count(); + expect(count).to.be.equal(0); + }))); + + it("Count query should count database values", () => Promise.all(connections.map(async connection => { + const repo = connection.getRepository(Test); + + await Promise.all([ + repo.save({ varcharField: 'ok', uuidField: '123e4567-e89b-12d3-a456-426614174000', intField: 4}), + repo.save({ varcharField: 'ok', uuidField: '123e4567-e89b-12d3-a456-426614174001', intField: 4}), + ]); + + const count = await repo.count(); + expect(count).to.be.equal(2); + }))); + + it("Count query should handle ambiguous values", () => Promise.all(connections.map(async connection => { + const repo = connection.getRepository(AmbigiousPrimaryKey); + + await Promise.all([ + repo.save({ a: 'A', b: 'AAA' }), + repo.save({ a: 'AAA', b: 'A' }), + repo.save({ a: 'AA', b: 'AA' }), + repo.save({ a: 'BB', b: 'BB' }), + repo.save({ a: 'B', b: 'BBB' }), + repo.save({ a: 'BBB', b: 'B' }) + ]); + + const count = await repo.count(); + expect(count).to.be.equal(6, connection.name); + }))); + + it("counting joined query should count database values", () => Promise.all(connections.map(async connection => { + const repo = connection.getRepository(Test); + + await Promise.all([ + repo.save({ varcharField: 'ok', uuidField: '123e4567-e89b-12d3-a456-426614174000', intField: 4}), + repo.save({ varcharField: 'ok', uuidField: '123e4567-e89b-12d3-a456-426614174001', intField: 4}), + ]); + + const count = await repo.createQueryBuilder() + .from(Test, 'main') + .leftJoin(Test, 'self', 'self.intField = main.intField') + .getCount(); + + expect(count).to.be.equal(2); + }))); + + it("counting joined queries should handle ambiguous values", () => Promise.all(connections.map(async connection => { + const repo = connection.getRepository(AmbigiousPrimaryKey); + + await Promise.all([ + repo.save({ a: 'A', b: 'AAA' }), + repo.save({ a: 'AAA', b: 'A' }), + repo.save({ a: 'AA', b: 'AA' }), + repo.save({ a: 'BB', b: 'BB' }), + repo.save({ a: 'B', b: 'BBB' }), + repo.save({ a: 'BBB', b: 'B' }) + ]); + + const count = await repo.createQueryBuilder() + .from(AmbigiousPrimaryKey, 'main') + .leftJoin(AmbigiousPrimaryKey, 'self', 'self.a = main.a') + .getCount(); + + expect(count).to.be.equal(6, connection.name); + }))); + +}); diff --git a/test/functional/query-builder/delete/query-builder-delete.ts b/test/functional/query-builder/delete/query-builder-delete.ts index 05d091dc46f..4c7be038831 100644 --- a/test/functional/query-builder/delete/query-builder-delete.ts +++ b/test/functional/query-builder/delete/query-builder-delete.ts @@ -96,6 +96,7 @@ describe("query builder > delete", () => { it("should return correct delete result", () => Promise.all(connections.map(async connection => { // don't run test for sqlite and sqljs as they don't return affected rows + // better-sqlite3 works correctly if (connection.name === "sqlite" || connection.name === "sqljs" || connection.name === "sap") return; diff --git a/test/functional/query-builder/insert-on-conflict/query-builder-insert-on-conflict.ts b/test/functional/query-builder/insert-on-conflict/query-builder-insert-on-conflict.ts index 03ee8e50bd4..67221d0f301 100644 --- a/test/functional/query-builder/insert-on-conflict/query-builder-insert-on-conflict.ts +++ b/test/functional/query-builder/insert-on-conflict/query-builder-insert-on-conflict.ts @@ -8,7 +8,7 @@ describe("query builder > insertion > on conflict", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["postgres", "sqlite"] // since on conflict statement is only supported in postgres and sqlite >= 3.24.0 + enabledDrivers: ["postgres", "sqlite", "better-sqlite3"] // since on conflict statement is only supported in postgres and sqlite >= 3.24.0 })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/query-builder/join/query-builder-joins.ts b/test/functional/query-builder/join/query-builder-joins.ts index 78bd1f1544a..ccc6638e8a1 100644 --- a/test/functional/query-builder/join/query-builder-joins.ts +++ b/test/functional/query-builder/join/query-builder-joins.ts @@ -10,7 +10,7 @@ import {Image} from "./entity/Image"; import {User} from "./entity/User"; describe("query builder > joins", () => { - + let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], diff --git a/test/functional/query-builder/locking/query-builder-locking.ts b/test/functional/query-builder/locking/query-builder-locking.ts index adcf7420879..bfa5d1583b9 100644 --- a/test/functional/query-builder/locking/query-builder-locking.ts +++ b/test/functional/query-builder/locking/query-builder-locking.ts @@ -18,6 +18,7 @@ import {SqlServerDriver} from "../../../../src/driver/sqlserver/SqlServerDriver" import {AbstractSqliteDriver} from "../../../../src/driver/sqlite-abstract/AbstractSqliteDriver"; import {OracleDriver} from "../../../../src/driver/oracle/OracleDriver"; import {LockNotSupportedOnGivenDriverError} from "../../../../src/error/LockNotSupportedOnGivenDriverError"; +import { VersionUtils } from "../../../../src/util/VersionUtils"; describe("query builder > locking", () => { @@ -99,6 +100,108 @@ describe("query builder > locking", () => { return; }))); + it("should throw error if pessimistic_partial_write lock used without transaction", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof PostgresDriver) { + return connection.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_partial_write") + .where("post.id = :id", { id: 1 }) + .getOne().should.be.rejectedWith(PessimisticLockTransactionRequiredError); + } + + if (connection.driver instanceof MysqlDriver) { + let [{ version }] = await connection.query( + "SELECT VERSION() as version;" + ); + version = version.toLowerCase(); + if (version.includes('maria')) return; // not supported in mariadb + if (VersionUtils.isGreaterOrEqual(version, '8.0.0')) { + return connection.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_partial_write") + .where("post.id = :id", { id: 1 }) + .getOne().should.be.rejectedWith(PessimisticLockTransactionRequiredError); + } + } + return; + }))); + + it("should not throw error if pessimistic_partial_write lock used with transaction", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof PostgresDriver) { + return connection.manager.transaction(entityManager => { + return Promise.all([entityManager.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_partial_write") + .where("post.id = :id", { id: 1}) + .getOne().should.not.be.rejected]); + }); + } + + if (connection.driver instanceof MysqlDriver) { + let [{ version }] = await connection.query( + "SELECT VERSION() as version;" + ); + version = version.toLowerCase(); + if (version.includes('maria')) return; // not supported in mariadb + if (VersionUtils.isGreaterOrEqual(version, '8.0.0')) { + return connection.manager.transaction(entityManager => { + return Promise.all([entityManager.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_partial_write") + .where("post.id = :id", { id: 1}) + .getOne().should.not.be.rejected]); + }); + } + } + return; + }))); + + it("should throw error if pessimistic_write_or_fail lock used without transaction", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof PostgresDriver) { + return connection.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_write_or_fail") + .where("post.id = :id", { id: 1 }) + .getOne().should.be.rejectedWith(PessimisticLockTransactionRequiredError); + } + + if (connection.driver instanceof MysqlDriver) { + let [{ version }] = await connection.query( + "SELECT VERSION() as version;" + ); + version = version.toLowerCase(); + if ((version.includes('maria') && VersionUtils.isGreaterOrEqual(version, "10.3.0")) || !version.includes('maria') && VersionUtils.isGreaterOrEqual(version, '8.0.0')) { + return connection.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_write_or_fail") + .where("post.id = :id", { id: 1 }) + .getOne().should.be.rejectedWith(PessimisticLockTransactionRequiredError); + } + } + return; + }))); + + it("should not throw error if pessimistic_write_or_fail lock used with transaction", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof PostgresDriver) { + return connection.manager.transaction(entityManager => { + return Promise.all([entityManager.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_write_or_fail") + .where("post.id = :id", { id: 1}) + .getOne().should.not.be.rejected]); + }); + } + + if (connection.driver instanceof MysqlDriver) { + let [{ version }] = await connection.query( + "SELECT VERSION() as version;" + ); + version = version.toLowerCase(); + if ((version.includes('maria') && VersionUtils.isGreaterOrEqual(version, "10.3.0")) || !version.includes('maria') && VersionUtils.isGreaterOrEqual(version, '8.0.0')) { + return connection.manager.transaction(entityManager => { + return Promise.all([entityManager.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_write_or_fail") + .where("post.id = :id", { id: 1}) + .getOne().should.not.be.rejected]); + }); + } + } + return; + }))); + it("should attach pessimistic read lock statement on query if locking enabled", () => Promise.all(connections.map(async connection => { if (connection.driver instanceof AbstractSqliteDriver || connection.driver instanceof CockroachDriver || connection.driver instanceof SapDriver) return; @@ -187,6 +290,54 @@ describe("query builder > locking", () => { }))); + it("should not attach pessimistic_partial_write lock statement on query if locking is not used", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof PostgresDriver || connection.driver instanceof MysqlDriver) { + const sql = connection.createQueryBuilder(PostWithVersion, "post") + .where("post.id = :id", { id: 1 }) + .getSql(); + + expect(sql.indexOf("FOR UPDATE SKIP LOCKED") === -1).to.be.true; + } + return; + }))); + + it("should attach pessimistic_partial_write lock statement on query if locking enabled", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof PostgresDriver || connection.driver instanceof MysqlDriver) { + const sql = connection.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_partial_write") + .where("post.id = :id", { id: 1 }) + .getSql(); + + expect(sql.indexOf("FOR UPDATE SKIP LOCKED") !== -1).to.be.true; + } + return; + + }))); + + it("should not attach pessimistic_write_or_fail lock statement on query if locking is not used", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof PostgresDriver || connection.driver instanceof MysqlDriver) { + const sql = connection.createQueryBuilder(PostWithVersion, "post") + .where("post.id = :id", { id: 1 }) + .getSql(); + + expect(sql.indexOf("FOR UPDATE NOWAIT") === -1).to.be.true; + } + return; + }))); + + it("should attach pessimistic_write_or_fail lock statement on query if locking enabled", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof PostgresDriver || connection.driver instanceof MysqlDriver) { + const sql = connection.createQueryBuilder(PostWithVersion, "post") + .setLock("pessimistic_write_or_fail") + .where("post.id = :id", { id: 1 }) + .getSql(); + + expect(sql.indexOf("FOR UPDATE NOWAIT") !== -1).to.be.true; + } + return; + + }))); + it("should throw error if optimistic lock used with getMany method", () => Promise.all(connections.map(async connection => { return connection.createQueryBuilder(PostWithVersion, "post") diff --git a/test/functional/query-builder/select/query-builder-select.ts b/test/functional/query-builder/select/query-builder-select.ts index 6b1ba78e893..c2883263a92 100644 --- a/test/functional/query-builder/select/query-builder-select.ts +++ b/test/functional/query-builder/select/query-builder-select.ts @@ -3,6 +3,7 @@ import {closeTestingConnections, createTestingConnections, reloadTestingDatabase import {Connection} from "../../../../src/connection/Connection"; import {Post} from "./entity/Post"; import {expect} from "chai"; +import {EntityNotFoundError} from "../../../../src/error/EntityNotFoundError"; describe("query builder > select", () => { @@ -110,4 +111,46 @@ describe("query builder > select", () => { expect(sql).to.equal("SELECT post.name FROM post post"); }))); + it("should return a single entity for getOne when found", () => Promise.all(connections.map(async connection => { + await connection.getRepository(Post).save({ id: 1, title: "Hello", description: 'World', rating: 0 }); + + const entity = await connection.createQueryBuilder(Post, "post") + .where("post.id = :id", { id: 1 }) + .getOne(); + + expect(entity).not.to.be.undefined; + expect(entity!.id).to.equal(1); + expect(entity!.title).to.equal("Hello"); + }))); + + it("should return undefined for getOne when not found", () => Promise.all(connections.map(async connection => { + await connection.getRepository(Post).save({ id: 1, title: "Hello", description: 'World', rating: 0 }); + + const entity = await connection.createQueryBuilder(Post, "post") + .where("post.id = :id", { id: 2 }) + .getOne(); + + expect(entity).to.be.undefined; + }))); + + it("should return a single entity for getOneOrFail when found", () => Promise.all(connections.map(async connection => { + await connection.getRepository(Post).save({ id: 1, title: "Hello", description: 'World', rating: 0 }); + + const entity = await connection.createQueryBuilder(Post, "post") + .where("post.id = :id", { id: 1 }) + .getOneOrFail(); + + expect(entity.id).to.equal(1); + expect(entity.title).to.equal("Hello"); + }))); + + it("should throw an Error for getOneOrFail when not found", () => Promise.all(connections.map(async connection => { + await connection.getRepository(Post).save({ id: 1, title: "Hello", description: 'World', rating: 0 }); + + await expect( + connection.createQueryBuilder(Post, "post") + .where("post.id = :id", { id: 2 }) + .getOneOrFail() + ).to.be.rejectedWith(EntityNotFoundError); + }))); }); diff --git a/test/functional/query-runner/create-table.ts b/test/functional/query-runner/create-table.ts index f940faf7ea5..3aa973fceac 100644 --- a/test/functional/query-runner/create-table.ts +++ b/test/functional/query-runner/create-table.ts @@ -12,7 +12,6 @@ import {AbstractSqliteDriver} from "../../../src/driver/sqlite-abstract/Abstract import {OracleDriver} from "../../../src/driver/oracle/OracleDriver"; import {Photo} from "./entity/Photo"; import {Book2, Book} from "./entity/Book"; -import {SqliteDriver} from "../../../src/driver/sqlite/SqliteDriver"; describe("query runner > create table", () => { @@ -332,7 +331,7 @@ describe("query runner > create table", () => { it("should correctly create table with different `withoutRowid` definitions", () => Promise.all(connections.map(async connection => { - if (connection.driver instanceof SqliteDriver) { + if (connection.driver instanceof AbstractSqliteDriver) { const queryRunner = connection.createQueryRunner(); // the table 'book' must contain a 'rowid' column @@ -358,7 +357,7 @@ describe("query runner > create table", () => { try { await connection.manager.query("SELECT rowid FROM book2"); } catch (e) { - expect(e.message).equal("SQLITE_ERROR: no such column: rowid"); + expect(e.message).contains("no such column: rowid"); } await queryRunner.dropTable("book2"); diff --git a/test/functional/query-runner/create-unique-constraint.ts b/test/functional/query-runner/create-unique-constraint.ts index ba39b2aa98e..b3022e0a367 100644 --- a/test/functional/query-runner/create-unique-constraint.ts +++ b/test/functional/query-runner/create-unique-constraint.ts @@ -10,7 +10,7 @@ describe("query runner > create unique constraint", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mssql", "postgres", "sqlite", "oracle", "cockroachdb"], // mysql and sap does not supports unique constraints + enabledDrivers: ["mssql", "postgres", "sqlite", "better-sqlite3", "oracle", "cockroachdb"], // mysql and sap does not supports unique constraints schemaCreate: true, dropSchema: true, }); diff --git a/test/functional/query-runner/drop-column.ts b/test/functional/query-runner/drop-column.ts index 5e03628eee8..2c422c2e9d6 100644 --- a/test/functional/query-runner/drop-column.ts +++ b/test/functional/query-runner/drop-column.ts @@ -28,6 +28,11 @@ describe("query runner > drop column", () => { nameColumn!.should.be.exist; versionColumn!.should.be.exist; + // better-sqlite3 seems not able to create a check constraint on a non-existing column + if (connection.name === "better-sqlite3") { + await queryRunner.dropCheckConstraints(table!, table!.checks); + } + // In Sqlite 'dropColumns' method is more optimal than 'dropColumn', because it recreate table just once, // without all removed columns. In other drivers it's no difference between these methods, because 'dropColumns' // calls 'dropColumn' method for each removed column. diff --git a/test/functional/query-runner/drop-unique-constraint.ts b/test/functional/query-runner/drop-unique-constraint.ts index fd00a7952d2..d2b5d5bcb02 100644 --- a/test/functional/query-runner/drop-unique-constraint.ts +++ b/test/functional/query-runner/drop-unique-constraint.ts @@ -8,7 +8,7 @@ describe("query runner > drop unique constraint", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mssql", "postgres", "sqlite", "oracle", "cockroachdb"], // mysql and sap does not supports unique constraints + enabledDrivers: ["mssql", "postgres", "sqlite", "better-sqlite3", "oracle", "cockroachdb"], // mysql and sap does not supports unique constraints schemaCreate: true, dropSchema: true, }); diff --git a/test/functional/relations/lazy-relations/basic-lazy-relation/basic-lazy-relations.ts b/test/functional/relations/lazy-relations/basic-lazy-relation/basic-lazy-relations.ts index 3c0d8fe7d1a..7c83e84e398 100644 --- a/test/functional/relations/lazy-relations/basic-lazy-relation/basic-lazy-relations.ts +++ b/test/functional/relations/lazy-relations/basic-lazy-relation/basic-lazy-relations.ts @@ -326,7 +326,7 @@ describe("basic-lazy-relations", () => { loadedPost.title.should.be.equal("post with great category"); }))); - it("should successfully load relations within a transaction", () => Promise.all(connections.filter((connection) => (new Set(["mysql", "sqlite", "postgres"])).has(connection.options.type)).map(async connection => { + it("should successfully load relations within a transaction", () => Promise.all(connections.filter((connection) => (new Set(["mysql", "sqlite", "better-sqlite3", "postgres"])).has(connection.options.type)).map(async connection => { await connection.manager.transaction(async (manager) => { const category = new Category(); category.name = "category of great post"; @@ -344,7 +344,7 @@ describe("basic-lazy-relations", () => { }); }))); - it("should successfully load relations outside a transaction with entity generated within a transaction", () => Promise.all(connections.filter((connection) => (new Set(["mysql", "sqlite", "postgres"])).has(connection.options.type)).map(async connection => { + it("should successfully load relations outside a transaction with entity generated within a transaction", () => Promise.all(connections.filter((connection) => (new Set(["mysql", "sqlite", "better-sqlite3", "postgres"])).has(connection.options.type)).map(async connection => { const loadedCategory = await connection.manager.transaction(async (manager) => { const category = new Category(); category.name = "category of great post"; diff --git a/test/functional/relations/multiple-primary-keys/multiple-primary-keys-many-to-many/entity/Category.ts b/test/functional/relations/multiple-primary-keys/multiple-primary-keys-many-to-many/entity/Category.ts index f07912c2c51..c973f4cbc51 100644 --- a/test/functional/relations/multiple-primary-keys/multiple-primary-keys-many-to-many/entity/Category.ts +++ b/test/functional/relations/multiple-primary-keys/multiple-primary-keys-many-to-many/entity/Category.ts @@ -10,10 +10,14 @@ import {Unique} from "../../../../../../src"; @Unique(["code", "version", "description"]) export class Category { - @PrimaryColumn() + @PrimaryColumn("varchar", { + length: 31, + }) name: string; - @PrimaryColumn() + @PrimaryColumn("varchar", { + length: 31, + }) type: string; @Column() @@ -43,4 +47,4 @@ export class Category { @ManyToMany(type => Tag, tag => tag.categoriesWithNonPKColumns) tagsWithNonPKColumns: Tag[]; -} \ No newline at end of file +} diff --git a/test/functional/relations/multiple-primary-keys/multiple-primary-keys-many-to-many/entity/Tag.ts b/test/functional/relations/multiple-primary-keys/multiple-primary-keys-many-to-many/entity/Tag.ts index 71453dbc5b8..fcb9c5436d7 100644 --- a/test/functional/relations/multiple-primary-keys/multiple-primary-keys-many-to-many/entity/Tag.ts +++ b/test/functional/relations/multiple-primary-keys/multiple-primary-keys-many-to-many/entity/Tag.ts @@ -11,10 +11,14 @@ export class Tag { @Column() code: number; - @PrimaryColumn() + @PrimaryColumn("varchar", { + length: 31, + }) title: string; - @PrimaryColumn() + @PrimaryColumn("varchar", { + length: 31, + }) description: string; @ManyToMany(type => Category, category => category.tags) @@ -64,4 +68,4 @@ export class Tag { }) categoriesWithNonPKColumns: Category[]; -} \ No newline at end of file +} diff --git a/test/functional/repository/basic-methods/repository-basic-methods.ts b/test/functional/repository/basic-methods/repository-basic-methods.ts index 677b5a4c607..44adfc6d54f 100644 --- a/test/functional/repository/basic-methods/repository-basic-methods.ts +++ b/test/functional/repository/basic-methods/repository-basic-methods.ts @@ -9,7 +9,7 @@ import {Question} from "./model/Question"; import {Blog} from "./entity/Blog"; import {Category} from "./entity/Category"; import {DeepPartial} from "../../../../src/common/DeepPartial"; -import {EntitySchema} from "../../../../src"; +import {EntitySchema, Repository} from "../../../../src"; describe("repository > basic methods", () => { @@ -43,7 +43,7 @@ describe("repository > basic methods", () => { })); }); - + describe("hasId", function() { it("should return true if entity has an id", () => connections.forEach(connection => { @@ -152,7 +152,7 @@ describe("repository > basic methods", () => { })); it("should create a new empty object if entity schema is used", () => connections.forEach(connection => { - const repository = connection.getRepository("User"); + const repository = connection.getRepository("User") as Repository; repository.create().should.be.eql({}); })); @@ -222,7 +222,7 @@ describe("repository > basic methods", () => { blog.text = "Blog about good people"; blog.categories = [category]; await blogRepository.save(blog); - + // and preload it const plainBlogWithId = { id: 1 }; const preloadedBlog = await blogRepository.preload(plainBlogWithId); @@ -247,7 +247,7 @@ describe("repository > basic methods", () => { blog.text = "Blog about good people"; blog.categories = [category]; await blogRepository.save(blog); - + // and preload it const plainBlogWithId = { id: 1, categories: [{ id: 1 }] }; const preloadedBlog = await blogRepository.preload(plainBlogWithId); @@ -324,8 +324,7 @@ describe("repository > basic methods", () => { }); describe("save", function () { - it("should update existing entity using transformers", async () => { - const connection = connections.find((c: Connection) => c.name === "sqlite"); + it("should update existing entity using transformers", () => Promise.all(connections.filter(c => c.name === "sqlite" || c.name === "better-sqlite3").map(async connection => { if (!connection || (connection.options as any).skip === true) { return; } @@ -349,12 +348,12 @@ describe("repository > basic methods", () => { const saved = await postRepository.save(dbPost); saved.should.be.instanceOf(Post); - + saved.id!.should.be.equal(1); saved.title.should.be.equal("New title"); saved.dateAdded.should.be.instanceof(Date); saved.dateAdded.getTime().should.be.equal(date.getTime()); - }); + }))); }); describe("preload also should also implement merge functionality", function() { diff --git a/test/functional/repository/find-options-operators/repository-find-operators.ts b/test/functional/repository/find-options-operators/repository-find-operators.ts index 4434721fb08..b41b1352d68 100644 --- a/test/functional/repository/find-options-operators/repository-find-operators.ts +++ b/test/functional/repository/find-options-operators/repository-find-operators.ts @@ -5,6 +5,7 @@ import { Between, Connection, Equal, + ILike, In, IsNull, LessThan, @@ -12,8 +13,7 @@ import { Like, MoreThan, MoreThanOrEqual, - Not, - PromiseUtils + Not } from "../../../../src"; import {Post} from "./entity/Post"; import {PostgresDriver} from "../../../../src/driver/postgres/PostgresDriver"; @@ -272,6 +272,44 @@ describe("repository > find options > operators", () => { }))); + it("ilike", () => Promise.all(connections.map(async connection => { + // insert some fake data + const post1 = new Post(); + post1.title = "about #1"; + post1.likes = 12; + await connection.manager.save(post1); + const post2 = new Post(); + post2.title = "ABOUT #2"; + post2.likes = 3; + await connection.manager.save(post2); + + // check operator + const loadedPosts = await connection.getRepository(Post).find({ + title: ILike("%out #%") + }); + loadedPosts.should.be.eql([{ id: 1, likes: 12, title: "about #1" }, { id: 2, likes: 3, title: "ABOUT #2" }]); + + }))); + + it("not(ilike)", () => Promise.all(connections.map(async connection => { + // insert some fake data + const post1 = new Post(); + post1.title = "about #1"; + post1.likes = 12; + await connection.manager.save(post1); + const post2 = new Post(); + post2.title = "ABOUT #2"; + post2.likes = 3; + await connection.manager.save(post2); + + // check operator + const loadedPosts = await connection.getRepository(Post).find({ + title: Not(ILike("%out #1")) + }); + loadedPosts.should.be.eql([{ id: 2, likes: 3, title: "ABOUT #2" }]); + + }))); + it("like", () => Promise.all(connections.map(async connection => { // insert some fake data @@ -389,6 +427,10 @@ describe("repository > find options > operators", () => { }); loadedPosts.should.be.eql([{ id: 2, likes: 3, title: "About #2" }]); + const noPosts = await connection.getRepository(Post).find({ + title: In([]) + }); + noPosts.length.should.be.eql(0); }))); it("not(in)", () => Promise.all(connections.map(async connection => { @@ -409,6 +451,10 @@ describe("repository > find options > operators", () => { }); loadedPosts.should.be.eql([{ id: 2, likes: 3, title: "About #2" }]); + const noPosts = await connection.getRepository(Post).find({ + title: Not(In([])) + }); + noPosts.length.should.be.eql(2); }))); it("any", () => Promise.all(connections.map(async connection => { @@ -532,21 +578,106 @@ describe("repository > find options > operators", () => { likes: Raw(columnAlias => "1 + " + columnAlias + " = 4") }); loadedPosts.should.be.eql([{ id: 2, likes: 3, title: "About #2" }]); - }))); - it("should work with ActiveRecord model", () => PromiseUtils.runInSequence(connections, async connection => { - PersonAR.useConnection(connection); + it("raw (function with object literal parameters)", () => Promise.all(connections.map(async connection => { + const createPost = (index: number): Post => { + const post = new Post(); + post.title = `About #${index}`; + post.likes = index; - const person = new PersonAR(); - person.name = "Timber"; - await connection.manager.save(person); + return post; + } + + // insert some fake data + await connection.manager.save([ + createPost(1), + createPost(2), + createPost(3), + createPost(4), + createPost(5), + createPost(6), + ]); - const loadedPeople = await PersonAR.find({ - name: In(["Timber"]) + // check operator + const result1 = await connection.getRepository(Post).find({ + likes: Raw((columnAlias) => { + return `(${columnAlias} = :value1) OR (${columnAlias} = :value2)` + }, { value1: 2, value2: 3 }), }); - expect(loadedPeople[0].name).to.be.equal("Timber"); + result1.should.be.eql([ + { id: 2, likes: 2, title: "About #2" }, + { id: 3, likes: 3, title: "About #3" }, + ]); - })); + // check operator + const result2 = await connection.getRepository(Post).find({ + likes: Raw((columnAlias) => { + return `(${columnAlias} IN (1, 4, 5, 6)) AND (${columnAlias} < :maxValue)` + }, { maxValue: 6 }), + }); + result2.should.be.eql([ + { id: 1, likes: 1, title: "About #1" }, + { id: 4, likes: 4, title: "About #4" }, + { id: 5, likes: 5, title: "About #5" }, + ]); + + // check operator + const result3 = await connection.getRepository(Post).find({ + title: Raw((columnAlias) => { + return `${columnAlias} IN (:a, :b, :c)`; + }, { a: "About #1", b: "About #3", c: "About #5" }), + likes: Raw((columnAlias) => `${columnAlias} IN (:d, :e)`, { d: 5, e: 1 }), + }); + result3.should.be.eql([ + { id: 1, likes: 1, title: "About #1" }, + { id: 5, likes: 5, title: "About #5" }, + ]); + + // check operator + const result4 = await connection.getRepository(Post).find({ + likes: Raw((columnAlias) => `${columnAlias} IN (2, 6)`, { }), + }); + result4.should.be.eql([ + { id: 2, likes: 2, title: "About #2" }, + { id: 6, likes: 6, title: "About #6" }, + ]); + + // check operator + const result5 = await connection.getRepository(Post).find({ + likes: Raw((columnAlias) => `${columnAlias} IN (2, :value, 6)`, { value: 3 }), + }); + result5.should.be.eql([ + { id: 2, likes: 2, title: "About #2" }, + { id: 3, likes: 3, title: "About #3" }, + { id: 6, likes: 6, title: "About #6" }, + ]); + + // check operator + const result6 = await connection.getRepository(Post).find({ + likes: Raw((columnAlias) => `${columnAlias} IN (:...values)`, { values: [2, 3, 6] }), + }); + result6.should.be.eql([ + { id: 2, likes: 2, title: "About #2" }, + { id: 3, likes: 3, title: "About #3" }, + { id: 6, likes: 6, title: "About #6" }, + ]); + }))); + + it("should work with ActiveRecord model", async () => { + // These must run sequentially as we have the global context of the `PersonAR` ActiveRecord class + for (const connection of connections) { + PersonAR.useConnection(connection); + + const person = new PersonAR(); + person.name = "Timber"; + await connection.manager.save(person); + + const loadedPeople = await PersonAR.find({ + name: In(["Timber"]) + }); + expect(loadedPeople[0].name).to.be.equal("Timber"); + } + }); }); diff --git a/test/functional/repository/find-options/repository-find-options.ts b/test/functional/repository/find-options/repository-find-options.ts index 42bffc16e11..cc40d87607b 100644 --- a/test/functional/repository/find-options/repository-find-options.ts +++ b/test/functional/repository/find-options/repository-find-options.ts @@ -6,13 +6,13 @@ import {User} from "./entity/User"; import {Category} from "./entity/Category"; import {Post} from "./entity/Post"; import {Photo} from "./entity/Photo"; +import sinon from "sinon"; describe("repository > find options", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); @@ -51,6 +51,35 @@ describe("repository > find options", () => { }))); + it("should execute select query inside transaction", () => Promise.all(connections.map(async connection => { + + const user = new User(); + user.name = "Alex Messer"; + await connection.manager.save(user); + + + const queryRunner = await connection.createQueryRunner(); + + const startTransactionFn = sinon.spy(queryRunner, "startTransaction"); + const commitTransactionFn = sinon.spy(queryRunner, "commitTransaction"); + + expect(startTransactionFn.called).to.be.false; + expect(commitTransactionFn.called).to.be.false; + + await connection + .createEntityManager(queryRunner) + .getRepository(User) + .findOne(1, { + transaction: true + }); + + expect(startTransactionFn.calledOnce).to.be.true; + expect(commitTransactionFn.calledOnce).to.be.true; + + await queryRunner.release(); + + }))); + it("should select specific columns", () => Promise.all(connections.map(async connection => { const category = new Category(); diff --git a/test/functional/repository/soft-delete/entity-soft-remove.ts b/test/functional/repository/soft-delete/entity-soft-remove.ts index f9c75399e65..f89f8289241 100644 --- a/test/functional/repository/soft-delete/entity-soft-remove.ts +++ b/test/functional/repository/soft-delete/entity-soft-remove.ts @@ -5,7 +5,6 @@ import {Connection} from "../../../../src/connection/Connection"; import {Post} from "./entity/Post"; import { PostWithoutDeleteDateColumn } from "./entity/PostWithoutDeleteDateColumn"; import { MissingDeleteDateColumnError } from "../../../../src/error/MissingDeleteDateColumnError"; -import { PromiseUtils } from "../../../../src"; describe("entity > soft-remove", () => { @@ -16,9 +15,7 @@ describe("entity > soft-remove", () => { beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("should perform soft removal and recovery correctly", () => PromiseUtils.runInSequence(connections, async connection => { - Post.useConnection(connection); // change connection each time because of AR specifics - + it("should perform soft removal and recovery correctly", () => Promise.all(connections.map(async connection => { const postRepository = connection.getRepository(Post); // save a new posts @@ -35,7 +32,7 @@ describe("entity > soft-remove", () => { await postRepository.save(newPost2); // soft-remove one - await newPost1.softRemove(); + await postRepository.softRemove(newPost1); // load to check const loadedPosts = await postRepository.find({ withDeleted: true }); @@ -53,7 +50,8 @@ describe("entity > soft-remove", () => { expect(loadedPost2!.name).to.equals("post#2"); // recover one - await loadedPost1!.recover(); + await postRepository.recover(loadedPost1!); + // load to check const recoveredPosts = await postRepository.find({ withDeleted: true }); @@ -68,12 +66,9 @@ describe("entity > soft-remove", () => { expect(recoveredPost2).to.exist; expect(recoveredPost2!.deletedAt).to.equals(null); expect(recoveredPost2!.name).to.equals("post#2"); + }))); - })); - - it("should throw error when delete date column is missing", () => PromiseUtils.runInSequence(connections, async connection => { - PostWithoutDeleteDateColumn.useConnection(connection); // change connection each time because of AR specifics - + it("should throw error when delete date column is missing", () => Promise.all(connections.map(async connection => { const postRepository = connection.getRepository(PostWithoutDeleteDateColumn); // save a new posts @@ -87,7 +82,7 @@ describe("entity > soft-remove", () => { let error1: Error | undefined; try { // soft-remove one - await newPost1.softRemove(); + await postRepository.softRemove(newPost1); } catch (err) { error1 = err; } @@ -96,11 +91,11 @@ describe("entity > soft-remove", () => { let error2: Error | undefined; try { // recover one - await newPost1.recover(); + await postRepository.recover(newPost1); } catch (err) { error2 = err; } expect(error2).to.be.an.instanceof(MissingDeleteDateColumnError); - })); + }))); }); diff --git a/test/functional/schema-builder/change-check-constraint.ts b/test/functional/schema-builder/change-check-constraint.ts index 5996b4eaf38..5e6d6cbf1a9 100644 --- a/test/functional/schema-builder/change-check-constraint.ts +++ b/test/functional/schema-builder/change-check-constraint.ts @@ -1,7 +1,6 @@ import "reflect-metadata"; import {Connection} from "../../../src/connection/Connection"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; -import {PromiseUtils} from "../../../src"; import {Teacher} from "./entity/Teacher"; import {Post} from "./entity/Post"; import {CheckMetadata} from "../../../src/metadata/CheckMetadata"; @@ -20,7 +19,7 @@ describe("schema builder > change check constraint", () => { beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("should correctly add new check constraint", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly add new check constraint", () => Promise.all(connections.map(async connection => { // Mysql does not support check constraints. if (connection.driver instanceof MysqlDriver) return; @@ -43,9 +42,9 @@ describe("schema builder > change check constraint", () => { await queryRunner.release(); table!.checks.length.should.be.equal(1); - })); + }))); - it("should correctly change check", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change check", () => Promise.all(connections.map(async connection => { // Mysql does not support check constraints. if (connection.driver instanceof MysqlDriver) return; @@ -61,9 +60,9 @@ describe("schema builder > change check constraint", () => { await queryRunner.release(); table!.checks[0].expression!.indexOf("2000").should.be.not.equal(-1); - })); + }))); - it("should correctly drop removed check", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly drop removed check", () => Promise.all(connections.map(async connection => { // Mysql does not support check constraints. if (connection.driver instanceof MysqlDriver) return; @@ -78,6 +77,6 @@ describe("schema builder > change check constraint", () => { await queryRunner.release(); table!.checks.length.should.be.equal(0); - })); + }))); }); diff --git a/test/functional/schema-builder/change-column.ts b/test/functional/schema-builder/change-column.ts index 2372e47400d..b39cca93173 100644 --- a/test/functional/schema-builder/change-column.ts +++ b/test/functional/schema-builder/change-column.ts @@ -1,6 +1,6 @@ import {expect} from "chai"; import "reflect-metadata"; -import {Connection, PromiseUtils} from "../../../src"; +import {Connection} from "../../../src"; import {AuroraDataApiDriver} from "../../../src/driver/aurora-data-api/AuroraDataApiDriver"; import {CockroachDriver} from "../../../src/driver/cockroachdb/CockroachDriver"; import {MysqlDriver} from "../../../src/driver/mysql/MysqlDriver"; @@ -26,7 +26,7 @@ describe("schema builder > change column", () => { beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("should correctly change column name", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change column name", () => Promise.all(connections.map(async connection => { const postMetadata = connection.getMetadata(Post); const nameColumn = postMetadata.findColumnWithPropertyName("name")!; nameColumn.propertyName = "title"; @@ -44,9 +44,9 @@ describe("schema builder > change column", () => { // revert changes nameColumn.propertyName = "name"; nameColumn.build(connection); - })); + }))); - it("should correctly change column length", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change column length", () => Promise.all(connections.map(async connection => { const postMetadata = connection.getMetadata(Post); const nameColumn = postMetadata.findColumnWithPropertyName("name")!; const textColumn = postMetadata.findColumnWithPropertyName("text")!; @@ -71,9 +71,9 @@ describe("schema builder > change column", () => { // revert changes nameColumn.length = "255"; textColumn.length = "255"; - })); + }))); - it("should correctly change column type", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change column type", () => Promise.all(connections.map(async connection => { const postMetadata = connection.getMetadata(Post); const versionColumn = postMetadata.findColumnWithPropertyName("version")!; @@ -95,9 +95,9 @@ describe("schema builder > change column", () => { // revert changes versionColumn.type = "varchar"; postVersionColumn.type = "varchar"; - })); + }))); - it("should correctly make column primary and generated", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly make column primary and generated", () => Promise.all(connections.map(async connection => { // CockroachDB does not allow changing PK if (connection.driver instanceof CockroachDriver) return; @@ -130,9 +130,9 @@ describe("schema builder > change column", () => { idColumn.isGenerated = false; idColumn.generationStrategy = undefined; versionColumn.isPrimary = false; - })); + }))); - it("should correctly change column `isGenerated` property when column is on foreign key", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change column `isGenerated` property when column is on foreign key", () => Promise.all(connections.map(async connection => { const teacherMetadata = connection.getMetadata("teacher"); const idColumn = teacherMetadata.findColumnWithPropertyName("id")!; idColumn.isGenerated = false; @@ -151,9 +151,9 @@ describe("schema builder > change column", () => { idColumn.isGenerated = true; idColumn.generationStrategy = "increment"; - })); + }))); - it("should correctly change non-generated column on to uuid-generated column", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change non-generated column on to uuid-generated column", () => Promise.all(connections.map(async connection => { // CockroachDB does not allow changing PK if (connection.driver instanceof CockroachDriver) return; @@ -199,9 +199,9 @@ describe("schema builder > change column", () => { postMetadata.generatedColumns.splice(postMetadata.generatedColumns.indexOf(idColumn), 1); postMetadata.hasUUIDGeneratedColumns = false; - })); + }))); - it("should correctly change generated column generation strategy", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change generated column generation strategy", () => Promise.all(connections.map(async connection => { // CockroachDB does not allow changing PK if (connection.driver instanceof CockroachDriver) return; @@ -246,6 +246,37 @@ describe("schema builder > change column", () => { idColumn.type = "int"; teacherColumn.type = "int"; - })); + }))); + it("should correctly change column comment", () => Promise.all(connections.map(async connection => { + // Skip thie contents of this test if not one of the drivers that support comments + if (!(connection.driver instanceof CockroachDriver || connection.driver instanceof PostgresDriver || connection.driver instanceof MysqlDriver)) { + return; + } + + const teacherMetadata = connection.getMetadata("teacher"); + const idColumn = teacherMetadata.findColumnWithPropertyName("id")!; + + idColumn.comment = "The Teacher's Key"; + + await connection.synchronize(); + + const queryRunnerA = connection.createQueryRunner(); + const teacherTableA = await queryRunnerA.getTable("teacher"); + await queryRunnerA.release(); + + expect(teacherTableA!.findColumnByName("id")!.comment).to.be.equal("The Teacher's Key", connection.name); + + // revert changes + idColumn.comment = ""; + + await connection.synchronize(); + + const queryRunnerB = connection.createQueryRunner(); + const teacherTableB = await queryRunnerB.getTable("teacher"); + await queryRunnerB.release(); + + expect(teacherTableB!.findColumnByName("id")!.comment).to.be.undefined; + + }))); }); diff --git a/test/functional/schema-builder/change-exclusion-constraint.ts b/test/functional/schema-builder/change-exclusion-constraint.ts index c390f64dcfe..dfd6366f744 100644 --- a/test/functional/schema-builder/change-exclusion-constraint.ts +++ b/test/functional/schema-builder/change-exclusion-constraint.ts @@ -1,7 +1,6 @@ import "reflect-metadata"; import {Connection} from "../../../src/connection/Connection"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; -import {PromiseUtils} from "../../../src"; import {Teacher} from "./entity/Teacher"; import {Post} from "./entity/Post"; import {ExclusionMetadata} from "../../../src/metadata/ExclusionMetadata"; @@ -20,7 +19,7 @@ describe("schema builder > change exclusion constraint", () => { beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("should correctly add new exclusion constraint", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly add new exclusion constraint", () => Promise.all(connections.map(async connection => { const teacherMetadata = connection.getMetadata(Teacher); const exclusionMetadata = new ExclusionMetadata({ @@ -40,9 +39,9 @@ describe("schema builder > change exclusion constraint", () => { await queryRunner.release(); table!.exclusions.length.should.be.equal(1); - })); + }))); - it("should correctly change exclusion", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change exclusion", () => Promise.all(connections.map(async connection => { const postMetadata = connection.getMetadata(Post); postMetadata.exclusions[0].expression = `USING gist ("tag" WITH =)`; @@ -55,9 +54,9 @@ describe("schema builder > change exclusion constraint", () => { await queryRunner.release(); table!.exclusions[0].expression!.indexOf("tag").should.be.not.equal(-1); - })); + }))); - it("should correctly drop removed exclusion", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly drop removed exclusion", () => Promise.all(connections.map(async connection => { const postMetadata = connection.getMetadata(Post); postMetadata.exclusions = []; @@ -69,6 +68,6 @@ describe("schema builder > change exclusion constraint", () => { await queryRunner.release(); table!.exclusions.length.should.be.equal(0); - })); + }))); }); diff --git a/test/functional/schema-builder/change-index.ts b/test/functional/schema-builder/change-index.ts index 47cb52a26de..0d8b9793a83 100644 --- a/test/functional/schema-builder/change-index.ts +++ b/test/functional/schema-builder/change-index.ts @@ -2,7 +2,6 @@ import "reflect-metadata"; import {Connection} from "../../../src/connection/Connection"; import {CockroachDriver} from "../../../src/driver/cockroachdb/CockroachDriver"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; -import {PromiseUtils} from "../../../src"; import {IndexMetadata} from "../../../src/metadata/IndexMetadata"; import {Teacher} from "./entity/Teacher"; import {Student} from "./entity/Student"; @@ -22,7 +21,7 @@ describe("schema builder > change index", () => { beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("should correctly add new index", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly add new index", () => Promise.all(connections.map(async connection => { const teacherMetadata = connection.getMetadata(Teacher); const nameColumn = teacherMetadata.findColumnWithPropertyName("name")!; const indexMetadata = new IndexMetadata({ @@ -46,9 +45,9 @@ describe("schema builder > change index", () => { // revert changes teacherMetadata.indices.splice(teacherMetadata.indices.indexOf(indexMetadata), 1); - })); + }))); - it("should correctly change index", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change index", () => Promise.all(connections.map(async connection => { const studentMetadata = connection.getMetadata(Student); studentMetadata.indices[0].name = "changed_index"; @@ -60,9 +59,9 @@ describe("schema builder > change index", () => { const index = studentTable!.indices.find(i => i.name === "changed_index"); expect(index).not.be.undefined; - })); + }))); - it("should correctly drop removed index", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly drop removed index", () => Promise.all(connections.map(async connection => { const studentMetadata = connection.getMetadata(Student); studentMetadata.indices.splice(0, 1); @@ -77,9 +76,9 @@ describe("schema builder > change index", () => { } else { studentTable!.indices.length.should.be.equal(0); } - })); + }))); - it("should ignore index synchronization when `synchronize` set to false", () => PromiseUtils.runInSequence(connections, async connection => { + it("should ignore index synchronization when `synchronize` set to false", () => Promise.all(connections.map(async connection => { // You can not disable synchronization for unique index in CockroachDB, because unique indices are stored as UNIQUE constraints @@ -116,6 +115,6 @@ describe("schema builder > change index", () => { await queryRunner.release(); - })); + }))); }); diff --git a/test/functional/schema-builder/change-unique-constraint.ts b/test/functional/schema-builder/change-unique-constraint.ts index dc020b8658f..7791e24e9ff 100644 --- a/test/functional/schema-builder/change-unique-constraint.ts +++ b/test/functional/schema-builder/change-unique-constraint.ts @@ -1,5 +1,4 @@ import "reflect-metadata"; -import {PromiseUtils} from "../../../src"; import {Connection} from "../../../src"; import {MysqlDriver} from "../../../src/driver/mysql/MysqlDriver"; import {SapDriver} from "../../../src/driver/sap/SapDriver"; @@ -23,7 +22,7 @@ describe("schema builder > change unique constraint", () => { beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("should correctly add new unique constraint", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly add new unique constraint", () => Promise.all(connections.map(async connection => { const teacherMetadata = connection.getMetadata(Teacher); const nameColumn = teacherMetadata.findColumnWithPropertyName("name")!; let uniqueIndexMetadata: IndexMetadata|undefined = undefined; @@ -74,9 +73,9 @@ describe("schema builder > change unique constraint", () => { // revert changes teacherMetadata.uniques.splice(teacherMetadata.uniques.indexOf(uniqueMetadata!), 1); } - })); + }))); - it("should correctly change unique constraint", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly change unique constraint", () => Promise.all(connections.map(async connection => { // Sqlite does not store unique constraint name if (connection.driver instanceof AbstractSqliteDriver) return; @@ -116,9 +115,9 @@ describe("schema builder > change unique constraint", () => { uniqueMetadata!.name = connection.namingStrategy.uniqueConstraintName(table!, uniqueMetadata!.columns.map(c => c.databaseName)); } - })); + }))); - it("should correctly drop removed unique constraint", () => PromiseUtils.runInSequence(connections, async connection => { + it("should correctly drop removed unique constraint", () => Promise.all(connections.map(async connection => { const postMetadata = connection.getMetadata(Post); // Mysql and SAP stores unique constraints as unique indices. @@ -143,6 +142,6 @@ describe("schema builder > change unique constraint", () => { } else { table!.uniques.length.should.be.equal(1); } - })); + }))); }); diff --git a/test/functional/sqlite/enable-wal.ts b/test/functional/sqlite/enable-wal.ts new file mode 100644 index 00000000000..b1af701ebf2 --- /dev/null +++ b/test/functional/sqlite/enable-wal.ts @@ -0,0 +1,24 @@ +import "reflect-metadata"; +import {expect} from "chai"; +import {Connection} from "../../../src/connection/Connection"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; + +describe("sqlite driver > enable wal", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [], + enabledDrivers: ["sqlite"], + driverSpecific: { + enableWAL: true + } + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should set the journal mode as expected", () => Promise.all(connections.map(async connection => { + // if we come this far, test was successful as a connection was established + const result = await connection.query('PRAGMA journal_mode'); + + expect(result).to.eql([{ journal_mode: 'wal'}]); + }))); +}); diff --git a/test/functional/sqlite/error-on-query-after-close.ts b/test/functional/sqlite/error-on-query-after-close.ts new file mode 100644 index 00000000000..21126ecc9f1 --- /dev/null +++ b/test/functional/sqlite/error-on-query-after-close.ts @@ -0,0 +1,21 @@ +import "reflect-metadata"; +import {expect} from "chai"; +import {Connection} from "../../../src/connection/Connection"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; + +describe("sqlite driver > throws an error when queried after closing connection", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [], + enabledDrivers: ["sqlite"], + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should throw", () => Promise.all(connections.map(async connection => { + await connection.close() + await expect(connection.query('select * from sqlite_master;')).to.rejectedWith( + 'Connection with sqlite database is not established. Check connection configuration.' + ); + }))); +}); diff --git a/test/functional/table-inheritance/single-table/database-option-inherited/database-option-inherited.ts b/test/functional/table-inheritance/single-table/database-option-inherited/database-option-inherited.ts index 18700f5e11c..66ba39cef9f 100644 --- a/test/functional/table-inheritance/single-table/database-option-inherited/database-option-inherited.ts +++ b/test/functional/table-inheritance/single-table/database-option-inherited/database-option-inherited.ts @@ -8,7 +8,7 @@ describe("table-inheritance > single-table > database-option-inherited", () => { before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], // creating more databases isn't always possible(e.g oracle official docker images) - enabledDrivers: ["postgres", "cockroachdb", "mariadb", "mssql", "mysql", "sqlite", "sqljs"] + enabledDrivers: ["postgres", "cockroachdb", "mariadb", "mssql", "mysql", "sqlite", "better-sqlite3", "sqljs"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/table-inheritance/single-table/non-virtual-discriminator-column/entity/Other.ts b/test/functional/table-inheritance/single-table/non-virtual-discriminator-column/entity/Other.ts new file mode 100644 index 00000000000..4ef0c856c3e --- /dev/null +++ b/test/functional/table-inheritance/single-table/non-virtual-discriminator-column/entity/Other.ts @@ -0,0 +1,11 @@ +import {Column} from "../../../../../../src/decorator/columns/Column"; +import {ChildEntity} from "../../../../../../src/decorator/entity/ChildEntity"; +import {Person} from "./Person"; + +@ChildEntity("") +export class Other extends Person { + + @Column() + mood: string; + +} diff --git a/test/functional/table-inheritance/single-table/non-virtual-discriminator-column/non-virtual-discriminator-column.ts b/test/functional/table-inheritance/single-table/non-virtual-discriminator-column/non-virtual-discriminator-column.ts index 700f9e616cc..a413b0b38a4 100644 --- a/test/functional/table-inheritance/single-table/non-virtual-discriminator-column/non-virtual-discriminator-column.ts +++ b/test/functional/table-inheritance/single-table/non-virtual-discriminator-column/non-virtual-discriminator-column.ts @@ -3,7 +3,9 @@ import {closeTestingConnections, createTestingConnections, reloadTestingDatabase import {Connection} from "../../../../../src/connection/Connection"; import {Student} from "./entity/Student"; import {Employee} from "./entity/Employee"; +import {Other} from "./entity/Other"; import {Person} from "./entity/Person"; +import {OracleDriver} from "../../../../../src/driver/oracle/OracleDriver"; describe("table-inheritance > single-table > non-virtual-discriminator-column", () => { @@ -30,6 +32,15 @@ describe("table-inheritance > single-table > non-virtual-discriminator-column", employee.salary = 1000; await connection.getRepository(Employee).save(employee); + if (!(connection.driver instanceof OracleDriver)) { + // In Oracle, empty string is a `null` so this isn't exactly possible there. + + const other = new Other(); + other.name = "Empty"; + other.mood = "Happy" + await connection.getRepository(Other).save(other); + } + // ------------------------------------------------------------------------- // Select // ------------------------------------------------------------------------- @@ -47,6 +58,15 @@ describe("table-inheritance > single-table > non-virtual-discriminator-column", persons[1].type.should.be.equal("employee-type"); persons[1].name.should.be.equal("Roger"); (persons[1] as Employee).salary.should.be.equal(1000); + + if (!(connection.driver instanceof OracleDriver)) { + // In Oracle, empty string is a `null` so this isn't exactly possible there. + + persons[2].id.should.be.equal(3); + persons[2].type.should.be.equal(""); + persons[2].name.should.be.equal("Empty"); + (persons[2] as Other).mood.should.be.equal("Happy"); + } }))); }); diff --git a/test/functional/table-inheritance/single-table/numeric-types/entity/Person.ts b/test/functional/table-inheritance/single-table/numeric-types/entity/Person.ts new file mode 100644 index 00000000000..16ee3285a1b --- /dev/null +++ b/test/functional/table-inheritance/single-table/numeric-types/entity/Person.ts @@ -0,0 +1,18 @@ +import {Column} from "../../../../../../src/decorator/columns/Column"; +import {TableInheritance} from "../../../../../../src/decorator/entity/TableInheritance"; +import {Entity} from "../../../../../../src/decorator/entity/Entity"; +import {PrimaryGeneratedColumn} from "../../../../../../src/decorator/columns/PrimaryGeneratedColumn"; + +@Entity() +@TableInheritance({ column: { name: "type", type: "int" } }) +export class Person { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @Column() + type: number; +} diff --git a/test/functional/table-inheritance/single-table/numeric-types/entity/Student.ts b/test/functional/table-inheritance/single-table/numeric-types/entity/Student.ts new file mode 100644 index 00000000000..14f96bc3a27 --- /dev/null +++ b/test/functional/table-inheritance/single-table/numeric-types/entity/Student.ts @@ -0,0 +1,11 @@ +import {Column} from "../../../../../../src/decorator/columns/Column"; +import {ChildEntity} from "../../../../../../src/decorator/entity/ChildEntity"; +import {Person} from "./Person"; + +@ChildEntity(0) +export class Student extends Person { + + @Column() + faculty: string; + +} diff --git a/test/functional/table-inheritance/single-table/numeric-types/entity/Teacher.ts b/test/functional/table-inheritance/single-table/numeric-types/entity/Teacher.ts new file mode 100644 index 00000000000..71657f528e2 --- /dev/null +++ b/test/functional/table-inheritance/single-table/numeric-types/entity/Teacher.ts @@ -0,0 +1,11 @@ +import {Column} from "../../../../../../src/decorator/columns/Column"; +import {ChildEntity} from "../../../../../../src/decorator/entity/ChildEntity"; +import {Person} from "./Person"; + +@ChildEntity(1) +export class Teacher extends Person { + + @Column() + specialization: string; + +} diff --git a/test/functional/table-inheritance/single-table/numeric-types/numeric-types.ts b/test/functional/table-inheritance/single-table/numeric-types/numeric-types.ts new file mode 100644 index 00000000000..4a603c23048 --- /dev/null +++ b/test/functional/table-inheritance/single-table/numeric-types/numeric-types.ts @@ -0,0 +1,57 @@ +import "reflect-metadata"; +import {expect} from "chai"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../../utils/test-utils"; +import {Connection} from "../../../../../src/connection/Connection"; +import {Student} from "./entity/Student"; +import {Teacher} from "./entity/Teacher"; +import {Person} from "./entity/Person"; +import {CockroachDriver} from "../../../../../src/driver/cockroachdb/CockroachDriver"; + +describe("table-inheritance > single-table > numeric types", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [Person, Student, Teacher] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should allow numeric types for the discriminator, including 0", () => Promise.all(connections.map(async connection => { + if (connection.driver instanceof CockroachDriver) { + return; + } + + // ------------------------------------------------------------------------- + // Create + // ------------------------------------------------------------------------- + + const student = new Student(); + student.name = "Alice"; + student.faculty = "Economics"; + await connection.getRepository(Student).save(student); + + const teacher = new Teacher(); + teacher.name = "Roger"; + teacher.specialization = "Math"; + await connection.getRepository(Teacher).save(teacher); + + // ------------------------------------------------------------------------- + // Select + // ------------------------------------------------------------------------- + + let persons = await connection.manager + .createQueryBuilder(Person, "person") + .getMany(); + + expect(persons[0].id).to.be.equal(1); + expect(persons[0].type).to.be.equal(0); + expect(persons[0].name).to.be.equal("Alice"); + expect((persons[0] as Student).faculty).to.be.equal("Economics"); + + expect(persons[1].id).to.be.equal(2); + expect(persons[1].type).to.be.equal(1); + expect(persons[1].name).to.be.equal("Roger"); + expect((persons[1] as Teacher).specialization).to.be.equal("Math"); + }))); + +}); diff --git a/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Faculty.ts b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Faculty.ts new file mode 100644 index 00000000000..2a85a9ef8e5 --- /dev/null +++ b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Faculty.ts @@ -0,0 +1,19 @@ +import {Column} from "../../../../../../../src/decorator/columns/Column"; +import {Entity} from "../../../../../../../src/decorator/entity/Entity"; +import {PrimaryGeneratedColumn} from "../../../../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import {Staff} from "./Staff"; +import {OneToMany} from "../../../../../../../src"; + +@Entity() +export class Faculty { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @OneToMany(type => Staff, staff => staff.faculty, { cascade: true, eager: true }) + staff: Staff[]; + +} diff --git a/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Professor.ts b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Professor.ts new file mode 100644 index 00000000000..82c742be89f --- /dev/null +++ b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Professor.ts @@ -0,0 +1,15 @@ +import {ChildEntity} from "../../../../../../../src/decorator/entity/ChildEntity"; +import {Staff} from "./Staff"; +import {Column} from "../../../../../../../src"; + +@ChildEntity("PROFESSOR") +export class Professor extends Staff { + + constructor(className: string) { + super(); + this.className = className; + } + + @Column() + className: string; +} diff --git a/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Researcher.ts b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Researcher.ts new file mode 100644 index 00000000000..c85ff87a3ff --- /dev/null +++ b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Researcher.ts @@ -0,0 +1,15 @@ +import {ChildEntity} from "../../../../../../../src/decorator/entity/ChildEntity"; +import {Staff} from "./Staff"; +import {Column} from "../../../../../../../src"; + +@ChildEntity("RESEARCHER") +export class Researcher extends Staff { + + constructor(areaOfStudy: string) { + super(); + this.areaOfStudy = areaOfStudy; + } + + @Column() + areaOfStudy: string; +} diff --git a/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Staff.ts b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Staff.ts new file mode 100644 index 00000000000..191e8ab950b --- /dev/null +++ b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/entity/Staff.ts @@ -0,0 +1,17 @@ +import {Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance} from "../../../../../../../src"; +import {Faculty} from "./Faculty"; + +@Entity() +@TableInheritance({ column: { name: "type", type: "varchar" } }) +export class Staff { + + @PrimaryGeneratedColumn() + id: number; + + @ManyToOne(type => Faculty, faculty => faculty.staff) + faculty: Faculty; + + @Column() + type: string; + +} diff --git a/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/one-to-many-cascade-save.ts b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/one-to-many-cascade-save.ts new file mode 100644 index 00000000000..42d5c8631bc --- /dev/null +++ b/test/functional/table-inheritance/single-table/relations/one-to-many-casecade-save/one-to-many-cascade-save.ts @@ -0,0 +1,42 @@ +import "reflect-metadata"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases +} from "../../../../../utils/test-utils"; +import {expect} from "chai"; +import {Connection} from "../../../../../../src/connection/Connection"; +import {Faculty} from "./entity/Faculty"; +import {Professor} from "./entity/Professor"; +import {Researcher} from "./entity/Researcher"; + +describe("table-inheritance > single-table > relations > one-to-many-cascade-save", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should work correctly with OneToMany relations", () => Promise.all(connections.map(async connection => { + + // ------------------------------------------------------------------------- + // Create + // ------------------------------------------------------------------------- + + const researcher = new Researcher("Economics"); + await connection.getRepository(Researcher).save(researcher); + + const faculty1 = new Faculty(); + faculty1.name = "Economics"; + faculty1.staff = [ new Professor("Economics 101"), researcher ]; + await connection.getRepository(Faculty).save(faculty1); + + const loadedFaculty = await connection.getRepository(Faculty).findOne() as Faculty; + + expect(loadedFaculty.staff.find(staff => staff.type === "PROFESSOR")).to.not.be.undefined; + expect(loadedFaculty.staff.find(staff => staff.type === "RESEARCHER")).to.not.be.undefined; + }))); + +}); diff --git a/test/functional/transaction/database-specific-isolation/sqlite-isolation.ts b/test/functional/transaction/database-specific-isolation/sqlite-isolation.ts index 907cb0da2d0..9915e444153 100644 --- a/test/functional/transaction/database-specific-isolation/sqlite-isolation.ts +++ b/test/functional/transaction/database-specific-isolation/sqlite-isolation.ts @@ -10,7 +10,7 @@ describe("transaction > transaction with sqlite connection partial isolation sup let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] // todo: for some reasons mariadb tests are not passing here + enabledDrivers: ["sqlite", "better-sqlite3"] // todo: for some reasons mariadb tests are not passing here })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/transaction/return-data-from-transaction/return-data-from-transaction.ts b/test/functional/transaction/return-data-from-transaction/return-data-from-transaction.ts index db10856a45e..55db390eb58 100644 --- a/test/functional/transaction/return-data-from-transaction/return-data-from-transaction.ts +++ b/test/functional/transaction/return-data-from-transaction/return-data-from-transaction.ts @@ -10,7 +10,7 @@ describe("transaction > return data from transaction", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "sqlite", "postgres"] // todo: for some reasons mariadb tests are not passing here + enabledDrivers: ["mysql", "sqlite", "better-sqlite3", "postgres"] // todo: for some reasons mariadb tests are not passing here })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/transaction/transaction-in-entity-manager/transaction-in-entity-manager.ts b/test/functional/transaction/transaction-in-entity-manager/transaction-in-entity-manager.ts index 9b20840dc97..a47c9d7fbd8 100644 --- a/test/functional/transaction/transaction-in-entity-manager/transaction-in-entity-manager.ts +++ b/test/functional/transaction/transaction-in-entity-manager/transaction-in-entity-manager.ts @@ -10,7 +10,7 @@ describe("transaction > transaction with entity manager", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "sqlite", "postgres"] // todo: for some reasons mariadb tests are not passing here + enabledDrivers: ["mysql", "sqlite", "better-sqlite3", "postgres"] // todo: for some reasons mariadb tests are not passing here })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/functional/uuid/sqlite/uuid-sqlite.ts b/test/functional/uuid/sqlite/uuid-sqlite.ts index f167faee6d1..ee03f28e3b6 100644 --- a/test/functional/uuid/sqlite/uuid-sqlite.ts +++ b/test/functional/uuid/sqlite/uuid-sqlite.ts @@ -11,7 +11,7 @@ describe("uuid-sqlite", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/functional/view-entity/mssql/view-entity-mssql.ts b/test/functional/view-entity/mssql/view-entity-mssql.ts index 274f6d34a8e..451d8aeff72 100644 --- a/test/functional/view-entity/mssql/view-entity-mssql.ts +++ b/test/functional/view-entity/mssql/view-entity-mssql.ts @@ -1,6 +1,5 @@ import {expect} from "chai"; import "reflect-metadata"; -import {CockroachDriver} from "../../../../src/driver/cockroachdb/CockroachDriver"; import {Category} from "./entity/Category"; import {Connection} from "../../../../src"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; @@ -49,13 +48,11 @@ describe("view entity > mssql", () => { const postCategories = await connection.manager.find(PostCategory); postCategories.length.should.be.equal(2); - const postId1 = connection.driver instanceof CockroachDriver ? "1" : 1; - postCategories[0].id.should.be.equal(postId1); + postCategories[0].id.should.be.equal(1); postCategories[0].postName.should.be.equal("About BMW"); postCategories[0].categoryName.should.be.equal("Cars"); - const postId2 = connection.driver instanceof CockroachDriver ? "2" : 2; - postCategories[1].id.should.be.equal(postId2); + postCategories[1].id.should.be.equal(2); postCategories[1].postName.should.be.equal("About Boeing"); postCategories[1].categoryName.should.be.equal("Airplanes"); diff --git a/test/functional/view-entity/mysql/view-entity-mysql.ts b/test/functional/view-entity/mysql/view-entity-mysql.ts index 4ff2e3c84d5..0e8ca35a752 100644 --- a/test/functional/view-entity/mysql/view-entity-mysql.ts +++ b/test/functional/view-entity/mysql/view-entity-mysql.ts @@ -1,6 +1,5 @@ import {expect} from "chai"; import "reflect-metadata"; -import {CockroachDriver} from "../../../../src/driver/cockroachdb/CockroachDriver"; import {Category} from "./entity/Category"; import {Connection} from "../../../../src"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; @@ -49,13 +48,11 @@ describe("view entity > mysql", () => { const postCategories = await connection.manager.find(PostCategory); postCategories.length.should.be.equal(2); - const postId1 = connection.driver instanceof CockroachDriver ? "1" : 1; - postCategories[0].id.should.be.equal(postId1); + postCategories[0].id.should.be.equal(1); postCategories[0].postName.should.be.equal("About BMW"); postCategories[0].categoryName.should.be.equal("Cars"); - const postId2 = connection.driver instanceof CockroachDriver ? "2" : 2; - postCategories[1].id.should.be.equal(postId2); + postCategories[1].id.should.be.equal(2); postCategories[1].postName.should.be.equal("About Boeing"); postCategories[1].categoryName.should.be.equal("Airplanes"); diff --git a/test/functional/view-entity/oracle/view-entity-oracle.ts b/test/functional/view-entity/oracle/view-entity-oracle.ts index 61cbc0f8173..620cd11e008 100644 --- a/test/functional/view-entity/oracle/view-entity-oracle.ts +++ b/test/functional/view-entity/oracle/view-entity-oracle.ts @@ -1,6 +1,5 @@ import {expect} from "chai"; import "reflect-metadata"; -import {CockroachDriver} from "../../../../src/driver/cockroachdb/CockroachDriver"; import {Category} from "./entity/Category"; import {Connection} from "../../../../src"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; @@ -49,13 +48,11 @@ describe("view entity > oracle", () => { const postCategories = await connection.manager.find(PostCategory); postCategories.length.should.be.equal(2); - const postId1 = connection.driver instanceof CockroachDriver ? "1" : 1; - postCategories[0].id.should.be.equal(postId1); + postCategories[0].id.should.be.equal(1); postCategories[0].postName.should.be.equal("About BMW"); postCategories[0].categoryName.should.be.equal("Cars"); - const postId2 = connection.driver instanceof CockroachDriver ? "2" : 2; - postCategories[1].id.should.be.equal(postId2); + postCategories[1].id.should.be.equal(2); postCategories[1].postName.should.be.equal("About Boeing"); postCategories[1].categoryName.should.be.equal("Airplanes"); diff --git a/test/functional/view-entity/postgres/view-entity-postgres.ts b/test/functional/view-entity/postgres/view-entity-postgres.ts index c034a4d7465..eb56de2e710 100644 --- a/test/functional/view-entity/postgres/view-entity-postgres.ts +++ b/test/functional/view-entity/postgres/view-entity-postgres.ts @@ -1,6 +1,5 @@ import {expect} from "chai"; import "reflect-metadata"; -import {CockroachDriver} from "../../../../src/driver/cockroachdb/CockroachDriver"; import {Category} from "./entity/Category"; import {Connection} from "../../../../src"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; @@ -75,13 +74,11 @@ describe("view entity > postgres", () => { const postCategories = await connection.manager.find(PostCategory); postCategories.length.should.be.equal(2); - const postId1 = connection.driver instanceof CockroachDriver ? "1" : 1; - postCategories[0].id.should.be.equal(postId1); + postCategories[0].id.should.be.equal(1); postCategories[0].postName.should.be.equal("About BMW"); postCategories[0].categoryName.should.be.equal("Cars"); - const postId2 = connection.driver instanceof CockroachDriver ? "2" : 2; - postCategories[1].id.should.be.equal(postId2); + postCategories[1].id.should.be.equal(2); postCategories[1].postName.should.be.equal("About Boeing"); postCategories[1].categoryName.should.be.equal("Airplanes"); diff --git a/test/functional/view-entity/sqlite/view-entity-sqlite.ts b/test/functional/view-entity/sqlite/view-entity-sqlite.ts index cd1b0aef514..3fe63aa72ef 100644 --- a/test/functional/view-entity/sqlite/view-entity-sqlite.ts +++ b/test/functional/view-entity/sqlite/view-entity-sqlite.ts @@ -1,6 +1,5 @@ import {expect} from "chai"; import "reflect-metadata"; -import {CockroachDriver} from "../../../../src/driver/cockroachdb/CockroachDriver"; import {Category} from "./entity/Category"; import {Connection} from "../../../../src"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; @@ -12,7 +11,7 @@ describe("view entity > sqlite", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); @@ -49,13 +48,11 @@ describe("view entity > sqlite", () => { const postCategories = await connection.manager.find(PostCategory); postCategories.length.should.be.equal(2); - const postId1 = connection.driver instanceof CockroachDriver ? "1" : 1; - postCategories[0].id.should.be.equal(postId1); + postCategories[0].id.should.be.equal(1); postCategories[0].postName.should.be.equal("About BMW"); postCategories[0].categoryName.should.be.equal("Cars"); - const postId2 = connection.driver instanceof CockroachDriver ? "2" : 2; - postCategories[1].id.should.be.equal(postId2); + postCategories[1].id.should.be.equal(2); postCategories[1].postName.should.be.equal("About Boeing"); postCategories[1].categoryName.should.be.equal("Airplanes"); diff --git a/test/github-issues/1014/issue-1014.ts b/test/github-issues/1014/issue-1014.ts index a94ea3999b2..787e9e878f2 100644 --- a/test/github-issues/1014/issue-1014.ts +++ b/test/github-issues/1014/issue-1014.ts @@ -3,7 +3,6 @@ import {closeTestingConnections, createTestingConnections, reloadTestingDatabase import {Connection} from "../../../src/connection/Connection"; import {TestEntity} from "./entity/TestEntity"; import {expect} from "chai"; -import {PromiseUtils} from "../../../src/util/PromiseUtils"; describe("github issues > #1014 Transaction doesn't rollback", () => { @@ -18,16 +17,14 @@ describe("github issues > #1014 Transaction doesn't rollback", () => { const testEntity = new TestEntity(); testEntity.name = "Hello Test"; - await connection.manager.save(testEntity); + await connection.manager.save(testEntity, { reload: true }); let error: any; try { - await connection.transaction(manager => { - return PromiseUtils.settle([ - manager.remove(TestEntity, { id: 1 }), - Promise.reject(new Error()), - new Promise((resolve, reject) => reject(new Error())), - ]); + await connection.transaction(async manager => { + await manager.remove(testEntity); + + throw new Error(); }); } catch (err) { error = err; } diff --git a/test/github-issues/1055/issue-1055.ts b/test/github-issues/1055/issue-1055.ts index 3b821c5eea4..ddccb85757f 100644 --- a/test/github-issues/1055/issue-1055.ts +++ b/test/github-issues/1055/issue-1055.ts @@ -4,7 +4,6 @@ import {Connection} from "../../../src/connection/Connection"; import {Parent} from "./entity/Parent"; import {Child} from "./entity/Child"; import {expect} from "chai"; -import {PromiseUtils} from "../../../src/util/PromiseUtils"; describe("github issues > #1055 ind with relations not working, correct syntax causes type error", () => { @@ -38,28 +37,6 @@ describe("github issues > #1055 ind with relations not working, correct syntax c expect(foundChild).not.to.be.undefined; }))); - - it("should be able to lookup from promise as well", () => Promise.all(connections.map(async connection => { - const manager = connection.manager; - - const parent = new Parent(); - parent.name = "Parent"; - await manager.save(parent); - - const loadedParent = await manager.findOne(Parent, 1); - expect(loadedParent).not.to.be.undefined; - - if (!loadedParent) return; - - const child = new Child(); - child.name = "Child"; - child.parent = Promise.resolve(loadedParent); - await manager.save(child); - - const foundChild = await manager.findOne(Child, { parent: PromiseUtils.create(loadedParent) }); - expect(foundChild).not.to.be.undefined; - }))); - it("should not have type errors with the primary key type", () => Promise.all(connections.map(async connection => { const manager = connection.manager; diff --git a/test/github-issues/1123/issue-1123.ts b/test/github-issues/1123/issue-1123.ts index da7ccabb4cb..a7aec5aad5b 100644 --- a/test/github-issues/1123/issue-1123.ts +++ b/test/github-issues/1123/issue-1123.ts @@ -54,7 +54,7 @@ describe("github issues > #1123 load relation eagerly by setting isEager propert const loadedPost = await connection.manager .createQueryBuilder("Post", "post") .where("post.id = :id", { id: 1 }) - .getOne(); + .getOne() as Post; loadedPost!.should.be.eql({ id: 1, diff --git a/test/github-issues/114/issue-114.ts b/test/github-issues/114/issue-114.ts index 0bf45946e2b..f3a322fb5c8 100644 --- a/test/github-issues/114/issue-114.ts +++ b/test/github-issues/114/issue-114.ts @@ -7,7 +7,7 @@ describe.skip("github issues > #114 Can not be parsed correctly the URL of pg.", let connection: Connection; before(() => { - connection = new Connection({ + connection = new Connection({ // Dummy Connection, won't be established type: "postgres", url: "postgres://test:test@localhost:5432/test", }); diff --git a/test/github-issues/1261/issue-1261.ts b/test/github-issues/1261/issue-1261.ts index 0a1431ab4d8..db374143e26 100644 --- a/test/github-issues/1261/issue-1261.ts +++ b/test/github-issues/1261/issue-1261.ts @@ -1,9 +1,7 @@ import "reflect-metadata"; import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils"; import {Connection} from "../../../src/connection/Connection"; -import {BaseEntity} from "../../../src/repository/BaseEntity"; import {Bar} from "./entity/Bar"; -import {PromiseUtils} from "../../../src"; describe("github issues > #1261 onDelete property on foreign key is not modified on sync", () => { @@ -13,10 +11,8 @@ describe("github issues > #1261 onDelete property on foreign key is not modified })); after(() => closeTestingConnections(connections)); - it("should modify onDelete property on foreign key on sync", () => PromiseUtils.runInSequence(connections, async connection => { - + it("should modify onDelete property on foreign key on sync", () => Promise.all(connections.map(async connection => { await connection.synchronize(); - BaseEntity.useConnection(connection); const queryRunner = connection.createQueryRunner(); let table = await queryRunner.getTable("bar"); @@ -31,6 +27,6 @@ describe("github issues > #1261 onDelete property on foreign key is not modified await queryRunner.release(); - })); + }))); }); diff --git a/test/github-issues/134/issue-134.ts b/test/github-issues/134/issue-134.ts index 8a364197fc0..1b4b8e1e2c3 100644 --- a/test/github-issues/134/issue-134.ts +++ b/test/github-issues/134/issue-134.ts @@ -9,7 +9,7 @@ describe("github issues > #134 Error TIME is converted to 'HH-mm' instead of 'HH let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "sqlite", "mssql", "postgres"] // Oracle does not support TIME data type. + enabledDrivers: ["mysql", "mariadb", "sqlite", "better-sqlite3", "mssql", "postgres"] // Oracle does not support TIME data type. })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1465/save-relation.ts b/test/github-issues/1465/issue-1465.ts similarity index 88% rename from test/github-issues/1465/save-relation.ts rename to test/github-issues/1465/issue-1465.ts index bd5ef090670..723f7c137f8 100644 --- a/test/github-issues/1465/save-relation.ts +++ b/test/github-issues/1465/issue-1465.ts @@ -5,12 +5,12 @@ import {closeTestingConnections, createTestingConnections, reloadTestingDatabase import {Account} from "./entity/Account"; import {AccountActivationToken} from "./entity/AccountActivationToken"; -describe("save child and parent entity", () => { +describe("github issues > #1465 save child and parent entity", () => { let connections: Connection[] = []; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "sqlite", "sqljs"] + enabledDrivers: ["mysql", "mariadb", "sqlite", "better-sqlite3", "sqljs"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1476/issue-1476.ts b/test/github-issues/1476/issue-1476.ts index b886071a408..7b73d5b468d 100644 --- a/test/github-issues/1476/issue-1476.ts +++ b/test/github-issues/1476/issue-1476.ts @@ -12,7 +12,7 @@ describe("github issues > #1476 subqueries", () => { let connections: Connection[] = []; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "sqlite", "sqljs"] + enabledDrivers: ["mysql", "mariadb", "sqlite", "better-sqlite3", "sqljs"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1545/entity/DataModel.ts b/test/github-issues/1545/entity/DataModel.ts index 204edd37456..67744624ebb 100644 --- a/test/github-issues/1545/entity/DataModel.ts +++ b/test/github-issues/1545/entity/DataModel.ts @@ -1,6 +1,6 @@ -import { Entity, ManyToOne, JoinColumn, Column } from "../../../../src/index"; -import { MainModel } from "./MainModel"; -import { ValidationModel } from "./ValidationModel"; +import {Column, Entity, JoinColumn, ManyToOne} from "../../../../src/index"; +import {MainModel} from "./MainModel"; +import {ValidationModel} from "./ValidationModel"; @Entity() export class DataModel { @@ -27,4 +27,4 @@ export class DataModel { default: false }) active: boolean; -} \ No newline at end of file +} diff --git a/test/github-issues/1805/issue-1805.ts b/test/github-issues/1805/issue-1805.ts index a1cc57b95a7..6d734cad031 100644 --- a/test/github-issues/1805/issue-1805.ts +++ b/test/github-issues/1805/issue-1805.ts @@ -2,7 +2,6 @@ import "reflect-metadata"; import {Connection} from "../../../src/connection/Connection"; import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils"; import {Account} from "./entity/Account"; -import {PromiseUtils} from "../../../src"; describe("github issues > #1805 bigint PK incorrectly returning as a number (expecting a string)", () => { @@ -17,16 +16,17 @@ describe("github issues > #1805 bigint PK incorrectly returning as a number (exp }); after(() => closeTestingConnections(connections)); - it("should return `bigint` column as string", () => PromiseUtils.runInSequence(connections, async connection => { - Account.useConnection(connection); - let account: Account|undefined; + it("should return `bigint` column as string", () => Promise.all(connections.map(async connection => { const bigIntId = "76561198016705746"; - account = new Account(); + const account = new Account(); account.id = bigIntId; - await account.save(); - account = await Account.findOne(bigIntId); - account!.id.should.be.equal(bigIntId); - })); + const accountRepository = await connection.getRepository(Account); + + await accountRepository.save(account); + + const loadedAccount = await accountRepository.findOne(bigIntId); + loadedAccount!.id.should.be.equal(bigIntId); + }))); }); diff --git a/test/github-issues/1898/issue-1898.ts b/test/github-issues/1898/issue-1898.ts index a591d7c1ae2..3eac4149625 100644 --- a/test/github-issues/1898/issue-1898.ts +++ b/test/github-issues/1898/issue-1898.ts @@ -9,7 +9,7 @@ describe("github issues > #1898 Simple JSON breaking in @next", () => { before(async () => { connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], schemaCreate: true, dropSchema: true }); diff --git a/test/github-issues/190/issue-190.ts b/test/github-issues/190/issue-190.ts index 665b3ab5c83..ab7f3970275 100644 --- a/test/github-issues/190/issue-190.ts +++ b/test/github-issues/190/issue-190.ts @@ -8,7 +8,7 @@ describe("github issues > #190 too many SQL variables when using setMaxResults i let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] // this issue only related to sqlite + enabledDrivers: ["sqlite", "better-sqlite3"] // this issue only related to sqlite })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1960/issue-1960.ts b/test/github-issues/1960/issue-1960.ts index 145a6b8981b..bf9c10c6400 100644 --- a/test/github-issues/1960/issue-1960.ts +++ b/test/github-issues/1960/issue-1960.ts @@ -8,8 +8,7 @@ describe.skip("github issues > #1960 Migration generator produces duplicated cha let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql"], - logging: true + enabledDrivers: ["mysql"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/1972/issue-1972.ts b/test/github-issues/1972/issue-1972.ts index 0b957b9732d..01391658c5a 100644 --- a/test/github-issues/1972/issue-1972.ts +++ b/test/github-issues/1972/issue-1972.ts @@ -10,6 +10,7 @@ describe("github issues > #1972 STI problem - empty columns", () => { before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ['mysql'] })); beforeEach(() => reloadTestingDatabases(connections)); @@ -31,7 +32,6 @@ describe("github issues > #1972 STI problem - empty columns", () => { // find user participant in the DB const result = await connection.manager.findOne(TournamentUserParticipant); - if (result) { assert(result.user instanceof User); } diff --git a/test/github-issues/1981/issue-1981.ts b/test/github-issues/1981/issue-1981.ts index 1134f42d756..3e653ec0cf1 100644 --- a/test/github-issues/1981/issue-1981.ts +++ b/test/github-issues/1981/issue-1981.ts @@ -7,7 +7,7 @@ describe("github issues > #1981 Boolean values not casted properly when used in let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/2005/issue-2005.ts b/test/github-issues/2005/issue-2005.ts index 52aaf9eb3e8..bb714b3158f 100644 --- a/test/github-issues/2005/issue-2005.ts +++ b/test/github-issues/2005/issue-2005.ts @@ -7,7 +7,7 @@ describe("github issues > #2005", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/2044/issue-2044.ts b/test/github-issues/2044/issue-2044.ts index 5960a5a2aab..f6aa3ce3b5a 100644 --- a/test/github-issues/2044/issue-2044.ts +++ b/test/github-issues/2044/issue-2044.ts @@ -31,7 +31,6 @@ describe("github issues > #2044 Should not double get embedded column value", () const photos = await connection.manager.find(Photo, { relations: ["user"] }); - // console.log(photos); const resultPhoto = photos[0]; @@ -39,4 +38,4 @@ describe("github issues > #2044 Should not double get embedded column value", () resultPhoto.user.id.should.be.eql(userId); }))); -}); \ No newline at end of file +}); diff --git a/test/github-issues/2065/entity/Post.ts b/test/github-issues/2065/entity/Post.ts new file mode 100644 index 00000000000..4d4a12218a6 --- /dev/null +++ b/test/github-issues/2065/entity/Post.ts @@ -0,0 +1,17 @@ +import { Entity, PrimaryColumn, Column } from "../../../../src"; + +@Entity() +export class Post { + + @PrimaryColumn() + id: number; + + @Column() + title: string; + + constructor(id: number, title: string) { + this.id = id; + this.title = title; + } + +} diff --git a/test/github-issues/2065/issue-2065.ts b/test/github-issues/2065/issue-2065.ts new file mode 100644 index 00000000000..0ae582edfd2 --- /dev/null +++ b/test/github-issues/2065/issue-2065.ts @@ -0,0 +1,23 @@ +import "reflect-metadata"; +import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Connection} from "../../../src/connection/Connection"; +import {Post} from "./entity/Post"; + +describe("github issues > #2065 TypeError: Cannot convert object to primitive value", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should save an entity created with Object.create(null)", () => Promise.all(connections.map(async connection => { + const post = Object.create(null) as Post; + post.id = 1; + post.title = "Hello Post"; + await connection.manager.save(Post, post); + }))); +}); diff --git a/test/github-issues/2096/issue-2096.ts b/test/github-issues/2096/issue-2096.ts index 05cbfc11bb0..768f48b17be 100644 --- a/test/github-issues/2096/issue-2096.ts +++ b/test/github-issues/2096/issue-2096.ts @@ -2,6 +2,7 @@ import "reflect-metadata"; import { expect } from "chai"; import { createConnection } from "../../../src"; import { getTypeOrmConfig } from "../../utils/test-utils"; +import {MysqlConnectionOptions} from "../../../src/driver/mysql/MysqlConnectionOptions"; describe("github issues > #2096 [mysql] Database name isn't read from url", () => { it("should be possible to define a database by connection url for mysql", async () => { @@ -9,10 +10,22 @@ describe("github issues > #2096 [mysql] Database name isn't read from url", () = // it is important to synchronize here, to trigger EntityMetadataValidator.validate // that previously threw the error where the database on the driver object was undefined - if (config.find(c => c.name === "mysql" && !c.skip)) { + const mysqlConfig: MysqlConnectionOptions = config.find(c => c.name === "mysql" && !c.skip) as MysqlConnectionOptions; + + if (mysqlConfig) { + const { + username, + password, + host, + port, + database + } = mysqlConfig; + + const url = `mysql://${username}:${password}@${host}:${port}/${database}` + const connection = await createConnection({ name: "#2096", - url: "mysql://root:admin@localhost:3306/test", + url, entities: [__dirname + "/entity/*{.js,.ts}"], synchronize: true, type: "mysql" diff --git a/test/github-issues/2131/entity/Post.ts b/test/github-issues/2131/entity/Post.ts new file mode 100644 index 00000000000..2004a609664 --- /dev/null +++ b/test/github-issues/2131/entity/Post.ts @@ -0,0 +1,12 @@ +import { PrimaryGeneratedColumn, Entity, Column } from "../../../../src"; + +@Entity() +export class Post { + + @PrimaryGeneratedColumn() + id: number | null; + + @Column() + title: string; + +} diff --git a/test/github-issues/2131/issue-2131.ts b/test/github-issues/2131/issue-2131.ts new file mode 100644 index 00000000000..6d67a4aaf81 --- /dev/null +++ b/test/github-issues/2131/issue-2131.ts @@ -0,0 +1,53 @@ +import "reflect-metadata"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import { Post } from "./entity/Post"; + +describe("github issues > #2131 InsertResult return the same primary key", () => { + let connections: Connection[]; + const posts: Post[] = [{ + id: null, + title: "Post 1", + }, { + id: null, + title: "Post 2", + }, { + id: null, + title: "Post 3", + }, { + id: null, + title: "Post 4", + }]; + + before( + async () => + (connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["sqlite", "mysql"], + })) + ); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should get correct insert ids for multiple entities inserted", () => + Promise.all( + connections.map(async (connection) => { + await connection + .createQueryBuilder() + .insert() + .into(Post) + .values(posts) + .execute(); + + expect(posts[0].id).to.equal(1); + expect(posts[1].id).to.equal(2); + expect(posts[2].id).to.equal(3); + expect(posts[3].id).to.equal(4); + }) + )); +}); diff --git a/test/github-issues/2199/issue-2199.ts b/test/github-issues/2199/issue-2199.ts index 3898c9f36fa..8a51691ac94 100644 --- a/test/github-issues/2199/issue-2199.ts +++ b/test/github-issues/2199/issue-2199.ts @@ -9,7 +9,7 @@ describe("github issues > #2199 - Inserting value for @PrimaryGeneratedColumn() let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["mysql", "mariadb", "sqlite"], + enabledDrivers: ["mysql", "mariadb", "sqlite", "better-sqlite3"], schemaCreate: true, dropSchema: true })); diff --git a/test/github-issues/2216/entity/Foo.ts b/test/github-issues/2216/entity/Foo.ts new file mode 100644 index 00000000000..d7e2038db88 --- /dev/null +++ b/test/github-issues/2216/entity/Foo.ts @@ -0,0 +1,16 @@ +import { + Column, + Entity, + PrimaryGeneratedColumn, +} from "../../../../src"; + +@Entity("foo") +export class Foo { + @PrimaryGeneratedColumn("uuid") public uuid: string; + + @Column({ type: "citext", nullable: false }) + public lowercaseval: string; + + @Column({ type: "citext", nullable: false }) + public lowercaseval2: string; +} diff --git a/test/github-issues/2216/issue-2216.ts b/test/github-issues/2216/issue-2216.ts new file mode 100644 index 00000000000..3cb4287d9fb --- /dev/null +++ b/test/github-issues/2216/issue-2216.ts @@ -0,0 +1,130 @@ +import sinon from "sinon"; +import {Connection} from "../../../src/connection/Connection"; +import {createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { EntityManager, QueryRunner, SimpleConsoleLogger } from "../../../src"; +import { Foo } from "./entity/Foo"; +import { expect } from "chai"; + +describe("github issues > #2216 - Ability to capture Postgres notifications in logger", () => { + let connections: Connection[]; + let queryRunner: QueryRunner; + let manager: EntityManager; + let logInfoStub: sinon.SinonStub; + + before(() => { + logInfoStub = sinon.stub(SimpleConsoleLogger.prototype, "log"); + }); + beforeEach(async () => { + await reloadTestingDatabases(connections); + }); + afterEach(() => logInfoStub.resetHistory()); + after(() => logInfoStub.restore()); + + describe("when logNotifications option is NOT enabled", () => { + before(async () => { + connections = await createTestingConnections({ + enabledDrivers: ["postgres"], + entities: [Foo], + logging: true, + createLogger: () => new SimpleConsoleLogger(), + }); + }); + after(() => closeTestingConnections(connections)); + + it("should NOT pass extension setup notices to client", async () => Promise.all(connections.map(async connection => { + sinon.assert.neverCalledWith( + logInfoStub, + "info", + `extension "uuid-ossp" already exists, skipping` + ); + sinon.assert.neverCalledWith( + logInfoStub, + "info", + `extension "citext" already exists, skipping` + ); + }))); + + it("should NOT pass manual notices to client", async () => Promise.all(connections.map(async connection => { + queryRunner = connection.createQueryRunner(); + await queryRunner.query(`DO $do$ BEGIN RAISE NOTICE 'this is a notice'; END $do$`); + sinon.assert.neverCalledWith( + logInfoStub, + "info", + "this is a notice" + ); + await queryRunner.release(); + }))); + + it("should NOT pass 'listen -> notify' messages to client", async () => Promise.all(connections.map(async connection => { + queryRunner = connection.createQueryRunner(); + await queryRunner.query("LISTEN foo;"); + await queryRunner.query("NOTIFY foo, 'bar!'"); + sinon.assert.neverCalledWith( + logInfoStub, + "info", + "Received NOTIFY on channel foo: bar!." + ); + await queryRunner.release(); + }))); + }); + + describe("when logNotifications option is enabled", () => { + before(async () => { + connections = await createTestingConnections({ + enabledDrivers: ["postgres"], + entities: [Foo], + logging: true, + createLogger: () => new SimpleConsoleLogger(), + driverSpecific: { logNotifications: true }, + }); + }); + after(() => closeTestingConnections(connections)); + + it("should pass extension setup notices to client", async () => Promise.all(connections.map(async connection => { + sinon.assert.calledWith( + logInfoStub, + "info", + `extension "uuid-ossp" already exists, skipping` + ); + sinon.assert.calledWith( + logInfoStub, + "info", + `extension "citext" already exists, skipping` + ); + }))); + + it("should pass manual notices to client", async () => Promise.all(connections.map(async connection => { + queryRunner = connection.createQueryRunner(); + await queryRunner.query(`DO $do$ BEGIN RAISE NOTICE 'this is a notice'; END $do$`); + sinon.assert.calledWith( + logInfoStub, + "info", + "this is a notice" + ); + await queryRunner.release(); + }))); + + it("should pass 'listen -> notify' messages to client", async () => Promise.all(connections.map(async connection => { + queryRunner = connection.createQueryRunner(); + await queryRunner.query("LISTEN foo;"); + await queryRunner.query("NOTIFY foo, 'bar!'"); + sinon.assert.calledWith( + logInfoStub, + "info", + "Received NOTIFY on channel foo: bar!." + ); + await queryRunner.release(); + }))); + + it("should not interfere with actual queries", async () => Promise.all(connections.map(async connection => { + manager = connection.manager; + await manager.save(Object.assign(new Foo(), { lowercaseval: "foo", lowercaseval2: "bar"})); + const loadedFoo = await manager.findOne(Foo); + expect(loadedFoo).not.to.be.undefined; + expect(loadedFoo).to.contain({ + lowercaseval: "foo", + lowercaseval2: "bar", + }); + }))); + }); +}); diff --git a/test/github-issues/2313/issue-2313.ts b/test/github-issues/2313/issue-2313.ts index fda6ff70337..b5b98f0eebd 100644 --- a/test/github-issues/2313/issue-2313.ts +++ b/test/github-issues/2313/issue-2313.ts @@ -1,6 +1,5 @@ import "reflect-metadata"; import {Connection} from "../../../src/connection/Connection"; -import {PromiseUtils} from "../../../src/util/PromiseUtils"; import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; import {Post} from "./entity/Post"; import {expect} from "chai"; @@ -16,35 +15,41 @@ describe("github issues > #2313 - BaseEntity has no findOneOrFail() method", () beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("should find the appropriate record when one exists", () => PromiseUtils.runInSequence(connections, async connection => { - Post.useConnection(connection); // change connection each time because of AR specifics + it("should find the appropriate record when one exists", async () => { + // These must run sequentially as we have the global context of the `Post` ActiveRecord class + for (const connection of connections) { + Post.useConnection(connection); // change connection each time because of AR specifics - const post1 = new Post(); - post1.data = 123; - await post1.save(); + const post1 = new Post(); + post1.data = 123; + await post1.save(); - const post2 = new Post(); - post2.data = 456; - await post2.save(); + const post2 = new Post(); + post2.data = 456; + await post2.save(); - const result1 = await Post.findOneOrFail(1); + const result1 = await Post.findOneOrFail(1); - result1.data.should.be.eql(123); + result1.data.should.be.eql(123); - const result2 = await Post.findOneOrFail(2); + const result2 = await Post.findOneOrFail(2); - result2.data.should.be.eql(456); - })); - - it("should throw no matching record exists", () => PromiseUtils.runInSequence(connections, async connection => { - Post.useConnection(connection); // change connection each time because of AR specifics - - try { - await Post.findOneOrFail(100); - expect.fail(); - } catch (e) { - e.should.be.instanceOf(EntityNotFoundError); + result2.data.should.be.eql(456); } - })); + }); + + it("should throw no matching record exists", async () => { + // These must run sequentially as we have the global context of the `Post` ActiveRecord class + for (const connection of connections) { + Post.useConnection(connection); // change connection each time because of AR specifics + + try { + await Post.findOneOrFail(100); + expect.fail(); + } catch (e) { + e.should.be.instanceOf(EntityNotFoundError); + } + } + }); }); diff --git a/test/github-issues/2434/entity/Item.ts b/test/github-issues/2434/entity/Item.ts new file mode 100644 index 00000000000..56ec4b15ced --- /dev/null +++ b/test/github-issues/2434/entity/Item.ts @@ -0,0 +1,18 @@ +import { CreateDateColumn, Column } from "../../../../src"; +import { PrimaryGeneratedColumn } from "../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import { Entity } from "../../../../src/decorator/entity/Entity"; + +@Entity("ITEM") +export class Item { + @PrimaryGeneratedColumn("uuid") + id: number; + + @CreateDateColumn() + date: Date; + + @Column() + itemName: string; + + @Column({nullable: true}) + itemDescription?: string; +} diff --git a/test/github-issues/2434/entity/Post.ts b/test/github-issues/2434/entity/Post.ts new file mode 100644 index 00000000000..1a433766d58 --- /dev/null +++ b/test/github-issues/2434/entity/Post.ts @@ -0,0 +1,16 @@ +import {Entity} from "../../../../src/decorator/entity/Entity"; +import {PrimaryColumn} from "../../../../src/decorator/columns/PrimaryColumn"; +import {Column} from "../../../../src/decorator/columns/Column"; + +@Entity({name: "POST"}) +export class Post { + + @PrimaryColumn() + id: number; + + @Column({ nullable: true }) + title?: string; + + @Column({name: "named_column", nullable: true}) + namedColumn?: string; +} diff --git a/test/github-issues/2434/issue-2434.ts b/test/github-issues/2434/issue-2434.ts new file mode 100644 index 00000000000..7a8a639c7bf --- /dev/null +++ b/test/github-issues/2434/issue-2434.ts @@ -0,0 +1,90 @@ +import "reflect-metadata"; +import {Connection} from "../../../src"; +import {Post} from './entity/Post'; +import {Item} from './entity/Item'; +import { closeTestingConnections, reloadTestingDatabases, createTestingConnections } from '../../utils/test-utils'; +import { expect } from 'chai'; + +describe("github issues > #2434 QueryBuilder insert for Oracle failed", () => { + let connections: Connection[] = []; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["oracle"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should insert multiple rows with QueryBuilder", () => Promise.all(connections.map(async connection => { + const result = await connection.createQueryBuilder() + .insert() + .into(Post) + .values([ + {id: 5, title: "title 1"}, + {id: 6}, + ]) + .execute(); + expect(result.raw).to.be.equal(2); + expect(result.identifiers).to.deep.equal([ + {id: 5}, + {id: 6}, + ]); + }))); + + it("should throw ORA-00001 error if constraint violated when inserting multiple rows", () => Promise.all(connections.map(async connection => { + try { + await connection.createQueryBuilder() + .insert() + .into(Post) + .values([ + {id: 6, title: "title 3"}, + {id: 6}, + ]) + .execute(); + } catch(err) { + expect(err.message).to.contain("ORA-00001"); + } + }))); + + it("should insert multiple rows of entity with generated columns with QueryBuilder", () => Promise.all(connections.map(async connection => { + const result = await connection.createQueryBuilder() + .insert() + .into(Item) + .values([ + {itemName: "item name 1"}, + {itemName: "item name 2"}, + ]) + .execute(); + expect(result.raw).to.be.equal(2); + const items = await connection.getRepository(Item).find(); + expect(items.length).to.be.equal(2); + }))); + + it("should still insert one row with QueryBuilder", () => Promise.all(connections.map(async connection => { + const result = await connection.createQueryBuilder() + .insert() + .into(Item) + .values({itemName: "item name 20"}) + .execute(); + expect(result.identifiers.length).to.be.equal(1); + const items = await connection.getRepository(Item).find(); + expect(items[0].itemName).to.be.equal("item name 20"); + }))); + + it("should still insert multiple rows with save", () => Promise.all(connections.map(async connection => { + const result = await connection.getRepository(Post).save([ + {id: 8, namedColumn: "test col 1"}, + {id: 9, title: "title id 9"}, + ]); + expect(result).to.deep.equal([ + {id: 8, title: null, namedColumn: "test col 1"}, + {id: 9, title: "title id 9", namedColumn: null}, + ]); + + }))); + + it("should still insert one row with save", () => Promise.all(connections.map(async connection => { + const result = await connection.getRepository(Post).save({id: 10}); + expect(result).to.deep.equal({id: 10, title: null, namedColumn: null}); + }))); + +}); diff --git a/test/github-issues/2518/issue-2518.ts b/test/github-issues/2518/issue-2518.ts index 538f1ee466f..8b451aba25f 100644 --- a/test/github-issues/2518/issue-2518.ts +++ b/test/github-issues/2518/issue-2518.ts @@ -12,7 +12,7 @@ describe("github issues > #2518 TreeRepository.findDescendantsTree does not load (connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], // data type text isn't compatible with oracle - enabledDrivers: ["postgres", "cockroachdb", "mariadb", "mssql", "mysql", "sqlite", "sqljs"] + enabledDrivers: ["postgres", "cockroachdb", "mariadb", "mssql", "mysql", "sqlite", "better-sqlite3", "sqljs"] })) ); diff --git a/test/github-issues/2703/memory-logger.ts b/test/github-issues/2703/memory-logger.ts index 3ec36711fc2..3221760f9f8 100644 --- a/test/github-issues/2703/memory-logger.ts +++ b/test/github-issues/2703/memory-logger.ts @@ -22,10 +22,6 @@ export class MemoryLogger implements Logger { log(level: "log" | "info" | "warn", message: any) {} - writeToConsole() { - this.queries.forEach(q => console.log(`query: ${q}`)); - } - clear() { this._queries = []; } diff --git a/test/github-issues/2733/issue-2733.ts b/test/github-issues/2733/issue-2733.ts index 02ff1074bf0..bb4c1eb6cb6 100644 --- a/test/github-issues/2733/issue-2733.ts +++ b/test/github-issues/2733/issue-2733.ts @@ -12,7 +12,7 @@ describe("github issues > #2733 should correctly handle function calls with uper entities: [__dirname + "/entity/MSSQLDummy{.js,.ts}"], schemaCreate: true, dropSchema: true, - enabledDrivers: ["mssql", "sqljs", "sqlite"], + enabledDrivers: ["mssql", "sqljs", "sqlite", "better-sqlite3"], }); await reloadTestingDatabases(connections); await Promise.all(connections.map(async connection => { diff --git a/test/github-issues/2943/entity/Test.ts b/test/github-issues/2943/entity/Test.ts new file mode 100644 index 00000000000..9055aa93b11 --- /dev/null +++ b/test/github-issues/2943/entity/Test.ts @@ -0,0 +1,22 @@ +import { Column, Entity, PrimaryColumn } from "../../../../src"; + +@Entity() +export class Test { + @PrimaryColumn() + id: number; + + @Column({ type: 'int', unsigned: true}) + uInt: number; + + @Column({ type: 'tinyint', unsigned: true}) + uTinyInt: number; + + @Column({ type: 'smallint', unsigned: true}) + uSmallInt: number; + + @Column({ type: 'mediumint', unsigned: true}) + uMediumInt: number; + + @Column({ type: 'bigint', unsigned: true}) + uBigInt: number; +} diff --git a/test/github-issues/2943/issue-2943.ts b/test/github-issues/2943/issue-2943.ts new file mode 100644 index 00000000000..5fee6e77542 --- /dev/null +++ b/test/github-issues/2943/issue-2943.ts @@ -0,0 +1,29 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import { Connection } from "../../../src"; +import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Test } from "./entity/Test"; + +describe("github issues > #2943 Inappropriate migration generated", () => { + + let connections: Connection[]; + + before(async () => { + connections = await createTestingConnections({ + enabledDrivers: ['mariadb', 'mysql'], + entities: [Test], + schemaCreate: true, + dropSchema: true + }); + }); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should not create migrations for unsigned numeric types with no specified width", () => + Promise.all(connections.map(async (connection) => { + const sqlInMemory = await connection.driver.createSchemaBuilder().log(); + + expect(sqlInMemory.upQueries).to.eql([]); + expect(sqlInMemory.downQueries).to.eql([]); + }))); +}); diff --git a/test/github-issues/2965/index.ts b/test/github-issues/2965/index.ts index 4ee0ecfd4b1..670ff3a462b 100644 --- a/test/github-issues/2965/index.ts +++ b/test/github-issues/2965/index.ts @@ -1,8 +1,6 @@ import "reflect-metadata"; - import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; import {Connection} from "../../../src"; - import {Person} from "./entity/person"; import {Note} from "./entity/note"; @@ -21,8 +19,8 @@ describe("github issues > #2965 Reuse preloaded lazy relations", () => { const repoPerson = connection.getRepository(Person); const repoNote = connection.getRepository(Note); - const personA = await repoPerson.create({ name: "personA" }); - const personB = await repoPerson.create({ name: "personB" }); + const personA = await repoPerson.create({ name: "personA" }); + const personB = await repoPerson.create({ name: "personB" }); await repoPerson.save([ personA, @@ -32,7 +30,6 @@ describe("github issues > #2965 Reuse preloaded lazy relations", () => { await repoNote.insert({ label: "note1", owner: personA }); await repoNote.insert({ label: "note2", owner: personB }); - const originalLoad: (...args: any[]) => Promise = connection.relationLoader.load; let loadCalledCounter = 0; connection.relationLoader.load = (...args: any[]): Promise => { diff --git a/test/github-issues/2984/entity/commit/note.ts b/test/github-issues/2984/entity/commit/note.ts new file mode 100755 index 00000000000..555c1523dbf --- /dev/null +++ b/test/github-issues/2984/entity/commit/note.ts @@ -0,0 +1,9 @@ +import {Entity, PrimaryGeneratedColumn} from "../../../../../src"; + +@Entity({name: "commitNote"}) +export class Note { + + @PrimaryGeneratedColumn() + public id: number; + +} diff --git a/test/github-issues/2984/entity/issue/note.ts b/test/github-issues/2984/entity/issue/note.ts new file mode 100755 index 00000000000..8b0368f0a18 --- /dev/null +++ b/test/github-issues/2984/entity/issue/note.ts @@ -0,0 +1,10 @@ +import {Entity, PrimaryGeneratedColumn, TableInheritance} from "../../../../../src"; + +@Entity({name: "issueNote"}) +@TableInheritance({column: {type: "varchar", name: "type"}}) +export class Note { + + @PrimaryGeneratedColumn() + public id: number; + +} diff --git a/test/github-issues/2984/entity/issue/ownernote.ts b/test/github-issues/2984/entity/issue/ownernote.ts new file mode 100644 index 00000000000..a76663d4c22 --- /dev/null +++ b/test/github-issues/2984/entity/issue/ownernote.ts @@ -0,0 +1,10 @@ +import {ChildEntity, Column} from "../../../../../src"; +import {Note} from "./note"; + +@ChildEntity() +export class OwnerNote extends Note { + + @Column() + public owner: string; + +} diff --git a/test/github-issues/2984/entity/wiki/note.ts b/test/github-issues/2984/entity/wiki/note.ts new file mode 100755 index 00000000000..b678caa5c06 --- /dev/null +++ b/test/github-issues/2984/entity/wiki/note.ts @@ -0,0 +1,10 @@ +import {Entity, PrimaryGeneratedColumn, TableInheritance} from "../../../../../src"; + +@Entity({name: "wikiNote"}) +@TableInheritance({column: {type: "varchar", name: "type"}}) +export class Note { + + @PrimaryGeneratedColumn() + public id: number; + +} diff --git a/test/github-issues/2984/entity/wiki/ownernote.ts b/test/github-issues/2984/entity/wiki/ownernote.ts new file mode 100644 index 00000000000..a76663d4c22 --- /dev/null +++ b/test/github-issues/2984/entity/wiki/ownernote.ts @@ -0,0 +1,10 @@ +import {ChildEntity, Column} from "../../../../../src"; +import {Note} from "./note"; + +@ChildEntity() +export class OwnerNote extends Note { + + @Column() + public owner: string; + +} diff --git a/test/github-issues/2984/issue-2984.ts b/test/github-issues/2984/issue-2984.ts new file mode 100644 index 00000000000..9e8b18acc15 --- /dev/null +++ b/test/github-issues/2984/issue-2984.ts @@ -0,0 +1,21 @@ +import "reflect-metadata"; + +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Connection} from "../../../src"; + +describe("github issues > #2984 Discriminator conflict reported even for non-inherited tables", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/**/*{.js,.ts}"], + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should load entities even with the same discriminator", () => Promise.all(connections.map(async connection => { + connection.entityMetadatas.should.have.length(5); + connection.entityMetadatas.forEach(metadata => + metadata.discriminatorValue!.should.be.oneOf(["Note", "OwnerNote"])); + }))); + +}); diff --git a/test/github-issues/300/issue-300.ts b/test/github-issues/300/issue-300.ts index a9b89927c4b..2e9c986372d 100644 --- a/test/github-issues/300/issue-300.ts +++ b/test/github-issues/300/issue-300.ts @@ -4,7 +4,7 @@ import {Connection} from "../../../src/connection/Connection"; import {expect} from "chai"; import {Race} from "./entity/Race"; -describe("github issues > support of embeddeds that are not set", () => { +describe("github issues > #300 support of embeddeds that are not set", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ diff --git a/test/github-issues/306/issue-306.ts b/test/github-issues/306/issue-306.ts index 1c570642a11..6900c03c88c 100644 --- a/test/github-issues/306/issue-306.ts +++ b/test/github-issues/306/issue-306.ts @@ -5,7 +5,7 @@ import {expect} from "chai"; import {Race} from "./entity/Race"; import {Duration} from "./entity/Duration"; -describe("github issues > embeddeds with custom column name don't work", () => { +describe("github issues > #306 embeddeds with custom column name don't work", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ diff --git a/test/github-issues/3111/entity/Test.ts b/test/github-issues/3111/entity/Test.ts new file mode 100644 index 00000000000..dfcf3d22775 --- /dev/null +++ b/test/github-issues/3111/entity/Test.ts @@ -0,0 +1,14 @@ +import {Column} from "../../../../src/decorator/columns/Column"; +import {Entity} from "../../../../src/decorator/entity/Entity"; +import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn"; + +export const DEFAULT_VALUE = "default-value"; + +@Entity() +export class Test { + @PrimaryGeneratedColumn() + id: number; + + @Column({default: DEFAULT_VALUE}) + value: string; +} diff --git a/test/github-issues/3111/issue-3111.ts b/test/github-issues/3111/issue-3111.ts new file mode 100644 index 00000000000..614525200f6 --- /dev/null +++ b/test/github-issues/3111/issue-3111.ts @@ -0,0 +1,30 @@ +import "reflect-metadata"; +import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Connection} from "../../../src/connection/Connection"; +import {expect} from "chai"; +import {InsertValuesMissingError} from "../../../src/error/InsertValuesMissingError"; +import {Test, DEFAULT_VALUE} from "./entity/Test"; + +describe("github issues > #3111 Inserting with query builder attempts to insert a default row when values is empty array", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should not insert with default values on .values([])", () => Promise.all(connections.map(async connection => { + const repo = connection.getRepository(Test); + await repo.createQueryBuilder().insert().values([]).execute(); + const rowsWithDefaultValue = await repo.find({ where: {value: DEFAULT_VALUE}}); + expect(rowsWithDefaultValue).to.have.lengthOf(0); + }))); + + it("should still error on missing .values()", () => Promise.all(connections.map(async connection => { + const repo = connection.getRepository(Test); + await repo.createQueryBuilder().insert().execute().should.be.rejectedWith(InsertValuesMissingError); + }))); +}); diff --git a/test/github-issues/3118/issue-3118.ts b/test/github-issues/3118/issue-3118.ts index 001e358e691..de30cc7aa9e 100644 --- a/test/github-issues/3118/issue-3118.ts +++ b/test/github-issues/3118/issue-3118.ts @@ -18,6 +18,7 @@ describe("github issues > #3118 shorten alias names (for RDBMS with a limit) whe let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["mysql", "postgres", "cockroachdb", "sap", "mariadb", "mssql"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/3158/issue-3158.ts b/test/github-issues/3158/issue-3158.ts index d074678c769..89768cb3382 100644 --- a/test/github-issues/3158/issue-3158.ts +++ b/test/github-issues/3158/issue-3158.ts @@ -2,21 +2,23 @@ import "reflect-metadata"; import { createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; import { Connection } from "../../../src/connection/Connection"; import { expect } from "chai"; -it("github issues > #3158 Cannot run sync a second time", async () => { + +describe("github issues > #3158 Cannot run sync a second time", async () => { let connections: Connection[]; - connections = await createTestingConnections({ - entities: [__dirname + "/entity/*{.js,.ts}"], - schemaCreate: true, - dropSchema: true, - enabledDrivers: ["mysql", "mariadb", "oracle", "mssql", "sqljs", "sqlite"], - // todo(AlexMesser): check why tests are failing under postgres driver - }); - await reloadTestingDatabases(connections); - await Promise.all(connections.map(async connection => { - const schemaBuilder = connection.driver.createSchemaBuilder(); - const syncQueries = await schemaBuilder.log(); - expect(syncQueries.downQueries).to.be.eql([]); - expect(syncQueries.upQueries).to.be.eql([]); - })); - await closeTestingConnections(connections); + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["mysql", "mariadb", "oracle", "mssql", "sqljs", "sqlite", "better-sqlite3"], + // todo(AlexMesser): check why tests are failing under postgres driver + })); + beforeEach(async () => await reloadTestingDatabases(connections)); + after(async () => await closeTestingConnections(connections)); + + it("can recognize model changes", () => Promise.all(connections.map(async connection => { + const schemaBuilder = connection.driver.createSchemaBuilder(); + const syncQueries = await schemaBuilder.log(); + expect(syncQueries.downQueries).to.be.eql([]); + expect(syncQueries.upQueries).to.be.eql([]); + }))); }); diff --git a/test/github-issues/341/issue-341.ts b/test/github-issues/341/issue-341.ts index 9510745533a..7776162e13d 100644 --- a/test/github-issues/341/issue-341.ts +++ b/test/github-issues/341/issue-341.ts @@ -5,7 +5,7 @@ import {Post} from "./entity/Post"; import {Category} from "./entity/Category"; import {expect} from "chai"; -describe("github issues > OneToOne relation with referencedColumnName does not work", () => { +describe("github issues > #341 OneToOne relation with referencedColumnName does not work", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ diff --git a/test/github-issues/3422/issue-3422.ts b/test/github-issues/3422/issue-3422.ts index 01ebf26034a..3687e994f91 100644 --- a/test/github-issues/3422/issue-3422.ts +++ b/test/github-issues/3422/issue-3422.ts @@ -2,7 +2,6 @@ import "reflect-metadata"; import {Connection} from "../../../src/connection/Connection"; import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils"; import {User} from "./entity/User"; -import {PromiseUtils} from "../../../src"; describe("github issues > #3422 cannot save to nested-tree table if schema is used in postgres", () => { @@ -16,7 +15,7 @@ describe("github issues > #3422 cannot save to nested-tree table if schema is us }); after(() => closeTestingConnections(connections)); - it("should not fail when using schema and nested-tree", () => PromiseUtils.runInSequence(connections, async connection => { + it("should not fail when using schema and nested-tree", () => Promise.all(connections.map(async connection => { await connection.query("CREATE SCHEMA IF NOT EXISTS admin"); await connection.synchronize(); const parent = new User(); @@ -28,5 +27,5 @@ describe("github issues > #3422 cannot save to nested-tree table if schema is us const user = await connection.manager.getRepository(User).findOne(child.id, {relations: ["manager"]}); user!.id.should.be.equal(child.id); user!.manager.id.should.be.equal(parent.id); - })); + }))); }); diff --git a/test/github-issues/345/issue-345.ts b/test/github-issues/345/issue-345.ts index bf3054b5766..1c987370e81 100644 --- a/test/github-issues/345/issue-345.ts +++ b/test/github-issues/345/issue-345.ts @@ -5,7 +5,7 @@ import {Post} from "./entity/Post"; import {Category} from "./entity/Category"; import {expect} from "chai"; -describe("github issues > Join query on ManyToMany relations not working", () => { +describe("github issues > #345 Join query on ManyToMany relations not working", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ diff --git a/test/github-issues/3496/issue-3496.ts b/test/github-issues/3496/issue-3496.ts index 8bcea0464db..f96eee3f1c6 100644 --- a/test/github-issues/3496/issue-3496.ts +++ b/test/github-issues/3496/issue-3496.ts @@ -2,7 +2,6 @@ import "reflect-metadata"; import {Connection} from "../../../src"; import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils"; import {Post} from "./entity/Post"; -import {PromiseUtils} from "../../../src"; describe("github issues > #3496 jsonb comparison doesn't work", () => { @@ -16,7 +15,7 @@ describe("github issues > #3496 jsonb comparison doesn't work", () => { }); after(() => closeTestingConnections(connections)); - it("the entity should not be updated a second time", () => PromiseUtils.runInSequence(connections, async connection => { + it("the entity should not be updated a second time", () => Promise.all(connections.map(async connection => { await connection.synchronize(); const repository = connection.getRepository(Post); @@ -34,5 +33,5 @@ describe("github issues > #3496 jsonb comparison doesn't work", () => { ); savedPost1!.version.should.be.equal(savedPost2!.version); - })); + }))); }); diff --git a/test/github-issues/3534/issue-3534.ts b/test/github-issues/3534/issue-3534.ts index 247bcb3d8ff..bcf25c022be 100644 --- a/test/github-issues/3534/issue-3534.ts +++ b/test/github-issues/3534/issue-3534.ts @@ -1,6 +1,6 @@ import "reflect-metadata"; import {expect} from "chai"; -import { Connection, PromiseUtils } from "../../../src"; +import { Connection } from "../../../src"; import { Foo } from "./entity/Foo"; import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; @@ -14,7 +14,7 @@ describe("github issues > #3534: store regexp", () => { beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); - it("allows entities with regexp columns", () => PromiseUtils.runInSequence(connections, async connection => { + it("allows entities with regexp columns", () => Promise.all(connections.map(async connection => { const repository = connection.getRepository(Foo); const foo = new Foo(); @@ -25,6 +25,6 @@ describe("github issues > #3534: store regexp", () => { const storedFoo = await repository.findOneOrFail(foo.id); expect(storedFoo.bar).to.instanceOf(RegExp); expect(storedFoo.bar.toString()).to.eq(/foo/i.toString()); - })); + }))); }); diff --git a/test/github-issues/3536/issue-3536.ts b/test/github-issues/3536/issue-3536.ts index 2a284ddbde4..39d454f768b 100644 --- a/test/github-issues/3536/issue-3536.ts +++ b/test/github-issues/3536/issue-3536.ts @@ -1,7 +1,6 @@ import "reflect-metadata"; import { Connection } from "../../../src/connection/Connection"; import { closeTestingConnections, createTestingConnections } from "../../utils/test-utils"; -import { PromiseUtils } from "../../../src"; import { Roles } from "./entity/Roles"; describe("github issues > #3536 Sync only works once for enums on entities with capital letters in entity name", () => { @@ -18,8 +17,8 @@ describe("github issues > #3536 Sync only works once for enums on entities with }); after(() => closeTestingConnections(connections)); - it("should run without throw error", () => PromiseUtils.runInSequence(connections, async connection => { + it("should run without throw error", () => Promise.all(connections.map(async connection => { await connection.synchronize(); await connection.synchronize(); - })); + }))); }); diff --git a/test/github-issues/3551/issue-3551.ts b/test/github-issues/3551/issue-3551.ts index 2e89dd812ff..8d5bc5bdcc4 100644 --- a/test/github-issues/3551/issue-3551.ts +++ b/test/github-issues/3551/issue-3551.ts @@ -1,7 +1,6 @@ import "reflect-metadata"; import {Connection} from "../../../src/connection/Connection"; import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils"; -import {PromiseUtils} from "../../../src"; import { Book } from "./entity/Book"; describe("github issues > #3551 array of embedded documents through multiple levels are not handled", () => { @@ -16,7 +15,7 @@ describe("github issues > #3551 array of embedded documents through multiple lev }); after(() => closeTestingConnections(connections)); - it("should return entity with all these embedded documents", () => PromiseUtils.runInSequence(connections, async connection => { + it("should return entity with all these embedded documents", () => Promise.all(connections.map(async connection => { const bookInput = { title: "Book 1", chapters: [ @@ -59,5 +58,5 @@ describe("github issues > #3551 array of embedded documents through multiple lev book!.chapters[1].pages.should.have.lengthOf(2); book!.chapters[1].pages[0].number.should.be.equal(bookInput.chapters[1].pages[0].number); book!.chapters[1].pages[1].number.should.be.equal(bookInput.chapters[1].pages[1].number); - })); -}); \ No newline at end of file + }))); +}); diff --git a/test/github-issues/3588/issue-3588.ts b/test/github-issues/3588/issue-3588.ts index ba45afecd32..cffa0598c43 100644 --- a/test/github-issues/3588/issue-3588.ts +++ b/test/github-issues/3588/issue-3588.ts @@ -2,20 +2,22 @@ import "reflect-metadata"; import { createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; import { Connection } from "../../../src/connection/Connection"; import { expect } from "chai"; -it("github issues > #3588 Migration:generate issue with onUpdate using mysql 8.0", async () => { + +describe("github issues > #3588 Migration:generate issue with onUpdate using mysql 8.0", async () => { let connections: Connection[]; - connections = await createTestingConnections({ - entities: [__dirname + "/entity/*{.js,.ts}"], - schemaCreate: true, - dropSchema: true, - enabledDrivers: ["mysql"], - }); - await reloadTestingDatabases(connections); - await Promise.all(connections.map(async connection => { - const schemaBuilder = connection.driver.createSchemaBuilder(); - const syncQueries = await schemaBuilder.log(); - expect(syncQueries.downQueries).to.be.eql([]); - expect(syncQueries.upQueries).to.be.eql([]); - })); - await closeTestingConnections(connections); + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["mysql"], + })); + beforeEach(async () => await reloadTestingDatabases(connections)); + after(async () => await closeTestingConnections(connections)); + + it("can recognize model changes", () => Promise.all(connections.map(async connection => { + const schemaBuilder = connection.driver.createSchemaBuilder(); + const syncQueries = await schemaBuilder.log(); + expect(syncQueries.downQueries).to.be.eql([]); + expect(syncQueries.upQueries).to.be.eql([]); + }))); }); diff --git a/test/github-issues/3685/entity/user.ts b/test/github-issues/3685/entity/user.ts new file mode 100644 index 00000000000..6c6c64daa56 --- /dev/null +++ b/test/github-issues/3685/entity/user.ts @@ -0,0 +1,15 @@ +import { Column, Entity, PrimaryGeneratedColumn } from "../../../../src"; + +@Entity() +export class User { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + firstName: string; + + @Column() + lastName: string; + +} diff --git a/test/github-issues/3685/issue-3685.ts b/test/github-issues/3685/issue-3685.ts new file mode 100644 index 00000000000..70fd13a681e --- /dev/null +++ b/test/github-issues/3685/issue-3685.ts @@ -0,0 +1,62 @@ +import {expect} from "chai"; +import {Brackets, Connection} from "../../../src"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {User} from "./entity/user"; + +describe("github issues > #3685 Brackets syntax failed when use where with object literal", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + dropSchema: true, + schemaCreate: true, + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => connections && closeTestingConnections(connections)); + + it("should accept objects in .where method (github issue #3685)", () => Promise.all(connections.map(async connection => { + + await Promise.all([ + connection.manager.save(Object.assign(new User(), { + firstName: "Jean", + lastName: "Doe", + })), + + connection.manager.save(Object.assign(new User(), { + firstName: "John", + lastName: "Doe", + })), + + connection.manager.save(Object.assign(new User(), { + firstName: "John", + lastName: "Dupont", + })), + + connection.manager.save(Object.assign(new User(), { + firstName: "Fred", + lastName: "Doe", + })) + ]); + + const qb = connection.createQueryBuilder(User, "u") + .where(new Brackets(qb => { + qb.where({ firstName: "John" }) + .orWhere("u.firstName = :firstName", { firstName: "Jean" }); + })) + .andWhere("u.lastName = :lastName", { lastName: "Doe" }) + .orderBy({ + "u.firstName": "ASC", + "u.lastName": "ASC", + }); + + const results = await qb.getMany(); + + expect(results.length).to.equal(2); + + expect(results[0].firstName).to.equal("Jean"); + expect(results[0].lastName).to.equal("Doe"); + + expect(results[1].firstName).to.equal("John"); + expect(results[1].lastName).to.equal("Doe"); + }))); +}); diff --git a/test/github-issues/3949/issue-3949.ts b/test/github-issues/3949/issue-3949.ts index 1e89826a45c..a2bf94e9171 100644 --- a/test/github-issues/3949/issue-3949.ts +++ b/test/github-issues/3949/issue-3949.ts @@ -11,7 +11,7 @@ describe("github issues > #3949 sqlite date hydration is susceptible to corrupti entities: [__dirname + "/entity/*{.js,.ts}"], schemaCreate: true, dropSchema: true, - enabledDrivers: ["sqlite", "sqljs"], + enabledDrivers: ["sqlite", "better-sqlite3", "sqljs"], }); }); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/github-issues/4096/issue-4096.ts b/test/github-issues/4096/issue-4096.ts index 3685d0d17a3..40f200f209d 100644 --- a/test/github-issues/4096/issue-4096.ts +++ b/test/github-issues/4096/issue-4096.ts @@ -9,7 +9,7 @@ describe("github issues > #4096 SQLite support for orUpdate", () => { before(async () => connections = await createTestingConnections({ entities: [User], - enabledDrivers: ["sqlite"], + enabledDrivers: ["sqlite", "better-sqlite3"], schemaCreate: true, dropSchema: true, })); diff --git a/test/github-issues/4147/issue-4147.ts b/test/github-issues/4147/issue-4147.ts index d57a645e72a..43b52aa1527 100644 --- a/test/github-issues/4147/issue-4147.ts +++ b/test/github-issues/4147/issue-4147.ts @@ -14,7 +14,7 @@ describe("github issues > #4147 `SQLITE_ERROR: near \"-\": syntax error` when us (connections = await createTestingConnections({ entities: [new EntitySchema(PostSchema)], dropSchema: true, - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })) ); beforeEach(() => reloadTestingDatabases(connections)); diff --git a/test/github-issues/4156/issue-4156.ts b/test/github-issues/4156/issue-4156.ts index e6ada524eca..26aa21815e8 100644 --- a/test/github-issues/4156/issue-4156.ts +++ b/test/github-issues/4156/issue-4156.ts @@ -41,7 +41,7 @@ describe("github issues > #4156 QueryExpressionMap doesn't clone all values corr const [loadedPost1, loadedPost2] = await Promise.all([ qb.clone().where({ id: 1 }).getOne(), qb.clone().where({ id: In([1]) }).getOne(), - ]); + ]) as Post[]; loadedPost1!.should.be.eql({ id: 1, @@ -67,7 +67,7 @@ describe("github issues > #4156 QueryExpressionMap doesn't clone all values corr const [loadedPost1, loadedPost2] = await Promise.all([ qb.clone().getOne(), qb.clone().getOne(), - ]); + ]) as Post[]; loadedPost1!.should.be.eql({ id: 1, diff --git a/test/github-issues/4410/entity/Username.ts b/test/github-issues/4410/entity/Username.ts new file mode 100644 index 00000000000..ad0ee27175c --- /dev/null +++ b/test/github-issues/4410/entity/Username.ts @@ -0,0 +1,12 @@ +import { Column } from "../../../../src/decorator/columns/Column"; +import { PrimaryColumn } from "../../../../src/decorator/columns/PrimaryColumn"; +import { Entity } from "../../../../src/decorator/entity/Entity"; + +@Entity() +export class Username { + @PrimaryColumn() + username: string; + + @Column() + email: string; +} diff --git a/test/github-issues/4410/issue-4410.ts b/test/github-issues/4410/issue-4410.ts new file mode 100644 index 00000000000..cf23b9856ff --- /dev/null +++ b/test/github-issues/4410/issue-4410.ts @@ -0,0 +1,87 @@ +import appRootPath from "app-root-path"; +import sinon from "sinon"; +import { Connection, FileLogger } from "../../../src"; +import { createTestingConnections, reloadTestingDatabases, closeTestingConnections, TestingOptions } from "../../utils/test-utils"; +import { Username } from "./entity/Username"; +import { PlatformTools } from "../../../src/platform/PlatformTools"; + +describe("github issues > #4410 allow custom filepath for FileLogger", () => { + let connections: Connection[]; + let stub: sinon.SinonStub; + + const testingOptions: TestingOptions = { + entities: [Username], + schemaCreate: true, + dropSchema: true, + }; + + before(() => stub = sinon.stub(PlatformTools, "appendFileSync")); + beforeEach(() => reloadTestingDatabases(connections)); + afterEach(async () => { + stub.resetHistory(); await closeTestingConnections(connections); + }); + + describe("when no option is passed", () => { + before(async () => { + connections = await createTestingConnections({ + ...testingOptions, + createLogger: () => new FileLogger("all"), + }); + }); + it("writes to the base path", async () => + Promise.all(connections.map(async (connection) => { + const testQuery = `SELECT COUNT(*) FROM ${connection.driver.escape('username')}`; + + await connection.query(testQuery); + sinon.assert.calledWith( + stub, + appRootPath.path + "/ormlogs.log", + sinon.match(testQuery) + ); + }))); + }); + + describe("when logPath option is passed as a file", () => { + before(async () => { + connections = await createTestingConnections({ + ...testingOptions, + createLogger: () => new FileLogger("all", { + logPath: "test.log" + }), + }); + }); + it("writes to the given filename", async () => + Promise.all(connections.map(async (connection) => { + const testQuery = `SELECT COUNT(*) FROM ${connection.driver.escape('username')}`; + + await connection.query(testQuery); + sinon.assert.calledWith( + stub, + appRootPath.path + "/test.log", + sinon.match(testQuery) + ); + }))); + }); + + describe("when logPath option is passed as a nested path", () => { + before(async () => { + connections = await createTestingConnections({ + ...testingOptions, + createLogger: () => new FileLogger("all", { + logPath: "./test/test.log" + }), + }); + }); + it("writes to the given path", () => + Promise.all(connections.map(async (connection) => { + const testQuery = `SELECT COUNT(*) FROM ${connection.driver.escape('username')}`; + + await connection.query(testQuery); + sinon.assert.calledWith( + stub, + appRootPath.path + "/test/test.log", + sinon.match(testQuery) + ); + }))); + }); +}); diff --git a/test/github-issues/4415/entity/Post.ts b/test/github-issues/4415/entity/Post.ts new file mode 100644 index 00000000000..94e6f92ea52 --- /dev/null +++ b/test/github-issues/4415/entity/Post.ts @@ -0,0 +1,14 @@ +import { PrimaryGeneratedColumn, Entity, Column, CreateDateColumn } from "../../../../src"; + +@Entity() +export class Post { + + @PrimaryGeneratedColumn() + id?: number; + + @Column() + title: string; + + @CreateDateColumn() + readonly createdAt?: Date; +} diff --git a/test/github-issues/4415/entity/Username.ts b/test/github-issues/4415/entity/Username.ts new file mode 100644 index 00000000000..b1f7b555527 --- /dev/null +++ b/test/github-issues/4415/entity/Username.ts @@ -0,0 +1,15 @@ +import { Column } from "../../../../src/decorator/columns/Column"; +import { PrimaryColumn } from "../../../../src/decorator/columns/PrimaryColumn"; +import { Entity } from "../../../../src/decorator/entity/Entity"; + +@Entity() +export class Username { + @PrimaryColumn() + username: string; + + @Column() + email: string; + + @Column() + something: string; +} diff --git a/test/github-issues/4415/issue-4415.ts b/test/github-issues/4415/issue-4415.ts new file mode 100644 index 00000000000..5d46f6400b4 --- /dev/null +++ b/test/github-issues/4415/issue-4415.ts @@ -0,0 +1,108 @@ +import sinon from "sinon"; +import { ConnectionOptions, ConnectionOptionsReader, DatabaseType } from "../../../src"; +import { setupTestingConnections, createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Username } from "./entity/Username"; +import { CommandUtils } from "../../../src/commands/CommandUtils"; +import { MigrationGenerateCommand } from "../../../src/commands/MigrationGenerateCommand"; +import { Post } from "./entity/Post"; +import { resultsTemplates } from "./results-templates"; + +describe("github issues > #4415 allow beautify generated migrations", () => { + let connectionOptions: ConnectionOptions[]; + let createFileStub: sinon.SinonStub; + let getConnectionOptionsStub: sinon.SinonStub; + let migrationGenerateCommand: MigrationGenerateCommand; + let connectionOptionsReader: ConnectionOptionsReader; + let baseConnectionOptions: ConnectionOptions; + + const enabledDrivers = [ + "postgres", + "mssql", + "mysql", + "mariadb", + "sqlite", + "better-sqlite3", + "oracle", + "cockroachdb" + ] as DatabaseType[]; + + // simulate args: `npm run typeorm migration:run -- -n test-migration -d test-directory` + const testHandlerArgs = (options: Record) => ({ + "$0": "test", + "_": ["test"], + "name": "test-migration", + "dir": "test-directory", + ...options + }); + + before(async () => { + // clean out db from any prior tests in case previous state impacts the generated migrations + const connections = await createTestingConnections({ + entities: [], + enabledDrivers + }); + await reloadTestingDatabases(connections); + await closeTestingConnections(connections); + + connectionOptions = setupTestingConnections({ + entities: [Username, Post], + enabledDrivers + }); + connectionOptionsReader = new ConnectionOptionsReader(); + migrationGenerateCommand = new MigrationGenerateCommand(); + createFileStub = sinon.stub(CommandUtils, "createFile"); + }); + after(() => createFileStub.restore()); + + it("writes regular migration file when no option is passed", async () => { + for (const connectionOption of connectionOptions) { + createFileStub.resetHistory(); + baseConnectionOptions = await connectionOptionsReader.get(connectionOption.name as string); + getConnectionOptionsStub = sinon.stub(ConnectionOptionsReader.prototype, "get").resolves({ + ...baseConnectionOptions, + entities: [Username, Post] + }); + + await migrationGenerateCommand.handler(testHandlerArgs({ + "connection": connectionOption.name + })); + + // compare against control test strings in results-templates.ts + for (const control of resultsTemplates[connectionOption.type as string].control) { + sinon.assert.calledWith( + createFileStub, + sinon.match(/test-directory.*test-migration.ts/), + sinon.match(control) + ); + } + + getConnectionOptionsStub.restore(); + } + }); + + it("writes pretty printed file when pretty option is passed", async () => { + for (const connectionOption of connectionOptions) { + createFileStub.resetHistory(); + baseConnectionOptions = await connectionOptionsReader.get(connectionOption.name as string); + getConnectionOptionsStub = sinon.stub(ConnectionOptionsReader.prototype, "get").resolves({ + ...baseConnectionOptions, + entities: [Username, Post] + }); + + await migrationGenerateCommand.handler(testHandlerArgs({ + "connection": connectionOption.name, + "pretty": true + })); + + // compare against "pretty" test strings in results-templates.ts + for (const pretty of resultsTemplates[connectionOption.type as string].pretty) { + sinon.assert.calledWith( + createFileStub, + sinon.match(/test-directory.*test-migration.ts/), + sinon.match(pretty) + ); + } + getConnectionOptionsStub.restore(); + } + }); +}); diff --git a/test/github-issues/4415/results-templates.ts b/test/github-issues/4415/results-templates.ts new file mode 100644 index 00000000000..ce7c7d63ee6 --- /dev/null +++ b/test/github-issues/4415/results-templates.ts @@ -0,0 +1,157 @@ +export const resultsTemplates: Record = { + + postgres: { + control: [ + `CREATE TABLE "post" ("id" SERIAL NOT NULL, "title" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id"))`, + `CREATE TABLE "username" ("username" character varying NOT NULL, "email" character varying NOT NULL, "something" character varying NOT NULL, CONSTRAINT "PK_b39ad32e514b17e90c93988888a" PRIMARY KEY ("username"))` + ], + pretty: [ + ` + CREATE TABLE "post" ( + "id" SERIAL NOT NULL, + "title" character varying NOT NULL, + "createdAt" TIMESTAMP NOT NULL DEFAULT now(), + CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id") + ) + `, + ` + CREATE TABLE "username" ( + "username" character varying NOT NULL, + "email" character varying NOT NULL, + "something" character varying NOT NULL, + CONSTRAINT "PK_b39ad32e514b17e90c93988888a" PRIMARY KEY ("username") + ) + ` + ] + }, + + mssql: { + control: [ + `CREATE TABLE "post" ("id" int NOT NULL IDENTITY(1,1), "title" nvarchar(255) NOT NULL, "createdAt" datetime2 NOT NULL CONSTRAINT "DF_fb91bea2d37140a877b775e6b2a" DEFAULT getdate(), CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id"))`, + `CREATE TABLE "username" ("username" nvarchar(255) NOT NULL, "email" nvarchar(255) NOT NULL, "something" nvarchar(255) NOT NULL, CONSTRAINT "PK_b39ad32e514b17e90c93988888a" PRIMARY KEY ("username"))` + ], + pretty: [ + ` + CREATE TABLE "post" ( + "id" int NOT NULL IDENTITY(1, 1), + "title" nvarchar(255) NOT NULL, + "createdAt" datetime2 NOT NULL CONSTRAINT "DF_fb91bea2d37140a877b775e6b2a" DEFAULT getdate(), + CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id") + ) + `, + ` + CREATE TABLE "username" ( + "username" nvarchar(255) NOT NULL, + "email" nvarchar(255) NOT NULL, + "something" nvarchar(255) NOT NULL, + CONSTRAINT "PK_b39ad32e514b17e90c93988888a" PRIMARY KEY ("username") + ) + ` + ] + }, + + sqlite: { + control: [ + `CREATE TABLE "post" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')))`, + `CREATE TABLE "username" ("username" varchar PRIMARY KEY NOT NULL, "email" varchar NOT NULL, "something" varchar NOT NULL)`, + ], + pretty: [ + ` + CREATE TABLE "post" ( + "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, + "title" varchar NOT NULL, + "createdAt" datetime NOT NULL DEFAULT (datetime('now')) + ) + `, + ` + CREATE TABLE "username" ( + "username" varchar PRIMARY KEY NOT NULL, + "email" varchar NOT NULL, + "something" varchar NOT NULL + ) + ` + ] + }, + + mysql: { + control: [ + `CREATE TABLE \`post\` (\`id\` int NOT NULL AUTO_INCREMENT, \`title\` varchar(255) NOT NULL, \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + `CREATE TABLE \`username\` (\`username\` varchar(255) NOT NULL, \`email\` varchar(255) NOT NULL, \`something\` varchar(255) NOT NULL, PRIMARY KEY (\`username\`)) ENGINE=InnoDB` + ], + pretty: [ + ` + CREATE TABLE \`post\` ( + \`id\` int NOT NULL AUTO_INCREMENT, + \`title\` varchar(255) NOT NULL, + \`createdAt\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + PRIMARY KEY (\`id\`) + ) ENGINE = InnoDB + `, + ` + CREATE TABLE \`username\` ( + \`username\` varchar(255) NOT NULL, + \`email\` varchar(255) NOT NULL, + \`something\` varchar(255) NOT NULL, + PRIMARY KEY (\`username\`) + ) ENGINE = InnoDB + ` + ] + }, + + oracle: { + control: [ + `CREATE TABLE "post" ("id" number GENERATED BY DEFAULT AS IDENTITY, "title" varchar2(255) NOT NULL, "createdAt" timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id"))`, + `CREATE TABLE "username" ("username" varchar2(255) NOT NULL, "email" varchar2(255) NOT NULL, "something" varchar2(255) NOT NULL, CONSTRAINT "PK_b39ad32e514b17e90c93988888a" PRIMARY KEY ("username"))` + ], + pretty: [ + ` + CREATE TABLE "post" ( + "id" number GENERATED BY DEFAULT AS IDENTITY, + "title" varchar2(255) NOT NULL, + "createdAt" timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, + CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id") + ) + `, + ` + CREATE TABLE "username" ( + "username" varchar2(255) NOT NULL, + "email" varchar2(255) NOT NULL, + "something" varchar2(255) NOT NULL, + CONSTRAINT "PK_b39ad32e514b17e90c93988888a" PRIMARY KEY ("username") + ) + ` + ] + }, + + cockroachdb: { + control: [ + `CREATE SEQUENCE "post_id_seq"`, + `CREATE TABLE "post" ("id" INT DEFAULT nextval('"post_id_seq"') NOT NULL, "title" varchar NOT NULL, "createdAt" timestamptz NOT NULL DEFAULT now(), CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id"))`, + `CREATE TABLE "username" ("username" varchar NOT NULL, "email" varchar NOT NULL, "something" varchar NOT NULL, CONSTRAINT "PK_b39ad32e514b17e90c93988888a" PRIMARY KEY ("username"))` + ], + pretty: [ + ` + CREATE SEQUENCE "post_id_seq" + `, + ` + CREATE TABLE "post" ( + "id" INT DEFAULT nextval('"post_id_seq"') NOT NULL, + "title" varchar NOT NULL, + "createdAt" timestamptz NOT NULL DEFAULT now(), + CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id") + ) + `, + ` + CREATE TABLE "username" ( + "username" varchar NOT NULL, + "email" varchar NOT NULL, + "something" varchar NOT NULL, + CONSTRAINT "PK_b39ad32e514b17e90c93988888a" PRIMARY KEY ("username") + ) + ` + ] + }, + + get mariadb() { return this.mysql; }, + get "better-sqlite3"() { return this.sqlite; }, +}; diff --git a/test/github-issues/4440/issue-4440.ts b/test/github-issues/4440/issue-4440.ts deleted file mode 100644 index e42ca2eb309..00000000000 --- a/test/github-issues/4440/issue-4440.ts +++ /dev/null @@ -1,43 +0,0 @@ -import "reflect-metadata"; -import { Connection } from "../../../src/connection/Connection"; -import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; -import { Post } from "./entity/Post"; - -describe("github issues > #4440 simple-json column type throws error for string with no value", () => { - - let connections: Connection[]; - before(async () => { - connections = await createTestingConnections({ - entities: [Post], - schemaCreate: true, - dropSchema: true - }); - }); - beforeEach(() => reloadTestingDatabases(connections)); - after(() => closeTestingConnections(connections)); - - it("should correctly add retrieve simple-json field with no value", () => - Promise.all(connections.map(async (connection) => { - const repo = connection.getRepository(Post); - const post = new Post(); - post.id = 1; - post.jsonField = ""; - await repo.save(post); - const postFound = await repo.findOne(1); - postFound!.id.should.eql(1); - postFound!.jsonField.should.eql({}); - }))); - - it("should correctly add retrieve simple-json field with some value", () => - Promise.all(connections.map(async (connection) => { - const repo = connection.getRepository(Post); - const post = new Post(); - post.id = 1; - post.jsonField = {"key": "value"}; - await repo.save(post); - const postFound = await repo.findOne(1); - postFound!.id.should.eql(1); - postFound!.jsonField.should.eql({"key": "value"}); - }))); - -}); diff --git a/test/github-issues/4782/issue-4782.ts b/test/github-issues/4782/issue-4782.ts index b54b58c4f9c..6e03bddb039 100644 --- a/test/github-issues/4782/issue-4782.ts +++ b/test/github-issues/4782/issue-4782.ts @@ -4,7 +4,7 @@ import {Connection} from "../../../src/connection/Connection"; import {expect} from "chai"; import { VersionUtils } from "../../../src/util/VersionUtils"; -describe("github issues > 4782 mariadb driver wants to recreate create/update date columns CURRENT_TIMESTAMP(6) === current_timestamp(6)", () => { +describe("github issues > #4782 mariadb driver wants to recreate create/update date columns CURRENT_TIMESTAMP(6) === current_timestamp(6)", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ diff --git a/test/github-issues/4958/entity/first.ts b/test/github-issues/4958/entity/first.ts new file mode 100644 index 00000000000..84a6174f2b7 --- /dev/null +++ b/test/github-issues/4958/entity/first.ts @@ -0,0 +1,11 @@ +import { Column, Entity } from "../../../../src"; + +@Entity({ name: "first" }) +export default class Testing { + @Column("int", { + nullable: false, + primary: true, + unique: true, + }) + public id!: number; +} diff --git a/test/github-issues/4958/entity/second.ts b/test/github-issues/4958/entity/second.ts new file mode 100644 index 00000000000..53d53a8cfbe --- /dev/null +++ b/test/github-issues/4958/entity/second.ts @@ -0,0 +1,11 @@ +import { Column, Entity } from "../../../../src"; + +@Entity({ name: "second" }) +export default class Testing { + @Column("int", { + nullable: false, + primary: true, + unique: true, + }) + public notId!: number; +} diff --git a/test/github-issues/4958/issue-4958.ts b/test/github-issues/4958/issue-4958.ts new file mode 100644 index 00000000000..c3e54917f48 --- /dev/null +++ b/test/github-issues/4958/issue-4958.ts @@ -0,0 +1,24 @@ +import {expect} from "chai"; +import {Connection} from "../../../src/connection/Connection"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import First from "./entity/first"; +import Second from "./entity/second"; + +describe("github issues > #4958 getRepository returns results from another Repo", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [First, Second], + enabledDrivers: ["sqlite"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("sql generated is for correct model", () => Promise.all(connections.map(async connection => { + const rawSql = await connection + .getRepository(Second) + .createQueryBuilder("a") + .getSql(); + + expect(rawSql).to.be.equal('SELECT "a"."notId" AS "a_notId" FROM "second" "a"'); + }))); +}); diff --git a/test/github-issues/5067/issue-5067.ts b/test/github-issues/5067/issue-5067.ts new file mode 100644 index 00000000000..3082a03894a --- /dev/null +++ b/test/github-issues/5067/issue-5067.ts @@ -0,0 +1,20 @@ +import "reflect-metadata"; +import {expect} from "chai"; +import {Connection} from "../../../src"; +import {closeTestingConnections, createTestingConnections} from "../../utils/test-utils"; + +describe("github issues > #5067 ORA-00972: identifier is too long", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + enabledDrivers: ["oracle"] + })); + after(() => closeTestingConnections(connections)); + + it("generated parameter name is within the size constraints", () => Promise.all(connections.map(async connection => { + const paramName = "output_that_is_really_long_and_must_be_truncated_in_this_driver"; + const createdParameter = await connection.driver.createParameter(paramName, 0); + + expect(createdParameter).to.be.an("String"); + expect(createdParameter.length).to.be.lessThan(30); + }))); +}); diff --git a/test/github-issues/513/issue-513.ts b/test/github-issues/513/issue-513.ts index be0cf0b754e..5d039ca8087 100644 --- a/test/github-issues/513/issue-513.ts +++ b/test/github-issues/513/issue-513.ts @@ -11,7 +11,7 @@ describe("github issues > #513 Incorrect time/datetime types for SQLite", () => let connections: Connection[]; before(async () => connections = await createTestingConnections({ entities: [__dirname + "/entity/*{.js,.ts}"], - enabledDrivers: ["sqlite"] + enabledDrivers: ["sqlite", "better-sqlite3"] })); beforeEach(() => reloadTestingDatabases(connections)); after(() => closeTestingConnections(connections)); diff --git a/test/github-issues/5407/entity/User.ts b/test/github-issues/5407/entity/User.ts new file mode 100644 index 00000000000..61bd4e6ab3b --- /dev/null +++ b/test/github-issues/5407/entity/User.ts @@ -0,0 +1,41 @@ +import {PrimaryColumn, Column} from "../../../../src"; +import {Entity} from "../../../../src/decorator/entity/Entity"; + +@Entity() +export class User { + + @PrimaryColumn() + id: number; + + @Column("decimal", { default: -0, precision: 3, scale: 1 }) + decimalWithDefault: number; + + @Column("decimal", { default: 100, precision: 3 }) + noScale: number; + + @Column("decimal", { default: 10, precision: 3, scale: 0 }) + zeroScale: number; + + @Column("decimal", { default: 9999999999 }) + maxDefault: number; + + @Column("decimal", { default: -9999999999 }) + minDefault: number; + + @Column("int", { default: -100 }) + intDefault: number; + + @Column("float", { default: 3.5 }) + floatDefault: number; + + @Column({ default: "New user" }) + stringDefault: string; + + // ER_PARSE_ERROR + // @Column("decimal", { default: 0, precision: 8, scale: -4 }) + // negativeScale: number; + + // ER_INVALID_DEFAULT + // @Column("decimal", { default: -12345.67890, precision: 8, scale: 4 }) + // defaultOverflow: number; +} diff --git a/test/github-issues/5407/issue-5407.ts b/test/github-issues/5407/issue-5407.ts new file mode 100644 index 00000000000..3bb05278f72 --- /dev/null +++ b/test/github-issues/5407/issue-5407.ts @@ -0,0 +1,30 @@ +import "reflect-metadata"; +import {Connection} from "../../../src"; +import {createTestingConnections, closeTestingConnections} from "../../utils/test-utils"; +import {User} from "./entity/User"; + +describe("github issues > #5407 Wrong migration created because of default column value format", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + migrations: [], + enabledDrivers: ["mysql", "mariadb", "postgres", "better-sqlite3", "cockroachdb", "sqlite"], + schemaCreate: false, + dropSchema: true, + entities: [User], + })); + after(() => closeTestingConnections(connections)); + + it("can recognize model changes", () => Promise.all(connections.map(async connection => { + const sqlInMemory = await connection.driver.createSchemaBuilder().log(); + sqlInMemory.upQueries.length.should.be.greaterThan(0); + sqlInMemory.downQueries.length.should.be.greaterThan(0); + }))); + + it("does not generate when no model changes", () => Promise.all(connections.map(async connection => { + await connection.driver.createSchemaBuilder().build(); + + const sqlInMemory = await connection.driver.createSchemaBuilder().log(); + sqlInMemory.upQueries.length.should.be.equal(0); + sqlInMemory.downQueries.length.should.be.equal(0); + }))); +}); diff --git a/test/github-issues/5444/entity/Author.ts b/test/github-issues/5444/entity/Author.ts new file mode 100644 index 00000000000..20191b21c57 --- /dev/null +++ b/test/github-issues/5444/entity/Author.ts @@ -0,0 +1,41 @@ +import {EntitySchemaOptions} from "../../../../src/entity-schema/EntitySchemaOptions"; +import {Post} from "./Post"; + +export class Author { + id: number; + + publisherId: number; + + name: string; + + posts: Post[]; +} + +export const AuthorSchema: EntitySchemaOptions = { + name: "Author", + + target: Author, + + columns: { + id: { + primary: true, + type: Number + }, + + publisherId: { + primary: true, + type: Number + }, + + name: { + type: "varchar" + } + }, + + relations: { + posts: { + target: () => Post, + type: "one-to-many" + } + } +}; diff --git a/test/github-issues/5444/entity/Post.ts b/test/github-issues/5444/entity/Post.ts new file mode 100644 index 00000000000..eb5239960c7 --- /dev/null +++ b/test/github-issues/5444/entity/Post.ts @@ -0,0 +1,53 @@ +import {EntitySchemaOptions} from "../../../../src/entity-schema/EntitySchemaOptions"; +import {Author} from "./Author"; + +export class Post { + authorPublisherId: number; + + authorId: number; + + id: number; + + title: string; + + author: Author; +} + +export const PostSchema: EntitySchemaOptions = { + name: "Post", + + target: Post, + + columns: { + authorPublisherId: { + primary: true, + type: Number + }, + + authorId: { + primary: true, + type: Number + }, + + id: { + primary: true, + type: Number + }, + + title: { + type: "varchar" + } + }, + + relations: { + author: { + target: () => Author, + type: "many-to-one", + eager: true, + joinColumn: [ + { name: "authorPublisherId", referencedColumnName: "publisherId" }, + { name: "authorId", referencedColumnName: "id" }, + ] + } + } +}; diff --git a/test/github-issues/5444/issue-5444.ts b/test/github-issues/5444/issue-5444.ts new file mode 100644 index 00000000000..b25f47cd8d7 --- /dev/null +++ b/test/github-issues/5444/issue-5444.ts @@ -0,0 +1,40 @@ +import { EntitySchemaTransformer } from "../../../src/entity-schema/EntitySchemaTransformer"; + +import {expect} from "chai"; + +import { Post, PostSchema } from "./entity/Post"; +import { Author, AuthorSchema } from "./entity/Author"; +import {EntitySchema} from "../../../src"; + + +describe("github issues > #5444 EntitySchema missing support for multiple joinColumns in relations", () => { + it("Update query returns the number of affected rows", async () => { + const transformer = new EntitySchemaTransformer(); + + const actual = transformer.transform( + [ + new EntitySchema(AuthorSchema), + new EntitySchema(PostSchema) + ] + ); + + const joinColumns = actual.joinColumns; + + expect(joinColumns.length).to.eq(2); + expect(joinColumns).to.deep.eq([ + { + target: Post, + propertyName: "author", + name: "authorPublisherId", + referencedColumnName: "publisherId" + }, + { + target: Post, + propertyName: "author", + name: "authorId", + referencedColumnName: "id" + }, + + ]); + }); +}); diff --git a/test/github-issues/4440/entity/Post.ts b/test/github-issues/5501/entity/Post.ts similarity index 99% rename from test/github-issues/4440/entity/Post.ts rename to test/github-issues/5501/entity/Post.ts index 5220e523cd1..72dbebb2ff5 100644 --- a/test/github-issues/4440/entity/Post.ts +++ b/test/github-issues/5501/entity/Post.ts @@ -12,5 +12,4 @@ export class Post { nullable: true }) jsonField: any; - } diff --git a/test/github-issues/5501/issue-5501.ts b/test/github-issues/5501/issue-5501.ts new file mode 100644 index 00000000000..b5b0c3cb11c --- /dev/null +++ b/test/github-issues/5501/issue-5501.ts @@ -0,0 +1,112 @@ +import "reflect-metadata"; +import { Connection } from "../../../src/connection/Connection"; +import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Post } from "./entity/Post"; +import { expect } from "chai"; + +describe("github issues > #5501 Incorrect data loading from JSON string for column type 'simple-json'", () => { + + let connections: Connection[]; + before(async () => { + connections = await createTestingConnections({ + entities: [Post], + schemaCreate: true, + dropSchema: true + }); + }); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should correctly store simple-json field", () => Promise.all(connections.map(async (connection) => { + let id = 0; + const runTestCase = async (input: any, expected: any, message: string) => { + id++; + + await connection.getRepository(Post).save({ id , jsonField: input }); + + const actual = ( + await connection.createQueryBuilder() + .from("Post", "post") + .select("post.jsonField", "json") + .where("post.id = :id", {id}) + .getRawOne() + )!.json; + + expect(actual).to.be.equal(expected, message); + } + + await runTestCase("hello world", "\"hello world\"", "normal string"); + await runTestCase("", "\"\"", "empty string"); + await runTestCase("null", "\"null\"", "string containing the word null"); + await runTestCase( { "key": "value" }, "{\"key\":\"value\"}", "object containing a key and string value"); + await runTestCase([ "hello" ], "[\"hello\"]", "array containing a string"); + await runTestCase(null, null, "a null object value"); + await runTestCase(1, "1", "the real number 1"); + await runTestCase(0.3, "0.3", "the number 0.3"); + await runTestCase(true, "true", "the boolean value true"); + await runTestCase( + [ { hello: "earth", planet: true }, { hello: "moon", planet: false } ], + "[{\"hello\":\"earth\",\"planet\":true},{\"hello\":\"moon\",\"planet\":false}]", + "a complex object example" + ); + }))); + + it("should correctly retrieve simple-json field", () => Promise.all(connections.map(async (connection) => { + let id = 0; + const runTestCase = async (input: string | null, expected: any, message: string) => { + id++; + await connection.createQueryBuilder() + .insert() + .into(Post) + .values({id, jsonField: () => ':field'} as any) // A bit of a hack to get the raw value inserting + .setParameter('field', input) + .execute(); + + const actual = ( + await connection.getRepository(Post).findOne({ where: { id } }) + )!.jsonField; + + expect(actual).to.be.eql(expected, message); + } + + await runTestCase("\"hello world\"", "hello world", "normal string"); + await runTestCase("\"\"", "", "empty string"); + await runTestCase("\"null\"", "null", "string containing the word null"); + await runTestCase("{\"key\":\"value\"}", { "key": "value" }, "object containing a key and string value"); + await runTestCase("[\"hello\"]", [ "hello" ], "array containing a string"); + await runTestCase(null, null, "a null object value");; + await runTestCase("1", 1, "the real number 1"); + await runTestCase("0.3", 0.3, "the number 0.3"); + await runTestCase("true", true, "the boolean value true"); + await runTestCase( + "[{\"hello\":\"earth\",\"planet\":true},{\"hello\":\"moon\",\"planet\":false}]", + [{"hello":"earth","planet":true},{"hello":"moon","planet":false}], + "a complex object example" + ); + }))); + + it("should throw an error when the data in the database is invalid", () => Promise.all(connections.map(async (connection) => { + const insert = (id: number, value: string | null) => + connection.createQueryBuilder() + .insert() + .into(Post) + .values({ id, jsonField: () => ':field' } as any) // A bit of a hack to get the raw value inserting + .setParameter('field', value) + .execute() + + // This was the likely data within the database in #4440 + // This will happen if you've tried to manually insert the data in ways where + // we aren't expecting you to - like switching the column type to a text & + // trying to push a value into it that is an object. + await insert(1, "[object Object]"); + + const repo = connection.getRepository(Post); + + const getJson = async (id: number) => + ( + await repo.findOne({ where: { id } }) + )!.jsonField; + + await expect(getJson(1)).to.be.rejected; + }))); +}); diff --git a/test/github-issues/6168/issue-6168.ts b/test/github-issues/6168/issue-6168.ts new file mode 100644 index 00000000000..acdb8a9e45e --- /dev/null +++ b/test/github-issues/6168/issue-6168.ts @@ -0,0 +1,106 @@ +import "reflect-metadata"; +import {Connection} from "../../../src/connection/Connection"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Table} from "../../../src/schema-builder/table/Table"; +import { QueryRunner } from "../../../src"; +import { expect } from "chai"; + +const questionName = "question"; +const categoryName = "category"; + +const createTables = async (queryRunner: QueryRunner, dbName: string) => { + const questionTableName = `${dbName}.${questionName}`; + const categoryTableName = `${dbName}.${categoryName}`; + + await queryRunner.createTable(new Table({ + name: questionTableName, + columns: [ + { + name: "id", + type: "int", + isPrimary: true, + isGenerated: true, + generationStrategy: "increment" + }, + { + name: "name", + type: "varchar", + } + ], + }), true); + + await queryRunner.createTable(new Table({ + name: categoryTableName, + columns: [ + { + name: "id", + type: "int", + isPrimary: true, + isGenerated: true, + generationStrategy: "increment" + }, + { + name: "questionId", + type: "int", + } + ], + foreignKeys: [ + { + columnNames: ["questionId"], + referencedTableName: questionTableName, + referencedColumnNames: ["id"], + name: "FK_CATEGORY_QUESTION" + } + ] + }), true); +}; + +describe("github issues > #6168 fix multiple foreign keys with the same name in a mysql multi-tenanted DB", () => { + + let connections: Connection[]; + before(async () => { + connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["mysql"], + schemaCreate: false, + dropSchema: false, + }); + + await reloadTestingDatabases(connections); + + for (const connection of connections) { + const queryRunner = connection.createQueryRunner(); + await createTables(queryRunner, String(connection.driver.database)); + await queryRunner.createDatabase("test2", true); + await createTables(queryRunner, "test2"); + await queryRunner.release(); + }; + }); + + after(async () => { + for (const connection of connections) { + const queryRunner = connection.createQueryRunner(); + await queryRunner.dropDatabase("test2"); + await queryRunner.release(); + }; + + await closeTestingConnections(connections); + }); + + it("should only have one foreign key column", () => Promise.all(connections.map(async connection => { + const queryRunner = connection.createQueryRunner(); + const tables = await queryRunner.getTables([questionName, categoryName]); + + const questionTable = tables.find(table => table.name === questionName) as Table; + const categoryTable = tables.find(table => table.name === categoryName) as Table; + + queryRunner.release(); + + expect(categoryTable.foreignKeys.length).to.eq(1); + expect(categoryTable.foreignKeys[0].name).to.eq("FK_CATEGORY_QUESTION"); + expect(categoryTable.foreignKeys[0].columnNames.length).to.eq(1); // before the fix this was 2, one for each schema + expect(categoryTable.foreignKeys[0].columnNames[0]).to.eq("questionId"); + + expect(questionTable.foreignKeys.length).to.eq(0); + }))); +}); diff --git a/test/github-issues/6266/entity/Post.ts b/test/github-issues/6266/entity/Post.ts new file mode 100644 index 00000000000..94e6f92ea52 --- /dev/null +++ b/test/github-issues/6266/entity/Post.ts @@ -0,0 +1,14 @@ +import { PrimaryGeneratedColumn, Entity, Column, CreateDateColumn } from "../../../../src"; + +@Entity() +export class Post { + + @PrimaryGeneratedColumn() + id?: number; + + @Column() + title: string; + + @CreateDateColumn() + readonly createdAt?: Date; +} diff --git a/test/github-issues/6266/issue-6266.ts b/test/github-issues/6266/issue-6266.ts new file mode 100644 index 00000000000..8e8a79e9c0e --- /dev/null +++ b/test/github-issues/6266/issue-6266.ts @@ -0,0 +1,60 @@ +import "reflect-metadata"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { Post } from "./entity/Post"; +import sinon from "sinon"; +import { SelectQueryBuilder } from "../../../src"; +import { assert } from "chai"; + +describe("github issues > #6266 Many identical selects after insert bunch of items", () => { + let connections: Connection[]; + const posts: Post[] = [ + { + title: "Post 1", + }, + { + title: "Post 2", + }, + { + title: "Post 3", + }, + { + title: "Post 4", + }, + ]; + + before( + async () => + (connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["mysql"], + })) + ); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should execute a single SELECT to get inserted default and generated values of multiple entities", () => + Promise.all( + connections.map(async (connection) => { + const selectSpy = sinon.spy( + SelectQueryBuilder.prototype, + "select" + ); + + await connection + .createQueryBuilder() + .insert() + .into(Post) + .values(posts) + .execute(); + + assert.strictEqual(selectSpy.calledOnce, true); + + selectSpy.restore(); + }) + )); +}); diff --git a/test/github-issues/6284/issue-6284.ts b/test/github-issues/6284/issue-6284.ts new file mode 100644 index 00000000000..85231df5090 --- /dev/null +++ b/test/github-issues/6284/issue-6284.ts @@ -0,0 +1,36 @@ +import {expect} from "chai"; +import { writeFileSync, unlinkSync } from "fs"; +import { ConnectionOptionsReader } from "../../../src/connection/ConnectionOptionsReader"; +import { importClassesFromDirectories } from "../../../src/util/DirectoryExportedClassesLoader"; +import { LoggerFactory } from "../../../src/logger/LoggerFactory"; + +describe("github issues > #6284 cli support for cjs extension", () => { + it("will load a cjs file", async () => { + const cjsConfigPath = [__dirname, "ormconfig.cjs"].join("/"); + const databaseType = "postgres"; + const config = `module.exports = {"type": "${databaseType}"};`; + + writeFileSync(cjsConfigPath, config); + const reader = new ConnectionOptionsReader({root: __dirname }); + + const results = await reader.all(); + expect(results).to.be.an("Array"); + expect(results[0]).to.be.an("Object"); + expect(results[0].type).to.equal(databaseType); + + + unlinkSync(cjsConfigPath); + }); + + it("loads cjs files via DirectoryExportedClassesloader", () => { + const klassPath = [__dirname, "klass.cjs"].join("/"); + const klass = `module.exports.Widget = class Widget {};`; + writeFileSync(klassPath, klass); + + const classes = importClassesFromDirectories(new LoggerFactory().create(), [`${__dirname}/*.cjs`]); + expect(classes).to.be.an("Array"); + expect(classes.length).to.eq(1); + + unlinkSync(klassPath); + }); +}); diff --git a/test/github-issues/6389/issue-6389.ts b/test/github-issues/6389/issue-6389.ts new file mode 100644 index 00000000000..a12e9adab16 --- /dev/null +++ b/test/github-issues/6389/issue-6389.ts @@ -0,0 +1,26 @@ +import { DriverUtils } from "../../../src/driver/DriverUtils"; +import { expect } from "chai"; + +describe("github issues > #6389 MongoDB URI Connection string with query params", () => { + it("should parse correctly mongodb URI", () => { + const obj: any = { + type: "mongodb", + username: "user", + password: "password", + host: "host", + database: "database", + port: 27017, + }; + + const url = `${obj.type}://${obj.username}:${obj.password}@${obj.host}:${obj.port}/${obj.database}?readPreference=primary`; + const options = DriverUtils.buildDriverOptions({url}); + + expect(options.type).to.eql(obj.type); + expect(options.username).to.eql(obj.username); + expect(options.username).to.eql(obj.username); + expect(options.password).to.eql(obj.password); + expect(options.host).to.eql(obj.host); + expect(options.port).to.eql(obj.port); + expect(options.database).to.eql(obj.database); + }); +}); diff --git a/test/github-issues/6399/entities.ts b/test/github-issues/6399/entities.ts new file mode 100644 index 00000000000..9629ee269ce --- /dev/null +++ b/test/github-issues/6399/entities.ts @@ -0,0 +1,52 @@ +import {Entity, OneToMany, ManyToOne} from "../../../src"; +import {Column} from "../../../src"; +import {PrimaryGeneratedColumn} from "../../../src"; +import { TableInheritance } from "../../../src"; +import {ChildEntity} from "../../../src"; +import {JoinColumn} from "../../../src"; + +@Entity() +export class Comment { + @PrimaryGeneratedColumn() + id: number; + + @Column() + text: string; + + @Column() + postId: number; + + @ManyToOne( + () => Post, + (entity) => entity.comments, + ) + @JoinColumn({ + name: "postId", + }) + post?: Post; + +} + +@Entity() +@TableInheritance({column: {type: "string", name: "postType"}}) +export class Post { + @PrimaryGeneratedColumn() + id: number; + + @Column() + title: string; + + @Column() + postType: string = "BasePost"; + + @OneToMany(() => Comment, (entity) => entity.post) + comments?: Comment[]; +} + +@ChildEntity("TargetPost") +export class TargetPost extends Post { + @Column() + postType: string = "TargetPost"; +} + + diff --git a/test/github-issues/6399/issue-6399.ts b/test/github-issues/6399/issue-6399.ts new file mode 100644 index 00000000000..a4f8f2d1b4c --- /dev/null +++ b/test/github-issues/6399/issue-6399.ts @@ -0,0 +1,49 @@ +import {Connection} from "../../../src"; +import {expect} from "chai"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Post, TargetPost, Comment} from "./entities"; + +describe("github issues > #6399 Process extraAppendedAndWhereCondition for inherited entity", () => { + let connections: Connection[]; + + before(async () => { + return connections = await createTestingConnections({ + entities: [Post, TargetPost, Comment], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["mysql"] + }); + }); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("Query with join and limit for inhered entity", () => Promise.all(connections.map(async (connection) => { + const targetPostRepo = connection.getRepository(TargetPost); + + const posts: TargetPost[] = [ + { + id: 1, + title: "Post 1", + postType: "TargetPost", + }, + { id: 2, + title: "Post 2", + postType: "TargetPost", + }, + { + id: 3, + title: "Post 3", + postType: "TargetPost", + }, + ]; + + await targetPostRepo.save(posts); + + const result = await targetPostRepo.createQueryBuilder("targetPosts") + .leftJoinAndSelect("targetPosts.comments", "comments") + .take(2) + .getMany(); + + expect(result.length).eq(2); + }))); +}); diff --git a/test/github-issues/6416/entity/Post.ts b/test/github-issues/6416/entity/Post.ts new file mode 100644 index 00000000000..d70667904e0 --- /dev/null +++ b/test/github-issues/6416/entity/Post.ts @@ -0,0 +1,52 @@ +import { EntitySchema } from "../../../../src"; + +import PostTag from "./PostTag"; +import PostAttachment from "./PostAttachment"; + +let id = 0; + +export default class Post { + postId: number; + + otherId: number; + + tags: PostTag[]; + + attachments: PostAttachment[]; + + constructor() { + this.postId = id++; + this.otherId = id++; + } +} + +export const PostSchema = new EntitySchema({ + name: "Post", + target: Post, + columns: { + otherId: { + type: Number, + primary: true, + nullable: false + }, + postId: { + type: Number, + primary: true, + nullable: false + } + }, + relations: { + tags: { + target: () => PostTag, + type: "one-to-many", + inverseSide: "post", + cascade: true + }, + attachments: { + target: () => PostAttachment, + type: "one-to-many", + inverseSide: "post", + cascade: true + } + } +}); diff --git a/test/github-issues/6416/entity/PostAttachment.ts b/test/github-issues/6416/entity/PostAttachment.ts new file mode 100644 index 00000000000..2408e5816ed --- /dev/null +++ b/test/github-issues/6416/entity/PostAttachment.ts @@ -0,0 +1,35 @@ +import { EntitySchema } from "../../../../src"; + +import Post from "./Post"; + +let id = 0; + +export default class PostAttachment { + attachmentId: number; + + post: Post; + + constructor () { + this.attachmentId = id++; + } +} + +export const PostAttachmentSchema = new EntitySchema({ + name: "PostAttachment", + target: PostAttachment, + columns: { + attachmentId: { + type: Number, + primary: true, + nullable: false + } + }, + relations: { + post: { + primary: true, + nullable: false, + target: () => Post, + type: "many-to-one" + } + } +}); diff --git a/test/github-issues/6416/entity/PostTag.ts b/test/github-issues/6416/entity/PostTag.ts new file mode 100644 index 00000000000..1217085738a --- /dev/null +++ b/test/github-issues/6416/entity/PostTag.ts @@ -0,0 +1,51 @@ +import { EntitySchema } from "../../../../src"; + +import Post from "./Post"; + +let id = 0; + +export default class PostTag { + tagId: number; + + tagOtherId: string; + + tagPostId: string; + + post: Post; + + constructor () { + this.tagId = id++; + } +} + +export const PostTagSchema = new EntitySchema({ + name: "PostTag", + target: PostTag, + columns: { + tagOtherId: { + type: Number, + primary: true + }, + tagPostId: { + type: Number, + primary: true + }, + tagId: { + type: Number, + primary: true, + nullable: false + } + }, + relations: { + post: { + primary: true, + nullable: false, + target: () => Post, + type: "many-to-one", + joinColumn: [ + { name: "tagPostId", referencedColumnName: "postId" }, + { name: "tagOtherId", referencedColumnName: "otherId" } + ] + } + } +}); diff --git a/test/github-issues/6416/issue-6416.ts b/test/github-issues/6416/issue-6416.ts new file mode 100644 index 00000000000..f09ab22b212 --- /dev/null +++ b/test/github-issues/6416/issue-6416.ts @@ -0,0 +1,66 @@ +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Connection} from "../../../src"; + +import { assert } from "chai"; + +import Post, { PostSchema } from "./entity/Post"; +import PostTag, { PostTagSchema } from "./entity/PostTag"; +import PostAttachment, { PostAttachmentSchema } from "./entity/PostAttachment"; + +describe("github issues > #6399 Combining ManyToOne, Cascade, & Composite Primary Key causes Unique Constraint issues", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [PostSchema, PostTagSchema, PostAttachmentSchema], + enabledDrivers: ["sqlite"], + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("persisting the cascading entities should succeed", () => Promise.all(connections.map(async connection => { + + const post = new Post(); + const postTag = new PostTag(); + post.tags = [postTag]; + + await connection.manager.save(post, { reload: true }); + + try { + await connection.manager.save(post); + } catch (e) { + assert.fail(e.toString(), null, "Second save had an exception"); + } + }))); + + it("persisting the cascading entities without JoinColumn should succeed", () => Promise.all(connections.map(async connection => { + + const post = new Post(); + const postAttachment = new PostAttachment(); + post.attachments = [postAttachment]; + + await connection.manager.save(post, { reload: true }); + + try { + await connection.manager.save(post); + } catch (e) { + assert.fail(e.toString(), null, "Second save had an exception"); + } + }))); + + it("persisting the child entity should succeed", () => Promise.all(connections.map(async connection => { + const post = new Post(); + + await connection.manager.save(post); + + const postTag = new PostTag(); + postTag.post = post; + + await connection.manager.save(postTag, { reload: true }); + + try { + await connection.manager.save(postTag); + } catch (e) { + assert.fail(e.toString(), null, "Second save had an exception"); + } + }))); +}); diff --git a/test/github-issues/6442/entity/v1/entities.ts b/test/github-issues/6442/entity/v1/entities.ts new file mode 100644 index 00000000000..f8df17ccd27 --- /dev/null +++ b/test/github-issues/6442/entity/v1/entities.ts @@ -0,0 +1,18 @@ +import { PrimaryColumn, Generated, Column, Entity } from "../../../../../src"; + +@Entity() +export class FooEntity { + @PrimaryColumn({ type: "int", width: 10, unsigned: true, nullable: false }) + @Generated() + public id: number; +} + +@Entity() +export class BarEntity { + @PrimaryColumn({ type: "int", width: 10, unsigned: true }) + @Generated() + public id: number; + + @Column("varchar", { nullable: false, length: 50 }) + public code: string; +} diff --git a/test/github-issues/6442/entity/v2/entities.ts b/test/github-issues/6442/entity/v2/entities.ts new file mode 100644 index 00000000000..fcf2d1a14b4 --- /dev/null +++ b/test/github-issues/6442/entity/v2/entities.ts @@ -0,0 +1,34 @@ +import { PrimaryColumn, Generated, ManyToMany, Entity, Column, JoinTable } from "../../../../../src"; + +@Entity() +export class FooEntity { + @PrimaryColumn({ type: "int", width: 10, unsigned: true, nullable: false }) + @Generated() + public id: number; + + @ManyToMany(() => BarEntity) + @JoinTable({ + name: "foo_bars", + joinColumns: [ + { + name: "foo_id", + } + ], + inverseJoinColumns: [ + { + name: "bar_id", + } + ] + }) + public fooBars: BarEntity[]; +} + +@Entity() +export class BarEntity { + @PrimaryColumn({ type: "int", width: 10, unsigned: true }) + @Generated() + public id: number; + + @Column("varchar", { nullable: false, length: 50 }) + public code: string; +} diff --git a/test/github-issues/6442/issue-6442.ts b/test/github-issues/6442/issue-6442.ts new file mode 100644 index 00000000000..9d775f65ff6 --- /dev/null +++ b/test/github-issues/6442/issue-6442.ts @@ -0,0 +1,67 @@ +import "reflect-metadata"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases, + setupSingleTestingConnection +} from "../../utils/test-utils"; +import { Connection, createConnection } from "../../../src"; +import { fail } from "assert"; +import { Query } from "../../../src/driver/Query"; +import { MysqlConnectionOptions } from "../../../src/driver/mysql/MysqlConnectionOptions"; + +describe("github issues > #6442 JoinTable does not respect inverseJoinColumns referenced column width", () => { + let connections: Connection[]; + + before(async () => { + return connections = await createTestingConnections({ + entities: [__dirname + "/entity/v1/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["mysql"] + }); + }); + beforeEach(async () => await reloadTestingDatabases(connections)); + after(async () => await closeTestingConnections(connections)); + + it("should generate column widths equal to the referenced column widths", async () => { + + await Promise.all(connections.map(async (connection) => { + const options = setupSingleTestingConnection( + connection.options.type, + { + name: `${connection.name}-v2`, + entities: [__dirname + "/entity/v2/*{.js,.ts}"], + dropSchema: false, + schemaCreate: false + } + ) as MysqlConnectionOptions; + + if (!options) { + await connection.close(); + fail(); + } + + const migrationConnection = await createConnection(options); + try { + const sqlInMemory = await migrationConnection.driver + .createSchemaBuilder() + .log(); + + const upQueries = sqlInMemory.upQueries.map( + (query: Query) => query.query + ); + + upQueries.should.eql([ + "CREATE TABLE `foo_bars` (`foo_id` int(10) UNSIGNED NOT NULL, `bar_id` int(10) UNSIGNED NOT NULL, INDEX `IDX_319290776f044043e3ef3ba5a8` (`foo_id`), INDEX `IDX_b7fd4be386fa7cdb87ef8b12b6` (`bar_id`), PRIMARY KEY (`foo_id`, `bar_id`)) ENGINE=InnoDB", + "ALTER TABLE `foo_bars` ADD CONSTRAINT `FK_319290776f044043e3ef3ba5a8d` FOREIGN KEY (`foo_id`) REFERENCES `foo_entity`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION", + "ALTER TABLE `foo_bars` ADD CONSTRAINT `FK_b7fd4be386fa7cdb87ef8b12b69` FOREIGN KEY (`bar_id`) REFERENCES `bar_entity`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION" + ]); + + } finally { + await connection.close(); + await migrationConnection.close(); + } + })); + }); +}); diff --git a/test/github-issues/6552/entity/Post.ts b/test/github-issues/6552/entity/Post.ts new file mode 100644 index 00000000000..8b1a13bc5fe --- /dev/null +++ b/test/github-issues/6552/entity/Post.ts @@ -0,0 +1,14 @@ +import { ObjectID, ObjectIdColumn } from "../../../../src"; +import { Column } from "../../../../src/decorator/columns/Column"; +import { Entity } from "../../../../src/decorator/entity/Entity"; + +@Entity() +export class Post { + + @ObjectIdColumn() + _id: ObjectID; + + @Column() + title: string; + +} diff --git a/test/github-issues/6552/entity/PostV2.ts b/test/github-issues/6552/entity/PostV2.ts new file mode 100644 index 00000000000..c9e798a4a82 --- /dev/null +++ b/test/github-issues/6552/entity/PostV2.ts @@ -0,0 +1,14 @@ +import { ObjectID, ObjectIdColumn } from "../../../../src"; +import { Column } from "../../../../src/decorator/columns/Column"; +import { Entity } from "../../../../src/decorator/entity/Entity"; + +@Entity() +export class PostV2 { + + @ObjectIdColumn() + postId: ObjectID; + + @Column() + title: string; + +} diff --git a/test/github-issues/6552/issue-6552.ts b/test/github-issues/6552/issue-6552.ts new file mode 100644 index 00000000000..368ea36ce95 --- /dev/null +++ b/test/github-issues/6552/issue-6552.ts @@ -0,0 +1,121 @@ +import { expect } from "chai"; +import "reflect-metadata"; +import { Connection } from "../../../src/connection/Connection"; +import { PlatformTools } from "../../../src/platform/PlatformTools"; +import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Post } from "./entity/Post"; +import { PostV2 } from "./entity/PostV2"; +import { FindConditions } from "../../../src"; + +describe("github issues > #6552 MongoRepository delete by ObjectId deletes the wrong entity", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["mongodb"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + // before the fix this would delete incorrectly post1 instead of post2 + it("should delete the correct entity when id column is called _id", () => Promise.all(connections.map(async function (connection) { + + // setup: create 2 posts + const post1 = new Post(); + post1.title = "Post 1"; + await connection.manager.save(post1); + + const post2 = new Post(); + post2.title = "Post 2"; + await connection.manager.save(post2); + + const objectIdInstance = PlatformTools.load("mongodb").ObjectID; + + // double check that post2._id is actually an ObjectID + expect(post2._id).to.be.not.null; + expect(post2._id).to.be.not.undefined; + expect(post2._id).to.be.instanceof(objectIdInstance); + + // delete Post 2 by ObjectId directly + await connection.manager.delete(Post, post2._id); + // This used to wrongly perform deleteOne({}) - deleting the first Post in the collection + + // Post 1 should remain in the DB + const count1 = await connection.manager.count(Post, { _id: post1._id } as FindConditions); + expect(count1).to.be.equal(1, "Post 1 should still exist"); + + // Post 2 should be deleted + const count2 = await connection.manager.count(Post, { _id: post2._id } as FindConditions); + expect(count2).to.be.equal(0, "Post 2 should be deleted"); + + + }))); + + // before the fix this wouldn't delete anything + it("should delete the correct entity when id column is not called _id", () => Promise.all(connections.map(async function (connection) { + + // setup: create 2 posts + const post1 = new PostV2(); + post1.title = "Post 1"; + await connection.manager.save(post1); + + const post2 = new PostV2(); + post2.title = "Post 2"; + await connection.manager.save(post2); + + const objectIdInstance = PlatformTools.load("mongodb").ObjectID; + + // double check that post2.postId is actually an ObjectID + expect(post2.postId).to.be.not.null; + expect(post2.postId).to.be.not.undefined; + expect(post2.postId).to.be.instanceof(objectIdInstance); + + // delete Post 2 by ObjectId directly + await connection.manager.delete(PostV2, post2.postId); + // This used to wrongly perform deleteOne({_id: Buffer}) - not deleting anything because Buffer is not an ObjectId + + // Post 1 should remain in the DB + const count1 = await connection.manager.count(PostV2, { _id: post1.postId } as FindConditions); + expect(count1).to.be.equal(1, "Post 1 should still exist"); + + // Post 2 should be deleted + const count2 = await connection.manager.count(PostV2, { _id: post2.postId } as FindConditions); + expect(count2).to.be.equal(0, "Post 2 should be deleted"); + + + }))); + + // before the fix this passed (added here to make sure we don't cause any regressions) + it("should delete the correct entity when deleting by _id query", () => Promise.all(connections.map(async function (connection) { + + // setup: create 2 posts + const post1 = new Post(); + post1.title = "Post 1"; + await connection.manager.save(post1); + + const post2 = new Post(); + post2.title = "Post 2"; + await connection.manager.save(post2); + + const objectIdInstance = PlatformTools.load("mongodb").ObjectID; + + // double check that post2._id is actually an ObjectID + expect(post2._id).to.be.not.null; + expect(post2._id).to.be.not.undefined; + expect(post2._id).to.be.instanceof(objectIdInstance); + + // delete Post 2 by ObjectId directly + await connection.manager.delete(Post, { _id: post2._id }); + + // Post 1 should remain in the DB + const count1 = await connection.manager.count(Post, { _id: post1._id } as FindConditions); + expect(count1).to.be.equal(1, "Post 1 should still exist"); + + // Post 2 should be deleted + const count2 = await connection.manager.count(Post, { _id: post2._id } as FindConditions); + expect(count2).to.be.equal(0, "Post 2 should be deleted"); + + + }))); + +}); diff --git a/test/github-issues/6580/entity/Comment.ts b/test/github-issues/6580/entity/Comment.ts new file mode 100644 index 00000000000..1ef8a1139b2 --- /dev/null +++ b/test/github-issues/6580/entity/Comment.ts @@ -0,0 +1,4 @@ +export class Comment { + any: any; + object: {[k: string]: any}; +} diff --git a/test/github-issues/6580/issue-6580.ts b/test/github-issues/6580/issue-6580.ts new file mode 100644 index 00000000000..d309ad411e9 --- /dev/null +++ b/test/github-issues/6580/issue-6580.ts @@ -0,0 +1,24 @@ +import {DeepPartial} from "../../../src"; +import {Comment} from "./entity/Comment"; + +describe("github issues > #6580 DeepPartial does not handle `any` and `{[k: string]}`", () => { + + function attemptDeepPartial(entityLike: DeepPartial): void { + } + + it("DeepPartial should correctly handle any", () => { + attemptDeepPartial({ + any: { + foo: 'bar', + } + }) + }); + + it("DeepPartial should correctly handle {[k: string]: any}", () => { + attemptDeepPartial({ + object: { + foo: 'bar' + }, + }) + }); +}); diff --git a/test/github-issues/6633/entity/Test.ts b/test/github-issues/6633/entity/Test.ts new file mode 100644 index 00000000000..61443c6dcda --- /dev/null +++ b/test/github-issues/6633/entity/Test.ts @@ -0,0 +1,11 @@ +import { Entity, PrimaryColumn, Index, Column } from "../../../../src"; + +@Entity() +export class Test { + @PrimaryColumn() + id: number; + + @Index("description_index", { fulltext: true }) + @Column() + description: string; +} diff --git a/test/github-issues/6633/issue-6633.ts b/test/github-issues/6633/issue-6633.ts new file mode 100644 index 00000000000..cd2dbbffa6f --- /dev/null +++ b/test/github-issues/6633/issue-6633.ts @@ -0,0 +1,29 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import { Connection } from "../../../src"; +import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Test } from "./entity/Test"; + +describe("github issues > #6633 Fulltext indices continually dropped & re-created", () => { + + let connections: Connection[]; + before(async () => { + connections = await createTestingConnections({ + entities: [Test], + schemaCreate: true, + dropSchema: true + }); + }); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should not create migrations for fulltext indices", () => + Promise.all(connections.map(async (connection) => { + const sqlInMemory = await connection.driver.createSchemaBuilder().log(); + + expect(sqlInMemory.upQueries).to.eql([]); + expect(sqlInMemory.downQueries).to.eql([]); + } + )) + ); +}); diff --git a/test/github-issues/6636/entity/Test.ts b/test/github-issues/6636/entity/Test.ts new file mode 100644 index 00000000000..086363bbbc3 --- /dev/null +++ b/test/github-issues/6636/entity/Test.ts @@ -0,0 +1,15 @@ +import { Column, Entity, PrimaryColumn } from "../../../../src"; + +@Entity() +export class Test { + + @PrimaryColumn() + id: number; + + @Column({ nullable: true, precision: 6 }) + startedAt?: Date; + + @Column({ type: 'decimal', precision: 5, scale: 2 }) + value: number; + +} diff --git a/test/github-issues/6636/issue-6636.ts b/test/github-issues/6636/issue-6636.ts new file mode 100644 index 00000000000..841f7c4a5af --- /dev/null +++ b/test/github-issues/6636/issue-6636.ts @@ -0,0 +1,25 @@ +import { Connection } from "../../../src"; +import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Test } from "./entity/Test"; +import { expect } from "chai"; + +describe("github issues > #6636 migration issues with scale & precision", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [Test], + enabledDrivers: ["sqljs", "sqlite", "better-sqlite3"], + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should not create migrations columns with precision", async () => { + await Promise.all(connections.map(async (connection) => { + const sqlInMemory = await connection.driver.createSchemaBuilder().log(); + expect(sqlInMemory.upQueries).to.eql([]); + expect(sqlInMemory.downQueries).to.eql([]); + } + )) + }); + +}); diff --git a/test/github-issues/6699/issue-6699.ts b/test/github-issues/6699/issue-6699.ts new file mode 100644 index 00000000000..b7f6d525c1f --- /dev/null +++ b/test/github-issues/6699/issue-6699.ts @@ -0,0 +1,30 @@ +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Connection} from "../../../src/connection/Connection"; +import {expect} from 'chai'; + +describe("github issues > #6699 MaxListenersExceededWarning occurs on Postgres", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [], + enabledDrivers: ["postgres"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("queries in a transaction do not cause an EventEmitter memory leak", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + const queryPromises = [...Array(10)].map( + () => manager.query('SELECT pg_sleep(0.0001)') + ); + + const pgConnection = await manager.queryRunner!.connect(); + + expect(pgConnection.listenerCount('error')).to.equal(1); + + // Wait for all of the queries to finish and drain the backlog + await Promise.all(queryPromises); + }); + }))); + +}); diff --git a/test/github-issues/6714/entity/session.ts b/test/github-issues/6714/entity/session.ts new file mode 100644 index 00000000000..9ea05da3ac1 --- /dev/null +++ b/test/github-issues/6714/entity/session.ts @@ -0,0 +1,17 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "../../../../src"; + +@Entity({ name: "Session" }) +export class Session { + + @PrimaryGeneratedColumn() + id?: number; + + @Column({ + type: "timestamp", + precision: 3, + default: () => "CURRENT_TIMESTAMP(3)", + onUpdate: "CURRENT_TIMESTAMP(3)", + }) + ts: Date; + +} diff --git a/test/github-issues/6714/entity/sessionchanged.ts b/test/github-issues/6714/entity/sessionchanged.ts new file mode 100644 index 00000000000..c689295e841 --- /dev/null +++ b/test/github-issues/6714/entity/sessionchanged.ts @@ -0,0 +1,15 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "../../../../src"; + +@Entity({ name: "Session" }) +export class Session { + @PrimaryGeneratedColumn() + id?: number; + + @Column({ + type: "timestamp", + precision: 4, + default: () => "CURRENT_TIMESTAMP(4)", + onUpdate: "CURRENT_TIMESTAMP(4)", + }) + ts: Date; +} diff --git a/test/github-issues/6714/issue-6714.ts b/test/github-issues/6714/issue-6714.ts new file mode 100644 index 00000000000..e51fd4a7187 --- /dev/null +++ b/test/github-issues/6714/issue-6714.ts @@ -0,0 +1,59 @@ +import "reflect-metadata"; +import { + createTestingConnections, + closeTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import { Session as baseEntity } from "./entity/session"; +import { Session as changedEntity } from "./entity/sessionchanged"; + +describe("github issues > #6714 Migration:generate issue with onUpdate using mariadb 10.4", () => { + it("dont change anything", async () => { + let connections: Connection[]; + connections = await createTestingConnections({ + entities: [baseEntity], + schemaCreate: false, + dropSchema: true, + enabledDrivers: ["mariadb"], + }); + await reloadTestingDatabases(connections); + await Promise.all( + connections.map(async (connection) => { + const schemaBuilder = connection.driver.createSchemaBuilder(); + const syncQueries = await schemaBuilder.log(); + expect(syncQueries.downQueries).to.be.eql([]); + expect(syncQueries.upQueries).to.be.eql([]); + }) + ); + await closeTestingConnections(connections); + }); + it("recognizing on update changes", async () => { + // this connection create database with a Session entity + const baseConnections = await createTestingConnections({ + entities: [baseEntity], + schemaCreate: true, // create the database + dropSchema: true, + enabledDrivers: ["mariadb"], + }); + // this connection change Session entity on update value + const connections = await createTestingConnections({ + entities: [changedEntity], + schemaCreate: false, // don't change the entity + dropSchema: false, + enabledDrivers: ["mariadb"], + name: "test", + }); + await Promise.all( + connections.map(async (connection) => { + const schemaBuilder = connection.driver.createSchemaBuilder(); + const syncQueries = await schemaBuilder.log(); + expect(syncQueries.downQueries.length).not.to.be.eql(0); + expect(syncQueries.upQueries.length).not.to.be.eql(0); + }) + ); + await closeTestingConnections(baseConnections); + await closeTestingConnections(connections); + }); +}); diff --git a/test/github-issues/6815/entity/ChildEntity.ts b/test/github-issues/6815/entity/ChildEntity.ts new file mode 100644 index 00000000000..3f5dc547d94 --- /dev/null +++ b/test/github-issues/6815/entity/ChildEntity.ts @@ -0,0 +1,7 @@ +import {Entity, PrimaryGeneratedColumn} from "../../../../src"; + +@Entity() +export class ChildEntity { + @PrimaryGeneratedColumn({type: "bigint"}) + id: string; +} diff --git a/test/github-issues/6815/entity/ParentEntity.ts b/test/github-issues/6815/entity/ParentEntity.ts new file mode 100644 index 00000000000..e3ec5530393 --- /dev/null +++ b/test/github-issues/6815/entity/ParentEntity.ts @@ -0,0 +1,15 @@ +import {Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn, RelationId} from "../../../../src"; +import {ChildEntity} from "./ChildEntity"; + +@Entity() +export class ParentEntity { + @PrimaryGeneratedColumn({type: "bigint"}) + id: string; + + @OneToOne(() => ChildEntity, {nullable: true}) + @JoinColumn() + child: ChildEntity|null; + + @RelationId((parent: ParentEntity) => parent.child) + childId: string|null; +} diff --git a/test/github-issues/6815/issue-6815.ts b/test/github-issues/6815/issue-6815.ts new file mode 100644 index 00000000000..27e6916ac9e --- /dev/null +++ b/test/github-issues/6815/issue-6815.ts @@ -0,0 +1,56 @@ +import {expect} from "chai"; +import {Connection} from "../../../src/connection/Connection"; +import {EntityManager} from "../../../src/entity-manager/EntityManager"; +import { + createTestingConnections, + closeTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import {ChildEntity} from "./entity/ChildEntity"; +import {ParentEntity} from "./entity/ParentEntity"; + +describe("github issues > #6815 RelationId() on nullable relation returns 'null' string", () => { + let connections: Connection[]; + + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["cockroachdb", "mariadb", "mssql", "mysql", "postgres"] + })); + + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should return null as childId if child doesn't exist", () => Promise.all( + connections.map(async connection => { + const em = new EntityManager(connection); + const parent = em.create(ParentEntity); + await em.save(parent); + + const loaded = await em.findOneOrFail(ParentEntity, parent.id); + expect(loaded.childId).to.be.null; + }) + )); + + it("should return string as childId if child exists", () => Promise.all( + connections.map(async connection => { + const em = new EntityManager(connection); + const child = em.create(ChildEntity); + await em.save(child); + + const parent = em.create(ParentEntity); + parent.child = child; + await em.save(parent); + + const loaded = await em.findOneOrFail(ParentEntity, parent.id); + + if (connection.name === "cockroachdb") { + // CockroachDB returns id as a number. + expect(loaded.childId).to.equal(child.id.toString()); + } else { + expect(loaded.childId).to.equal(child.id); + } + }) + )); +}); diff --git a/test/github-issues/6833/entity/test.ts b/test/github-issues/6833/entity/test.ts new file mode 100644 index 00000000000..e0cb47ad6be --- /dev/null +++ b/test/github-issues/6833/entity/test.ts @@ -0,0 +1,12 @@ +import { Entity, PrimaryColumn } from "../../../../src"; + +export class MyId { + first: number; + second: number; +} + +@Entity({ name: "jsonb_key_tests" }) +export class JSONBKeyTest { + @PrimaryColumn("jsonb") + id: MyId; +} diff --git a/test/github-issues/6833/issue-6833.ts b/test/github-issues/6833/issue-6833.ts new file mode 100644 index 00000000000..4e1ed1179ba --- /dev/null +++ b/test/github-issues/6833/issue-6833.ts @@ -0,0 +1,81 @@ +import "reflect-metadata"; +import { + createTestingConnections, + closeTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import { JSONBKeyTest } from "./entity/test"; + +describe("github issues > #6833 Entities with JSON key columns are incorrectly grouped", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [JSONBKeyTest], + dropSchema: true, + schemaCreate: true, + enabledDrivers: ["postgres"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("jsonB keys are correctly resolved", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + await manager.save(manager.create(JSONBKeyTest, { id: { first: 1, second: 2 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 1, second: 3 } })); + + const entities = await manager.createQueryBuilder(JSONBKeyTest, "json_test").select().getMany(); + expect(entities.length).to.equal(2); + }); + }))); + + it("jsonB keys can be found", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + await manager.save(manager.create(JSONBKeyTest, { id: { first: 3, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 4, second: 3 } })); + + const entities = await manager.find(JSONBKeyTest, { where: { id: { first: 3, second: 3 } } } ); + expect(entities.length).to.equal(1); + expect(entities[0].id).to.deep.equal({ first: 3, second: 3 }); + }); + }))); + + it("jsonB keys can be found with IN", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + await manager.save(manager.create(JSONBKeyTest, { id: { first: 3, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 4, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 5, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 6, second: 4 } })); + + const entities = await manager + .createQueryBuilder(JSONBKeyTest, "json_test") + .select() + .where("id IN (:...ids)", { ids: [{first: 5, second: 3}, {first: 6, second: 4}]}) + .getMany(); + expect(entities.length).to.equal(2); + expect(entities[0].id).to.deep.equal({ first: 5, second: 3 }); + expect(entities[1].id).to.deep.equal({ first: 6, second: 4 }); + }); + }))); + + it("jsonB keys can be found regardless of order", () => Promise.all(connections.map(async connection => { + await connection.transaction(async manager => { + await manager.save(manager.create(JSONBKeyTest, { id: { first: 3, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 4, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 5, second: 3 } })); + await manager.save(manager.create(JSONBKeyTest, { id: { first: 6, second: 4 } })); + + + const payload = { second: 2, first: 1 }; + await manager.save(manager.create(JSONBKeyTest, { id: payload })); + const entities = await manager.find(JSONBKeyTest, { where: { id: payload } }); + expect(entities.length).to.equal(1); + expect(entities[0].id).to.deep.equal({ first: 1, second: 2 }); + + const entitiesOtherOrder = await manager.find(JSONBKeyTest, { where: { id: {first: 1, second: 2} } }); + expect(entitiesOtherOrder.length).to.equal(1); + expect(entitiesOtherOrder[0].id).to.deep.equal({ first: 1, second: 2 }); + + }); + }))); +}); diff --git a/test/github-issues/6900/entity/Warn.ts b/test/github-issues/6900/entity/Warn.ts new file mode 100644 index 00000000000..cbabc4df7e2 --- /dev/null +++ b/test/github-issues/6900/entity/Warn.ts @@ -0,0 +1,22 @@ +import { Entity, ObjectID, ObjectIdColumn, Column } from "../../../../src"; + +@Entity("warnings") +export class Warn { + @ObjectIdColumn() + id!: ObjectID + + @Column() + guild!: string; + + @Column() + user!: string; + + @Column() + moderator!: string; + + @Column() + reason!: string; + + @Column() + createdAt!: Date; +} diff --git a/test/github-issues/6900/issue-6900.ts b/test/github-issues/6900/issue-6900.ts new file mode 100644 index 00000000000..801ae54940d --- /dev/null +++ b/test/github-issues/6900/issue-6900.ts @@ -0,0 +1,80 @@ +import { expect } from "chai"; +import { + closeTestingConnections, reloadTestingDatabases, + setupTestingConnections +} from "../../utils/test-utils"; +import {MongoDriver} from "../../../src/driver/mongodb/MongoDriver"; +import {Connection, ConnectionOptions, createConnection, MongoClient} from "../../../src"; +import {Warn} from "./entity/Warn"; +import {MongoConnectionOptions} from "../../../src/driver/mongodb/MongoConnectionOptions"; + +describe("github issues > #6900 MongoDB ConnectionManager doesn't select given database, creates new database \"test\" instead", () => { + let connections: Connection[] = []; + afterEach(async () => { + await closeTestingConnections(connections); + connections.length = 0; + }); + + it("should connect to the expected database", async () => { + const options = setupTestingConnections({ enabledDrivers: ["mongodb"] }); + + if (options.length === 0) { + // Skip if we can't grab the mongodb + return; + } + + const host: string = (options[0] as MongoConnectionOptions).host || 'localhost'; + + const connection = await createConnection({ + ...options[0], + url: `mongodb://${host}`, + database: 'foo' + } as ConnectionOptions); + connections.push(connection); + + await reloadTestingDatabases(connections); + + const mongoDriver = connection.driver as MongoDriver ; + const client = (mongoDriver.queryRunner!.databaseConnection as any) as MongoClient; + + expect(client.db().databaseName).to.be.equal('foo'); + expect(mongoDriver.database).to.be.equal('foo'); + }); + + it("should write data to the correct database", async () => { + const options = setupTestingConnections({ enabledDrivers: ["mongodb"] }); + + if (options.length === 0) { + // Skip if we can't grab the mongodb + return; + } + + const host: string = (options[0] as MongoConnectionOptions).host || 'localhost'; + + const connection = await createConnection({ + ...options[0], + entities: [ Warn ], + url: `mongodb://${host}`, + database: 'foo' + } as ConnectionOptions); + connections.push(connection); + + await reloadTestingDatabases(connections); + + const repo = connection.getRepository(Warn) + + await repo.insert({ + id: Math.floor(Math.random() * 1000000), + guild: "Hello", + user: "WORLD", + moderator: "Good Moderator", + reason: "For Mongo not writing correctly to the databsae!", + createdAt: new Date() + }); + + const mongoDriver = connection.driver as MongoDriver ; + const client = (mongoDriver.queryRunner!.databaseConnection as any) as MongoClient; + + expect(await client.db('foo').collection('warnings').count({})).to.be.greaterThan(0); + }) +}); diff --git a/test/github-issues/6947/entity/Category.ts b/test/github-issues/6947/entity/Category.ts new file mode 100644 index 00000000000..7ef4ca9faef --- /dev/null +++ b/test/github-issues/6947/entity/Category.ts @@ -0,0 +1,17 @@ +import { Entity, PrimaryGeneratedColumn, Column, Tree, TreeParent, TreeChildren } from "../../../../src"; + +@Entity() +@Tree("closure-table") +export class Category { + @PrimaryGeneratedColumn() + cat_id: number; + + @Column() + cat_name: string; + + @TreeParent() + parent: Category; + + @TreeChildren({ cascade: true }) + children: Category[]; +} diff --git a/test/github-issues/6947/issue-6947.ts b/test/github-issues/6947/issue-6947.ts new file mode 100644 index 00000000000..25cf1176372 --- /dev/null +++ b/test/github-issues/6947/issue-6947.ts @@ -0,0 +1,54 @@ +import "reflect-metadata"; +import { Category } from "./entity/Category"; +import { Connection } from "../../../src/connection/Connection"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases, +} from "../../../test/utils/test-utils"; + +describe("github issues > #6947 Custom primary column for TreeRepository based entities unable to get tree descendants", () => { + let connections: Connection[]; + before( + async () => + (connections = await createTestingConnections({ + entities: [Category], + })) + ); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("entities with custom primary column names should work", () => + Promise.all( + connections.map(async (connection) => { + const categoryRepository = connection.getTreeRepository( + Category + ); + + const parent = new Category(); + parent.cat_name = "parent"; + await categoryRepository.save(parent); + + const child = new Category(); + child.cat_name = "child"; + child.parent = parent; + await categoryRepository.save(child); + + const tree = await categoryRepository.findDescendantsTree( + (await categoryRepository.findOne({ cat_name: "parent" }))! + ); + + tree.should.deep.include({ + cat_id: 1, + cat_name: "parent", + children: [ + { + cat_id: 2, + cat_name: "child", + children: [], + }, + ], + }); + }) + )); +}); diff --git a/test/github-issues/6948/entity/Category.ts b/test/github-issues/6948/entity/Category.ts new file mode 100644 index 00000000000..66e7d15ef25 --- /dev/null +++ b/test/github-issues/6948/entity/Category.ts @@ -0,0 +1,17 @@ +import { Entity, PrimaryGeneratedColumn, Column, Tree, TreeParent, TreeChildren } from "../../../../src"; + +@Entity() +@Tree("materialized-path") +export class Category { + @PrimaryGeneratedColumn() + cat_id: number; + + @Column() + cat_name: string; + + @TreeParent() + cat_parent: Category; + + @TreeChildren({ cascade: true }) + cat_children: Category[]; +} diff --git a/test/github-issues/6948/issue-6948.ts b/test/github-issues/6948/issue-6948.ts new file mode 100644 index 00000000000..0891f1a1870 --- /dev/null +++ b/test/github-issues/6948/issue-6948.ts @@ -0,0 +1,38 @@ +import "reflect-metadata"; +import { Category } from "./entity/Category"; +import { Connection } from "../../../src/connection/Connection"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases, +} from "../../../test/utils/test-utils"; + +describe("github issues > #6948 TreeRepository's findRoots query incorrectly when using a custom primary key", () => { + let connections: Connection[]; + before( + async () => + (connections = await createTestingConnections({ + entities: [Category], + })) + ); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("entity parent column should work with custom primary column names ", () => + Promise.all( + connections.map(async (connection) => { + const categoryRepository = connection.getTreeRepository( + Category + ); + await categoryRepository.save( + categoryRepository.create({ + cat_name: "Root node", + }) + ); + const rootNodes = await categoryRepository.findRoots(); + rootNodes[0].should.deep.include({ + cat_name: "Root node", + }); + }) + )); +}); diff --git a/test/github-issues/6990/entity/foo.ts b/test/github-issues/6990/entity/foo.ts new file mode 100644 index 00000000000..a76c47b9276 --- /dev/null +++ b/test/github-issues/6990/entity/foo.ts @@ -0,0 +1,15 @@ +import { Column, Entity, PrimaryGeneratedColumn } from "../../../../src"; + +@Entity() +export class Foo { + @PrimaryGeneratedColumn() + id: number; + + @Column({ + array: true, + type: "varchar", + length: 64, + nullable: true, + }) + varchararray: string[]; +} diff --git a/test/github-issues/6990/issue-6990.ts b/test/github-issues/6990/issue-6990.ts new file mode 100644 index 00000000000..7606ea3cba5 --- /dev/null +++ b/test/github-issues/6990/issue-6990.ts @@ -0,0 +1,46 @@ +import "reflect-metadata"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { Foo } from "./entity/foo"; +import { expect } from "chai"; + +describe("github issues > #6990 synchronize drops array columns in postgres if a length is set", () => { + let connections: Connection[]; + before( + async () => + (connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + subscribers: [__dirname + "/subscriber/*{.js,.ts}"], + enabledDrivers: ["postgres"], + })) + ); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should not drop varchar array column on synchronize using postgres driver", () => + Promise.all( + connections.map(async function (connection) { + const foo = new Foo(); + foo.id = 1; + foo.varchararray = ["able", "baker", "charlie"]; + await connection.manager.save(foo); + + await connection.synchronize(); + + const loadedFoo: + | Foo + | undefined = await connection.manager.findOne(Foo, 1); + + expect(loadedFoo).to.be.not.empty; + expect(loadedFoo!.varchararray).to.deep.eq([ + "able", + "baker", + "charlie", + ]); + }) + )); +}); diff --git a/test/github-issues/6995/entity/default-update-date.ts b/test/github-issues/6995/entity/default-update-date.ts new file mode 100644 index 00000000000..ba95bcec3b0 --- /dev/null +++ b/test/github-issues/6995/entity/default-update-date.ts @@ -0,0 +1,16 @@ +import { PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, Entity } from '../../../../src'; + +@Entity() +export class DefaultUpdateDate { + + @PrimaryGeneratedColumn({ + type: "int" + }) + public id: number; + + @CreateDateColumn() + public createdDate: Date; + + @UpdateDateColumn() + public updatedDate: Date; +} \ No newline at end of file diff --git a/test/github-issues/6995/issue-6995.ts b/test/github-issues/6995/issue-6995.ts new file mode 100644 index 00000000000..02a4057f51e --- /dev/null +++ b/test/github-issues/6995/issue-6995.ts @@ -0,0 +1,25 @@ +import "reflect-metadata"; +import { Connection } from "../../../src"; +import { createTestingConnections, closeTestingConnections } from "../../utils/test-utils"; +import { DefaultUpdateDate } from './entity/default-update-date'; + +describe("github issues > #6995 Generating migrations for UpdateDateColumn should generate on update clause", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + migrations: [], + enabledDrivers: ["mysql", "mariadb"], + schemaCreate: false, + dropSchema: true, + entities: [DefaultUpdateDate] + })); + after(() => closeTestingConnections(connections)); + + it("should create migration with default ON UPDATE clause", () => Promise.all(connections.map(async connection => { + + const sqlInMemory = await connection.driver.createSchemaBuilder().log(); + sqlInMemory.upQueries.filter(i => i.query.includes("ON UPDATE")).length.should.be.greaterThan(0); + + }))); + +}); \ No newline at end of file diff --git a/test/github-issues/7030/entity/Post.ts b/test/github-issues/7030/entity/Post.ts new file mode 100644 index 00000000000..306188a6587 --- /dev/null +++ b/test/github-issues/7030/entity/Post.ts @@ -0,0 +1,20 @@ +import { Column, Entity, PrimaryGeneratedColumn } from '../../../../src'; + + +@Entity() +export class Post { + + @PrimaryGeneratedColumn({ + type: 'integer', + name: 'id', + }) + oldId: number; + + @Column({ + nullable: false, + unique: true, + length: 38, + name: 'new_id', + }) + id: string; +} diff --git a/test/github-issues/7030/issue-7030.ts b/test/github-issues/7030/issue-7030.ts new file mode 100644 index 00000000000..9dcd705e27f --- /dev/null +++ b/test/github-issues/7030/issue-7030.ts @@ -0,0 +1,37 @@ +import { expect } from "chai"; +import {Connection} from "../../../src/connection/Connection"; +import { + createTestingConnections, + closeTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import {Post} from "./entity/Post"; + +describe("github issues > #7030", () => { + let connections: Connection[]; + + before(async () => connections = await createTestingConnections({ + entities: [Post], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["postgres"] + })); + + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should insert and fetch from the expected column", () => Promise.all(connections.map(async connection => { + const id = '123e4567-e89b-12d3-a456-426614174000' + + const post = new Post(); + post.id = id; + + let postRepository = connection.getRepository(Post); + + await postRepository.save(post); + + const actualPost = await postRepository.findOneOrFail({ id }); + + expect(actualPost!.id).to.be.equal(id); + }))); +}); diff --git a/test/github-issues/7068/entity/Category.ts b/test/github-issues/7068/entity/Category.ts new file mode 100644 index 00000000000..dfe54addc88 --- /dev/null +++ b/test/github-issues/7068/entity/Category.ts @@ -0,0 +1,30 @@ +import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import {Column} from "../../../../src/decorator/columns/Column"; +import {TreeParent} from "../../../../src/decorator/tree/TreeParent"; +import {TreeChildren} from "../../../../src/decorator/tree/TreeChildren"; +import {Entity} from "../../../../src/decorator/entity/Entity"; +import {Tree} from "../../../../src/decorator/tree/Tree"; + +@Entity() +@Tree("closure-table", { + closureTableName: "category_xyz_closure", + ancestorColumnName: (column) => "ancestor_xyz_" + column.propertyName, + descendantColumnName: (column) => "descendant_xyz_" + column.propertyName, +}) +export class Category { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @TreeParent() + parentCategory: Category; + + @TreeChildren({cascade: true}) + childCategories: Category[]; + + // @TreeLevelColumn() + // level: number; +} diff --git a/test/github-issues/7068/issue-7068.ts b/test/github-issues/7068/issue-7068.ts new file mode 100644 index 00000000000..f961c284e18 --- /dev/null +++ b/test/github-issues/7068/issue-7068.ts @@ -0,0 +1,190 @@ +import "reflect-metadata"; +import {Category} from "./entity/Category"; +import {Connection} from "../../../src/connection/Connection"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; + +describe.only("github issues > #7068", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [Category] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("categories should be attached via parent and saved properly", () => Promise.all(connections.map(async connection => { + const categoryRepository = connection.getTreeRepository(Category); + + const a1 = new Category(); + a1.name = "a1"; + await categoryRepository.save(a1); + + const a11 = new Category(); + a11.name = "a11"; + a11.parentCategory = a1; + await categoryRepository.save(a11); + + const a12 = new Category(); + a12.name = "a12"; + a12.parentCategory = a1; + await categoryRepository.save(a12); + + const rootCategories = await categoryRepository.findRoots(); + rootCategories.should.be.eql([{ + id: 1, + name: "a1" + }]); + + const a11Parent = await categoryRepository.findAncestors(a11); + a11Parent.length.should.be.equal(2); + a11Parent.should.deep.include({ id: 1, name: "a1" }); + a11Parent.should.deep.include({ id: 2, name: "a11" }); + + const a1Children = await categoryRepository.findDescendants(a1); + a1Children.length.should.be.equal(3); + a1Children.should.deep.include({ id: 1, name: "a1" }); + a1Children.should.deep.include({ id: 2, name: "a11" }); + a1Children.should.deep.include({ id: 3, name: "a12" }); + }))); + + it("categories should be attached via children and saved properly", () => Promise.all(connections.map(async connection => { + const categoryRepository = connection.getTreeRepository(Category); + + const a1 = new Category(); + a1.name = "a1"; + await categoryRepository.save(a1); + + const a11 = new Category(); + a11.name = "a11"; + + const a12 = new Category(); + a12.name = "a12"; + + a1.childCategories = [a11, a12]; + await categoryRepository.save(a1); + + const rootCategories = await categoryRepository.findRoots(); + rootCategories.should.be.eql([{ + id: 1, + name: "a1" + }]); + + const a11Parent = await categoryRepository.findAncestors(a11); + a11Parent.length.should.be.equal(2); + a11Parent.should.deep.include({ id: 1, name: "a1" }); + a11Parent.should.deep.include({ id: 2, name: "a11" }); + + const a1Children = await categoryRepository.findDescendants(a1); + a1Children.length.should.be.equal(3); + a1Children.should.deep.include({ id: 1, name: "a1" }); + a1Children.should.deep.include({ id: 2, name: "a11" }); + a1Children.should.deep.include({ id: 3, name: "a12" }); + }))); + + it("categories should be attached via children and saved properly and everything must be saved in cascades", () => Promise.all(connections.map(async connection => { + const categoryRepository = connection.getTreeRepository(Category); + + const a1 = new Category(); + a1.name = "a1"; + + const a11 = new Category(); + a11.name = "a11"; + + const a12 = new Category(); + a12.name = "a12"; + + const a111 = new Category(); + a111.name = "a111"; + + const a112 = new Category(); + a112.name = "a112"; + + a1.childCategories = [a11, a12]; + a11.childCategories = [a111, a112]; + await categoryRepository.save(a1); + + const rootCategories = await categoryRepository.findRoots(); + rootCategories.should.be.eql([{ + id: 1, + name: "a1" + }]); + + const a11Parent = await categoryRepository.findAncestors(a11); + a11Parent.length.should.be.equal(2); + a11Parent.should.deep.include({ id: 1, name: "a1" }); + a11Parent.should.deep.include({ id: 2, name: "a11" }); + + const a1Children = await categoryRepository.findDescendants(a1); + const a1ChildrenNames = a1Children.map(child => child.name); + a1ChildrenNames.length.should.be.equal(5); + a1ChildrenNames.should.deep.include("a1"); + a1ChildrenNames.should.deep.include("a11"); + a1ChildrenNames.should.deep.include("a12"); + a1ChildrenNames.should.deep.include("a111"); + a1ChildrenNames.should.deep.include("a112"); + }))); + + // todo: finish implementation and implement on other trees + it.skip("categories should remove removed children", () => Promise.all(connections.map(async connection => { + const categoryRepository = connection.getTreeRepository(Category); + + const a1 = new Category(); + a1.name = "a1"; + const a11 = new Category(); + a11.name = "a11"; + const a12 = new Category(); + a12.name = "a12"; + a1.childCategories = [a11, a12]; + await categoryRepository.save(a1); + + const a1Children1 = await categoryRepository.findDescendants(a1); + const a1ChildrenNames1 = a1Children1.map(child => child.name); + a1ChildrenNames1.length.should.be.equal(3); + a1ChildrenNames1.should.deep.include("a1"); + a1ChildrenNames1.should.deep.include("a11"); + a1ChildrenNames1.should.deep.include("a12"); + + // a1.childCategories = [a11]; + // await categoryRepository.save(a1); + // + // const a1Children2 = await categoryRepository.findDescendants(a1); + // const a1ChildrenNames2 = a1Children2.map(child => child.name); + // a1ChildrenNames2.length.should.be.equal(3); + // a1ChildrenNames2.should.deep.include("a1"); + // a1ChildrenNames2.should.deep.include("a11"); + // a1ChildrenNames2.should.deep.include("a12"); + }))); + + // todo: finish implementation and implement on other trees + it.skip("sub-category should be removed with all its children", () => Promise.all(connections.map(async connection => { + const categoryRepository = connection.getTreeRepository(Category); + + const a1 = new Category(); + a1.name = "a1"; + const a11 = new Category(); + a11.name = "a11"; + const a12 = new Category(); + a12.name = "a12"; + a1.childCategories = [a11, a12]; + await categoryRepository.save(a1); + + const a1Children1 = await categoryRepository.findDescendants(a1); + const a1ChildrenNames1 = a1Children1.map(child => child.name); + a1ChildrenNames1.length.should.be.equal(3); + a1ChildrenNames1.should.deep.include("a1"); + a1ChildrenNames1.should.deep.include("a11"); + a1ChildrenNames1.should.deep.include("a12"); + + await categoryRepository.remove(a1); + + // a1.childCategories = [a11]; + // await categoryRepository.save(a1); + // + // const a1Children2 = await categoryRepository.findDescendants(a1); + // const a1ChildrenNames2 = a1Children2.map(child => child.name); + // a1ChildrenNames2.length.should.be.equal(3); + // a1ChildrenNames2.should.deep.include("a1"); + // a1ChildrenNames2.should.deep.include("a11"); + // a1ChildrenNames2.should.deep.include("a12"); + }))); +}); diff --git a/test/github-issues/7079/entities.ts b/test/github-issues/7079/entities.ts new file mode 100644 index 00000000000..bda9c4784ed --- /dev/null +++ b/test/github-issues/7079/entities.ts @@ -0,0 +1,43 @@ +import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "../../../src"; + + +@Entity() +export class User { + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @OneToMany( + () => Post, + (post) => post.user, + ) + posts: Post[]; +} + +export class PublishInfo { + @Column({ nullable: true }) + date: Date; +} + +@Entity() +export class Post { + @PrimaryGeneratedColumn() + id: number; + + @Column() + text: string; + + @Column(_type => PublishInfo) + blog: PublishInfo; + + @Column(_type => PublishInfo) + newsletter: PublishInfo; + + @ManyToOne( + () => User, + (user) => user.posts, + ) + user: User +} \ No newline at end of file diff --git a/test/github-issues/7079/issue-7079.ts b/test/github-issues/7079/issue-7079.ts new file mode 100644 index 00000000000..48b45f9a884 --- /dev/null +++ b/test/github-issues/7079/issue-7079.ts @@ -0,0 +1,62 @@ +import "reflect-metadata"; +import { createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import {Post, User} from "./entities" + +describe("github issues > #7079 Error when sorting by an embedded entity while using join and skip/take", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [Post, User], + schemaCreate: true, + dropSchema: true, + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should be able to getMany with join and sorting by an embedded entity column while user take and skip", () => Promise.all(connections.map(async connection => { + const postRepo = connection.getRepository(Post) + const userRepo = connection.getRepository(User) + + const users = [ + { id: 1, name: "Mike" }, + { id: 2, name: "Alice" } + ] + await userRepo.save(users) + + const posts = [ + { + id: 1, + text: "Happy Holidays", + userId: 1, + blog: { date: new Date().toISOString() }, + newsletter: { date: new Date().toISOString() } + }, + { + id: 2, + text: "My Vacation", + userId: 1, + blog: { date: new Date().toISOString() }, + newsletter: { date: new Date().toISOString() } + }, + { + id: 3, + text: "Working with TypeORM", + userId: 2, + blog: { date: new Date().toISOString() }, + newsletter: { date: new Date().toISOString() } + } + ] + await postRepo.save(posts); + + const result = await postRepo.createQueryBuilder("post") + .leftJoinAndSelect("post.user", "user") + .orderBy("post.blog.date") + .take(2) + .skip(1) + .getMany(); + expect(result.length).eq(2); + }))); + +}); \ No newline at end of file diff --git a/test/github-issues/7106/entity/ReallyReallyVeryVeryVeryLongTableName.ts b/test/github-issues/7106/entity/ReallyReallyVeryVeryVeryLongTableName.ts new file mode 100644 index 00000000000..b9a8f7f132f --- /dev/null +++ b/test/github-issues/7106/entity/ReallyReallyVeryVeryVeryLongTableName.ts @@ -0,0 +1,17 @@ +import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import {Column, Entity, Generated } from "../../../../src"; + +@Entity() +export class ReallyReallyVeryVeryVeryLongTableName { + @PrimaryGeneratedColumn() // typeORM requires a pkey + PrimaryGeneratedColumnIDBlahBlahBlahThisIsReallyLong: number; + + @Column() + Name: string; + + @Column() @Generated("increment") + MyNumber: number; + + +} + diff --git a/test/github-issues/7106/entity/ShortTableName.ts b/test/github-issues/7106/entity/ShortTableName.ts new file mode 100644 index 00000000000..cef4f851130 --- /dev/null +++ b/test/github-issues/7106/entity/ShortTableName.ts @@ -0,0 +1,15 @@ +import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import {Column, Entity } from "../../../../src"; + +@Entity() +export class ShortTableName { + @PrimaryGeneratedColumn() // typeORM requires a pkey + PrimaryGeneratedColumnIDBlahBlahBlahThisIsReallyLong: number; + + @Column() + Name: string; + + @Column() + Value: number; +} + diff --git a/test/github-issues/7106/issue-7106.ts b/test/github-issues/7106/issue-7106.ts new file mode 100644 index 00000000000..c815102a478 --- /dev/null +++ b/test/github-issues/7106/issue-7106.ts @@ -0,0 +1,41 @@ +import "reflect-metadata"; +import {expect} from "chai"; +import {Connection} from "../../../src/connection/Connection"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import { ShortTableName } from "./entity/ShortTableName"; +import { ReallyReallyVeryVeryVeryLongTableName } from "./entity/ReallyReallyVeryVeryVeryLongTableName"; +import { QueryFailedError } from "../../../src/error/QueryFailedError"; + + +/** + * @see https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + * "The system uses no more than NAMEDATALEN-1 bytes of an identifier; longer names can be + * written in commands, but they will be truncated. By default, NAMEDATALEN is 64 so the + * maximum identifier length is 63 bytes. If this limit is problematic, it can be raised + * by changing the NAMEDATALEN constant in src/include/pg_config_manual.h." + */ +describe("github issues > #7106 shorten sequence names (for RDBMS with a limit) when they are longer than 63 characters", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["postgres"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should be able to work with long sequence name with short table name", () => Promise.all(connections.map(async (connection) => { + const short = new ShortTableName(); + short.Name = "Dharawal"; + short.Value = 2500; + await connection.getRepository(ShortTableName).save(short); + return expect(connection.synchronize()).to.not.be.rejectedWith(QueryFailedError); + }))); + + it("should be able to work with long sequence name with long table name", () => Promise.all(connections.map(async (connection) => { + const long = new ReallyReallyVeryVeryVeryLongTableName(); + long.Name = "Eora"; + await connection.getRepository(ReallyReallyVeryVeryVeryLongTableName).save(long); + return expect(connection.synchronize()).to.not.be.rejectedWith(QueryFailedError); + }))); + +}); diff --git a/test/github-issues/7109/entity/Dummy.ts b/test/github-issues/7109/entity/Dummy.ts new file mode 100644 index 00000000000..9c91d783a1c --- /dev/null +++ b/test/github-issues/7109/entity/Dummy.ts @@ -0,0 +1,13 @@ +import {Entity, PrimaryGeneratedColumn} from "../../../../src"; +import {Column} from "../../../../src/decorator/columns/Column"; + +@Entity() +export class Dummy { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + field: string; + +} \ No newline at end of file diff --git a/test/github-issues/7109/issue-7109.ts b/test/github-issues/7109/issue-7109.ts new file mode 100644 index 00000000000..5474540b80b --- /dev/null +++ b/test/github-issues/7109/issue-7109.ts @@ -0,0 +1,46 @@ +import "reflect-metadata"; +import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Connection} from "../../../src/connection/Connection"; +import {Dummy} from './entity/Dummy'; +import {ReadStream} from 'fs'; +import {expect} from "chai"; + +function ingestStream (stream: ReadStream): Promise { + let chunks: any[] = []; + return new Promise((resolve, reject) => { + stream.on('data', chunk => chunks.push(chunk)) + stream.on('error', reject) + stream.on('end', () => resolve(chunks)) + }) + } + +describe("github issues > #7109 stream() bug from 0.2.25 to 0.2.26 with postgresql", () => { + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + enabledDrivers: ["postgres", "mysql", "mariadb", "cockroachdb"] + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should release the QueryRunner created by a SelectQueryBuilder", () => Promise.all(connections.map(async connection => { + const values = [{field: "abc"}, {field: "def"}, {field: "ghi"}]; + // First create some test data + await connection.createQueryBuilder() + .insert() + .into(Dummy) + .values(values) + .execute(); + + // Stream data: + const stream = await connection.createQueryBuilder().from(Dummy, 'dummy').select('field').stream(); + const streamedEntities = await ingestStream(stream); + + // If the runner is properly released, the test is already successful; this assert is just a sanity check. + const extractFields = (val: {field: string}) => val.field; + expect(streamedEntities.map(extractFields)).to.have.members(values.map(extractFields)); + + }))); +}); \ No newline at end of file diff --git a/test/github-issues/7146/entity/Category.ts b/test/github-issues/7146/entity/Category.ts new file mode 100644 index 00000000000..d04a78b6781 --- /dev/null +++ b/test/github-issues/7146/entity/Category.ts @@ -0,0 +1,18 @@ +import { Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "../../../../src"; +import { Post } from "./Post"; + +@Entity() +export class Category { + + @PrimaryGeneratedColumn() + id: number; + + @OneToOne(type => Post, post => post.lazyOneToOne, { nullable: true, eager: false }) + @JoinColumn() + backRef1: Post; + + @OneToOne(type => Post, post => post.eagerOneToOne, { nullable: true, eager: false }) + @JoinColumn() + backRef2: Post; + +} diff --git a/test/github-issues/7146/entity/Post.ts b/test/github-issues/7146/entity/Post.ts new file mode 100644 index 00000000000..08414825b47 --- /dev/null +++ b/test/github-issues/7146/entity/Post.ts @@ -0,0 +1,32 @@ +import { Entity, JoinColumn, ManyToOne, OneToOne, PrimaryGeneratedColumn } from "../../../../src"; +import { Category } from "./Category"; + +@Entity() +export class Post { + + @PrimaryGeneratedColumn() + id: number; + + @ManyToOne(type => Category, { nullable: true, eager: false }) + lazyManyToOne: Promise; + + @ManyToOne(type => Category, { nullable: true, eager: true }) + eagerManyToOne: Category | null; + + @OneToOne(type => Category, { nullable: true, eager: false }) + @JoinColumn() + lazyOneToOneOwner: Promise; + + @OneToOne(type => Category, { nullable: true, eager: true }) + @JoinColumn() + eagerOneToOneOwner: Category | null; + + // Not a column; actual value is stored on the other side of this relation + @OneToOne(type => Category, category => category.backRef1, { eager: false }) + lazyOneToOne: Promise; + + // Not a column; actual value is stored on the other side of this relation + @OneToOne(type => Category, category => category.backRef2, { eager: true }) + eagerOneToOne: Category | null; + +} diff --git a/test/github-issues/7146/issue-7146.ts b/test/github-issues/7146/issue-7146.ts new file mode 100644 index 00000000000..dac333a9a99 --- /dev/null +++ b/test/github-issues/7146/issue-7146.ts @@ -0,0 +1,92 @@ +import "reflect-metadata"; +import { createTestingConnections, closeTestingConnections, reloadTestingDatabases } from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import { Post } from "./entity/Post"; + +describe("github issues > #7146 Lazy relations resolve to 'undefined' instead of 'null'", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + schemaCreate: true, + dropSchema: true, + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + async function prepareData(connection: Connection) { + const savedPost = new Post(); + await connection.manager.save(savedPost); + } + + // The following 3 tests hilight the reported issue. + // The remaining 6 tests were already succeeding before, but are included for completeness sake. + + describe("lazy-loaded relations", () => { + + it("should return null if ManyToOne relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1)); + expect(await post.lazyManyToOne).to.be.null; + }))); + + it("should return null if OneToOne+JoinColumn relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1)); + expect(await post.lazyOneToOneOwner).to.be.null; + }))); + + it("should return null if OneToOne relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1)); + expect(await post.lazyOneToOne).to.be.null; + }))); + + }); + + describe("lazy-loaded relations included in 'relations' find option", () => { + + it("should return null if ManyToOne relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1, { relations: ['lazyManyToOne'] })); + expect(await post.lazyManyToOne).to.be.null; + }))); + + it("should return null if OneToOne+JoinColumn relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1, { relations: ['lazyOneToOneOwner'] })); + expect(await post.lazyOneToOneOwner).to.be.null; + }))); + + it("should return null if OneToOne relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1, { relations: ['lazyOneToOne'] })); + expect(await post.lazyOneToOne).to.be.null; + }))); + + }); + + describe("eager-loaded relations", () => { + + it("should return null if ManyToOne relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1)); + expect(post.eagerManyToOne).to.be.null; + }))); + + it("should return null if OneToOne+JoinColumn relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1)); + expect(post.eagerOneToOneOwner).to.be.null; + }))); + + it("should return null if OneToOne relation has NULL in database", () => Promise.all(connections.map(async connection => { + await prepareData(connection); + const post = (await connection.manager.findOneOrFail(Post, 1)); + expect(post.eagerOneToOne).to.be.null; + }))); + + }); + +}); diff --git a/test/github-issues/7203/issue-7203.ts b/test/github-issues/7203/issue-7203.ts new file mode 100644 index 00000000000..8bd38357d9d --- /dev/null +++ b/test/github-issues/7203/issue-7203.ts @@ -0,0 +1,23 @@ +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../utils/test-utils"; +import {Connection} from "../../../src/connection/Connection"; +import {expect} from "chai"; + +describe("github issues > #7203 QueryExpressionMap doesn't clone comment field", () => { + let connections: Connection[]; + before( + async () => + (connections = await createTestingConnections({ + dropSchema: true, + enabledDrivers: ["postgres"], + })) + ); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should be able to clone comment field", () => Promise.all(connections.map(async connection => { + const comment = "a comment"; + const queryBuilder = await connection.createQueryBuilder().comment(comment); + const clonedQueryBuilder = queryBuilder.clone(); + expect(clonedQueryBuilder.expressionMap.comment).to.be.eq(comment); + }))); +}); diff --git a/test/github-issues/798/issue-798.ts b/test/github-issues/798/issue-798.ts index 445cbb31263..34a05c6775b 100644 --- a/test/github-issues/798/issue-798.ts +++ b/test/github-issues/798/issue-798.ts @@ -28,4 +28,11 @@ describe("github issues > #798 sqlite: 'database' path in ormconfig.json is not assert.strictEqual(connection.isConnected, true); }); + it("should find the sqlite database if the cwd is changed for better-sqlite3", async function () { + const options = await getConnectionOptions("better-sqlite3"); + connection = await createConnection(options); + + assert.strictEqual(connection.isConnected, true); + }); + }); \ No newline at end of file diff --git a/test/github-issues/799/issue-799.ts b/test/github-issues/799/issue-799.ts index 28152caff3b..d87a1c2ec4f 100644 --- a/test/github-issues/799/issue-799.ts +++ b/test/github-issues/799/issue-799.ts @@ -34,4 +34,14 @@ describe("github issues > #799 sqlite: 'database' path should be created", () => assert.strictEqual(connection.isConnected, true); }); + it("should create the whole path to database file for better-sqlite3", async function () { + connection = await createConnection({ + "name": "better-sqlite3", + "type": "better-sqlite3", + "database": path + }); + + assert.strictEqual(connection.isConnected, true); + }); + }); diff --git a/test/github-issues/863/issue-863.ts b/test/github-issues/863/issue-863.ts index 53a89a76f6f..937a22f6989 100644 --- a/test/github-issues/863/issue-863.ts +++ b/test/github-issues/863/issue-863.ts @@ -5,7 +5,7 @@ import { Connection } from "../../../src/connection/Connection"; import { Master } from "./entities/master"; import { Detail } from "./entities/detail"; -describe("indices > create schema", () => { +describe("github issues > #863 indices > create schema", () => { let connections: Connection[]; before(async () => connections = await createTestingConnections({ diff --git a/test/utils/test-utils.ts b/test/utils/test-utils.ts index 093df0c6917..ac76f4487ec 100644 --- a/test/utils/test-utils.ts +++ b/test/utils/test-utils.ts @@ -6,7 +6,6 @@ import {DatabaseType} from "../../src/driver/types/DatabaseType"; import {EntitySchema} from "../../src/entity-schema/EntitySchema"; import {createConnections} from "../../src/index"; import {NamingStrategyInterface} from "../../src/naming-strategy/NamingStrategyInterface"; -import {PromiseUtils} from "../../src/util/PromiseUtils"; import {QueryResultCache} from "../../src/cache/QueryResultCache"; import {Logger} from "../../src/logger/Logger"; @@ -252,7 +251,10 @@ export async function createTestingConnections(options?: TestingOptions): Promis }); const queryRunner = connection.createQueryRunner(); - await PromiseUtils.runInSequence(databases, database => queryRunner.createDatabase(database, true)); + + for (const database of databases) { + await queryRunner.createDatabase(database, true); + } // create new schemas if (connection.driver instanceof PostgresDriver || connection.driver instanceof SqlServerDriver) { @@ -269,7 +271,9 @@ export async function createTestingConnections(options?: TestingOptions): Promis if (schema && schemaPaths.indexOf(schema) === -1) schemaPaths.push(schema); - await PromiseUtils.runInSequence(schemaPaths, schemaPath => queryRunner.createSchema(schemaPath, true)); + for (const schemaPath of schemaPaths) { + await queryRunner.createSchema(schemaPath, true); + } } await queryRunner.release(); diff --git a/tsconfig.json b/tsconfig.json index 4dafdb0c76d..6dee19ac7a9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,9 @@ { - "version": "2.1.1", "compilerOptions": { "lib": ["es5", "es6"], "outDir": "build/compiled", "allowSyntheticDefaultImports": true, - "esModuleInterop": true, + "esModuleInterop": true, "target": "es5", "module": "commonjs", "moduleResolution": "node", @@ -22,6 +21,11 @@ "noUnusedLocals": true, "downlevelIteration": true }, + "include": [ + "sample", + "src", + "test" + ], "exclude": [ "tmp", "temp", diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 550a8cf6e29..00000000000 --- a/tslint.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "rules": { - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "indent": [ - true, - "spaces" - ], - "no-unused-variable": true, - "no-duplicate-variable": true, - "no-eval": true, - "no-internal-module": true, - "no-var-keyword": true, - "one-line": [ - true, - "check-open-brace", - "check-whitespace" - ], - "quotemark": [ - true, - "double" - ], - "semicolon": true, - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "variable-name": [ - true, - "ban-keywords" - ], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ] - } -} \ No newline at end of file