Compare commits

..

2 Commits

Author SHA1 Message Date
Kamil Myśliwiec
e5612e0197 chore: merge 10.0.0 2023-06-12 10:58:30 +02:00
Kamil Myśliwiec
6c54448bf7 fix(fastify): validate middleware paths 2023-06-12 10:51:02 +02:00
266 changed files with 157572 additions and 121094 deletions

View File

@@ -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,89 +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
@@ -146,9 +105,9 @@ jobs:
- run:
name: Upgrade Node.js
command: |
nvm install << pipeline.parameters.maintenance-node-version >>
nvm install v16
node -v
nvm alias default << pipeline.parameters.maintenance-node-version >>
nvm alias default v16
- run:
name: Install Docker Compose
command: |
@@ -171,7 +130,7 @@ jobs:
codechecks_benchmarks:
working_directory: ~/nest
docker:
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
- image: cimg/node:16.19
steps:
- checkout
- *restore-cache
@@ -187,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
@@ -199,21 +158,16 @@ 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

View File

@@ -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',
},
},
{

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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',
},
]);
});
});

View File

@@ -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 {}

View File

@@ -1,6 +0,0 @@
import { DiscoveryService } from '@nestjs/core';
export const Webhook = DiscoveryService.createDecorator<{ name: string }>();
export const WebhookHandler = DiscoveryService.createDecorator<{
event: string;
}>();

View File

@@ -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');
}
}

View File

@@ -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');
}
}

View File

@@ -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 {}

View File

@@ -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,
};
}),
};
});
}
}

View File

@@ -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",
]
}

View File

@@ -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:

View File

@@ -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) {

View File

@@ -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.',
);
}
});
});

View File

@@ -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();
});
});

View File

@@ -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);
}
}

View File

@@ -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 {}

View File

@@ -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 {}

View File

@@ -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 {}

View File

@@ -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();

View File

@@ -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,
],
})

View File

@@ -10,6 +10,7 @@ class ConfigService implements MongooseOptionsFactory {
createMongooseOptions(): MongooseModuleOptions {
return {
uri: 'mongodb://localhost:27017/test',
useNewUrlParser: true,
};
}
}

View File

@@ -10,6 +10,7 @@ class ConfigService implements MongooseOptionsFactory {
createMongooseOptions(): MongooseModuleOptions {
return {
uri: 'mongodb://localhost:27017/test',
useNewUrlParser: true,
};
}
}

View File

@@ -6,6 +6,7 @@ import { CatsModule } from './cats/cats.module';
imports: [
MongooseModule.forRootAsync({
useFactory: () => ({
useNewUrlParser: true,
uri: 'mongodb://localhost:27017/test',
}),
}),

View File

@@ -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);
});
});

View File

@@ -1,6 +1,6 @@
{
"type": "mysql",
"host": "127.0.0.1",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "root",

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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();
});
});
});

View File

@@ -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 {}

View File

@@ -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"!';
}
}

View File

@@ -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';
}
}

View File

@@ -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';
}
}

View File

@@ -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());
});

View File

@@ -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();
});

View File

@@ -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 },
};
}
}

View File

@@ -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();
}
}

View File

@@ -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 },
};
}
}

View File

@@ -3,5 +3,5 @@
"packages": [
"packages/*"
],
"version": "10.2.2"
"version": "9.4.2"
}

38214
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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.2",
"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.1",
"@grpc/proto-loader": "0.7.9",
"@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.7",
"@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.1",
"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.1",
"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.5",
"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.8",
"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.2.2",
"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",

View File

@@ -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

View File

@@ -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) {

View File

@@ -31,10 +31,6 @@ export interface WsArgumentsHost {
* Returns the client object.
*/
getClient<T = any>(): T;
/**
* Returns the pattern for the event
*/
getPattern(): string;
}
/**

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/common",
"version": "10.2.2",
"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.2",
"tslib": "2.5.2",
"uid": "2.0.2"
},
"peerDependencies": {

View File

@@ -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)
);

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)',

View File

@@ -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)',

View File

@@ -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)',

View File

@@ -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${

View File

@@ -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;
}

View File

@@ -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.
*/

View File

@@ -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,
};
/**

View File

@@ -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),

View File

@@ -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', () => {

View File

@@ -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', () => {

View File

@@ -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 () => {

View File

@@ -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', () => {

View File

@@ -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 () => {

View File

@@ -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 () => {

View File

@@ -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', () => {

View File

@@ -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

View File

@@ -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,
);
}
}

View File

@@ -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;
}

View File

@@ -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()".`,
);
}
}

View File

@@ -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),
});
}
}

View File

@@ -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>;

View File

@@ -8,6 +8,5 @@ export class SilentLogger extends Logger {
warn = noop;
debug = noop;
verbose = noop;
fatal = noop;
setLogLevels = noop;
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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()),

View File

@@ -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
*/

View File

@@ -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;
}
});
}
};

View File

@@ -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;

View File

@@ -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
);
}
}
}

View File

@@ -177,10 +177,6 @@ export class NestApplication
}
public async init(): Promise<this> {
if (this.isInitialized) {
return this;
}
this.applyOptions();
await this.httpAdapter?.init();

View File

@@ -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,

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/core",
"version": "10.2.2",
"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.2",
"tslib": "2.5.2",
"uid": "2.0.2"
},
"devDependencies": {
"@nestjs/common": "10.2.2"
"@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"
},

View File

@@ -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,
);
}
}
}

View File

@@ -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()

View File

@@ -1,38 +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<TParam = any, TTransformed = TParam> {
/**
* The key for the metadata.
* @default uid(21)
*/
key?: string;
/**
* The transform function to apply to the metadata value.
* @default value => value
*/
transform?: (value: TParam) => TTransformed;
}
type CreateDecoratorWithTransformOptions<
TParam,
TTransformed = TParam,
> = CreateDecoratorOptions<TParam, TTransformed> &
Required<Pick<CreateDecoratorOptions<TParam, TTransformed>, 'transform'>>;
/**
* @publicApi
*/
export type ReflectableDecorator<TParam, TTransformed = TParam> = ((
opts?: TParam,
) => CustomDecorator) & {
KEY: string;
};
/**
* Helper class providing Nest reflection capabilities.
@@ -42,49 +9,6 @@ export type ReflectableDecorator<TParam, TTransformed = TParam> = ((
* @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<TParam>(
options?: CreateDecoratorOptions<TParam>,
): ReflectableDecorator<TParam>;
static createDecorator<TParam, TTransformed>(
options: CreateDecoratorWithTransformOptions<TParam, TTransformed>,
): ReflectableDecorator<TParam, TTransformed>;
static createDecorator<TParam, TTransformed = TParam>(
options: CreateDecoratorOptions<TParam, TTransformed> = {},
): ReflectableDecorator<TParam, TTransformed> {
const metadataKey = options.key ?? uid(21);
const decoratorFn =
(metadataValue: TParam) =>
(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<TParam, TTransformed>;
}
/**
* 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<any, infer R> ? R : unknown;
/**
* Retrieve metadata for a specified key for a specified target.
*
@@ -98,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.
*
@@ -145,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.
*
@@ -180,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);
@@ -217,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.
*
@@ -238,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;
}

View File

@@ -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]`);

View File

@@ -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;
});
});
});

View File

@@ -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);

View File

@@ -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';

View File

@@ -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);
});
});

View File

@@ -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',
// },
],
},
]);
});
});
});
});

View File

@@ -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