mirror of
https://github.com/nestjs/nest.git
synced 2026-02-23 15:52:50 +00:00
Compare commits
811 Commits
Dominic-Pr
...
v7.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
087e2cbfa5 | ||
|
|
eea77b720a | ||
|
|
1ade11ede4 | ||
|
|
b93812978c | ||
|
|
306adfb751 | ||
|
|
0241aa0a68 | ||
|
|
9bbe4f11a1 | ||
|
|
7ecc6c7807 | ||
|
|
5dd7abb9fc | ||
|
|
ffb36a9309 | ||
|
|
44bbff494c | ||
|
|
04d76ff4b2 | ||
|
|
ea70f0348c | ||
|
|
3f52b32a75 | ||
|
|
54434e75c5 | ||
|
|
26f1ffa265 | ||
|
|
08d29be41d | ||
|
|
5cbe2404dc | ||
|
|
c4f94df2e7 | ||
|
|
b10b60a9b3 | ||
|
|
b9007dd93f | ||
|
|
9d5bc076ff | ||
|
|
50c709c4d5 | ||
|
|
2166af0eeb | ||
|
|
c91317e91c | ||
|
|
5ec7246c18 | ||
|
|
6df9595b35 | ||
|
|
7b5e05e082 | ||
|
|
c09028c60d | ||
|
|
b013847b42 | ||
|
|
fc37771375 | ||
|
|
a749e0c53e | ||
|
|
2625b8829d | ||
|
|
6eda39b8f4 | ||
|
|
4c816a7efd | ||
|
|
9ac0cf1457 | ||
|
|
645075750e | ||
|
|
f0e7597635 | ||
|
|
77e2f395e7 | ||
|
|
7a749add7c | ||
|
|
c420898cfa | ||
|
|
0006d31ffe | ||
|
|
752033edac | ||
|
|
63d0693360 | ||
|
|
443da3993b | ||
|
|
1e3b5f5f51 | ||
|
|
74f89a737f | ||
|
|
33912c8613 | ||
|
|
f6982aa2a3 | ||
|
|
c56b1ae778 | ||
|
|
c516135c9a | ||
|
|
aa93a69158 | ||
|
|
0a3a36377b | ||
|
|
1901a623ac | ||
|
|
046ff4ccb4 | ||
|
|
a794f27dd1 | ||
|
|
1796109481 | ||
|
|
936aae57e0 | ||
|
|
e4c7628e6b | ||
|
|
3c235176ed | ||
|
|
288b01a774 | ||
|
|
0fc2270092 | ||
|
|
61ab2053de | ||
|
|
9023429dce | ||
|
|
402b916d84 | ||
|
|
cbd3971c7d | ||
|
|
1b43bac548 | ||
|
|
5f2027dc1e | ||
|
|
a93d7d5d18 | ||
|
|
c23afcd643 | ||
|
|
91d5c6ec53 | ||
|
|
646fd79d67 | ||
|
|
c120503e46 | ||
|
|
ff86d1bd22 | ||
|
|
89d8592388 | ||
|
|
e277b3bc3a | ||
|
|
535755c925 | ||
|
|
fdd3da241a | ||
|
|
c3d2add2de | ||
|
|
85b0dc8495 | ||
|
|
f21ebbc346 | ||
|
|
b39cf9f210 | ||
|
|
be50e6d834 | ||
|
|
c2fec010dc | ||
|
|
a792e88989 | ||
|
|
9cc3b149f4 | ||
|
|
6f49c76f04 | ||
|
|
0f7f413931 | ||
|
|
c5b4e78a34 | ||
|
|
75545d132e | ||
|
|
e79941fae2 | ||
|
|
50a7d35f4c | ||
|
|
b9242f7283 | ||
|
|
4307da2f92 | ||
|
|
833f0f5532 | ||
|
|
c970b50291 | ||
|
|
9f80222fdc | ||
|
|
cbf2c3cf42 | ||
|
|
6cf9db941b | ||
|
|
5a8b7bc3a0 | ||
|
|
ee01e60a95 | ||
|
|
79ea8d2a79 | ||
|
|
9b54ccb229 | ||
|
|
ebe8255379 | ||
|
|
46db75dd79 | ||
|
|
070df5a52b | ||
|
|
445b296b76 | ||
|
|
408aa39c2e | ||
|
|
3a2ee4296a | ||
|
|
42a84cda48 | ||
|
|
6e84e2f425 | ||
|
|
58ca5771a1 | ||
|
|
4da3b5812a | ||
|
|
44c8555402 | ||
|
|
7e9fcc231d | ||
|
|
0fb9778dad | ||
|
|
c1467c5c16 | ||
|
|
77d9a9b9c4 | ||
|
|
0b91ce9da8 | ||
|
|
2af96b578e | ||
|
|
7fa5082fa6 | ||
|
|
75216f4794 | ||
|
|
614ed4ff2f | ||
|
|
71cc3c0f8f | ||
|
|
92eef778e6 | ||
|
|
8c59111764 | ||
|
|
668e7bc80f | ||
|
|
a839a07713 | ||
|
|
82021d876b | ||
|
|
02baf5eac9 | ||
|
|
42683d5008 | ||
|
|
7fbe1f2c3f | ||
|
|
d355bc8464 | ||
|
|
f11455d9c9 | ||
|
|
d233c2fb35 | ||
|
|
ac2827b090 | ||
|
|
f81d65af58 | ||
|
|
285b14e127 | ||
|
|
cc85f59869 | ||
|
|
3bc77d2c50 | ||
|
|
e158fbed4b | ||
|
|
0d2870aeb6 | ||
|
|
54863260c8 | ||
|
|
80b11f1cd8 | ||
|
|
e3adee8249 | ||
|
|
be20f8f1bc | ||
|
|
39e0024a96 | ||
|
|
a3cdf2ad05 | ||
|
|
52acdc5ae3 | ||
|
|
d071c99a9c | ||
|
|
b85844c316 | ||
|
|
daeb99f858 | ||
|
|
3dca681a08 | ||
|
|
442e2f66a1 | ||
|
|
659c52f8bc | ||
|
|
835e4ed94d | ||
|
|
5642ca7f5f | ||
|
|
d9cfdd0846 | ||
|
|
83251e3a45 | ||
|
|
eef373c93f | ||
|
|
83b3b6d9d5 | ||
|
|
0ac56fd62a | ||
|
|
8b97f94c38 | ||
|
|
b5a7a6cda4 | ||
|
|
2ce40138b9 | ||
|
|
53070f07f3 | ||
|
|
60c92fe5a8 | ||
|
|
c51984e440 | ||
|
|
05cc5b83f8 | ||
|
|
28fcfe6647 | ||
|
|
2fc29847f3 | ||
|
|
1c846e46f9 | ||
|
|
42fedbb81b | ||
|
|
7f2c35174d | ||
|
|
1931283e75 | ||
|
|
dcabde2fb5 | ||
|
|
b4f3cdc73b | ||
|
|
32ed43b390 | ||
|
|
9d781cde22 | ||
|
|
aed62cc7bd | ||
|
|
a4252b4285 | ||
|
|
171cc9d392 | ||
|
|
64b7237c23 | ||
|
|
c97e495c11 | ||
|
|
1534cf5d0a | ||
|
|
8436c27a65 | ||
|
|
ab0b29b627 | ||
|
|
6d034f5c1f | ||
|
|
1ddab1b2bb | ||
|
|
364a963426 | ||
|
|
72ff7ae40e | ||
|
|
184fffe043 | ||
|
|
d92e85f297 | ||
|
|
acce5e953d | ||
|
|
dfd7846779 | ||
|
|
d895c367fe | ||
|
|
b9cf26708e | ||
|
|
18979b3b46 | ||
|
|
877cc3e4d6 | ||
|
|
feeed43c56 | ||
|
|
a10bb787b8 | ||
|
|
ca7e348478 | ||
|
|
67e15afbf3 | ||
|
|
d59c7b87bd | ||
|
|
cf5e5bceca | ||
|
|
b5e8197500 | ||
|
|
17969748e9 | ||
|
|
8836251c88 | ||
|
|
d74e173e59 | ||
|
|
9511984f2c | ||
|
|
eb7decd35c | ||
|
|
cd6555edd3 | ||
|
|
5b15dbfbaa | ||
|
|
8dcced7256 | ||
|
|
cf10752f40 | ||
|
|
1789753248 | ||
|
|
fb62ffe48c | ||
|
|
f38223ade4 | ||
|
|
f95b219a3c | ||
|
|
b4adc2c771 | ||
|
|
d56267fb99 | ||
|
|
2971ba6a3c | ||
|
|
f7e4885c2d | ||
|
|
cce0bc6407 | ||
|
|
ed4becbe99 | ||
|
|
3cac1ca2b2 | ||
|
|
9059c3d5ac | ||
|
|
3433ab06f4 | ||
|
|
55bd8ab1c7 | ||
|
|
2e5eb4b8e9 | ||
|
|
59ad650c8f | ||
|
|
764d26b28e | ||
|
|
2f29d7790b | ||
|
|
6a53d1e131 | ||
|
|
d37f68d05e | ||
|
|
72c16ccd9a | ||
|
|
3f9be96815 | ||
|
|
7bcf90cb8c | ||
|
|
f4f85f254c | ||
|
|
fc625f2957 | ||
|
|
61b6c7df25 | ||
|
|
79caf671ea | ||
|
|
e24f23d030 | ||
|
|
d184f906ff | ||
|
|
7efc27c186 | ||
|
|
27de22fdfb | ||
|
|
2342c65efc | ||
|
|
7d03b2d78d | ||
|
|
c0e6139419 | ||
|
|
f574663414 | ||
|
|
7f3a9955be | ||
|
|
cc828d493a | ||
|
|
dda9d52c4c | ||
|
|
1194fbae9f | ||
|
|
b3542806d8 | ||
|
|
3991fb9525 | ||
|
|
91cf3190b3 | ||
|
|
1ddeaa2aad | ||
|
|
2bdcdb5814 | ||
|
|
bb4ed69b0e | ||
|
|
b69f3be0aa | ||
|
|
96136f72e7 | ||
|
|
724403067b | ||
|
|
12b807f149 | ||
|
|
07237a07c7 | ||
|
|
0e7197c828 | ||
|
|
7c64c77031 | ||
|
|
6e2033ba7e | ||
|
|
ba2bdea524 | ||
|
|
12b9940b65 | ||
|
|
8a02e39001 | ||
|
|
b82f6f6b5b | ||
|
|
6a754515a1 | ||
|
|
517cad59e9 | ||
|
|
2714f57fcf | ||
|
|
e91d1b1ced | ||
|
|
6e49223e4c | ||
|
|
0b33f8fe31 | ||
|
|
b315162269 | ||
|
|
cd21b2ff93 | ||
|
|
4a1eed1e44 | ||
|
|
09a3ad2950 | ||
|
|
5e9d5c57ed | ||
|
|
d7c69707f6 | ||
|
|
f6114abea9 | ||
|
|
33d6c393e0 | ||
|
|
97edbe0c2a | ||
|
|
2e9fcb1a42 | ||
|
|
2581903818 | ||
|
|
ab4295ed02 | ||
|
|
f05786d8c1 | ||
|
|
772e589327 | ||
|
|
82d0171bcf | ||
|
|
706f55488c | ||
|
|
adce6c8ec0 | ||
|
|
7d209e3d8f | ||
|
|
a718809f30 | ||
|
|
25ba42cca2 | ||
|
|
eb1828431d | ||
|
|
038beb0c54 | ||
|
|
2a7bf6a357 | ||
|
|
d03830e998 | ||
|
|
dc35501e74 | ||
|
|
570dc303c9 | ||
|
|
300f0e39a5 | ||
|
|
c76e12eb17 | ||
|
|
5ca6a1e794 | ||
|
|
eb8f58a624 | ||
|
|
9e7c610ebe | ||
|
|
f17d29bf99 | ||
|
|
59c6cebb22 | ||
|
|
75e1e71e10 | ||
|
|
d4f93cd909 | ||
|
|
b5d553604d | ||
|
|
41764370eb | ||
|
|
e14670ee16 | ||
|
|
1c4ab360e3 | ||
|
|
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 | ||
|
|
f8e788a929 | ||
|
|
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 |
@@ -70,10 +70,10 @@ jobs:
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
|
||||
test_node_8:
|
||||
test_node_14:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
- image: circleci/node:14
|
||||
|
||||
lint:
|
||||
working_directory: ~/nest
|
||||
@@ -160,15 +160,15 @@ workflows:
|
||||
build-and-test:
|
||||
jobs:
|
||||
- build
|
||||
- test_node_14:
|
||||
requires:
|
||||
- build
|
||||
- test_node_12:
|
||||
requires:
|
||||
- build
|
||||
- test_node_10:
|
||||
requires:
|
||||
- build
|
||||
- test_node_8:
|
||||
requires:
|
||||
- build
|
||||
- lint:
|
||||
requires:
|
||||
- build
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
|
||||
54
.github/workflows/codeql-analysis.yml
vendored
Normal file
54
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 17 * * 4'
|
||||
|
||||
jobs:
|
||||
analyse:
|
||||
name: Analyse
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
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
|
||||
|
||||
61
Readme.md
61
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).
|
||||
@@ -46,32 +54,43 @@ With official support, you can get expert help straight from Nest core team. We
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
#### Principal Sponsors
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td><td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="180" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### 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>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr>
|
||||
<tr><td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ services:
|
||||
- "9001:9001"
|
||||
restart: always
|
||||
mysql:
|
||||
image: mysql:5.7.29
|
||||
image: mysql:5.7.31
|
||||
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!
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { fail } from 'assert';
|
||||
import { expect } from 'chai';
|
||||
import { ApplicationModule } from '../src/app.module';
|
||||
|
||||
@@ -54,19 +53,14 @@ describe('Hello world (fastify adapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/host',
|
||||
})
|
||||
.then(
|
||||
({ payload }) => {
|
||||
fail(`Unexpected success: ${payload}`);
|
||||
},
|
||||
err => {
|
||||
expect(err.getResponse()).to.be.eql({
|
||||
error: 'Internal Server Error',
|
||||
message:
|
||||
'HTTP adapter does not support filtering on host: ":tenant.example.com"',
|
||||
statusCode: 500,
|
||||
});
|
||||
},
|
||||
);
|
||||
.then(({ payload }) => {
|
||||
expect(JSON.parse(payload)).to.be.eql({
|
||||
error: 'Internal Server Error',
|
||||
message:
|
||||
'HTTP adapter does not support filtering on host: ":tenant.example.com"',
|
||||
statusCode: 500,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
34
integration/injector/e2e/introspection.spec.ts
Normal file
34
integration/injector/e2e/introspection.spec.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { ScopedModule, STATIC_FACTORY } from '../src/scoped/scoped.module';
|
||||
import { ScopedService } from '../src/scoped/scoped.service';
|
||||
import { TransientService } from '../src/scoped/transient.service';
|
||||
|
||||
describe('Providers introspection', () => {
|
||||
let testingModule: TestingModule;
|
||||
let moduleRef: ModuleRef;
|
||||
|
||||
beforeEach(async () => {
|
||||
testingModule = await Test.createTestingModule({
|
||||
imports: [ScopedModule],
|
||||
}).compile();
|
||||
moduleRef = testingModule.get(ModuleRef);
|
||||
});
|
||||
|
||||
it('should properly introspect a transient provider', async () => {
|
||||
const introspectionResult = moduleRef.introspect(TransientService);
|
||||
expect(introspectionResult.scope).to.be.equal(Scope.TRANSIENT);
|
||||
});
|
||||
|
||||
it('should properly introspect a singleton provider', async () => {
|
||||
const introspectionResult = moduleRef.introspect(STATIC_FACTORY);
|
||||
expect(introspectionResult.scope).to.be.equal(Scope.DEFAULT);
|
||||
});
|
||||
|
||||
it('should properly introspect a request-scoped provider', async () => {
|
||||
const introspectionResult = moduleRef.introspect(ScopedService);
|
||||
expect(introspectionResult.scope).to.be.equal(Scope.REQUEST);
|
||||
});
|
||||
});
|
||||
@@ -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,47 @@ 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 ctxId = { id: 1 };
|
||||
const requestProvider = { host: 'localhost' };
|
||||
testingModule.registerRequestByContextId(requestProvider, ctxId);
|
||||
|
||||
const request3 = await testingModule.resolve(ScopedService, ctxId);
|
||||
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;
|
||||
expect(request3.request).to.be.equal(requestProvider);
|
||||
});
|
||||
|
||||
it('should dynamically resolve request-scoped controller', async () => {
|
||||
@@ -46,11 +79,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 {}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
import { REQUEST } from '@nestjs/core';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class ScopedService {}
|
||||
export class ScopedService {
|
||||
constructor(@Inject(REQUEST) public readonly request) {}
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
|
||||
@@ -50,6 +50,20 @@ describe('RPC transport', () => {
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (useFactory client)`, () => {
|
||||
return request(server)
|
||||
.post('/useFactory?command=sum')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (useClass client)`, () => {
|
||||
return request(server)
|
||||
.post('/useClass?command=sum')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (concurrent)`, () => {
|
||||
return request(server)
|
||||
.post('/concurrent')
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { Body, Controller, HttpCode, Post, Query } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
HttpCode,
|
||||
Inject,
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
Client,
|
||||
ClientProxy,
|
||||
@@ -11,6 +18,10 @@ import { scan } from 'rxjs/operators';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(
|
||||
@Inject('USE_CLASS_CLIENT') private useClassClient: ClientProxy,
|
||||
@Inject('USE_FACTORY_CLIENT') private useFactoryClient: ClientProxy,
|
||||
) {}
|
||||
static IS_NOTIFIED = false;
|
||||
|
||||
@Client({ transport: Transport.TCP })
|
||||
@@ -22,6 +33,24 @@ export class AppController {
|
||||
return this.client.send<number>({ cmd }, data);
|
||||
}
|
||||
|
||||
@Post('useFactory')
|
||||
@HttpCode(200)
|
||||
callWithClientUseFactory(
|
||||
@Query('command') cmd,
|
||||
@Body() data: number[],
|
||||
): Observable<number> {
|
||||
return this.useFactoryClient.send<number>({ cmd }, data);
|
||||
}
|
||||
|
||||
@Post('useClass')
|
||||
@HttpCode(200)
|
||||
callWithClientUseClass(
|
||||
@Query('command') cmd,
|
||||
@Body() data: number[],
|
||||
): Observable<number> {
|
||||
return this.useClassClient.send<number>({ cmd }, data);
|
||||
}
|
||||
|
||||
@Post('stream')
|
||||
@HttpCode(200)
|
||||
stream(@Body() data: number[]): Observable<number> {
|
||||
|
||||
@@ -1,7 +1,59 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { Module, Injectable } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import {
|
||||
ClientsModule,
|
||||
Transport,
|
||||
ClientsModuleOptionsFactory,
|
||||
ClientOptions,
|
||||
} from '@nestjs/microservices';
|
||||
|
||||
@Injectable()
|
||||
class ConfigService {
|
||||
private readonly config = {
|
||||
transport: Transport.TCP,
|
||||
};
|
||||
get(key: string) {
|
||||
return this.config[key];
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [ConfigService],
|
||||
exports: [ConfigService],
|
||||
})
|
||||
class ConfigModule {}
|
||||
|
||||
@Injectable()
|
||||
class ClientOptionService implements ClientsModuleOptionsFactory {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
createClientOptions(): Promise<ClientOptions> | ClientOptions {
|
||||
return {
|
||||
transport: this.configService.get('transport'),
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ClientsModule.registerAsync([
|
||||
{
|
||||
imports: [ConfigModule],
|
||||
name: 'USE_FACTORY_CLIENT',
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
transport: configService.get('transport'),
|
||||
options: {},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
},
|
||||
{
|
||||
imports: [ConfigModule],
|
||||
name: 'USE_CLASS_CLIENT',
|
||||
useClass: ClientOptionService,
|
||||
inject: [ConfigService],
|
||||
},
|
||||
]),
|
||||
],
|
||||
controllers: [AppController],
|
||||
})
|
||||
export class ApplicationModule {}
|
||||
|
||||
@@ -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.4.2"
|
||||
}
|
||||
|
||||
11548
package-lock.json
generated
11548
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
139
package.json
139
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "7.0.3",
|
||||
"version": "7.4.0",
|
||||
"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'",
|
||||
@@ -53,116 +53,117 @@
|
||||
"dependencies": {
|
||||
"@nuxtjs/opencollective": "0.2.2",
|
||||
"axios": "0.19.2",
|
||||
"class-transformer": "0.2.3",
|
||||
"class-validator": "0.11.1",
|
||||
"class-transformer": "0.3.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.2.3",
|
||||
"fast-safe-stringify": "2.0.7",
|
||||
"iterare": "1.2.0",
|
||||
"iterare": "1.2.1",
|
||||
"object-hash": "2.0.3",
|
||||
"path-to-regexp": "3.2.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.5.4",
|
||||
"rxjs": "6.6.0",
|
||||
"socket.io": "2.3.0",
|
||||
"uuid": "7.0.2",
|
||||
"tslib": "1.11.1"
|
||||
"uuid": "8.3.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",
|
||||
"@nestjs/typeorm": "7.0.0",
|
||||
"@commitlint/cli": "9.1.1",
|
||||
"@commitlint/config-angular": "9.1.1",
|
||||
"@grpc/proto-loader": "0.5.5",
|
||||
"@nestjs/graphql": "7.6.0",
|
||||
"@nestjs/mongoose": "7.0.2",
|
||||
"@nestjs/typeorm": "7.1.0",
|
||||
"@types/amqplib": "0.5.13",
|
||||
"@types/bytes": "3.1.0",
|
||||
"@types/cache-manager": "2.10.2",
|
||||
"@types/chai": "4.2.10",
|
||||
"@types/chai-as-promised": "7.1.2",
|
||||
"@types/cache-manager": "2.10.3",
|
||||
"@types/chai": "4.2.12",
|
||||
"@types/chai-as-promised": "7.1.3",
|
||||
"@types/cors": "2.8.6",
|
||||
"@types/express": "4.17.3",
|
||||
"@types/fastify-cors": "2.1.0",
|
||||
"@types/express": "4.17.7",
|
||||
"@types/gulp": "4.0.6",
|
||||
"@types/kafka-node": "2.0.9",
|
||||
"@types/mocha": "7.0.2",
|
||||
"@types/mongoose": "5.7.7",
|
||||
"@types/mocha": "8.0.0",
|
||||
"@types/mongoose": "5.7.34",
|
||||
"@types/node": "10.17.3",
|
||||
"@types/redis": "2.8.16",
|
||||
"@types/redis": "2.8.25",
|
||||
"@types/reflect-metadata": "0.0.5",
|
||||
"@types/sinon": "7.5.2",
|
||||
"@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/sinon": "9.0.4",
|
||||
"@types/socket.io": "2.1.10",
|
||||
"@types/ws": "7.2.6",
|
||||
"@typescript-eslint/eslint-plugin": "3.7.1",
|
||||
"@typescript-eslint/parser": "3.7.1",
|
||||
"amqp-connection-manager": "3.2.0",
|
||||
"amqplib": "0.5.5",
|
||||
"apollo-server-express": "2.11.0",
|
||||
"artillery": "1.6.0",
|
||||
"amqplib": "0.6.0",
|
||||
"apollo-server-express": "2.16.1",
|
||||
"artillery": "1.6.1",
|
||||
"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.3",
|
||||
"eslint": "7.5.0",
|
||||
"eslint-config-prettier": "6.11.0",
|
||||
"eslint-plugin-import": "2.22.0",
|
||||
"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",
|
||||
"grpc": "1.24.2",
|
||||
"fastify": "3.1.1",
|
||||
"fastify-cors": "4.1.0",
|
||||
"fastify-formbody": "4.0.3",
|
||||
"fastify-multipart": "2.0.3",
|
||||
"fastify-static": "3.2.0",
|
||||
"graphql": "15.3.0",
|
||||
"graphql-tools": "6.0.15",
|
||||
"grpc": "1.24.3",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-clang-format": "1.0.27",
|
||||
"gulp-clean": "0.4.0",
|
||||
"gulp-sourcemaps": "2.6.5",
|
||||
"gulp-typescript": "5.0.1",
|
||||
"gulp-watch": "5.0.1",
|
||||
"husky": "4.2.3",
|
||||
"imports-loader": "0.8.0",
|
||||
"husky": "4.2.5",
|
||||
"imports-loader": "1.1.0",
|
||||
"json-loader": "0.5.7",
|
||||
"kafkajs": "1.12.0",
|
||||
"lerna": "2.11.0",
|
||||
"lint-staged": "10.0.9",
|
||||
"light-my-request": "4.0.1",
|
||||
"lint-staged": "10.2.11",
|
||||
"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",
|
||||
"middie": "5.1.0",
|
||||
"mocha": "8.0.1",
|
||||
"mongoose": "5.9.26",
|
||||
"mqtt": "4.1.0",
|
||||
"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.4",
|
||||
"nyc": "15.1.0",
|
||||
"point-of-view": "4.2.0",
|
||||
"prettier": "2.0.5",
|
||||
"redis": "3.0.2",
|
||||
"rxjs-compat": "6.5.4",
|
||||
"sinon": "9.0.1",
|
||||
"rxjs-compat": "6.6.0",
|
||||
"sinon": "9.0.2",
|
||||
"sinon-chai": "3.5.0",
|
||||
"socket.io-client": "2.3.0",
|
||||
"subscriptions-transport-ws": "0.9.16",
|
||||
"subscriptions-transport-ws": "0.9.17",
|
||||
"supertest": "4.0.2",
|
||||
"ts-morph": "7.0.0",
|
||||
"ts-node": "8.8.1",
|
||||
"typeorm": "0.2.24",
|
||||
"typescript": "3.7.2",
|
||||
"ts-morph": "7.1.3",
|
||||
"ts-node": "8.10.2",
|
||||
"typeorm": "0.2.25",
|
||||
"typescript": "3.9.7",
|
||||
"wrk": "1.2.0",
|
||||
"ws": "7.2.3"
|
||||
"ws": "7.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
@@ -193,7 +194,7 @@
|
||||
"packages/microservices/microservices-module.ts",
|
||||
"packages/core/middleware/middleware-module.ts",
|
||||
"packages/core/injector/module-ref.ts",
|
||||
"packages/core/injector/container-scanner.ts",
|
||||
"packages/core/injector/instance-links-host.ts",
|
||||
"packages/common/cache/**/*",
|
||||
"packages/common/serializer/**/*",
|
||||
"packages/common/services/logger.service.ts"
|
||||
|
||||
@@ -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).
|
||||
@@ -46,32 +54,43 @@ With official support, you can get expert help straight from Nest core team. We
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
#### Principal Sponsors
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td><td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="180" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### 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>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr>
|
||||
<tr><td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
HttpServer,
|
||||
NestInterceptor,
|
||||
} from '../../interfaces';
|
||||
import { isNil } from '../../utils/shared.utils';
|
||||
import {
|
||||
CACHE_KEY_METADATA,
|
||||
CACHE_MANAGER,
|
||||
@@ -44,13 +45,13 @@ export class CacheInterceptor implements NestInterceptor {
|
||||
}
|
||||
try {
|
||||
const value = await this.cacheManager.get(key);
|
||||
if (value) {
|
||||
if (!isNil(value)) {
|
||||
return of(value);
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -13,7 +13,9 @@ export * from './unsupported-media-type.exception';
|
||||
export * from './unprocessable-entity.exception';
|
||||
export * from './internal-server-error.exception';
|
||||
export * from './not-implemented.exception';
|
||||
export * from './http-version-not-supported.exception';
|
||||
export * from './bad-gateway.exception';
|
||||
export * from './service-unavailable.exception';
|
||||
export * from './gateway-timeout.exception';
|
||||
export * from './im-a-teapot.exception';
|
||||
export * from './precondition-failed.exception';
|
||||
|
||||
49
packages/common/exceptions/precondition-failed.exception.ts
Normal file
49
packages/common/exceptions/precondition-failed.exception.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { HttpStatus } from '../enums/http-status.enum';
|
||||
import { HttpException } from './http.exception';
|
||||
|
||||
/**
|
||||
* Defines an HTTP exception for *Precondition Failed* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export class PreconditionFailedException extends HttpException {
|
||||
/**
|
||||
* Instantiate a `PreconditionFailedException` Exception.
|
||||
*
|
||||
* @example
|
||||
* `throw new PreconditionFailedException()`
|
||||
*
|
||||
* @usageNotes
|
||||
* The HTTP response status code will be 412.
|
||||
* - The `objectOrError` argument defines the JSON response body or the message string.
|
||||
* - The `description` argument contains a short description of the HTTP error.
|
||||
*
|
||||
* By default, the JSON response body contains two properties:
|
||||
* - `statusCode`: this will be the value 412.
|
||||
* - `message`: the string `'Precondition Failed'` by default; override this by supplying
|
||||
* a string in the `objectOrError` parameter.
|
||||
*
|
||||
* If the parameter `objectOrError` is a string, the response body will contain an
|
||||
* additional property, `error`, with a short description of the HTTP error. To override the
|
||||
* entire JSON response body, pass an object instead. Nest will serialize the object
|
||||
* and return it as the JSON response body.
|
||||
*
|
||||
* @param objectOrError string or object describing the error condition.
|
||||
* @param description a short description of the HTTP error.
|
||||
*/
|
||||
constructor(
|
||||
objectOrError?: string | object | any,
|
||||
description = 'Precondition Failed',
|
||||
) {
|
||||
super(
|
||||
HttpException.createBody(
|
||||
objectOrError,
|
||||
description,
|
||||
HttpStatus.PRECONDITION_FAILED,
|
||||
),
|
||||
HttpStatus.PRECONDITION_FAILED,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,8 +30,11 @@ export {
|
||||
INestApplication,
|
||||
INestApplicationContext,
|
||||
INestMicroservice,
|
||||
IntrospectionResult,
|
||||
MiddlewareConsumer,
|
||||
ModuleMetadata,
|
||||
NestApplicationOptions,
|
||||
NestHybridApplicationOptions,
|
||||
NestInterceptor,
|
||||
NestMiddleware,
|
||||
NestModule,
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -54,7 +54,9 @@ export interface HttpServer<TRequest = any, TResponse = any> {
|
||||
setViewEngine?(engineOrOptions: any): this;
|
||||
createMiddlewareFactory(
|
||||
method: RequestMethod,
|
||||
): (path: string, callback: Function) => any;
|
||||
):
|
||||
| ((path: string, callback: Function) => any)
|
||||
| Promise<(path: string, callback: Function) => any>;
|
||||
getRequestHostname?(request: TRequest): string;
|
||||
getRequestMethod?(request: TRequest): string;
|
||||
getRequestUrl?(request: TResponse): string;
|
||||
@@ -65,4 +67,5 @@ export interface HttpServer<TRequest = any, TResponse = any> {
|
||||
initHttpServer(options: NestApplicationOptions): void;
|
||||
close(): any;
|
||||
getType(): string;
|
||||
init?(): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,9 @@ export * from './features/pipe-transform.interface';
|
||||
export * from './hooks';
|
||||
export * from './http/http-server.interface';
|
||||
export * from './injectable.interface';
|
||||
export * from './microservices/nest-hybrid-application-options.interface';
|
||||
export * from './middleware';
|
||||
export * from './modules/dynamic-module.interface';
|
||||
export * from './modules/forward-reference.interface';
|
||||
export * from './modules/module-metadata.interface';
|
||||
export * from './modules/nest-module.interface';
|
||||
export * from './modules/provider.interface';
|
||||
export * from './modules';
|
||||
export * from './nest-application-context.interface';
|
||||
export * from './nest-application-options.interface';
|
||||
export * from './nest-application.interface';
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface NestHybridApplicationOptions {
|
||||
inheritAppConfig?: boolean;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './dynamic-module.interface';
|
||||
export * from './forward-reference.interface';
|
||||
export * from './introspection-result.interface';
|
||||
export * from './module-metadata.interface';
|
||||
export * from './nest-module.interface';
|
||||
export * from './provider.interface';
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Scope } from '../scope-options.interface';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export interface IntrospectionResult {
|
||||
/**
|
||||
* Enum defining lifetime of host class or factory.
|
||||
*/
|
||||
scope: Scope;
|
||||
}
|
||||
@@ -34,6 +34,15 @@ export interface INestApplicationContext {
|
||||
options?: { strict: boolean },
|
||||
): Promise<TResult>;
|
||||
|
||||
/**
|
||||
* Registers the request/context object for a given context ID (DI container sub-tree).
|
||||
* @returns {void}
|
||||
*/
|
||||
registerRequestByContextId<T = any>(
|
||||
request: T,
|
||||
contextId: { id: number },
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Terminates the application
|
||||
* @returns {Promise<void>}
|
||||
|
||||
@@ -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.4.2",
|
||||
"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.1",
|
||||
"tslib": "2.0.0",
|
||||
"uuid": "8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"reflect-metadata": "^0.1.12",
|
||||
|
||||
25
packages/common/pipes/default-value.pipe.ts
Normal file
25
packages/common/pipes/default-value.pipe.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
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) ||
|
||||
(typeof value === 'number' && isNaN((value as unknown) as number))
|
||||
) {
|
||||
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';
|
||||
@@ -6,6 +6,7 @@ import { ArgumentMetadata, ValidationError } from '../index';
|
||||
import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface';
|
||||
import { ValidatorOptions } from '../interfaces/external/validator-options.interface';
|
||||
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
|
||||
import { Type } from '../interfaces/type.interface';
|
||||
import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
@@ -20,6 +21,7 @@ export interface ValidationPipeOptions extends ValidatorOptions {
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
exceptionFactory?: (errors: ValidationError[]) => any;
|
||||
validateCustomDecorators?: boolean;
|
||||
expectedType?: Type<any>;
|
||||
}
|
||||
|
||||
let classValidator: any = {};
|
||||
@@ -32,6 +34,7 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
protected validatorOptions: ValidatorOptions;
|
||||
protected transformOptions: ClassTransformOptions;
|
||||
protected errorHttpStatusCode: ErrorHttpStatusCode;
|
||||
protected expectedType: Type<any>;
|
||||
protected exceptionFactory: (errors: ValidationError[]) => any;
|
||||
protected validateCustomDecorators: boolean;
|
||||
|
||||
@@ -41,6 +44,7 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
transform,
|
||||
disableErrorMessages,
|
||||
errorHttpStatusCode,
|
||||
expectedType,
|
||||
transformOptions,
|
||||
validateCustomDecorators,
|
||||
...validatorOptions
|
||||
@@ -52,6 +56,7 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
this.isDetailedOutputDisabled = disableErrorMessages;
|
||||
this.validateCustomDecorators = validateCustomDecorators || false;
|
||||
this.errorHttpStatusCode = errorHttpStatusCode || HttpStatus.BAD_REQUEST;
|
||||
this.expectedType = expectedType;
|
||||
this.exceptionFactory =
|
||||
options.exceptionFactory || this.createExceptionFactory();
|
||||
|
||||
@@ -64,7 +69,7 @@ export class ValidationPipe implements PipeTransform<any> {
|
||||
}
|
||||
|
||||
public async transform(value: any, metadata: ArgumentMetadata) {
|
||||
const { metatype } = metadata;
|
||||
const metatype = this.expectedType || metadata.metatype;
|
||||
if (!metatype || !this.toValidate(metadata)) {
|
||||
return this.isTransformEnabled
|
||||
? this.transformPrimitive(value, metadata)
|
||||
@@ -120,12 +125,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 +164,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 +172,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,10 +1,10 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Inject, Injectable } from '../decorators/core';
|
||||
import { Inject, Injectable, Optional } from '../decorators/core';
|
||||
import { CallHandler, ExecutionContext, NestInterceptor } from '../interfaces';
|
||||
import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface';
|
||||
import { loadPackage } from '../utils/load-package.util';
|
||||
import { isObject } from '../utils/shared.utils';
|
||||
import { CallHandler, ExecutionContext, NestInterceptor } from '../interfaces';
|
||||
import { CLASS_SERIALIZER_OPTIONS } from './class-serializer.constants';
|
||||
|
||||
let classTransformer: any = {};
|
||||
@@ -20,7 +20,10 @@ const REFLECTOR = 'Reflector';
|
||||
|
||||
@Injectable()
|
||||
export class ClassSerializerInterceptor implements NestInterceptor {
|
||||
constructor(@Inject(REFLECTOR) protected readonly reflector: any) {
|
||||
constructor(
|
||||
@Inject(REFLECTOR) protected readonly reflector: any,
|
||||
@Optional() protected readonly defaultOptions: ClassTransformOptions = {},
|
||||
) {
|
||||
classTransformer = loadPackage(
|
||||
'class-transformer',
|
||||
'ClassSerializerInterceptor',
|
||||
@@ -30,7 +33,11 @@ export class ClassSerializerInterceptor implements NestInterceptor {
|
||||
}
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||
const options = this.getContextOptions(context);
|
||||
const contextOptions = this.getContextOptions(context);
|
||||
const options = {
|
||||
...this.defaultOptions,
|
||||
...contextOptions,
|
||||
};
|
||||
return next
|
||||
.handle()
|
||||
.pipe(
|
||||
|
||||
@@ -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 () => {
|
||||
@@ -352,4 +396,31 @@ describe('ValidationPipe', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('option: "expectedType"', () => {
|
||||
class TestModel2 {
|
||||
@IsString()
|
||||
public prop1: string;
|
||||
|
||||
@IsBoolean()
|
||||
public prop2: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
public optionalProp: string;
|
||||
}
|
||||
|
||||
it('should validate against the expected type if presented', async () => {
|
||||
const m: ArgumentMetadata = {
|
||||
type: 'body',
|
||||
metatype: TestModel2,
|
||||
data: '',
|
||||
};
|
||||
|
||||
target = new ValidationPipe({ expectedType: TestModel });
|
||||
const testObj = { prop1: 'value1', prop2: 'value2' };
|
||||
|
||||
expect(await target.transform(testObj, m)).to.equal(testObj);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
NotFoundException,
|
||||
NotImplementedException,
|
||||
PayloadTooLargeException,
|
||||
PreconditionFailedException,
|
||||
RequestTimeoutException,
|
||||
ServiceUnavailableException,
|
||||
UnauthorizedException,
|
||||
@@ -35,6 +36,7 @@ export type ErrorHttpStatusCode =
|
||||
| HttpStatus.NOT_FOUND
|
||||
| HttpStatus.NOT_IMPLEMENTED
|
||||
| HttpStatus.PAYLOAD_TOO_LARGE
|
||||
| HttpStatus.PRECONDITION_FAILED
|
||||
| HttpStatus.REQUEST_TIMEOUT
|
||||
| HttpStatus.SERVICE_UNAVAILABLE
|
||||
| HttpStatus.UNAUTHORIZED
|
||||
@@ -55,6 +57,7 @@ export const HttpErrorByCode: Record<ErrorHttpStatusCode, Type<unknown>> = {
|
||||
[HttpStatus.NOT_FOUND]: NotFoundException,
|
||||
[HttpStatus.NOT_IMPLEMENTED]: NotImplementedException,
|
||||
[HttpStatus.PAYLOAD_TOO_LARGE]: PayloadTooLargeException,
|
||||
[HttpStatus.PRECONDITION_FAILED]: PreconditionFailedException,
|
||||
[HttpStatus.REQUEST_TIMEOUT]: RequestTimeoutException,
|
||||
[HttpStatus.SERVICE_UNAVAILABLE]: ServiceUnavailableException,
|
||||
[HttpStatus.UNAUTHORIZED]: UnauthorizedException,
|
||||
|
||||
@@ -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).
|
||||
@@ -46,32 +54,43 @@ With official support, you can get expert help straight from Nest core team. We
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
#### Principal Sponsors
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td><td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="180" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### 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>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr>
|
||||
<tr><td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ export abstract class AbstractHttpAdapter<
|
||||
|
||||
constructor(protected readonly instance: any) {}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
public async init() {}
|
||||
|
||||
public use(...args: any[]) {
|
||||
return this.instance.use(...args);
|
||||
}
|
||||
@@ -97,6 +100,8 @@ export abstract class AbstractHttpAdapter<
|
||||
abstract enableCors(options: CorsOptions, prefix?: string);
|
||||
abstract createMiddlewareFactory(
|
||||
requestMethod: RequestMethod,
|
||||
): (path: string, callback: Function) => any;
|
||||
):
|
||||
| ((path: string, callback: Function) => any)
|
||||
| Promise<(path: string, callback: Function) => any>;
|
||||
abstract getType(): string;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { RuntimeException } from './runtime.exception';
|
||||
|
||||
export class UnknownElementException extends RuntimeException {
|
||||
constructor(name?: string) {
|
||||
constructor(name?: string | symbol) {
|
||||
name = name && name.toString();
|
||||
super(
|
||||
`Nest could not find ${
|
||||
name || 'given'
|
||||
|
||||
@@ -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,12 +1,12 @@
|
||||
import { BeforeApplicationShutdown } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import { iterate } from 'iterare';
|
||||
import {
|
||||
getNonTransientInstances,
|
||||
getTransientInstances,
|
||||
} from '../injector/transient-instances';
|
||||
} from '../injector/helpers/transient-instances';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
|
||||
/**
|
||||
* Checks if the given instance has the `beforeApplicationShutdown` function
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { OnApplicationBootstrap } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import { iterate } from 'iterare';
|
||||
import {
|
||||
getNonTransientInstances,
|
||||
getTransientInstances,
|
||||
} from '../injector/transient-instances';
|
||||
} from '../injector/helpers/transient-instances';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
|
||||
/**
|
||||
* Checks if the given instance has the `onApplicationBootstrap` function
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { OnApplicationShutdown } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import { iterate } from 'iterare';
|
||||
import {
|
||||
getNonTransientInstances,
|
||||
getTransientInstances,
|
||||
} from '../injector/transient-instances';
|
||||
} from '../injector/helpers/transient-instances';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
|
||||
/**
|
||||
* Checks if the given instance has the `onApplicationShutdown` function
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { OnModuleDestroy } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import { iterate } from 'iterare';
|
||||
import {
|
||||
getNonTransientInstances,
|
||||
getTransientInstances,
|
||||
} from '../injector/transient-instances';
|
||||
} from '../injector/helpers/transient-instances';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
|
||||
/**
|
||||
* Returns true or false if the given instance has a `onModuleDestroy` function
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { OnModuleInit } from '@nestjs/common';
|
||||
import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import iterate from 'iterare';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
import { iterate } from 'iterare';
|
||||
import {
|
||||
getNonTransientInstances,
|
||||
getTransientInstances,
|
||||
} from '../injector/transient-instances';
|
||||
} from '../injector/helpers/transient-instances';
|
||||
import { InstanceWrapper } from '../injector/instance-wrapper';
|
||||
import { Module } from '../injector/module';
|
||||
|
||||
/**
|
||||
* Returns true or false if the given instance has a `onModuleInit` function
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { Abstract, Scope } from '@nestjs/common/interfaces';
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { InvalidClassScopeException } from '../errors/exceptions/invalid-class-scope.exception';
|
||||
import { UnknownElementException } from '../errors/exceptions/unknown-element.exception';
|
||||
import { NestContainer } from './container';
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
import { Module } from './module';
|
||||
|
||||
type HostCollection = 'providers' | 'controllers' | 'injectables';
|
||||
|
||||
export class ContainerScanner {
|
||||
private flatContainer: Partial<Module>;
|
||||
|
||||
constructor(private readonly container: NestContainer) {}
|
||||
|
||||
public find<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | Abstract<TInput> | string | symbol,
|
||||
): TResult {
|
||||
this.initFlatContainer();
|
||||
return this.findInstanceByToken<TInput, TResult>(
|
||||
typeOrToken,
|
||||
this.flatContainer,
|
||||
);
|
||||
}
|
||||
|
||||
public getWrapperCollectionPair<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | Abstract<TInput> | string | symbol,
|
||||
): [InstanceWrapper<TResult>, Map<string, InstanceWrapper>] {
|
||||
this.initFlatContainer();
|
||||
return this.getWrapperCollectionPairByHost<TInput, TResult>(
|
||||
typeOrToken,
|
||||
this.flatContainer,
|
||||
);
|
||||
}
|
||||
|
||||
public findInstanceByToken<TInput = any, TResult = TInput>(
|
||||
metatypeOrToken: Type<TInput> | Abstract<TInput> | string | symbol,
|
||||
contextModule: Partial<Module>,
|
||||
): TResult {
|
||||
const [instanceWrapper] = this.getWrapperCollectionPairByHost(
|
||||
metatypeOrToken,
|
||||
contextModule,
|
||||
);
|
||||
if (
|
||||
instanceWrapper.scope === Scope.REQUEST ||
|
||||
instanceWrapper.scope === Scope.TRANSIENT
|
||||
) {
|
||||
throw new InvalidClassScopeException(metatypeOrToken);
|
||||
}
|
||||
return (instanceWrapper.instance as unknown) as TResult;
|
||||
}
|
||||
|
||||
public getWrapperCollectionPairByHost<TInput = any, TResult = TInput>(
|
||||
metatypeOrToken: Type<TInput> | Abstract<TInput> | string | symbol,
|
||||
contextModule: Partial<Module>,
|
||||
): [InstanceWrapper<TResult>, Map<string, InstanceWrapper>] {
|
||||
const name = isFunction(metatypeOrToken)
|
||||
? (metatypeOrToken as Function).name
|
||||
: metatypeOrToken;
|
||||
const collectionName = this.getHostCollection(
|
||||
name as string,
|
||||
contextModule,
|
||||
);
|
||||
const instanceWrapper = contextModule[collectionName].get(name as string);
|
||||
if (!instanceWrapper) {
|
||||
throw new UnknownElementException(name && name.toString());
|
||||
}
|
||||
return [
|
||||
instanceWrapper as InstanceWrapper<TResult>,
|
||||
contextModule[collectionName],
|
||||
];
|
||||
}
|
||||
|
||||
private initFlatContainer() {
|
||||
if (this.flatContainer) {
|
||||
return;
|
||||
}
|
||||
const modules = this.container.getModules();
|
||||
const initialValue: Record<string, any[]> = {
|
||||
providers: [],
|
||||
controllers: [],
|
||||
injectables: [],
|
||||
};
|
||||
const merge = <T = unknown>(
|
||||
initial: Map<string, T> | T[],
|
||||
arr: Map<string, T>,
|
||||
) => [...initial, ...arr];
|
||||
|
||||
const partialModule = ([...modules.values()].reduce(
|
||||
(current, next) => ({
|
||||
providers: merge(current.providers, next.providers),
|
||||
controllers: merge(current.controllers, next.controllers),
|
||||
injectables: merge(current.injectables, next.injectables),
|
||||
}),
|
||||
initialValue,
|
||||
) as any) as Partial<Module>;
|
||||
|
||||
this.flatContainer = {
|
||||
providers: new Map(partialModule.providers),
|
||||
controllers: new Map(partialModule.controllers),
|
||||
injectables: new Map(partialModule.injectables),
|
||||
};
|
||||
}
|
||||
|
||||
private getHostCollection(
|
||||
token: string,
|
||||
{ providers, controllers }: Partial<Module>,
|
||||
): HostCollection {
|
||||
if (providers.has(token)) {
|
||||
return 'providers';
|
||||
}
|
||||
if (controllers.has(token)) {
|
||||
return 'controllers';
|
||||
}
|
||||
return 'injectables';
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -194,7 +196,7 @@ export class NestContainer {
|
||||
}
|
||||
|
||||
public bindGlobalModuleToModule(target: Module, globalModule: Module) {
|
||||
if (target === globalModule) {
|
||||
if (target === globalModule || target === this.internalCoreModule) {
|
||||
return;
|
||||
}
|
||||
target.addRelatedModule(globalModule);
|
||||
@@ -203,7 +205,7 @@ export class NestContainer {
|
||||
public getDynamicMetadataByToken(
|
||||
token: string,
|
||||
metadataKey: keyof DynamicModule,
|
||||
): any[] {
|
||||
) {
|
||||
const metadata = this.dynamicModulesMetadata.get(token);
|
||||
if (metadata && metadata[metadataKey]) {
|
||||
return metadata[metadataKey] as any[];
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import iterate from 'iterare';
|
||||
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
import { iterate } from 'iterare';
|
||||
import { InstanceWrapper } from '../instance-wrapper';
|
||||
|
||||
/**
|
||||
* Returns the instances which are transient
|
||||
@@ -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);
|
||||
|
||||
79
packages/core/injector/instance-links-host.ts
Normal file
79
packages/core/injector/instance-links-host.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Abstract, Type } from '@nestjs/common';
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { UnknownElementException } from '../errors/exceptions/unknown-element.exception';
|
||||
import { NestContainer } from './container';
|
||||
import { InstanceWrapper } from './instance-wrapper';
|
||||
import { Module } from './module';
|
||||
|
||||
type InstanceToken = string | symbol | Type<any> | Abstract<any> | Function;
|
||||
type HostCollection = 'providers' | 'controllers' | 'injectables';
|
||||
|
||||
export interface InstanceLink<T = any> {
|
||||
token: InstanceToken;
|
||||
wrapperRef: InstanceWrapper<T>;
|
||||
collection: Map<any, InstanceWrapper>;
|
||||
moduleId: string;
|
||||
}
|
||||
|
||||
export class InstanceLinksHost {
|
||||
private readonly instanceLinks = new Map<InstanceToken, InstanceLink[]>();
|
||||
|
||||
constructor(private readonly container: NestContainer) {
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
get<T = any>(token: InstanceToken, moduleId?: string): InstanceLink<T> {
|
||||
const name = isFunction(token)
|
||||
? (token as Function).name
|
||||
: (token as string | symbol);
|
||||
const modulesMap = this.instanceLinks.get(name);
|
||||
|
||||
if (!modulesMap) {
|
||||
throw new UnknownElementException(name);
|
||||
}
|
||||
const instanceLink = moduleId
|
||||
? modulesMap.find(item => item.moduleId === moduleId)
|
||||
: modulesMap[modulesMap.length - 1];
|
||||
|
||||
if (!instanceLink) {
|
||||
throw new UnknownElementException(name);
|
||||
}
|
||||
return instanceLink;
|
||||
}
|
||||
|
||||
private initialize() {
|
||||
const modules = this.container.getModules();
|
||||
modules.forEach(moduleRef => {
|
||||
const { providers, injectables, controllers } = moduleRef;
|
||||
providers.forEach((wrapper, token) =>
|
||||
this.addLink(wrapper, token, moduleRef, 'providers'),
|
||||
);
|
||||
injectables.forEach((wrapper, token) =>
|
||||
this.addLink(wrapper, token, moduleRef, 'injectables'),
|
||||
);
|
||||
controllers.forEach((wrapper, token) =>
|
||||
this.addLink(wrapper, token, moduleRef, 'controllers'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private addLink(
|
||||
wrapper: InstanceWrapper,
|
||||
token: InstanceToken,
|
||||
moduleRef: Module,
|
||||
collectionName: HostCollection,
|
||||
) {
|
||||
const instanceLink: InstanceLink = {
|
||||
moduleId: moduleRef.id,
|
||||
wrapperRef: wrapper,
|
||||
collection: moduleRef[collectionName],
|
||||
token,
|
||||
};
|
||||
const existingLinks = this.instanceLinks.get(token);
|
||||
if (!existingLinks) {
|
||||
this.instanceLinks.set(token, [instanceLink]);
|
||||
} else {
|
||||
existingLinks.push(instanceLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,19 +1,26 @@
|
||||
import { Type } from '@nestjs/common';
|
||||
import { IntrospectionResult, Scope, 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';
|
||||
import { InstanceLinksHost } from './instance-links-host';
|
||||
import { ContextId, InstanceWrapper } from './instance-wrapper';
|
||||
import { Module } from './module';
|
||||
|
||||
export abstract class ModuleRef {
|
||||
private readonly injector = new Injector();
|
||||
private readonly containerScanner: ContainerScanner;
|
||||
private _instanceLinksHost: InstanceLinksHost;
|
||||
|
||||
constructor(protected readonly container: NestContainer) {
|
||||
this.containerScanner = new ContainerScanner(container);
|
||||
private get instanceLinksHost() {
|
||||
if (!this._instanceLinksHost) {
|
||||
this._instanceLinksHost = new InstanceLinksHost(this.container);
|
||||
}
|
||||
return this._instanceLinksHost;
|
||||
}
|
||||
|
||||
constructor(protected readonly container: NestContainer) {}
|
||||
|
||||
public abstract get<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | string | symbol,
|
||||
options?: { strict: boolean },
|
||||
@@ -25,10 +32,70 @@ export abstract class ModuleRef {
|
||||
): Promise<TResult>;
|
||||
public abstract create<T = any>(type: Type<T>): Promise<T>;
|
||||
|
||||
public introspect<T = any>(
|
||||
token: Type<T> | string | symbol,
|
||||
): IntrospectionResult {
|
||||
const { wrapperRef } = this.instanceLinksHost.get(token);
|
||||
|
||||
let scope = Scope.DEFAULT;
|
||||
if (!wrapperRef.isDependencyTreeStatic()) {
|
||||
scope = Scope.REQUEST;
|
||||
} else if (wrapperRef.isTransient) {
|
||||
scope = Scope.TRANSIENT;
|
||||
}
|
||||
return { scope };
|
||||
}
|
||||
|
||||
public registerRequestByContextId<T = any>(request: T, contextId: ContextId) {
|
||||
this.container.registerRequestProvider(request, contextId);
|
||||
}
|
||||
|
||||
protected find<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | string | symbol,
|
||||
contextModule?: Module,
|
||||
): TResult {
|
||||
return this.containerScanner.find<TInput, TResult>(typeOrToken);
|
||||
const moduleId = contextModule && contextModule.id;
|
||||
const { wrapperRef } = this.instanceLinksHost.get<TResult>(
|
||||
typeOrToken,
|
||||
moduleId,
|
||||
);
|
||||
if (
|
||||
wrapperRef.scope === Scope.REQUEST ||
|
||||
wrapperRef.scope === Scope.TRANSIENT
|
||||
) {
|
||||
throw new InvalidClassScopeException(typeOrToken);
|
||||
}
|
||||
return wrapperRef.instance;
|
||||
}
|
||||
|
||||
protected async resolvePerContext<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | string | symbol,
|
||||
contextModule: Module,
|
||||
contextId: ContextId,
|
||||
options?: { strict: boolean },
|
||||
): Promise<TResult> {
|
||||
const isStrictModeEnabled = options && options.strict;
|
||||
const instanceLink = isStrictModeEnabled
|
||||
? this.instanceLinksHost.get(typeOrToken, contextModule.id)
|
||||
: this.instanceLinksHost.get(typeOrToken);
|
||||
|
||||
const { wrapperRef, collection } = instanceLink;
|
||||
if (wrapperRef.isDependencyTreeStatic() && !wrapperRef.isTransient) {
|
||||
return this.get(typeOrToken);
|
||||
}
|
||||
|
||||
const ctorHost = wrapperRef.instance || { constructor: typeOrToken };
|
||||
const instance = await this.injector.loadPerContext(
|
||||
ctorHost,
|
||||
wrapperRef.host,
|
||||
collection,
|
||||
contextId,
|
||||
wrapperRef,
|
||||
);
|
||||
if (!instance) {
|
||||
throw new UnknownElementException();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected async instantiateClass<T = any>(
|
||||
@@ -38,8 +105,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) => {
|
||||
@@ -64,47 +131,4 @@ export abstract class ModuleRef {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected findInstanceByToken<TInput = any, TResult = TInput>(
|
||||
metatypeOrToken: Type<TInput> | string | symbol,
|
||||
contextModule: Module,
|
||||
): TResult {
|
||||
return this.containerScanner.findInstanceByToken<TInput, TResult>(
|
||||
metatypeOrToken,
|
||||
contextModule,
|
||||
);
|
||||
}
|
||||
|
||||
protected async resolvePerContext<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | string | symbol,
|
||||
contextModule: Module,
|
||||
contextId: ContextId,
|
||||
options?: { strict: boolean },
|
||||
): Promise<TResult> {
|
||||
let wrapper: InstanceWrapper, collection: Map<string, InstanceWrapper>;
|
||||
if (!(options && options.strict)) {
|
||||
[wrapper, collection] = this.containerScanner.getWrapperCollectionPair(
|
||||
typeOrToken,
|
||||
);
|
||||
} else {
|
||||
[
|
||||
wrapper,
|
||||
collection,
|
||||
] = this.containerScanner.getWrapperCollectionPairByHost(
|
||||
typeOrToken,
|
||||
contextModule,
|
||||
);
|
||||
}
|
||||
const instance = await this.injector.loadPerContext(
|
||||
wrapper.instance,
|
||||
wrapper.host,
|
||||
collection,
|
||||
contextId,
|
||||
wrapper,
|
||||
);
|
||||
if (!instance) {
|
||||
throw new UnknownElementException();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -482,7 +479,7 @@ export class Module {
|
||||
return [...this._providers].filter(([_, wrapper]) => !wrapper.isAlias);
|
||||
}
|
||||
|
||||
public createModuleReferenceType(): any {
|
||||
public createModuleReferenceType(): Type<ModuleRef> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self = this;
|
||||
return class extends ModuleRef {
|
||||
@@ -494,10 +491,9 @@ export class Module {
|
||||
typeOrToken: Type<TInput> | string | symbol,
|
||||
options: { strict: boolean } = { strict: true },
|
||||
): TResult {
|
||||
if (!(options && options.strict)) {
|
||||
return this.find<TInput, TResult>(typeOrToken);
|
||||
}
|
||||
return this.findInstanceByToken<TInput, TResult>(typeOrToken, self);
|
||||
return !(options && options.strict)
|
||||
? this.find<TInput, TResult>(typeOrToken)
|
||||
: this.find<TInput, TResult>(typeOrToken, self);
|
||||
}
|
||||
|
||||
public resolve<TInput = any, TResult = TInput>(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ export class MiddlewareModule {
|
||||
if (isUndefined(instance.use)) {
|
||||
throw new InvalidMiddlewareException(metatype.name);
|
||||
}
|
||||
const router = applicationRef.createMiddlewareFactory(method);
|
||||
const router = await applicationRef.createMiddlewareFactory(method);
|
||||
const isStatic = wrapper.isDependencyTreeStatic();
|
||||
if (isStatic) {
|
||||
const proxy = await this.createProxy(instance);
|
||||
|
||||
@@ -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>>(
|
||||
|
||||
@@ -4,10 +4,12 @@ import {
|
||||
LoggerService,
|
||||
ShutdownSignal,
|
||||
} from '@nestjs/common';
|
||||
import { Abstract } from '@nestjs/common/interfaces';
|
||||
import { Abstract, Scope } 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';
|
||||
@@ -20,9 +22,8 @@ import {
|
||||
} from './hooks';
|
||||
import { ContextId } from './injector';
|
||||
import { NestContainer } from './injector/container';
|
||||
import { ContainerScanner } from './injector/container-scanner';
|
||||
import { Injector } from './injector/injector';
|
||||
import { InstanceWrapper } from './injector/instance-wrapper';
|
||||
import { InstanceLinksHost } from './injector/instance-links-host';
|
||||
import { Module } from './injector/module';
|
||||
|
||||
/**
|
||||
@@ -33,15 +34,20 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
protected readonly injector = new Injector();
|
||||
private shutdownCleanupRef?: (...args: unknown[]) => unknown;
|
||||
private readonly activeShutdownSignals = new Array<string>();
|
||||
private readonly containerScanner: ContainerScanner;
|
||||
private _instanceLinksHost: InstanceLinksHost;
|
||||
|
||||
private get instanceLinksHost() {
|
||||
if (!this._instanceLinksHost) {
|
||||
this._instanceLinksHost = new InstanceLinksHost(this.container);
|
||||
}
|
||||
return this._instanceLinksHost;
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected readonly container: NestContainer,
|
||||
private readonly scope = new Array<Type<any>>(),
|
||||
private contextModule: Module = null,
|
||||
) {
|
||||
this.containerScanner = new ContainerScanner(container);
|
||||
}
|
||||
) {}
|
||||
|
||||
public selectContextModule() {
|
||||
const modules = this.container.getModules().values();
|
||||
@@ -66,13 +72,9 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
typeOrToken: Type<TInput> | Abstract<TInput> | string | symbol,
|
||||
options: { strict: boolean } = { strict: false },
|
||||
): TResult {
|
||||
if (!(options && options.strict)) {
|
||||
return this.find<TInput, TResult>(typeOrToken);
|
||||
}
|
||||
return this.findInstanceByToken<TInput, TResult>(
|
||||
typeOrToken,
|
||||
this.contextModule,
|
||||
);
|
||||
return !(options && options.strict)
|
||||
? this.find<TInput, TResult>(typeOrToken)
|
||||
: this.find<TInput, TResult>(typeOrToken, this.contextModule);
|
||||
}
|
||||
|
||||
public resolve<TInput = any, TResult = TInput>(
|
||||
@@ -88,6 +90,10 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
);
|
||||
}
|
||||
|
||||
public registerRequestByContextId<T = any>(request: T, contextId: ContextId) {
|
||||
this.container.registerRequestProvider(request, contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initalizes the Nest application.
|
||||
* Calls the Nest lifecycle events.
|
||||
@@ -110,7 +116,7 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
await this.callBeforeShutdownHook();
|
||||
await this.dispose();
|
||||
await this.callShutdownHook();
|
||||
await this.unsubscribeFromProcessSignals();
|
||||
this.unsubscribeFromProcessSignals();
|
||||
}
|
||||
|
||||
public useLogger(logger: LoggerService) {
|
||||
@@ -137,10 +143,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;
|
||||
@@ -253,18 +260,20 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
|
||||
protected find<TInput = any, TResult = TInput>(
|
||||
typeOrToken: Type<TInput> | Abstract<TInput> | string | symbol,
|
||||
contextModule?: Module,
|
||||
): TResult {
|
||||
return this.containerScanner.find<TInput, TResult>(typeOrToken);
|
||||
}
|
||||
|
||||
protected findInstanceByToken<TInput = any, TResult = TInput>(
|
||||
metatypeOrToken: Type<TInput> | Abstract<TInput> | string | symbol,
|
||||
contextModule: Partial<Module>,
|
||||
): TResult {
|
||||
return this.containerScanner.findInstanceByToken<TInput, TResult>(
|
||||
metatypeOrToken,
|
||||
contextModule,
|
||||
const moduleId = contextModule && contextModule.id;
|
||||
const { wrapperRef } = this.instanceLinksHost.get<TResult>(
|
||||
typeOrToken,
|
||||
moduleId,
|
||||
);
|
||||
if (
|
||||
wrapperRef.scope === Scope.REQUEST ||
|
||||
wrapperRef.scope === Scope.TRANSIENT
|
||||
) {
|
||||
throw new InvalidClassScopeException(typeOrToken);
|
||||
}
|
||||
return wrapperRef.instance;
|
||||
}
|
||||
|
||||
protected async resolvePerContext<TInput = any, TResult = TInput>(
|
||||
@@ -273,23 +282,20 @@ export class NestApplicationContext implements INestApplicationContext {
|
||||
contextId: ContextId,
|
||||
options?: { strict: boolean },
|
||||
): Promise<TResult> {
|
||||
let wrapper: InstanceWrapper, collection: Map<string, InstanceWrapper>;
|
||||
if (!(options && options.strict)) {
|
||||
[wrapper, collection] = this.containerScanner.getWrapperCollectionPair(
|
||||
typeOrToken,
|
||||
);
|
||||
} else {
|
||||
[
|
||||
wrapper,
|
||||
collection,
|
||||
] = this.containerScanner.getWrapperCollectionPairByHost(
|
||||
typeOrToken,
|
||||
contextModule,
|
||||
);
|
||||
const isStrictModeEnabled = options && options.strict;
|
||||
const instanceLink = isStrictModeEnabled
|
||||
? this.instanceLinksHost.get(typeOrToken, contextModule.id)
|
||||
: this.instanceLinksHost.get(typeOrToken);
|
||||
|
||||
const { wrapperRef, collection } = instanceLink;
|
||||
if (wrapperRef.isDependencyTreeStatic() && !wrapperRef.isTransient) {
|
||||
return this.get(typeOrToken);
|
||||
}
|
||||
|
||||
const ctorHost = wrapperRef.instance || { constructor: typeOrToken };
|
||||
const instance = await this.injector.loadPerContext(
|
||||
wrapper.instance,
|
||||
wrapper.host,
|
||||
ctorHost,
|
||||
wrapperRef.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';
|
||||
@@ -137,6 +138,7 @@ export class NestApplication extends NestApplicationContext
|
||||
|
||||
public async init(): Promise<this> {
|
||||
this.applyOptions();
|
||||
await this.httpAdapter?.init();
|
||||
|
||||
const useBodyParser =
|
||||
this.appOptions && this.appOptions.bodyParser !== false;
|
||||
@@ -170,17 +172,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();
|
||||
|
||||
@@ -147,12 +147,15 @@ export class NestFactoryStatic {
|
||||
httpServer: HttpServer = null,
|
||||
) {
|
||||
const instanceLoader = new InstanceLoader(container);
|
||||
const metadataScanner = new MetadataScanner();
|
||||
const dependenciesScanner = new DependenciesScanner(
|
||||
container,
|
||||
new MetadataScanner(),
|
||||
metadataScanner,
|
||||
config,
|
||||
);
|
||||
container.setHttpAdapter(httpServer);
|
||||
|
||||
await httpServer?.init();
|
||||
try {
|
||||
this.logger.log(MESSAGES.APPLICATION_START);
|
||||
await ExceptionsZone.asyncRun(async () => {
|
||||
@@ -190,7 +193,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.4.2",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
@@ -29,11 +29,14 @@
|
||||
"dependencies": {
|
||||
"@nuxtjs/opencollective": "0.2.2",
|
||||
"fast-safe-stringify": "2.0.7",
|
||||
"iterare": "1.2.0",
|
||||
"iterare": "1.2.1",
|
||||
"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.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/common": "7.4.2"
|
||||
},
|
||||
"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;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class RouteParamsFactory implements IRouteParamsFactory {
|
||||
case RouteParamtypes.QUERY:
|
||||
return data ? req.query[data] : req.query;
|
||||
case RouteParamtypes.HEADERS:
|
||||
return data ? req.headers[data] : req.headers;
|
||||
return data ? req.headers[data.toLowerCase()] : req.headers;
|
||||
case RouteParamtypes.SESSION:
|
||||
return req.session;
|
||||
case RouteParamtypes.FILE:
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user