mirror of
https://github.com/nestjs/nest.git
synced 2026-02-24 00:02:56 +00:00
Compare commits
5 Commits
feat/parse
...
feat/modul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3f85e9c04 | ||
|
|
c4a905b996 | ||
|
|
094c35cf79 | ||
|
|
30110d49e0 | ||
|
|
fb72a8ec9f |
@@ -129,6 +129,9 @@ jobs:
|
||||
- run:
|
||||
name: Lint
|
||||
command: npm run lint
|
||||
- run:
|
||||
name: Lint commit
|
||||
command: ./node_modules/.bin/commitlint-circle -c .commitlintrc.json
|
||||
|
||||
integration_tests:
|
||||
working_directory: ~/nest
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
8
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
@@ -38,12 +38,10 @@ body:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Minimum reproduction code"
|
||||
placeholder: "https://github.com/..."
|
||||
description: |
|
||||
An URL to some Git repository/[StackBlitz](https://stackblitz.com/fork/github/nestjs/typescript-starter)/[CodeSandbox](https://codesandbox.io/s/github/nestjs/typescript-starter/tree/master) project that reproduces your issue. [What is a minimum reproduction?](https://jmcdo29.github.io/wtf-is-a-minimum-reproduction)
|
||||
|
||||
> [!WARNING]
|
||||
> We may close this Issue if we don't manage to reproduce the potential bug. [Read this](https://antfu.me/posts/why-reproductions-are-required) to understand why.
|
||||
An URL to some Git repository/[StackBlitz](https://stackblitz.com/fork/github/nestjs/typescript-starter)/[CodeSandbox](https://codesandbox.io/s/github/nestjs/typescript-starter/tree/master) project that reproduces your issue. [What is a minimum reproduction?](https://jmcdo29.github.io/wtf-is-a-minimum-reproduction)
|
||||
:warning: **NOTE:** We can close this issue if we don't manage to reproduce your potential bug. [Here](https://antfu.me/posts/why-reproductions-are-required) is why.
|
||||
placeholder: "https://github.com/..."
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,3 @@
|
||||
packages/*/package-lock.json
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
@@ -49,4 +47,4 @@ yarn-error.log
|
||||
build/config\.gypi
|
||||
|
||||
.npmrc
|
||||
pnpm-lock.yaml
|
||||
pnpm-lock.yaml
|
||||
121
Readme.md
121
Readme.md
@@ -57,84 +57,71 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Principal Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://microsoft.com/" target="_blank"><img src="https://nestjs.com/img/logos/microsoft-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://mojam.co" target="_blank"><img src="https://nestjs.com/img/logos/mojam-logo.png" width="80" valign="middle" /></a></td>
|
||||
<td><a href="https://marblism.com?utm_source=nest" target="_blank"><img src="https://nestjs.com/img/logos/marblism-logo.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="170" valign="middle" /></a></td>
|
||||
<td><a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/logos/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/logos/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/logos/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/logos/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/logos/jetbrains-logo.svg" width="90" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/logos/snyk-logo-black.png" width="185" valign="middle" /></a></td>
|
||||
<td><a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/logos/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/logos/ridi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/logos/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/logos/skunk-logo.png" height="60" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></tr><tr>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="90" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td><td>
|
||||
<a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/ridi-logo.svg" width="105" valign="middle" /></a></td><td>
|
||||
<a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
</tr><tr><td>
|
||||
<a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/skunk-logo.png" height="60" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.mercedes-benz.com/" target="_blank"><img src="https://nestjs.com/img/logos/mercedes-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td><a href="https://www.dinii.jp/" target="_blank"><img src="https://nestjs.com/img/logos/dinii-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://bloodycase.com/?promocode=NEST" target="_blank"><img src="https://nestjs.com/img/logos/bloodycase-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://handsontable.com/docs/react-data-grid/?utm_source=NestJS_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024" target="_blank"><img src="https://nestjs.com/img/logos/handsontable-dark-logo.svg#2" width="150" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.itflashcards.com/" target="_blank"><img src="https://nestjs.com/img/logos/it_flashcards-logo.png" width="170" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://arcjet.com/?ref=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/arcjet-logo.svg" width="170" valign="middle" /></a></td>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td>
|
||||
<td><a href="https://twistag.com/" target="_blank"><img src="https://nestjs.com/img/twistag-logo.png" width="120" valign="middle" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/logos/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/logos/novologic.png" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/logos/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/logos/triplebyte.png" width="107" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/logos/nearpod-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/logos/genuinebee.svg" width="97" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/logos/vpn-review-logo.png" width="85" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/logos/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/logos/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/logos/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/logos/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/logos/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/logos/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/logos/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/logos/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/logos/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/logos/julienferand-logo.jpeg" width="55" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/logos/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/logos/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.route4me.com/" target="_blank"><img src="https://nestjs.com/img/logos/route4me-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.slotsup.com/" target="_blank"><img src="https://nestjs.com/img/logos/slotsup-logo.png" width="60" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<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://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" width="95" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" width="107" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/julienferand-logo.jpeg" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
|
||||
@@ -45,11 +45,6 @@ class TestController {
|
||||
overviewById() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('multiple/exclude')
|
||||
multipleExclude() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -64,7 +59,6 @@ class TestModule {
|
||||
path: 'middleware',
|
||||
method: RequestMethod.POST,
|
||||
})
|
||||
.exclude('multiple/exclude')
|
||||
.forRoutes('*');
|
||||
}
|
||||
}
|
||||
@@ -116,12 +110,6 @@ describe('Exclude middleware', () => {
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
it(`should exclude "/multiple/exclude" endpoint`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/multiple/exclude')
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -45,10 +45,6 @@ describe('Hello world (express instance)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('/HEAD should respond to with a 200', () => {
|
||||
return request(server).head('/hello').expect(200);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -87,15 +87,6 @@ describe('Hello world (fastify adapter)', () => {
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
});
|
||||
|
||||
it('/HEAD should respond to with a 200', () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'HEAD',
|
||||
url: '/hello',
|
||||
})
|
||||
.then(({ statusCode }) => expect(statusCode).to.be.eq(200));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -4,15 +4,11 @@ import { INestApplication } from '@nestjs/common';
|
||||
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { fail } from 'assert';
|
||||
import { expect, use } from 'chai';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import { expect } from 'chai';
|
||||
import { join } from 'path';
|
||||
import * as sinon from 'sinon';
|
||||
import * as request from 'supertest';
|
||||
import { GrpcController } from '../src/grpc/grpc.controller';
|
||||
|
||||
use(chaiAsPromised);
|
||||
|
||||
describe('GRPC transport', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
@@ -36,7 +32,6 @@ describe('GRPC transport', () => {
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// Start gRPC microservice
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
@@ -154,50 +149,6 @@ describe('GRPC transport', () => {
|
||||
expect(receivedIds).to.deep.equal(expectedIds);
|
||||
});
|
||||
|
||||
describe('streaming calls that error', () => {
|
||||
// We want to assert that the application does not crash when an error is encountered with an unhandledRejection
|
||||
// the best way to do that is to listen for the unhandledRejection event and fail the test if it is called
|
||||
let processSpy: sinon.SinonSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
processSpy = sinon.spy();
|
||||
process.on('unhandledRejection', processSpy);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.off('unhandledRejection', processSpy);
|
||||
});
|
||||
|
||||
it('should not crash when replying with an error', async () => {
|
||||
const call = new Promise<void>((resolve, reject) => {
|
||||
const stream = client.streamDivide({
|
||||
data: [{ dividend: 1, divisor: 0 }],
|
||||
});
|
||||
|
||||
stream.on('data', () => {
|
||||
fail('Stream should not have emitted any data');
|
||||
});
|
||||
|
||||
stream.on('error', err => {
|
||||
if (err.code !== GRPC.status.CANCELLED) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
await expect(call).to.eventually.be.rejectedWith(
|
||||
'3 INVALID_ARGUMENT: dividing by 0 is not possible',
|
||||
);
|
||||
|
||||
// if this fails the application has crashed
|
||||
expect(processSpy.called).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
RpcException,
|
||||
} from '@nestjs/microservices';
|
||||
import { join } from 'path';
|
||||
import { Observable, of, catchError, from, mergeMap } from 'rxjs';
|
||||
import { Observable, of, catchError } from 'rxjs';
|
||||
|
||||
class ErrorHandlingProxy extends ClientGrpcProxy {
|
||||
serializeError(err) {
|
||||
@@ -107,17 +107,6 @@ export class GrpcController {
|
||||
};
|
||||
}
|
||||
|
||||
// contrived example meant to show when an error is encountered, like dividing by zero, the
|
||||
// application does not crash and the error is returned appropriately to the client
|
||||
@GrpcMethod('Math', 'StreamDivide')
|
||||
streamDivide({
|
||||
data,
|
||||
}: {
|
||||
data: { dividend: number; divisor: number }[];
|
||||
}): Observable<any> {
|
||||
return from(data).pipe(mergeMap(request => this.divide(request)));
|
||||
}
|
||||
|
||||
@GrpcMethod('Math2')
|
||||
async sum2({ data }: { data: number[] }): Promise<any> {
|
||||
return of({
|
||||
|
||||
@@ -7,9 +7,7 @@ service Math {
|
||||
rpc SumStream(stream RequestSum) returns(stream SumResult);
|
||||
rpc SumStreamPass(stream RequestSum) returns(stream SumResult);
|
||||
rpc Divide (RequestDivide) returns (DivideResult);
|
||||
rpc StreamLargeMessages(Empty) returns (stream BackpressureData);
|
||||
/* Given a series of dividend and divisor, stream back the division results for each */
|
||||
rpc StreamDivide (StreamDivideRequest) returns (stream StreamDivideResponse);
|
||||
rpc StreamLargeMessages(Empty) returns (stream BackpressureData) {}
|
||||
}
|
||||
|
||||
message BackpressureData {
|
||||
@@ -35,11 +33,3 @@ message RequestDivide {
|
||||
message DivideResult {
|
||||
int32 result = 1;
|
||||
}
|
||||
|
||||
message StreamDivideRequest {
|
||||
repeated RequestDivide data = 1;
|
||||
}
|
||||
|
||||
message StreamDivideResponse {
|
||||
DivideResult data = 1;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Observable } from 'rxjs';
|
||||
import { SumDto } from './dto/sum.dto';
|
||||
|
||||
/**
|
||||
* The following function explicitly sends messages to the key representing the partition.
|
||||
* The following function explicity sends messages to the key representing the partition.
|
||||
*/
|
||||
const explicitPartitioner = () => {
|
||||
return ({ message }: PartitionerArgs) => {
|
||||
|
||||
@@ -573,7 +573,7 @@ allow_anonymous true
|
||||
# not given then the access is read/write. <topic> can contain the + or #
|
||||
# wildcards as in subscriptions.
|
||||
#
|
||||
# The "deny" option can used to explicitly deny access to a topic that would
|
||||
# The "deny" option can used to explicity deny access to a topic that would
|
||||
# otherwise be granted by a broader read/write/readwrite statement. Any "deny"
|
||||
# topics are handled before topics that grant read/write access.
|
||||
#
|
||||
|
||||
@@ -130,18 +130,6 @@ describe('Global prefix', () => {
|
||||
.expect(200, { '0': 'params', tenantId: 'test' });
|
||||
});
|
||||
|
||||
it(`should execute middleware only once`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/'] });
|
||||
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
|
||||
await request(server)
|
||||
.get('/')
|
||||
.expect(200, 'Extras: Data attached in middleware, Count: 1');
|
||||
await request(server).get('/api/count').expect(200, '2');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -26,14 +26,4 @@ export class AppController {
|
||||
postTest(): string {
|
||||
return 'test';
|
||||
}
|
||||
|
||||
@Get()
|
||||
getHome(@Req() req) {
|
||||
return 'Extras: ' + req.extras?.data + ', Count: ' + req.count;
|
||||
}
|
||||
|
||||
@Get('count')
|
||||
getCount(@Req() req) {
|
||||
return req.count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ export const MIDDLEWARE_PARAM_VALUE = 'middleware_param';
|
||||
controllers: [AppController],
|
||||
})
|
||||
export class AppModule {
|
||||
private count = 0;
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.end(MIDDLEWARE_VALUE))
|
||||
@@ -28,12 +27,6 @@ export class AppModule {
|
||||
req.middlewareParams = req.params;
|
||||
next();
|
||||
})
|
||||
.forRoutes({ path: '*', method: RequestMethod.GET })
|
||||
.apply((req, res, next) => {
|
||||
this.count += 1;
|
||||
req.count = this.count;
|
||||
next();
|
||||
})
|
||||
.forRoutes('*');
|
||||
.forRoutes({ path: '*', method: RequestMethod.GET });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import { INestApplication, VersioningType } from '@nestjs/common';
|
||||
import {
|
||||
FastifyAdapter,
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
/**
|
||||
* `.enableVersioning()` uses `VersioningType.URI` type by default
|
||||
* Regression test for #13496
|
||||
* @see [Versioning](https://docs.nestjs.com/techniques/versioning)
|
||||
*/
|
||||
describe('Default Versioning behavior', () => {
|
||||
// ======================================================================== //
|
||||
describe('Express', () => {
|
||||
let app: INestApplication;
|
||||
before(async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
app.enableVersioning();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('GET /', () => {
|
||||
it('V1', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/v1')
|
||||
.expect(200)
|
||||
.expect('Hello World V1!');
|
||||
});
|
||||
|
||||
it('No Version', () => {
|
||||
return request(app.getHttpServer()).get('/').expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /neutral', () => {
|
||||
it('No Version', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/neutral')
|
||||
.expect(200)
|
||||
.expect('Neutral');
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
// ======================================================================== //
|
||||
describe('Fastify', () => {
|
||||
let app: INestApplication;
|
||||
before(async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter(),
|
||||
);
|
||||
app.enableVersioning();
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
});
|
||||
|
||||
describe('GET /', () => {
|
||||
it('V1', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/v1')
|
||||
.expect(200)
|
||||
.expect('Hello World V1!');
|
||||
});
|
||||
|
||||
it('No Version', () => {
|
||||
return request(app.getHttpServer()).get('/').expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /neutral', () => {
|
||||
it('No Version', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/neutral')
|
||||
.expect(200)
|
||||
.expect('Neutral');
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,5 +3,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "10.4.7"
|
||||
"version": "10.3.3"
|
||||
}
|
||||
|
||||
18366
package-lock.json
generated
18366
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
114
package.json
114
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "10.4.5",
|
||||
"version": "10.2.7",
|
||||
"description": "Modern, fast, powerful node.js web framework",
|
||||
"homepage": "https://nestjs.com",
|
||||
"repository": {
|
||||
@@ -63,75 +63,76 @@
|
||||
"class-validator": "0.14.1",
|
||||
"cli-color": "2.0.4",
|
||||
"cors": "2.8.5",
|
||||
"express": "4.21.1",
|
||||
"fast-json-stringify": "6.0.0",
|
||||
"express": "4.18.3",
|
||||
"fast-json-stringify": "5.13.0",
|
||||
"fast-safe-stringify": "2.1.1",
|
||||
"graphql-subscriptions": "2.0.0",
|
||||
"iterare": "1.2.1",
|
||||
"object-hash": "3.0.0",
|
||||
"path-to-regexp": "3.2.0",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"reflect-metadata": "0.2.1",
|
||||
"rxjs": "7.8.1",
|
||||
"socket.io": "4.8.0",
|
||||
"tslib": "2.7.0",
|
||||
"socket.io": "4.7.5",
|
||||
"tslib": "2.6.2",
|
||||
"uid": "2.0.2",
|
||||
"uuid": "10.0.0"
|
||||
"uuid": "9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/server": "4.11.0",
|
||||
"@apollo/server": "4.10.1",
|
||||
"@codechecks/client": "0.1.12",
|
||||
"@commitlint/cli": "19.5.0",
|
||||
"@commitlint/config-angular": "19.5.0",
|
||||
"@commitlint/cli": "19.1.0",
|
||||
"@commitlint/config-angular": "19.1.0",
|
||||
"@fastify/cors": "9.0.1",
|
||||
"@fastify/formbody": "7.4.0",
|
||||
"@fastify/middie": "8.3.1",
|
||||
"@fastify/multipart": "8.3.0",
|
||||
"@fastify/static": "7.0.4",
|
||||
"@fastify/view": "9.1.0",
|
||||
"@grpc/grpc-js": "1.11.1",
|
||||
"@grpc/proto-loader": "0.7.13",
|
||||
"@nestjs/apollo": "12.2.0",
|
||||
"@nestjs/graphql": "12.2.0",
|
||||
"@nestjs/mongoose": "10.0.10",
|
||||
"@fastify/middie": "8.3.0",
|
||||
"@fastify/multipart": "8.1.0",
|
||||
"@fastify/static": "7.0.1",
|
||||
"@fastify/view": "9.0.0",
|
||||
"@grpc/grpc-js": "1.10.2",
|
||||
"@grpc/proto-loader": "0.7.10",
|
||||
"@nestjs/apollo": "12.1.0",
|
||||
"@nestjs/graphql": "12.1.1",
|
||||
"@nestjs/mongoose": "10.0.4",
|
||||
"@nestjs/typeorm": "10.0.2",
|
||||
"@types/amqplib": "0.10.5",
|
||||
"@types/bytes": "3.1.4",
|
||||
"@types/chai": "4.3.16",
|
||||
"@types/chai": "4.3.11",
|
||||
"@types/chai-as-promised": "7.1.8",
|
||||
"@types/cors": "2.8.17",
|
||||
"@types/express": "4.17.21",
|
||||
"@types/gulp": "4.0.17",
|
||||
"@types/http-errors": "2.0.4",
|
||||
"@types/mocha": "10.0.8",
|
||||
"@types/node": "22.5.5",
|
||||
"@types/mocha": "10.0.6",
|
||||
"@types/node": "20.11.27",
|
||||
"@types/sinon": "17.0.3",
|
||||
"@types/supertest": "2.0.16",
|
||||
"@types/ws": "8.5.12",
|
||||
"@typescript-eslint/eslint-plugin": "7.18.0",
|
||||
"@typescript-eslint/parser": "7.18.0",
|
||||
"@types/ws": "8.5.10",
|
||||
"@typescript-eslint/eslint-plugin": "7.2.0",
|
||||
"@typescript-eslint/parser": "7.2.0",
|
||||
"amqp-connection-manager": "4.1.14",
|
||||
"amqplib": "0.10.4",
|
||||
"artillery": "2.0.20",
|
||||
"body-parser": "1.20.3",
|
||||
"amqplib": "0.10.3",
|
||||
"artillery": "2.0.7",
|
||||
"body-parser": "1.20.2",
|
||||
"bytes": "3.1.2",
|
||||
"cache-manager": "5.7.6",
|
||||
"cache-manager": "5.4.0",
|
||||
"cache-manager-redis-store": "3.0.1",
|
||||
"chai": "4.5.0",
|
||||
"chai-as-promised": "7.1.2",
|
||||
"chai": "4.4.1",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"clang-format": "1.8.0",
|
||||
"concurrently": "9.0.1",
|
||||
"conventional-changelog": "6.0.0",
|
||||
"core-js": "3.38.1",
|
||||
"commitlint-circle": "1.0.0",
|
||||
"concurrently": "8.2.2",
|
||||
"conventional-changelog": "5.1.0",
|
||||
"core-js": "3.36.0",
|
||||
"coveralls": "3.1.1",
|
||||
"delete-empty": "3.0.0",
|
||||
"engine.io-client": "6.6.1",
|
||||
"eslint": "8.57.1",
|
||||
"engine.io-client": "6.5.3",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eventsource": "2.0.2",
|
||||
"fancy-log": "2.0.0",
|
||||
"fastify": "4.28.1",
|
||||
"graphql": "16.9.0",
|
||||
"fastify": "4.26.2",
|
||||
"graphql": "16.8.1",
|
||||
"graphql-tools": "9.0.1",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-clang-format": "1.0.27",
|
||||
@@ -140,38 +141,39 @@
|
||||
"gulp-typescript": "5.0.1",
|
||||
"gulp-watch": "5.0.1",
|
||||
"http-errors": "2.0.0",
|
||||
"husky": "9.1.5",
|
||||
"husky": "9.0.11",
|
||||
"imports-loader": "5.0.0",
|
||||
"ioredis": "5.4.1",
|
||||
"ioredis": "5.3.2",
|
||||
"json-loader": "0.5.7",
|
||||
"kafkajs": "2.2.4",
|
||||
"lerna": "2.11.0",
|
||||
"lerna-changelog": "2.2.0",
|
||||
"light-my-request": "6.1.0",
|
||||
"lint-staged": "15.2.10",
|
||||
"light-my-request": "5.12.0",
|
||||
"lint-staged": "15.2.2",
|
||||
"markdown-table": "2.0.0",
|
||||
"mocha": "10.7.3",
|
||||
"mongoose": "8.6.2",
|
||||
"mqtt": "5.6.0",
|
||||
"merge-graphql-schemas": "1.7.8",
|
||||
"mocha": "10.3.0",
|
||||
"mongoose": "8.2.1",
|
||||
"mqtt": "5.4.0",
|
||||
"multer": "1.4.4",
|
||||
"mysql2": "3.11.3",
|
||||
"nats": "2.28.2",
|
||||
"nodemon": "3.1.5",
|
||||
"mysql2": "3.9.2",
|
||||
"nats": "2.19.0",
|
||||
"nodemon": "3.1.0",
|
||||
"nyc": "14.1.1",
|
||||
"prettier": "3.3.3",
|
||||
"redis": "4.7.0",
|
||||
"prettier": "3.2.5",
|
||||
"redis": "4.6.13",
|
||||
"rxjs-compat": "6.6.7",
|
||||
"sinon": "19.0.2",
|
||||
"sinon": "17.0.1",
|
||||
"sinon-chai": "3.7.0",
|
||||
"socket.io-client": "4.8.0",
|
||||
"socket.io-client": "4.7.5",
|
||||
"subscriptions-transport-ws": "0.11.0",
|
||||
"supertest": "7.0.0",
|
||||
"ts-morph": "23.0.0",
|
||||
"supertest": "6.3.3",
|
||||
"ts-morph": "22.0.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typeorm": "0.3.20",
|
||||
"typescript": "5.6.2",
|
||||
"typescript": "5.4.2",
|
||||
"wrk": "1.2.1",
|
||||
"ws": "8.18.0"
|
||||
"ws": "8.16.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
|
||||
Nest is a framework for building efficient, scalable <a href="https://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="https://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also provides compatibility with a wide range of other libraries, like <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad of third-party plugins which are available.</p>
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
|
||||
## Philosophy
|
||||
|
||||
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a>, and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a>, which improve developer productivity and enable the construction of fast, testable, and extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers, and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
||||
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, and loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
||||
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a> and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a> which improve developer productivity and enable the construction of fast, testable, extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
||||
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
||||
|
||||
## Getting started
|
||||
|
||||
@@ -57,84 +57,71 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Principal Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://microsoft.com/" target="_blank"><img src="https://nestjs.com/img/logos/microsoft-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://mojam.co" target="_blank"><img src="https://nestjs.com/img/logos/mojam-logo.png" width="80" valign="middle" /></a></td>
|
||||
<td><a href="https://marblism.com?utm_source=nest" target="_blank"><img src="https://nestjs.com/img/logos/marblism-logo.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="170" valign="middle" /></a></td>
|
||||
<td><a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/logos/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/logos/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/logos/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/logos/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/logos/jetbrains-logo.svg" width="90" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/logos/snyk-logo-black.png" width="185" valign="middle" /></a></td>
|
||||
<td><a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/logos/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/logos/ridi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/logos/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/logos/skunk-logo.png" height="60" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></tr><tr>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="90" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td><td>
|
||||
<a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/ridi-logo.svg" width="105" valign="middle" /></a></td><td>
|
||||
<a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
</tr><tr><td>
|
||||
<a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/skunk-logo.png" height="60" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.mercedes-benz.com/" target="_blank"><img src="https://nestjs.com/img/logos/mercedes-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td><a href="https://www.dinii.jp/" target="_blank"><img src="https://nestjs.com/img/logos/dinii-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://bloodycase.com/?promocode=NEST" target="_blank"><img src="https://nestjs.com/img/logos/bloodycase-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://handsontable.com/docs/react-data-grid/?utm_source=NestJS_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024" target="_blank"><img src="https://nestjs.com/img/logos/handsontable-logo.svg" width="150" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.itflashcards.com/" target="_blank"><img src="https://nestjs.com/img/logos/it_flashcards-logo.png" width="170" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://arcjet.com/?ref=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/arcjet-logo.svg" width="170" valign="middle" /></a></td>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td>
|
||||
<td><a href="https://twistag.com/" target="_blank"><img src="https://nestjs.com/img/twistag-logo.png" width="120" valign="middle" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/logos/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/logos/novologic.png" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/logos/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/logos/triplebyte.png" width="107" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/logos/nearpod-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/logos/genuinebee.svg" width="97" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/logos/vpn-review-logo.png" width="85" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/logos/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/logos/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/logos/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/logos/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/logos/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/logos/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/logos/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/logos/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/logos/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/logos/julienferand-logo.jpeg" width="55" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/logos/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/logos/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.route4me.com/" target="_blank"><img src="https://nestjs.com/img/logos/route4me-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.slotsup.com/" target="_blank"><img src="https://nestjs.com/img/logos/slotsup-logo.png" width="60" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<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://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" width="95" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" width="107" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/julienferand-logo.jpeg" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
PARAMTYPES_METADATA,
|
||||
PROPERTY_DEPS_METADATA,
|
||||
SELF_DECLARED_DEPS_METADATA,
|
||||
} from '../../constants';
|
||||
@@ -37,13 +36,13 @@ import { isUndefined } from '../../utils/shared.utils';
|
||||
export function Inject<T = any>(
|
||||
token?: T,
|
||||
): PropertyDecorator & ParameterDecorator {
|
||||
const injectCallHasArguments = arguments.length > 0;
|
||||
|
||||
return (target: object, key: string | symbol | undefined, index?: number) => {
|
||||
let type = token || Reflect.getMetadata('design:type', target, key);
|
||||
// Try to infer the token in a constructor-based injection
|
||||
if (!type && !injectCallHasArguments) {
|
||||
type = Reflect.getMetadata(PARAMTYPES_METADATA, target, key)?.[index];
|
||||
const type = token || Reflect.getMetadata('design:type', target, key);
|
||||
|
||||
if (!type) {
|
||||
throw new Error(`Token is undefined at index: ${index}. This often occurs due to circular dependencies.
|
||||
Ensure there are no circular dependencies in your files or barrel files.
|
||||
For more details, refer to https://trilon.io/blog/avoiding-circular-dependencies-in-nestjs.`);
|
||||
}
|
||||
|
||||
if (!isUndefined(index)) {
|
||||
|
||||
@@ -15,7 +15,7 @@ export type CustomDecorator<TKey = string> = MethodDecorator &
|
||||
*
|
||||
* Example: `@SetMetadata('roles', ['admin'])`
|
||||
*
|
||||
* @see [Reflection](https://docs.nestjs.com/fundamentals/execution-context#reflection-and-metadata)
|
||||
* @see [Reflection](https://docs.nestjs.com/guards#reflection)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -508,53 +508,6 @@ export function Body(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route handler parameter decorator. Extracts the `rawBody` Buffer
|
||||
* property from the `req` object and populates the decorated parameter with that value.
|
||||
*
|
||||
* For example:
|
||||
* ```typescript
|
||||
* async create(@RawBody() rawBody: Buffer | undefined)
|
||||
* ```
|
||||
*
|
||||
* @see [Request object](https://docs.nestjs.com/controllers#request-object)
|
||||
* @see [Raw body](https://docs.nestjs.com/faq/raw-body)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function RawBody(): ParameterDecorator;
|
||||
|
||||
/**
|
||||
* Route handler parameter decorator. Extracts the `rawBody` Buffer
|
||||
* property from the `req` object and populates the decorated parameter with that value.
|
||||
* Also applies pipes to the bound rawBody parameter.
|
||||
*
|
||||
* For example:
|
||||
* ```typescript
|
||||
* async create(@RawBody(new ValidationPipe()) rawBody: Buffer)
|
||||
* ```
|
||||
*
|
||||
* @param pipes one or more pipes - either instances or classes - to apply to
|
||||
* the bound body parameter.
|
||||
*
|
||||
* @see [Request object](https://docs.nestjs.com/controllers#request-object)
|
||||
* @see [Raw body](https://docs.nestjs.com/faq/raw-body)
|
||||
* @see [Working with pipes](https://docs.nestjs.com/custom-decorators#working-with-pipes)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function RawBody(
|
||||
...pipes: (
|
||||
| Type<PipeTransform<Buffer | undefined>>
|
||||
| PipeTransform<Buffer | undefined>
|
||||
)[]
|
||||
): ParameterDecorator {
|
||||
return createPipesRouteParamDecorator(RouteParamtypes.RAW_BODY)(
|
||||
undefined,
|
||||
...pipes,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Route handler parameter decorator. Extracts the `params`
|
||||
* property from the `req` object and populates the decorated
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
export enum RouteParamtypes {
|
||||
REQUEST = 0,
|
||||
RESPONSE = 1,
|
||||
NEXT = 2,
|
||||
BODY = 3,
|
||||
QUERY = 4,
|
||||
PARAM = 5,
|
||||
HEADERS = 6,
|
||||
SESSION = 7,
|
||||
FILE = 8,
|
||||
FILES = 9,
|
||||
HOST = 10,
|
||||
IP = 11,
|
||||
RAW_BODY = 12,
|
||||
REQUEST,
|
||||
RESPONSE,
|
||||
NEXT,
|
||||
BODY,
|
||||
QUERY,
|
||||
PARAM,
|
||||
HEADERS,
|
||||
SESSION,
|
||||
FILE,
|
||||
FILES,
|
||||
HOST,
|
||||
IP,
|
||||
}
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
/**
|
||||
* Options for `StreamableFile`
|
||||
*
|
||||
* @see [Streaming files](https://docs.nestjs.com/techniques/streaming-files)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface StreamableFileOptions {
|
||||
/**
|
||||
* The value that will be used for the `Content-Type` response header.
|
||||
* @default `"application/octet-stream"`
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* The value that will be used for the `Content-Disposition` response header.
|
||||
*/
|
||||
disposition?: string;
|
||||
/**
|
||||
* The value that will be used for the `Content-Length` response header.
|
||||
*/
|
||||
length?: number;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MiddlewareConsumer } from './middleware-consumer.interface';
|
||||
*/
|
||||
export interface MiddlewareConfigProxy {
|
||||
/**
|
||||
* Routes to exclude from the current middleware.
|
||||
* Excludes routes from the currently processed middleware.
|
||||
*
|
||||
* @param {(string | RouteInfo)[]} routes
|
||||
* @returns {MiddlewareConfigProxy}
|
||||
@@ -15,9 +15,8 @@ export interface MiddlewareConfigProxy {
|
||||
exclude(...routes: (string | RouteInfo)[]): MiddlewareConfigProxy;
|
||||
|
||||
/**
|
||||
* Attaches either routes or controllers to the current middleware.
|
||||
* If you pass a controller class, Nest will attach the current middleware to every path
|
||||
* defined within it.
|
||||
* Attaches passed either routes or controllers to the currently configured middleware.
|
||||
* If you pass a class, Nest would attach middleware to every path defined within this controller.
|
||||
*
|
||||
* @param {(string | Type | RouteInfo)[]} routes
|
||||
* @returns {MiddlewareConsumer}
|
||||
|
||||
@@ -44,4 +44,13 @@ export class NestApplicationContextOptions {
|
||||
* @default false
|
||||
*/
|
||||
snapshot?: boolean;
|
||||
|
||||
/**
|
||||
* Determines what algorithm use to generate module ids.
|
||||
* When set to `deep-hash`, the module id is generated based on the serialized module definition.
|
||||
* When set to `reference`, each module obtains a unique id based on its reference.
|
||||
*
|
||||
* @default 'reference'
|
||||
*/
|
||||
moduleIdGeneratorAlgorithm?: 'deep-hash' | 'reference';
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/common",
|
||||
"version": "10.4.7",
|
||||
"version": "10.3.3",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"homepage": "https://nestjs.com",
|
||||
@@ -19,7 +19,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iterare": "1.2.1",
|
||||
"tslib": "2.7.0",
|
||||
"tslib": "2.6.2",
|
||||
"uid": "2.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
export * from './default-value.pipe';
|
||||
export * from './file';
|
||||
export * from './parse-array.pipe';
|
||||
export * from './parse-bool.pipe';
|
||||
export * from './parse-date.pipe';
|
||||
export * from './parse-enum.pipe';
|
||||
export * from './parse-float.pipe';
|
||||
export * from './parse-int.pipe';
|
||||
export * from './parse-float.pipe';
|
||||
export * from './parse-enum.pipe';
|
||||
export * from './parse-uuid.pipe';
|
||||
export * from './validation.pipe';
|
||||
export * from './file';
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
PipeTransform,
|
||||
} from '../interfaces/features/pipe-transform.interface';
|
||||
import { HttpErrorByCode } from '../utils/http-error-by-code.util';
|
||||
import { isNil, isString, isUndefined } from '../utils/shared.utils';
|
||||
import { isNil, isUndefined, isString } from '../utils/shared.utils';
|
||||
import { ValidationPipe, ValidationPipeOptions } from './validation.pipe';
|
||||
|
||||
const VALIDATION_ERROR_MESSAGE = 'Validation failed (parsable array expected)';
|
||||
@@ -21,26 +21,9 @@ export interface ParseArrayOptions
|
||||
ValidationPipeOptions,
|
||||
'transform' | 'validateCustomDecorators' | 'exceptionFactory'
|
||||
> {
|
||||
/**
|
||||
* Type for items to be converted into
|
||||
*/
|
||||
items?: Type<unknown>;
|
||||
/**
|
||||
* Items separator to split string by
|
||||
* @default ','
|
||||
*/
|
||||
separator?: string;
|
||||
/**
|
||||
* If true, the pipe will return null or undefined if the value is not provided
|
||||
* @default false
|
||||
*/
|
||||
optional?: boolean;
|
||||
/**
|
||||
* A factory function that returns an exception object to be thrown
|
||||
* if validation fails.
|
||||
* @param error Error message or object
|
||||
* @returns The exception object
|
||||
*/
|
||||
exceptionFactory?: (error: any) => any;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,21 +15,8 @@ import { isNil } from '../utils/shared.utils';
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ParseBoolPipeOptions {
|
||||
/**
|
||||
* The HTTP status code to be used in the response when the validation fails.
|
||||
*/
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
/**
|
||||
* A factory function that returns an exception object to be thrown
|
||||
* if validation fails.
|
||||
* @param error Error message
|
||||
* @returns The exception object
|
||||
*/
|
||||
exceptionFactory?: (error: string) => any;
|
||||
/**
|
||||
* If true, the pipe will return null or undefined if the value is not provided
|
||||
* @default false
|
||||
*/
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import { Injectable } from '../decorators/core/injectable.decorator';
|
||||
import { HttpStatus } from '../enums/http-status.enum';
|
||||
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
|
||||
import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
import { isNil } from '../utils/shared.utils';
|
||||
|
||||
export interface ParseDatePipeOptions {
|
||||
/**
|
||||
* If true, the pipe will return null or undefined if the value is not provided
|
||||
* @default false
|
||||
*/
|
||||
optional?: boolean;
|
||||
/**
|
||||
* Default value for the date
|
||||
*/
|
||||
default?: () => Date;
|
||||
/**
|
||||
* The HTTP status code to be used in the response when the validation fails.
|
||||
*/
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
/**
|
||||
* A factory function that returns an exception object to be thrown
|
||||
* if validation fails.
|
||||
* @param error Error message
|
||||
* @returns The exception object
|
||||
*/
|
||||
exceptionFactory?: (error: string) => any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ParseDatePipe
|
||||
implements PipeTransform<string | number | undefined | null>
|
||||
{
|
||||
protected exceptionFactory: (error: string) => any;
|
||||
|
||||
constructor(private readonly options: ParseDatePipeOptions = {}) {
|
||||
const { exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST } =
|
||||
options;
|
||||
|
||||
this.exceptionFactory =
|
||||
exceptionFactory ||
|
||||
(error => new HttpErrorByCode[errorHttpStatusCode](error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that accesses and performs optional transformation on argument for
|
||||
* in-flight requests.
|
||||
*
|
||||
* @param value currently processed route argument
|
||||
* @param metadata contains metadata about the currently processed route argument
|
||||
*/
|
||||
transform(value: string | number | undefined | null): Date {
|
||||
if (this.options.optional && isNil(value)) {
|
||||
return this.options.default
|
||||
? this.options.default()
|
||||
: (value as undefined | null);
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
throw this.exceptionFactory('Validation failed (no Date provided)');
|
||||
}
|
||||
|
||||
const transformedValue = new Date(value);
|
||||
|
||||
if (isNaN(transformedValue.getTime())) {
|
||||
throw this.exceptionFactory('Validation failed (invalid date format)');
|
||||
}
|
||||
|
||||
return transformedValue;
|
||||
}
|
||||
}
|
||||
@@ -11,21 +11,8 @@ import { isNil } from '../utils/shared.utils';
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ParseEnumPipeOptions {
|
||||
/**
|
||||
* If true, the pipe will return null or undefined if the value is not provided
|
||||
* @default false
|
||||
*/
|
||||
optional?: boolean;
|
||||
/**
|
||||
* The HTTP status code to be used in the response when the validation fails.
|
||||
*/
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
/**
|
||||
* A factory function that returns an exception object to be thrown
|
||||
* if validation fails.
|
||||
* @param error Error message
|
||||
* @returns The exception object
|
||||
*/
|
||||
exceptionFactory?: (error: string) => any;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,21 +11,8 @@ import { isNil } from '../utils/shared.utils';
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ParseFloatPipeOptions {
|
||||
/**
|
||||
* The HTTP status code to be used in the response when the validation fails.
|
||||
*/
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
/**
|
||||
* A factory function that returns an exception object to be thrown
|
||||
* if validation fails.
|
||||
* @param error Error message
|
||||
* @returns The exception object
|
||||
*/
|
||||
exceptionFactory?: (error: string) => any;
|
||||
/**
|
||||
* If true, the pipe will return null or undefined if the value is not provided
|
||||
* @default false
|
||||
*/
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,21 +15,8 @@ import { isNil } from '../utils/shared.utils';
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ParseIntPipeOptions {
|
||||
/**
|
||||
* The HTTP status code to be used in the response when the validation fails.
|
||||
*/
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
/**
|
||||
* A factory function that returns an exception object to be thrown
|
||||
* if validation fails.
|
||||
* @param error Error message
|
||||
* @returns The exception object
|
||||
*/
|
||||
exceptionFactory?: (error: string) => any;
|
||||
/**
|
||||
* If true, the pipe will return null or undefined if the value is not provided
|
||||
* @default false
|
||||
*/
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,25 +15,9 @@ import { isNil, isString } from '../utils/shared.utils';
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ParseUUIDPipeOptions {
|
||||
/**
|
||||
* UUID version to validate
|
||||
*/
|
||||
version?: '3' | '4' | '5' | '7';
|
||||
/**
|
||||
* The HTTP status code to be used in the response when the validation fails.
|
||||
*/
|
||||
version?: '3' | '4' | '5';
|
||||
errorHttpStatusCode?: ErrorHttpStatusCode;
|
||||
/**
|
||||
* A factory function that returns an exception object to be thrown
|
||||
* if validation fails.
|
||||
* @param error Error message
|
||||
* @returns The exception object
|
||||
*/
|
||||
exceptionFactory?: (errors: string) => any;
|
||||
/**
|
||||
* If true, the pipe will return null or undefined if the value is not provided
|
||||
* @default false
|
||||
*/
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
@@ -50,10 +34,9 @@ export class ParseUUIDPipe implements PipeTransform<string> {
|
||||
3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
|
||||
4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
|
||||
5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
|
||||
7: /^[0-9A-F]{8}-[0-9A-F]{4}-7[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
|
||||
all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
|
||||
};
|
||||
private readonly version: '3' | '4' | '5' | '7';
|
||||
private readonly version: '3' | '4' | '5';
|
||||
protected exceptionFactory: (errors: string) => any;
|
||||
|
||||
constructor(@Optional() protected readonly options?: ParseUUIDPipeOptions) {
|
||||
|
||||
@@ -22,4 +22,14 @@ describe('@Inject', () => {
|
||||
];
|
||||
expect(metadata).to.be.eql(expectedMetadata);
|
||||
});
|
||||
|
||||
it('should throw an error when token is undefined', () => {
|
||||
const defineInvalidClass = () => {
|
||||
class Test {
|
||||
constructor(@Inject(undefined) invalidParam) {}
|
||||
}
|
||||
};
|
||||
|
||||
expect(defineInvalidClass).to.throw(/^Token is undefined/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import { BadRequestException } from '../../exceptions';
|
||||
import { ParseDatePipe } from '../../pipes/parse-date.pipe';
|
||||
|
||||
describe('ParseDatePipe', () => {
|
||||
let target: ParseDatePipe;
|
||||
|
||||
beforeEach(() => {
|
||||
target = new ParseDatePipe();
|
||||
});
|
||||
|
||||
describe('transform', () => {
|
||||
describe('when validation passes', () => {
|
||||
it('should return a valid date object', () => {
|
||||
const date = new Date().toISOString();
|
||||
|
||||
const transformedDate = target.transform(date);
|
||||
expect(transformedDate).to.be.instanceOf(Date);
|
||||
expect(transformedDate.toISOString()).to.equal(date);
|
||||
|
||||
const asNumber = transformedDate.getTime();
|
||||
const transformedNumber = target.transform(asNumber);
|
||||
expect(transformedNumber).to.be.instanceOf(Date);
|
||||
expect(transformedNumber.getTime()).to.equal(asNumber);
|
||||
});
|
||||
|
||||
it('should not throw an error if the value is undefined/null and optional is true', () => {
|
||||
const target = new ParseDatePipe({ optional: true });
|
||||
const value = target.transform(undefined);
|
||||
expect(value).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
describe('when default value is provided', () => {
|
||||
it('should return the default value if the value is undefined/null', () => {
|
||||
const defaultValue = new Date();
|
||||
const target = new ParseDatePipe({
|
||||
optional: true,
|
||||
default: () => defaultValue,
|
||||
});
|
||||
const value = target.transform(undefined);
|
||||
expect(value).to.equal(defaultValue);
|
||||
});
|
||||
});
|
||||
describe('when validation fails', () => {
|
||||
it('should throw an error', () => {
|
||||
try {
|
||||
target.transform('123abc');
|
||||
expect.fail();
|
||||
} catch (error) {
|
||||
expect(error).to.be.instanceOf(BadRequestException);
|
||||
expect(error.message).to.equal(
|
||||
'Validation failed (invalid date format)',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('when empty value', () => {
|
||||
it('should throw an error', () => {
|
||||
try {
|
||||
target.transform('');
|
||||
expect.fail();
|
||||
} catch (error) {
|
||||
expect(error).to.be.instanceOf(BadRequestException);
|
||||
expect(error.message).to.equal(
|
||||
'Validation failed (no Date provided)',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -478,56 +478,6 @@ describe('ValidationPipe', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('option: "validateCustomDecorators" when metadata.type is not `body`', () => {
|
||||
describe('when is set to `true`', () => {
|
||||
it('should transform and validate', async () => {
|
||||
const target = new ValidationPipe({
|
||||
validateCustomDecorators: true,
|
||||
});
|
||||
|
||||
const metadata: ArgumentMetadata = {
|
||||
type: 'custom',
|
||||
metatype: TestModel,
|
||||
data: '',
|
||||
};
|
||||
|
||||
const testObj = { prop1: 'value1', prop2: 'value2' };
|
||||
expect(await target.transform(testObj, metadata)).to.not.be.undefined;
|
||||
});
|
||||
});
|
||||
describe('when is set to `false`', () => {
|
||||
it('should throw an error', async () => {
|
||||
const target = new ValidationPipe({
|
||||
validateCustomDecorators: false,
|
||||
});
|
||||
|
||||
const metadata: ArgumentMetadata = {
|
||||
type: 'custom',
|
||||
metatype: TestModel,
|
||||
data: '',
|
||||
};
|
||||
|
||||
const objNotFollowingTestModel = { prop1: undefined, prop2: 'value2' };
|
||||
expect(await target.transform(objNotFollowingTestModel, metadata)).to
|
||||
.not.be.undefined;
|
||||
});
|
||||
});
|
||||
describe('when is not supplied', () => {
|
||||
it('should transform and validate', async () => {
|
||||
const target = new ValidationPipe({});
|
||||
|
||||
const metadata: ArgumentMetadata = {
|
||||
type: 'custom',
|
||||
metatype: TestModel,
|
||||
data: '',
|
||||
};
|
||||
|
||||
const testObj = { prop1: 'value1', prop2: 'value2' };
|
||||
expect(await target.transform(testObj, metadata)).to.not.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('option: "errorHttpStatusCode"', () => {
|
||||
describe('when validation fails', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Logger } from '../services/logger.service';
|
||||
|
||||
const MISSING_REQUIRED_DEPENDENCY = (name: string, reason: string) =>
|
||||
`The "${name}" package is missing. Please, make sure to install it to take advantage of ${reason}.`;
|
||||
`The "${name}" package is missing. Please, make sure to install this library ($ npm install ${name}) to take advantage of ${reason}.`;
|
||||
|
||||
const logger = new Logger('PackageLoader');
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
|
||||
Nest is a framework for building efficient, scalable <a href="https://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="https://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also provides compatibility with a wide range of other libraries, like <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad of third-party plugins which are available.</p>
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
|
||||
## Philosophy
|
||||
|
||||
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a>, and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a>, which improve developer productivity and enable the construction of fast, testable, and extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers, and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
||||
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, and loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
||||
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a> and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a> which improve developer productivity and enable the construction of fast, testable, extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
||||
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
||||
|
||||
## Getting started
|
||||
|
||||
@@ -57,84 +57,71 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Principal Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://microsoft.com/" target="_blank"><img src="https://nestjs.com/img/logos/microsoft-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://mojam.co" target="_blank"><img src="https://nestjs.com/img/logos/mojam-logo.png" width="80" valign="middle" /></a></td>
|
||||
<td><a href="https://marblism.com?utm_source=nest" target="_blank"><img src="https://nestjs.com/img/logos/marblism-logo.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="170" valign="middle" /></a></td>
|
||||
<td><a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/logos/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/logos/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/logos/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/logos/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/logos/jetbrains-logo.svg" width="90" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/logos/snyk-logo-black.png" width="185" valign="middle" /></a></td>
|
||||
<td><a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/logos/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/logos/ridi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/logos/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/logos/skunk-logo.png" height="60" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></tr><tr>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="90" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td><td>
|
||||
<a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/ridi-logo.svg" width="105" valign="middle" /></a></td><td>
|
||||
<a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
</tr><tr><td>
|
||||
<a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/skunk-logo.png" height="60" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.mercedes-benz.com/" target="_blank"><img src="https://nestjs.com/img/logos/mercedes-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td><a href="https://www.dinii.jp/" target="_blank"><img src="https://nestjs.com/img/logos/dinii-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://bloodycase.com/?promocode=NEST" target="_blank"><img src="https://nestjs.com/img/logos/bloodycase-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://handsontable.com/docs/react-data-grid/?utm_source=NestJS_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024" target="_blank"><img src="https://nestjs.com/img/logos/handsontable-logo.svg" width="150" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.itflashcards.com/" target="_blank"><img src="https://nestjs.com/img/logos/it_flashcards-logo.png" width="170" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://arcjet.com/?ref=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/arcjet-logo.svg" width="170" valign="middle" /></a></td>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td>
|
||||
<td><a href="https://twistag.com/" target="_blank"><img src="https://nestjs.com/img/twistag-logo.png" width="120" valign="middle" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/logos/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/logos/novologic.png" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/logos/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/logos/triplebyte.png" width="107" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/logos/nearpod-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/logos/genuinebee.svg" width="97" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/logos/vpn-review-logo.png" width="85" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/logos/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/logos/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/logos/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/logos/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/logos/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/logos/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/logos/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/logos/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/logos/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/logos/julienferand-logo.jpeg" width="55" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/logos/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/logos/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.route4me.com/" target="_blank"><img src="https://nestjs.com/img/logos/route4me-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.slotsup.com/" target="_blank"><img src="https://nestjs.com/img/logos/slotsup-logo.png" width="60" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<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://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" width="95" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" width="107" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/julienferand-logo.jpeg" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ export class DiscoverableMetaHostCollection {
|
||||
// But since calling `wrapper.instance` could degrade overall performance
|
||||
// we must defer it as much we can.
|
||||
instanceWrapper.metatype || instanceWrapper.inject
|
||||
? (instanceWrapper.instance?.constructor ?? instanceWrapper.metatype)
|
||||
? instanceWrapper.instance?.constructor ?? instanceWrapper.metatype
|
||||
: instanceWrapper.metatype,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export class ExceptionsZone {
|
||||
public static run(
|
||||
callback: () => void,
|
||||
teardown: (err: any) => void = DEFAULT_TEARDOWN,
|
||||
autoFlushLogs: boolean,
|
||||
autoFlushLogs?: boolean,
|
||||
) {
|
||||
try {
|
||||
callback();
|
||||
@@ -25,7 +25,7 @@ export class ExceptionsZone {
|
||||
public static async asyncRun(
|
||||
callback: () => Promise<void>,
|
||||
teardown: (err: any) => void = DEFAULT_TEARDOWN,
|
||||
autoFlushLogs: boolean,
|
||||
autoFlushLogs?: boolean,
|
||||
) {
|
||||
try {
|
||||
await callback();
|
||||
|
||||
@@ -89,11 +89,15 @@ export class ContextUtils {
|
||||
instance?: object,
|
||||
callback?: Function,
|
||||
): (args: unknown[]) => ExecutionContextHost {
|
||||
const type = instance && (instance.constructor as Type<unknown>);
|
||||
return (args: unknown[]) => {
|
||||
const ctx = new ExecutionContextHost(args, type, callback);
|
||||
const contextFactory = (args: unknown[]) => {
|
||||
const ctx = new ExecutionContextHost(
|
||||
args,
|
||||
instance && (instance.constructor as Type<unknown>),
|
||||
callback,
|
||||
);
|
||||
ctx.setType(contextType);
|
||||
return ctx;
|
||||
};
|
||||
return contextFactory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ForwardReference,
|
||||
Type,
|
||||
} from '@nestjs/common/interfaces';
|
||||
import { ModuleTokenFactory } from './module-token-factory';
|
||||
import { ModuleOpaqueKeyFactory } from './opaque-key-factory/interfaces/module-opaque-key-factory.interface';
|
||||
|
||||
export interface ModuleFactory {
|
||||
type: Type<any>;
|
||||
@@ -12,36 +12,59 @@ export interface ModuleFactory {
|
||||
}
|
||||
|
||||
export class ModuleCompiler {
|
||||
constructor(private readonly moduleTokenFactory = new ModuleTokenFactory()) {}
|
||||
constructor(
|
||||
private readonly _moduleOpaqueKeyFactory: ModuleOpaqueKeyFactory,
|
||||
) {}
|
||||
|
||||
get moduleOpaqueKeyFactory(): ModuleOpaqueKeyFactory {
|
||||
return this._moduleOpaqueKeyFactory;
|
||||
}
|
||||
|
||||
public async compile(
|
||||
metatype: Type<any> | DynamicModule | Promise<DynamicModule>,
|
||||
moduleClsOrDynamic:
|
||||
| Type
|
||||
| DynamicModule
|
||||
| ForwardReference
|
||||
| Promise<DynamicModule>,
|
||||
): Promise<ModuleFactory> {
|
||||
const { type, dynamicMetadata } = this.extractMetadata(await metatype);
|
||||
const token = this.moduleTokenFactory.create(type, dynamicMetadata);
|
||||
moduleClsOrDynamic = await moduleClsOrDynamic;
|
||||
|
||||
const { type, dynamicMetadata } = this.extractMetadata(moduleClsOrDynamic);
|
||||
const token = dynamicMetadata
|
||||
? this._moduleOpaqueKeyFactory.createForDynamic(
|
||||
type,
|
||||
dynamicMetadata,
|
||||
moduleClsOrDynamic as DynamicModule | ForwardReference,
|
||||
)
|
||||
: this._moduleOpaqueKeyFactory.createForStatic(
|
||||
type,
|
||||
moduleClsOrDynamic as Type,
|
||||
);
|
||||
|
||||
return { type, dynamicMetadata, token };
|
||||
}
|
||||
|
||||
public extractMetadata(
|
||||
metatype: Type<any> | ForwardReference | DynamicModule,
|
||||
moduleClsOrDynamic: Type | ForwardReference | DynamicModule,
|
||||
): {
|
||||
type: Type<any>;
|
||||
dynamicMetadata?: Partial<DynamicModule> | undefined;
|
||||
type: Type;
|
||||
dynamicMetadata: Omit<DynamicModule, 'module'> | undefined;
|
||||
} {
|
||||
if (!this.isDynamicModule(metatype)) {
|
||||
if (!this.isDynamicModule(moduleClsOrDynamic)) {
|
||||
return {
|
||||
type: (metatype as ForwardReference)?.forwardRef
|
||||
? (metatype as ForwardReference).forwardRef()
|
||||
: metatype,
|
||||
type: (moduleClsOrDynamic as ForwardReference)?.forwardRef
|
||||
? (moduleClsOrDynamic as ForwardReference).forwardRef()
|
||||
: moduleClsOrDynamic,
|
||||
dynamicMetadata: undefined,
|
||||
};
|
||||
}
|
||||
const { module: type, ...dynamicMetadata } = metatype;
|
||||
const { module: type, ...dynamicMetadata } = moduleClsOrDynamic;
|
||||
return { type, dynamicMetadata };
|
||||
}
|
||||
|
||||
public isDynamicModule(
|
||||
module: Type<any> | DynamicModule | ForwardReference,
|
||||
): module is DynamicModule {
|
||||
return !!(module as DynamicModule).module;
|
||||
moduleClsOrDynamic: Type | DynamicModule | ForwardReference,
|
||||
): moduleClsOrDynamic is DynamicModule {
|
||||
return !!(moduleClsOrDynamic as DynamicModule).module;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
GLOBAL_MODULE_METADATA,
|
||||
} from '@nestjs/common/constants';
|
||||
import { Injectable, Type } from '@nestjs/common/interfaces';
|
||||
import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface';
|
||||
import { ApplicationConfig } from '../application-config';
|
||||
import { DiscoverableMetaHostCollection } from '../discovery/discoverable-meta-host-collection';
|
||||
import {
|
||||
@@ -19,16 +20,16 @@ import { ContextId } from './instance-wrapper';
|
||||
import { InternalCoreModule } from './internal-core-module/internal-core-module';
|
||||
import { InternalProvidersStorage } from './internal-providers-storage';
|
||||
import { Module } from './module';
|
||||
import { ModuleTokenFactory } from './module-token-factory';
|
||||
import { ModulesContainer } from './modules-container';
|
||||
import { ByReferenceModuleOpaqueKeyFactory } from './opaque-key-factory/by-reference-module-opaque-key-factory';
|
||||
import { DeepHashedModuleOpaqueKeyFactory } from './opaque-key-factory/deep-hashed-module-opaque-key-factory';
|
||||
import { ModuleOpaqueKeyFactory } from './opaque-key-factory/interfaces/module-opaque-key-factory.interface';
|
||||
|
||||
type ModuleMetatype = Type<any> | DynamicModule | Promise<DynamicModule>;
|
||||
type ModuleScope = Type<any>[];
|
||||
|
||||
export class NestContainer {
|
||||
private readonly globalModules = new Set<Module>();
|
||||
private readonly moduleTokenFactory = new ModuleTokenFactory();
|
||||
private readonly moduleCompiler = new ModuleCompiler(this.moduleTokenFactory);
|
||||
private readonly modules = new ModulesContainer();
|
||||
private readonly dynamicModulesMetadata = new Map<
|
||||
string,
|
||||
@@ -36,11 +37,27 @@ export class NestContainer {
|
||||
>();
|
||||
private readonly internalProvidersStorage = new InternalProvidersStorage();
|
||||
private readonly _serializedGraph = new SerializedGraph();
|
||||
private moduleCompiler: ModuleCompiler;
|
||||
private internalCoreModule: Module;
|
||||
|
||||
constructor(
|
||||
private readonly _applicationConfig: ApplicationConfig = undefined,
|
||||
) {}
|
||||
private readonly _applicationConfig:
|
||||
| ApplicationConfig
|
||||
| undefined = undefined,
|
||||
private readonly _contextOptions:
|
||||
| NestApplicationContextOptions
|
||||
| undefined = undefined,
|
||||
) {
|
||||
const moduleOpaqueKeyFactory =
|
||||
this._contextOptions?.moduleIdGeneratorAlgorithm === 'deep-hash'
|
||||
? new DeepHashedModuleOpaqueKeyFactory()
|
||||
: new ByReferenceModuleOpaqueKeyFactory({
|
||||
keyGenerationStrategy: this._contextOptions?.snapshot
|
||||
? 'shallow'
|
||||
: 'random',
|
||||
});
|
||||
this.moduleCompiler = new ModuleCompiler(moduleOpaqueKeyFactory);
|
||||
}
|
||||
|
||||
get serializedGraph(): SerializedGraph {
|
||||
return this._serializedGraph;
|
||||
@@ -313,7 +330,7 @@ export class NestContainer {
|
||||
metadataKey?: Exclude<keyof DynamicModule, 'global' | 'module'>,
|
||||
) {
|
||||
const metadata = this.dynamicModulesMetadata.get(token);
|
||||
return metadataKey ? (metadata?.[metadataKey] ?? []) : metadata;
|
||||
return metadataKey ? metadata?.[metadataKey] ?? [] : metadata;
|
||||
}
|
||||
|
||||
public registerCoreModuleRef(moduleRef: Module) {
|
||||
@@ -321,8 +338,8 @@ export class NestContainer {
|
||||
this.modules[InternalCoreModule.name] = moduleRef;
|
||||
}
|
||||
|
||||
public getModuleTokenFactory(): ModuleTokenFactory {
|
||||
return this.moduleTokenFactory;
|
||||
public getModuleTokenFactory(): ModuleOpaqueKeyFactory {
|
||||
return this.moduleCompiler.moduleOpaqueKeyFactory;
|
||||
}
|
||||
|
||||
public registerRequestProvider<T = any>(request: T, contextId: ContextId) {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Logger } from '@nestjs/common';
|
||||
import { ExternalContextCreator } from '../../helpers/external-context-creator';
|
||||
import { HttpAdapterHost } from '../../helpers/http-adapter-host';
|
||||
import { GraphInspector } from '../../inspector/graph-inspector';
|
||||
import { InitializeOnPreviewAllowlist } from '../../inspector/initialize-on-preview.allowlist';
|
||||
import { SerializedGraph } from '../../inspector/serialized-graph';
|
||||
import { ModuleOverride } from '../../interfaces/module-override.interface';
|
||||
import { DependenciesScanner } from '../../scanner';
|
||||
@@ -43,8 +42,6 @@ export class InternalCoreModuleFactory {
|
||||
);
|
||||
};
|
||||
|
||||
InitializeOnPreviewAllowlist.add(InternalCoreModule);
|
||||
|
||||
return InternalCoreModule.register([
|
||||
{
|
||||
provide: ExternalContextCreator,
|
||||
|
||||
@@ -653,7 +653,12 @@ export class Module {
|
||||
|
||||
private generateUuid(): string {
|
||||
const prefix = 'M_';
|
||||
const key = this.name?.toString() ?? this.token?.toString();
|
||||
const key = this.token
|
||||
? this.token.includes(':')
|
||||
? this.token.split(':')[1]
|
||||
: this.token
|
||||
: this.name;
|
||||
|
||||
return key ? UuidFactory.get(`${prefix}_${key}`) : randomStringGenerator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { DynamicModule } from '@nestjs/common/interfaces/modules/dynamic-module.interface';
|
||||
import { ForwardReference } from '@nestjs/common/interfaces/modules/forward-reference.interface';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
|
||||
import { createHash } from 'crypto';
|
||||
import { ModuleOpaqueKeyFactory } from './interfaces/module-opaque-key-factory.interface';
|
||||
|
||||
const K_MODULE_ID = Symbol('K_MODULE_ID');
|
||||
|
||||
export class ByReferenceModuleOpaqueKeyFactory
|
||||
implements ModuleOpaqueKeyFactory
|
||||
{
|
||||
private readonly keyGenerationStrategy: 'random' | 'shallow';
|
||||
|
||||
constructor(options?: { keyGenerationStrategy: 'random' | 'shallow' }) {
|
||||
this.keyGenerationStrategy = options?.keyGenerationStrategy ?? 'random';
|
||||
}
|
||||
|
||||
public createForStatic(
|
||||
moduleCls: Type,
|
||||
originalRef: Type | ForwardReference = moduleCls,
|
||||
): string {
|
||||
return this.getOrCreateModuleId(moduleCls, undefined, originalRef);
|
||||
}
|
||||
|
||||
public createForDynamic(
|
||||
moduleCls: Type<unknown>,
|
||||
dynamicMetadata: Omit<DynamicModule, 'module'>,
|
||||
originalRef: DynamicModule | ForwardReference,
|
||||
): string {
|
||||
return this.getOrCreateModuleId(moduleCls, dynamicMetadata, originalRef);
|
||||
}
|
||||
|
||||
private getOrCreateModuleId(
|
||||
moduleCls: Type<unknown>,
|
||||
dynamicMetadata: Partial<DynamicModule> | undefined,
|
||||
originalRef: Type | DynamicModule | ForwardReference,
|
||||
): string {
|
||||
if (originalRef[K_MODULE_ID]) {
|
||||
return originalRef[K_MODULE_ID];
|
||||
}
|
||||
|
||||
let moduleId: string;
|
||||
if (this.keyGenerationStrategy === 'random') {
|
||||
moduleId = this.generateRandomString();
|
||||
} else {
|
||||
moduleId = dynamicMetadata
|
||||
? `${this.generateRandomString()}:${this.hashString(moduleCls.name + JSON.stringify(dynamicMetadata))}`
|
||||
: `${this.generateRandomString()}:${this.hashString(moduleCls.toString())}`;
|
||||
}
|
||||
|
||||
originalRef[K_MODULE_ID] = moduleId;
|
||||
return moduleId;
|
||||
}
|
||||
|
||||
private hashString(value: string): string {
|
||||
return createHash('sha256').update(value).digest('hex');
|
||||
}
|
||||
|
||||
private generateRandomString(): string {
|
||||
return randomStringGenerator();
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,28 @@
|
||||
import { DynamicModule, Logger } from '@nestjs/common';
|
||||
import { DynamicModule } from '@nestjs/common/interfaces/modules/dynamic-module.interface';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
import { Logger } from '@nestjs/common/services/logger.service';
|
||||
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
|
||||
import { isFunction, isSymbol } from '@nestjs/common/utils/shared.utils';
|
||||
import { createHash } from 'crypto';
|
||||
import stringify from 'fast-safe-stringify';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { ModuleOpaqueKeyFactory } from './interfaces/module-opaque-key-factory.interface';
|
||||
|
||||
const CLASS_STR = 'class ';
|
||||
const CLASS_STR_LEN = CLASS_STR.length;
|
||||
|
||||
export class ModuleTokenFactory {
|
||||
private readonly moduleTokenCache = new Map<string, string>();
|
||||
export class DeepHashedModuleOpaqueKeyFactory
|
||||
implements ModuleOpaqueKeyFactory
|
||||
{
|
||||
private readonly moduleIdsCache = new WeakMap<Type<unknown>, string>();
|
||||
private readonly logger = new Logger(ModuleTokenFactory.name, {
|
||||
private readonly moduleTokenCache = new Map<string, string>();
|
||||
private readonly logger = new Logger(DeepHashedModuleOpaqueKeyFactory.name, {
|
||||
timestamp: true,
|
||||
});
|
||||
|
||||
public create(
|
||||
metatype: Type<unknown>,
|
||||
dynamicModuleMetadata?: Partial<DynamicModule> | undefined,
|
||||
): string {
|
||||
const moduleId = this.getModuleId(metatype);
|
||||
public createForStatic(moduleCls: Type): string {
|
||||
const moduleId = this.getModuleId(moduleCls);
|
||||
const moduleName = this.getModuleName(moduleCls);
|
||||
|
||||
if (!dynamicModuleMetadata) {
|
||||
return this.getStaticModuleToken(moduleId, this.getModuleName(metatype));
|
||||
}
|
||||
const opaqueToken = {
|
||||
id: moduleId,
|
||||
module: this.getModuleName(metatype),
|
||||
dynamic: dynamicModuleMetadata,
|
||||
};
|
||||
const start = performance.now();
|
||||
const opaqueTokenString = this.getStringifiedOpaqueToken(opaqueToken);
|
||||
const timeSpentInMs = performance.now() - start;
|
||||
|
||||
if (timeSpentInMs > 10) {
|
||||
const formattedTimeSpent = timeSpentInMs.toFixed(2);
|
||||
this.logger.warn(
|
||||
`The module "${opaqueToken.module}" is taking ${formattedTimeSpent}ms to serialize, this may be caused by larger objects statically assigned to the module. More details: https://github.com/nestjs/nest/issues/12738`,
|
||||
);
|
||||
}
|
||||
|
||||
return this.hashString(opaqueTokenString);
|
||||
}
|
||||
|
||||
public getStaticModuleToken(moduleId: string, moduleName: string): string {
|
||||
const key = `${moduleId}_${moduleName}`;
|
||||
if (this.moduleTokenCache.has(key)) {
|
||||
return this.moduleTokenCache.get(key);
|
||||
@@ -55,6 +33,31 @@ export class ModuleTokenFactory {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public createForDynamic(
|
||||
moduleCls: Type<unknown>,
|
||||
dynamicMetadata: Omit<DynamicModule, 'module'>,
|
||||
): string {
|
||||
const moduleId = this.getModuleId(moduleCls);
|
||||
const moduleName = this.getModuleName(moduleCls);
|
||||
const opaqueToken = {
|
||||
id: moduleId,
|
||||
module: moduleName,
|
||||
dynamic: dynamicMetadata,
|
||||
};
|
||||
const start = performance.now();
|
||||
const opaqueTokenString = this.getStringifiedOpaqueToken(opaqueToken);
|
||||
const timeSpentInMs = performance.now() - start;
|
||||
|
||||
if (timeSpentInMs > 10) {
|
||||
const formattedTimeSpent = timeSpentInMs.toFixed(2);
|
||||
this.logger.warn(
|
||||
`The module "${opaqueToken.module}" is taking ${formattedTimeSpent}ms to serialize, this may be caused by larger objects statically assigned to the module. Consider changing the "moduleIdGeneratorAlgorithm" option to "reference" to improve the performance.`,
|
||||
);
|
||||
}
|
||||
|
||||
return this.hashString(opaqueTokenString);
|
||||
}
|
||||
|
||||
public getStringifiedOpaqueToken(opaqueToken: object | undefined): string {
|
||||
// Uses safeStringify instead of JSON.stringify to support circular dynamic modules
|
||||
// The replacer function is also required in order to obtain real class names
|
||||
@@ -0,0 +1,26 @@
|
||||
import { DynamicModule } from '@nestjs/common/interfaces/modules/dynamic-module.interface';
|
||||
import { ForwardReference } from '@nestjs/common/interfaces/modules/forward-reference.interface';
|
||||
import { Type } from '@nestjs/common/interfaces/type.interface';
|
||||
|
||||
export interface ModuleOpaqueKeyFactory {
|
||||
/**
|
||||
* Creates a unique opaque key for the given static module.
|
||||
* @param moduleCls A static module class.
|
||||
* @param originalRef Original object reference. In most cases, it's the same as `moduleCls`.
|
||||
*/
|
||||
createForStatic(
|
||||
moduleCls: Type,
|
||||
originalRef: Type | ForwardReference,
|
||||
): string;
|
||||
/**
|
||||
* Creates a unique opaque key for the given dynamic module.
|
||||
* @param moduleCls A dynamic module class reference.
|
||||
* @param dynamicMetadata Dynamic module metadata.
|
||||
* @param originalRef Original object reference.
|
||||
*/
|
||||
createForDynamic(
|
||||
moduleCls: Type<unknown>,
|
||||
dynamicMetadata: Omit<DynamicModule, 'module'>,
|
||||
originalRef: DynamicModule | ForwardReference,
|
||||
): string;
|
||||
}
|
||||
@@ -144,7 +144,7 @@ export class SerializedGraph {
|
||||
if (typeof value === 'symbol') {
|
||||
return value.toString();
|
||||
}
|
||||
return typeof value === 'function' ? (value.name ?? 'Function') : value;
|
||||
return typeof value === 'function' ? value.name ?? 'Function' : value;
|
||||
};
|
||||
return JSON.stringify(this.toJSON(), replacer, 2);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ export class InterceptorsConsumer {
|
||||
return defer(AsyncResource.bind(() => this.transformDeferred(next)));
|
||||
}
|
||||
const handler: CallHandler = {
|
||||
handle: () =>
|
||||
defer(AsyncResource.bind(() => nextFn(i + 1))).pipe(mergeAll()),
|
||||
handle: () => fromPromise(nextFn(i + 1)).pipe(mergeAll()),
|
||||
};
|
||||
return interceptors[i].intercept(context, handler);
|
||||
};
|
||||
|
||||
@@ -58,9 +58,8 @@ export class MiddlewareBuilder implements MiddlewareConsumer {
|
||||
public exclude(
|
||||
...routes: Array<string | RouteInfo>
|
||||
): MiddlewareConfigProxy {
|
||||
this.excludedRoutes = [
|
||||
...this.excludedRoutes,
|
||||
...this.getRoutesFlatList(routes).reduce((excludedRoutes, route) => {
|
||||
this.excludedRoutes = this.getRoutesFlatList(routes).reduce(
|
||||
(excludedRoutes, route) => {
|
||||
for (const routePath of this.routeInfoPathExtractor.extractPathFrom(
|
||||
route,
|
||||
)) {
|
||||
@@ -71,8 +70,9 @@ export class MiddlewareBuilder implements MiddlewareConsumer {
|
||||
}
|
||||
|
||||
return excludedRoutes;
|
||||
}, [] as RouteInfo[]),
|
||||
];
|
||||
},
|
||||
[] as RouteInfo[],
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -190,14 +190,12 @@ export class MiddlewareModule<
|
||||
for (const metatype of middlewareCollection) {
|
||||
const collection = middlewareContainer.getMiddlewareCollection(moduleKey);
|
||||
const instanceWrapper = collection.get(metatype);
|
||||
|
||||
if (isUndefined(instanceWrapper)) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
if (instanceWrapper.isTransient) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.graphInspector.insertClassNode(
|
||||
moduleRef,
|
||||
instanceWrapper,
|
||||
@@ -329,11 +327,7 @@ export class MiddlewareModule<
|
||||
}
|
||||
return next();
|
||||
};
|
||||
const pathsToApplyMiddleware = [];
|
||||
paths.some(path => path.match(/^\/?$/))
|
||||
? pathsToApplyMiddleware.push('/')
|
||||
: pathsToApplyMiddleware.push(...paths);
|
||||
pathsToApplyMiddleware.forEach(path => router(path, middlewareFunction));
|
||||
paths.forEach(path => router(path, middlewareFunction));
|
||||
}
|
||||
|
||||
private getContextId(request: unknown, isTreeDurable: boolean): ContextId {
|
||||
|
||||
@@ -35,15 +35,11 @@ export class RouteInfoPathExtractor {
|
||||
if (this.isAWildcard(path)) {
|
||||
const entries =
|
||||
versionPaths.length > 0
|
||||
? versionPaths
|
||||
.map(versionPath => [
|
||||
this.prefixPath + versionPath + '$',
|
||||
? versionPaths.map(
|
||||
versionPath =>
|
||||
this.prefixPath + versionPath + addLeadingSlash(path),
|
||||
])
|
||||
.flat()
|
||||
: this.prefixPath
|
||||
? [this.prefixPath + '$', this.prefixPath + addLeadingSlash(path)]
|
||||
: [addLeadingSlash(path)];
|
||||
)
|
||||
: [this.prefixPath + addLeadingSlash(path)];
|
||||
|
||||
return Array.isArray(this.excludedGlobalPrefixRoutes)
|
||||
? [
|
||||
|
||||
@@ -50,7 +50,7 @@ export class NestApplicationContext<
|
||||
|
||||
private shouldFlushLogsOnOverride = false;
|
||||
private readonly activeShutdownSignals = new Array<string>();
|
||||
private readonly moduleCompiler = new ModuleCompiler();
|
||||
private readonly moduleCompiler: ModuleCompiler;
|
||||
private shutdownCleanupRef?: (...args: unknown[]) => unknown;
|
||||
private _instanceLinksHost: InstanceLinksHost;
|
||||
private _moduleRefsForHooksByDistance?: Array<Module>;
|
||||
@@ -70,6 +70,7 @@ export class NestApplicationContext<
|
||||
) {
|
||||
super();
|
||||
this.injector = new Injector();
|
||||
this.moduleCompiler = container.getModuleCompiler();
|
||||
|
||||
if (this.appOptions.preview) {
|
||||
this.printInPreviewModeWarning();
|
||||
@@ -95,7 +96,13 @@ export class NestApplicationContext<
|
||||
const moduleTokenFactory = this.container.getModuleTokenFactory();
|
||||
const { type, dynamicMetadata } =
|
||||
this.moduleCompiler.extractMetadata(moduleType);
|
||||
const token = moduleTokenFactory.create(type, dynamicMetadata);
|
||||
const token = dynamicMetadata
|
||||
? moduleTokenFactory.createForDynamic(
|
||||
type,
|
||||
dynamicMetadata,
|
||||
moduleType as DynamicModule,
|
||||
)
|
||||
: moduleTokenFactory.createForStatic(type, moduleType as Type);
|
||||
|
||||
const selectedModule = modulesContainer.get(token);
|
||||
if (!selectedModule) {
|
||||
|
||||
@@ -269,13 +269,9 @@ export class NestFactoryStatic {
|
||||
|
||||
return (...args: unknown[]) => {
|
||||
let result: unknown;
|
||||
ExceptionsZone.run(
|
||||
() => {
|
||||
result = receiver[prop](...args);
|
||||
},
|
||||
teardown,
|
||||
this.autoFlushLogs,
|
||||
);
|
||||
ExceptionsZone.run(() => {
|
||||
result = receiver[prop](...args);
|
||||
}, teardown);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "10.4.7",
|
||||
"version": "10.3.3",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
@@ -31,12 +31,12 @@
|
||||
"@nuxtjs/opencollective": "0.3.2",
|
||||
"fast-safe-stringify": "2.1.1",
|
||||
"iterare": "1.2.1",
|
||||
"path-to-regexp": "3.3.0",
|
||||
"tslib": "2.7.0",
|
||||
"path-to-regexp": "3.2.0",
|
||||
"tslib": "2.6.2",
|
||||
"uid": "2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/common": "10.4.7"
|
||||
"@nestjs/common": "10.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0",
|
||||
|
||||
@@ -20,8 +20,6 @@ export class RouteParamsFactory implements IRouteParamsFactory {
|
||||
return res as any;
|
||||
case RouteParamtypes.BODY:
|
||||
return data && req.body ? req.body[data] : req.body;
|
||||
case RouteParamtypes.RAW_BODY:
|
||||
return req.rawBody;
|
||||
case RouteParamtypes.PARAM:
|
||||
return data ? req.params[data] : req.params;
|
||||
case RouteParamtypes.HOST:
|
||||
|
||||
@@ -345,7 +345,6 @@ export class RouterExecutionContext {
|
||||
public isPipeable(type: number | string): boolean {
|
||||
return (
|
||||
type === RouteParamtypes.BODY ||
|
||||
type === RouteParamtypes.RAW_BODY ||
|
||||
type === RouteParamtypes.QUERY ||
|
||||
type === RouteParamtypes.PARAM ||
|
||||
type === RouteParamtypes.FILE ||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { isObject } from '@nestjs/common/utils/shared.utils';
|
||||
import { IncomingMessage } from 'http';
|
||||
import { EMPTY, lastValueFrom, Observable, isObservable } from 'rxjs';
|
||||
import { catchError, concatMap, map } from 'rxjs/operators';
|
||||
import { catchError, debounce, map } from 'rxjs/operators';
|
||||
import {
|
||||
AdditionalHeaders,
|
||||
WritableHeaderStream,
|
||||
@@ -128,7 +128,7 @@ export class RouterResponseController {
|
||||
|
||||
return { data: message as object | string };
|
||||
}),
|
||||
concatMap(
|
||||
debounce(
|
||||
message =>
|
||||
new Promise<void>(resolve =>
|
||||
stream.writeMessage(message, () => resolve()),
|
||||
@@ -153,9 +153,6 @@ export class RouterResponseController {
|
||||
|
||||
request.on('close', () => {
|
||||
subscription.unsubscribe();
|
||||
if (!stream.writableEnded) {
|
||||
stream.end();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ export class SseStream extends Transform {
|
||||
message.id = this.lastEventId.toString();
|
||||
}
|
||||
|
||||
if (!this.write(message, 'utf-8')) {
|
||||
if (!this.write(message, 'utf-8', cb)) {
|
||||
this.once('drain', cb);
|
||||
} else {
|
||||
process.nextTick(cb);
|
||||
|
||||
@@ -522,14 +522,10 @@ export class DependenciesScanner {
|
||||
}
|
||||
|
||||
public insertExportedProvider(
|
||||
// TODO: improve the type definition below because it doesn't reflects the real usage of this method
|
||||
exportedProvider: Type<Injectable> | ForwardReference,
|
||||
exportedProvider: Type<Injectable>,
|
||||
token: string,
|
||||
) {
|
||||
const fulfilledProvider = this.isForwardReference(exportedProvider)
|
||||
? exportedProvider.forwardRef()
|
||||
: exportedProvider;
|
||||
this.container.addExportedProvider(fulfilledProvider, token);
|
||||
this.container.addExportedProvider(exportedProvider, token);
|
||||
}
|
||||
|
||||
public insertController(controller: Type<Controller>, token: string) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { ExceptionsZone } from '../../../errors/exceptions-zone';
|
||||
|
||||
describe('ExceptionsZone', () => {
|
||||
@@ -14,7 +13,7 @@ describe('ExceptionsZone', () => {
|
||||
callback = sinon.spy();
|
||||
});
|
||||
it('should call callback', () => {
|
||||
ExceptionsZone.run(callback as any, rethrow, false);
|
||||
ExceptionsZone.run(callback as any, rethrow);
|
||||
expect(callback.called).to.be.true;
|
||||
});
|
||||
describe('when callback throws exception', () => {
|
||||
@@ -22,54 +21,26 @@ describe('ExceptionsZone', () => {
|
||||
handle: () => {},
|
||||
};
|
||||
let handleSpy: sinon.SinonSpy;
|
||||
let LoggerFlushSpy: sinon.SinonSpy;
|
||||
before(() => {
|
||||
(ExceptionsZone as any).exceptionHandler = exceptionHandler;
|
||||
handleSpy = sinon.spy(exceptionHandler, 'handle');
|
||||
LoggerFlushSpy = sinon.spy(Logger, 'flush');
|
||||
});
|
||||
after(() => {
|
||||
LoggerFlushSpy.restore();
|
||||
});
|
||||
describe('when callback throws exception and autoFlushLogs is false', () => {
|
||||
it('should call "handle" method of exceptionHandler and rethrows and not flush logs', () => {
|
||||
const throwsCallback = () => {
|
||||
throw new Error('');
|
||||
};
|
||||
expect(() =>
|
||||
ExceptionsZone.run(throwsCallback, rethrow, false),
|
||||
).to.throws();
|
||||
|
||||
expect(handleSpy.called).to.be.true;
|
||||
|
||||
expect(LoggerFlushSpy.called).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('when callback throws exception and autoFlushLogs is true', () => {
|
||||
it('should call "handle" method of exceptionHandler and rethrows and flush logs', () => {
|
||||
const throwsCallback = () => {
|
||||
throw new Error('');
|
||||
};
|
||||
expect(() =>
|
||||
ExceptionsZone.run(throwsCallback, rethrow, true),
|
||||
).to.throws();
|
||||
|
||||
expect(handleSpy.called).to.be.true;
|
||||
|
||||
expect(LoggerFlushSpy.called).to.be.true;
|
||||
});
|
||||
it('should call "handle" method of exceptionHandler and rethrows', () => {
|
||||
const throwsCallback = () => {
|
||||
throw new Error('');
|
||||
};
|
||||
expect(() => ExceptionsZone.run(throwsCallback, rethrow)).to.throws();
|
||||
expect(handleSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('asyncRun', () => {
|
||||
let callback: sinon.SinonSpy;
|
||||
beforeEach(() => {
|
||||
callback = sinon.spy();
|
||||
});
|
||||
it('should call callback', async () => {
|
||||
await ExceptionsZone.asyncRun(callback as any, rethrow, false);
|
||||
await ExceptionsZone.asyncRun(callback as any, rethrow);
|
||||
expect(callback.called).to.be.true;
|
||||
});
|
||||
describe('when callback throws exception', () => {
|
||||
@@ -77,40 +48,16 @@ describe('ExceptionsZone', () => {
|
||||
handle: () => {},
|
||||
};
|
||||
let handleSpy: sinon.SinonSpy;
|
||||
let LoggerFlushSpy: sinon.SinonSpy;
|
||||
before(() => {
|
||||
(ExceptionsZone as any).exceptionHandler = exceptionHandler;
|
||||
handleSpy = sinon.spy(exceptionHandler, 'handle');
|
||||
LoggerFlushSpy = sinon.spy(Logger, 'flush');
|
||||
});
|
||||
after(() => {
|
||||
LoggerFlushSpy.restore();
|
||||
});
|
||||
describe('when callback throws exception and autoFlushLogs is false', () => {
|
||||
it('should call "handle" method of exceptionHandler and rethrows error and not flush logs', async () => {
|
||||
const throwsCallback = () => {
|
||||
throw new Error('');
|
||||
};
|
||||
expect(ExceptionsZone.asyncRun(throwsCallback, rethrow, false)).to
|
||||
.eventually.be.rejected;
|
||||
|
||||
expect(handleSpy.called).to.be.true;
|
||||
|
||||
expect(LoggerFlushSpy.called).to.be.false;
|
||||
});
|
||||
});
|
||||
describe('when callback throws exception and autoFlushLogs is true', () => {
|
||||
it('should call "handle" method of exceptionHandler and rethrows error and flush logs', async () => {
|
||||
const throwsCallback = () => {
|
||||
throw new Error('');
|
||||
};
|
||||
expect(ExceptionsZone.asyncRun(throwsCallback, rethrow, true)).to
|
||||
.eventually.be.rejected;
|
||||
|
||||
expect(handleSpy.called).to.be.true;
|
||||
|
||||
expect(LoggerFlushSpy.called).to.be.true;
|
||||
});
|
||||
it('should call "handle" method of exceptionHandler and rethrows error', async () => {
|
||||
const throwsCallback = () => {
|
||||
throw new Error('');
|
||||
};
|
||||
expect(ExceptionsZone.asyncRun(throwsCallback, rethrow)).to.eventually
|
||||
.be.rejected;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
import { expect } from 'chai';
|
||||
import { ModuleCompiler } from '../../injector/compiler';
|
||||
import { ByReferenceModuleOpaqueKeyFactory } from '../../injector/opaque-key-factory/by-reference-module-opaque-key-factory';
|
||||
|
||||
describe('ModuleCompiler', () => {
|
||||
let compiler: ModuleCompiler;
|
||||
beforeEach(() => {
|
||||
compiler = new ModuleCompiler();
|
||||
compiler = new ModuleCompiler(new ByReferenceModuleOpaqueKeyFactory());
|
||||
});
|
||||
|
||||
describe('extractMetadata', () => {
|
||||
describe('when module is a dynamic module', () => {
|
||||
it('should return object with "type" and "dynamicMetadata" property', async () => {
|
||||
it('should return object with "type" and "dynamicMetadata" property', () => {
|
||||
const obj = { module: 'test', providers: [] };
|
||||
const { module, ...dynamicMetadata } = obj;
|
||||
expect(await compiler.extractMetadata(obj as any)).to.be.deep.equal({
|
||||
expect(compiler.extractMetadata(obj as any)).to.be.deep.equal({
|
||||
type: module,
|
||||
dynamicMetadata,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('when module is a not dynamic module', () => {
|
||||
it('should return object with "type" property', async () => {
|
||||
it('should return object with "type" property', () => {
|
||||
const type = 'test';
|
||||
expect(await compiler.extractMetadata(type as any)).to.be.deep.equal({
|
||||
expect(compiler.extractMetadata(type as any)).to.be.deep.equal({
|
||||
type,
|
||||
dynamicMetadata: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('Injector', () => {
|
||||
|
||||
constructor(
|
||||
public one: DependencyOne,
|
||||
@Inject() public two: DependencyTwo,
|
||||
public two: DependencyTwo,
|
||||
) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { ByReferenceModuleOpaqueKeyFactory } from '../../../injector/opaque-key-factory/by-reference-module-opaque-key-factory';
|
||||
|
||||
describe('ByReferenceModuleOpaqueKeyFactory', () => {
|
||||
const moduleId = 'constId';
|
||||
let factory: ByReferenceModuleOpaqueKeyFactory;
|
||||
|
||||
describe('when generating algorithm is random', () => {
|
||||
beforeEach(() => {
|
||||
factory = new ByReferenceModuleOpaqueKeyFactory();
|
||||
sinon.stub(factory as any, 'generateRandomString').returns(moduleId);
|
||||
});
|
||||
|
||||
describe('createForStatic', () => {
|
||||
class Module {}
|
||||
|
||||
it('should return expected token', () => {
|
||||
const type = Module;
|
||||
const token1 = factory.createForStatic(type);
|
||||
const token2 = factory.createForStatic(type);
|
||||
expect(token1).to.be.deep.eq(token2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createForDynamic', () => {
|
||||
class Module {}
|
||||
|
||||
it('should include dynamic metadata', () => {
|
||||
const dynamicModule = {
|
||||
module: Module,
|
||||
providers: [
|
||||
{
|
||||
provide: 'test',
|
||||
useValue: 'test',
|
||||
},
|
||||
],
|
||||
};
|
||||
const token1 = factory.createForDynamic(
|
||||
dynamicModule.module,
|
||||
{
|
||||
providers: dynamicModule.providers,
|
||||
},
|
||||
dynamicModule,
|
||||
);
|
||||
const token2 = factory.createForDynamic(
|
||||
dynamicModule.module,
|
||||
{
|
||||
providers: dynamicModule.providers,
|
||||
},
|
||||
dynamicModule,
|
||||
);
|
||||
|
||||
expect(token1).to.be.deep.eq(token2);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('when generating algorithm is shallow', () => {
|
||||
beforeEach(() => {
|
||||
factory = new ByReferenceModuleOpaqueKeyFactory({
|
||||
keyGenerationStrategy: 'shallow',
|
||||
});
|
||||
sinon.stub(factory as any, 'generateRandomString').returns(moduleId);
|
||||
});
|
||||
|
||||
describe('createForStatic', () => {
|
||||
class Module {}
|
||||
|
||||
it('should return expected token', () => {
|
||||
const type = Module;
|
||||
const token1 = factory.createForStatic(type);
|
||||
const token2 = factory.createForStatic(type);
|
||||
|
||||
expect(token1).to.be.deep.eq(token2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createForDynamic', () => {
|
||||
class Module {}
|
||||
|
||||
it('should include dynamic metadata', () => {
|
||||
const dynamicModule = {
|
||||
module: Module,
|
||||
providers: [
|
||||
{
|
||||
provide: 'test',
|
||||
useValue: 'test',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const token1 = factory.createForDynamic(
|
||||
dynamicModule.module,
|
||||
{
|
||||
providers: dynamicModule.providers,
|
||||
},
|
||||
dynamicModule,
|
||||
);
|
||||
const token2 = factory.createForDynamic(
|
||||
dynamicModule.module,
|
||||
{
|
||||
providers: dynamicModule.providers,
|
||||
},
|
||||
dynamicModule,
|
||||
);
|
||||
|
||||
expect(token1).to.be.deep.eq(token2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,41 +1,48 @@
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { ModuleTokenFactory } from '../../injector/module-token-factory';
|
||||
import { DeepHashedModuleOpaqueKeyFactory } from '../../../injector/opaque-key-factory/deep-hashed-module-opaque-key-factory';
|
||||
|
||||
describe('ModuleTokenFactory', () => {
|
||||
describe('DeepHashedModuleOpaqueKeyFactory', () => {
|
||||
const moduleId = 'constId';
|
||||
let factory: ModuleTokenFactory;
|
||||
let factory: DeepHashedModuleOpaqueKeyFactory;
|
||||
|
||||
beforeEach(() => {
|
||||
factory = new ModuleTokenFactory();
|
||||
factory = new DeepHashedModuleOpaqueKeyFactory();
|
||||
sinon.stub(factory, 'getModuleId').returns(moduleId);
|
||||
});
|
||||
describe('create', () => {
|
||||
describe('createForStatic', () => {
|
||||
class Module {}
|
||||
|
||||
it('should return expected token', () => {
|
||||
const type = Module;
|
||||
const token1 = factory.create(type, undefined);
|
||||
const token2 = factory.create(type, undefined);
|
||||
const token1 = factory.createForStatic(type);
|
||||
const token2 = factory.createForStatic(type);
|
||||
expect(token1).to.be.deep.eq(token2);
|
||||
});
|
||||
});
|
||||
describe('createForDynamic', () => {
|
||||
class Module {}
|
||||
|
||||
it('should include dynamic metadata', () => {
|
||||
const type = Module;
|
||||
const token1 = factory.create(type, {
|
||||
const token1 = factory.createForDynamic(type, {
|
||||
providers: [{}],
|
||||
} as any);
|
||||
const token2 = factory.create(type, {
|
||||
const token2 = factory.createForDynamic(type, {
|
||||
providers: [{}],
|
||||
} as any);
|
||||
|
||||
expect(token1).to.be.deep.eq(token2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getModuleName', () => {
|
||||
it('should map module metatype to name', () => {
|
||||
const metatype = () => {};
|
||||
expect(factory.getModuleName(metatype as any)).to.be.eql(metatype.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStringifiedOpaqueToken', () => {
|
||||
describe('when metadata exists', () => {
|
||||
it('should return hash', () => {
|
||||
@@ -80,6 +87,7 @@ describe('ModuleTokenFactory', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when metadata does not exist', () => {
|
||||
it('should return empty string', () => {
|
||||
expect(factory.getStringifiedOpaqueToken(undefined)).to.be.eql('');
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
|
||||
import { AsyncLocalStorage } from 'async_hooks';
|
||||
import { expect } from 'chai';
|
||||
import { Observable, defer, lastValueFrom, merge, of, retry } from 'rxjs';
|
||||
import { Observable, lastValueFrom, of, retry } from 'rxjs';
|
||||
import * as sinon from 'sinon';
|
||||
import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer';
|
||||
|
||||
@@ -86,7 +86,7 @@ describe('InterceptorsConsumer', () => {
|
||||
});
|
||||
|
||||
describe('when AsyncLocalStorage is used', () => {
|
||||
it('should allow an interceptor to set values in AsyncLocalStorage that are accessible from the controller', async () => {
|
||||
it('should allow an interceptor to set values in AsyncLocalStorage that are accesible from the controller', async () => {
|
||||
const storage = new AsyncLocalStorage<Record<string, any>>();
|
||||
class StorageInterceptor implements NestInterceptor {
|
||||
intercept(
|
||||
@@ -179,51 +179,6 @@ describe('InterceptorsConsumer', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('deferred promise conversion', () => {
|
||||
it('should convert promise to observable deferred', async () => {
|
||||
class TestError extends Error {}
|
||||
const testInterceptors = [
|
||||
{
|
||||
intercept: sinon.stub().callsFake(async (ctx, handler) => {
|
||||
return merge(
|
||||
handler.handle(),
|
||||
defer(() => {
|
||||
throw new TestError();
|
||||
}),
|
||||
);
|
||||
}),
|
||||
},
|
||||
{
|
||||
intercept: sinon
|
||||
.stub()
|
||||
.callsFake(async (ctx, handler) => handler.handle()),
|
||||
},
|
||||
{
|
||||
intercept: sinon
|
||||
.stub()
|
||||
.callsFake(async (ctx, handler) => handler.handle()),
|
||||
},
|
||||
,
|
||||
];
|
||||
|
||||
const observable = await consumer.intercept(
|
||||
testInterceptors,
|
||||
null,
|
||||
{ constructor: null },
|
||||
null,
|
||||
async () => 1,
|
||||
);
|
||||
|
||||
try {
|
||||
await transformToResult(observable);
|
||||
} catch (error) {
|
||||
if (!(error instanceof TestError)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
expect(testInterceptors[2].intercept.called).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function transformToResult(resultOrDeferred: any) {
|
||||
|
||||
@@ -193,7 +193,7 @@ describe('MiddlewareBuilder', () => {
|
||||
expect(proxy.getExcludedRoutes()).to.be.eql([
|
||||
{
|
||||
path,
|
||||
method: -1,
|
||||
method: -1 as any,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('RouteInfoPathExtractor', () => {
|
||||
method: RequestMethod.ALL,
|
||||
version: '1',
|
||||
}),
|
||||
).to.eql(['/v1$', '/v1/*']);
|
||||
).to.eql(['/v1/*']);
|
||||
});
|
||||
|
||||
it(`should return correct paths when set global prefix`, () => {
|
||||
@@ -42,7 +42,7 @@ describe('RouteInfoPathExtractor', () => {
|
||||
path: '*',
|
||||
method: RequestMethod.ALL,
|
||||
}),
|
||||
).to.eql(['/api$', '/api/*']);
|
||||
).to.eql(['/api/*']);
|
||||
|
||||
expect(
|
||||
routeInfoPathExtractor.extractPathsFrom({
|
||||
@@ -50,7 +50,7 @@ describe('RouteInfoPathExtractor', () => {
|
||||
method: RequestMethod.ALL,
|
||||
version: '1',
|
||||
}),
|
||||
).to.eql(['/api/v1$', '/api/v1/*']);
|
||||
).to.eql(['/api/v1/*']);
|
||||
});
|
||||
|
||||
it(`should return correct paths when set global prefix and global prefix options`, () => {
|
||||
@@ -66,7 +66,7 @@ describe('RouteInfoPathExtractor', () => {
|
||||
path: '*',
|
||||
method: RequestMethod.ALL,
|
||||
}),
|
||||
).to.eql(['/api$', '/api/*', '/foo']);
|
||||
).to.eql(['/api/*', '/foo']);
|
||||
|
||||
expect(
|
||||
routeInfoPathExtractor.extractPathsFrom({
|
||||
@@ -74,7 +74,7 @@ describe('RouteInfoPathExtractor', () => {
|
||||
method: RequestMethod.ALL,
|
||||
version: '1',
|
||||
}),
|
||||
).to.eql(['/api/v1$', '/api/v1/*', '/v1/foo']);
|
||||
).to.eql(['/api/v1/*', '/v1/foo']);
|
||||
|
||||
expect(
|
||||
routeInfoPathExtractor.extractPathsFrom({
|
||||
|
||||
@@ -13,7 +13,6 @@ describe('RouteParamsFactory', () => {
|
||||
const req = {
|
||||
ip: 'ip',
|
||||
session: null,
|
||||
rawBody: Buffer.from('{"foo":"bar"}'),
|
||||
body: {
|
||||
foo: 'bar',
|
||||
},
|
||||
@@ -68,16 +67,6 @@ describe('RouteParamsFactory', () => {
|
||||
).to.be.eql(req.body);
|
||||
});
|
||||
});
|
||||
describe(`RouteParamtypes.RAW_BODY`, () => {
|
||||
it('should return rawBody buffer', () => {
|
||||
expect(
|
||||
(factory as any).exchangeKeyForValue(
|
||||
RouteParamtypes.RAW_BODY,
|
||||
...args,
|
||||
),
|
||||
).to.be.eql(req.rawBody);
|
||||
});
|
||||
});
|
||||
describe(`RouteParamtypes.HEADERS`, () => {
|
||||
it('should return headers object', () => {
|
||||
expect(
|
||||
|
||||
@@ -210,7 +210,7 @@ describe('RouterExecutionContext', () => {
|
||||
beforeEach(() => {
|
||||
consumerApplySpy = sinon.spy(consumer, 'apply');
|
||||
});
|
||||
describe('when paramtype is query, body, rawBody or param', () => {
|
||||
describe('when paramtype is query, body or param', () => {
|
||||
it('should call "consumer.apply" with expected arguments', () => {
|
||||
contextCreator.getParamValue(
|
||||
value,
|
||||
@@ -238,19 +238,6 @@ describe('RouterExecutionContext', () => {
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
contextCreator.getParamValue(
|
||||
value,
|
||||
{ metatype, type: RouteParamtypes.RAW_BODY, data: null },
|
||||
transforms,
|
||||
);
|
||||
expect(
|
||||
consumerApplySpy.calledWith(
|
||||
value,
|
||||
{ metatype, type: RouteParamtypes.RAW_BODY, data: null },
|
||||
transforms,
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
contextCreator.getParamValue(
|
||||
value,
|
||||
{ metatype, type: RouteParamtypes.PARAM, data: null },
|
||||
@@ -274,7 +261,6 @@ describe('RouterExecutionContext', () => {
|
||||
});
|
||||
it('otherwise', () => {
|
||||
expect(contextCreator.isPipeable(RouteParamtypes.BODY)).to.be.true;
|
||||
expect(contextCreator.isPipeable(RouteParamtypes.RAW_BODY)).to.be.true;
|
||||
expect(contextCreator.isPipeable(RouteParamtypes.QUERY)).to.be.true;
|
||||
expect(contextCreator.isPipeable(RouteParamtypes.PARAM)).to.be.true;
|
||||
expect(contextCreator.isPipeable(RouteParamtypes.FILE)).to.be.true;
|
||||
|
||||
@@ -7,7 +7,6 @@ import { PassThrough, Writable } from 'stream';
|
||||
import { HttpStatus, RequestMethod } from '../../../common';
|
||||
import { RouterResponseController } from '../../router/router-response-controller';
|
||||
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
|
||||
import { SseStream } from '../../router/sse-stream';
|
||||
|
||||
describe('RouterResponseController', () => {
|
||||
let adapter: NoopHttpAdapter;
|
||||
@@ -375,71 +374,6 @@ data: test
|
||||
done();
|
||||
});
|
||||
|
||||
describe('when writing data too densely', () => {
|
||||
const DEFAULT_MAX_LISTENERS = SseStream.defaultMaxListeners;
|
||||
const MAX_LISTENERS = 1;
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
beforeEach(() => {
|
||||
// Can't access to the internal sseStream,
|
||||
// as a workaround, set `defaultMaxListeners` of `SseStream` and reset the max listeners of `process`
|
||||
const PROCESS_MAX_LISTENERS = process.getMaxListeners();
|
||||
SseStream.defaultMaxListeners = MAX_LISTENERS;
|
||||
process.setMaxListeners(PROCESS_MAX_LISTENERS);
|
||||
|
||||
const sseStream = sinon.createStubInstance(SseStream);
|
||||
const originalWrite = SseStream.prototype.write;
|
||||
// Make `.write()` always return false, so as to listen `drain` event
|
||||
sseStream.write.callsFake(function (...args: any[]) {
|
||||
originalWrite.apply(this, args);
|
||||
return false;
|
||||
});
|
||||
sandbox.replace(SseStream.prototype, 'write', sseStream.write);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
SseStream.defaultMaxListeners = DEFAULT_MAX_LISTENERS;
|
||||
});
|
||||
|
||||
it('should not cause memory leak', async () => {
|
||||
let maxDrainListenersExceededWarning = null;
|
||||
process.on('warning', (warning: any) => {
|
||||
if (
|
||||
warning.name === 'MaxListenersExceededWarning' &&
|
||||
warning.emitter instanceof SseStream &&
|
||||
warning.type === 'drain' &&
|
||||
warning.count === MAX_LISTENERS + 1
|
||||
) {
|
||||
maxDrainListenersExceededWarning = warning;
|
||||
}
|
||||
});
|
||||
|
||||
const result = new Subject();
|
||||
|
||||
const response = new Writable();
|
||||
response._write = () => {};
|
||||
|
||||
const request = new Writable();
|
||||
request._write = () => {};
|
||||
|
||||
routerResponseController.sse(
|
||||
result,
|
||||
response as unknown as ServerResponse,
|
||||
request as unknown as IncomingMessage,
|
||||
);
|
||||
|
||||
// Send multiple messages simultaneously
|
||||
Array.from({ length: MAX_LISTENERS + 1 }).forEach((_, i) =>
|
||||
result.next(String(i)),
|
||||
);
|
||||
|
||||
await new Promise(resolve => process.nextTick(resolve));
|
||||
|
||||
expect(maxDrainListenersExceededWarning).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is an error', () => {
|
||||
it('should close the request', done => {
|
||||
const result = new Subject();
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
|
||||
Nest is a framework for building efficient, scalable <a href="https://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="https://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also provides compatibility with a wide range of other libraries, like <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad of third-party plugins which are available.</p>
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
|
||||
## Philosophy
|
||||
|
||||
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a>, and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a>, which improve developer productivity and enable the construction of fast, testable, and extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers, and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
||||
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, and loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
||||
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a> and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a> which improve developer productivity and enable the construction of fast, testable, extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
||||
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
||||
|
||||
## Getting started
|
||||
|
||||
@@ -57,84 +57,71 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
#### Principal Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://microsoft.com/" target="_blank"><img src="https://nestjs.com/img/logos/microsoft-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://mojam.co" target="_blank"><img src="https://nestjs.com/img/logos/mojam-logo.png" width="80" valign="middle" /></a></td>
|
||||
<td><a href="https://marblism.com?utm_source=nest" target="_blank"><img src="https://nestjs.com/img/logos/marblism-logo.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="170" valign="middle" /></a></td>
|
||||
<td><a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/logos/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/logos/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/logos/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/logos/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/logos/jetbrains-logo.svg" width="90" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/logos/snyk-logo-black.png" width="185" valign="middle" /></a></td>
|
||||
<td><a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/logos/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/logos/ridi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/logos/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td><a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/logos/skunk-logo.png" height="60" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></tr><tr>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="90" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td><td>
|
||||
<a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/ridi-logo.svg" width="105" valign="middle" /></a></td><td>
|
||||
<a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
</tr><tr><td>
|
||||
<a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/skunk-logo.png" height="60" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.mercedes-benz.com/" target="_blank"><img src="https://nestjs.com/img/logos/mercedes-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td><a href="https://www.dinii.jp/" target="_blank"><img src="https://nestjs.com/img/logos/dinii-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://bloodycase.com/?promocode=NEST" target="_blank"><img src="https://nestjs.com/img/logos/bloodycase-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://handsontable.com/docs/react-data-grid/?utm_source=NestJS_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024" target="_blank"><img src="https://nestjs.com/img/logos/handsontable-logo.svg" width="150" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.itflashcards.com/" target="_blank"><img src="https://nestjs.com/img/logos/it_flashcards-logo.png" width="170" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://arcjet.com/?ref=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/arcjet-logo.svg" width="170" valign="middle" /></a></td>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td>
|
||||
<td><a href="https://twistag.com/" target="_blank"><img src="https://nestjs.com/img/twistag-logo.png" width="120" valign="middle" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/logos/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/logos/novologic.png" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/logos/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/logos/triplebyte.png" width="107" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/logos/nearpod-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/logos/genuinebee.svg" width="97" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/logos/vpn-review-logo.png" width="85" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/logos/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/logos/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/logos/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/logos/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/logos/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/logos/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/logos/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/logos/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/logos/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/logos/julienferand-logo.jpeg" width="55" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/logos/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/logos/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.route4me.com/" target="_blank"><img src="https://nestjs.com/img/logos/route4me-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.slotsup.com/" target="_blank"><img src="https://nestjs.com/img/logos/slotsup-logo.png" width="60" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<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://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" width="95" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" width="107" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/julienferand-logo.jpeg" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ let natsPackage = {} as any;
|
||||
export class ClientNats extends ClientProxy {
|
||||
protected readonly logger = new Logger(ClientNats.name);
|
||||
protected natsClient: Client;
|
||||
protected clientConnectionPromise: Promise<Client>;
|
||||
|
||||
constructor(protected readonly options: NatsOptions['options']) {
|
||||
super();
|
||||
@@ -31,15 +30,13 @@ export class ClientNats extends ClientProxy {
|
||||
public async close() {
|
||||
await this.natsClient?.close();
|
||||
this.natsClient = null;
|
||||
this.clientConnectionPromise = null;
|
||||
}
|
||||
|
||||
public async connect(): Promise<any> {
|
||||
if (this.clientConnectionPromise) {
|
||||
return this.clientConnectionPromise;
|
||||
if (this.natsClient) {
|
||||
return this.natsClient;
|
||||
}
|
||||
this.clientConnectionPromise = this.createClient();
|
||||
this.natsClient = await this.clientConnectionPromise;
|
||||
this.natsClient = await this.createClient();
|
||||
this.handleStatusUpdates(this.natsClient);
|
||||
return this.natsClient;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export class ClientRMQ extends ClientProxy {
|
||||
public createClient(): AmqpConnectionManager {
|
||||
const socketOptions = this.getOptionsProp(this.options, 'socketOptions');
|
||||
return rmqPackage.connect(this.urls, {
|
||||
connectionOptions: socketOptions?.connectionOptions,
|
||||
connectionOptions: socketOptions,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class BaseRpcContext<T = unknown[]> {
|
||||
constructor(protected readonly args: T) {}
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ type KafkaContextArgs = [
|
||||
producer: Producer,
|
||||
];
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class KafkaContext extends BaseRpcContext<KafkaContextArgs> {
|
||||
constructor(args: KafkaContextArgs) {
|
||||
super(args);
|
||||
|
||||
@@ -2,9 +2,6 @@ import { BaseRpcContext } from './base-rpc.context';
|
||||
|
||||
type MqttContextArgs = [string, Record<string, any>];
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class MqttContext extends BaseRpcContext<MqttContextArgs> {
|
||||
constructor(args: MqttContextArgs) {
|
||||
super(args);
|
||||
|
||||
@@ -2,9 +2,6 @@ import { BaseRpcContext } from './base-rpc.context';
|
||||
|
||||
type NatsContextArgs = [string, any];
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class NatsContext extends BaseRpcContext<NatsContextArgs> {
|
||||
constructor(args: NatsContextArgs) {
|
||||
super(args);
|
||||
|
||||
@@ -2,9 +2,6 @@ import { BaseRpcContext } from './base-rpc.context';
|
||||
|
||||
type RedisContextArgs = [string];
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class RedisContext extends BaseRpcContext<RedisContextArgs> {
|
||||
constructor(args: RedisContextArgs) {
|
||||
super(args);
|
||||
|
||||
@@ -2,9 +2,6 @@ import { BaseRpcContext } from './base-rpc.context';
|
||||
|
||||
type RmqContextArgs = [Record<string, any>, any, string];
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class RmqContext extends BaseRpcContext<RmqContextArgs> {
|
||||
constructor(args: RmqContextArgs) {
|
||||
super(args);
|
||||
|
||||
@@ -3,9 +3,6 @@ import { BaseRpcContext } from './base-rpc.context';
|
||||
|
||||
type TcpContextArgs = [TcpSocket, string];
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class TcpContext extends BaseRpcContext<TcpContextArgs> {
|
||||
constructor(args: TcpContextArgs) {
|
||||
super(args);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum';
|
||||
|
||||
export enum RpcParamtype {
|
||||
PAYLOAD = RouteParamtypes.BODY,
|
||||
CONTEXT = RouteParamtypes.HEADERS,
|
||||
GRPC_CALL = RouteParamtypes.FILES,
|
||||
PAYLOAD = 3,
|
||||
CONTEXT = 6,
|
||||
GRPC_CALL = 9,
|
||||
}
|
||||
|
||||
@@ -66,7 +66,6 @@ export interface GrpcOptions {
|
||||
protoLoader?: string;
|
||||
packageDefinition?: any;
|
||||
gracefulShutdown?: boolean;
|
||||
onLoadPackageDefinition?: (pkg: any, server: any) => void;
|
||||
loader?: {
|
||||
keepCase?: boolean;
|
||||
alternateCommentMode?: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/microservices",
|
||||
"version": "10.4.7",
|
||||
"version": "10.3.3",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@microservices)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
@@ -19,11 +19,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"iterare": "1.2.1",
|
||||
"tslib": "2.7.0"
|
||||
"tslib": "2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/common": "10.4.7",
|
||||
"@nestjs/core": "10.4.7"
|
||||
"@nestjs/common": "10.3.3",
|
||||
"@nestjs/core": "10.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@grpc/grpc-js": "*",
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export interface MqttRecordOptions {
|
||||
/**
|
||||
* The QoS
|
||||
@@ -29,9 +26,6 @@ export interface MqttRecordOptions {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class MqttRecord<TData = any> {
|
||||
constructor(
|
||||
public readonly data: TData,
|
||||
@@ -39,9 +33,6 @@ export class MqttRecord<TData = any> {
|
||||
) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class MqttRecordBuilder<TData> {
|
||||
private options?: MqttRecordOptions;
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class NatsRecord<TData = any, THeaders = any> {
|
||||
constructor(
|
||||
public readonly data: TData,
|
||||
@@ -8,9 +5,6 @@ export class NatsRecord<TData = any, THeaders = any> {
|
||||
) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class NatsRecordBuilder<TData> {
|
||||
private headers?: any;
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export interface RmqRecordOptions {
|
||||
expiration?: string | number;
|
||||
userId?: string;
|
||||
@@ -19,9 +16,6 @@ export interface RmqRecordOptions {
|
||||
appId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class RmqRecord<TData = any> {
|
||||
constructor(
|
||||
public readonly data: TData,
|
||||
@@ -29,9 +23,6 @@ export class RmqRecord<TData = any> {
|
||||
) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class RmqRecordBuilder<TData> {
|
||||
private options?: RmqRecordOptions;
|
||||
|
||||
|
||||
@@ -42,9 +42,6 @@ interface GrpcCall<TRequest = any, TMetadata = any> {
|
||||
emit: Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
public readonly transportId = Transport.GRPC;
|
||||
|
||||
@@ -84,7 +81,6 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
|
||||
public async start(callback?: () => void) {
|
||||
await this.bindEvents();
|
||||
this.grpcClient.start();
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -125,6 +121,7 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
const service = {};
|
||||
|
||||
for (const methodName in grpcService.prototype) {
|
||||
let pattern = '';
|
||||
let methodHandler = null;
|
||||
let streamingType = GrpcMethodStreamingType.NO_STREAMING;
|
||||
|
||||
@@ -134,32 +131,32 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
if (!isUndefined(methodReqStreaming) && methodReqStreaming) {
|
||||
// Try first pattern to be presented, RX streaming pattern would be
|
||||
// a preferable pattern to select among a few defined
|
||||
methodHandler = this.getMessageHandler(
|
||||
pattern = this.createPattern(
|
||||
name,
|
||||
methodName,
|
||||
GrpcMethodStreamingType.RX_STREAMING,
|
||||
methodFunction,
|
||||
);
|
||||
methodHandler = this.messageHandlers.get(pattern);
|
||||
streamingType = GrpcMethodStreamingType.RX_STREAMING;
|
||||
// If first pattern didn't match to any of handlers then try
|
||||
// pass-through handler to be presented
|
||||
if (!methodHandler) {
|
||||
methodHandler = this.getMessageHandler(
|
||||
pattern = this.createPattern(
|
||||
name,
|
||||
methodName,
|
||||
GrpcMethodStreamingType.PT_STREAMING,
|
||||
methodFunction,
|
||||
);
|
||||
methodHandler = this.messageHandlers.get(pattern);
|
||||
streamingType = GrpcMethodStreamingType.PT_STREAMING;
|
||||
}
|
||||
} else {
|
||||
// Select handler if any presented for No-Streaming pattern
|
||||
methodHandler = this.getMessageHandler(
|
||||
pattern = this.createPattern(
|
||||
name,
|
||||
methodName,
|
||||
GrpcMethodStreamingType.NO_STREAMING,
|
||||
methodFunction,
|
||||
);
|
||||
// Select handler if any presented for No-Streaming pattern
|
||||
methodHandler = this.messageHandlers.get(pattern);
|
||||
streamingType = GrpcMethodStreamingType.NO_STREAMING;
|
||||
}
|
||||
if (!methodHandler) {
|
||||
@@ -174,22 +171,6 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
return service;
|
||||
}
|
||||
|
||||
getMessageHandler(
|
||||
serviceName: string,
|
||||
methodName: string,
|
||||
streaming: GrpcMethodStreamingType,
|
||||
grpcMethod: { path?: string },
|
||||
) {
|
||||
let pattern = this.createPattern(serviceName, methodName, streaming);
|
||||
let methodHandler = this.messageHandlers.get(pattern);
|
||||
if (!methodHandler) {
|
||||
const packageServiceName = grpcMethod.path?.split?.('/')[1];
|
||||
pattern = this.createPattern(packageServiceName, methodName, streaming);
|
||||
methodHandler = this.messageHandlers.get(pattern);
|
||||
}
|
||||
return methodHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will create a string of a JSON serialized format
|
||||
*
|
||||
@@ -260,6 +241,7 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
return async (call: GrpcCall, callback: Function) => {
|
||||
const handler = methodHandler(call.request, call.metadata, call);
|
||||
const result$ = this.transformToObservable(await handler);
|
||||
|
||||
await this.writeObservableToGrpc(result$, call);
|
||||
};
|
||||
}
|
||||
@@ -274,13 +256,11 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
* @param call The GRPC call we want to write to.
|
||||
* @returns A promise that resolves when we're done writing to the call.
|
||||
*/
|
||||
private writeObservableToGrpc<T>(
|
||||
public writeObservableToGrpc<T>(
|
||||
source: Observable<T>,
|
||||
call: GrpcCall<T>,
|
||||
): Promise<void> {
|
||||
// this promise should **not** reject, as we're handling errors in the observable for the Call
|
||||
// the promise is only needed to signal when writing/draining has been completed
|
||||
return new Promise((resolve, _doNotUse) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const valuesWaitingToBeDrained: T[] = [];
|
||||
let shouldErrorAfterDraining = false;
|
||||
let error: any;
|
||||
@@ -293,14 +273,17 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
// If the call is cancelled, unsubscribe from the source
|
||||
const cancelHandler = () => {
|
||||
subscription.unsubscribe();
|
||||
// Calls that are cancelled by the client should be successfully resolved here
|
||||
// The call has been cancelled, so we need to either resolve
|
||||
// or reject the promise. We're resolving in this case because
|
||||
// rejection is noisy. If at any point in the future, we need to
|
||||
// know that cancellation happened, we can either reject or
|
||||
// start resolving with some sort of outcome value.
|
||||
resolve();
|
||||
};
|
||||
call.on(CANCEL_EVENT, cancelHandler);
|
||||
subscription.add(() => call.off(CANCEL_EVENT, cancelHandler));
|
||||
|
||||
// In all cases, when we finalize, end the writable stream
|
||||
// being careful that errors and writes must be emitted _before_ this call is ended
|
||||
subscription.add(() => call.end());
|
||||
|
||||
const drain = () => {
|
||||
@@ -324,7 +307,7 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
} else if (shouldErrorAfterDraining) {
|
||||
call.emit('error', error);
|
||||
subscription.unsubscribe();
|
||||
resolve();
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -349,7 +332,7 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
// reject and teardown.
|
||||
call.emit('error', err);
|
||||
subscription.unsubscribe();
|
||||
resolve();
|
||||
reject(err);
|
||||
} else {
|
||||
// We're waiting for a drain event, record the
|
||||
// error so it can be handled after everything is drained.
|
||||
@@ -515,14 +498,6 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
this.options,
|
||||
grpcProtoLoaderPackage,
|
||||
);
|
||||
|
||||
if (this.options.onLoadPackageDefinition) {
|
||||
this.options.onLoadPackageDefinition(
|
||||
packageDefinition,
|
||||
this.grpcClient,
|
||||
);
|
||||
}
|
||||
|
||||
return grpcPackage.loadPackageDefinition(packageDefinition);
|
||||
} catch (err) {
|
||||
const invalidProtoError = new InvalidProtoDefinitionException(err.path);
|
||||
@@ -567,19 +542,14 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
||||
? deepDefinition.service !== false
|
||||
: false;
|
||||
|
||||
// grpc namespace object does not have 'format' or 'service' properties defined
|
||||
const isFormatDefined =
|
||||
deepDefinition && !isUndefined(deepDefinition.format);
|
||||
|
||||
if (isServiceDefined && isServiceBoolean) {
|
||||
accumulator.push({
|
||||
name: nameExtended,
|
||||
service: deepDefinition,
|
||||
});
|
||||
} else if (isFormatDefined) {
|
||||
// Do nothing
|
||||
} else {
|
||||
// Continue recursion for namespace object until objects end or service definition found
|
||||
}
|
||||
// Continue recursion until objects end or service definition found
|
||||
else {
|
||||
this.collectDeepServices(nameExtended, deepDefinition, accumulator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,6 @@ import { Server } from './server';
|
||||
|
||||
let kafkaPackage: any = {};
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class ServerKafka extends Server implements CustomTransportStrategy {
|
||||
public readonly transportId = Transport.KAFKA;
|
||||
|
||||
|
||||
@@ -26,9 +26,6 @@ import { Server } from './server';
|
||||
|
||||
let mqttPackage: any = {};
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class ServerMqtt extends Server implements CustomTransportStrategy {
|
||||
public readonly transportId = Transport.MQTT;
|
||||
|
||||
|
||||
@@ -13,9 +13,6 @@ import { Server } from './server';
|
||||
|
||||
let natsPackage = {} as any;
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class ServerNats extends Server implements CustomTransportStrategy {
|
||||
public readonly transportId = Transport.NATS;
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ type Redis = any;
|
||||
|
||||
let redisPackage = {} as any;
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class ServerRedis extends Server implements CustomTransportStrategy {
|
||||
public readonly transportId = Transport.REDIS;
|
||||
|
||||
|
||||
@@ -36,9 +36,6 @@ let rmqPackage: any = {};
|
||||
|
||||
const INFINITE_CONNECTION_ATTEMPTS = -1;
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class ServerRMQ extends Server implements CustomTransportStrategy {
|
||||
public readonly transportId = Transport.RMQ;
|
||||
|
||||
@@ -143,7 +140,7 @@ export class ServerRMQ extends Server implements CustomTransportStrategy {
|
||||
public createClient<T = any>(): T {
|
||||
const socketOptions = this.getOptionsProp(this.options, 'socketOptions');
|
||||
return rmqPackage.connect(this.urls, {
|
||||
connectionOptions: socketOptions?.connectionOptions,
|
||||
connectionOptions: socketOptions,
|
||||
heartbeatIntervalInSeconds: socketOptions?.heartbeatIntervalInSeconds,
|
||||
reconnectTimeInSeconds: socketOptions?.reconnectTimeInSeconds,
|
||||
});
|
||||
|
||||
@@ -26,9 +26,6 @@ import {
|
||||
import { TcpOptions } from '../interfaces/microservice-configuration.interface';
|
||||
import { Server } from './server';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class ServerTCP extends Server implements CustomTransportStrategy {
|
||||
public readonly transportId = Transport.TCP;
|
||||
|
||||
|
||||
@@ -34,9 +34,6 @@ import { ConsumerSerializer } from '../interfaces/serializer.interface';
|
||||
import { IdentitySerializer } from '../serializers/identity.serializer';
|
||||
import { transformPatternToRoute } from '../utils';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export abstract class Server {
|
||||
protected readonly messageHandlers = new Map<string, MessageHandler>();
|
||||
protected readonly logger: LoggerService = new Logger(Server.name);
|
||||
|
||||
@@ -253,7 +253,6 @@ describe('ClientNats', () => {
|
||||
describe('when is not connected', () => {
|
||||
beforeEach(async () => {
|
||||
client['natsClient'] = null;
|
||||
client['clientConnectionPromise'] = null;
|
||||
await client.connect();
|
||||
});
|
||||
it('should call "handleStatusUpdatesSpy" once', async () => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user