mirror of
https://github.com/nestjs/nest.git
synced 2026-02-24 00:02:56 +00:00
Compare commits
493 Commits
Dominic-Pr
...
v7.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5126ff4abc | ||
|
|
54570aa0c8 | ||
|
|
0b02176c2b | ||
|
|
9f14b14b9e | ||
|
|
9c60cc82e3 | ||
|
|
471335a336 | ||
|
|
6ebe9bf1e0 | ||
|
|
d8715eb7ff | ||
|
|
84bc3581cd | ||
|
|
439736f051 | ||
|
|
410fe85081 | ||
|
|
7ea7fd1f85 | ||
|
|
e3a2c74558 | ||
|
|
7a8696365c | ||
|
|
1d15dc650e | ||
|
|
ff75c1297c | ||
|
|
033ac9cab2 | ||
|
|
4f6d658467 | ||
|
|
5435fab03b | ||
|
|
c573fb3b89 | ||
|
|
ae08b12119 | ||
|
|
75e110f4c0 | ||
|
|
230b84f163 | ||
|
|
60b2615d25 | ||
|
|
b8ce4121ed | ||
|
|
486a40b852 | ||
|
|
6337793a84 | ||
|
|
e5f0f54946 | ||
|
|
eee8faf433 | ||
|
|
07cb5a83e1 | ||
|
|
fa10ab7f7c | ||
|
|
f315ca2b90 | ||
|
|
b397cec0fd | ||
|
|
0d6ae6fe88 | ||
|
|
34f74561de | ||
|
|
28b1784966 | ||
|
|
a683a545af | ||
|
|
d1816a3199 | ||
|
|
3e4f8bcbf1 | ||
|
|
a09e77a50a | ||
|
|
7970c29f16 | ||
|
|
09fea2b45c | ||
|
|
3f68b08bfc | ||
|
|
5ba0164f35 | ||
|
|
d768b5df32 | ||
|
|
5388ffb09b | ||
|
|
3fae1c2503 | ||
|
|
2aa0881b46 | ||
|
|
55032c15f4 | ||
|
|
de54172f6b | ||
|
|
2dcff086b3 | ||
|
|
edadbb1af9 | ||
|
|
d562bc0225 | ||
|
|
07237f5aed | ||
|
|
0432284865 | ||
|
|
e2c8f74f19 | ||
|
|
19b4083936 | ||
|
|
baadb3f925 | ||
|
|
f9ce824eaf | ||
|
|
43a4ea4d39 | ||
|
|
138e2c41f0 | ||
|
|
53d3574a43 | ||
|
|
f3a8f7f174 | ||
|
|
47cd27e0cc | ||
|
|
91d7564d92 | ||
|
|
b236e30231 | ||
|
|
dc1bd46c03 | ||
|
|
8c146fe341 | ||
|
|
c49c271c22 | ||
|
|
b59965ba76 | ||
|
|
b1ed6db4fb | ||
|
|
661664d8be | ||
|
|
64df4a96c9 | ||
|
|
7775081159 | ||
|
|
dcf3ca0d4c | ||
|
|
9ee7c3ffa3 | ||
|
|
1f8b45bf9b | ||
|
|
97061348f5 | ||
|
|
a8da4f74da | ||
|
|
1bc9939000 | ||
|
|
e4184145cb | ||
|
|
3e0fb387a0 | ||
|
|
630c3be032 | ||
|
|
1f880c7e8b | ||
|
|
47184d4ddb | ||
|
|
822bbba826 | ||
|
|
91f44a0978 | ||
|
|
53965ec396 | ||
|
|
e394f73a39 | ||
|
|
f76ea42d4d | ||
|
|
bfcd6f25d6 | ||
|
|
d08c7c742b | ||
|
|
0fa229529e | ||
|
|
47fcaaf855 | ||
|
|
a4ea40dacc | ||
|
|
48ab7359e4 | ||
|
|
cb65d90249 | ||
|
|
341746313a | ||
|
|
d1d583af07 | ||
|
|
60e77e92e2 | ||
|
|
306adf0b3f | ||
|
|
092a6af52c | ||
|
|
5462d1d152 | ||
|
|
9179b43f94 | ||
|
|
824b543a94 | ||
|
|
25d9e138c1 | ||
|
|
e32270c202 | ||
|
|
643d74df46 | ||
|
|
2bda4fca88 | ||
|
|
d5807f160b | ||
|
|
fe9afa34ee | ||
|
|
79c5014d82 | ||
|
|
3208409654 | ||
|
|
0845dab3a3 | ||
|
|
56a24622b6 | ||
|
|
62b0330a1c | ||
|
|
c0acada6a8 | ||
|
|
75830c9eaf | ||
|
|
bb71ceaf3d | ||
|
|
34a6704b12 | ||
|
|
cbda11b4fb | ||
|
|
b4c06f2242 | ||
|
|
1cb1c76f03 | ||
|
|
ff6365225d | ||
|
|
902e6cd322 | ||
|
|
ca065578f8 | ||
|
|
221c2c839f | ||
|
|
a0b84ffd1e | ||
|
|
e6233cbd5b | ||
|
|
a2e1cfcd62 | ||
|
|
8d2c79fa83 | ||
|
|
8a2c6063b7 | ||
|
|
a4f8419698 | ||
|
|
4044d60583 | ||
|
|
16ba70cba2 | ||
|
|
c31f2f6c5f | ||
|
|
b463a8e93b | ||
|
|
4e736b925c | ||
|
|
ec29c7e19c | ||
|
|
36c2ed1b9e | ||
|
|
8dac4d0f1c | ||
|
|
289e04dc86 | ||
|
|
90bfda4311 | ||
|
|
dd9086d612 | ||
|
|
c8e678682a | ||
|
|
7b03a6c361 | ||
|
|
6634898334 | ||
|
|
c7d890e604 | ||
|
|
1d938fd911 | ||
|
|
995e2870d5 | ||
|
|
6bb1d88d36 | ||
|
|
ea8fe39e43 | ||
|
|
3b189faa67 | ||
|
|
535dff89c0 | ||
|
|
7aed631783 | ||
|
|
06f68651ec | ||
|
|
447e1e3ed6 | ||
|
|
c8f5fc483d | ||
|
|
2379f9775e | ||
|
|
d4943751fa | ||
|
|
86e98dc74f | ||
|
|
6f658624fd | ||
|
|
aad7ec2862 | ||
|
|
1ee4bc56b0 | ||
|
|
bacdc8676b | ||
|
|
36ba59553c | ||
|
|
9e9c649e6b | ||
|
|
0bb5f91a67 | ||
|
|
011ae85143 | ||
|
|
b6433fc7ee | ||
|
|
12a92d98c4 | ||
|
|
142005573e | ||
|
|
9349189a1a | ||
|
|
8fff239f1b | ||
|
|
c7fee93706 | ||
|
|
c70bbb1923 | ||
|
|
1e98a8e790 | ||
|
|
2d33f52b63 | ||
|
|
1280dd6188 | ||
|
|
2b7725bef5 | ||
|
|
a39a6e9ca5 | ||
|
|
29ad228655 | ||
|
|
f9e39c1b20 | ||
|
|
b6374e8b0d | ||
|
|
a11da77144 | ||
|
|
10bd87cb7f | ||
|
|
b2d37b8de6 | ||
|
|
ab2c58d573 | ||
|
|
de577292e3 | ||
|
|
04cb13aa4d | ||
|
|
380a68d3be | ||
|
|
abdeee69b4 | ||
|
|
47e25265f6 | ||
|
|
e95d527ed9 | ||
|
|
694704c27b | ||
|
|
a92f1262e4 | ||
|
|
f2b382538b | ||
|
|
87bdf87d2f | ||
|
|
afc19bdd5d | ||
|
|
649033ddbc | ||
|
|
a215322ace | ||
|
|
0c2e73c78e | ||
|
|
74d7a2f4ca | ||
|
|
fb44f21e96 | ||
|
|
b271d89146 | ||
|
|
1f6deb0c11 | ||
|
|
392a13f758 | ||
|
|
e0a2eb7398 | ||
|
|
426a3b04f2 | ||
|
|
f0973d607f | ||
|
|
55a828a901 | ||
|
|
23181cb819 | ||
|
|
a5a8712668 | ||
|
|
77fc5da848 | ||
|
|
b2bd950cc9 | ||
|
|
e5db8e414e | ||
|
|
678986aa7e | ||
|
|
f4d8fad2d4 | ||
|
|
aaf4cb591e | ||
|
|
41f59daa58 | ||
|
|
8a253c5f76 | ||
|
|
1117b246c6 | ||
|
|
661424f389 | ||
|
|
d8ab987130 | ||
|
|
92908c8f0f | ||
|
|
5e820cc788 | ||
|
|
8edb035a87 | ||
|
|
4022347860 | ||
|
|
6ee10bef9d | ||
|
|
cf2c279be2 | ||
|
|
4c9d1de10e | ||
|
|
1a0b213d97 | ||
|
|
8a7fc27513 | ||
|
|
17edde1a22 | ||
|
|
387f918973 | ||
|
|
11794b99c0 | ||
|
|
4a14444088 | ||
|
|
44a23593aa | ||
|
|
e84d90ae86 | ||
|
|
e7ee336b8c | ||
|
|
7b241818a5 | ||
|
|
6d68283c4b | ||
|
|
f2726c19cb | ||
|
|
87c06eef42 | ||
|
|
810bb029be | ||
|
|
e5dc2c29a5 | ||
|
|
94a21eb683 | ||
|
|
ddb75ae335 | ||
|
|
90506e09a6 | ||
|
|
fc704b7cb9 | ||
|
|
307fbbf9df | ||
|
|
bf09428376 | ||
|
|
99de33c79e | ||
|
|
017791186d | ||
|
|
f5a3269561 | ||
|
|
002878e091 | ||
|
|
d439758319 | ||
|
|
95f7eadd8d | ||
|
|
1bade34fe1 | ||
|
|
c591059904 | ||
|
|
cf65f16ff3 | ||
|
|
3a4b17271b | ||
|
|
799ccc24d5 | ||
|
|
67bdae06e9 | ||
|
|
33b40ff44f | ||
|
|
fb3040f866 | ||
|
|
6c986e8c8d | ||
|
|
f54f864b94 | ||
|
|
e834ec7f5f | ||
|
|
12de529a48 | ||
|
|
c604ad18a9 | ||
|
|
3ce5a028c7 | ||
|
|
a2bf742111 | ||
|
|
b0dc39e589 | ||
|
|
653cc9a787 | ||
|
|
18547a3879 | ||
|
|
50f91e798b | ||
|
|
95269309f8 | ||
|
|
ac37717cc3 | ||
|
|
352e643c1a | ||
|
|
83c2ebfcba | ||
|
|
edbac299c8 | ||
|
|
9a694cb853 | ||
|
|
2376aa43a5 | ||
|
|
9ae9aa3059 | ||
|
|
014824121b | ||
|
|
09ab0087af | ||
|
|
647235f1ac | ||
|
|
b14b0f4376 | ||
|
|
7f5750660a | ||
|
|
ac87ed8448 | ||
|
|
2cd6cb9460 | ||
|
|
e4682d537d | ||
|
|
e4b8e5d08e | ||
|
|
88f7bd5583 | ||
|
|
8b8156981a | ||
|
|
2b15735a33 | ||
|
|
ec0190eeaa | ||
|
|
571eff638d | ||
|
|
f23eba1f08 | ||
|
|
765a3191ae | ||
|
|
915d346855 | ||
|
|
d6e4af4b30 | ||
|
|
f729b2edd1 | ||
|
|
55c5ec8f46 | ||
|
|
aa17f033ab | ||
|
|
cc6cfef4f0 | ||
|
|
9edc6b8fe7 | ||
|
|
016116a71c | ||
|
|
2c1407ad77 | ||
|
|
57435c2135 | ||
|
|
761d5d458a | ||
|
|
c0d3152492 | ||
|
|
c007ef4025 | ||
|
|
e4598e484d | ||
|
|
2e928ca46f | ||
|
|
0262e9fac1 | ||
|
|
7dfb6ce9d7 | ||
|
|
3e7fd83e61 | ||
|
|
f12ad1bc3a | ||
|
|
10c8dd40d5 | ||
|
|
995aba763c | ||
|
|
c4e87f51f9 | ||
|
|
e2674c6326 | ||
|
|
c34e6901ae | ||
|
|
d0dd846fcf | ||
|
|
b70a5ebff9 | ||
|
|
005c2c4360 | ||
|
|
edffcc5463 | ||
|
|
d9e4bf47db | ||
|
|
4fb7177f3b | ||
|
|
88aa0cef8b | ||
|
|
9b11c86175 | ||
|
|
8fde3283ac | ||
|
|
8832d15ba0 | ||
|
|
515dbdb2c4 | ||
|
|
3870e1d98e | ||
|
|
a835bf29f4 | ||
|
|
f3fc345cf2 | ||
|
|
2d99ddc0a1 | ||
|
|
eebbb26de3 | ||
|
|
4dc5c7514c | ||
|
|
364085771e | ||
|
|
9d829f5753 | ||
|
|
8484c0c922 | ||
|
|
2ab3629a8a | ||
|
|
4c4242f37d | ||
|
|
25a478a5a6 | ||
|
|
d4c6cfeed0 | ||
|
|
4bb193f64d | ||
|
|
dce8d9c5e8 | ||
|
|
7a7a71d911 | ||
|
|
956baed7cc | ||
|
|
5d484c53ae | ||
|
|
da4b1a3cce | ||
|
|
abb465d4dc | ||
|
|
789f8a4b30 | ||
|
|
549bb979de | ||
|
|
1ef5fb8572 | ||
|
|
1a0d47a22d | ||
|
|
fc0d73e971 | ||
|
|
eb4e40873d | ||
|
|
0609d6e647 | ||
|
|
cb944970ac | ||
|
|
417d312692 | ||
|
|
c48b2efbfa | ||
|
|
fad533ba4d | ||
|
|
430e9cf8b3 | ||
|
|
f77d1df06b | ||
|
|
814b3b58e6 | ||
|
|
a998251239 | ||
|
|
9f19643924 | ||
|
|
6cba88126a | ||
|
|
f400673401 | ||
|
|
fbcb1c44a4 | ||
|
|
e21a71568a | ||
|
|
4396820aab | ||
|
|
5a507d4293 | ||
|
|
82c9ce272c | ||
|
|
3b0e266084 | ||
|
|
65f6c5fe51 | ||
|
|
d68bb4a9db | ||
|
|
ada8808742 | ||
|
|
269b01d449 | ||
|
|
e06915f148 | ||
|
|
c0ecc34881 | ||
|
|
94f52fc741 | ||
|
|
c6039a326f | ||
|
|
7f58fc6d85 | ||
|
|
19d459aded | ||
|
|
f153d5f6bb | ||
|
|
89321242cc | ||
|
|
46318a7514 | ||
|
|
6757530709 | ||
|
|
640bf671ff | ||
|
|
4945764719 | ||
|
|
fed0f75565 | ||
|
|
5fcbd1a59b | ||
|
|
150a763c05 | ||
|
|
4870a55adb | ||
|
|
e35016ce7c | ||
|
|
52da4bb224 | ||
|
|
173dffe37c | ||
|
|
55c737e305 | ||
|
|
76ff1016da | ||
|
|
7c7125ac7f | ||
|
|
58eb602c09 | ||
|
|
e936dd0da0 | ||
|
|
3f456f09b1 | ||
|
|
09f87ac0d4 | ||
|
|
37aca3d3c6 | ||
|
|
7d5ff96957 | ||
|
|
4f91f18f3c | ||
|
|
3fe047ca55 | ||
|
|
6cf3f468c3 | ||
|
|
8dff04260c | ||
|
|
661370e3e8 | ||
|
|
e57f4a6b28 | ||
|
|
1d852b2ebf | ||
|
|
fc5d61b357 | ||
|
|
e58a1efe3d | ||
|
|
616cb19832 | ||
|
|
3510629672 | ||
|
|
0a84b2e59b | ||
|
|
def30207ae | ||
|
|
c377a19f15 | ||
|
|
4eb449dd3d | ||
|
|
41b7f58bb3 | ||
|
|
0d13d2ef32 | ||
|
|
f3d2b0c2f5 | ||
|
|
3bc1c93bee | ||
|
|
c62d9817c7 | ||
|
|
c19acf05ec | ||
|
|
72ed4b595e | ||
|
|
f74375baa8 | ||
|
|
7ac6624a3c | ||
|
|
26b266048e | ||
|
|
e5a4bb66f4 | ||
|
|
793b991621 | ||
|
|
1fc4833c06 | ||
|
|
f92ccae9d4 | ||
|
|
73094c3b43 | ||
|
|
51b18db412 | ||
|
|
f53cc505f1 | ||
|
|
5ed98e220c | ||
|
|
142a2e9113 | ||
|
|
a5bcb21388 | ||
|
|
0b38029830 | ||
|
|
0a94d65490 | ||
|
|
691383c0a2 | ||
|
|
8e6b6fa969 | ||
|
|
6135b623a0 | ||
|
|
e743162482 | ||
|
|
522ae200b5 | ||
|
|
296e1aecf7 | ||
|
|
7076aa46ee | ||
|
|
38a0610d38 | ||
|
|
e060888d37 | ||
|
|
6562f9faab | ||
|
|
abb4471d64 | ||
|
|
134ef15093 | ||
|
|
4ebe4504b9 | ||
|
|
95b904efee | ||
|
|
b039ac9f6d | ||
|
|
605e5baf58 | ||
|
|
263b38e302 | ||
|
|
9cba17d040 | ||
|
|
16b593765b | ||
|
|
15f3ee9ad6 | ||
|
|
e4a8cfbfa9 | ||
|
|
749ab49153 | ||
|
|
8fad078b5c | ||
|
|
9d87d17ae3 | ||
|
|
b417928177 | ||
|
|
75799a31dc | ||
|
|
fb194199ad | ||
|
|
e9dcd8fc84 | ||
|
|
93313f505a | ||
|
|
05b3dee522 | ||
|
|
4b3d476571 | ||
|
|
0168673d45 | ||
|
|
333677f009 | ||
|
|
70b7813915 | ||
|
|
41381dcd42 | ||
|
|
d6bc71ddd4 | ||
|
|
47fd693e15 | ||
|
|
6f1675f594 | ||
|
|
8d809c56ea | ||
|
|
e0dc092745 | ||
|
|
cf44f1ab2c | ||
|
|
08559a1bbc | ||
|
|
182792ef24 | ||
|
|
4b7dcd6b2c |
@@ -19,6 +19,8 @@ module.exports = {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
},
|
||||
};
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2017-2019 Kamil Myśliwiec <http://kamilmysliwiec.com>
|
||||
Copyright (c) 2017-2020 Kamil Mysliwiec <https://kamilmysliwiec.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
51
Readme.md
51
Readme.md
@@ -38,6 +38,14 @@ Nest is a framework for building efficient, scalable <a href="http://nodejs.org"
|
||||
* 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:
|
||||
|
||||
## Questions
|
||||
|
||||
For questions and support please use the official [Discord channel](https://discord.gg/G7Qnnhy). The issue list of this repo is **exclusively** for bug reports and feature requests.
|
||||
|
||||
## Issues
|
||||
|
||||
Please make sure to read the [Issue Reporting Checklist](https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md#-submitting-an-issue) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
|
||||
|
||||
## Consulting
|
||||
|
||||
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
@@ -48,30 +56,35 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="220" /></a>
|
||||
|
||||
#### Silver Sponsors
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" height="95" /></a>
|
||||
<a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="150" valign="middle" /></a>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></a>
|
||||
|
||||
<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://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="170" valign="middle" /></a> </td><td>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></td></tr></table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a> <a href="https://blueanchor.io/" target="_blank"><img src="https://nestjs.com/img/blueanchor.png" width="150" valign="middle" /></a>
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a> <a href="https://hostpresto.com" target="_blank"><img src="https://nestjs.com/img/hostpresto.png" height="24" valign="middle" /></a>
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" height="14" valign="middle" /></a>
|
||||
<a href="https://buddy.works/" target="_blank"><img src="https://nestjs.com/img/buddy-logo.svg" height="25" valign="middle" /></a>
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" height="25" valign="middle" /></a> <a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" height="27" valign="middle" /></a> <a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" height="20" valign="middle" /></a> <a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" height="22" valign="middle" /></a> <a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" height="19" valign="middle" /></a> <a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" height="20" valign="middle" /></a>
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" height="18" valign="middle" /></a>
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a>
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a>
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a>
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a>
|
||||
<a href="https://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" valign="middle" /></a>
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a>
|
||||
<a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a>
|
||||
|
||||
<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://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://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" 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></tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ services:
|
||||
- "9001:9001"
|
||||
restart: always
|
||||
mysql:
|
||||
image: mysql:5.7.29
|
||||
image: mysql:5.7.30
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: test
|
||||
|
||||
@@ -2,9 +2,22 @@
|
||||
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
|
||||
# ------------------------------------------------------
|
||||
|
||||
type Recipe {
|
||||
id: ID!
|
||||
title: String!
|
||||
description: String
|
||||
creationDate: Date!
|
||||
ingredients: [String!]!
|
||||
}
|
||||
|
||||
"""Date custom scalar type"""
|
||||
scalar Date
|
||||
|
||||
type Query {
|
||||
recipe(id: String!): Recipe!
|
||||
recipes(skip: Int = 0, take: Int = 25): [Recipe!]!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
addRecipe(newRecipeData: NewRecipeInput!): Recipe!
|
||||
removeRecipe(id: String!): Boolean!
|
||||
@@ -16,19 +29,6 @@ input NewRecipeInput {
|
||||
ingredients: [String!]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
recipe(id: String!): Recipe!
|
||||
recipes(skip: Int = 0, take: Int = 25): [Recipe!]!
|
||||
}
|
||||
|
||||
type Recipe {
|
||||
id: ID!
|
||||
title: String!
|
||||
description: String
|
||||
creationDate: Date!
|
||||
ingredients: [String!]!
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
recipeAdded: Recipe!
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ class TestController {
|
||||
}
|
||||
|
||||
@Get('tests/wildcard_nested')
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
wildcard_nested() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ class TestController {
|
||||
}
|
||||
|
||||
@Get('tests/wildcard_nested')
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
wildcard_nested() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,15 @@ import { InvalidClassScopeException } from '@nestjs/core/errors/exceptions/inval
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { ScopedController } from '../src/scoped/scoped.controller';
|
||||
import { ScopedModule } from '../src/scoped/scoped.module';
|
||||
import {
|
||||
REQUEST_SCOPED_FACTORY,
|
||||
ScopedModule,
|
||||
STATIC_FACTORY,
|
||||
TRANSIENT_SCOPED_FACTORY,
|
||||
} from '../src/scoped/scoped.module';
|
||||
import { ScopedService } from '../src/scoped/scoped.service';
|
||||
import { TransientService } from '../src/scoped/transient.service';
|
||||
import { Transient3Service } from '../src/scoped/transient3.service';
|
||||
|
||||
describe('Scoped Instances', () => {
|
||||
let testingModule: TestingModule;
|
||||
@@ -20,20 +26,41 @@ describe('Scoped Instances', () => {
|
||||
const contextId = createContextId();
|
||||
const transient1 = await testingModule.resolve(TransientService, contextId);
|
||||
const transient2 = await testingModule.resolve(TransientService, contextId);
|
||||
const transientFactory = await testingModule.resolve(
|
||||
TRANSIENT_SCOPED_FACTORY,
|
||||
);
|
||||
|
||||
expect(transient1).to.be.instanceOf(TransientService);
|
||||
expect(transient2).to.be.instanceOf(TransientService);
|
||||
expect(transient1).to.be.equal(transient2);
|
||||
expect(transientFactory).to.be.true;
|
||||
});
|
||||
|
||||
it('should dynamically resolve nested transient provider', async () => {
|
||||
const contextId = createContextId();
|
||||
const transientTwoDepthLevel = await testingModule.resolve(
|
||||
TransientService,
|
||||
contextId,
|
||||
);
|
||||
const transientThreeDepthLevel = await testingModule.resolve(
|
||||
Transient3Service,
|
||||
contextId,
|
||||
);
|
||||
|
||||
expect(transientTwoDepthLevel.svc.logger).to.not.be.undefined;
|
||||
expect(transientThreeDepthLevel.svc.svc.logger).to.not.be.undefined;
|
||||
});
|
||||
|
||||
it('should dynamically resolve request-scoped provider', async () => {
|
||||
const request1 = await testingModule.resolve(ScopedService);
|
||||
const request2 = await testingModule.resolve(ScopedService);
|
||||
const request3 = await testingModule.resolve(ScopedService, { id: 1 });
|
||||
const requestFactory = await testingModule.resolve(REQUEST_SCOPED_FACTORY);
|
||||
|
||||
expect(request1).to.be.instanceOf(ScopedService);
|
||||
expect(request2).to.be.instanceOf(ScopedService);
|
||||
expect(request3).to.not.be.equal(request2);
|
||||
expect(requestFactory).to.be.true;
|
||||
});
|
||||
|
||||
it('should dynamically resolve request-scoped controller', async () => {
|
||||
@@ -46,11 +73,19 @@ describe('Scoped Instances', () => {
|
||||
expect(request3).to.not.be.equal(request2);
|
||||
});
|
||||
|
||||
it('should throw an exception when "get()" method is used', async () => {
|
||||
it('should throw an exception when "get()" method is used for scoped providers', () => {
|
||||
try {
|
||||
testingModule.get(ScopedController);
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceOf(InvalidClassScopeException);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw an exception when "resolve()" method is used for static providers', async () => {
|
||||
try {
|
||||
await testingModule.resolve(STATIC_FACTORY);
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceOf(InvalidClassScopeException);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,35 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { Module, Scope } from '@nestjs/common';
|
||||
import { ScopedController } from './scoped.controller';
|
||||
import { ScopedService } from './scoped.service';
|
||||
import { TransientService } from './transient.service';
|
||||
import { Transient2Service } from './transient2.service';
|
||||
import { Transient3Service } from './transient3.service';
|
||||
|
||||
export const STATIC_FACTORY = 'STATIC_FACTORY';
|
||||
export const REQUEST_SCOPED_FACTORY = 'REQUEST_SCOPED_FACTORY';
|
||||
export const TRANSIENT_SCOPED_FACTORY = 'TRANSIENT_SCOPED_FACTORY';
|
||||
|
||||
@Module({
|
||||
controllers: [ScopedController],
|
||||
providers: [ScopedService, TransientService, Transient2Service],
|
||||
providers: [
|
||||
ScopedService,
|
||||
TransientService,
|
||||
Transient2Service,
|
||||
Transient3Service,
|
||||
{
|
||||
provide: STATIC_FACTORY,
|
||||
useFactory: () => true,
|
||||
},
|
||||
{
|
||||
provide: REQUEST_SCOPED_FACTORY,
|
||||
useFactory: () => true,
|
||||
scope: Scope.REQUEST,
|
||||
},
|
||||
{
|
||||
provide: TRANSIENT_SCOPED_FACTORY,
|
||||
useFactory: () => true,
|
||||
scope: Scope.TRANSIENT,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class ScopedModule {}
|
||||
|
||||
@@ -3,5 +3,5 @@ import { Transient2Service } from './transient2.service';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class TransientService {
|
||||
constructor(private readonly svc: Transient2Service) {}
|
||||
constructor(public readonly svc: Transient2Service) {}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { Injectable, Logger, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class Transient2Service {}
|
||||
export class Transient2Service {
|
||||
logger = new Logger();
|
||||
}
|
||||
|
||||
7
integration/injector/src/scoped/transient3.service.ts
Normal file
7
integration/injector/src/scoped/transient3.service.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { TransientService } from './transient.service';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class Transient3Service {
|
||||
constructor(public readonly svc: TransientService) {}
|
||||
}
|
||||
@@ -25,8 +25,11 @@ describe('GRPC transport', () => {
|
||||
app.connectMicroservice({
|
||||
transport: Transport.GRPC,
|
||||
options: {
|
||||
package: 'math',
|
||||
protoPath: join(__dirname, '../src/grpc/math.proto'),
|
||||
package: ['math', 'math2'],
|
||||
protoPath: [
|
||||
join(__dirname, '../src/grpc/math.proto'),
|
||||
join(__dirname, '../src/grpc/math2.proto'),
|
||||
],
|
||||
},
|
||||
});
|
||||
// Start gRPC microservice
|
||||
@@ -47,7 +50,19 @@ describe('GRPC transport', () => {
|
||||
|
||||
it(`GRPC Sending and Receiving HTTP POST`, () => {
|
||||
return request(server)
|
||||
.post('/')
|
||||
.post('/sum')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200, { result: 15 });
|
||||
});
|
||||
|
||||
it(`GRPC Sending and Receiving HTTP POST (multiple proto)`, async () => {
|
||||
await request(server)
|
||||
.post('/multi/sum')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200, { result: 15 });
|
||||
|
||||
await request(server)
|
||||
.post('/multi/sum2')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200, { result: 15 });
|
||||
});
|
||||
|
||||
@@ -31,9 +31,9 @@ export class GrpcController {
|
||||
],
|
||||
},
|
||||
})
|
||||
client2: ClientGrpc;
|
||||
clientMulti: ClientGrpc;
|
||||
|
||||
@Post()
|
||||
@Post('sum')
|
||||
@HttpCode(200)
|
||||
call(@Body() data: number[]): Observable<number> {
|
||||
const svc = this.client.getService<any>('Math');
|
||||
@@ -77,10 +77,17 @@ export class GrpcController {
|
||||
});
|
||||
}
|
||||
|
||||
@Post()
|
||||
@Post('multi/sum')
|
||||
@HttpCode(200)
|
||||
call2(@Body() data: number[]): Observable<number> {
|
||||
const svc = this.client2.getService<any>('Math2');
|
||||
callMultiSum(@Body() data: number[]): Observable<number> {
|
||||
const svc = this.clientMulti.getService<any>('Math');
|
||||
return svc.sum({ data });
|
||||
}
|
||||
|
||||
@Post('multi/sum2')
|
||||
@HttpCode(200)
|
||||
callMultiSum2(@Body() data: number[]): Observable<number> {
|
||||
const svc = this.clientMulti.getService<any>('Math2');
|
||||
return svc.sum2({ data });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "7.0.5"
|
||||
"version": "7.1.3"
|
||||
}
|
||||
|
||||
6563
package-lock.json
generated
6563
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
99
package.json
99
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.11",
|
||||
"description": "Modern, fast, powerful node.js web framework",
|
||||
"homepage": "https://nestjs.com",
|
||||
"repository": {
|
||||
@@ -27,7 +27,7 @@
|
||||
"test": "nyc --require ts-node/register mocha packages/**/*.spec.ts --reporter spec --retries 3 --require 'node_modules/reflect-metadata/Reflect.js' --exit",
|
||||
"test:integration": "mocha \"integration/*/{,!(node_modules)/**/}/*.spec.ts\" --reporter spec --require ts-node/register --require 'node_modules/reflect-metadata/Reflect.js' --exit",
|
||||
"test:docker:up": "docker-compose -f integration/docker-compose.yml up -d",
|
||||
"test:docker:down": "docker-compose -f integration/docker-compose.yml down -d",
|
||||
"test:docker:down": "docker-compose -f integration/docker-compose.yml down",
|
||||
"lint": "concurrently 'npm run lint:packages' 'npm run lint:integration' 'npm run lint:spec'",
|
||||
"lint:integration": "eslint 'integration/*/{,!(node_modules)/**/}/*.ts' -c '.eslintrc.spec.js' --fix",
|
||||
"lint:packages": "eslint 'packages/**/**.ts' --fix --ignore-pattern 'packages/**/*.spec.ts'",
|
||||
@@ -54,28 +54,28 @@
|
||||
"@nuxtjs/opencollective": "0.2.2",
|
||||
"axios": "0.19.2",
|
||||
"class-transformer": "0.2.3",
|
||||
"class-validator": "0.11.1",
|
||||
"class-validator": "0.12.2",
|
||||
"cli-color": "2.0.0",
|
||||
"cors": "2.8.5",
|
||||
"express": "4.17.1",
|
||||
"fast-json-stringify": "1.18.0",
|
||||
"fast-json-stringify": "2.0.0",
|
||||
"fast-safe-stringify": "2.0.7",
|
||||
"iterare": "1.2.0",
|
||||
"object-hash": "2.0.3",
|
||||
"path-to-regexp": "3.2.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.5.4",
|
||||
"rxjs": "6.5.5",
|
||||
"socket.io": "2.3.0",
|
||||
"uuid": "7.0.2",
|
||||
"tslib": "1.11.1"
|
||||
"uuid": "8.0.0",
|
||||
"tslib": "2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codechecks/client": "0.1.10",
|
||||
"@commitlint/cli": "8.3.5",
|
||||
"@commitlint/config-angular": "8.3.4",
|
||||
"@grpc/proto-loader": "0.5.3",
|
||||
"@nestjs/graphql": "7.0.15",
|
||||
"@nestjs/mongoose": "6.4.0",
|
||||
"@grpc/proto-loader": "0.5.4",
|
||||
"@nestjs/graphql": "7.3.7",
|
||||
"@nestjs/mongoose": "7.0.0",
|
||||
"@nestjs/typeorm": "7.0.0",
|
||||
"@types/amqplib": "0.5.13",
|
||||
"@types/bytes": "3.1.0",
|
||||
@@ -83,49 +83,48 @@
|
||||
"@types/chai": "4.2.10",
|
||||
"@types/chai-as-promised": "7.1.2",
|
||||
"@types/cors": "2.8.6",
|
||||
"@types/express": "4.17.3",
|
||||
"@types/express": "4.17.6",
|
||||
"@types/fastify-cors": "2.1.0",
|
||||
"@types/gulp": "4.0.6",
|
||||
"@types/kafka-node": "2.0.9",
|
||||
"@types/mocha": "7.0.2",
|
||||
"@types/mongoose": "5.7.7",
|
||||
"@types/mongoose": "5.7.16",
|
||||
"@types/node": "10.17.3",
|
||||
"@types/redis": "2.8.16",
|
||||
"@types/redis": "2.8.20",
|
||||
"@types/reflect-metadata": "0.0.5",
|
||||
"@types/sinon": "7.5.2",
|
||||
"@types/sinon": "9.0.0",
|
||||
"@types/socket.io": "2.1.4",
|
||||
"@types/ws": "7.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "2.25.0",
|
||||
"@typescript-eslint/parser": "2.25.0",
|
||||
"@types/ws": "7.2.4",
|
||||
"@typescript-eslint/eslint-plugin": "3.0.0",
|
||||
"@typescript-eslint/parser": "3.0.0",
|
||||
"amqp-connection-manager": "3.2.0",
|
||||
"amqplib": "0.5.5",
|
||||
"apollo-server-express": "2.11.0",
|
||||
"apollo-server-express": "2.13.1",
|
||||
"artillery": "1.6.0",
|
||||
"awesome-typescript-loader": "5.2.1",
|
||||
"body-parser": "1.19.0",
|
||||
"bytes": "3.1.0",
|
||||
"cache-manager": "3.2.1",
|
||||
"cache-manager": "3.3.0",
|
||||
"chai": "4.2.0",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"clang-format": "1.4.0",
|
||||
"commitlint-circle": "1.0.0",
|
||||
"concurrently": "5.1.0",
|
||||
"conventional-changelog": "3.1.18",
|
||||
"core-js": "3.6.4",
|
||||
"coveralls": "3.0.11",
|
||||
"concurrently": "5.2.0",
|
||||
"conventional-changelog": "3.1.21",
|
||||
"core-js": "3.6.5",
|
||||
"coveralls": "3.1.0",
|
||||
"delete-empty": "3.0.0",
|
||||
"engine.io-client": "3.4.0",
|
||||
"eslint": "6.8.0",
|
||||
"eslint-config-prettier": "6.10.1",
|
||||
"eslint-plugin-import": "2.20.1",
|
||||
"engine.io-client": "3.4.1",
|
||||
"eslint": "7.0.0",
|
||||
"eslint-config-prettier": "6.11.0",
|
||||
"eslint-plugin-import": "2.20.2",
|
||||
"fancy-log": "1.3.3",
|
||||
"fastify": "2.13.0",
|
||||
"fastify-cors": "3.0.2",
|
||||
"fastify-formbody": "3.1.0",
|
||||
"fastify-multipart": "1.0.5",
|
||||
"fastify-static": "2.6.0",
|
||||
"graphql": "14.6.0",
|
||||
"graphql-tools": "4.0.7",
|
||||
"fastify": "2.14.1",
|
||||
"fastify-cors": "3.0.3",
|
||||
"fastify-formbody": "3.2.0",
|
||||
"fastify-multipart": "1.0.6",
|
||||
"fastify-static": "2.7.0",
|
||||
"graphql": "15.0.0",
|
||||
"graphql-tools": "6.0.0",
|
||||
"grpc": "1.24.2",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-clang-format": "1.0.27",
|
||||
@@ -133,36 +132,36 @@
|
||||
"gulp-sourcemaps": "2.6.5",
|
||||
"gulp-typescript": "5.0.1",
|
||||
"gulp-watch": "5.0.1",
|
||||
"husky": "4.2.3",
|
||||
"husky": "4.2.5",
|
||||
"imports-loader": "0.8.0",
|
||||
"json-loader": "0.5.7",
|
||||
"kafkajs": "1.12.0",
|
||||
"lerna": "2.11.0",
|
||||
"lint-staged": "10.0.9",
|
||||
"lint-staged": "10.2.2",
|
||||
"markdown-table": "2.0.0",
|
||||
"merge-graphql-schemas": "1.7.6",
|
||||
"mocha": "7.1.1",
|
||||
"mongoose": "5.9.6",
|
||||
"mqtt": "3.0.0",
|
||||
"merge-graphql-schemas": "1.7.8",
|
||||
"mocha": "7.1.2",
|
||||
"mongoose": "5.9.13",
|
||||
"mqtt": "4.0.1",
|
||||
"multer": "1.4.2",
|
||||
"mysql": "2.18.1",
|
||||
"nats": "1.4.8",
|
||||
"nodemon": "2.0.2",
|
||||
"nyc": "15.0.0",
|
||||
"prettier": "2.0.2",
|
||||
"nats": "1.4.9",
|
||||
"nodemon": "2.0.3",
|
||||
"nyc": "15.0.1",
|
||||
"prettier": "2.0.5",
|
||||
"redis": "3.0.2",
|
||||
"rxjs-compat": "6.5.4",
|
||||
"sinon": "9.0.1",
|
||||
"rxjs-compat": "6.5.5",
|
||||
"sinon": "9.0.2",
|
||||
"sinon-chai": "3.5.0",
|
||||
"socket.io-client": "2.3.0",
|
||||
"subscriptions-transport-ws": "0.9.16",
|
||||
"supertest": "4.0.2",
|
||||
"ts-morph": "7.0.0",
|
||||
"ts-node": "8.8.1",
|
||||
"ts-morph": "7.0.3",
|
||||
"ts-node": "8.10.1",
|
||||
"typeorm": "0.2.24",
|
||||
"typescript": "3.7.2",
|
||||
"wrk": "1.2.0",
|
||||
"ws": "7.2.3"
|
||||
"ws": "7.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
|
||||
@@ -38,6 +38,14 @@ Nest is a framework for building efficient, scalable <a href="http://nodejs.org"
|
||||
* 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:
|
||||
|
||||
## Questions
|
||||
|
||||
For questions and support please use the official [Discord channel](https://discord.gg/G7Qnnhy). The issue list of this repo is **exclusively** for bug reports and feature requests.
|
||||
|
||||
## Issues
|
||||
|
||||
Please make sure to read the [Issue Reporting Checklist](https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md#-submitting-an-issue) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
|
||||
|
||||
## Consulting
|
||||
|
||||
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
@@ -48,29 +56,35 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="220" /></a>
|
||||
|
||||
#### Silver Sponsors
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" height="95" /></a>
|
||||
<a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="150" valign="middle" /></a>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></a>
|
||||
|
||||
<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://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="170" valign="middle" /></a> </td><td>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></td></tr></table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a> <a href="https://blueanchor.io/" target="_blank"><img src="https://nestjs.com/img/blueanchor.png" width="150" valign="middle" /></a>
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a> <a href="https://hostpresto.com" target="_blank"><img src="https://nestjs.com/img/hostpresto.png" height="24" valign="middle" /></a>
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" height="14" valign="middle" /></a>
|
||||
<a href="https://buddy.works/" target="_blank"><img src="https://nestjs.com/img/buddy-logo.svg" height="25" valign="middle" /></a>
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" height="25" valign="middle" /></a> <a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" height="27" valign="middle" /></a> <a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" height="20" valign="middle" /></a> <a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" height="22" valign="middle" /></a> <a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" height="19" valign="middle" /></a> <a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" height="20" valign="middle" /></a>
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" height="18" valign="middle" /></a>
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a>
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a>
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a>
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a>
|
||||
<a href="https://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" valign="middle" /></a>
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a>
|
||||
<a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a>
|
||||
<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://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://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" 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></tr></table>
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
HttpServer,
|
||||
NestInterceptor,
|
||||
} from '../../interfaces';
|
||||
import { isNil } from '../../utils/shared.utils';
|
||||
import {
|
||||
CACHE_KEY_METADATA,
|
||||
CACHE_MANAGER,
|
||||
@@ -50,7 +51,7 @@ export class CacheInterceptor implements NestInterceptor {
|
||||
|
||||
return next.handle().pipe(
|
||||
tap(response => {
|
||||
const args = ttl ? [key, response, { ttl }] : [key, response];
|
||||
const args = isNil(ttl) ? [key, response] : [key, response, { ttl }];
|
||||
this.cacheManager.set(...args);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -9,7 +9,7 @@ export type CustomDecorator<TKey = string> = MethodDecorator &
|
||||
*
|
||||
* Requires two parameters:
|
||||
* - `key` - a value defining the key under which the metadata is stored
|
||||
* - `value[]` - array of metadata values to be associated with `key`
|
||||
* - `value` - metadata to be associated with `key`
|
||||
*
|
||||
* This metadata can be reflected using the `Reflector` class.
|
||||
*
|
||||
|
||||
@@ -5,7 +5,7 @@ import { REDIRECT_METADATA } from '../../constants';
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function Redirect(url: string, statusCode?: number): MethodDecorator {
|
||||
export function Redirect(url = '', statusCode?: number): MethodDecorator {
|
||||
return (
|
||||
target: object,
|
||||
key: string | symbol,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import Axios, {
|
||||
AxiosInstance,
|
||||
AxiosPromise,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
AxiosPromise,
|
||||
CancelTokenSource,
|
||||
} from 'axios';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Inject } from '../decorators';
|
||||
@@ -72,13 +73,14 @@ export class HttpService {
|
||||
...args: any[]
|
||||
) {
|
||||
return new Observable<AxiosResponse<T>>(subscriber => {
|
||||
let config = args[args.length - 1];
|
||||
if (!config) {
|
||||
config = {};
|
||||
args[args.length - 1] = config;
|
||||
const config: AxiosRequestConfig = { ...(args[args.length - 1] || {}) };
|
||||
|
||||
let cancelSource: CancelTokenSource;
|
||||
if (!config.cancelToken) {
|
||||
cancelSource = Axios.CancelToken.source();
|
||||
config.cancelToken = cancelSource.token;
|
||||
}
|
||||
const cancelSource = Axios.CancelToken.source();
|
||||
config.cancelToken = cancelSource.token;
|
||||
|
||||
axios(...args)
|
||||
.then(res => {
|
||||
subscriber.next(res);
|
||||
@@ -88,7 +90,11 @@ export class HttpService {
|
||||
subscriber.error(err);
|
||||
});
|
||||
return () => {
|
||||
if (config.responseType !== 'stream') {
|
||||
if (config.responseType === 'stream') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelSource) {
|
||||
cancelSource.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,6 +35,7 @@ export {
|
||||
NestInterceptor,
|
||||
NestMiddleware,
|
||||
NestModule,
|
||||
NestHybridApplicationOptions,
|
||||
OnApplicationBootstrap,
|
||||
OnApplicationShutdown,
|
||||
OnModuleDestroy,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ExecutionContext } from './execution-context.interface';
|
||||
*/
|
||||
export interface CallHandler<T = any> {
|
||||
/**
|
||||
* Returns an `Observable` reprsenting the response stream from the route
|
||||
* Returns an `Observable` representing the response stream from the route
|
||||
* handler.
|
||||
*/
|
||||
handle(): Observable<T>;
|
||||
|
||||
@@ -28,3 +28,4 @@ export * from './nest-microservice.interface';
|
||||
export * from './scope-options.interface';
|
||||
export * from './type.interface';
|
||||
export * from './websockets/web-socket-adapter.interface';
|
||||
export * from './microservices/nest-hybrid-application-options.interface';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface NestHybridApplicationOptions {
|
||||
inheritAppConfig?: boolean;
|
||||
}
|
||||
@@ -2,7 +2,12 @@ import { CorsOptions } from './external/cors-options.interface';
|
||||
import { CanActivate } from './features/can-activate.interface';
|
||||
import { NestInterceptor } from './features/nest-interceptor.interface';
|
||||
import { HttpServer } from './http/http-server.interface';
|
||||
import { ExceptionFilter, INestMicroservice, PipeTransform } from './index';
|
||||
import {
|
||||
ExceptionFilter,
|
||||
INestMicroservice,
|
||||
NestHybridApplicationOptions,
|
||||
PipeTransform,
|
||||
} from './index';
|
||||
import { INestApplicationContext } from './nest-application-context.interface';
|
||||
import { WebSocketAdapter } from './websockets/web-socket-adapter.interface';
|
||||
|
||||
@@ -80,9 +85,13 @@ export interface INestApplication extends INestApplicationContext {
|
||||
* to a hybrid instance.
|
||||
*
|
||||
* @param {T} options Microservice options object
|
||||
* @param {NestHybridApplicationOptions} hybridOptions Hybrid options object
|
||||
* @returns {INestMicroservice}
|
||||
*/
|
||||
connectMicroservice<T extends object = any>(options: T): INestMicroservice;
|
||||
connectMicroservice<T extends object = any>(
|
||||
options: T,
|
||||
hybridOptions?: NestHybridApplicationOptions,
|
||||
): INestMicroservice;
|
||||
|
||||
/**
|
||||
* Returns array of the microservices connected to the NestApplication.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/common",
|
||||
"version": "7.0.5",
|
||||
"version": "7.1.3",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"homepage": "https://nestjs.com",
|
||||
@@ -19,8 +19,9 @@
|
||||
"dependencies": {
|
||||
"axios": "0.19.2",
|
||||
"cli-color": "2.0.0",
|
||||
"tslib": "1.11.1",
|
||||
"uuid": "7.0.2"
|
||||
"iterare": "1.2.0",
|
||||
"tslib": "2.0.0",
|
||||
"uuid": "8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"reflect-metadata": "^0.1.12",
|
||||
|
||||
22
packages/common/pipes/default-value.pipe.ts
Normal file
22
packages/common/pipes/default-value.pipe.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ArgumentMetadata, Injectable, PipeTransform } from '../index';
|
||||
import { isNil } from '../utils/shared.utils';
|
||||
|
||||
/**
|
||||
* Defines the built-in DefaultValue Pipe
|
||||
*
|
||||
* @see [Built-in Pipes](https://docs.nestjs.com/pipes#built-in-pipes)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
@Injectable()
|
||||
export class DefaultValuePipe<T = any, R = any>
|
||||
implements PipeTransform<T, T | R> {
|
||||
constructor(private readonly defaultValue: R) {}
|
||||
|
||||
transform(value?: T, _metadata?: ArgumentMetadata): T | R {
|
||||
if (isNil(value)) {
|
||||
return this.defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './default-value.pipe';
|
||||
export * from './parse-array.pipe';
|
||||
export * from './parse-bool.pipe';
|
||||
export * from './parse-int.pipe';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { Optional } from '../decorators';
|
||||
import { Injectable } from '../decorators/core';
|
||||
import { HttpStatus } from '../enums/http-status.enum';
|
||||
@@ -120,12 +120,7 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
if (this.isDetailedOutputDisabled) {
|
||||
return new HttpErrorByCode[this.errorHttpStatusCode]();
|
||||
}
|
||||
const errors = iterate(validationErrors)
|
||||
.filter(item => !!item.constraints)
|
||||
.map(item => Object.values(item.constraints))
|
||||
.flatten()
|
||||
.toArray();
|
||||
|
||||
const errors = this.flattenValidationErrors(validationErrors);
|
||||
return new HttpErrorByCode[this.errorHttpStatusCode](errors);
|
||||
};
|
||||
}
|
||||
@@ -164,7 +159,7 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
private stripProtoKeys(value: Record<string, any>) {
|
||||
delete value.__proto__;
|
||||
const keys = Object.keys(value);
|
||||
keys
|
||||
iterate(keys)
|
||||
.filter(key => typeof value[key] === 'object' && value[key])
|
||||
.forEach(key => this.stripProtoKeys(value[key]));
|
||||
}
|
||||
@@ -172,4 +167,46 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
private isPrimitive(value: unknown): boolean {
|
||||
return ['number', 'boolean', 'string'].includes(typeof value);
|
||||
}
|
||||
|
||||
private flattenValidationErrors(
|
||||
validationErrors: ValidationError[],
|
||||
): string[] {
|
||||
return iterate(validationErrors)
|
||||
.map(error => this.mapChildrenToValidationErrors(error))
|
||||
.flatten()
|
||||
.filter(item => !!item.constraints)
|
||||
.map(item => Object.values(item.constraints))
|
||||
.flatten()
|
||||
.toArray();
|
||||
}
|
||||
|
||||
private mapChildrenToValidationErrors(
|
||||
error: ValidationError,
|
||||
): ValidationError[] {
|
||||
if (!(error.children && error.children.length)) {
|
||||
return [error];
|
||||
}
|
||||
const validationErrors = [];
|
||||
for (const item of error.children) {
|
||||
if (item.children && item.children.length) {
|
||||
validationErrors.push(...this.mapChildrenToValidationErrors(item));
|
||||
}
|
||||
validationErrors.push(this.prependConstraintsWithParentProp(error, item));
|
||||
}
|
||||
return validationErrors;
|
||||
}
|
||||
|
||||
private prependConstraintsWithParentProp(
|
||||
parentError: ValidationError,
|
||||
error: ValidationError,
|
||||
): ValidationError {
|
||||
const constraints = {};
|
||||
for (const key in error.constraints) {
|
||||
constraints[key] = `${parentError.property}.${error.constraints[key]}`;
|
||||
}
|
||||
return {
|
||||
...error,
|
||||
constraints,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from 'chai';
|
||||
import { ROUTE_ARGS_METADATA } from '../../constants';
|
||||
import { Bind } from '../../decorators/core/bind.decorator';
|
||||
import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants';
|
||||
import { Req } from '../../decorators/http/route-params.decorator';
|
||||
|
||||
describe('@Bind', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ParseIntPipe } from '@nestjs/common';
|
||||
import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants';
|
||||
import { expect } from 'chai';
|
||||
import { ROUTE_ARGS_METADATA } from '../../constants';
|
||||
import { createParamDecorator } from '../../decorators/http/create-route-param-metadata.decorator';
|
||||
import { ParseIntPipe } from '../../index';
|
||||
|
||||
describe('createParamDecorator', () => {
|
||||
let result;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai';
|
||||
import { Redirect } from '../../decorators/http/redirect.decorator';
|
||||
import { REDIRECT_METADATA } from '../../constants';
|
||||
import { HttpStatus } from '@nestjs/common';
|
||||
import { Redirect } from '../../decorators/http/redirect.decorator';
|
||||
import { HttpStatus } from '../../index';
|
||||
|
||||
describe('@Redirect', () => {
|
||||
const url = 'http://test.com';
|
||||
|
||||
@@ -8,17 +8,17 @@ class Guard {}
|
||||
describe('@UseGuards', () => {
|
||||
const guards = [Guard, Guard];
|
||||
|
||||
@UseGuards(...(guards as any))
|
||||
@UseGuards(...guards)
|
||||
class Test {}
|
||||
|
||||
class TestWithMethod {
|
||||
@UseGuards(...(guards as any))
|
||||
@UseGuards(...guards)
|
||||
public static test() {}
|
||||
}
|
||||
|
||||
class Test2 {
|
||||
@UseGuards(...(guards as any))
|
||||
@UseGuards(...(guards as any))
|
||||
@UseGuards(...guards)
|
||||
@UseGuards(...guards)
|
||||
public static test() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ class Interceptor {}
|
||||
describe('@UseInterceptors', () => {
|
||||
const interceptors = [Interceptor, Interceptor];
|
||||
|
||||
@UseInterceptors(...(interceptors as any))
|
||||
@UseInterceptors(...interceptors)
|
||||
class Test {}
|
||||
|
||||
class TestWithMethod {
|
||||
@UseInterceptors(...(interceptors as any))
|
||||
@UseInterceptors(...interceptors)
|
||||
public static test() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ class Pipe {
|
||||
describe('@UsePipes', () => {
|
||||
const pipes = [new Pipe(), new Pipe()];
|
||||
|
||||
@UsePipes(...(pipes as any))
|
||||
@UsePipes(...pipes)
|
||||
class Test {}
|
||||
|
||||
class TestWithMethod {
|
||||
@UsePipes(...(pipes as any))
|
||||
@UsePipes(...pipes)
|
||||
public static test() {}
|
||||
}
|
||||
|
||||
|
||||
18
packages/common/test/http/http.service.spec.ts
Normal file
18
packages/common/test/http/http.service.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { expect } from 'chai';
|
||||
import { HttpService } from '../../http/http.service';
|
||||
import { AxiosRequestConfig, AxiosInstance } from 'axios';
|
||||
|
||||
describe('HttpService', () => {
|
||||
it('should not mutate user-given axios options object', done => {
|
||||
const http = new HttpService({ get: () => Promise.resolve() } as any);
|
||||
const options: AxiosRequestConfig = {};
|
||||
|
||||
http
|
||||
.get('/', options)
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
expect(options.cancelToken).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
20
packages/common/test/pipes/default-value.pipe.spec.ts
Normal file
20
packages/common/test/pipes/default-value.pipe.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { expect } from 'chai';
|
||||
import { DefaultValuePipe } from '../../pipes/default-value.pipe';
|
||||
|
||||
describe('DefaultValuePipe', () => {
|
||||
const defaultValue = 'default';
|
||||
const target = new DefaultValuePipe(defaultValue);
|
||||
|
||||
describe('transform', () => {
|
||||
it('should return original value if one was provided', () => {
|
||||
const value = 'value';
|
||||
const result = target.transform(value);
|
||||
expect(result).to.equal(value);
|
||||
});
|
||||
|
||||
it('should return default value if no value was provided', () => {
|
||||
const result = target.transform(undefined);
|
||||
expect(result).to.equal(defaultValue);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,14 @@
|
||||
import * as chai from 'chai';
|
||||
import { expect } from 'chai';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import { Exclude, Expose } from 'class-transformer';
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
import { Exclude, Expose, Type } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsDefined,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import { HttpStatus } from '../../enums';
|
||||
import { UnprocessableEntityException } from '../../exceptions';
|
||||
import { ArgumentMetadata } from '../../interfaces';
|
||||
@@ -27,10 +33,11 @@ class TestModelInternal {
|
||||
}
|
||||
|
||||
class TestModel {
|
||||
constructor() {}
|
||||
@IsString() public prop1: string;
|
||||
@IsString()
|
||||
public prop1: string;
|
||||
|
||||
@IsString() public prop2: string;
|
||||
@IsString()
|
||||
public prop2: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@@ -112,6 +119,43 @@ describe('ValidationPipe', () => {
|
||||
const testObj = { prop1: 'value1' };
|
||||
return expect(target.transform(testObj, metadata)).to.be.rejected;
|
||||
});
|
||||
|
||||
class TestModel2 {
|
||||
@IsString()
|
||||
public prop1: string;
|
||||
|
||||
@IsBoolean()
|
||||
public prop2: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
public optionalProp: string;
|
||||
}
|
||||
class TestModelWithNested {
|
||||
@IsString()
|
||||
prop: string;
|
||||
|
||||
@IsDefined()
|
||||
@Type(() => TestModel2)
|
||||
@ValidateNested()
|
||||
test: TestModel2;
|
||||
}
|
||||
it('should flatten nested errors', async () => {
|
||||
try {
|
||||
const model = new TestModelWithNested();
|
||||
model.test = new TestModel2();
|
||||
await target.transform(model, {
|
||||
type: 'body',
|
||||
metatype: TestModelWithNested,
|
||||
});
|
||||
} catch (err) {
|
||||
expect(err.getResponse().message).to.be.eql([
|
||||
'prop must be a string',
|
||||
'test.prop1 must be a string',
|
||||
'test.prop2 must be a boolean value',
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('when validation transforms', () => {
|
||||
it('should return a TestModel instance', async () => {
|
||||
|
||||
@@ -38,6 +38,14 @@ Nest is a framework for building efficient, scalable <a href="http://nodejs.org"
|
||||
* 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:
|
||||
|
||||
## Questions
|
||||
|
||||
For questions and support please use the official [Discord channel](https://discord.gg/G7Qnnhy). The issue list of this repo is **exclusively** for bug reports and feature requests.
|
||||
|
||||
## Issues
|
||||
|
||||
Please make sure to read the [Issue Reporting Checklist](https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md#-submitting-an-issue) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
|
||||
|
||||
## Consulting
|
||||
|
||||
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
@@ -48,29 +56,35 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="220" /></a>
|
||||
|
||||
#### Silver Sponsors
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" height="95" /></a>
|
||||
<a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="150" valign="middle" /></a>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></a>
|
||||
|
||||
<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://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="170" valign="middle" /></a> </td><td>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></td></tr></table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a> <a href="https://blueanchor.io/" target="_blank"><img src="https://nestjs.com/img/blueanchor.png" width="150" valign="middle" /></a>
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a> <a href="https://hostpresto.com" target="_blank"><img src="https://nestjs.com/img/hostpresto.png" height="24" valign="middle" /></a>
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" height="14" valign="middle" /></a>
|
||||
<a href="https://buddy.works/" target="_blank"><img src="https://nestjs.com/img/buddy-logo.svg" height="25" valign="middle" /></a>
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" height="25" valign="middle" /></a> <a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" height="27" valign="middle" /></a> <a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" height="20" valign="middle" /></a> <a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" height="22" valign="middle" /></a> <a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" height="19" valign="middle" /></a> <a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" height="20" valign="middle" /></a>
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" height="18" valign="middle" /></a>
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a>
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a>
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a>
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a>
|
||||
<a href="https://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" valign="middle" /></a>
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a>
|
||||
<a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a>
|
||||
<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://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://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" 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></tr></table>
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
@@ -2,8 +2,7 @@ import { INVALID_MODULE_MESSAGE } from '../messages';
|
||||
import { RuntimeException } from './runtime.exception';
|
||||
|
||||
export class InvalidModuleException extends RuntimeException {
|
||||
constructor(trace: any[]) {
|
||||
const scope = (trace || []).map(module => module.name).join(' -> ');
|
||||
super(INVALID_MODULE_MESSAGE`${scope}`);
|
||||
constructor(parentModule: any, index: number, scope: any[]) {
|
||||
super(INVALID_MODULE_MESSAGE(parentModule, index, scope));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { UNDEFINED_FORWARDREF_MESSAGE } from '../messages';
|
||||
import { RuntimeException } from './runtime.exception';
|
||||
import { Type } from '@nestjs/common';
|
||||
|
||||
export class UndefinedForwardRefException extends RuntimeException {
|
||||
constructor(scope: Type<any>[]) {
|
||||
super(UNDEFINED_FORWARDREF_MESSAGE(scope));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { RuntimeException } from './runtime.exception';
|
||||
import { UNDEFINED_MODULE_MESSAGE } from '../messages';
|
||||
|
||||
export class UndefinedModuleException extends RuntimeException {
|
||||
constructor(parentModule: any, index: number, scope: any[]) {
|
||||
super(UNDEFINED_MODULE_MESSAGE(parentModule, index, scope));
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { UNKNOWN_EXPORT_MESSAGE } from '../messages';
|
||||
import { RuntimeException } from './runtime.exception';
|
||||
|
||||
export class UnknownExportException extends RuntimeException {
|
||||
constructor(token: string, module: string) {
|
||||
super(UNKNOWN_EXPORT_MESSAGE(token, module));
|
||||
constructor(token: string | symbol, moduleName: string) {
|
||||
super(UNKNOWN_EXPORT_MESSAGE(token, moduleName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ const getDependencyName = (dependency: InjectorDependency): string =>
|
||||
const getModuleName = (module: Module) =>
|
||||
(module && getInstanceName(module.metatype)) || 'current';
|
||||
|
||||
const stringifyScope = (scope: any[]): string =>
|
||||
(scope || []).map(getInstanceName).join(' -> ');
|
||||
|
||||
export const UNKNOWN_DEPENDENCIES_MESSAGE = (
|
||||
type: string | symbol,
|
||||
unknownDependencyContext: InjectorDependencyContext,
|
||||
@@ -84,17 +87,50 @@ export const INVALID_MIDDLEWARE_MESSAGE = (
|
||||
name: string,
|
||||
) => `The middleware doesn't provide the 'use' method (${name})`;
|
||||
|
||||
export const INVALID_MODULE_MESSAGE = (
|
||||
text: TemplateStringsArray,
|
||||
scope: string,
|
||||
) =>
|
||||
`Nest cannot create the module instance. Often, this is because of a circular dependency between modules. Use forwardRef() to avoid it.
|
||||
export const UNDEFINED_FORWARDREF_MESSAGE = (
|
||||
scope: Type<any>[],
|
||||
) => `Nest cannot create the module instance. Often, this is because of a circular dependency between modules. Use forwardRef() to avoid it.
|
||||
|
||||
(Read more: https://docs.nestjs.com/fundamentals/circular-dependency)
|
||||
Scope [${scope}]
|
||||
`;
|
||||
Scope [${stringifyScope(scope)}]
|
||||
`;
|
||||
|
||||
export const INVALID_MODULE_MESSAGE = (
|
||||
parentModule: any,
|
||||
index: number,
|
||||
scope: any[],
|
||||
) => {
|
||||
const parentModuleName = parentModule?.name || 'module';
|
||||
|
||||
return `Nest cannot create the ${parentModuleName} instance.
|
||||
Received an unexpected value at index [${index}] of the ${parentModuleName} "imports" array.
|
||||
|
||||
Scope [${stringifyScope(scope)}]`;
|
||||
};
|
||||
|
||||
export const UNDEFINED_MODULE_MESSAGE = (
|
||||
parentModule: any,
|
||||
index: number,
|
||||
scope: any[],
|
||||
) => {
|
||||
const parentModuleName = parentModule?.name || 'module';
|
||||
|
||||
return `Nest cannot create the ${parentModuleName} instance.
|
||||
The module at index [${index}] of the ${parentModuleName} "imports" array is undefined.
|
||||
|
||||
Potential causes:
|
||||
- A circular dependency between modules. Use forwardRef() to avoid it. Read more: https://docs.nestjs.com/fundamentals/circular-dependency
|
||||
- The module at index [${index}] is of type "undefined". Check your import statements and the type of the module.
|
||||
|
||||
Scope [${stringifyScope(scope)}]`;
|
||||
};
|
||||
|
||||
export const UNKNOWN_EXPORT_MESSAGE = (
|
||||
token: string | symbol = 'item',
|
||||
module: string,
|
||||
) => {
|
||||
token = isSymbol(token) ? token.toString() : token;
|
||||
|
||||
export const UNKNOWN_EXPORT_MESSAGE = (token = 'item', module: string) => {
|
||||
return `Nest cannot export a provider/module that is not a part of the currently processed module (${module}). Please verify whether the exported ${token} is available in this particular context.
|
||||
|
||||
Possible Solutions:
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FILTER_CATCH_EXCEPTIONS } from '@nestjs/common/constants';
|
||||
import { Type } from '@nestjs/common/interfaces';
|
||||
import { ExceptionFilter } from '@nestjs/common/interfaces/exceptions/exception-filter.interface';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { ContextCreator } from '../helpers/context-creator';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HttpException } from '@nestjs/common';
|
||||
import { HttpException, Type } from '@nestjs/common';
|
||||
import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions/exception-filter-metadata.interface';
|
||||
import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface';
|
||||
import { isEmpty } from '@nestjs/common/utils/shared.utils';
|
||||
@@ -26,14 +26,15 @@ export class ExceptionsHandler extends BaseExceptionFilter {
|
||||
exception: T,
|
||||
ctx: ArgumentsHost,
|
||||
): boolean {
|
||||
if (isEmpty(this.filters)) return false;
|
||||
if (isEmpty(this.filters)) {
|
||||
return false;
|
||||
}
|
||||
const isInstanceOf = (metatype: Type<unknown>) =>
|
||||
exception instanceof metatype;
|
||||
|
||||
const filter = this.filters.find(({ exceptionMetatypes }) => {
|
||||
const typeExists =
|
||||
!exceptionMetatypes.length ||
|
||||
exceptionMetatypes.some(
|
||||
ExceptionMetatype => exception instanceof ExceptionMetatype,
|
||||
);
|
||||
!exceptionMetatypes.length || exceptionMetatypes.some(isInstanceOf);
|
||||
return typeExists;
|
||||
});
|
||||
filter && filter.func(exception, ctx);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { RouterProxyCallback } from '../router/router-proxy';
|
||||
import { BaseExceptionFilterContext } from './base-exception-filter-context';
|
||||
import { ExternalExceptionsHandler } from './external-exceptions-handler';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
export class ExternalExceptionFilterContext extends BaseExceptionFilterContext {
|
||||
constructor(
|
||||
@@ -54,10 +55,11 @@ export class ExternalExceptionFilterContext extends BaseExceptionFilterContext {
|
||||
return globalFilters;
|
||||
}
|
||||
const scopedFilterWrappers = this.config.getGlobalRequestFilters() as InstanceWrapper[];
|
||||
const scopedFilters = scopedFilterWrappers
|
||||
const scopedFilters = iterate(scopedFilterWrappers)
|
||||
.map(wrapper => wrapper.getInstanceByContextId(contextId, inquirerId))
|
||||
.filter(host => host)
|
||||
.map(host => host.instance);
|
||||
.filter(host => !!host)
|
||||
.map(host => host.instance)
|
||||
.toArray();
|
||||
|
||||
return globalFilters.concat(scopedFilters) as T;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions';
|
||||
import { ArgumentsHost } from '@nestjs/common/interfaces/features/arguments-host.interface';
|
||||
import { isEmpty } from '@nestjs/common/utils/shared.utils';
|
||||
@@ -29,11 +30,13 @@ export class ExternalExceptionsHandler extends ExternalExceptionFilter {
|
||||
if (isEmpty(this.filters)) {
|
||||
return null;
|
||||
}
|
||||
const isInstanceOf = metatype => exception instanceof metatype;
|
||||
const filter = this.filters.find(({ exceptionMetatypes, func }) => {
|
||||
const hasMetatype =
|
||||
const isInstanceOf = (metatype: Type<unknown>) =>
|
||||
exception instanceof metatype;
|
||||
|
||||
const filter = this.filters.find(({ exceptionMetatypes }) => {
|
||||
const typeExists =
|
||||
!exceptionMetatypes.length || exceptionMetatypes.some(isInstanceOf);
|
||||
return hasMetatype;
|
||||
return typeExists;
|
||||
});
|
||||
return filter ? filter.func(exception, host) : null;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CanActivate } from '@nestjs/common';
|
||||
import { GUARDS_METADATA } from '@nestjs/common/constants';
|
||||
import { Controller } from '@nestjs/common/interfaces';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { ContextCreator } from '../helpers/context-creator';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
@@ -100,10 +100,11 @@ export class GuardsContextCreator extends ContextCreator {
|
||||
return globalGuards;
|
||||
}
|
||||
const scopedGuardWrappers = this.config.getGlobalRequestGuards() as InstanceWrapper[];
|
||||
const scopedGuards = scopedGuardWrappers
|
||||
const scopedGuards = iterate(scopedGuardWrappers)
|
||||
.map(wrapper => wrapper.getInstanceByContextId(contextId, inquirerId))
|
||||
.filter(host => host)
|
||||
.map(host => host.instance);
|
||||
.filter(host => !!host)
|
||||
.map(host => host.instance)
|
||||
.toArray();
|
||||
|
||||
return globalGuards.concat(scopedGuards) as T;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BeforeApplicationShutdown } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OnApplicationBootstrap } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OnApplicationShutdown } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OnModuleDestroy } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OnModuleInit } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { CircularDependencyException } from '../errors/exceptions/circular-dependency.exception';
|
||||
import { InvalidModuleException } from '../errors/exceptions/invalid-module.exception';
|
||||
import { UndefinedForwardRefException } from '../errors/exceptions/undefined-forwardref.exception';
|
||||
import { UnknownModuleException } from '../errors/exceptions/unknown-module.exception';
|
||||
import { ExternalContextCreator } from '../helpers/external-context-creator';
|
||||
import { HttpAdapterHost } from '../helpers/http-adapter-host';
|
||||
@@ -55,8 +55,10 @@ export class NestContainer {
|
||||
metatype: Type<any> | DynamicModule | Promise<DynamicModule>,
|
||||
scope: Type<any>[],
|
||||
): Promise<Module> {
|
||||
// In DependenciesScanner#scanForModules we already check for undefined or invalid modules
|
||||
// We sill need to catch the edge-case of `forwardRef(() => undefined)`
|
||||
if (!metatype) {
|
||||
throw new InvalidModuleException(scope);
|
||||
throw new UndefinedForwardRefException(scope);
|
||||
}
|
||||
const { type, dynamicMetadata, token } = await this.moduleCompiler.compile(
|
||||
metatype,
|
||||
@@ -64,7 +66,7 @@ export class NestContainer {
|
||||
if (this.modules.has(token)) {
|
||||
return;
|
||||
}
|
||||
const moduleRef = new Module(type, scope, this);
|
||||
const moduleRef = new Module(type, this);
|
||||
this.modules.set(token, moduleRef);
|
||||
this.addDynamicMetadata(token, dynamicMetadata, [].concat(scope, type));
|
||||
|
||||
@@ -180,11 +182,11 @@ export class NestContainer {
|
||||
}
|
||||
|
||||
public replace(toReplace: any, options: any & { scope: any[] | null }) {
|
||||
this.modules.forEach(module => module.replace(toReplace, options));
|
||||
this.modules.forEach(moduleRef => moduleRef.replace(toReplace, options));
|
||||
}
|
||||
|
||||
public bindGlobalScope() {
|
||||
this.modules.forEach(module => this.bindGlobalsToImports(module));
|
||||
this.modules.forEach(moduleRef => this.bindGlobalsToImports(moduleRef));
|
||||
}
|
||||
|
||||
public bindGlobalsToImports(moduleRef: Module) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
isString,
|
||||
isUndefined,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { RuntimeException } from '../errors/exceptions/runtime.exception';
|
||||
import { UndefinedDependencyException } from '../errors/exceptions/undefined-dependency.exception';
|
||||
import { UnknownDependenciesException } from '../errors/exceptions/unknown-dependencies.exception';
|
||||
@@ -612,7 +613,7 @@ export class Injector {
|
||||
if (!isObject(instance)) {
|
||||
return undefined;
|
||||
}
|
||||
properties
|
||||
iterate(properties)
|
||||
.filter(item => !isNil(item.instance))
|
||||
.forEach(item => (instance[item.key] = item.instance));
|
||||
}
|
||||
@@ -665,8 +666,11 @@ export class Injector {
|
||||
wrapper?: InstanceWrapper,
|
||||
): Promise<T> {
|
||||
if (!wrapper) {
|
||||
const ctor = instance.constructor;
|
||||
wrapper = collection.get(ctor && ctor.name);
|
||||
const providerCtor = instance.constructor;
|
||||
const injectionToken =
|
||||
(providerCtor && providerCtor.name) ||
|
||||
((providerCtor as unknown) as string);
|
||||
wrapper = collection.get(injectionToken);
|
||||
}
|
||||
await this.loadInstance(wrapper, collection, moduleRef, ctx, wrapper);
|
||||
await this.loadEnhancersPerContext(wrapper, ctx, wrapper);
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from '@nestjs/common/interfaces';
|
||||
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
|
||||
import { isNil, isUndefined } from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { STATIC_CONTEXT } from './constants';
|
||||
import { Module } from './module';
|
||||
|
||||
@@ -37,9 +38,9 @@ export class InstanceWrapper<T = any> {
|
||||
public readonly name: any;
|
||||
public readonly async?: boolean;
|
||||
public readonly host?: Module;
|
||||
public readonly scope?: Scope = Scope.DEFAULT;
|
||||
public readonly isAlias: boolean = false;
|
||||
|
||||
public scope?: Scope = Scope.DEFAULT;
|
||||
public metatype: Type<T> | Function;
|
||||
public inject?: (string | symbol | Function | Type<any>)[];
|
||||
public forwardRef?: boolean;
|
||||
@@ -255,9 +256,11 @@ export class InstanceWrapper<T = any> {
|
||||
): boolean {
|
||||
const isDependencyTreeStatic = this.isDependencyTreeStatic();
|
||||
|
||||
return ((!isDependencyTreeStatic &&
|
||||
return (
|
||||
!isDependencyTreeStatic &&
|
||||
contextId !== STATIC_CONTEXT &&
|
||||
(!this.isTransient || (this.isTransient && inquirer))) as any) as boolean;
|
||||
(!this.isTransient || (this.isTransient && !!inquirer))
|
||||
);
|
||||
}
|
||||
|
||||
public isLazyTransient(
|
||||
@@ -279,10 +282,11 @@ export class InstanceWrapper<T = any> {
|
||||
contextId: ContextId,
|
||||
inquirer?: InstanceWrapper,
|
||||
): boolean {
|
||||
const isSelfRequested = inquirer === this;
|
||||
return (
|
||||
this.isDependencyTreeStatic() &&
|
||||
contextId !== STATIC_CONTEXT &&
|
||||
inquirer === this
|
||||
(isSelfRequested || (inquirer && inquirer.scope === Scope.TRANSIENT))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -297,7 +301,8 @@ export class InstanceWrapper<T = any> {
|
||||
return (
|
||||
this.isDependencyTreeStatic() &&
|
||||
contextId === STATIC_CONTEXT &&
|
||||
(!this.isTransient || (isStaticTransient && !!inquirer))
|
||||
(!this.isTransient ||
|
||||
(isStaticTransient && !!inquirer && !inquirer.isTransient))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -306,15 +311,17 @@ export class InstanceWrapper<T = any> {
|
||||
return [];
|
||||
}
|
||||
const instances = [...this.transientMap.values()];
|
||||
return instances
|
||||
return iterate(instances)
|
||||
.map(item => item.get(STATIC_CONTEXT))
|
||||
.filter(item => !!item);
|
||||
.filter(item => !!item)
|
||||
.toArray();
|
||||
}
|
||||
|
||||
public mergeWith(provider: Provider) {
|
||||
if ((provider as ValueProvider).useValue) {
|
||||
this.metatype = null;
|
||||
this.inject = null;
|
||||
this.scope = Scope.DEFAULT;
|
||||
|
||||
this.setInstanceByContextId(STATIC_CONTEXT, {
|
||||
instance: (provider as ValueProvider).useValue,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { InvalidClassScopeException } from '../errors/exceptions/invalid-class-scope.exception';
|
||||
import { UnknownElementException } from '../errors/exceptions/unknown-element.exception';
|
||||
import { getClassScope } from '../helpers/get-class-scope';
|
||||
import { NestContainer } from './container';
|
||||
import { ContainerScanner } from './container-scanner';
|
||||
import { Injector } from './injector';
|
||||
@@ -38,8 +40,8 @@ export abstract class ModuleRef {
|
||||
const wrapper = new InstanceWrapper({
|
||||
name: type && type.name,
|
||||
metatype: type,
|
||||
instance: undefined,
|
||||
isResolved: false,
|
||||
scope: getClassScope(type),
|
||||
host: moduleRef,
|
||||
});
|
||||
return new Promise<T>(async (resolve, reject) => {
|
||||
@@ -82,7 +84,9 @@ export abstract class ModuleRef {
|
||||
options?: { strict: boolean },
|
||||
): Promise<TResult> {
|
||||
let wrapper: InstanceWrapper, collection: Map<string, InstanceWrapper>;
|
||||
if (!(options && options.strict)) {
|
||||
|
||||
const isStrictModeEnabled = options && options.strict;
|
||||
if (!isStrictModeEnabled) {
|
||||
[wrapper, collection] = this.containerScanner.getWrapperCollectionPair(
|
||||
typeOrToken,
|
||||
);
|
||||
@@ -95,8 +99,13 @@ export abstract class ModuleRef {
|
||||
contextModule,
|
||||
);
|
||||
}
|
||||
if (wrapper.isDependencyTreeStatic() && !wrapper.isTransient) {
|
||||
throw new InvalidClassScopeException(typeOrToken);
|
||||
}
|
||||
|
||||
const ctorHost = wrapper.instance || { constructor: typeOrToken };
|
||||
const instance = await this.injector.loadPerContext(
|
||||
wrapper.instance,
|
||||
ctorHost,
|
||||
wrapper.host,
|
||||
collection,
|
||||
contextId,
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
isSymbol,
|
||||
isUndefined,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { InvalidClassException } from '../errors/exceptions/invalid-class.exception';
|
||||
import { RuntimeException } from '../errors/exceptions/runtime.exception';
|
||||
@@ -49,7 +50,6 @@ export class Module {
|
||||
|
||||
constructor(
|
||||
private readonly _metatype: Type<any>,
|
||||
private readonly _scope: Type<any>[],
|
||||
private readonly container: NestContainer,
|
||||
) {
|
||||
this.addCoreProviders();
|
||||
@@ -60,10 +60,6 @@ export class Module {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get scope(): Type<any>[] {
|
||||
return this._scope;
|
||||
}
|
||||
|
||||
get providers(): Map<any, InstanceWrapper<Injectable>> {
|
||||
return this._providers;
|
||||
}
|
||||
@@ -397,15 +393,16 @@ export class Module {
|
||||
return token;
|
||||
}
|
||||
const importsArray = [...this._imports.values()];
|
||||
const importsNames = importsArray
|
||||
.filter(item => item)
|
||||
const importsNames = iterate(importsArray)
|
||||
.filter(item => !!item)
|
||||
.map(({ metatype }) => metatype)
|
||||
.filter(metatype => metatype)
|
||||
.map(({ name }) => name);
|
||||
.filter(metatype => !!metatype)
|
||||
.map(({ name }) => name)
|
||||
.toArray();
|
||||
|
||||
if (!importsNames.includes(token as any)) {
|
||||
if (!importsNames.includes(token as string)) {
|
||||
const { name } = this.metatype;
|
||||
throw new UnknownExportException(token as any, name);
|
||||
throw new UnknownExportException(token, name);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INTERCEPTORS_METADATA } from '@nestjs/common/constants';
|
||||
import { Controller, NestInterceptor } from '@nestjs/common/interfaces';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { ContextCreator } from '../helpers/context-creator';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
@@ -104,10 +104,11 @@ export class InterceptorsContextCreator extends ContextCreator {
|
||||
return globalInterceptors;
|
||||
}
|
||||
const scopedInterceptorWrappers = this.config.getGlobalRequestInterceptors() as InstanceWrapper[];
|
||||
const scopedInterceptors = scopedInterceptorWrappers
|
||||
const scopedInterceptors = iterate(scopedInterceptorWrappers)
|
||||
.map(wrapper => wrapper.getInstanceByContextId(contextId, inquirerId))
|
||||
.filter(host => host)
|
||||
.map(host => host.instance);
|
||||
.filter(host => !!host)
|
||||
.map(host => host.instance)
|
||||
.toArray();
|
||||
|
||||
return globalInterceptors.concat(scopedInterceptors) as T;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
isFunction,
|
||||
isNil,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
export class MetadataScanner {
|
||||
public scanFromPrototype<T extends Injectable, R = any>(
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
|
||||
import { RoutesMapper } from './routes-mapper';
|
||||
import { filterMiddleware } from './utils';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
export class MiddlewareBuilder implements MiddlewareConsumer {
|
||||
private readonly middlewareCollection = new Set<MiddlewareConfiguration>();
|
||||
@@ -49,21 +50,16 @@ export class MiddlewareBuilder implements MiddlewareConsumer {
|
||||
public exclude(
|
||||
...routes: Array<string | RouteInfo>
|
||||
): MiddlewareConfigProxy {
|
||||
const { routesMapper } = this.builder;
|
||||
this.excludedRoutes = this.mapRoutesToFlatList(
|
||||
routes.map(route => routesMapper.mapRouteToRouteInfo(route)),
|
||||
);
|
||||
this.excludedRoutes = this.getRoutesFlatList(routes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public forRoutes(
|
||||
...routes: Array<string | Type<any> | RouteInfo>
|
||||
): MiddlewareConsumer {
|
||||
const { middlewareCollection, routesMapper } = this.builder;
|
||||
const { middlewareCollection } = this.builder;
|
||||
|
||||
const forRoutes = this.mapRoutesToFlatList(
|
||||
routes.map(route => routesMapper.mapRouteToRouteInfo(route)),
|
||||
);
|
||||
const forRoutes = this.getRoutesFlatList(routes);
|
||||
const configuration = {
|
||||
middleware: filterMiddleware(
|
||||
this.middleware,
|
||||
@@ -76,8 +72,15 @@ export class MiddlewareBuilder implements MiddlewareConsumer {
|
||||
return this.builder;
|
||||
}
|
||||
|
||||
private mapRoutesToFlatList(forRoutes: RouteInfo[][]): RouteInfo[] {
|
||||
return forRoutes.reduce((a, b) => a.concat(b));
|
||||
private getRoutesFlatList(
|
||||
routes: Array<string | Type<any> | RouteInfo>,
|
||||
): RouteInfo[] {
|
||||
const { routesMapper } = this.builder;
|
||||
|
||||
return iterate(routes)
|
||||
.map(route => routesMapper.mapRouteToRouteInfo(route))
|
||||
.flatten()
|
||||
.toArray();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { HttpServer, RouteInfo, Type } from '@nestjs/common/interfaces';
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import * as pathToRegexp from 'path-to-regexp';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
type RouteInfoRegex = RouteInfo & { regex: RegExp };
|
||||
|
||||
@@ -16,10 +17,11 @@ export const filterMiddleware = <T extends Function | Type<any> = any>(
|
||||
...route,
|
||||
regex: pathToRegexp(route.path),
|
||||
}));
|
||||
return []
|
||||
return iterate([])
|
||||
.concat(middleware)
|
||||
.filter(isFunction)
|
||||
.map((item: T) => mapToClass(item, excluded, httpAdapter));
|
||||
.map((item: T) => mapToClass(item, excluded, httpAdapter))
|
||||
.toArray();
|
||||
};
|
||||
|
||||
export const mapToClass = <T extends Function | Type<any>>(
|
||||
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
import { Abstract } from '@nestjs/common/interfaces';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
import { isEmpty } from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { MESSAGES } from './constants';
|
||||
import { InvalidClassScopeException } from './errors/exceptions/invalid-class-scope.exception';
|
||||
import { UnknownElementException } from './errors/exceptions/unknown-element.exception';
|
||||
import { UnknownModuleException } from './errors/exceptions/unknown-module.exception';
|
||||
import { createContextId } from './helpers';
|
||||
@@ -137,10 +139,11 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
signals = Array.from(new Set(signals));
|
||||
}
|
||||
|
||||
signals = signals
|
||||
signals = iterate(signals)
|
||||
.map((signal: string) => signal.toString().toUpperCase().trim())
|
||||
// filter out the signals which is already listening to
|
||||
.filter(signal => !this.activeShutdownSignals.includes(signal));
|
||||
.filter(signal => !this.activeShutdownSignals.includes(signal))
|
||||
.toArray();
|
||||
|
||||
this.listenToShutdownSignals(signals);
|
||||
return this;
|
||||
@@ -274,7 +277,9 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
options?: { strict: boolean },
|
||||
): Promise<TResult> {
|
||||
let wrapper: InstanceWrapper, collection: Map<string, InstanceWrapper>;
|
||||
if (!(options && options.strict)) {
|
||||
|
||||
const isStrictModeEnabled = options && options.strict;
|
||||
if (!isStrictModeEnabled) {
|
||||
[wrapper, collection] = this.containerScanner.getWrapperCollectionPair(
|
||||
typeOrToken,
|
||||
);
|
||||
@@ -287,8 +292,13 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
contextModule,
|
||||
);
|
||||
}
|
||||
if (wrapper.isDependencyTreeStatic() && !wrapper.isTransient) {
|
||||
throw new InvalidClassScopeException(typeOrToken);
|
||||
}
|
||||
|
||||
const ctorHost = wrapper.instance || { constructor: typeOrToken };
|
||||
const instance = await this.injector.loadPerContext(
|
||||
wrapper.instance,
|
||||
ctorHost,
|
||||
wrapper.host,
|
||||
collection,
|
||||
contextId,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
HttpServer,
|
||||
INestApplication,
|
||||
INestMicroservice,
|
||||
NestHybridApplicationOptions,
|
||||
NestInterceptor,
|
||||
PipeTransform,
|
||||
WebSocketAdapter,
|
||||
@@ -13,7 +14,7 @@ import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-applicati
|
||||
import { Logger } from '@nestjs/common/services/logger.service';
|
||||
import { loadPackage } from '@nestjs/common/utils/load-package.util';
|
||||
import { isObject, validatePath } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { platform } from 'os';
|
||||
import { AbstractHttpAdapter } from './adapters';
|
||||
import { ApplicationConfig } from './application-config';
|
||||
@@ -170,17 +171,23 @@ export class NestApplication extends NestApplicationContext
|
||||
this.routesResolver.registerExceptionHandler();
|
||||
}
|
||||
|
||||
public connectMicroservice<T extends object>(options: T): INestMicroservice {
|
||||
public connectMicroservice<T extends object>(
|
||||
microserviceOptions: T,
|
||||
hybridAppOptions: NestHybridApplicationOptions = {},
|
||||
): INestMicroservice {
|
||||
const { NestMicroservice } = loadPackage(
|
||||
'@nestjs/microservices',
|
||||
'NestFactory',
|
||||
() => require('@nestjs/microservices'),
|
||||
);
|
||||
const { inheritAppConfig } = hybridAppOptions;
|
||||
const applicationConfig = inheritAppConfig
|
||||
? this.config
|
||||
: new ApplicationConfig();
|
||||
|
||||
const applicationConfig = new ApplicationConfig();
|
||||
const instance = new NestMicroservice(
|
||||
this.container,
|
||||
options,
|
||||
microserviceOptions,
|
||||
applicationConfig,
|
||||
);
|
||||
instance.registerListeners();
|
||||
|
||||
@@ -190,7 +190,7 @@ export class NestFactoryStatic {
|
||||
prop: string,
|
||||
): Function {
|
||||
return (...args: unknown[]) => {
|
||||
let result;
|
||||
let result: unknown;
|
||||
ExceptionsZone.run(() => {
|
||||
result = receiver[prop](...args);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "7.0.5",
|
||||
"version": "7.1.3",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
@@ -32,8 +32,11 @@
|
||||
"iterare": "1.2.0",
|
||||
"object-hash": "2.0.3",
|
||||
"path-to-regexp": "3.2.0",
|
||||
"tslib": "1.11.1",
|
||||
"uuid": "7.0.2"
|
||||
"tslib": "2.0.0",
|
||||
"uuid": "8.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/common": "7.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^7.0.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PIPES_METADATA } from '@nestjs/common/constants';
|
||||
import { Controller, PipeTransform } from '@nestjs/common/interfaces';
|
||||
import { isEmpty, isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { iterate } from 'iterare';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { ContextCreator } from '../helpers/context-creator';
|
||||
import { STATIC_CONTEXT } from '../injector/constants';
|
||||
@@ -96,10 +96,11 @@ export class PipesContextCreator extends ContextCreator {
|
||||
return globalPipes;
|
||||
}
|
||||
const scopedPipeWrappers = this.config.getGlobalRequestPipes() as InstanceWrapper[];
|
||||
const scopedPipes = scopedPipeWrappers
|
||||
const scopedPipes = iterate(scopedPipeWrappers)
|
||||
.map(wrapper => wrapper.getInstanceByContextId(contextId, inquirerId))
|
||||
.filter(host => host)
|
||||
.map(host => host.instance);
|
||||
.filter(host => !!host)
|
||||
.map(host => host.instance)
|
||||
.toArray();
|
||||
|
||||
return globalPipes.concat(scopedPipes) as T;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { STATIC_CONTEXT } from '../injector/constants';
|
||||
import { NestContainer } from '../injector/container';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { RouterProxyCallback } from './router-proxy';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
export class RouterExceptionFilters extends BaseExceptionFilterContext {
|
||||
constructor(
|
||||
@@ -52,10 +53,11 @@ export class RouterExceptionFilters extends BaseExceptionFilterContext {
|
||||
return globalFilters;
|
||||
}
|
||||
const scopedFilterWrappers = this.config.getGlobalRequestFilters() as InstanceWrapper[];
|
||||
const scopedFilters = scopedFilterWrappers
|
||||
const scopedFilters = iterate(scopedFilterWrappers)
|
||||
.map(wrapper => wrapper.getInstanceByContextId(contextId, inquirerId))
|
||||
.filter(host => host)
|
||||
.map(host => host.instance);
|
||||
.filter(host => !!host)
|
||||
.map(host => host.instance)
|
||||
.toArray();
|
||||
|
||||
return globalFilters.concat(scopedFilters) as T;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
isNil,
|
||||
isUndefined,
|
||||
} from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import { ApplicationConfig } from './application-config';
|
||||
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from './constants';
|
||||
import { CircularDependencyException } from './errors/exceptions/circular-dependency.exception';
|
||||
@@ -42,6 +43,8 @@ import { NestContainer } from './injector/container';
|
||||
import { InstanceWrapper } from './injector/instance-wrapper';
|
||||
import { Module } from './injector/module';
|
||||
import { MetadataScanner } from './metadata-scanner';
|
||||
import { InvalidModuleException } from './errors/exceptions/invalid-module.exception';
|
||||
import { UndefinedModuleException } from './errors/exceptions/undefined-module.exception';
|
||||
|
||||
interface ApplicationProviderWrapper {
|
||||
moduleKey: string;
|
||||
@@ -89,7 +92,14 @@ export class DependenciesScanner {
|
||||
...((module as DynamicModule).imports || []),
|
||||
];
|
||||
|
||||
for (const innerModule of modules) {
|
||||
for (const [index, innerModule] of modules.entries()) {
|
||||
// In case of a circular dependency (ES module system), JavaScript will resolve the type to `undefined`.
|
||||
if (innerModule === undefined) {
|
||||
throw new UndefinedModuleException(module, index, scope);
|
||||
}
|
||||
if (!innerModule) {
|
||||
throw new InvalidModuleException(module, index, scope);
|
||||
}
|
||||
if (ctxRegistry.includes(innerModule)) {
|
||||
continue;
|
||||
}
|
||||
@@ -259,7 +269,7 @@ export class DependenciesScanner {
|
||||
|
||||
public async calculateModulesDistance(modules: ModulesContainer) {
|
||||
const modulesGenerator = modules.values();
|
||||
const rootModule = modulesGenerator.next().value;
|
||||
const rootModule = modulesGenerator.next().value as Module;
|
||||
const modulesStack = [rootModule];
|
||||
|
||||
const calculateDistance = (moduleRef: Module, distance = 1) => {
|
||||
@@ -378,24 +388,20 @@ export class DependenciesScanner {
|
||||
* to all controllers metadata storage
|
||||
*/
|
||||
public addScopedEnhancersMetadata() {
|
||||
const scopedGlobalProviders = this.applicationProvidersApplyMap.filter(
|
||||
wrapper => this.isRequestOrTransient(wrapper.scope),
|
||||
);
|
||||
iterate(this.applicationProvidersApplyMap)
|
||||
.filter(wrapper => this.isRequestOrTransient(wrapper.scope))
|
||||
.forEach(({ moduleKey, providerKey }) => {
|
||||
const modulesContainer = this.container.getModules();
|
||||
const { injectables } = modulesContainer.get(moduleKey);
|
||||
const instanceWrapper = injectables.get(providerKey);
|
||||
|
||||
scopedGlobalProviders.forEach(({ moduleKey, providerKey }) => {
|
||||
const modulesContainer = this.container.getModules();
|
||||
const { injectables } = modulesContainer.get(moduleKey);
|
||||
const instanceWrapper = injectables.get(providerKey);
|
||||
|
||||
const modules = [...modulesContainer.values()];
|
||||
const controllersArray = modules.map(module => [
|
||||
...module.controllers.values(),
|
||||
]);
|
||||
const controllers = this.flatten(controllersArray);
|
||||
controllers.forEach(controller =>
|
||||
controller.addEnhancerMetadata(instanceWrapper),
|
||||
);
|
||||
});
|
||||
iterate(modulesContainer.values())
|
||||
.map(module => module.controllers.values())
|
||||
.flatten()
|
||||
.forEach(controller =>
|
||||
controller.addEnhancerMetadata(instanceWrapper),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public applyApplicationProviders() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { isObject } from '@nestjs/common/utils/shared.utils';
|
||||
import { isEmpty, isObject } from '@nestjs/common/utils/shared.utils';
|
||||
|
||||
/**
|
||||
* Helper class providing Nest reflection capabilities.
|
||||
@@ -53,7 +53,14 @@ export class Reflector {
|
||||
metadataKey: TKey,
|
||||
targets: (Type<any> | Function)[],
|
||||
): TResult {
|
||||
const metadataCollection = this.getAll(metadataKey, targets);
|
||||
const metadataCollection = this.getAll<TResult, TKey>(
|
||||
metadataKey,
|
||||
targets,
|
||||
).filter(item => item !== undefined);
|
||||
|
||||
if (isEmpty(metadataCollection)) {
|
||||
return metadataCollection as TResult;
|
||||
}
|
||||
return metadataCollection.reduce((a, b) => {
|
||||
if (Array.isArray(a)) {
|
||||
return a.concat(b);
|
||||
|
||||
@@ -2,161 +2,204 @@ import { expect } from 'chai';
|
||||
import { UnknownDependenciesException } from '../../../errors/exceptions/unknown-dependencies.exception';
|
||||
import { Module } from '../../../injector/module';
|
||||
import { stringCleaner } from '../../utils/string.cleaner';
|
||||
import {
|
||||
UNDEFINED_MODULE_MESSAGE,
|
||||
INVALID_MODULE_MESSAGE,
|
||||
} from '../../../errors/messages';
|
||||
|
||||
describe('UnknownDependenciesMessage', () => {
|
||||
const index = 0;
|
||||
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.
|
||||
describe('Error Messages', () => {
|
||||
const CatsModule = { name: 'CatsModule' };
|
||||
const AppModule = { name: 'AppModule' };
|
||||
|
||||
Potential solutions:
|
||||
- If dependency is a provider, is it part of the current current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
describe('UNKNOWN_DEPENDENCIES_MESSAGE', () => {
|
||||
const index = 0;
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
|
||||
class CatService {}
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatService', {
|
||||
index,
|
||||
dependencies: ['', CatService],
|
||||
}).message,
|
||||
);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
})
|
||||
`);
|
||||
|
||||
class CatService {}
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatService', {
|
||||
index,
|
||||
dependencies: ['', 'MY_TOKEN'],
|
||||
}).message,
|
||||
);
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatService', {
|
||||
index,
|
||||
dependencies: ['', CatService],
|
||||
}).message,
|
||||
);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatService', {
|
||||
index,
|
||||
dependencies: ['', 'MY_TOKEN'],
|
||||
}).message,
|
||||
);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
|
||||
function CatFunction() {}
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatService', {
|
||||
index,
|
||||
dependencies: ['', CatFunction],
|
||||
}).message,
|
||||
);
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
`);
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatService', {
|
||||
index,
|
||||
dependencies: ['', undefined],
|
||||
}).message,
|
||||
);
|
||||
function CatFunction() {}
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatService', {
|
||||
index,
|
||||
dependencies: ['', CatFunction],
|
||||
}).message,
|
||||
);
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatService', {
|
||||
index,
|
||||
dependencies: ['', undefined],
|
||||
}).message,
|
||||
);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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:
|
||||
- If dependency is a provider, is it part of the current TestModule?
|
||||
- If dependency is exported from a separate @Module, is that module imported within TestModule?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
|
||||
class MetaType {
|
||||
name: string;
|
||||
}
|
||||
class TestModule {
|
||||
metatype: MetaType;
|
||||
}
|
||||
const myModule = new TestModule();
|
||||
const myMetaType = new MetaType();
|
||||
myMetaType.name = 'TestModule';
|
||||
myModule.metatype = myMetaType;
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException(
|
||||
'CatService',
|
||||
{ index, dependencies: ['', 'MY_TOKEN'] },
|
||||
myModule as Module,
|
||||
).message,
|
||||
);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException(Symbol('CatProvider'), {
|
||||
index,
|
||||
dependencies: [''],
|
||||
}).message,
|
||||
);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatProvider', {
|
||||
index,
|
||||
dependencies: ['', Symbol('DogProvider')],
|
||||
}).message,
|
||||
);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
});
|
||||
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:
|
||||
- If dependency is a provider, is it part of the current TestModule?
|
||||
- If dependency is exported from a separate @Module, is that module imported within TestModule?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
describe('UNDEFINED_MODULE_EXCEPTION', () => {
|
||||
it('should display the module name with the undefined index and scope', () => {
|
||||
const expectedMessage = stringCleaner(`Nest cannot create the CatsModule instance.
|
||||
The module at index [0] of the CatsModule "imports" array is undefined.
|
||||
|
||||
class MetaType {
|
||||
name: string;
|
||||
}
|
||||
class TestModule {
|
||||
metatype: MetaType;
|
||||
}
|
||||
const myModule = new TestModule();
|
||||
const myMetaType = new MetaType();
|
||||
myMetaType.name = 'TestModule';
|
||||
myModule.metatype = myMetaType;
|
||||
Potential causes:
|
||||
- A circular dependency between modules. Use forwardRef() to avoid it. Read more: https://docs.nestjs.com/fundamentals/circular-dependency
|
||||
- The module at index [0] is of type "undefined". Check your import statements and the type of the module.
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException(
|
||||
'CatService',
|
||||
{ index, dependencies: ['', 'MY_TOKEN'] },
|
||||
myModule as Module,
|
||||
).message,
|
||||
);
|
||||
Scope [AppModule -> CatsModule]`);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
const actualMessage = stringCleaner(
|
||||
UNDEFINED_MODULE_MESSAGE(CatsModule, 0, [AppModule, CatsModule]),
|
||||
);
|
||||
|
||||
expect(actualMessage).to.be.eq(expectedMessage);
|
||||
});
|
||||
});
|
||||
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 current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
describe('INVALID_MODULE_MESSAGE', () => {
|
||||
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.
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException(Symbol('CatProvider'), {
|
||||
index,
|
||||
dependencies: [''],
|
||||
}).message,
|
||||
);
|
||||
Scope [AppModule -> CatsModule]`);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
});
|
||||
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.
|
||||
const actualMessage = stringCleaner(
|
||||
INVALID_MODULE_MESSAGE(CatsModule, 0, [AppModule, CatsModule]),
|
||||
);
|
||||
|
||||
Potential solutions:
|
||||
- If dependency is a provider, is it part of the current current?
|
||||
- If dependency is exported from a separate @Module, is that module imported within current?
|
||||
@Module({
|
||||
imports: [ /* the Module containing dependency */ ]
|
||||
})
|
||||
`);
|
||||
|
||||
const actualMessage = stringCleaner(
|
||||
new UnknownDependenciesException('CatProvider', {
|
||||
index,
|
||||
dependencies: ['', Symbol('DogProvider')],
|
||||
}).message,
|
||||
);
|
||||
|
||||
expect(actualMessage).to.equal(expectedResult);
|
||||
expect(actualMessage).to.be.eq(expectedMessage);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
49
packages/core/test/hooks/before-app-shutdown.hook.spec.ts
Normal file
49
packages/core/test/hooks/before-app-shutdown.hook.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { BeforeApplicationShutdown } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { callBeforeAppShutdownHook } from '../../hooks/before-app-shutdown.hook';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { Module } from '../../injector/module';
|
||||
|
||||
class SampleProvider implements BeforeApplicationShutdown {
|
||||
beforeApplicationShutdown(signal?: string) {}
|
||||
}
|
||||
|
||||
class SampleModule implements BeforeApplicationShutdown {
|
||||
beforeApplicationShutdown(signal?: string) {}
|
||||
}
|
||||
|
||||
class WithoutHookProvider {}
|
||||
|
||||
describe('BeforeAppShutdown', () => {
|
||||
let moduleRef: Module;
|
||||
let sampleProvider: SampleProvider;
|
||||
|
||||
beforeEach(() => {
|
||||
sampleProvider = new SampleProvider();
|
||||
moduleRef = new Module(SampleModule, new NestContainer());
|
||||
|
||||
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule.name);
|
||||
moduleWrapperRef.instance = new SampleModule();
|
||||
|
||||
moduleRef.addProvider({
|
||||
provide: SampleProvider,
|
||||
useValue: sampleProvider,
|
||||
});
|
||||
moduleRef.addProvider({
|
||||
provide: WithoutHookProvider,
|
||||
useValue: new WithoutHookProvider(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('callBeforeAppShutdownHook', () => {
|
||||
it('should call "beforeApplicationShutdown" hook for the entire module', async () => {
|
||||
const signal = 'SIGTERM';
|
||||
|
||||
const hookSpy = sinon.spy(sampleProvider, 'beforeApplicationShutdown');
|
||||
await callBeforeAppShutdownHook(moduleRef, signal);
|
||||
|
||||
expect(hookSpy.calledWith(signal)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
47
packages/core/test/hooks/on-app-bootstrap.hook.spec.ts
Normal file
47
packages/core/test/hooks/on-app-bootstrap.hook.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { OnApplicationBootstrap } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { callModuleBootstrapHook } from '../../hooks/on-app-bootstrap.hook';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { Module } from '../../injector/module';
|
||||
|
||||
class SampleProvider implements OnApplicationBootstrap {
|
||||
onApplicationBootstrap() {}
|
||||
}
|
||||
|
||||
class SampleModule implements OnApplicationBootstrap {
|
||||
onApplicationBootstrap() {}
|
||||
}
|
||||
|
||||
class WithoutHookProvider {}
|
||||
|
||||
describe('OnApplicationBootstrap', () => {
|
||||
let moduleRef: Module;
|
||||
let sampleProvider: SampleProvider;
|
||||
|
||||
beforeEach(() => {
|
||||
sampleProvider = new SampleProvider();
|
||||
moduleRef = new Module(SampleModule, new NestContainer());
|
||||
|
||||
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule.name);
|
||||
moduleWrapperRef.instance = new SampleModule();
|
||||
|
||||
moduleRef.addProvider({
|
||||
provide: SampleProvider,
|
||||
useValue: sampleProvider,
|
||||
});
|
||||
moduleRef.addProvider({
|
||||
provide: WithoutHookProvider,
|
||||
useValue: new WithoutHookProvider(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('callModuleBootstrapHook', () => {
|
||||
it('should call "onApplicationBootstrap" hook for the entire module', async () => {
|
||||
const hookSpy = sinon.spy(sampleProvider, 'onApplicationBootstrap');
|
||||
await callModuleBootstrapHook(moduleRef);
|
||||
|
||||
expect(hookSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
47
packages/core/test/hooks/on-app-shutdown.hook.spec.ts
Normal file
47
packages/core/test/hooks/on-app-shutdown.hook.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { OnApplicationShutdown } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { callAppShutdownHook } from '../../hooks/on-app-shutdown.hook';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { Module } from '../../injector/module';
|
||||
|
||||
class SampleProvider implements OnApplicationShutdown {
|
||||
onApplicationShutdown() {}
|
||||
}
|
||||
|
||||
class SampleModule implements OnApplicationShutdown {
|
||||
onApplicationShutdown() {}
|
||||
}
|
||||
|
||||
class WithoutHookProvider {}
|
||||
|
||||
describe('OnApplicationShutdown', () => {
|
||||
let moduleRef: Module;
|
||||
let sampleProvider: SampleProvider;
|
||||
|
||||
beforeEach(() => {
|
||||
sampleProvider = new SampleProvider();
|
||||
moduleRef = new Module(SampleModule, new NestContainer());
|
||||
|
||||
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule.name);
|
||||
moduleWrapperRef.instance = new SampleModule();
|
||||
|
||||
moduleRef.addProvider({
|
||||
provide: SampleProvider,
|
||||
useValue: sampleProvider,
|
||||
});
|
||||
moduleRef.addProvider({
|
||||
provide: WithoutHookProvider,
|
||||
useValue: new WithoutHookProvider(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('callAppShutdownHook', () => {
|
||||
it('should call "onApplicationShutdown" hook for the entire module', async () => {
|
||||
const hookSpy = sinon.spy(sampleProvider, 'onApplicationShutdown');
|
||||
await callAppShutdownHook(moduleRef);
|
||||
|
||||
expect(hookSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
47
packages/core/test/hooks/on-module-destroy.hook.spec.ts
Normal file
47
packages/core/test/hooks/on-module-destroy.hook.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { OnModuleDestroy } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { callModuleDestroyHook } from '../../hooks/on-module-destroy.hook';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { Module } from '../../injector/module';
|
||||
|
||||
class SampleProvider implements OnModuleDestroy {
|
||||
onModuleDestroy() {}
|
||||
}
|
||||
|
||||
class SampleModule implements OnModuleDestroy {
|
||||
onModuleDestroy() {}
|
||||
}
|
||||
|
||||
class WithoutHookProvider {}
|
||||
|
||||
describe('OnModuleDestroy', () => {
|
||||
let moduleRef: Module;
|
||||
let sampleProvider: SampleProvider;
|
||||
|
||||
beforeEach(() => {
|
||||
sampleProvider = new SampleProvider();
|
||||
moduleRef = new Module(SampleModule, new NestContainer());
|
||||
|
||||
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule.name);
|
||||
moduleWrapperRef.instance = new SampleModule();
|
||||
|
||||
moduleRef.addProvider({
|
||||
provide: SampleProvider,
|
||||
useValue: sampleProvider,
|
||||
});
|
||||
moduleRef.addProvider({
|
||||
provide: WithoutHookProvider,
|
||||
useValue: new WithoutHookProvider(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('callModuleDestroyHook', () => {
|
||||
it('should call "onModuleDestroy" hook for the entire module', async () => {
|
||||
const hookSpy = sinon.spy(sampleProvider, 'onModuleDestroy');
|
||||
await callModuleDestroyHook(moduleRef);
|
||||
|
||||
expect(hookSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
47
packages/core/test/hooks/on-module-init.hook.spec.ts
Normal file
47
packages/core/test/hooks/on-module-init.hook.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { OnModuleInit } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { callModuleInitHook } from '../../hooks/on-module-init.hook';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
import { Module } from '../../injector/module';
|
||||
|
||||
class SampleProvider implements OnModuleInit {
|
||||
onModuleInit() {}
|
||||
}
|
||||
|
||||
class SampleModule implements OnModuleInit {
|
||||
onModuleInit() {}
|
||||
}
|
||||
|
||||
class WithoutHookProvider {}
|
||||
|
||||
describe('OnModuleInit', () => {
|
||||
let moduleRef: Module;
|
||||
let sampleProvider: SampleProvider;
|
||||
|
||||
beforeEach(() => {
|
||||
sampleProvider = new SampleProvider();
|
||||
moduleRef = new Module(SampleModule, new NestContainer());
|
||||
|
||||
const moduleWrapperRef = moduleRef.getProviderByKey(SampleModule.name);
|
||||
moduleWrapperRef.instance = new SampleModule();
|
||||
|
||||
moduleRef.addProvider({
|
||||
provide: SampleProvider,
|
||||
useValue: sampleProvider,
|
||||
});
|
||||
moduleRef.addProvider({
|
||||
provide: WithoutHookProvider,
|
||||
useValue: new WithoutHookProvider(),
|
||||
});
|
||||
});
|
||||
|
||||
describe('callModuleInitHook', () => {
|
||||
it('should call "onModuleInit" hook for the entire module', async () => {
|
||||
const hookSpy = sinon.spy(sampleProvider, 'onModuleInit');
|
||||
await callModuleInitHook(moduleRef);
|
||||
|
||||
expect(hookSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -36,7 +36,7 @@ describe('Injector', () => {
|
||||
let mainTest, depOne, depTwo;
|
||||
|
||||
beforeEach(() => {
|
||||
moduleDeps = new Module(DependencyTwo as any, [], new NestContainer());
|
||||
moduleDeps = new Module(DependencyTwo, new NestContainer());
|
||||
mainTest = new InstanceWrapper({
|
||||
name: 'MainTest',
|
||||
metatype: MainTest,
|
||||
@@ -133,7 +133,7 @@ describe('Injector', () => {
|
||||
let test;
|
||||
|
||||
beforeEach(() => {
|
||||
moduleDeps = new Module(Test as any, [], new NestContainer());
|
||||
moduleDeps = new Module(Test, new NestContainer());
|
||||
test = new InstanceWrapper({
|
||||
name: 'Test',
|
||||
metatype: Test,
|
||||
@@ -712,12 +712,12 @@ describe('Injector', () => {
|
||||
const wrapper = new InstanceWrapper();
|
||||
wrapper.addEnhancerMetadata(
|
||||
new InstanceWrapper({
|
||||
host: new Module(class {}, [], new NestContainer()),
|
||||
host: new Module(class {}, new NestContainer()),
|
||||
}),
|
||||
);
|
||||
wrapper.addEnhancerMetadata(
|
||||
new InstanceWrapper({
|
||||
host: new Module(class {}, [], new NestContainer()),
|
||||
host: new Module(class {}, new NestContainer()),
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('Module', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
container = new NestContainer();
|
||||
module = new Module(TestModule as any, [], container);
|
||||
module = new Module(TestModule, container);
|
||||
});
|
||||
|
||||
it('should add controller', () => {
|
||||
@@ -418,7 +418,7 @@ describe('Module', () => {
|
||||
it('should behave as identity', () => {
|
||||
const metatype = { name: token };
|
||||
(module as any)._imports = new Set([
|
||||
new Module(metatype as any, [], new NestContainer()),
|
||||
new Module(metatype as any, new NestContainer()),
|
||||
]);
|
||||
expect(module.validateExportedProvider(token)).to.be.eql(token);
|
||||
});
|
||||
|
||||
@@ -33,8 +33,8 @@ describe('MiddlewareContainer', () => {
|
||||
const nestContainer = new NestContainer();
|
||||
const modules = nestContainer.getModules();
|
||||
|
||||
modules.set('Module', new Module(ExampleModule, [], nestContainer));
|
||||
modules.set('Test', new Module(ExampleModule, [], nestContainer));
|
||||
modules.set('Module', new Module(ExampleModule, nestContainer));
|
||||
modules.set('Test', new Module(ExampleModule, nestContainer));
|
||||
|
||||
container = new MiddlewareContainer(nestContainer);
|
||||
});
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('MiddlewareModule', () => {
|
||||
const stubContainer = new NestContainer();
|
||||
stubContainer
|
||||
.getModules()
|
||||
.set('Test', new Module(class {}, [], stubContainer));
|
||||
.set('Test', new Module(class {}, stubContainer));
|
||||
|
||||
await middlewareModule.loadConfiguration(
|
||||
new MiddlewareContainer(stubContainer),
|
||||
@@ -87,7 +87,7 @@ describe('MiddlewareModule', () => {
|
||||
nestContainer = new NestContainer();
|
||||
nestContainer
|
||||
.getModules()
|
||||
.set('Test', new Module(TestModule, [], nestContainer));
|
||||
.set('Test', new Module(TestModule, nestContainer));
|
||||
});
|
||||
it('should throw "RuntimeException" exception when middleware is not stored in container', () => {
|
||||
const route = { path: 'Test' };
|
||||
@@ -162,7 +162,7 @@ describe('MiddlewareModule', () => {
|
||||
const stubContainer = new NestContainer();
|
||||
stubContainer
|
||||
.getModules()
|
||||
.set('Test', new Module(TestModule, [], stubContainer));
|
||||
.set('Test', new Module(TestModule, stubContainer));
|
||||
|
||||
const container = new MiddlewareContainer(stubContainer);
|
||||
const moduleKey = 'Test';
|
||||
@@ -178,7 +178,7 @@ describe('MiddlewareModule', () => {
|
||||
);
|
||||
sinon
|
||||
.stub(stubContainer, 'getModuleByKey')
|
||||
.callsFake(() => new Module(class {}, [], stubContainer));
|
||||
.callsFake(() => new Module(class {}, stubContainer));
|
||||
middlewareModule['container'] = stubContainer;
|
||||
|
||||
await middlewareModule.registerRouteMiddleware(
|
||||
|
||||
54
packages/core/test/nest-application.spec.ts
Normal file
54
packages/core/test/nest-application.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { expect } from 'chai';
|
||||
import { NestApplication } from '../nest-application';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { NestContainer } from '../injector';
|
||||
import { NoopHttpAdapter } from './utils/noop-adapter.spec';
|
||||
|
||||
describe('NestApplication', () => {
|
||||
describe('Hybrid Application', () => {
|
||||
class Interceptor {
|
||||
public intercept(context, next) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
it('default should use new ApplicationConfig', () => {
|
||||
const applicationConfig = new ApplicationConfig();
|
||||
const container = new NestContainer(applicationConfig);
|
||||
const instance = new NestApplication(
|
||||
container,
|
||||
new NoopHttpAdapter({}),
|
||||
applicationConfig,
|
||||
{},
|
||||
);
|
||||
instance.useGlobalInterceptors(new Interceptor());
|
||||
const microservice = instance.connectMicroservice({});
|
||||
expect((instance as any).config.getGlobalInterceptors().length).to.equal(
|
||||
1,
|
||||
);
|
||||
expect(
|
||||
(microservice as any).applicationConfig.getGlobalInterceptors().length,
|
||||
).to.equal(0);
|
||||
});
|
||||
it('should inherit existing ApplicationConfig', () => {
|
||||
const applicationConfig = new ApplicationConfig();
|
||||
const container = new NestContainer(applicationConfig);
|
||||
const instance = new NestApplication(
|
||||
container,
|
||||
new NoopHttpAdapter({}),
|
||||
applicationConfig,
|
||||
{},
|
||||
);
|
||||
instance.useGlobalInterceptors(new Interceptor());
|
||||
const microservice = instance.connectMicroservice(
|
||||
{},
|
||||
{ inheritAppConfig: true },
|
||||
);
|
||||
expect((instance as any).config.getGlobalInterceptors().length).to.equal(
|
||||
1,
|
||||
);
|
||||
expect(
|
||||
(microservice as any).applicationConfig.getGlobalInterceptors().length,
|
||||
).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,8 @@ import { NestContainer } from '../injector/container';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { MetadataScanner } from '../metadata-scanner';
|
||||
import { DependenciesScanner } from '../scanner';
|
||||
import { UndefinedModuleException } from '../errors/exceptions/undefined-module.exception';
|
||||
import { InvalidModuleException } from '../errors/exceptions/invalid-module.exception';
|
||||
|
||||
describe('DependenciesScanner', () => {
|
||||
class Guard {}
|
||||
@@ -36,6 +38,16 @@ describe('DependenciesScanner', () => {
|
||||
})
|
||||
class TestModule {}
|
||||
|
||||
@Module({
|
||||
imports: [undefined],
|
||||
})
|
||||
class UndefinedModule {}
|
||||
|
||||
@Module({
|
||||
imports: [null],
|
||||
})
|
||||
class InvalidModule {}
|
||||
|
||||
let scanner: DependenciesScanner;
|
||||
let mockContainer: sinon.SinonMock;
|
||||
let container: NestContainer;
|
||||
@@ -432,4 +444,20 @@ describe('DependenciesScanner', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('scanForModules', () => {
|
||||
it('should throw an exception when the imports array includes undefined', () => {
|
||||
try {
|
||||
scanner.scanForModules(UndefinedModule, [UndefinedModule]);
|
||||
} catch (exception) {
|
||||
expect(exception instanceof UndefinedModuleException).to.be.true;
|
||||
}
|
||||
});
|
||||
it('should throw an exception when the imports array includes an invalid value', () => {
|
||||
try {
|
||||
scanner.scanForModules(InvalidModule, [InvalidModule]);
|
||||
} catch (exception) {
|
||||
expect(exception instanceof InvalidModuleException).to.be.true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,6 +38,14 @@ Nest is a framework for building efficient, scalable <a href="http://nodejs.org"
|
||||
* 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:
|
||||
|
||||
## Questions
|
||||
|
||||
For questions and support please use the official [Discord channel](https://discord.gg/G7Qnnhy). The issue list of this repo is **exclusively** for bug reports and feature requests.
|
||||
|
||||
## Issues
|
||||
|
||||
Please make sure to read the [Issue Reporting Checklist](https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md#-submitting-an-issue) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
|
||||
|
||||
## Consulting
|
||||
|
||||
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
@@ -48,29 +56,35 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="220" /></a>
|
||||
|
||||
#### Silver Sponsors
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" height="95" /></a>
|
||||
<a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="150" valign="middle" /></a>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></a>
|
||||
|
||||
<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://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="170" valign="middle" /></a> </td><td>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></td></tr></table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a> <a href="https://blueanchor.io/" target="_blank"><img src="https://nestjs.com/img/blueanchor.png" width="150" valign="middle" /></a>
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a> <a href="https://hostpresto.com" target="_blank"><img src="https://nestjs.com/img/hostpresto.png" height="24" valign="middle" /></a>
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" height="14" valign="middle" /></a>
|
||||
<a href="https://buddy.works/" target="_blank"><img src="https://nestjs.com/img/buddy-logo.svg" height="25" valign="middle" /></a>
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" height="25" valign="middle" /></a> <a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" height="27" valign="middle" /></a> <a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" height="20" valign="middle" /></a> <a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" height="22" valign="middle" /></a> <a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" height="19" valign="middle" /></a> <a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" height="20" valign="middle" /></a>
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" height="18" valign="middle" /></a>
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a>
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a>
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a>
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a>
|
||||
<a href="https://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" valign="middle" /></a>
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a>
|
||||
<a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a>
|
||||
<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://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://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" 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></tr></table>
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
@@ -64,6 +64,7 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
|
||||
if (!clientRef) {
|
||||
throw new InvalidGrpcServiceException();
|
||||
}
|
||||
|
||||
const maxSendMessageLengthKey = 'grpc.max_send_message_length';
|
||||
const maxReceiveMessageLengthKey = 'grpc.max_receive_message_length';
|
||||
const maxMessageLengthOptions = {
|
||||
@@ -78,24 +79,24 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
|
||||
GRPC_DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH,
|
||||
),
|
||||
};
|
||||
const maxMetadataSize = this.getOptionsProp(
|
||||
this.options,
|
||||
'maxMetadataSize',
|
||||
-1,
|
||||
);
|
||||
if (maxMetadataSize > 0) {
|
||||
maxMessageLengthOptions['grpc.max_metadata_size'] = maxMetadataSize;
|
||||
}
|
||||
|
||||
const keepaliveOptions = this.getKeepaliveOptions();
|
||||
const options: Record<string, unknown> = isObject(this.options)
|
||||
? {
|
||||
...this.options,
|
||||
...maxMessageLengthOptions,
|
||||
...keepaliveOptions,
|
||||
loader: '',
|
||||
}
|
||||
: {
|
||||
...maxMessageLengthOptions,
|
||||
};
|
||||
const options: Record<string, string | number> = {
|
||||
...(this.options.channelOptions || {}),
|
||||
...maxMessageLengthOptions,
|
||||
...keepaliveOptions,
|
||||
};
|
||||
|
||||
const credentials =
|
||||
options.credentials || grpcPackage.credentials.createInsecure();
|
||||
|
||||
delete options.credentials;
|
||||
delete options.keepalive;
|
||||
this.options.credentials || grpcPackage.credentials.createInsecure();
|
||||
|
||||
const grpcClient = new clientRef[name](this.url, credentials, options);
|
||||
this.clients.set(name, grpcClient);
|
||||
@@ -125,7 +126,7 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
|
||||
this.options.keepalive,
|
||||
)) {
|
||||
const key = keepaliveKeys[optionKey];
|
||||
if (!key) {
|
||||
if (key === undefined) {
|
||||
continue;
|
||||
}
|
||||
keepaliveOptions[key] = optionValue;
|
||||
|
||||
@@ -93,11 +93,9 @@ export class ClientRedis extends ClientProxy {
|
||||
}
|
||||
|
||||
public getClientOptions(error$: Subject<Error>): Partial<ClientOpts> {
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
const retry_strategy = (options: RetryStrategyOptions) =>
|
||||
this.createRetryStrategy(options, error$);
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/camelcase
|
||||
retry_strategy,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export const SUBSCRIBE = 'subscribe';
|
||||
export const CANCEL_EVENT = 'cancelled';
|
||||
|
||||
export const PATTERN_METADATA = 'microservices:pattern';
|
||||
export const TRANSPORT_METADATA = 'microservices:transport';
|
||||
export const CLIENT_CONFIGURATION_METADATA = 'microservices:client';
|
||||
export const PATTERN_HANDLER_METADATA = 'microservices:handler_type';
|
||||
export const CLIENT_METADATA = 'microservices:is_client_instance';
|
||||
|
||||
@@ -8,6 +8,7 @@ import { NestContainer } from '@nestjs/core/injector/container';
|
||||
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RpcExceptionsHandler } from '../exceptions/rpc-exceptions-handler';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
export class ExceptionFiltersContext extends BaseExceptionFilterContext {
|
||||
constructor(
|
||||
@@ -50,10 +51,11 @@ export class ExceptionFiltersContext extends BaseExceptionFilterContext {
|
||||
return globalFilters;
|
||||
}
|
||||
const scopedFilterWrappers = this.config.getGlobalRequestFilters() as InstanceWrapper[];
|
||||
const scopedFilters = scopedFilterWrappers
|
||||
const scopedFilters = iterate(scopedFilterWrappers)
|
||||
.map(wrapper => wrapper.getInstanceByContextId(contextId, inquirerId))
|
||||
.filter(host => host)
|
||||
.map(host => host.instance);
|
||||
.filter(host => !!host)
|
||||
.map(host => host.instance)
|
||||
.toArray();
|
||||
|
||||
return globalFilters.concat(scopedFilters) as T;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { PATTERN_HANDLER_METADATA, PATTERN_METADATA } from '../constants';
|
||||
import {
|
||||
PATTERN_HANDLER_METADATA,
|
||||
PATTERN_METADATA,
|
||||
TRANSPORT_METADATA,
|
||||
} from '../constants';
|
||||
import { PatternHandler } from '../enums/pattern-handler.enum';
|
||||
import { Transport } from '../enums';
|
||||
|
||||
/**
|
||||
* Subscribes to incoming events which fulfils chosen pattern.
|
||||
*/
|
||||
export const EventPattern = <T = string>(metadata?: T): MethodDecorator => {
|
||||
export const EventPattern = <T = string>(
|
||||
metadata?: T,
|
||||
transport?: Transport,
|
||||
): MethodDecorator => {
|
||||
return (
|
||||
target: object,
|
||||
key: string | symbol,
|
||||
@@ -16,6 +24,7 @@ export const EventPattern = <T = string>(metadata?: T): MethodDecorator => {
|
||||
PatternHandler.EVENT,
|
||||
descriptor.value,
|
||||
);
|
||||
Reflect.defineMetadata(TRANSPORT_METADATA, transport, descriptor.value);
|
||||
return descriptor;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-use-before-define */
|
||||
import { PATTERN_HANDLER_METADATA, PATTERN_METADATA } from '../constants';
|
||||
import {
|
||||
PATTERN_HANDLER_METADATA,
|
||||
PATTERN_METADATA,
|
||||
TRANSPORT_METADATA,
|
||||
} from '../constants';
|
||||
import { PatternHandler } from '../enums/pattern-handler.enum';
|
||||
import { PatternMetadata } from '../interfaces/pattern-metadata.interface';
|
||||
import { Transport } from '../enums';
|
||||
|
||||
export enum GrpcMethodStreamingType {
|
||||
NO_STREAMING = 'no_stream',
|
||||
@@ -14,6 +19,7 @@ export enum GrpcMethodStreamingType {
|
||||
*/
|
||||
export const MessagePattern = <T = PatternMetadata | string>(
|
||||
metadata?: T,
|
||||
transport?: Transport,
|
||||
): MethodDecorator => {
|
||||
return (
|
||||
target: object,
|
||||
@@ -26,6 +32,7 @@ export const MessagePattern = <T = PatternMetadata | string>(
|
||||
PatternHandler.MESSAGE,
|
||||
descriptor.value,
|
||||
);
|
||||
Reflect.defineMetadata(TRANSPORT_METADATA, transport, descriptor.value);
|
||||
return descriptor;
|
||||
};
|
||||
};
|
||||
@@ -42,7 +49,7 @@ export function GrpcMethod(service: string, method?: string): MethodDecorator {
|
||||
descriptor: PropertyDescriptor,
|
||||
) => {
|
||||
const metadata = createGrpcMethodMetadata(target, key, service, method);
|
||||
return MessagePattern(metadata)(target, key, descriptor);
|
||||
return MessagePattern(metadata, Transport.GRPC)(target, key, descriptor);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -76,7 +83,7 @@ export function GrpcStreamMethod(
|
||||
method,
|
||||
GrpcMethodStreamingType.RX_STREAMING,
|
||||
);
|
||||
return MessagePattern(metadata)(target, key, descriptor);
|
||||
return MessagePattern(metadata, Transport.GRPC)(target, key, descriptor);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -110,7 +117,7 @@ export function GrpcStreamCall(
|
||||
method,
|
||||
GrpcMethodStreamingType.PT_STREAMING,
|
||||
);
|
||||
return MessagePattern(metadata)(target, key, descriptor);
|
||||
return MessagePattern(metadata, Transport.GRPC)(target, key, descriptor);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
16
packages/microservices/external/grpc-options.interface.ts
vendored
Normal file
16
packages/microservices/external/grpc-options.interface.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* An interface that contains options used when initializing a Channel instance.
|
||||
* This listing is incomplete. Full reference: https://grpc.github.io/grpc/core/group__grpc__arg__keys.html
|
||||
*/
|
||||
export interface ChannelOptions {
|
||||
'grpc.ssl_target_name_override'?: string;
|
||||
'grpc.primary_user_agent'?: string;
|
||||
'grpc.secondary_user_agent'?: string;
|
||||
'grpc.default_authority'?: string;
|
||||
'grpc.service_config'?: string;
|
||||
'grpc.max_concurrent_streams'?: number;
|
||||
'grpc.initial_reconnect_backoff_ms'?: number;
|
||||
'grpc.max_reconnect_backoff_ms'?: number;
|
||||
'grpc.use_local_subchannel_pool'?: number;
|
||||
[key: string]: string | number | undefined;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/adjacent-overload-signatures */
|
||||
/* eslint-disable @typescript-eslint/camelcase */
|
||||
|
||||
export interface RetryStrategyOptions {
|
||||
error: Error;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Transport } from '../enums';
|
||||
|
||||
export interface CustomTransportStrategy {
|
||||
readonly transportId?: Transport;
|
||||
listen(callback: () => void): any;
|
||||
close(): any;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Transport } from '../enums/transport.enum';
|
||||
import { ChannelOptions } from '../external/grpc-options.interface';
|
||||
import {
|
||||
CompressionTypes,
|
||||
ConsumerConfig,
|
||||
@@ -32,6 +33,7 @@ export interface GrpcOptions {
|
||||
url?: string;
|
||||
maxSendMessageLength?: number;
|
||||
maxReceiveMessageLength?: number;
|
||||
maxMetadataSize?: number;
|
||||
keepalive?: {
|
||||
keepaliveTimeMs?: number;
|
||||
keepaliveTimeoutMs?: number;
|
||||
@@ -41,6 +43,7 @@ export interface GrpcOptions {
|
||||
http2MinPingIntervalWithoutDataMs?: number;
|
||||
http2MaxPingStrikes?: number;
|
||||
};
|
||||
channelOptions?: ChannelOptions;
|
||||
credentials?: any;
|
||||
protoPath: string | string[];
|
||||
package: string | string[];
|
||||
|
||||
@@ -6,7 +6,9 @@ import {
|
||||
CLIENT_METADATA,
|
||||
PATTERN_HANDLER_METADATA,
|
||||
PATTERN_METADATA,
|
||||
TRANSPORT_METADATA,
|
||||
} from './constants';
|
||||
import { Transport } from './enums';
|
||||
import { PatternHandler } from './enums/pattern-handler.enum';
|
||||
import { ClientOptions } from './interfaces/client-metadata.interface';
|
||||
import { PatternMetadata } from './interfaces/pattern-metadata.interface';
|
||||
@@ -21,6 +23,7 @@ export interface PatternProperties {
|
||||
methodKey: string;
|
||||
isEventHandler: boolean;
|
||||
targetCallback: (...args: any[]) => any;
|
||||
transport?: Transport;
|
||||
}
|
||||
|
||||
export interface MessageRequestProperties {
|
||||
@@ -54,10 +57,12 @@ export class ListenerMetadataExplorer {
|
||||
return;
|
||||
}
|
||||
const pattern = Reflect.getMetadata(PATTERN_METADATA, targetCallback);
|
||||
const transport = Reflect.getMetadata(TRANSPORT_METADATA, targetCallback);
|
||||
return {
|
||||
methodKey,
|
||||
targetCallback,
|
||||
pattern,
|
||||
transport,
|
||||
isEventHandler: handlerType === PatternHandler.EVENT,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common/interfaces';
|
||||
import { Controller } from '@nestjs/common/interfaces/controllers/controller.interface';
|
||||
import { isUndefined } from '@nestjs/common/utils/shared.utils';
|
||||
import { ContextIdFactory } from '@nestjs/core/helpers/context-id-factory';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
||||
import { STATIC_CONTEXT } from '@nestjs/core/injector/constants';
|
||||
@@ -61,31 +62,38 @@ export class ListenersController {
|
||||
? DEFAULT_GRPC_CALLBACK_METADATA
|
||||
: DEFAULT_CALLBACK_METADATA;
|
||||
|
||||
patternHandlers.forEach(
|
||||
({ pattern, targetCallback, methodKey, isEventHandler }) => {
|
||||
if (isStatic) {
|
||||
const proxy = this.contextCreator.create(
|
||||
instance as object,
|
||||
targetCallback,
|
||||
patternHandlers
|
||||
.filter(
|
||||
({ transport }) =>
|
||||
isUndefined(transport) ||
|
||||
isUndefined(server.transportId) ||
|
||||
transport === server.transportId,
|
||||
)
|
||||
.forEach(
|
||||
({ pattern, targetCallback, methodKey, transport, isEventHandler }) => {
|
||||
if (isStatic) {
|
||||
const proxy = this.contextCreator.create(
|
||||
instance as object,
|
||||
targetCallback,
|
||||
moduleKey,
|
||||
methodKey,
|
||||
STATIC_CONTEXT,
|
||||
undefined,
|
||||
defaultCallMetadata,
|
||||
);
|
||||
return server.addHandler(pattern, proxy, isEventHandler);
|
||||
}
|
||||
const asyncHandler = this.createRequestScopedHandler(
|
||||
instanceWrapper,
|
||||
pattern,
|
||||
moduleRef,
|
||||
moduleKey,
|
||||
methodKey,
|
||||
STATIC_CONTEXT,
|
||||
undefined,
|
||||
defaultCallMetadata,
|
||||
);
|
||||
return server.addHandler(pattern, proxy, isEventHandler);
|
||||
}
|
||||
const asyncHandler = this.createRequestScopedHandler(
|
||||
instanceWrapper,
|
||||
pattern,
|
||||
moduleRef,
|
||||
moduleKey,
|
||||
methodKey,
|
||||
defaultCallMetadata,
|
||||
);
|
||||
server.addHandler(pattern, asyncHandler, isEventHandler);
|
||||
},
|
||||
);
|
||||
server.addHandler(pattern, asyncHandler, isEventHandler);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
public assignClientsToProperties(instance: Controller | Injectable) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/microservices",
|
||||
"version": "7.0.5",
|
||||
"version": "7.1.3",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@microservices)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
@@ -19,7 +19,11 @@
|
||||
"dependencies": {
|
||||
"iterare": "1.2.0",
|
||||
"json-socket": "0.3.0",
|
||||
"tslib": "1.11.1"
|
||||
"tslib": "2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/common": "7.1.3",
|
||||
"@nestjs/core": "7.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^7.0.0",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user