mirror of
https://github.com/nestjs/nest.git
synced 2026-02-24 00:02:56 +00:00
Compare commits
2 Commits
v10.2.1
...
fix/fastif
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5612e0197 | ||
|
|
6c54448bf7 |
@@ -1,31 +1,9 @@
|
||||
version: 2.1
|
||||
|
||||
parameters:
|
||||
check-legacy-node-version:
|
||||
type: boolean
|
||||
default: false
|
||||
legacy-node-version:
|
||||
type: string
|
||||
default: "14.21.3"
|
||||
maintenance-node-version:
|
||||
type: string
|
||||
default: "16.20"
|
||||
active-node-version:
|
||||
type: string
|
||||
default: "18.17"
|
||||
current-node-version:
|
||||
type: string
|
||||
default: "20.5"
|
||||
version: 2
|
||||
|
||||
aliases:
|
||||
- &restore-cache
|
||||
restore_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
- &save-cache
|
||||
save_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ./node_modules
|
||||
- &install-deps
|
||||
run:
|
||||
name: Install dependencies
|
||||
@@ -39,73 +17,70 @@ aliases:
|
||||
name: Test
|
||||
command: npm run test
|
||||
|
||||
unit-tests-template: &unit-tests-template
|
||||
working_directory: ~/nest
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- *run-unit-tests
|
||||
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
|
||||
- image: cimg/node:16.19
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Update NPM version
|
||||
command: 'sudo npm install -g npm@^9'
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *save-cache
|
||||
- *build-packages
|
||||
command: 'sudo npm install -g npm@^8'
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: npm ci --legacy-peer-deps
|
||||
- save_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ./node_modules
|
||||
- run:
|
||||
name: Build
|
||||
command: npm run build
|
||||
|
||||
test:
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
test_node_16:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< parameters.node-version >>
|
||||
- image: cimg/node:16.19
|
||||
steps:
|
||||
- when:
|
||||
condition:
|
||||
and:
|
||||
- equal: [ "<< parameters.node-version >>", "<< pipeline.parameters.legacy-node-version >>" ]
|
||||
- not: << pipeline.parameters.check-legacy-node-version >>
|
||||
steps:
|
||||
- run:
|
||||
name: Skip
|
||||
command: |
|
||||
echo Skipping
|
||||
- when:
|
||||
condition:
|
||||
or:
|
||||
- not:
|
||||
equal: [ "<< parameters.node-version >>", "<< pipeline.parameters.legacy-node-version >>" ]
|
||||
- << pipeline.parameters.check-legacy-node-version >>
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- when:
|
||||
condition:
|
||||
equal: [ "<< parameters.node-version >>", "<< pipeline.parameters.maintenance-node-version >>" ]
|
||||
steps:
|
||||
- run:
|
||||
name: Test (coverage)
|
||||
command: npm run test:cov
|
||||
- run:
|
||||
name: Collect coverage
|
||||
command: npm run coverage
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
- when:
|
||||
condition:
|
||||
not:
|
||||
equal: [ "<< parameters.node-version >>", "<< pipeline.parameters.maintenance-node-version >>" ]
|
||||
steps:
|
||||
- *run-unit-tests
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- run:
|
||||
name: Test (coverage)
|
||||
command: npm run test:cov
|
||||
- run:
|
||||
name: Collect coverage
|
||||
command: npm run coverage
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
|
||||
test_node_18:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
- image: cimg/node:18.14
|
||||
|
||||
test_node_19:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
- image: cimg/node:19.8
|
||||
|
||||
lint:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
|
||||
- image: circleci/node:16
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
@@ -118,64 +93,44 @@ jobs:
|
||||
command: ./node_modules/.bin/commitlint-circle -c .commitlintrc.json
|
||||
|
||||
integration_tests:
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
working_directory: ~/nest
|
||||
machine: true
|
||||
steps:
|
||||
- when:
|
||||
condition:
|
||||
and:
|
||||
- equal: [ "<< parameters.node-version >>", "<< pipeline.parameters.legacy-node-version >>" ]
|
||||
- not: << pipeline.parameters.check-legacy-node-version >>
|
||||
steps:
|
||||
- run:
|
||||
name: Skip
|
||||
command: |
|
||||
echo Skipping
|
||||
- when:
|
||||
condition:
|
||||
or:
|
||||
- not:
|
||||
equal: [ "<< parameters.node-version >>", "<< pipeline.parameters.legacy-node-version >>" ]
|
||||
- << pipeline.parameters.check-legacy-node-version >>
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Prepare nvm
|
||||
command: |
|
||||
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
|
||||
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
|
||||
- run:
|
||||
name: Upgrade Node.js
|
||||
command: |
|
||||
nvm install << parameters.node-version >>
|
||||
node -v
|
||||
nvm alias default << parameters.node-version >>
|
||||
- run:
|
||||
name: Install Docker Compose
|
||||
command: |
|
||||
curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
|
||||
chmod +x ~/docker-compose
|
||||
sudo mv ~/docker-compose /usr/local/bin/docker-compose
|
||||
- *install-deps
|
||||
- run:
|
||||
name: Prepare tests
|
||||
command: |
|
||||
bash ./scripts/prepare.sh
|
||||
sleep 10
|
||||
- run:
|
||||
name: List containers
|
||||
command: docker ps
|
||||
- run:
|
||||
name: Integration tests
|
||||
command: npm run test:integration
|
||||
- checkout
|
||||
- run:
|
||||
name: Prepare nvm
|
||||
command: |
|
||||
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
|
||||
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
|
||||
- run:
|
||||
name: Upgrade Node.js
|
||||
command: |
|
||||
nvm install v16
|
||||
node -v
|
||||
nvm alias default v16
|
||||
- run:
|
||||
name: Install Docker Compose
|
||||
command: |
|
||||
curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
|
||||
chmod +x ~/docker-compose
|
||||
sudo mv ~/docker-compose /usr/local/bin/docker-compose
|
||||
- *install-deps
|
||||
- run:
|
||||
name: Prepare tests
|
||||
command: |
|
||||
bash ./scripts/prepare.sh
|
||||
sleep 10
|
||||
- run:
|
||||
name: List containers
|
||||
command: docker ps
|
||||
- run:
|
||||
name: Integration tests
|
||||
command: npm run test:integration
|
||||
|
||||
codechecks_benchmarks:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
|
||||
- image: cimg/node:16.19
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
@@ -191,9 +146,9 @@ jobs:
|
||||
samples:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
|
||||
- image: cimg/node:16.19
|
||||
environment:
|
||||
DISABLE_OPENCOLLECTIVE: "true"
|
||||
- DISABLE_OPENCOLLECTIVE: true
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
@@ -203,34 +158,22 @@ jobs:
|
||||
command: npm run build:samples
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-test:
|
||||
jobs:
|
||||
- build
|
||||
- test:
|
||||
- test_node_16:
|
||||
requires:
|
||||
- build
|
||||
- test_node_18:
|
||||
requires:
|
||||
- build
|
||||
matrix:
|
||||
parameters:
|
||||
node-version: [
|
||||
"<< pipeline.parameters.legacy-node-version >>",
|
||||
"<< pipeline.parameters.maintenance-node-version >>",
|
||||
"<< pipeline.parameters.active-node-version >>",
|
||||
"<< pipeline.parameters.current-node-version >>"
|
||||
]
|
||||
- lint:
|
||||
requires:
|
||||
- build
|
||||
- integration_tests:
|
||||
requires:
|
||||
- build
|
||||
matrix:
|
||||
parameters:
|
||||
node-version: [
|
||||
"<< pipeline.parameters.legacy-node-version >>",
|
||||
"<< pipeline.parameters.maintenance-node-version >>",
|
||||
"<< pipeline.parameters.active-node-version >>",
|
||||
"<< pipeline.parameters.current-node-version >>"
|
||||
]
|
||||
- samples:
|
||||
requires:
|
||||
- build
|
||||
|
||||
@@ -23,7 +23,6 @@ module.exports = {
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-array-constructor': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
@@ -6,7 +6,7 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
|
||||
|
||||
|
||||
If you are not sure that your issue is a bug, you could:
|
||||
|
||||
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/Feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/Feature_request.yml
vendored
@@ -6,7 +6,7 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
|
||||
|
||||
|
||||
If you are not sure that your issue is a bug, you could:
|
||||
|
||||
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/Regression.yml
vendored
2
.github/ISSUE_TEMPLATE/Regression.yml
vendored
@@ -6,7 +6,7 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
|
||||
|
||||
|
||||
If you are not sure that your issue is a bug, you could:
|
||||
|
||||
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page
|
||||
|
||||
90
Readme.md
90
Readme.md
@@ -23,7 +23,7 @@
|
||||
|
||||
## Description
|
||||
|
||||
Nest is a framework for building efficient, scalable <a href="https://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="https://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
|
||||
@@ -34,10 +34,10 @@ Nest is a framework for building efficient, scalable <a href="https://nodejs.org
|
||||
|
||||
## Getting started
|
||||
|
||||
- To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
- 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
- [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
|
||||
- [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
|
||||
* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
* [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
|
||||
* [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
|
||||
|
||||
## Questions
|
||||
|
||||
@@ -56,29 +56,26 @@ With official support, you can get expert help straight from Nest core team. We
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support from the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td><td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></tr><tr>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="90" valign="middle" /></a></td><td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="110" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td><td>
|
||||
<a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td>
|
||||
@@ -87,9 +84,17 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td>
|
||||
<td><a href="https://twistag.com/" target="_blank"><img src="https://nestjs.com/img/twistag-logo.png" width="120" valign="middle" /></td></tr>
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a> </td><td>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" width="100" /></a> </td><td>
|
||||
<a href="https://www.myleodsc.com/" target="_blank"><img src="https://nestjs.com/img/myleo-logo.png" width="180" valign="middle" /></td><td>
|
||||
<a href="https://careers.meetdandy.com/?gh_src=063ba61e3us" target="_blank"><img src="https://nestjs.com/img/dandy-roles-logo.svg" width="150" valign="middle" /></td><td>
|
||||
<a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
|
||||
<td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
|
||||
<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
|
||||
<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td>
|
||||
<td><a href="https://chax.at" target="_blank"><img src="https://nestjs.com/img/chaxat-logo.png" width="100" valign="middle" /></td></tr>
|
||||
</table>
|
||||
|
||||
#### Sponsors
|
||||
@@ -97,25 +102,42 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<table><tr><td align="center" valign="middle">
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a></td><td align="center" valign="middle">
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" width="72" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" width="120" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" width="125" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" width="100" valign="middle" /></a> </td></tr><tr><td align="center" valign="middle">
|
||||
<a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" width="95" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" width="107" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td>
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" width="71" valign="middle" /></a></td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a></td></tr><tr><td align="center" valign="middle">
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://www.thebigphonestore.co.uk/" target="_blank"><img src="https://nestjs.com/img/the-big-phone-store-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td>
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td></tr><tr><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://pickwriters.com/top-10-translation-services" target="_blank"><img src="https://nestjs.com/img/pickwriters-logo.png" width="40" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://thewordpoint.com/services/localization" target="_blank"><img src="https://nestjs.com/img/thewordpoint-logo.png" width="40" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://meercode.io/" target="_blank"><img src="https://nestjs.com/img/meercode-logo.png" width="60" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://thestandarddaily.com/" target="_blank"><img src="https://nestjs.com/img/the-standard-daily-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://houseofangular.io/" target="_blank"><img src="https://nestjs.com/img/house-of-angular.png" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.bystored.com/" target="_blank"><img src="https://nestjs.com/img/stored-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://studyclerk.com/pay-for-research-paper" target="_blank"><img src="https://nestjs.com/img/studyclerk-logo.png" width="125" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://xyndata.com" target="_blank"><img src="https://nestjs.com/img/xyndata-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://messaged.com/" target="_blank"><img src="https://nestjs.com/img/messaged-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://klqc.de" target="_blank"><img src="https://nestjs.com/img/klcqcl-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://themobilereality.com/" target="_blank"><img src="https://nestjs.com/img/mobile-reality-logo.png" width="45" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/julienferand-logo.jpeg" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
@@ -124,9 +146,9 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
* Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||
* Website - [https://nestjs.com](https://nestjs.com/)
|
||||
* Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { WebhooksExplorer } from '../src/webhooks.explorer';
|
||||
|
||||
describe('DiscoveryModule', () => {
|
||||
it('should discover all providers & handlers with corresponding annotations', async () => {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
const testingModule = await builder.compile();
|
||||
const webhooksExplorer = testingModule.get(WebhooksExplorer);
|
||||
|
||||
expect(webhooksExplorer.getWebhooks()).to.be.eql([
|
||||
{
|
||||
handlers: [
|
||||
{
|
||||
event: 'start',
|
||||
methodName: 'onStart',
|
||||
},
|
||||
],
|
||||
name: 'cleanup',
|
||||
},
|
||||
{
|
||||
handlers: [
|
||||
{
|
||||
event: 'start',
|
||||
methodName: 'onStart',
|
||||
},
|
||||
],
|
||||
name: 'flush',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DiscoveryModule } from '@nestjs/core';
|
||||
import { MyWebhookModule } from './my-webhook/my-webhook.module';
|
||||
import { WebhooksExplorer } from './webhooks.explorer';
|
||||
|
||||
@Module({
|
||||
imports: [MyWebhookModule, DiscoveryModule],
|
||||
providers: [WebhooksExplorer],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { DiscoveryService } from '@nestjs/core';
|
||||
|
||||
export const Webhook = DiscoveryService.createDecorator<{ name: string }>();
|
||||
export const WebhookHandler = DiscoveryService.createDecorator<{
|
||||
event: string;
|
||||
}>();
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Webhook, WebhookHandler } from '../decorators/webhook.decorators';
|
||||
|
||||
@Webhook({ name: 'cleanup' })
|
||||
export class CleanupWebhook {
|
||||
@WebhookHandler({ event: 'start' })
|
||||
onStart() {
|
||||
console.log('cleanup started');
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Webhook, WebhookHandler } from '../decorators/webhook.decorators';
|
||||
|
||||
@Webhook({ name: 'flush' })
|
||||
export class FlushWebhook {
|
||||
@WebhookHandler({ event: 'start' })
|
||||
onStart() {
|
||||
console.log('flush started');
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CleanupWebhook } from './cleanup.webhook';
|
||||
import { FlushWebhook } from './flush.webhook';
|
||||
|
||||
@Module({ providers: [CleanupWebhook, FlushWebhook] })
|
||||
export class MyWebhookModule {}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
|
||||
import { Webhook, WebhookHandler } from './decorators/webhook.decorators';
|
||||
|
||||
@Injectable()
|
||||
export class WebhooksExplorer {
|
||||
constructor(
|
||||
private readonly discoveryService: DiscoveryService,
|
||||
private readonly metadataScanner: MetadataScanner,
|
||||
) {}
|
||||
|
||||
getWebhooks() {
|
||||
const webhooks = this.discoveryService.getProviders({
|
||||
metadataKey: Webhook.KEY,
|
||||
});
|
||||
return webhooks.map(wrapper => {
|
||||
const { name } = this.discoveryService.getMetadataByDecorator(
|
||||
Webhook,
|
||||
wrapper,
|
||||
);
|
||||
return {
|
||||
name,
|
||||
handlers: this.metadataScanner
|
||||
.getAllMethodNames(wrapper.metatype.prototype)
|
||||
.map(methodName => {
|
||||
const { event } = this.discoveryService.getMetadataByDecorator(
|
||||
WebhookHandler,
|
||||
wrapper,
|
||||
methodName,
|
||||
);
|
||||
return {
|
||||
methodName,
|
||||
event,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
"@nestjs/common/*": ["../../packages/common/*"],
|
||||
"@nestjs/core": ["../../packages/core"],
|
||||
"@nestjs/core/*": ["../../packages/core/*"],
|
||||
"@nestjs/microservices": ["../../packages/microservices"],
|
||||
"@nestjs/microservices/*": ["../../packages/microservices/*"],
|
||||
"@nestjs/websockets": ["../../packages/websockets"],
|
||||
"@nestjs/websockets/*": ["../../packages/websockets/*"],
|
||||
"@nestjs/testing": ["../../packages/testing"],
|
||||
"@nestjs/testing/*": ["../../packages/testing/*"],
|
||||
"@nestjs/platform-express": ["../../packages/platform-express"],
|
||||
"@nestjs/platform-express/*": ["../../packages/platform-express/*"],
|
||||
"@nestjs/platform-socket.io": ["../../packages/platform-socket.io"],
|
||||
"@nestjs/platform-socket.io/*": ["../../packages/platform-socket.io/*"],
|
||||
"@nestjs/platform-ws": ["../../packages/platform-ws"],
|
||||
"@nestjs/platform-ws/*": ["../../packages/platform-ws/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"e2e/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
]
|
||||
}
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
- "9001:9001"
|
||||
restart: always
|
||||
mysql:
|
||||
image: mysql:8.0.33
|
||||
image: mysql:8.0.32
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: test
|
||||
@@ -50,7 +50,7 @@ services:
|
||||
zookeeper:
|
||||
container_name: test-zookeeper
|
||||
hostname: zookeeper
|
||||
image: confluentinc/cp-zookeeper:7.4.1
|
||||
image: confluentinc/cp-zookeeper:7.3.2
|
||||
ports:
|
||||
- "2181:2181"
|
||||
environment:
|
||||
@@ -59,7 +59,7 @@ services:
|
||||
kafka:
|
||||
container_name: test-kafka
|
||||
hostname: kafka
|
||||
image: confluentinc/cp-kafka:7.4.1
|
||||
image: confluentinc/cp-kafka:7.3.2
|
||||
depends_on:
|
||||
- zookeeper
|
||||
ports:
|
||||
|
||||
@@ -192,7 +192,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('should execute middleware only once for given routes', () => {
|
||||
describe.only('should execute middleware only once for given routes', () => {
|
||||
class Middleware implements NestMiddleware {
|
||||
use(request: any, reply: any, next: () => void) {
|
||||
if (request.middlewareExecutionCount === undefined) {
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Controller, Injectable, Module } from '@nestjs/common';
|
||||
|
||||
class B {}
|
||||
|
||||
@Injectable()
|
||||
class A {
|
||||
constructor(b: B) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class BImpl {
|
||||
constructor(a: A) {}
|
||||
}
|
||||
|
||||
@Controller()
|
||||
class AppController {
|
||||
constructor(a: A) {}
|
||||
}
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AppController],
|
||||
providers: [A, { provide: B, useClass: BImpl }],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
describe('Circular custom providers', () => {
|
||||
it('should throw an exception (useClass + regular provider)', async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
await builder.compile();
|
||||
|
||||
expect(true).to.be.eql(false);
|
||||
} catch (err) {
|
||||
expect(err.message).to.be.eql(
|
||||
'A circular dependency has been detected inside "A". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw an exception (2 factories)', async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'ABC', useFactory: () => ({}), inject: ['DEF'] },
|
||||
{ provide: 'DEF', useFactory: () => ({}), inject: ['ABC'] },
|
||||
],
|
||||
});
|
||||
await builder.compile();
|
||||
|
||||
expect(true).to.be.eql(false);
|
||||
} catch (err) {
|
||||
expect(err.message).to.be.eql(
|
||||
'A circular dependency has been detected inside "ABC". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as chai from 'chai';
|
||||
import { expect } from 'chai';
|
||||
import chaiAsPromised = require('chai-as-promised');
|
||||
import { AppModule } from '../src/app.module';
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
describe('Lazy imports', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpAdapter().getInstance();
|
||||
});
|
||||
|
||||
it(`should allow imports of global modules`, async () => {
|
||||
await expect(app.init()).to.eventually.be.fulfilled;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { LazyModuleLoader } from '@nestjs/core';
|
||||
import { EagerModule } from './eager.module';
|
||||
import { GlobalModule } from './global.module';
|
||||
import { LazyModule } from './lazy.module';
|
||||
|
||||
@Module({
|
||||
imports: [GlobalModule, EagerModule],
|
||||
})
|
||||
export class AppModule {
|
||||
constructor(public loader: LazyModuleLoader) {}
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
await this.loader.load(() => LazyModule);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Module, Injectable } from '@nestjs/common';
|
||||
import { GlobalService } from './global.module';
|
||||
|
||||
@Injectable()
|
||||
export class EagerService {
|
||||
constructor(public globalService: GlobalService) {}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [EagerService],
|
||||
})
|
||||
export class EagerModule {}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Module, Injectable, Global } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class GlobalService {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [GlobalService],
|
||||
exports: [GlobalService],
|
||||
})
|
||||
export class GlobalModule {}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Module, Injectable } from '@nestjs/common';
|
||||
import { GlobalService } from './global.module';
|
||||
|
||||
@Injectable()
|
||||
export class LazyService {
|
||||
constructor(public globalService: GlobalService) {}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [LazyService],
|
||||
})
|
||||
export class LazyModule {}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
await app.listen(3000);
|
||||
}
|
||||
bootstrap();
|
||||
@@ -4,7 +4,9 @@ import { CatsModule } from './cats/cats.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forRoot('mongodb://localhost:27017/test'),
|
||||
MongooseModule.forRoot('mongodb://localhost:27017/test', {
|
||||
useNewUrlParser: true,
|
||||
}),
|
||||
CatsModule,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@ class ConfigService implements MongooseOptionsFactory {
|
||||
createMongooseOptions(): MongooseModuleOptions {
|
||||
return {
|
||||
uri: 'mongodb://localhost:27017/test',
|
||||
useNewUrlParser: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ class ConfigService implements MongooseOptionsFactory {
|
||||
createMongooseOptions(): MongooseModuleOptions {
|
||||
return {
|
||||
uri: 'mongodb://localhost:27017/test',
|
||||
useNewUrlParser: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { CatsModule } from './cats/cats.module';
|
||||
imports: [
|
||||
MongooseModule.forRootAsync({
|
||||
useFactory: () => ({
|
||||
useNewUrlParser: true,
|
||||
uri: 'mongodb://localhost:27017/test',
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -77,7 +77,7 @@ describe('Fastify FileSend', () => {
|
||||
expect(headers['content-disposition']).to.equal(
|
||||
'attachment; filename="Readme.md"',
|
||||
);
|
||||
expect(headers['content-length']).to.equal(`${readme.byteLength}`);
|
||||
expect(headers['content-length']).to.equal(readme.byteLength);
|
||||
expect(payload).to.equal(readmeString);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"type": "mysql",
|
||||
"host": "127.0.0.1",
|
||||
"host": "localhost",
|
||||
"port": 3306,
|
||||
"username": "root",
|
||||
"password": "root",
|
||||
|
||||
@@ -7,7 +7,7 @@ import { PhotoModule } from './photo/photo.module';
|
||||
imports: [
|
||||
TypeOrmModule.forRoot({
|
||||
type: 'mysql',
|
||||
host: '127.0.0.1',
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
username: 'root',
|
||||
password: 'root',
|
||||
|
||||
@@ -11,7 +11,7 @@ class ConfigService implements TypeOrmOptionsFactory {
|
||||
createTypeOrmOptions(): TypeOrmModuleOptions {
|
||||
return {
|
||||
type: 'mysql',
|
||||
host: '127.0.0.1',
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
username: 'root',
|
||||
password: 'root',
|
||||
|
||||
@@ -11,7 +11,7 @@ class ConfigService implements TypeOrmOptionsFactory {
|
||||
createTypeOrmOptions(): TypeOrmModuleOptions {
|
||||
return {
|
||||
type: 'mysql',
|
||||
host: '127.0.0.1',
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
username: 'root',
|
||||
password: 'root',
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PhotoModule } from './photo/photo.module';
|
||||
TypeOrmModule.forRootAsync({
|
||||
useFactory: () => ({
|
||||
type: 'mysql',
|
||||
host: '127.0.0.1',
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
username: 'root',
|
||||
password: 'root',
|
||||
|
||||
@@ -11,7 +11,7 @@ export class DatabaseModule {
|
||||
imports: [
|
||||
TypeOrmModule.forRoot({
|
||||
type: 'mysql',
|
||||
host: '127.0.0.1',
|
||||
host: 'localhost',
|
||||
port: 3306,
|
||||
username: 'root',
|
||||
password: 'root',
|
||||
|
||||
@@ -418,67 +418,4 @@ describe('URI Versioning', () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
// ======================================================================== //
|
||||
describe('with middleware applied', () => {
|
||||
before(async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
app.enableVersioning({
|
||||
type: VersioningType.URI,
|
||||
defaultVersion: '1',
|
||||
});
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('GET /middleware', () => {
|
||||
it('should return "Hello from middleware function!"', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/v1/middleware')
|
||||
.expect(200)
|
||||
.expect('Hello from middleware function!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /middleware/override', () => {
|
||||
it('should return "Hello from middleware function!"', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/v2/middleware/override')
|
||||
.expect(200)
|
||||
.expect('Hello from middleware function!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /middleware/multiple', () => {
|
||||
it('should return "Hello from middleware function!" (v1)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/v1/middleware/multiple')
|
||||
.expect(200)
|
||||
.expect('Hello from middleware function!');
|
||||
});
|
||||
|
||||
it('should return "Hello from middleware function!" (v2)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/v2/middleware/multiple')
|
||||
.expect(200)
|
||||
.expect('Hello from middleware function!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /middleware/neutral', () => {
|
||||
it('should return "Hello from middleware function!"', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/middleware/neutral')
|
||||
.expect(200)
|
||||
.expect('Hello from middleware function!');
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { MiddlewareConsumer, Module } from '@nestjs/common';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppV1Controller } from './app-v1.controller';
|
||||
import { AppV2Controller } from './app-v2.controller';
|
||||
import { MiddlewareController } from './middleware.controller';
|
||||
import { MultipleMiddlewareVersionController } from './multiple-middleware.controller';
|
||||
import { MultipleVersionController } from './multiple.controller';
|
||||
import { VersionNeutralMiddlewareController } from './neutral-middleware.controller';
|
||||
import { VersionNeutralController } from './neutral.controller';
|
||||
import { NoVersioningController } from './no-versioning.controller';
|
||||
import { OverridePartialController } from './override-partial.controller';
|
||||
import { VersionNeutralController } from './neutral.controller';
|
||||
import { OverrideController } from './override.controller';
|
||||
import { OverridePartialController } from './override-partial.controller';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
@@ -20,19 +17,6 @@ import { OverrideController } from './override.controller';
|
||||
VersionNeutralController,
|
||||
OverrideController,
|
||||
OverridePartialController,
|
||||
MiddlewareController,
|
||||
MultipleMiddlewareVersionController,
|
||||
VersionNeutralMiddlewareController,
|
||||
],
|
||||
})
|
||||
export class AppModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res) => res.end('Hello from middleware function!'))
|
||||
.forRoutes(
|
||||
MiddlewareController,
|
||||
MultipleMiddlewareVersionController,
|
||||
VersionNeutralMiddlewareController,
|
||||
);
|
||||
}
|
||||
}
|
||||
export class AppModule {}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Controller, Get, Version } from '@nestjs/common';
|
||||
|
||||
@Controller({
|
||||
path: 'middleware',
|
||||
version: '1',
|
||||
})
|
||||
export class MiddlewareController {
|
||||
@Get('/')
|
||||
hello() {
|
||||
return 'Hello from "MiddlewareController"!';
|
||||
}
|
||||
|
||||
@Version('2')
|
||||
@Get('/override')
|
||||
hellov2() {
|
||||
return 'Hello from "MiddlewareController"!';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
|
||||
@Controller({
|
||||
version: ['1', '2'],
|
||||
path: 'middleware',
|
||||
})
|
||||
export class MultipleMiddlewareVersionController {
|
||||
@Get('/multiple')
|
||||
multiple() {
|
||||
return 'Multiple Versions 1 or 2';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Controller, Get, VERSION_NEUTRAL } from '@nestjs/common';
|
||||
|
||||
@Controller({
|
||||
path: 'middleware',
|
||||
version: VERSION_NEUTRAL,
|
||||
})
|
||||
export class VersionNeutralMiddlewareController {
|
||||
@Get('/neutral')
|
||||
neutral() {
|
||||
return 'Neutral';
|
||||
}
|
||||
}
|
||||
@@ -66,21 +66,5 @@ describe('WebSocketGateway', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it(`should be able to get the pattern in an interceptor`, async () => {
|
||||
app = await createNestApp(ApplicationGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = io('http://localhost:8080');
|
||||
ws.emit('getClient', {
|
||||
test: 'test',
|
||||
});
|
||||
await new Promise<void>(resolve =>
|
||||
ws.on('popClient', data => {
|
||||
expect(data.path).to.be.eql('getClient');
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => app.close());
|
||||
});
|
||||
|
||||
@@ -194,30 +194,6 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should let the execution context have a getPattern() method on getClient()', async () => {
|
||||
app = await createNestApp(ApplicationGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = new WebSocket('ws://localhost:8080');
|
||||
await new Promise(resolve => ws.on('open', resolve));
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: 'getClient',
|
||||
data: {
|
||||
test: 'test',
|
||||
},
|
||||
}),
|
||||
);
|
||||
await new Promise<void>(resolve =>
|
||||
ws.on('message', data => {
|
||||
expect(JSON.parse(data).data.path).to.be.eql('getClient');
|
||||
ws.close();
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async function () {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { UseInterceptors } from '@nestjs/common';
|
||||
import {
|
||||
MessageBody,
|
||||
SubscribeMessage,
|
||||
WebSocketGateway,
|
||||
} from '@nestjs/websockets';
|
||||
import { RequestInterceptor } from './request.interceptor';
|
||||
|
||||
@WebSocketGateway(8080)
|
||||
export class ApplicationGateway {
|
||||
@@ -15,13 +13,4 @@ export class ApplicationGateway {
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
@UseInterceptors(RequestInterceptor)
|
||||
@SubscribeMessage('getClient')
|
||||
getPathCalled(client, data) {
|
||||
return {
|
||||
event: 'popClient',
|
||||
data: { ...data, path: client.pattern },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { CallHandler, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class RequestInterceptor {
|
||||
intercept(context: ExecutionContext, next: CallHandler) {
|
||||
const client = context.switchToWs().getClient();
|
||||
const pattern = context.switchToWs().getPattern();
|
||||
client.pattern = pattern;
|
||||
return next.handle();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import { UseInterceptors } from '@nestjs/common';
|
||||
import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets';
|
||||
import { RequestInterceptor } from './request.interceptor';
|
||||
|
||||
@WebSocketGateway()
|
||||
export class ServerGateway {
|
||||
@@ -11,13 +9,4 @@ export class ServerGateway {
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
@UseInterceptors(RequestInterceptor)
|
||||
@SubscribeMessage('getClient')
|
||||
getPathCalled(client, data) {
|
||||
return {
|
||||
event: 'popClient',
|
||||
data: { ...data, path: client.pattern },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "10.2.1"
|
||||
"version": "9.4.2"
|
||||
}
|
||||
|
||||
38199
package-lock.json
generated
38199
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
100
package.json
100
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "10.1.3",
|
||||
"version": "9.4.0",
|
||||
"description": "Modern, fast, powerful node.js web framework",
|
||||
"homepage": "https://nestjs.com",
|
||||
"repository": {
|
||||
@@ -25,7 +25,7 @@
|
||||
"move:node_modules": "gulp move:node_modules",
|
||||
"build:samples": "gulp install:samples && npm run build && npm run move:samples && gulp build:samples && gulp test:samples && gulp test:e2e:samples",
|
||||
"codechecks:benchmarks": "codechecks ./tools/benchmarks/check-benchmarks.ts",
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls -v",
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||
"format": "prettier \"**/*.ts\" \"packages/**/*.json\" --ignore-path ./.prettierignore --write && git status",
|
||||
"postinstall": "opencollective",
|
||||
"test": "mocha packages/**/*.spec.ts",
|
||||
@@ -64,7 +64,7 @@
|
||||
"cli-color": "2.0.3",
|
||||
"cors": "2.8.5",
|
||||
"express": "4.18.2",
|
||||
"fast-json-stringify": "5.8.0",
|
||||
"fast-json-stringify": "5.7.0",
|
||||
"fast-safe-stringify": "2.1.1",
|
||||
"graphql-subscriptions": "2.0.0",
|
||||
"iterare": "1.2.1",
|
||||
@@ -72,67 +72,67 @@
|
||||
"path-to-regexp": "3.2.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "7.8.1",
|
||||
"socket.io": "4.7.2",
|
||||
"tslib": "2.6.1",
|
||||
"socket.io": "4.6.1",
|
||||
"tslib": "2.5.2",
|
||||
"uid": "2.0.2",
|
||||
"uuid": "9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/server": "4.7.5",
|
||||
"@apollo/server": "4.7.1",
|
||||
"@codechecks/client": "0.1.12",
|
||||
"@commitlint/cli": "17.7.1",
|
||||
"@commitlint/config-angular": "17.7.0",
|
||||
"@fastify/cors": "8.3.0",
|
||||
"@commitlint/cli": "17.6.3",
|
||||
"@commitlint/config-angular": "17.6.3",
|
||||
"@fastify/cors": "8.2.1",
|
||||
"@fastify/formbody": "7.4.0",
|
||||
"@fastify/middie": "8.3.0",
|
||||
"@fastify/multipart": "7.7.3",
|
||||
"@fastify/static": "6.10.2",
|
||||
"@fastify/view": "8.0.0",
|
||||
"@grpc/grpc-js": "1.9.0",
|
||||
"@grpc/proto-loader": "0.7.8",
|
||||
"@nestjs/apollo": "12.0.4",
|
||||
"@nestjs/graphql": "12.0.7",
|
||||
"@nestjs/mongoose": "10.0.1",
|
||||
"@nestjs/typeorm": "10.0.0",
|
||||
"@fastify/multipart": "7.6.0",
|
||||
"@fastify/static": "6.10.1",
|
||||
"@fastify/view": "7.4.1",
|
||||
"@grpc/grpc-js": "1.8.14",
|
||||
"@grpc/proto-loader": "0.7.7",
|
||||
"@nestjs/apollo": "11.0.5",
|
||||
"@nestjs/graphql": "11.0.5",
|
||||
"@nestjs/mongoose": "9.2.2",
|
||||
"@nestjs/typeorm": "9.0.1",
|
||||
"@types/amqplib": "0.10.1",
|
||||
"@types/bytes": "3.1.1",
|
||||
"@types/chai": "4.3.5",
|
||||
"@types/chai-as-promised": "7.1.5",
|
||||
"@types/cors": "2.8.13",
|
||||
"@types/express": "4.17.17",
|
||||
"@types/gulp": "4.0.13",
|
||||
"@types/gulp": "4.0.10",
|
||||
"@types/http-errors": "2.0.1",
|
||||
"@types/mocha": "10.0.1",
|
||||
"@types/node": "20.5.0",
|
||||
"@types/sinon": "10.0.16",
|
||||
"@types/node": "20.2.3",
|
||||
"@types/sinon": "10.0.15",
|
||||
"@types/supertest": "2.0.12",
|
||||
"@types/ws": "8.5.5",
|
||||
"@typescript-eslint/eslint-plugin": "6.4.1",
|
||||
"@typescript-eslint/parser": "6.4.1",
|
||||
"amqp-connection-manager": "4.1.14",
|
||||
"@types/ws": "8.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "4.33.0",
|
||||
"@typescript-eslint/parser": "4.33.0",
|
||||
"amqp-connection-manager": "4.1.13",
|
||||
"amqplib": "0.10.3",
|
||||
"artillery": "1.7.9",
|
||||
"body-parser": "1.20.2",
|
||||
"bytes": "3.1.2",
|
||||
"cache-manager": "5.2.3",
|
||||
"cache-manager": "5.2.1",
|
||||
"cache-manager-redis-store": "3.0.1",
|
||||
"chai": "4.3.7",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"clang-format": "1.8.0",
|
||||
"commitlint-circle": "1.0.0",
|
||||
"concurrently": "8.2.0",
|
||||
"conventional-changelog": "4.0.0",
|
||||
"core-js": "3.32.1",
|
||||
"concurrently": "8.0.1",
|
||||
"conventional-changelog": "3.1.25",
|
||||
"core-js": "3.30.2",
|
||||
"coveralls": "3.1.1",
|
||||
"delete-empty": "3.0.0",
|
||||
"engine.io-client": "6.5.2",
|
||||
"eslint": "8.47.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-plugin-import": "2.28.0",
|
||||
"engine.io-client": "6.4.0",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eventsource": "2.0.2",
|
||||
"fancy-log": "2.0.0",
|
||||
"fastify": "4.21.0",
|
||||
"graphql": "16.8.0",
|
||||
"fastify": "4.17.0",
|
||||
"graphql": "16.6.0",
|
||||
"graphql-tools": "9.0.0",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-clang-format": "1.0.27",
|
||||
@@ -148,35 +148,35 @@
|
||||
"kafkajs": "2.2.4",
|
||||
"lerna": "2.11.0",
|
||||
"lerna-changelog": "2.2.0",
|
||||
"light-my-request": "5.10.0",
|
||||
"lint-staged": "14.0.1",
|
||||
"light-my-request": "5.9.1",
|
||||
"lint-staged": "13.2.2",
|
||||
"markdown-table": "2.0.0",
|
||||
"merge-graphql-schemas": "1.7.8",
|
||||
"mocha": "10.2.0",
|
||||
"mongoose": "7.4.3",
|
||||
"mqtt": "5.0.3",
|
||||
"mongoose": "7.2.0",
|
||||
"mqtt": "4.3.7",
|
||||
"multer": "1.4.4",
|
||||
"mysql2": "3.6.0",
|
||||
"nats": "2.16.0",
|
||||
"nodemon": "3.0.1",
|
||||
"mysql2": "3.3.1",
|
||||
"nats": "2.13.1",
|
||||
"nodemon": "2.0.22",
|
||||
"nyc": "15.1.0",
|
||||
"prettier": "3.0.2",
|
||||
"redis": "4.6.7",
|
||||
"prettier": "2.8.8",
|
||||
"redis": "4.6.6",
|
||||
"rxjs-compat": "6.6.7",
|
||||
"sinon": "15.2.0",
|
||||
"sinon": "15.1.0",
|
||||
"sinon-chai": "3.7.0",
|
||||
"socket.io-client": "4.7.2",
|
||||
"socket.io-client": "4.6.1",
|
||||
"subscriptions-transport-ws": "0.11.0",
|
||||
"supertest": "6.3.3",
|
||||
"ts-morph": "19.0.0",
|
||||
"ts-morph": "18.0.0",
|
||||
"ts-node": "10.9.1",
|
||||
"typeorm": "0.3.17",
|
||||
"typescript": "5.1.6",
|
||||
"typeorm": "0.3.16",
|
||||
"typescript": "5.0.4",
|
||||
"wrk": "1.2.1",
|
||||
"ws": "8.13.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
"node": ">= 12.9.0"
|
||||
},
|
||||
"collective": {
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
## Description
|
||||
|
||||
Nest is a framework for building efficient, scalable <a href="https://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="https://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
|
||||
@@ -34,10 +34,10 @@ Nest is a framework for building efficient, scalable <a href="https://nodejs.org
|
||||
|
||||
## Getting started
|
||||
|
||||
- To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
- 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
- [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
|
||||
- [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
|
||||
* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
* [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
|
||||
* [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
|
||||
|
||||
## Questions
|
||||
|
||||
@@ -56,29 +56,26 @@ With official support, you can get expert help straight from Nest core team. We
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support from the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td><td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></tr><tr>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="90" valign="middle" /></a></td><td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="110" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td><td>
|
||||
<a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td>
|
||||
@@ -87,9 +84,17 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td>
|
||||
<td><a href="https://twistag.com/" target="_blank"><img src="https://nestjs.com/img/twistag-logo.png" width="120" valign="middle" /></td></tr>
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a> </td><td>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" width="100" /></a> </td><td>
|
||||
<a href="https://www.myleodsc.com/" target="_blank"><img src="https://nestjs.com/img/myleo-logo.png" width="180" valign="middle" /></td><td>
|
||||
<a href="https://careers.meetdandy.com/?gh_src=063ba61e3us" target="_blank"><img src="https://nestjs.com/img/dandy-roles-logo.svg" width="150" valign="middle" /></td><td>
|
||||
<a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
|
||||
<td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
|
||||
<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
|
||||
<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td>
|
||||
<td><a href="https://chax.at" target="_blank"><img src="https://nestjs.com/img/chaxat-logo.png" width="100" valign="middle" /></td></tr>
|
||||
</table>
|
||||
|
||||
#### Sponsors
|
||||
@@ -97,25 +102,41 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<table><tr><td align="center" valign="middle">
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a></td><td align="center" valign="middle">
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" width="72" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" width="120" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" width="125" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" width="100" valign="middle" /></a> </td></tr><tr><td align="center" valign="middle">
|
||||
<a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" width="95" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" width="107" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td>
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" width="71" valign="middle" /></a></td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a></td></tr><tr><td align="center" valign="middle">
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://www.thebigphonestore.co.uk/" target="_blank"><img src="https://nestjs.com/img/the-big-phone-store-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td>
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td></tr><tr><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://pickwriters.com/top-10-translation-services" target="_blank"><img src="https://nestjs.com/img/pickwriters-logo.png" width="40" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://thewordpoint.com/services/localization" target="_blank"><img src="https://nestjs.com/img/thewordpoint-logo.png" width="40" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://meercode.io/" target="_blank"><img src="https://nestjs.com/img/meercode-logo.png" width="60" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://thestandarddaily.com/" target="_blank"><img src="https://nestjs.com/img/the-standard-daily-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://houseofangular.io/" target="_blank"><img src="https://nestjs.com/img/house-of-angular.png" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.bystored.com/" target="_blank"><img src="https://nestjs.com/img/stored-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://studyclerk.com/pay-for-research-paper" target="_blank"><img src="https://nestjs.com/img/studyclerk-logo.png" width="125" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://xyndata.com" target="_blank"><img src="https://nestjs.com/img/xyndata-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://messaged.com/" target="_blank"><img src="https://nestjs.com/img/messaged-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://klqc.de" target="_blank"><img src="https://nestjs.com/img/klcqcl-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://themobilereality.com/" target="_blank"><img src="https://nestjs.com/img/mobile-reality-logo.png" width="45" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/julienferand-logo.jpeg" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
@@ -124,9 +145,9 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
* Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||
* Website - [https://nestjs.com](https://nestjs.com/)
|
||||
* Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ import {
|
||||
import { isObject, isString } from '../utils/shared.utils';
|
||||
|
||||
export interface HttpExceptionOptions {
|
||||
/** original cause of the error */
|
||||
cause?: unknown;
|
||||
cause?: Error;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
@@ -69,13 +68,14 @@ export class HttpException extends Error {
|
||||
this.initCause();
|
||||
}
|
||||
|
||||
public cause: unknown;
|
||||
public cause: Error | undefined;
|
||||
|
||||
/**
|
||||
* Configures error chaining support
|
||||
*
|
||||
* @see https://nodejs.org/en/blog/release/v16.9.0/#error-cause
|
||||
* @see https://github.com/microsoft/TypeScript/issues/45167
|
||||
* See:
|
||||
* - https://nodejs.org/en/blog/release/v16.9.0/#error-cause
|
||||
* - https://github.com/microsoft/TypeScript/issues/45167
|
||||
*/
|
||||
public initCause(): void {
|
||||
if (this.options?.cause) {
|
||||
|
||||
@@ -31,10 +31,6 @@ export interface WsArgumentsHost {
|
||||
* Returns the client object.
|
||||
*/
|
||||
getClient<T = any>(): T;
|
||||
/**
|
||||
* Returns the pattern for the event
|
||||
*/
|
||||
getPattern(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/common",
|
||||
"version": "10.2.1",
|
||||
"version": "9.4.2",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"homepage": "https://nestjs.com",
|
||||
@@ -19,7 +19,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iterare": "1.2.1",
|
||||
"tslib": "2.6.1",
|
||||
"tslib": "2.5.2",
|
||||
"uid": "2.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -16,21 +16,17 @@ export type FileTypeValidatorOptions = {
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export class FileTypeValidator extends FileValidator<
|
||||
FileTypeValidatorOptions,
|
||||
IFile
|
||||
> {
|
||||
export class FileTypeValidator extends FileValidator<FileTypeValidatorOptions> {
|
||||
buildErrorMessage(): string {
|
||||
return `Validation failed (expected type is ${this.validationOptions.fileType})`;
|
||||
}
|
||||
|
||||
isValid(file?: IFile): boolean {
|
||||
isValid<TFile extends IFile = any>(file: TFile): boolean {
|
||||
if (!this.validationOptions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
!!file &&
|
||||
'mimetype' in file &&
|
||||
!!file.mimetype.match(this.validationOptions.fileType)
|
||||
);
|
||||
|
||||
@@ -6,17 +6,16 @@ import { IFile } from './interfaces';
|
||||
* @see {ParseFilePipe}
|
||||
* @publicApi
|
||||
*/
|
||||
export abstract class FileValidator<
|
||||
TValidationOptions = Record<string, any>,
|
||||
TFile extends IFile = IFile,
|
||||
> {
|
||||
export abstract class FileValidator<TValidationOptions = Record<string, any>> {
|
||||
constructor(protected readonly validationOptions: TValidationOptions) {}
|
||||
|
||||
/**
|
||||
* Indicates if this file should be considered valid, according to the options passed in the constructor.
|
||||
* @param file the file from the request object
|
||||
*/
|
||||
abstract isValid(file?: TFile | TFile[] | Record<string, TFile[]>): boolean | Promise<boolean>;
|
||||
abstract isValid<TFile extends IFile = any>(
|
||||
file?: TFile,
|
||||
): boolean | Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Builds an error message in case the validation fails.
|
||||
|
||||
@@ -13,10 +13,7 @@ export type MaxFileSizeValidatorOptions = {
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export class MaxFileSizeValidator extends FileValidator<
|
||||
MaxFileSizeValidatorOptions,
|
||||
IFile
|
||||
> {
|
||||
export class MaxFileSizeValidator extends FileValidator<MaxFileSizeValidatorOptions> {
|
||||
buildErrorMessage(): string {
|
||||
if ('message' in this.validationOptions) {
|
||||
if (typeof this.validationOptions.message === 'function') {
|
||||
@@ -29,8 +26,8 @@ export class MaxFileSizeValidator extends FileValidator<
|
||||
return `Validation failed (expected size is less than ${this.validationOptions.maxSize})`;
|
||||
}
|
||||
|
||||
public isValid(file?: IFile): boolean {
|
||||
if (!this.validationOptions || !file) {
|
||||
public isValid<TFile extends IFile = any>(file: TFile): boolean {
|
||||
if (!this.validationOptions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
import { isNil } from '../utils/shared.utils';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
@@ -17,7 +16,6 @@ import { isNil } from '../utils/shared.utils';
|
||||
export interface ParseBoolPipeOptions {
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
exceptionFactory?: (error: string) => any;
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,7 +31,7 @@ export class ParseBoolPipe
|
||||
{
|
||||
protected exceptionFactory: (error: string) => any;
|
||||
|
||||
constructor(@Optional() protected readonly options?: ParseBoolPipeOptions) {
|
||||
constructor(@Optional() options?: ParseBoolPipeOptions) {
|
||||
options = options || {};
|
||||
const { exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST } =
|
||||
options;
|
||||
@@ -53,9 +51,6 @@ export class ParseBoolPipe
|
||||
value: string | boolean,
|
||||
metadata: ArgumentMetadata,
|
||||
): Promise<boolean> {
|
||||
if (isNil(value) && this.options?.optional) {
|
||||
return value;
|
||||
}
|
||||
if (this.isTrue(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { Injectable, Optional } from '../decorators/core';
|
||||
import { ArgumentMetadata, HttpStatus } from '../index';
|
||||
import { ArgumentMetadata, HttpStatus, Injectable, Optional } from '../index';
|
||||
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
|
||||
import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
import { isNil } from '../utils/shared.utils';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ParseEnumPipeOptions {
|
||||
optional?: boolean;
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
exceptionFactory?: (error: string) => any;
|
||||
}
|
||||
@@ -26,9 +23,10 @@ export interface ParseEnumPipeOptions {
|
||||
@Injectable()
|
||||
export class ParseEnumPipe<T = any> implements PipeTransform<T> {
|
||||
protected exceptionFactory: (error: string) => any;
|
||||
|
||||
constructor(
|
||||
protected readonly enumType: T,
|
||||
@Optional() protected readonly options?: ParseEnumPipeOptions,
|
||||
@Optional() options?: ParseEnumPipeOptions,
|
||||
) {
|
||||
if (!enumType) {
|
||||
throw new Error(
|
||||
@@ -52,9 +50,6 @@ export class ParseEnumPipe<T = any> implements PipeTransform<T> {
|
||||
* @param metadata contains metadata about the currently processed route argument
|
||||
*/
|
||||
async transform(value: T, metadata: ArgumentMetadata): Promise<T> {
|
||||
if (isNil(value) && this.options?.optional) {
|
||||
return value;
|
||||
}
|
||||
if (!this.isEnum(value)) {
|
||||
throw this.exceptionFactory(
|
||||
'Validation failed (enum string is expected)',
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Injectable, Optional } from '../decorators/core';
|
||||
import { ArgumentMetadata, HttpStatus } from '../index';
|
||||
import { Injectable, Optional } from '../decorators/core';
|
||||
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
|
||||
import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
import { isNil } from '../utils/shared.utils';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
@@ -13,7 +12,6 @@ import { isNil } from '../utils/shared.utils';
|
||||
export interface ParseFloatPipeOptions {
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
exceptionFactory?: (error: string) => any;
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +25,7 @@ export interface ParseFloatPipeOptions {
|
||||
export class ParseFloatPipe implements PipeTransform<string> {
|
||||
protected exceptionFactory: (error: string) => any;
|
||||
|
||||
constructor(@Optional() protected readonly options?: ParseFloatPipeOptions) {
|
||||
constructor(@Optional() options?: ParseFloatPipeOptions) {
|
||||
options = options || {};
|
||||
const { exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST } =
|
||||
options;
|
||||
@@ -45,9 +43,6 @@ export class ParseFloatPipe implements PipeTransform<string> {
|
||||
* @param metadata contains metadata about the currently processed route argument
|
||||
*/
|
||||
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
|
||||
if (isNil(value) && this.options?.optional) {
|
||||
return value;
|
||||
}
|
||||
if (!this.isNumeric(value)) {
|
||||
throw this.exceptionFactory(
|
||||
'Validation failed (numeric string is expected)',
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
import { isNil } from '../utils/shared.utils';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
@@ -17,7 +16,6 @@ import { isNil } from '../utils/shared.utils';
|
||||
export interface ParseIntPipeOptions {
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
exceptionFactory?: (error: string) => any;
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +29,7 @@ export interface ParseIntPipeOptions {
|
||||
export class ParseIntPipe implements PipeTransform<string> {
|
||||
protected exceptionFactory: (error: string) => any;
|
||||
|
||||
constructor(@Optional() protected readonly options?: ParseIntPipeOptions) {
|
||||
constructor(@Optional() options?: ParseIntPipeOptions) {
|
||||
options = options || {};
|
||||
const { exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST } =
|
||||
options;
|
||||
@@ -49,9 +47,6 @@ export class ParseIntPipe implements PipeTransform<string> {
|
||||
* @param metadata contains metadata about the currently processed route argument
|
||||
*/
|
||||
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
|
||||
if (isNil(value) && this.options?.optional) {
|
||||
return value;
|
||||
}
|
||||
if (!this.isNumeric(value)) {
|
||||
throw this.exceptionFactory(
|
||||
'Validation failed (numeric string is expected)',
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
import { isNil, isString } from '../utils/shared.utils';
|
||||
import { isString } from '../utils/shared.utils';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
@@ -18,7 +18,6 @@ export interface ParseUUIDPipeOptions {
|
||||
version?: '3' | '4' | '5';
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
exceptionFactory?: (errors: string) => any;
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +38,7 @@ export class ParseUUIDPipe implements PipeTransform<string> {
|
||||
private readonly version: '3' | '4' | '5';
|
||||
protected exceptionFactory: (errors: string) => any;
|
||||
|
||||
constructor(@Optional() protected readonly options?: ParseUUIDPipeOptions) {
|
||||
constructor(@Optional() options?: ParseUUIDPipeOptions) {
|
||||
options = options || {};
|
||||
const {
|
||||
exceptionFactory,
|
||||
@@ -54,9 +53,6 @@ export class ParseUUIDPipe implements PipeTransform<string> {
|
||||
}
|
||||
|
||||
async transform(value: string, metadata: ArgumentMetadata): Promise<string> {
|
||||
if (isNil(value) && this.options?.optional) {
|
||||
return value;
|
||||
}
|
||||
if (!this.isUUID(value, this.version)) {
|
||||
throw this.exceptionFactory(
|
||||
`Validation failed (uuid${
|
||||
|
||||
@@ -26,7 +26,6 @@ const DEFAULT_LOG_LEVELS: LogLevel[] = [
|
||||
'warn',
|
||||
'debug',
|
||||
'verbose',
|
||||
'fatal',
|
||||
];
|
||||
|
||||
const dateTimeFormatter = new Intl.DateTimeFormat(undefined, {
|
||||
@@ -146,23 +145,6 @@ export class ConsoleLogger implements LoggerService {
|
||||
this.printMessages(messages, context, 'verbose');
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a 'fatal' level log, if the configured level allows for it.
|
||||
* Prints to `stdout` with newline.
|
||||
*/
|
||||
fatal(message: any, context?: string): void;
|
||||
fatal(message: any, ...optionalParams: [...any, string?]): void;
|
||||
fatal(message: any, ...optionalParams: any[]) {
|
||||
if (!this.isLevelEnabled('fatal')) {
|
||||
return;
|
||||
}
|
||||
const { messages, context } = this.getContextAndMessagesToPrint([
|
||||
message,
|
||||
...optionalParams,
|
||||
]);
|
||||
this.printMessages(messages, context, 'fatal');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set log levels
|
||||
* @param levels log levels
|
||||
@@ -348,8 +330,6 @@ export class ConsoleLogger implements LoggerService {
|
||||
return clc.red;
|
||||
case 'verbose':
|
||||
return clc.cyanBright;
|
||||
case 'fatal':
|
||||
return clc.bold;
|
||||
default:
|
||||
return clc.green;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { isLogLevelEnabled } from './utils';
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export type LogLevel = 'log' | 'error' | 'warn' | 'debug' | 'verbose' | 'fatal';
|
||||
export type LogLevel = 'log' | 'error' | 'warn' | 'debug' | 'verbose';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
@@ -37,11 +37,6 @@ export interface LoggerService {
|
||||
*/
|
||||
verbose?(message: any, ...optionalParams: any[]): any;
|
||||
|
||||
/**
|
||||
* Write a 'fatal' level log.
|
||||
*/
|
||||
fatal?(message: any, ...optionalParams: any[]): any;
|
||||
|
||||
/**
|
||||
* Set log levels.
|
||||
* @param levels log levels
|
||||
@@ -190,19 +185,6 @@ export class Logger implements LoggerService {
|
||||
this.localInstance?.verbose?.(message, ...optionalParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a 'fatal' level log.
|
||||
*/
|
||||
fatal(message: any, context?: string): void;
|
||||
fatal(message: any, ...optionalParams: [...any, string?]): void;
|
||||
@Logger.WrapBuffer
|
||||
fatal(message: any, ...optionalParams: any[]) {
|
||||
optionalParams = this.context
|
||||
? optionalParams.concat(this.context)
|
||||
: optionalParams;
|
||||
this.localInstance?.fatal?.(message, ...optionalParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an 'error' level log.
|
||||
*/
|
||||
@@ -259,16 +241,6 @@ export class Logger implements LoggerService {
|
||||
this.staticInstanceRef?.verbose?.(message, ...optionalParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a 'fatal' level log.
|
||||
*/
|
||||
static fatal(message: any, context?: string): void;
|
||||
static fatal(message: any, ...optionalParams: [...any, string?]): void;
|
||||
@Logger.WrapBuffer
|
||||
static fatal(message: any, ...optionalParams: any[]) {
|
||||
this.staticInstanceRef?.fatal?.(message, ...optionalParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print buffered logs and detach buffer.
|
||||
*/
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { LogLevel } from '../logger.service';
|
||||
|
||||
const LOG_LEVEL_VALUES: Record<LogLevel, number> = {
|
||||
verbose: 0,
|
||||
debug: 1,
|
||||
debug: 0,
|
||||
verbose: 1,
|
||||
log: 2,
|
||||
warn: 3,
|
||||
error: 4,
|
||||
fatal: 5,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -104,6 +104,7 @@ describe('ConfigurableModuleBuilder', () => {
|
||||
|
||||
expect(definition.global).to.equal(true);
|
||||
expect(definition.providers).to.have.length(5);
|
||||
console.log(definition.providers);
|
||||
expect(definition.providers).to.deep.contain('test');
|
||||
expect(definition.providers).to.include.members(
|
||||
provideInjectionTokensFrom.slice(0, 2),
|
||||
|
||||
@@ -72,14 +72,6 @@ describe('FileTypeValidator', () => {
|
||||
|
||||
expect(fileTypeValidator.isValid(requestFile)).to.equal(false);
|
||||
});
|
||||
|
||||
it('should return false when no file provided', () => {
|
||||
const fileTypeValidator = new FileTypeValidator({
|
||||
fileType: 'image/jpeg',
|
||||
});
|
||||
|
||||
expect(fileTypeValidator.isValid()).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildErrorMessage', () => {
|
||||
|
||||
@@ -40,14 +40,6 @@ describe('MaxFileSizeValidator', () => {
|
||||
|
||||
expect(maxFileSizeValidator.isValid(requestFile)).to.equal(false);
|
||||
});
|
||||
|
||||
it('should return true when no file provided', () => {
|
||||
const maxFileSizeValidator = new MaxFileSizeValidator({
|
||||
maxSize: oneKb,
|
||||
});
|
||||
|
||||
expect(maxFileSizeValidator.isValid()).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildErrorMessage', () => {
|
||||
|
||||
@@ -18,12 +18,6 @@ describe('ParseBoolPipe', () => {
|
||||
expect(await target.transform(false, {} as ArgumentMetadata)).to.be
|
||||
.false;
|
||||
});
|
||||
|
||||
it('should not throw an error if the value is undefined/null and optional is true', async () => {
|
||||
const target = new ParseBoolPipe({ optional: true });
|
||||
const value = await target.transform(undefined, {} as ArgumentMetadata);
|
||||
expect(value).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
describe('when validation fails', () => {
|
||||
it('should throw an error', async () => {
|
||||
|
||||
@@ -14,7 +14,6 @@ describe('ParseEnumPipe', () => {
|
||||
Up = 'UP',
|
||||
}
|
||||
let target: ParseEnumPipe;
|
||||
|
||||
beforeEach(() => {
|
||||
target = new ParseEnumPipe(Direction, {
|
||||
exceptionFactory: (error: any) => new CustomTestError(),
|
||||
@@ -27,12 +26,6 @@ describe('ParseEnumPipe', () => {
|
||||
Direction.Up,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not throw an error if enumType is undefined/null and optional is true', async () => {
|
||||
const target = new ParseEnumPipe('DOWN', { optional: true });
|
||||
const value = await target.transform(undefined, {} as ArgumentMetadata);
|
||||
expect(value).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
describe('when validation fails', () => {
|
||||
it('should throw an error', async () => {
|
||||
@@ -40,16 +33,6 @@ describe('ParseEnumPipe', () => {
|
||||
target.transform('DOWN', {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(CustomTestError);
|
||||
});
|
||||
|
||||
it('should throw an error if enumType is wrong and optional is true', async () => {
|
||||
target = new ParseEnumPipe(Direction, {
|
||||
exceptionFactory: (error: any) => new CustomTestError(),
|
||||
optional: true,
|
||||
});
|
||||
return expect(
|
||||
target.transform('DOWN', {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(CustomTestError);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('constructor', () => {
|
||||
|
||||
@@ -25,11 +25,6 @@ describe('ParseFloatPipe', () => {
|
||||
parseFloat(num),
|
||||
);
|
||||
});
|
||||
it('should not throw an error if the value is undefined/null and optional is true', async () => {
|
||||
const target = new ParseFloatPipe({ optional: true });
|
||||
const value = await target.transform(undefined, {} as ArgumentMetadata);
|
||||
expect(value).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
describe('when validation fails', () => {
|
||||
it('should throw an error', async () => {
|
||||
|
||||
@@ -30,11 +30,6 @@ describe('ParseIntPipe', () => {
|
||||
-3,
|
||||
);
|
||||
});
|
||||
it('should not throw an error if the value is undefined/null and optional is true', async () => {
|
||||
const target = new ParseIntPipe({ optional: true });
|
||||
const value = await target.transform(undefined, {} as ArgumentMetadata);
|
||||
expect(value).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
describe('when validation fails', () => {
|
||||
it('should throw an error', async () => {
|
||||
|
||||
@@ -41,11 +41,6 @@ describe('ParseUUIDPipe', () => {
|
||||
target = new ParseUUIDPipe({ version: '5', exceptionFactory });
|
||||
expect(await target.transform(v5, {} as ArgumentMetadata)).to.equal(v5);
|
||||
});
|
||||
it('should not throw an error if the value is undefined/null and optional is true', async () => {
|
||||
const target = new ParseUUIDPipe({ optional: true });
|
||||
const value = await target.transform(undefined, {} as ArgumentMetadata);
|
||||
expect(value).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when validation fails', () => {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
## Description
|
||||
|
||||
Nest is a framework for building efficient, scalable <a href="https://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="https://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
|
||||
@@ -34,10 +34,10 @@ Nest is a framework for building efficient, scalable <a href="https://nodejs.org
|
||||
|
||||
## Getting started
|
||||
|
||||
- To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
- 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
- [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
|
||||
- [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
|
||||
* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
* [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
|
||||
* [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
|
||||
|
||||
## Questions
|
||||
|
||||
@@ -56,29 +56,26 @@ With official support, you can get expert help straight from Nest core team. We
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support from the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td><td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></tr><tr>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="90" valign="middle" /></a></td><td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="110" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td><td>
|
||||
<a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td>
|
||||
@@ -87,9 +84,17 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td>
|
||||
<td><a href="https://twistag.com/" target="_blank"><img src="https://nestjs.com/img/twistag-logo.png" width="120" valign="middle" /></td></tr>
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a> </td><td>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" width="100" /></a> </td><td>
|
||||
<a href="https://www.myleodsc.com/" target="_blank"><img src="https://nestjs.com/img/myleo-logo.png" width="180" valign="middle" /></td><td>
|
||||
<a href="https://careers.meetdandy.com/?gh_src=063ba61e3us" target="_blank"><img src="https://nestjs.com/img/dandy-roles-logo.svg" width="150" valign="middle" /></td><td>
|
||||
<a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
|
||||
<td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
|
||||
<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
|
||||
<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td>
|
||||
<td><a href="https://chax.at" target="_blank"><img src="https://nestjs.com/img/chaxat-logo.png" width="100" valign="middle" /></td></tr>
|
||||
</table>
|
||||
|
||||
#### Sponsors
|
||||
@@ -97,25 +102,41 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<table><tr><td align="center" valign="middle">
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a></td><td align="center" valign="middle">
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" width="72" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" width="120" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" width="125" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" width="100" valign="middle" /></a> </td></tr><tr><td align="center" valign="middle">
|
||||
<a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" width="95" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" width="107" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td>
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" width="71" valign="middle" /></a></td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a></td></tr><tr><td align="center" valign="middle">
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://www.thebigphonestore.co.uk/" target="_blank"><img src="https://nestjs.com/img/the-big-phone-store-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td>
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td></tr><tr><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://pickwriters.com/top-10-translation-services" target="_blank"><img src="https://nestjs.com/img/pickwriters-logo.png" width="40" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://thewordpoint.com/services/localization" target="_blank"><img src="https://nestjs.com/img/thewordpoint-logo.png" width="40" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://meercode.io/" target="_blank"><img src="https://nestjs.com/img/meercode-logo.png" width="60" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://thestandarddaily.com/" target="_blank"><img src="https://nestjs.com/img/the-standard-daily-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://houseofangular.io/" target="_blank"><img src="https://nestjs.com/img/house-of-angular.png" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.bystored.com/" target="_blank"><img src="https://nestjs.com/img/stored-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://studyclerk.com/pay-for-research-paper" target="_blank"><img src="https://nestjs.com/img/studyclerk-logo.png" width="125" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://xyndata.com" target="_blank"><img src="https://nestjs.com/img/xyndata-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://messaged.com/" target="_blank"><img src="https://nestjs.com/img/messaged-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://klqc.de" target="_blank"><img src="https://nestjs.com/img/klcqcl-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://themobilereality.com/" target="_blank"><img src="https://nestjs.com/img/mobile-reality-logo.png" width="45" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/julienferand-logo.jpeg" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
@@ -124,9 +145,9 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
* Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||
* Website - [https://nestjs.com](https://nestjs.com/)
|
||||
* Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { ModulesContainer } from '../injector/modules-container';
|
||||
|
||||
export class DiscoverableMetaHostCollection {
|
||||
/**
|
||||
* A map of class references to metadata keys.
|
||||
*/
|
||||
public static readonly metaHostLinks = new Map<Type | Function, string>();
|
||||
|
||||
/**
|
||||
* A map of metadata keys to instance wrappers (providers) with the corresponding metadata key.
|
||||
* The map is weakly referenced by the modules container (unique per application).
|
||||
*/
|
||||
private static readonly providersByMetaKey = new WeakMap<
|
||||
ModulesContainer,
|
||||
Map<string, Set<InstanceWrapper>>
|
||||
>();
|
||||
|
||||
/**
|
||||
* A map of metadata keys to instance wrappers (controllers) with the corresponding metadata key.
|
||||
* The map is weakly referenced by the modules container (unique per application).
|
||||
*/
|
||||
private static readonly controllersByMetaKey = new WeakMap<
|
||||
ModulesContainer,
|
||||
Map<string, Set<InstanceWrapper>>
|
||||
>();
|
||||
|
||||
/**
|
||||
* Adds a link between a class reference and a metadata key.
|
||||
* @param target The class reference.
|
||||
* @param metadataKey The metadata key.
|
||||
*/
|
||||
public static addClassMetaHostLink(
|
||||
target: Type | Function,
|
||||
metadataKey: string,
|
||||
) {
|
||||
this.metaHostLinks.set(target, metadataKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects a provider instance wrapper and adds it to the collection of providers
|
||||
* if it has a metadata key.
|
||||
* @param hostContainerRef A reference to the modules container.
|
||||
* @param instanceWrapper A provider instance wrapper.
|
||||
* @returns void
|
||||
*/
|
||||
public static inspectProvider(
|
||||
hostContainerRef: ModulesContainer,
|
||||
instanceWrapper: InstanceWrapper,
|
||||
) {
|
||||
return this.inspectInstanceWrapper(
|
||||
hostContainerRef,
|
||||
instanceWrapper,
|
||||
this.providersByMetaKey,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects a controller instance wrapper and adds it to the collection of controllers
|
||||
* if it has a metadata key.
|
||||
* @param hostContainerRef A reference to the modules container.
|
||||
* @param instanceWrapper A controller's instance wrapper.
|
||||
* @returns void
|
||||
*/
|
||||
public static inspectController(
|
||||
hostContainerRef: ModulesContainer,
|
||||
instanceWrapper: InstanceWrapper,
|
||||
) {
|
||||
return this.inspectInstanceWrapper(
|
||||
hostContainerRef,
|
||||
instanceWrapper,
|
||||
this.controllersByMetaKey,
|
||||
);
|
||||
}
|
||||
|
||||
public static insertByMetaKey(
|
||||
metaKey: string,
|
||||
instanceWrapper: InstanceWrapper,
|
||||
collection: Map<string, Set<InstanceWrapper>>,
|
||||
) {
|
||||
if (collection.has(metaKey)) {
|
||||
const wrappers = collection.get(metaKey);
|
||||
wrappers.add(instanceWrapper);
|
||||
} else {
|
||||
const wrappers = new Set<InstanceWrapper>();
|
||||
wrappers.add(instanceWrapper);
|
||||
collection.set(metaKey, wrappers);
|
||||
}
|
||||
}
|
||||
|
||||
public static getProvidersByMetaKey(
|
||||
hostContainerRef: ModulesContainer,
|
||||
metaKey: string,
|
||||
): Set<InstanceWrapper> {
|
||||
const wrappersByMetaKey = this.providersByMetaKey.get(hostContainerRef);
|
||||
return wrappersByMetaKey.get(metaKey);
|
||||
}
|
||||
|
||||
public static getControllersByMetaKey(
|
||||
hostContainerRef: ModulesContainer,
|
||||
metaKey: string,
|
||||
): Set<InstanceWrapper> {
|
||||
const wrappersByMetaKey = this.controllersByMetaKey.get(hostContainerRef);
|
||||
return wrappersByMetaKey.get(metaKey);
|
||||
}
|
||||
|
||||
private static inspectInstanceWrapper(
|
||||
hostContainerRef: ModulesContainer,
|
||||
instanceWrapper: InstanceWrapper,
|
||||
wrapperByMetaKeyMap: WeakMap<
|
||||
ModulesContainer,
|
||||
Map<string, Set<InstanceWrapper>>
|
||||
>,
|
||||
) {
|
||||
const metaKey =
|
||||
DiscoverableMetaHostCollection.getMetaKeyByInstanceWrapper(
|
||||
instanceWrapper,
|
||||
);
|
||||
if (!metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
let collection: Map<string, Set<InstanceWrapper>>;
|
||||
if (wrapperByMetaKeyMap.has(hostContainerRef)) {
|
||||
collection = wrapperByMetaKeyMap.get(hostContainerRef);
|
||||
} else {
|
||||
collection = new Map<string, Set<InstanceWrapper>>();
|
||||
wrapperByMetaKeyMap.set(hostContainerRef, collection);
|
||||
}
|
||||
this.insertByMetaKey(metaKey, instanceWrapper, collection);
|
||||
}
|
||||
|
||||
private static getMetaKeyByInstanceWrapper(
|
||||
instanceWrapper: InstanceWrapper<any>,
|
||||
) {
|
||||
return this.metaHostLinks.get(
|
||||
// NOTE: Regarding the ternary statement below,
|
||||
// - The condition `!wrapper.metatype` is needed because when we use `useValue`
|
||||
// the value of `wrapper.metatype` will be `null`.
|
||||
// - The condition `wrapper.inject` is needed here because when we use
|
||||
// `useFactory`, the value of `wrapper.metatype` will be the supplied
|
||||
// factory function.
|
||||
// For both cases, we should use `wrapper.instance.constructor` instead
|
||||
// of `wrapper.metatype` to resolve processor's class properly.
|
||||
// But since calling `wrapper.instance` could degrade overall performance
|
||||
// we must defer it as much we can.
|
||||
instanceWrapper.metatype || instanceWrapper.inject
|
||||
? instanceWrapper.instance?.constructor ?? instanceWrapper.metatype
|
||||
: instanceWrapper.metatype,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,15 @@
|
||||
import {
|
||||
CustomDecorator,
|
||||
flatten,
|
||||
Injectable,
|
||||
SetMetadata,
|
||||
} from '@nestjs/common';
|
||||
import { uid } from 'uid';
|
||||
import { flatten, Injectable } from '@nestjs/common';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import { ModulesContainer } from '../injector/modules-container';
|
||||
import { DiscoverableMetaHostCollection } from './discoverable-meta-host-collection';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export interface FilterByInclude {
|
||||
/**
|
||||
* List of modules to include (whitelist) into the discovery process.
|
||||
*/
|
||||
export interface DiscoveryOptions {
|
||||
include?: Function[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export interface FilterByMetadataKey {
|
||||
/**
|
||||
* A key to filter controllers and providers by.
|
||||
* Only instance wrappers with the specified metadata key will be returned.
|
||||
*/
|
||||
metadataKey?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export type DiscoveryOptions = FilterByInclude | FilterByMetadataKey;
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export type DiscoverableDecorator<T> = ((opts?: T) => CustomDecorator) & {
|
||||
KEY: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
@@ -50,107 +17,24 @@ export type DiscoverableDecorator<T> = ((opts?: T) => CustomDecorator) & {
|
||||
export class DiscoveryService {
|
||||
constructor(private readonly modulesContainer: ModulesContainer) {}
|
||||
|
||||
/**
|
||||
* Creates a decorator that can be used to decorate classes and methods with metadata.
|
||||
* The decorator will also add the class to the collection of discoverable classes (by metadata key).
|
||||
* Decorated classes can be discovered using the `getProviders` and `getControllers` methods.
|
||||
* @returns A decorator function.
|
||||
*/
|
||||
static createDecorator<T>(): DiscoverableDecorator<T> {
|
||||
const metadataKey = uid(21);
|
||||
const decoratorFn =
|
||||
(opts: T) =>
|
||||
(target: object | Function, key?: string | symbol, descriptor?: any) => {
|
||||
if (!descriptor) {
|
||||
DiscoverableMetaHostCollection.addClassMetaHostLink(
|
||||
target as Function,
|
||||
metadataKey,
|
||||
);
|
||||
}
|
||||
SetMetadata(metadataKey, opts ?? {})(target, key, descriptor);
|
||||
};
|
||||
|
||||
decoratorFn.KEY = metadataKey;
|
||||
return decoratorFn as DiscoverableDecorator<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of instance wrappers (providers).
|
||||
* Depending on the options, the array will contain either all providers or only providers with the specified metadata key.
|
||||
* @param options Discovery options.
|
||||
* @param modules A list of modules to filter by.
|
||||
* @returns An array of instance wrappers (providers).
|
||||
*/
|
||||
public getProviders(
|
||||
getProviders(
|
||||
options: DiscoveryOptions = {},
|
||||
modules: Module[] = this.getModules(options),
|
||||
): InstanceWrapper[] {
|
||||
if ('metadataKey' in options) {
|
||||
const providers = DiscoverableMetaHostCollection.getProvidersByMetaKey(
|
||||
this.modulesContainer,
|
||||
options.metadataKey,
|
||||
);
|
||||
return Array.from(providers);
|
||||
}
|
||||
|
||||
const providers = modules.map(item => [...item.providers.values()]);
|
||||
return flatten(providers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of instance wrappers (controllers).
|
||||
* Depending on the options, the array will contain either all controllers or only controllers with the specified metadata key.
|
||||
* @param options Discovery options.
|
||||
* @param modules A list of modules to filter by.
|
||||
* @returns An array of instance wrappers (controllers).
|
||||
*/
|
||||
public getControllers(
|
||||
getControllers(
|
||||
options: DiscoveryOptions = {},
|
||||
modules: Module[] = this.getModules(options),
|
||||
): InstanceWrapper[] {
|
||||
if ('metadataKey' in options) {
|
||||
const controllers =
|
||||
DiscoverableMetaHostCollection.getControllersByMetaKey(
|
||||
this.modulesContainer,
|
||||
options.metadataKey,
|
||||
);
|
||||
return Array.from(controllers);
|
||||
}
|
||||
|
||||
const controllers = modules.map(item => [...item.controllers.values()]);
|
||||
return flatten(controllers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves metadata from the specified instance wrapper.
|
||||
* @param decorator The decorator to retrieve metadata of.
|
||||
* @param instanceWrapper Reference to the instance wrapper.
|
||||
* @param methodKey An optional method key to retrieve metadata from.
|
||||
* @returns Discovered metadata.
|
||||
*/
|
||||
public getMetadataByDecorator<T extends DiscoverableDecorator<any>>(
|
||||
decorator: T,
|
||||
instanceWrapper: InstanceWrapper,
|
||||
methodKey?: string,
|
||||
): T extends DiscoverableDecorator<infer R> ? R | undefined : T | undefined {
|
||||
if (methodKey) {
|
||||
return Reflect.getMetadata(
|
||||
decorator.KEY,
|
||||
instanceWrapper.instance[methodKey],
|
||||
);
|
||||
}
|
||||
|
||||
const clsRef =
|
||||
instanceWrapper.instance?.constructor ?? instanceWrapper.metatype;
|
||||
return Reflect.getMetadata(decorator.KEY, clsRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of modules to be used for discovery.
|
||||
*/
|
||||
protected getModules(options: DiscoveryOptions = {}): Module[] {
|
||||
const includeInOpts = 'include' in options;
|
||||
if (!includeInOpts) {
|
||||
if (!options.include) {
|
||||
const moduleRefs = [...this.modulesContainer.values()];
|
||||
return moduleRefs;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ export class CircularDependencyException extends RuntimeException {
|
||||
constructor(context?: string) {
|
||||
const ctx = context ? ` inside ${context}` : ``;
|
||||
super(
|
||||
`A circular dependency has been detected${ctx}. Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.`,
|
||||
`A circular dependency has been detected${ctx}. Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ export class ExecutionContextHost implements ExecutionContext {
|
||||
return Object.assign(this, {
|
||||
getClient: () => this.getArgByIndex(0),
|
||||
getData: () => this.getArgByIndex(1),
|
||||
getPattern: () => this.getArgByIndex(this.getArgs().length - 1),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
} from '@nestjs/common/constants';
|
||||
import { Injectable, Type } from '@nestjs/common/interfaces';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { DiscoverableMetaHostCollection } from '../discovery/discoverable-meta-host-collection';
|
||||
import {
|
||||
CircularDependencyException,
|
||||
UndefinedForwardRefException,
|
||||
@@ -71,13 +70,7 @@ export class NestContainer {
|
||||
public async addModule(
|
||||
metatype: ModuleMetatype,
|
||||
scope: ModuleScope,
|
||||
): Promise<
|
||||
| {
|
||||
moduleRef: Module;
|
||||
inserted: boolean;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
): Promise<Module | undefined> {
|
||||
// In DependenciesScanner#scanForModules we already check for undefined or invalid modules
|
||||
// We still need to catch the edge-case of `forwardRef(() => undefined)`
|
||||
if (!metatype) {
|
||||
@@ -87,36 +80,24 @@ export class NestContainer {
|
||||
metatype,
|
||||
);
|
||||
if (this.modules.has(token)) {
|
||||
return {
|
||||
moduleRef: this.modules.get(token),
|
||||
inserted: true,
|
||||
};
|
||||
return this.modules.get(token);
|
||||
}
|
||||
|
||||
return {
|
||||
moduleRef: await this.setModule(
|
||||
{
|
||||
token,
|
||||
type,
|
||||
dynamicMetadata,
|
||||
},
|
||||
scope,
|
||||
),
|
||||
inserted: true,
|
||||
};
|
||||
return this.setModule(
|
||||
{
|
||||
token,
|
||||
type,
|
||||
dynamicMetadata,
|
||||
},
|
||||
scope,
|
||||
);
|
||||
}
|
||||
|
||||
public async replaceModule(
|
||||
metatypeToReplace: ModuleMetatype,
|
||||
newMetatype: ModuleMetatype,
|
||||
scope: ModuleScope,
|
||||
): Promise<
|
||||
| {
|
||||
moduleRef: Module;
|
||||
inserted: boolean;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
): Promise<Module | undefined> {
|
||||
// In DependenciesScanner#scanForModules we already check for undefined or invalid modules
|
||||
// We still need to catch the edge-case of `forwardRef(() => undefined)`
|
||||
if (!metatypeToReplace || !newMetatype) {
|
||||
@@ -128,17 +109,14 @@ export class NestContainer {
|
||||
newMetatype,
|
||||
);
|
||||
|
||||
return {
|
||||
moduleRef: await this.setModule(
|
||||
{
|
||||
token,
|
||||
type,
|
||||
dynamicMetadata,
|
||||
},
|
||||
scope,
|
||||
),
|
||||
inserted: false,
|
||||
};
|
||||
return this.setModule(
|
||||
{
|
||||
token,
|
||||
type,
|
||||
dynamicMetadata,
|
||||
},
|
||||
scope,
|
||||
);
|
||||
}
|
||||
|
||||
private async setModule(
|
||||
@@ -224,7 +202,7 @@ export class NestContainer {
|
||||
relatedModule,
|
||||
);
|
||||
const related = this.modules.get(relatedModuleToken);
|
||||
moduleRef.addImport(related);
|
||||
moduleRef.addRelatedModule(related);
|
||||
}
|
||||
|
||||
public addProvider(
|
||||
@@ -239,12 +217,7 @@ export class NestContainer {
|
||||
if (!moduleRef) {
|
||||
throw new UnknownModuleException();
|
||||
}
|
||||
const providerKey = moduleRef.addProvider(provider, enhancerSubtype);
|
||||
const providerRef = moduleRef.getProviderByKey(providerKey);
|
||||
|
||||
DiscoverableMetaHostCollection.inspectProvider(this.modules, providerRef);
|
||||
|
||||
return providerKey as Function;
|
||||
return moduleRef.addProvider(provider, enhancerSubtype) as Function;
|
||||
}
|
||||
|
||||
public addInjectable(
|
||||
@@ -274,12 +247,6 @@ export class NestContainer {
|
||||
}
|
||||
const moduleRef = this.modules.get(token);
|
||||
moduleRef.addController(controller);
|
||||
|
||||
const controllerRef = moduleRef.controllers.get(controller);
|
||||
DiscoverableMetaHostCollection.inspectController(
|
||||
this.modules,
|
||||
controllerRef,
|
||||
);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
@@ -304,7 +271,7 @@ export class NestContainer {
|
||||
if (target === globalModule || target === this.internalCoreModule) {
|
||||
return;
|
||||
}
|
||||
target.addImport(globalModule);
|
||||
target.addRelatedModule(globalModule);
|
||||
}
|
||||
|
||||
public getDynamicMetadataByToken(token: string): Partial<DynamicModule>;
|
||||
|
||||
@@ -8,6 +8,5 @@ export class SilentLogger extends Logger {
|
||||
warn = noop;
|
||||
debug = noop;
|
||||
verbose = noop;
|
||||
fatal = noop;
|
||||
setLogLevels = noop;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { CircularDependencyException } from '../errors/exceptions';
|
||||
import { RuntimeException } from '../errors/exceptions/runtime.exception';
|
||||
import { UndefinedDependencyException } from '../errors/exceptions/undefined-dependency.exception';
|
||||
import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception';
|
||||
@@ -36,7 +35,6 @@ import {
|
||||
PropertyMetadata,
|
||||
} from './instance-wrapper';
|
||||
import { Module } from './module';
|
||||
import { SettlementSignal } from './settlement-signal';
|
||||
|
||||
/**
|
||||
* The type of an injectable dependency
|
||||
@@ -113,21 +111,14 @@ export class Injector {
|
||||
this.getContextId(contextId, wrapper),
|
||||
inquirerId,
|
||||
);
|
||||
|
||||
if (instanceHost.isPending) {
|
||||
const settlementSignal = wrapper.settlementSignal;
|
||||
if (inquirer && settlementSignal?.isCycle(inquirer.id)) {
|
||||
throw new CircularDependencyException(`"${wrapper.name}"`);
|
||||
}
|
||||
|
||||
return instanceHost.donePromise.then((err?: unknown) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const settlementSignal = this.applySettlementSignal(instanceHost, wrapper);
|
||||
const done = this.applyDoneHook(instanceHost);
|
||||
const token = wrapper.token || wrapper.name;
|
||||
|
||||
const { inject } = wrapper;
|
||||
@@ -136,7 +127,7 @@ export class Injector {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (instanceHost.isResolved) {
|
||||
return settlementSignal.complete();
|
||||
return done();
|
||||
}
|
||||
try {
|
||||
const t0 = this.getNowTimestamp();
|
||||
@@ -158,7 +149,7 @@ export class Injector {
|
||||
);
|
||||
this.applyProperties(instance, properties);
|
||||
wrapper.initTime = this.getNowTimestamp() - t0;
|
||||
settlementSignal.complete();
|
||||
done();
|
||||
};
|
||||
await this.resolveConstructorParams<T>(
|
||||
wrapper,
|
||||
@@ -170,7 +161,7 @@ export class Injector {
|
||||
inquirer,
|
||||
);
|
||||
} catch (err) {
|
||||
settlementSignal.error(err);
|
||||
done(err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -246,16 +237,15 @@ export class Injector {
|
||||
await this.loadEnhancersPerContext(wrapper, contextId, wrapper);
|
||||
}
|
||||
|
||||
public applySettlementSignal<T>(
|
||||
instancePerContext: InstancePerContext<T>,
|
||||
host: InstanceWrapper<T>,
|
||||
) {
|
||||
const settlementSignal = new SettlementSignal();
|
||||
instancePerContext.donePromise = settlementSignal.asPromise();
|
||||
instancePerContext.isPending = true;
|
||||
host.settlementSignal = settlementSignal;
|
||||
|
||||
return settlementSignal;
|
||||
public applyDoneHook<T>(
|
||||
wrapper: InstancePerContext<T>,
|
||||
): (err?: unknown) => void {
|
||||
let done: (err?: unknown) => void;
|
||||
wrapper.donePromise = new Promise<unknown>((resolve, reject) => {
|
||||
done = resolve;
|
||||
});
|
||||
wrapper.isPending = true;
|
||||
return done;
|
||||
}
|
||||
|
||||
public async resolveConstructorParams<T>(
|
||||
@@ -467,8 +457,6 @@ export class Injector {
|
||||
inquirerId,
|
||||
);
|
||||
if (!instanceHost.isResolved && !instanceWrapper.forwardRef) {
|
||||
inquirer?.settlementSignal?.insertRef(instanceWrapper.id);
|
||||
|
||||
await this.loadProvider(
|
||||
instanceWrapper,
|
||||
instanceWrapper.host ?? moduleRef,
|
||||
@@ -593,7 +581,6 @@ export class Injector {
|
||||
}
|
||||
this.printLookingForProviderLog(name, relatedModule);
|
||||
moduleRegistry.push(relatedModule.id);
|
||||
|
||||
const { providers, exports } = relatedModule;
|
||||
if (!exports.has(name) || !providers.has(name)) {
|
||||
const instanceRef = await this.lookupComponentInImports(
|
||||
@@ -622,8 +609,6 @@ export class Injector {
|
||||
inquirerId,
|
||||
);
|
||||
if (!instanceHost.isResolved && !instanceWrapperRef.forwardRef) {
|
||||
wrapper.settlementSignal?.insertRef(instanceWrapperRef.id);
|
||||
|
||||
await this.loadProvider(
|
||||
instanceWrapperRef,
|
||||
relatedModule,
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
isValueProvider,
|
||||
} from './helpers/provider-classifier';
|
||||
import { Module } from './module';
|
||||
import { SettlementSignal } from './settlement-signal';
|
||||
|
||||
export const INSTANCE_METADATA_SYMBOL = Symbol.for('instance_metadata:cache');
|
||||
export const INSTANCE_ID_SYMBOL = Symbol.for('instance_metadata:id');
|
||||
@@ -64,13 +63,13 @@ export class InstanceWrapper<T = any> {
|
||||
public readonly host?: Module;
|
||||
public readonly isAlias: boolean = false;
|
||||
public readonly subtype?: EnhancerSubtype;
|
||||
|
||||
public scope?: Scope = Scope.DEFAULT;
|
||||
public metatype: Type<T> | Function;
|
||||
public inject?: FactoryProvider['inject'];
|
||||
public forwardRef?: boolean;
|
||||
public durable?: boolean;
|
||||
public initTime?: number;
|
||||
public settlementSignal?: SettlementSignal;
|
||||
|
||||
private static logger: LoggerService = new Logger(InstanceWrapper.name);
|
||||
|
||||
@@ -105,11 +104,8 @@ export class InstanceWrapper<T = any> {
|
||||
}
|
||||
|
||||
get isNotMetatype(): boolean {
|
||||
return !this.metatype || this.isFactory;
|
||||
}
|
||||
|
||||
get isFactory(): boolean {
|
||||
return this.metatype && !isNil(this.inject);
|
||||
const isFactory = this.metatype && !isNil(this.inject);
|
||||
return !this.metatype || isFactory;
|
||||
}
|
||||
|
||||
get isTransient(): boolean {
|
||||
|
||||
@@ -31,7 +31,6 @@ export class LazyModuleLoader {
|
||||
const moduleInstances = await this.dependenciesScanner.scanForModules({
|
||||
moduleDefinition: moduleClassOrDynamicDefinition,
|
||||
overrides: this.moduleOverrides,
|
||||
lazy: true,
|
||||
});
|
||||
if (moduleInstances.length === 0) {
|
||||
// The module has been loaded already. In this case, we must
|
||||
|
||||
@@ -240,11 +240,11 @@ export class Module {
|
||||
return instanceWrapper;
|
||||
}
|
||||
|
||||
public addProvider(provider: Provider): InjectionToken;
|
||||
public addProvider(provider: Provider): Provider | InjectionToken;
|
||||
public addProvider(
|
||||
provider: Provider,
|
||||
enhancerSubtype: EnhancerSubtype,
|
||||
): InjectionToken;
|
||||
): Provider | InjectionToken;
|
||||
public addProvider(provider: Provider, enhancerSubtype?: EnhancerSubtype) {
|
||||
if (this.isCustomProvider(provider)) {
|
||||
if (this.isEntryProvider(provider.provide)) {
|
||||
@@ -517,13 +517,6 @@ export class Module {
|
||||
});
|
||||
}
|
||||
|
||||
public addImport(moduleRef: Module) {
|
||||
this._imports.add(moduleRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public addRelatedModule(module: Module) {
|
||||
this._imports.add(module);
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* SettlementSignal is used to signal the resolution of a provider/instance.
|
||||
* Calling `complete` or `error` will resolve the promise returned by `asPromise`.
|
||||
* Can be used to detect circular dependencies.
|
||||
*/
|
||||
export class SettlementSignal {
|
||||
private readonly _refs = new Set();
|
||||
private readonly settledPromise: Promise<unknown>;
|
||||
private settleFn!: (err?: unknown) => void;
|
||||
private completed = false;
|
||||
|
||||
constructor() {
|
||||
this.settledPromise = new Promise<unknown>(resolve => {
|
||||
this.settleFn = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the promise returned by `asPromise`.
|
||||
*/
|
||||
public complete() {
|
||||
this.completed = true;
|
||||
this.settleFn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rejects the promise returned by `asPromise` with the given error.
|
||||
* @param err Error to reject the promise returned by `asPromise` with.
|
||||
*/
|
||||
public error(err: unknown) {
|
||||
this.completed = true;
|
||||
this.settleFn(err);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will be resolved when `complete` or `error` is called.
|
||||
* @returns Promise that will be resolved when `complete` or `error` is called.
|
||||
*/
|
||||
public asPromise() {
|
||||
return this.settledPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a wrapper id that the host of this signal depends on.
|
||||
* @param wrapperId Wrapper id to insert.
|
||||
*/
|
||||
public insertRef(wrapperId: string) {
|
||||
this._refs.add(wrapperId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if relationship is circular.
|
||||
* @param wrapperId Wrapper id to check.
|
||||
* @returns True if relationship is circular, false otherwise.
|
||||
*/
|
||||
public isCycle(wrapperId: string) {
|
||||
return !this.completed && this._refs.has(wrapperId);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,7 @@ import {
|
||||
Controller,
|
||||
} from '@nestjs/common/interfaces';
|
||||
import { isEmpty } from '@nestjs/common/utils/shared.utils';
|
||||
import { AsyncResource } from 'async_hooks';
|
||||
import { Observable, defer, from as fromPromise } from 'rxjs';
|
||||
import { defer, from as fromPromise, Observable } from 'rxjs';
|
||||
import { mergeAll, switchMap } from 'rxjs/operators';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context-host';
|
||||
|
||||
@@ -27,7 +26,7 @@ export class InterceptorsConsumer {
|
||||
|
||||
const nextFn = async (i = 0) => {
|
||||
if (i >= interceptors.length) {
|
||||
return defer(AsyncResource.bind(() => this.transformDeferred(next)));
|
||||
return this.transformDeferred(next);
|
||||
}
|
||||
const handler: CallHandler = {
|
||||
handle: () => fromPromise(nextFn(i + 1)).pipe(mergeAll()),
|
||||
|
||||
@@ -9,7 +9,7 @@ export class MetadataScanner {
|
||||
private readonly cachedScannedPrototypes: Map<object, string[]> = new Map();
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @deprecated
|
||||
* @see {@link getAllMethodNames}
|
||||
* @see getAllMethodNames
|
||||
*/
|
||||
@@ -62,7 +62,7 @@ export class MetadataScanner {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @deprecated
|
||||
* @see {@link getAllMethodNames}
|
||||
* @see getAllMethodNames
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
MiddlewareConfiguration,
|
||||
RouteInfo,
|
||||
} from '@nestjs/common/interfaces/middleware';
|
||||
import { stripEndSlash } from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { RouteInfoPathExtractor } from './route-info-path-extractor';
|
||||
import { RoutesMapper } from './routes-mapper';
|
||||
@@ -101,27 +100,21 @@ export class MiddlewareBuilder implements MiddlewareConsumer {
|
||||
const routesWithRegex = routes
|
||||
.filter(route => route.path.includes(':'))
|
||||
.map(route => ({
|
||||
method: route.method,
|
||||
path: route.path,
|
||||
regex: new RegExp(
|
||||
'^(' + route.path.replace(regexMatchParams, wildcard) + ')$',
|
||||
'g',
|
||||
),
|
||||
}));
|
||||
|
||||
return routes.filter(route => {
|
||||
const isOverlapped = (item: { regex: RegExp } & RouteInfo): boolean => {
|
||||
if (route.method !== item.method) {
|
||||
return false;
|
||||
}
|
||||
const normalizedRoutePath = stripEndSlash(route.path);
|
||||
return (
|
||||
normalizedRoutePath !== item.path &&
|
||||
item.regex.test(normalizedRoutePath)
|
||||
);
|
||||
const isOverlapped = (v: { path: string; regex: RegExp }) => {
|
||||
return route.path !== v.path && route.path.match(v.regex);
|
||||
};
|
||||
const routeMatch = routesWithRegex.find(isOverlapped);
|
||||
return routeMatch === undefined;
|
||||
|
||||
if (routeMatch === undefined) {
|
||||
return route;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -66,7 +66,7 @@ export class MiddlewareModule<
|
||||
config,
|
||||
appRef,
|
||||
);
|
||||
this.routesMapper = new RoutesMapper(container, config);
|
||||
this.routesMapper = new RoutesMapper(container);
|
||||
this.resolver = new MiddlewareResolver(middlewareContainer, injector);
|
||||
this.routeInfoPathExtractor = new RouteInfoPathExtractor(config);
|
||||
this.injector = injector;
|
||||
|
||||
@@ -1,48 +1,35 @@
|
||||
import {
|
||||
MODULE_PATH,
|
||||
PATH_METADATA,
|
||||
VERSION_METADATA,
|
||||
} from '@nestjs/common/constants';
|
||||
import {
|
||||
RouteInfo,
|
||||
Type,
|
||||
VERSION_NEUTRAL,
|
||||
VersionValue,
|
||||
} from '@nestjs/common/interfaces';
|
||||
import { MODULE_PATH, PATH_METADATA } from '@nestjs/common/constants';
|
||||
import { RouteInfo, Type } from '@nestjs/common/interfaces';
|
||||
import {
|
||||
addLeadingSlash,
|
||||
isString,
|
||||
isUndefined,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { Module } from '../injector/module';
|
||||
import { MetadataScanner } from '../metadata-scanner';
|
||||
import { PathsExplorer, RouteDefinition } from '../router/paths-explorer';
|
||||
import { PathsExplorer } from '../router/paths-explorer';
|
||||
import { targetModulesByContainer } from '../router/router-module';
|
||||
|
||||
export class RoutesMapper {
|
||||
private readonly pathsExplorer: PathsExplorer;
|
||||
|
||||
constructor(
|
||||
private readonly container: NestContainer,
|
||||
private readonly applicationConfig: ApplicationConfig,
|
||||
) {
|
||||
constructor(private readonly container: NestContainer) {
|
||||
this.pathsExplorer = new PathsExplorer(new MetadataScanner());
|
||||
}
|
||||
|
||||
public mapRouteToRouteInfo(
|
||||
controllerOrRoute: Type<any> | RouteInfo | string,
|
||||
route: Type<any> | RouteInfo | string,
|
||||
): RouteInfo[] {
|
||||
if (isString(controllerOrRoute)) {
|
||||
return this.getRouteInfoFromPath(controllerOrRoute);
|
||||
if (isString(route)) {
|
||||
return this.getRouteInfoFromPath(route);
|
||||
}
|
||||
const routePathOrPaths = this.getRoutePath(controllerOrRoute);
|
||||
if (this.isRouteInfo(routePathOrPaths, controllerOrRoute)) {
|
||||
return this.getRouteInfoFromObject(controllerOrRoute);
|
||||
const routePathOrPaths = this.getRoutePath(route);
|
||||
if (this.isRouteInfo(routePathOrPaths, route)) {
|
||||
return this.getRouteInfoFromObject(route);
|
||||
}
|
||||
|
||||
return this.getRouteInfoFromController(controllerOrRoute, routePathOrPaths);
|
||||
return this.getRouteInfoFromController(route, routePathOrPaths);
|
||||
}
|
||||
|
||||
private getRouteInfoFromPath(routePath: string): RouteInfo[] {
|
||||
@@ -75,47 +62,33 @@ export class RoutesMapper {
|
||||
Object.create(controller),
|
||||
controller.prototype,
|
||||
);
|
||||
const controllerVersion = this.getVersionMetadata(controller);
|
||||
const versioningConfig = this.applicationConfig.getVersioning();
|
||||
const moduleRef = this.getHostModuleOfController(controller);
|
||||
const modulePath = this.getModulePath(moduleRef?.metatype);
|
||||
|
||||
const concatPaths = <T>(acc: T[], currentValue: T[]) =>
|
||||
acc.concat(currentValue);
|
||||
|
||||
const toUndefinedIfNeural = (version: VersionValue) =>
|
||||
version === VERSION_NEUTRAL ? undefined : version;
|
||||
|
||||
const toRouteInfo = (item: RouteDefinition, prefix: string) =>
|
||||
item.path
|
||||
?.map(p => {
|
||||
let endpointPath = modulePath ?? '';
|
||||
endpointPath += this.normalizeGlobalPath(prefix) + addLeadingSlash(p);
|
||||
|
||||
const routeInfo: RouteInfo = {
|
||||
path: endpointPath,
|
||||
method: item.requestMethod,
|
||||
};
|
||||
const version = item.version ?? controllerVersion;
|
||||
if (version && versioningConfig) {
|
||||
if (typeof version !== 'string' && Array.isArray(version)) {
|
||||
return version.map(v => ({
|
||||
...routeInfo,
|
||||
version: toUndefinedIfNeural(v),
|
||||
}));
|
||||
}
|
||||
routeInfo.version = toUndefinedIfNeural(version);
|
||||
}
|
||||
|
||||
return routeInfo;
|
||||
})
|
||||
.flat() as RouteInfo[];
|
||||
|
||||
return []
|
||||
.concat(routePath)
|
||||
.map(routePath =>
|
||||
controllerPaths
|
||||
.map(item => toRouteInfo(item, routePath))
|
||||
.map(item =>
|
||||
item.path?.map(p => {
|
||||
let path = modulePath ?? '';
|
||||
path += this.normalizeGlobalPath(routePath) + addLeadingSlash(p);
|
||||
|
||||
const routeInfo: RouteInfo = {
|
||||
path,
|
||||
method: item.requestMethod,
|
||||
};
|
||||
|
||||
if (item.version) {
|
||||
routeInfo.version = item.version;
|
||||
}
|
||||
|
||||
return routeInfo;
|
||||
}),
|
||||
)
|
||||
.reduce(concatPaths, []),
|
||||
)
|
||||
.reduce(concatPaths, []);
|
||||
@@ -168,16 +141,4 @@ export class RoutesMapper {
|
||||
);
|
||||
return modulePath ?? Reflect.getMetadata(MODULE_PATH, metatype);
|
||||
}
|
||||
|
||||
private getVersionMetadata(
|
||||
metatype: Type<unknown> | Function,
|
||||
): VersionValue | undefined {
|
||||
const versioningConfig = this.applicationConfig.getVersioning();
|
||||
if (versioningConfig) {
|
||||
return (
|
||||
Reflect.getMetadata(VERSION_METADATA, metatype) ??
|
||||
versioningConfig.defaultVersion
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,10 +177,6 @@ export class NestApplication
|
||||
}
|
||||
|
||||
public async init(): Promise<this> {
|
||||
if (this.isInitialized) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.applyOptions();
|
||||
await this.httpAdapter?.init();
|
||||
|
||||
|
||||
@@ -157,13 +157,13 @@ export class NestFactoryStatic {
|
||||
moduleCls: any,
|
||||
options?: NestApplicationContextOptions,
|
||||
): Promise<INestApplicationContext> {
|
||||
const applicationConfig = new ApplicationConfig();
|
||||
const container = new NestContainer(applicationConfig);
|
||||
const container = new NestContainer();
|
||||
const graphInspector = this.createGraphInspector(options, container);
|
||||
|
||||
this.setAbortOnError(options);
|
||||
this.registerLoggerConfiguration(options);
|
||||
|
||||
const applicationConfig = undefined;
|
||||
await this.initialize(
|
||||
moduleCls,
|
||||
container,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "10.2.1",
|
||||
"version": "9.4.2",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
@@ -32,17 +32,17 @@
|
||||
"fast-safe-stringify": "2.1.1",
|
||||
"iterare": "1.2.1",
|
||||
"path-to-regexp": "3.2.0",
|
||||
"tslib": "2.6.1",
|
||||
"tslib": "2.5.2",
|
||||
"uid": "2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/common": "10.2.1"
|
||||
"@nestjs/common": "9.4.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/microservices": "^10.0.0",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@nestjs/websockets": "^10.0.0",
|
||||
"@nestjs/common": "^9.0.0",
|
||||
"@nestjs/microservices": "^9.0.0",
|
||||
"@nestjs/platform-express": "^9.0.0",
|
||||
"@nestjs/websockets": "^9.0.0",
|
||||
"reflect-metadata": "^0.1.12",
|
||||
"rxjs": "^7.1.0"
|
||||
},
|
||||
|
||||
@@ -231,7 +231,6 @@ export class RouterExplorer {
|
||||
},
|
||||
};
|
||||
|
||||
this.copyMetadataToCallback(targetCallback, routeHandler);
|
||||
routerMethodRef(path, routeHandler);
|
||||
|
||||
this.graphInspector.insertEntrypointDefinition<HttpEntrypointMetadata>(
|
||||
@@ -423,17 +422,4 @@ export class RouterExplorer {
|
||||
}
|
||||
return contextId;
|
||||
}
|
||||
|
||||
private copyMetadataToCallback(
|
||||
originalCallback: RouterProxyCallback,
|
||||
targetCallback: Function,
|
||||
) {
|
||||
for (const key of Reflect.getMetadataKeys(originalCallback)) {
|
||||
Reflect.defineMetadata(
|
||||
key,
|
||||
Reflect.getMetadata(key, originalCallback),
|
||||
targetCallback,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { DynamicModule, ForwardReference, Provider } from '@nestjs/common';
|
||||
import {
|
||||
CATCH_WATERMARK,
|
||||
CONTROLLER_WATERMARK,
|
||||
EnhancerSubtype,
|
||||
ENHANCER_KEY_TO_SUBTYPE_MAP,
|
||||
EXCEPTION_FILTERS_METADATA,
|
||||
EnhancerSubtype,
|
||||
GUARDS_METADATA,
|
||||
INJECTABLE_WATERMARK,
|
||||
INTERCEPTORS_METADATA,
|
||||
@@ -68,7 +68,6 @@ interface ModulesScanParameters {
|
||||
scope?: Type<unknown>[];
|
||||
ctxRegistry?: (ForwardReference | DynamicModule | Type<unknown>)[];
|
||||
overrides?: ModuleOverride[];
|
||||
lazy?: boolean;
|
||||
}
|
||||
|
||||
export class DependenciesScanner {
|
||||
@@ -100,24 +99,23 @@ export class DependenciesScanner {
|
||||
|
||||
public async scanForModules({
|
||||
moduleDefinition,
|
||||
lazy,
|
||||
scope = [],
|
||||
ctxRegistry = [],
|
||||
overrides = [],
|
||||
}: ModulesScanParameters): Promise<Module[]> {
|
||||
const { moduleRef: moduleInstance, inserted: moduleInserted } =
|
||||
(await this.insertOrOverrideModule(moduleDefinition, overrides, scope)) ??
|
||||
{};
|
||||
const moduleInstance = await this.insertOrOverrideModule(
|
||||
moduleDefinition,
|
||||
overrides,
|
||||
scope,
|
||||
);
|
||||
|
||||
moduleDefinition =
|
||||
this.getOverrideModuleByModule(moduleDefinition, overrides)?.newModule ??
|
||||
moduleDefinition;
|
||||
|
||||
moduleDefinition =
|
||||
moduleDefinition instanceof Promise
|
||||
? await moduleDefinition
|
||||
: moduleDefinition;
|
||||
|
||||
ctxRegistry.push(moduleDefinition);
|
||||
|
||||
if (this.isForwardReference(moduleDefinition)) {
|
||||
@@ -155,30 +153,19 @@ export class DependenciesScanner {
|
||||
scope: [].concat(scope, moduleDefinition),
|
||||
ctxRegistry,
|
||||
overrides,
|
||||
lazy,
|
||||
});
|
||||
registeredModuleRefs = registeredModuleRefs.concat(moduleRefs);
|
||||
}
|
||||
if (!moduleInstance) {
|
||||
return registeredModuleRefs;
|
||||
}
|
||||
|
||||
if (lazy && moduleInserted) {
|
||||
this.container.bindGlobalsToImports(moduleInstance);
|
||||
}
|
||||
return [moduleInstance].concat(registeredModuleRefs);
|
||||
}
|
||||
|
||||
public async insertModule(
|
||||
moduleDefinition: any,
|
||||
scope: Type<unknown>[],
|
||||
): Promise<
|
||||
| {
|
||||
moduleRef: Module;
|
||||
inserted: boolean;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
): Promise<Module | undefined> {
|
||||
const moduleToAdd = this.isForwardReference(moduleDefinition)
|
||||
? moduleDefinition.forwardRef()
|
||||
: moduleDefinition;
|
||||
@@ -536,13 +523,7 @@ export class DependenciesScanner {
|
||||
moduleDefinition: ModuleDefinition,
|
||||
overrides: ModuleOverride[],
|
||||
scope: Type<unknown>[],
|
||||
): Promise<
|
||||
| {
|
||||
moduleRef: Module;
|
||||
inserted: boolean;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
): Promise<Module | undefined> {
|
||||
const overrideModule = this.getOverrideModuleByModule(
|
||||
moduleDefinition,
|
||||
overrides,
|
||||
@@ -582,13 +563,7 @@ export class DependenciesScanner {
|
||||
moduleToOverride: ModuleDefinition,
|
||||
newModule: ModuleDefinition,
|
||||
scope: Type<unknown>[],
|
||||
): Promise<
|
||||
| {
|
||||
moduleRef: Module;
|
||||
inserted: boolean;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
): Promise<Module | undefined> {
|
||||
return this.container.replaceModule(
|
||||
this.isForwardReference(moduleToOverride)
|
||||
? moduleToOverride.forwardRef()
|
||||
|
||||
@@ -1,30 +1,5 @@
|
||||
import { CustomDecorator, SetMetadata, Type } from '@nestjs/common';
|
||||
import { Type } from '@nestjs/common';
|
||||
import { isEmpty, isObject } from '@nestjs/common/utils/shared.utils';
|
||||
import { uid } from 'uid';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export interface CreateDecoratorOptions<T = any> {
|
||||
/**
|
||||
* The key for the metadata.
|
||||
* @default uid(21)
|
||||
*/
|
||||
key?: string;
|
||||
|
||||
/**
|
||||
* The transform function to apply to the metadata value.
|
||||
* @default value => value
|
||||
*/
|
||||
transform?: (value: T) => T;
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export type ReflectableDecorator<T> = ((opts?: T) => CustomDecorator) & {
|
||||
KEY: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class providing Nest reflection capabilities.
|
||||
@@ -34,43 +9,6 @@ export type ReflectableDecorator<T> = ((opts?: T) => CustomDecorator) & {
|
||||
* @publicApi
|
||||
*/
|
||||
export class Reflector {
|
||||
/**
|
||||
* Creates a decorator that can be used to decorate classes and methods with metadata.
|
||||
* Can be used as a strongly-typed alternative to `@SetMetadata`.
|
||||
* @param options Decorator options.
|
||||
* @returns A decorator function.
|
||||
*/
|
||||
static createDecorator<T>(
|
||||
options: CreateDecoratorOptions = {},
|
||||
): ReflectableDecorator<T> {
|
||||
const metadataKey = options.key ?? uid(21);
|
||||
const decoratorFn =
|
||||
(metadataValue: T) =>
|
||||
(target: object | Function, key?: string | symbol, descriptor?: any) => {
|
||||
const value = options.transform
|
||||
? options.transform(metadataValue)
|
||||
: metadataValue;
|
||||
SetMetadata(metadataKey, value ?? {})(target, key, descriptor);
|
||||
};
|
||||
|
||||
decoratorFn.KEY = metadataKey;
|
||||
return decoratorFn as ReflectableDecorator<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve metadata for a reflectable decorator for a specified target.
|
||||
*
|
||||
* @example
|
||||
* `const roles = this.reflector.get(Roles, context.getHandler());`
|
||||
*
|
||||
* @param decorator reflectable decorator created through `Reflector.createDecorator`
|
||||
* @param target context (decorated object) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public get<T extends ReflectableDecorator<any>>(
|
||||
decorator: T,
|
||||
target: Type<any> | Function,
|
||||
): T extends ReflectableDecorator<infer R> ? R : unknown;
|
||||
/**
|
||||
* Retrieve metadata for a specified key for a specified target.
|
||||
*
|
||||
@@ -84,43 +22,10 @@ export class Reflector {
|
||||
public get<TResult = any, TKey = any>(
|
||||
metadataKey: TKey,
|
||||
target: Type<any> | Function,
|
||||
): TResult;
|
||||
/**
|
||||
* Retrieve metadata for a specified key or decorator for a specified target.
|
||||
*
|
||||
* @example
|
||||
* `const roles = this.reflector.get<string[]>('roles', context.getHandler());`
|
||||
*
|
||||
* @param metadataKey lookup key or decorator for metadata to retrieve
|
||||
* @param target context (decorated object) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public get<TResult = any, TKey = any>(
|
||||
metadataKeyOrDecorator: TKey,
|
||||
target: Type<any> | Function,
|
||||
): TResult {
|
||||
const metadataKey =
|
||||
(metadataKeyOrDecorator as ReflectableDecorator<unknown>).KEY ??
|
||||
metadataKeyOrDecorator;
|
||||
|
||||
return Reflect.getMetadata(metadataKey, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve metadata for a specified decorator for a specified set of targets.
|
||||
*
|
||||
* @param decorator lookup decorator for metadata to retrieve
|
||||
* @param targets context (decorated objects) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public getAll<T extends ReflectableDecorator<any>>(
|
||||
decorator: T,
|
||||
targets: (Type<any> | Function)[],
|
||||
): T extends ReflectableDecorator<infer R>
|
||||
? R extends Array<any>
|
||||
? R
|
||||
: R[]
|
||||
: unknown;
|
||||
/**
|
||||
* Retrieve metadata for a specified key for a specified set of targets.
|
||||
*
|
||||
@@ -131,34 +36,12 @@ export class Reflector {
|
||||
public getAll<TResult extends any[] = any[], TKey = any>(
|
||||
metadataKey: TKey,
|
||||
targets: (Type<any> | Function)[],
|
||||
): TResult;
|
||||
/**
|
||||
* Retrieve metadata for a specified key or decorator for a specified set of targets.
|
||||
*
|
||||
* @param metadataKeyOrDecorator lookup key or decorator for metadata to retrieve
|
||||
* @param targets context (decorated objects) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public getAll<TResult extends any[] = any[], TKey = any>(
|
||||
metadataKeyOrDecorator: TKey,
|
||||
targets: (Type<any> | Function)[],
|
||||
): TResult {
|
||||
return (targets || []).map(target =>
|
||||
this.get(metadataKeyOrDecorator, target),
|
||||
this.get(metadataKey, target),
|
||||
) as TResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve metadata for a specified decorator for a specified set of targets and merge results.
|
||||
*
|
||||
* @param decorator lookup decorator for metadata to retrieve
|
||||
* @param targets context (decorated objects) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public getAllAndMerge<T extends ReflectableDecorator<any>>(
|
||||
decorator: T,
|
||||
targets: (Type<any> | Function)[],
|
||||
): T extends ReflectableDecorator<infer R> ? R : unknown;
|
||||
/**
|
||||
* Retrieve metadata for a specified key for a specified set of targets and merge results.
|
||||
*
|
||||
@@ -166,23 +49,12 @@ export class Reflector {
|
||||
* @param targets context (decorated objects) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public getAllAndMerge<TResult extends any[] | object = any[], TKey = any>(
|
||||
public getAllAndMerge<TResult extends any[] = any[], TKey = any>(
|
||||
metadataKey: TKey,
|
||||
targets: (Type<any> | Function)[],
|
||||
): TResult;
|
||||
/**
|
||||
* Retrieve metadata for a specified key or decorator for a specified set of targets and merge results.
|
||||
*
|
||||
* @param metadataKeyOrDecorator lookup key for metadata to retrieve
|
||||
* @param targets context (decorated objects) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public getAllAndMerge<TResult extends any[] | object = any[], TKey = any>(
|
||||
metadataKeyOrDecorator: TKey,
|
||||
targets: (Type<any> | Function)[],
|
||||
): TResult {
|
||||
const metadataCollection = this.getAll<any[], TKey>(
|
||||
metadataKeyOrDecorator,
|
||||
const metadataCollection = this.getAll<TResult, TKey>(
|
||||
metadataKey,
|
||||
targets,
|
||||
).filter(item => item !== undefined);
|
||||
|
||||
@@ -203,17 +75,6 @@ export class Reflector {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve metadata for a specified decorator for a specified set of targets and return a first not undefined value.
|
||||
*
|
||||
* @param decorator lookup decorator for metadata to retrieve
|
||||
* @param targets context (decorated objects) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public getAllAndOverride<T extends ReflectableDecorator<any>>(
|
||||
decorator: T,
|
||||
targets: (Type<any> | Function)[],
|
||||
): T extends ReflectableDecorator<infer R> ? R : unknown;
|
||||
/**
|
||||
* Retrieve metadata for a specified key for a specified set of targets and return a first not undefined value.
|
||||
*
|
||||
@@ -224,20 +85,9 @@ export class Reflector {
|
||||
public getAllAndOverride<TResult = any, TKey = any>(
|
||||
metadataKey: TKey,
|
||||
targets: (Type<any> | Function)[],
|
||||
): TResult;
|
||||
/**
|
||||
* Retrieve metadata for a specified key or decorator for a specified set of targets and return a first not undefined value.
|
||||
*
|
||||
* @param metadataKeyOrDecorator lookup key or metadata for metadata to retrieve
|
||||
* @param targets context (decorated objects) to retrieve metadata from
|
||||
*
|
||||
*/
|
||||
public getAllAndOverride<TResult = any, TKey = any>(
|
||||
metadataKeyOrDecorator: TKey,
|
||||
targets: (Type<any> | Function)[],
|
||||
): TResult {
|
||||
for (const target of targets) {
|
||||
const result = this.get(metadataKeyOrDecorator, target);
|
||||
const result = this.get(metadataKey, target);
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('Error Messages', () => {
|
||||
it('should display class', () => {
|
||||
const expectedResult =
|
||||
stringCleaner(`Nest can't resolve dependencies of the CatService (?, CatService). Please make sure that the argument dependency at index [0] is available in the current context.
|
||||
|
||||
|
||||
Potential solutions:
|
||||
- If dependency is a provider, is it part of the current Module?
|
||||
- If dependency is exported from a separate @Module, is that module imported within Module?
|
||||
@@ -39,7 +39,7 @@ describe('Error Messages', () => {
|
||||
it('should display the provide token', () => {
|
||||
const expectedResult =
|
||||
stringCleaner(`Nest can't resolve dependencies of the CatService (?, MY_TOKEN). Please make sure that the argument dependency at index [0] is available in the current context.
|
||||
|
||||
|
||||
Potential solutions:
|
||||
- If dependency is a provider, is it part of the current Module?
|
||||
- If dependency is exported from a separate @Module, is that module imported within Module?
|
||||
@@ -60,7 +60,7 @@ describe('Error Messages', () => {
|
||||
it('should display the function name', () => {
|
||||
const expectedResult =
|
||||
stringCleaner(`Nest can't resolve dependencies of the CatService (?, CatFunction). Please make sure that the argument dependency at index [0] is available in the current context.
|
||||
|
||||
|
||||
Potential solutions:
|
||||
- If dependency is a provider, is it part of the current Module?
|
||||
- If dependency is exported from a separate @Module, is that module imported within Module?
|
||||
@@ -81,7 +81,7 @@ describe('Error Messages', () => {
|
||||
it('should use "+" if unknown dependency name', () => {
|
||||
const expectedResult =
|
||||
stringCleaner(`Nest can't resolve dependencies of the CatService (?, +). Please make sure that the argument dependency at index [0] is available in the current context.
|
||||
|
||||
|
||||
Potential solutions:
|
||||
- If dependency is a provider, is it part of the current Module?
|
||||
- If dependency is exported from a separate @Module, is that module imported within Module?
|
||||
@@ -102,7 +102,7 @@ describe('Error Messages', () => {
|
||||
it('should display the module name', () => {
|
||||
const expectedResult =
|
||||
stringCleaner(`Nest can't resolve dependencies of the CatService (?, MY_TOKEN). Please make sure that the argument dependency at index [0] is available in the TestModule context.
|
||||
|
||||
|
||||
Potential solutions:
|
||||
- Is TestModule a valid NestJS module?
|
||||
- If dependency is a provider, is it part of the current TestModule?
|
||||
@@ -136,7 +136,7 @@ describe('Error Messages', () => {
|
||||
it('should display the symbol name of the provider', () => {
|
||||
const expectedResult =
|
||||
stringCleaner(`Nest can't resolve dependencies of the Symbol(CatProvider) (?). Please make sure that the argument dependency at index [0] is available in the current context.
|
||||
|
||||
|
||||
Potential solutions:
|
||||
- If dependency is a provider, is it part of the current Module?
|
||||
- If dependency is exported from a separate @Module, is that module imported within Module?
|
||||
@@ -157,7 +157,7 @@ describe('Error Messages', () => {
|
||||
it('should display the symbol dependency of the provider', () => {
|
||||
const expectedResult =
|
||||
stringCleaner(`Nest can't resolve dependencies of the CatProvider (?, Symbol(DogProvider)). Please make sure that the argument dependency at index [0] is available in the current context.
|
||||
|
||||
|
||||
Potential solutions:
|
||||
- If dependency is a provider, is it part of the current Module?
|
||||
- If dependency is exported from a separate @Module, is that module imported within Module?
|
||||
@@ -201,7 +201,7 @@ Scope [AppModule -> CatsModule]`);
|
||||
it('should display the module name with the invalid index and scope', () => {
|
||||
const expectedMessage =
|
||||
stringCleaner(`Nest cannot create the CatsModule instance.
|
||||
Received an unexpected value at index [0] of the CatsModule "imports" array.
|
||||
Received an unexpected value at index [0] of the CatsModule "imports" array.
|
||||
|
||||
Scope [AppModule -> CatsModule]`);
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ describe('NestContainer', () => {
|
||||
'bindGlobalModuleToModule',
|
||||
);
|
||||
container.bindGlobalsToImports({
|
||||
addImport: sinon.spy(),
|
||||
addRelatedModule: sinon.spy(),
|
||||
} as any);
|
||||
expect(bindGlobalModuleToModuleSpy.calledTwice).to.be.true;
|
||||
});
|
||||
@@ -152,17 +152,17 @@ describe('NestContainer', () => {
|
||||
|
||||
describe('bindGlobalModuleToModule', () => {
|
||||
describe('when "module" is not "globalModule"', () => {
|
||||
it('should call "addImport"', () => {
|
||||
const module = { addImport: sinon.spy() };
|
||||
it('should call "addRelatedModule"', () => {
|
||||
const module = { addRelatedModule: sinon.spy() };
|
||||
container.bindGlobalModuleToModule(module as any, null);
|
||||
expect(module.addImport.calledOnce).to.be.true;
|
||||
expect(module.addRelatedModule.calledOnce).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('when "module" is "globalModule"', () => {
|
||||
it('should not call "addImport"', () => {
|
||||
const module = { addImport: sinon.spy() };
|
||||
it('should not call "addRelatedModule"', () => {
|
||||
const module = { addRelatedModule: sinon.spy() };
|
||||
container.bindGlobalModuleToModule(module as any, module as any);
|
||||
expect(module.addImport.calledOnce).to.be.false;
|
||||
expect(module.addRelatedModule.calledOnce).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Optional } from '@nestjs/common';
|
||||
import { PARAMTYPES_METADATA } from '@nestjs/common/constants';
|
||||
import * as chai from 'chai';
|
||||
import { expect } from 'chai';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
@@ -11,6 +10,7 @@ import { NestContainer } from '../../injector/container';
|
||||
import { Injector, PropertyDependency } from '../../injector/injector';
|
||||
import { InstanceWrapper } from '../../injector/instance-wrapper';
|
||||
import { Module } from '../../injector/module';
|
||||
import { PARAMTYPES_METADATA } from '@nestjs/common/constants';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
@@ -710,17 +710,17 @@ describe('Injector', () => {
|
||||
const container = new NestContainer();
|
||||
const moduleCtor = class TestModule {};
|
||||
const ctx = STATIC_CONTEXT;
|
||||
const { moduleRef } = await container.addModule(moduleCtor, []);
|
||||
const module = await container.addModule(moduleCtor, []);
|
||||
|
||||
moduleRef.addProvider({
|
||||
module.addProvider({
|
||||
provide: TestClass,
|
||||
useClass: TestClass,
|
||||
});
|
||||
|
||||
const instance = await injector.loadPerContext(
|
||||
new TestClass(),
|
||||
moduleRef,
|
||||
moduleRef.providers,
|
||||
module,
|
||||
module.providers,
|
||||
ctx,
|
||||
);
|
||||
expect(instance).to.be.instanceOf(TestClass);
|
||||
|
||||
@@ -129,7 +129,7 @@ describe('GraphInspector', () => {
|
||||
class RandomPipe {}
|
||||
|
||||
it('should inspect all modules', async () => {
|
||||
const { moduleRef } = await container.addModule(TestModule, []);
|
||||
const moduleRef = await container.addModule(TestModule, []);
|
||||
moduleRef.addController(AController);
|
||||
|
||||
const subtype = 'interceptor';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
|
||||
import { AsyncLocalStorage } from 'async_hooks';
|
||||
import { expect } from 'chai';
|
||||
import { Observable, lastValueFrom, of, retry } from 'rxjs';
|
||||
import { Observable, lastValueFrom, of } from 'rxjs';
|
||||
import * as sinon from 'sinon';
|
||||
import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer';
|
||||
|
||||
@@ -85,8 +85,8 @@ describe('InterceptorsConsumer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when AsyncLocalStorage is used', () => {
|
||||
it('should allow an interceptor to set values in AsyncLocalStorage that are accesible from the controller', async () => {
|
||||
describe('AsyncLocalStorage', () => {
|
||||
it('Allows an interceptor to set values in AsyncLocalStorage that are accesible from the controller', async () => {
|
||||
const storage = new AsyncLocalStorage<Record<string, any>>();
|
||||
class StorageInterceptor implements NestInterceptor {
|
||||
intercept(
|
||||
@@ -96,9 +96,7 @@ describe('InterceptorsConsumer', () => {
|
||||
return storage.run({ value: 'hello' }, () => next.handle());
|
||||
}
|
||||
}
|
||||
const next = () => {
|
||||
return Promise.resolve(storage.getStore().value);
|
||||
};
|
||||
const next = () => Promise.resolve(storage.getStore().value);
|
||||
const intercepted = await consumer.intercept(
|
||||
[new StorageInterceptor()],
|
||||
null,
|
||||
@@ -110,35 +108,6 @@ describe('InterceptorsConsumer', () => {
|
||||
expect(result).to.equal('hello');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when retrying is enabled', () => {
|
||||
it('should retry a specified amount of times', async () => {
|
||||
let count = 0;
|
||||
const next = () => {
|
||||
count++;
|
||||
if (count < 3) {
|
||||
return Promise.reject(new Error('count not reached'));
|
||||
}
|
||||
return Promise.resolve(count);
|
||||
};
|
||||
class RetryInterceptor implements NestInterceptor {
|
||||
intercept(
|
||||
_context: ExecutionContext,
|
||||
next: CallHandler<any>,
|
||||
): Observable<any> | Promise<Observable<any>> {
|
||||
return next.handle().pipe(retry(4));
|
||||
}
|
||||
}
|
||||
const intercepted = await consumer.intercept(
|
||||
[new RetryInterceptor()],
|
||||
null,
|
||||
{ constructor: null },
|
||||
null,
|
||||
next,
|
||||
);
|
||||
expect(await transformToResult(intercepted)).to.equal(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('createContext', () => {
|
||||
it('should return execution context object', () => {
|
||||
@@ -174,7 +143,7 @@ describe('InterceptorsConsumer', () => {
|
||||
const val = 3;
|
||||
const next = async () => of(val);
|
||||
expect(
|
||||
await lastValueFrom(consumer.transformDeferred(next) as any),
|
||||
await await lastValueFrom(consumer.transformDeferred(next) as any),
|
||||
).to.be.eql(val);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Head,
|
||||
Options,
|
||||
Patch,
|
||||
Post,
|
||||
Put,
|
||||
RequestMethod,
|
||||
Version,
|
||||
VersioningType,
|
||||
} from '../../../common';
|
||||
import { MiddlewareConfigProxy } from '../../../common/interfaces';
|
||||
import { Controller, Get, RequestMethod, Version } from '../../../common';
|
||||
import { ApplicationConfig } from '../../application-config';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { MiddlewareBuilder } from '../../middleware/builder';
|
||||
@@ -26,26 +13,23 @@ describe('MiddlewareBuilder', () => {
|
||||
beforeEach(() => {
|
||||
const container = new NestContainer();
|
||||
const appConfig = new ApplicationConfig();
|
||||
appConfig.enableVersioning({ type: VersioningType.URI });
|
||||
builder = new MiddlewareBuilder(
|
||||
new RoutesMapper(container, appConfig),
|
||||
new RoutesMapper(container),
|
||||
new NoopHttpAdapter({}),
|
||||
new RouteInfoPathExtractor(appConfig),
|
||||
);
|
||||
});
|
||||
describe('apply', () => {
|
||||
let configProxy;
|
||||
beforeEach(() => {
|
||||
configProxy = builder.apply([]);
|
||||
});
|
||||
it('should return configuration proxy', () => {
|
||||
const configProxy = builder.apply([]);
|
||||
const metatype = (MiddlewareBuilder as any).ConfigProxy;
|
||||
expect(configProxy instanceof metatype).to.be.true;
|
||||
});
|
||||
|
||||
describe('configuration proxy', () => {
|
||||
describe('when "forRoutes()" called', () => {
|
||||
let configProxy: MiddlewareConfigProxy;
|
||||
beforeEach(() => {
|
||||
configProxy = builder.apply([]);
|
||||
});
|
||||
@Controller('path')
|
||||
class Test {
|
||||
@Get('route')
|
||||
@@ -56,7 +40,6 @@ describe('MiddlewareBuilder', () => {
|
||||
public getAllVersioned() {}
|
||||
}
|
||||
const route = { path: '/test', method: RequestMethod.GET };
|
||||
|
||||
it('should store configuration passed as argument', () => {
|
||||
configProxy.forRoutes(route, Test);
|
||||
|
||||
@@ -81,106 +64,6 @@ describe('MiddlewareBuilder', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@Controller('users')
|
||||
class UsersController {
|
||||
@Head('rsvp')
|
||||
hRsvp() {}
|
||||
|
||||
@Options('rsvp')
|
||||
oRsvp() {}
|
||||
|
||||
@Get('rsvp')
|
||||
gRsvp() {}
|
||||
|
||||
@Post('rsvp')
|
||||
pRsvp() {}
|
||||
|
||||
@Put('rsvp')
|
||||
puRsvp() {}
|
||||
|
||||
@Patch('rsvp')
|
||||
ptRsvp() {}
|
||||
|
||||
@Delete('rsvp')
|
||||
dRsvp() {}
|
||||
|
||||
@Post()
|
||||
create() {}
|
||||
|
||||
@Get()
|
||||
findAll() {}
|
||||
|
||||
@Get(':id')
|
||||
findOne() {}
|
||||
|
||||
@Patch(':id')
|
||||
update() {}
|
||||
|
||||
@Delete(':id')
|
||||
remove() {}
|
||||
}
|
||||
|
||||
it('should remove overlapping routes', () => {
|
||||
configProxy.forRoutes(UsersController);
|
||||
|
||||
expect(builder.build()).to.deep.equal([
|
||||
{
|
||||
middleware: [],
|
||||
forRoutes: [
|
||||
{
|
||||
method: RequestMethod.HEAD,
|
||||
path: '/users/rsvp',
|
||||
},
|
||||
{
|
||||
method: RequestMethod.OPTIONS,
|
||||
path: '/users/rsvp',
|
||||
},
|
||||
{
|
||||
method: RequestMethod.POST,
|
||||
path: '/users/rsvp',
|
||||
},
|
||||
{
|
||||
method: RequestMethod.PUT,
|
||||
path: '/users/rsvp',
|
||||
},
|
||||
{
|
||||
method: RequestMethod.POST,
|
||||
path: '/users/',
|
||||
},
|
||||
{
|
||||
method: RequestMethod.GET,
|
||||
path: '/users/',
|
||||
},
|
||||
{
|
||||
method: RequestMethod.GET,
|
||||
path: '/users/:id',
|
||||
},
|
||||
{
|
||||
method: RequestMethod.PATCH,
|
||||
path: '/users/:id',
|
||||
},
|
||||
{
|
||||
method: RequestMethod.DELETE,
|
||||
path: '/users/:id',
|
||||
},
|
||||
// Overlapping:
|
||||
// {
|
||||
// method: RequestMethod.GET,
|
||||
// path: '/users/rsvp',
|
||||
// },
|
||||
// {
|
||||
// method: RequestMethod.PATCH,
|
||||
// path: '/users/rsvp',
|
||||
// },
|
||||
// {
|
||||
// method: RequestMethod.DELETE,
|
||||
// path: '/users/rsvp',
|
||||
// },
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Version } from '../../../common';
|
||||
import { MiddlewareConfiguration } from '../../../common/interfaces';
|
||||
import { expect } from 'chai';
|
||||
import { Version, VersioningType } from '../../../common';
|
||||
import { Controller } from '../../../common/decorators/core/controller.decorator';
|
||||
import {
|
||||
Get,
|
||||
RequestMapping,
|
||||
} from '../../../common/decorators/http/request-mapping.decorator';
|
||||
import { RequestMethod } from '../../../common/enums/request-method.enum';
|
||||
import { MiddlewareConfiguration } from '../../../common/interfaces';
|
||||
import { ApplicationConfig } from '../../application-config';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { RoutesMapper } from '../../middleware/routes-mapper';
|
||||
|
||||
@@ -27,9 +26,7 @@ describe('RoutesMapper', () => {
|
||||
|
||||
let mapper: RoutesMapper;
|
||||
beforeEach(() => {
|
||||
const appConfig = new ApplicationConfig();
|
||||
appConfig.enableVersioning({ type: VersioningType.URI });
|
||||
mapper = new RoutesMapper(new NestContainer(), appConfig);
|
||||
mapper = new RoutesMapper(new NestContainer());
|
||||
});
|
||||
|
||||
it('should map @Controller() to "ControllerMetadata" in forRoutes', () => {
|
||||
@@ -84,47 +81,4 @@ describe('RoutesMapper', () => {
|
||||
{ path: '/test2/another', method: RequestMethod.DELETE },
|
||||
]);
|
||||
});
|
||||
|
||||
@Controller({
|
||||
version: '1',
|
||||
path: 'versioned',
|
||||
})
|
||||
class VersionedController {
|
||||
@Get()
|
||||
hello() {
|
||||
return 'Hello from "VersionedController"!';
|
||||
}
|
||||
|
||||
@Version('2')
|
||||
@Get('/override')
|
||||
override() {
|
||||
return 'Hello from "VersionedController"!';
|
||||
}
|
||||
}
|
||||
|
||||
@Controller({
|
||||
version: ['1', '2'],
|
||||
})
|
||||
class MultipleVersionController {
|
||||
@Get('multiple')
|
||||
multiple() {
|
||||
return 'Multiple Versions 1 or 2';
|
||||
}
|
||||
}
|
||||
|
||||
it('should map a versioned controller to the corresponding route info objects (single version)', () => {
|
||||
expect(mapper.mapRouteToRouteInfo(VersionedController)).to.deep.equal([
|
||||
{ path: '/versioned/', version: '1', method: RequestMethod.GET },
|
||||
{ path: '/versioned/override', version: '2', method: RequestMethod.GET },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should map a versioned controller to the corresponding route info objects (multiple versions)', () => {
|
||||
expect(mapper.mapRouteToRouteInfo(MultipleVersionController)).to.deep.equal(
|
||||
[
|
||||
{ path: '/multiple', version: '1', method: RequestMethod.GET },
|
||||
{ path: '/multiple', version: '2', method: RequestMethod.GET },
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user