mirror of
https://github.com/nestjs/nest.git
synced 2026-02-24 00:02:56 +00:00
Compare commits
2 Commits
fix/patter
...
chore/expr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
969a35bc51 | ||
|
|
bc1a992918 |
@@ -29,7 +29,7 @@ aliases:
|
||||
- &install-deps
|
||||
run:
|
||||
name: Install dependencies
|
||||
command: npm install --legacy-peer-deps
|
||||
command: npm ci --legacy-peer-deps
|
||||
- &build-packages
|
||||
run:
|
||||
name: Build
|
||||
@@ -165,16 +165,28 @@ jobs:
|
||||
name: Integration tests
|
||||
command: npm run test:integration
|
||||
|
||||
codechecks_benchmarks:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- run:
|
||||
name: Install native wrk
|
||||
command: .circleci/install-wrk.sh
|
||||
- run:
|
||||
name: Run codechecks with benchmarks
|
||||
command: yarn codechecks:benchmarks
|
||||
|
||||
samples:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
|
||||
- image: redis:8-alpine
|
||||
name: redis
|
||||
environment:
|
||||
DISABLE_OPENCOLLECTIVE: 'true'
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
@@ -183,7 +195,6 @@ jobs:
|
||||
name: Build all samples
|
||||
command: npm run build:samples
|
||||
|
||||
|
||||
workflows:
|
||||
build-and-test:
|
||||
jobs:
|
||||
@@ -209,4 +220,6 @@ workflows:
|
||||
- samples:
|
||||
requires:
|
||||
- build
|
||||
|
||||
- codechecks_benchmarks:
|
||||
requires:
|
||||
- build
|
||||
|
||||
13
.circleci/install-wrk.sh
Executable file
13
.circleci/install-wrk.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# based on https://medium.com/@felipedutratine/intelligent-benchmark-with-wrk-163986c1587f
|
||||
|
||||
cd /tmp/
|
||||
sudo apt-get install build-essential libssl-dev git -y
|
||||
git clone --depth=1 https://github.com/wg/wrk.git wrk
|
||||
cd wrk
|
||||
sudo make
|
||||
# move the executable to somewhere in your PATH, ex:
|
||||
sudo cp wrk /usr/local/bin
|
||||
@@ -37,8 +37,7 @@
|
||||
"socket.io",
|
||||
"ws",
|
||||
"testing",
|
||||
"websockets",
|
||||
"release"
|
||||
"websockets"
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
40
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
40
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: "\U0001F41B Bug Report"
|
||||
description: "If something isn't working as expected \U0001F914"
|
||||
labels: ["needs triage"]
|
||||
type: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -64,13 +63,46 @@ body:
|
||||
label: "Expected behavior"
|
||||
description: "A clear and concise description of what you expected to happened (or code)"
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
---
|
||||
|
||||
- type: checkboxes
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Package"
|
||||
description: |
|
||||
Which package (or packages) do you think your issue is related to?
|
||||
**Tip**: The first line of the stack trace can help you to figure out this
|
||||
|
||||
The package isn't listed below? Try to find its repository [here](https://github.com/orgs/nestjs/repositories) and open the issue there instead
|
||||
options:
|
||||
- label: "I don't know. Or some 3rd-party package"
|
||||
- label: "<code>@nestjs/common</code>"
|
||||
- label: "<code>@nestjs/core</code>"
|
||||
- label: "<code>@nestjs/microservices</code>"
|
||||
- label: "<code>@nestjs/platform-express</code>"
|
||||
- label: "<code>@nestjs/platform-fastify</code>"
|
||||
- label: "<code>@nestjs/platform-socket.io</code>"
|
||||
- label: "<code>@nestjs/platform-ws</code>"
|
||||
- label: "<code>@nestjs/testing</code>"
|
||||
- label: "<code>@nestjs/websockets</code>"
|
||||
- label: "Other (see below)"
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: "Other package"
|
||||
description: "If your issue is related to some package that is not listed above nor under @nestjs org, write its name here"
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: "NestJS version"
|
||||
description: |
|
||||
Which exact version of `@nestjs/core` package are you using?
|
||||
Which version of `@nestjs/core` are you using?
|
||||
**Tip**: Make sure that all of yours `@nestjs/*` dependencies are in sync!
|
||||
placeholder: "10.0.0"
|
||||
placeholder: "8.1.3"
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
@@ -89,7 +121,7 @@ body:
|
||||
attributes:
|
||||
label: "Node.js version"
|
||||
description: "Which version of Node.js are you using?"
|
||||
placeholder: "24.0.0"
|
||||
placeholder: "14.17.6"
|
||||
|
||||
- type: checkboxes
|
||||
validations:
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/Feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/Feature_request.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: "\U0001F680 Feature Request"
|
||||
description: "I have a suggestion \U0001F63B!"
|
||||
labels: ["type: enhancement :wolf:", "needs triage"]
|
||||
type: feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/Regression.yml
vendored
1
.github/ISSUE_TEMPLATE/Regression.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: "\U0001F4A5 Regression"
|
||||
description: "Report an unexpected while upgrading your Nest application!"
|
||||
labels: ["type: bug :sob:", "needs triage"]
|
||||
type: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
@@ -2,7 +2,6 @@ title: "perf: "
|
||||
name: "\U0001F525 Suggestion for Improving Performance"
|
||||
description: "I have a suggestion that might improve the performance of Nest \U00002728"
|
||||
labels: ["type: enhancement :wolf:", "needs triage"]
|
||||
type: task
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
|
||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
queries: +security-extended
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -58,4 +58,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
packages/*/package-lock.json
|
||||
sample/**/package-lock.json
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
@@ -11,7 +10,7 @@ node_modules/
|
||||
/.devcontainer
|
||||
/.classpath
|
||||
/.project
|
||||
/.settings
|
||||
/.settings
|
||||
*.code-workspace
|
||||
|
||||
# Vim
|
||||
@@ -34,7 +33,6 @@ yarn-error.log
|
||||
/packages/**/.npmignore
|
||||
/packages/**/LICENSE
|
||||
*.tsbuildinfo
|
||||
/.nx/workspace-data
|
||||
|
||||
# example
|
||||
/quick-start
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
packages/**/*.d.ts
|
||||
packages/**/*.js
|
||||
.nyc_output
|
||||
|
||||
/.nx/workspace-data
|
||||
@@ -25,7 +25,7 @@ Stack Overflow is a much better place to ask questions since:
|
||||
|
||||
<!-- - there are thousands of people willing to help on Stack Overflow [maybe one day] -->
|
||||
|
||||
- questions and answers stay available for public viewing so your question / answer might help someone else.
|
||||
- questions and answers stay available for public viewing so your question / answer might help someone else
|
||||
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
||||
|
||||
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2017-present Kamil Mysliwiec <https://kamilmysliwiec.com>
|
||||
Copyright (c) 2017-2024 Kamil Mysliwiec <https://kamilmysliwiec.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
21
Readme.md
21
Readme.md
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<a href="https://nestjs.com/" target="_blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
||||
<a href="https://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
@@ -8,7 +8,7 @@
|
||||
<p align="center">A progressive <a href="https://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://github.com/nestjs/nest/blob/master/LICENSE" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
@@ -49,7 +49,7 @@ Please make sure to read the [Issue Reporting Checklist](https://github.com/nest
|
||||
|
||||
## Consulting
|
||||
|
||||
With official support, you can get expert help straight from the Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
|
||||
## Support
|
||||
|
||||
@@ -62,8 +62,9 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<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://serpapi.com/" target="_blank"><img src="https://nestjs.com/img/logos/serpapi-logo.png" width="150" 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>
|
||||
|
||||
@@ -79,7 +80,10 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
</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>
|
||||
|
||||
@@ -89,12 +93,10 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<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>
|
||||
<td align="center" valign="middle"><a href="https://crawljobs.com" target="_blank"><img src="https://nestjs.com/img/logos/crawljobs-logo.svg" width="130" valign="middle" /></a></td>
|
||||
</tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://pandektes.com" target="_blank"><img src="https://nestjs.com/img/logos/pandektes-logo.png" width="65" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -118,16 +120,19 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<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>
|
||||
<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>
|
||||
</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>
|
||||
|
||||
|
||||
88
benchmarks/all_output.txt
Normal file
88
benchmarks/all_output.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
-----------------------
|
||||
express
|
||||
-----------------------
|
||||
Running 10s test @ http://localhost:3000
|
||||
1024 connections
|
||||
|
||||
┌─────────┬───────┬───────┬───────┬────────┬──────────┬──────────┬────────┐
|
||||
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
|
||||
├─────────┼───────┼───────┼───────┼────────┼──────────┼──────────┼────────┤
|
||||
│ Latency │ 55 ms │ 58 ms │ 91 ms │ 138 ms │ 61.88 ms │ 23.95 ms │ 747 ms │
|
||||
└─────────┴───────┴───────┴───────┴────────┴──────────┴──────────┴────────┘
|
||||
┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬─────────┬─────────┐
|
||||
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼─────────┼─────────┤
|
||||
│ Req/Sec │ 8407 │ 8407 │ 17407 │ 17743 │ 16454.41 │ 2716.94 │ 8402 │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼─────────┼─────────┤
|
||||
│ Bytes/Sec │ 1.81 MB │ 1.81 MB │ 3.74 MB │ 3.81 MB │ 3.54 MB │ 584 kB │ 1.81 MB │
|
||||
└───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴─────────┴─────────┘
|
||||
|
||||
Req/Bytes counts sampled once per second.
|
||||
|
||||
165k requests in 10.17s, 35.4 MB read
|
||||
-----------------------
|
||||
nest (with "@nestjs/platform-express")
|
||||
-----------------------
|
||||
Running 10s test @ http://localhost:3000
|
||||
1024 connections
|
||||
|
||||
┌─────────┬───────┬───────┬───────┬───────┬──────────┬──────────┬────────┐
|
||||
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
|
||||
├─────────┼───────┼───────┼───────┼───────┼──────────┼──────────┼────────┤
|
||||
│ Latency │ 61 ms │ 64 ms │ 71 ms │ 94 ms │ 65.44 ms │ 17.35 ms │ 325 ms │
|
||||
└─────────┴───────┴───────┴───────┴───────┴──────────┴──────────┴────────┘
|
||||
┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬────────┬─────────┐
|
||||
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼────────┼─────────┤
|
||||
│ Req/Sec │ 14183 │ 14183 │ 15767 │ 15991 │ 15640 │ 501.13 │ 14182 │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼────────┼─────────┤
|
||||
│ Bytes/Sec │ 3.06 MB │ 3.06 MB │ 3.41 MB │ 3.45 MB │ 3.38 MB │ 108 kB │ 3.06 MB │
|
||||
└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴────────┴─────────┘
|
||||
|
||||
Req/Bytes counts sampled once per second.
|
||||
|
||||
156k requests in 10.24s, 33.8 MB read
|
||||
-----------------------
|
||||
fastify
|
||||
-----------------------
|
||||
Running 10s test @ http://localhost:3000
|
||||
1024 connections
|
||||
|
||||
┌─────────┬───────┬───────┬───────┬───────┬──────────┬──────────┬─────────┐
|
||||
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
|
||||
├─────────┼───────┼───────┼───────┼───────┼──────────┼──────────┼─────────┤
|
||||
│ Latency │ 27 ms │ 30 ms │ 39 ms │ 78 ms │ 31.62 ms │ 26.59 ms │ 1232 ms │
|
||||
└─────────┴───────┴───────┴───────┴───────┴──────────┴──────────┴─────────┘
|
||||
┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
|
||||
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
|
||||
│ Req/Sec │ 19935 │ 19935 │ 33247 │ 34111 │ 32030.4 │ 4103.84 │ 19931 │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
|
||||
│ Bytes/Sec │ 3.03 MB │ 3.03 MB │ 5.05 MB │ 5.19 MB │ 4.87 MB │ 624 kB │ 3.03 MB │
|
||||
└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
|
||||
|
||||
Req/Bytes counts sampled once per second.
|
||||
|
||||
320k requests in 10.18s, 48.7 MB read
|
||||
-----------------------
|
||||
nest (with "@nestjs/platform-fastify")
|
||||
-----------------------
|
||||
Running 10s test @ http://localhost:3000
|
||||
1024 connections
|
||||
|
||||
┌─────────┬───────┬───────┬───────┬───────┬──────────┬──────────┬────────┐
|
||||
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
|
||||
├─────────┼───────┼───────┼───────┼───────┼──────────┼──────────┼────────┤
|
||||
│ Latency │ 31 ms │ 33 ms │ 38 ms │ 52 ms │ 34.41 ms │ 11.73 ms │ 245 ms │
|
||||
└─────────┴───────┴───────┴───────┴───────┴──────────┴──────────┴────────┘
|
||||
┌───────────┬─────────┬─────────┬────────┬─────────┬─────────┬─────────┬─────────┐
|
||||
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
|
||||
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
|
||||
│ Req/Sec │ 24911 │ 24911 │ 30031 │ 30335 │ 29470.4 │ 1564.48 │ 24907 │
|
||||
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
|
||||
│ Bytes/Sec │ 3.81 MB │ 3.81 MB │ 4.6 MB │ 4.64 MB │ 4.51 MB │ 239 kB │ 3.81 MB │
|
||||
└───────────┴─────────┴─────────┴────────┴─────────┴─────────┴─────────┴─────────┘
|
||||
|
||||
Req/Bytes counts sampled once per second.
|
||||
|
||||
295k requests in 10.17s, 45.1 MB read
|
||||
7
benchmarks/express.js
Normal file
7
benchmarks/express.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
app.get('/', async (req, res) => res.send('Hello world'));
|
||||
app.listen(3000);
|
||||
7
benchmarks/fastify.js
Normal file
7
benchmarks/fastify.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const fastify = require('fastify')();
|
||||
fastify.get('/', async (req, reply) => reply.send('Hello world'));
|
||||
fastify.listen({
|
||||
port: 3000
|
||||
});
|
||||
14
benchmarks/nest-fastify.js
Normal file
14
benchmarks/nest-fastify.js
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const core_1 = require('@nestjs/core');
|
||||
const fastify_platform_1 = require('@nestjs/platform-fastify');
|
||||
const app_module_1 = require('./nest/app.module');
|
||||
core_1.NestFactory.create(
|
||||
app_module_1.AppModule,
|
||||
new fastify_platform_1.FastifyAdapter(),
|
||||
{
|
||||
logger: false,
|
||||
bodyParser: false,
|
||||
},
|
||||
).then(app => app.listen(3000));
|
||||
//# sourceMappingURL=main.js.map
|
||||
9
benchmarks/nest.js
Normal file
9
benchmarks/nest.js
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const core_1 = require('@nestjs/core');
|
||||
const app_module_1 = require('./nest/app.module');
|
||||
core_1.NestFactory.create(app_module_1.AppModule, {
|
||||
logger: false,
|
||||
bodyParser: false,
|
||||
}).then(app => app.listen(3000));
|
||||
//# sourceMappingURL=main.js.map
|
||||
3
benchmarks/nest/app.controller.d.ts
vendored
Normal file
3
benchmarks/nest/app.controller.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export declare class AppController {
|
||||
root(): string;
|
||||
}
|
||||
47
benchmarks/nest/app.controller.js
Normal file
47
benchmarks/nest/app.controller.js
Normal file
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
var __decorate =
|
||||
(this && this.__decorate) ||
|
||||
function(decorators, target, key, desc) {
|
||||
var c = arguments.length,
|
||||
r =
|
||||
c < 3
|
||||
? target
|
||||
: desc === null
|
||||
? (desc = Object.getOwnPropertyDescriptor(target, key))
|
||||
: desc,
|
||||
d;
|
||||
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function')
|
||||
r = Reflect.decorate(decorators, target, key, desc);
|
||||
else
|
||||
for (var i = decorators.length - 1; i >= 0; i--)
|
||||
if ((d = decorators[i]))
|
||||
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata =
|
||||
(this && this.__metadata) ||
|
||||
function(k, v) {
|
||||
if (typeof Reflect === 'object' && typeof Reflect.metadata === 'function')
|
||||
return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const common_1 = require('@nestjs/common');
|
||||
let AppController = class AppController {
|
||||
root() {
|
||||
return 'Hello world!';
|
||||
}
|
||||
};
|
||||
__decorate(
|
||||
[
|
||||
common_1.Get(),
|
||||
__metadata('design:type', Function),
|
||||
__metadata('design:paramtypes', []),
|
||||
__metadata('design:returntype', String),
|
||||
],
|
||||
AppController.prototype,
|
||||
'root',
|
||||
null,
|
||||
);
|
||||
AppController = __decorate([common_1.Controller()], AppController);
|
||||
exports.AppController = AppController;
|
||||
//# sourceMappingURL=app.controller.js.map
|
||||
1
benchmarks/nest/app.controller.js.map
Normal file
1
benchmarks/nest/app.controller.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,2CAAiD;AAGjD,IAAa,aAAa,GAA1B,MAAa,aAAa;IAExB,IAAI;QACF,OAAO,cAAc,CAAA;IACvB,CAAC;CACF,CAAA;AAHC;IADC,YAAG,EAAE;;;;yCAGL;AAJU,aAAa;IADzB,mBAAU,EAAE;GACA,aAAa,CAKzB;AALY,sCAAa"}
|
||||
1
benchmarks/nest/app.module.d.ts
vendored
Normal file
1
benchmarks/nest/app.module.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare class AppModule {}
|
||||
20
benchmarks/nest/app.module.js
Normal file
20
benchmarks/nest/app.module.js
Normal file
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const common_1 = require("@nestjs/common");
|
||||
const app_controller_1 = require("./app.controller");
|
||||
let AppModule = class AppModule {
|
||||
};
|
||||
AppModule = __decorate([
|
||||
common_1.Module({
|
||||
imports: [],
|
||||
controllers: [app_controller_1.AppController],
|
||||
})
|
||||
], AppModule);
|
||||
exports.AppModule = AppModule;
|
||||
//# sourceMappingURL=app.module.js.map
|
||||
1
benchmarks/nest/app.module.js.map
Normal file
1
benchmarks/nest/app.module.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,2CAAwC;AACxC,qDAAiD;AAMjD,IAAa,SAAS,GAAtB,MAAa,SAAS;CAAG,CAAA;AAAZ,SAAS;IAJrB,eAAM,CAAC;QACN,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,CAAC,8BAAa,CAAC;KAC7B,CAAC;GACW,SAAS,CAAG;AAAZ,8BAAS"}
|
||||
1
benchmarks/nest/main.d.ts
vendored
Normal file
1
benchmarks/nest/main.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
1
benchmarks/nest/main.js.map
Normal file
1
benchmarks/nest/main.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAChD,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AACD,SAAS,EAAE,CAAC"}
|
||||
@@ -20,6 +20,7 @@ export default tseslint.config(
|
||||
ecmaVersion: 5,
|
||||
sourceType: 'module',
|
||||
parserOptions: {
|
||||
project: ['tsconfig.json', 'tsconfig.spec.json'],
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
@@ -37,19 +38,18 @@ export default tseslint.config(
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'warn',
|
||||
'@typescript-eslint/no-misused-promises': [
|
||||
'error',
|
||||
"@typescript-eslint/no-misused-promises": [
|
||||
"error",
|
||||
{
|
||||
checksVoidReturn: false,
|
||||
checksConditionals: false,
|
||||
},
|
||||
"checksVoidReturn": false,
|
||||
"checksConditionals": false
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/require-await': 'off',
|
||||
"@typescript-eslint/require-await": "off",
|
||||
'@typescript-eslint/prefer-promise-reject-errors': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/unbound-method': 'off',
|
||||
'@typescript-eslint/only-throw-error': 'off',
|
||||
},
|
||||
},
|
||||
);
|
||||
);
|
||||
@@ -38,12 +38,10 @@ describe.skip('Fastify Cors', () => {
|
||||
);
|
||||
|
||||
let requestId = 0;
|
||||
const configDelegation = {
|
||||
delegator: function (req, cb) {
|
||||
const config = configs[requestId];
|
||||
requestId++;
|
||||
cb(null, config);
|
||||
},
|
||||
const configDelegation = function (req, cb) {
|
||||
const config = configs[requestId];
|
||||
requestId++;
|
||||
cb(null, config);
|
||||
};
|
||||
app.enableCors(configDelegation);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
- "9001:9001"
|
||||
restart: always
|
||||
mysql:
|
||||
image: mysql:9.6.0
|
||||
image: mysql:8.4.3
|
||||
environment:
|
||||
MYSQL_ROOT_HOST: '%'
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
@@ -51,7 +51,7 @@ services:
|
||||
zookeeper:
|
||||
container_name: test-zookeeper
|
||||
hostname: zookeeper
|
||||
image: confluentinc/cp-zookeeper:7.9.5
|
||||
image: confluentinc/cp-zookeeper:7.7.1
|
||||
ports:
|
||||
- "2181:2181"
|
||||
environment:
|
||||
@@ -60,7 +60,7 @@ services:
|
||||
kafka:
|
||||
container_name: test-kafka
|
||||
hostname: kafka
|
||||
image: confluentinc/cp-kafka:8.1.1
|
||||
image: confluentinc/cp-kafka:7.7.1
|
||||
depends_on:
|
||||
- zookeeper
|
||||
ports:
|
||||
|
||||
@@ -46,6 +46,6 @@ export class RecipesResolver {
|
||||
|
||||
@Subscription(returns => Recipe)
|
||||
recipeAdded() {
|
||||
return pubSub.asyncIterableIterator('recipeAdded');
|
||||
return pubSub.asyncIterator('recipeAdded');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,6 @@ export class CatsResolvers {
|
||||
|
||||
@Subscription('catCreated')
|
||||
catCreated() {
|
||||
return pubSub.asyncIterableIterator('catCreated');
|
||||
return pubSub.asyncIterator('catCreated');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Injectable,
|
||||
MiddlewareConsumer,
|
||||
Module,
|
||||
NestModule,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
FastifyAdapter,
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('Middleware before init (FastifyAdapter)', () => {
|
||||
let app: NestFastifyApplication;
|
||||
|
||||
@Injectable()
|
||||
class TestService {
|
||||
getData(): string {
|
||||
return 'test_data';
|
||||
}
|
||||
}
|
||||
|
||||
@Controller()
|
||||
class TestController {
|
||||
constructor(private readonly testService: TestService) {}
|
||||
|
||||
@Get('test')
|
||||
test() {
|
||||
return { data: this.testService.getData() };
|
||||
}
|
||||
|
||||
@Get('health')
|
||||
health() {
|
||||
return { status: 'ok' };
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
controllers: [TestController],
|
||||
providers: [TestService],
|
||||
})
|
||||
class TestModule implements NestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => {
|
||||
res.setHeader('x-middleware', 'applied');
|
||||
next();
|
||||
})
|
||||
.forRoutes('*');
|
||||
}
|
||||
}
|
||||
|
||||
describe('should queue middleware when registered before init', () => {
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter(),
|
||||
);
|
||||
|
||||
// Register middleware before init - should be queued
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('x-global-middleware', 'applied');
|
||||
next();
|
||||
});
|
||||
|
||||
// Now init the app - queued middleware should be registered
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
});
|
||||
|
||||
it('should apply queued middleware after init', () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/test',
|
||||
})
|
||||
.then(({ statusCode, payload, headers }) => {
|
||||
expect(statusCode).to.equal(200);
|
||||
expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' });
|
||||
// Verify both module-level and global middleware were applied
|
||||
expect(headers['x-middleware']).to.equal('applied');
|
||||
expect(headers['x-global-middleware']).to.equal('applied');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should work when app is initialized before middleware registration', () => {
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter(),
|
||||
);
|
||||
|
||||
// Initialize app first
|
||||
await app.init();
|
||||
|
||||
// Now middleware registration should work
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('x-global-middleware', 'applied');
|
||||
next();
|
||||
});
|
||||
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
});
|
||||
|
||||
it('should register middleware successfully after init', () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/test',
|
||||
})
|
||||
.then(({ statusCode, payload }) => {
|
||||
expect(statusCode).to.equal(200);
|
||||
expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' });
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,163 +0,0 @@
|
||||
import { ConsoleLogger, INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('ForceConsole Option', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
describe('When forceConsole is true', () => {
|
||||
let consoleLogSpy: sinon.SinonSpy;
|
||||
let consoleErrorSpy: sinon.SinonSpy;
|
||||
let processStdoutSpy: sinon.SinonSpy;
|
||||
let processStderrSpy: sinon.SinonSpy;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Spy on console and process methods
|
||||
consoleLogSpy = sinon.spy(console, 'log');
|
||||
consoleErrorSpy = sinon.spy(console, 'error');
|
||||
processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
processStderrSpy = sinon.spy(process.stderr, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication({
|
||||
forceConsole: true,
|
||||
logger: ['log', 'error'],
|
||||
});
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
consoleLogSpy.restore();
|
||||
consoleErrorSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
processStderrSpy.restore();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should use console.log instead of process.stdout.write', async () => {
|
||||
const logger = new ConsoleLogger('TestContext', { forceConsole: true });
|
||||
logger.log('Test log message');
|
||||
|
||||
// Should use console.log when forceConsole is true
|
||||
expect(consoleLogSpy.called).to.be.true;
|
||||
// Verify console.log was called with the message
|
||||
const consoleLogCalls = consoleLogSpy
|
||||
.getCalls()
|
||||
.filter(call =>
|
||||
call.args.some(arg => String(arg).includes('Test log message')),
|
||||
);
|
||||
expect(consoleLogCalls.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should use console.error instead of process.stderr.write', async () => {
|
||||
const logger = new ConsoleLogger('TestContext', { forceConsole: true });
|
||||
logger.error('Test error message');
|
||||
|
||||
// Should use console.error when forceConsole is true
|
||||
expect(consoleErrorSpy.called).to.be.true;
|
||||
// Verify console.error was called with the message
|
||||
const consoleErrorCalls = consoleErrorSpy
|
||||
.getCalls()
|
||||
.filter(call =>
|
||||
call.args.some(arg => String(arg).includes('Test error message')),
|
||||
);
|
||||
expect(consoleErrorCalls.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should handle GET request with forceConsole option enabled', () => {
|
||||
return request(app.getHttpServer()).get('/hello').expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When forceConsole is false (default)', () => {
|
||||
let consoleLogSpy: sinon.SinonSpy;
|
||||
let consoleErrorSpy: sinon.SinonSpy;
|
||||
let processStdoutSpy: sinon.SinonSpy;
|
||||
let processStderrSpy: sinon.SinonSpy;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Spy on console and process methods
|
||||
consoleLogSpy = sinon.spy(console, 'log');
|
||||
consoleErrorSpy = sinon.spy(console, 'error');
|
||||
processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
processStderrSpy = sinon.spy(process.stderr, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication({
|
||||
logger: ['log', 'error'],
|
||||
// forceConsole is not set, defaults to false
|
||||
});
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
consoleLogSpy.restore();
|
||||
consoleErrorSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
processStderrSpy.restore();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should not directly call console.log when forceConsole is false', async () => {
|
||||
const logger = new ConsoleLogger('TestContext');
|
||||
|
||||
// Reset spy to ensure clean state
|
||||
consoleLogSpy.resetHistory();
|
||||
|
||||
logger.log('Test log message');
|
||||
|
||||
// When forceConsole is false, should not call console.log
|
||||
expect(consoleLogSpy.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should not directly call console.error when forceConsole is false', async () => {
|
||||
const logger = new ConsoleLogger('TestContext');
|
||||
|
||||
// Reset spy to ensure clean state
|
||||
consoleErrorSpy.resetHistory();
|
||||
|
||||
logger.error('Test error message');
|
||||
|
||||
// When forceConsole is false, should not call console.error
|
||||
expect(consoleErrorSpy.called).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('When forceConsole is set via NestFactory.create', () => {
|
||||
it('should apply forceConsole to the default logger', async () => {
|
||||
const consoleLogSpy = sinon.spy(console, 'log');
|
||||
const processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
const testApp = moduleRef.createNestApplication({
|
||||
forceConsole: true,
|
||||
});
|
||||
|
||||
await testApp.init();
|
||||
|
||||
// The logger created by NestFactory should respect forceConsole option
|
||||
const logger = new ConsoleLogger('AppContext', { forceConsole: true });
|
||||
logger.log('Application started');
|
||||
|
||||
expect(consoleLogSpy.called).to.be.true;
|
||||
|
||||
consoleLogSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
await testApp.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,56 +1,20 @@
|
||||
import {
|
||||
Global,
|
||||
INestApplication,
|
||||
MiddlewareConsumer,
|
||||
Module,
|
||||
} from '@nestjs/common';
|
||||
import { INestApplication, MiddlewareConsumer, Module } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
|
||||
const RETURN_VALUE_A = 'test_A';
|
||||
const RETURN_VALUE_B = 'test_B';
|
||||
const RETURN_VALUE_X = 'test_X';
|
||||
const RETURN_VALUE_GLOBAL = 'test_GLOBAL';
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
class GlobalModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_GLOBAL))
|
||||
.forRoutes('ping');
|
||||
}
|
||||
}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
class GlobalModule2 {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_GLOBAL + '2'))
|
||||
.forRoutes('ping');
|
||||
}
|
||||
}
|
||||
|
||||
@Module({ imports: [GlobalModule, GlobalModule2] })
|
||||
class ModuleX {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_X))
|
||||
.forRoutes('hello')
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_X))
|
||||
.forRoutes('ping');
|
||||
}
|
||||
}
|
||||
|
||||
@Module({ imports: [ModuleX] })
|
||||
@Module({
|
||||
imports: [],
|
||||
})
|
||||
class ModuleA {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_A))
|
||||
.forRoutes('hello')
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_A))
|
||||
.forRoutes('ping');
|
||||
.apply((req, res, next) => {
|
||||
res.send(RETURN_VALUE_A);
|
||||
})
|
||||
.forRoutes('hello');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +24,10 @@ class ModuleA {
|
||||
class ModuleB {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_B))
|
||||
.forRoutes('hello')
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_B))
|
||||
.forRoutes('ping');
|
||||
.apply((req, res, next) => {
|
||||
res.send(RETURN_VALUE_B);
|
||||
})
|
||||
.forRoutes('hello');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,12 +55,6 @@ describe('Middleware (execution order)', () => {
|
||||
.expect(200, RETURN_VALUE_B);
|
||||
});
|
||||
|
||||
it('should execute global middleware first', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/ping')
|
||||
.expect(200, RETURN_VALUE_GLOBAL);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
Module,
|
||||
NestMiddleware,
|
||||
NestModule,
|
||||
Param,
|
||||
Query,
|
||||
Req,
|
||||
RequestMethod,
|
||||
@@ -208,15 +207,6 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.then(({ payload }) => expect(payload).to.be.eql(INCLUDED_VALUE));
|
||||
});
|
||||
|
||||
it(`GET forRoutes(POST /tests/%69ncluded) - ensure middleware is executed correctly with encoded characters`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'POST',
|
||||
url: '/tests/%69ncluded', // 'i' character is encoded
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(INCLUDED_VALUE));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
@@ -449,11 +439,6 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
async rootPath(@Req() req: FastifyRequest['raw']) {
|
||||
return { success: true, root: true };
|
||||
}
|
||||
|
||||
@Get('record/:id')
|
||||
async record(@Req() req: FastifyRequest['raw'], @Param('id') id: string) {
|
||||
return { success: true, record: id };
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -465,14 +450,9 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.apply((req, res, next) => {
|
||||
req.extras = { data: 'Data attached in middleware' };
|
||||
req.headers['ping'] = 'pong';
|
||||
|
||||
// When global prefix is set and the route is the root path
|
||||
if (req.originalUrl === '/api') {
|
||||
return res.end(JSON.stringify({ success: true, pong: 'pong' }));
|
||||
}
|
||||
next();
|
||||
})
|
||||
.forRoutes('{*path}');
|
||||
.forRoutes('*');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +464,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
).createNestApplication<NestFastifyApplication>(new FastifyAdapter());
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix (route: /api/pong)`, async () => {
|
||||
it(`GET forRoutes('*') with global prefix`, async () => {
|
||||
app.setGlobalPrefix('/api');
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
@@ -503,26 +483,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix (route: /api)`, async () => {
|
||||
app.setGlobalPrefix('/api');
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/api',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
pong: 'pong',
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') without prefix config`, async () => {
|
||||
it(`GET forRoutes('*') without prefix config`, async () => {
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
return app
|
||||
@@ -540,7 +501,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix and exclude patterns`, async () => {
|
||||
it(`GET forRoutes('*') with global prefix and exclude patterns`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/'] });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
@@ -550,33 +511,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.expect(200, { success: true, root: true });
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix and exclude pattern with wildcard`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/record/{*path}'] });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get('/api/pong')
|
||||
.expect(200, { success: true, pong: 'pong' });
|
||||
await request(app.getHttpServer())
|
||||
.get('/record/abc123')
|
||||
.expect(200, { success: true, record: 'abc123' });
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix and exclude pattern with parameter`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/record/:id'] });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get('/record/abc123')
|
||||
.expect(200, { success: true, record: 'abc123' });
|
||||
await request(app.getHttpServer())
|
||||
.get('/api/pong')
|
||||
.expect(200, { success: true, pong: 'pong' });
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix and global prefix options`, async () => {
|
||||
it(`GET forRoutes('*') with global prefix and global prefix options`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/'] });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
@@ -593,7 +528,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.expect(200, { success: true, root: true });
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix that not starts with /`, async () => {
|
||||
it(`GET forRoutes('*') with global prefix that not starts with /`, async () => {
|
||||
app.setGlobalPrefix('api');
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
@@ -612,158 +547,4 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should respect fastify routing options', () => {
|
||||
const MIDDLEWARE_RETURN_VALUE = 'middleware_return';
|
||||
|
||||
@Controller()
|
||||
class TestController {
|
||||
@Get('abc/def')
|
||||
included() {
|
||||
return 'whatnot';
|
||||
}
|
||||
}
|
||||
@Module({
|
||||
imports: [AppModule],
|
||||
controllers: [TestController],
|
||||
})
|
||||
class TestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.end(MIDDLEWARE_RETURN_VALUE))
|
||||
.forRoutes({ path: 'abc/def', method: RequestMethod.GET });
|
||||
}
|
||||
}
|
||||
|
||||
describe('[ignoreTrailingSlash] attribute', () => {
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter({
|
||||
ignoreTrailingSlash: true,
|
||||
// routerOptions: {
|
||||
// ignoreTrailingSlash: true,
|
||||
// },
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`GET forRoutes(GET /abc/def/)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/abc/def/', // trailing slash
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(MIDDLEWARE_RETURN_VALUE),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[ignoreDuplicateSlashes] attribute', () => {
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter({
|
||||
routerOptions: {
|
||||
ignoreDuplicateSlashes: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`GET forRoutes(GET /abc//def)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/abc//def', // duplicate slashes
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(MIDDLEWARE_RETURN_VALUE),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[caseSensitive] attribute', () => {
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter({
|
||||
routerOptions: {
|
||||
caseSensitive: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`GET forRoutes(GET /ABC/DEF)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/ABC/DEF', // different case
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(MIDDLEWARE_RETURN_VALUE),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[useSemicolonDelimiter] attribute', () => {
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter({
|
||||
routerOptions: { useSemicolonDelimiter: true } as any,
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`GET forRoutes(GET /abc/def;foo=bar)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/abc/def;foo=bar', // semicolon delimiter
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(MIDDLEWARE_RETURN_VALUE),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,21 +51,4 @@ describe('enableShutdownHooks', () => {
|
||||
expect(result.stdout.toString().trim()).to.be.eq('');
|
||||
done();
|
||||
}).timeout(10000);
|
||||
|
||||
it('should call the correct hooks with useProcessExit option', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGHUP',
|
||||
'SIGHUP',
|
||||
'graceful',
|
||||
]);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).to.equal('beforeApplicationShutdown SIGHUP');
|
||||
expect(calls[1]).to.equal('onApplicationShutdown SIGHUP');
|
||||
expect(result.status).to.equal(0);
|
||||
done();
|
||||
}).timeout(10000);
|
||||
});
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
const SIGNAL = process.argv[2];
|
||||
const SIGNAL_TO_LISTEN = process.argv[3];
|
||||
const USE_GRACEFUL_EXIT = process.argv[4] === 'graceful';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable
|
||||
@@ -30,12 +29,10 @@ class AppModule {}
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, { logger: false });
|
||||
|
||||
const shutdownOptions = USE_GRACEFUL_EXIT ? { useProcessExit: true } : {};
|
||||
|
||||
if (SIGNAL_TO_LISTEN && SIGNAL_TO_LISTEN !== 'NONE') {
|
||||
app.enableShutdownHooks([SIGNAL_TO_LISTEN], shutdownOptions);
|
||||
app.enableShutdownHooks([SIGNAL_TO_LISTEN]);
|
||||
} else if (SIGNAL_TO_LISTEN !== 'NONE') {
|
||||
app.enableShutdownHooks([], shutdownOptions);
|
||||
app.enableShutdownHooks();
|
||||
}
|
||||
|
||||
await app.listen(1800);
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { Global, Inject, Injectable, Module, Scope } from '@nestjs/common';
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule1 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule2 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule3 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule4 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule5 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule6 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule7 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule8 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule9 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule10 {}
|
||||
|
||||
@Injectable()
|
||||
class TransientProvider {}
|
||||
|
||||
@Injectable()
|
||||
class RequestProvider {}
|
||||
|
||||
@Injectable()
|
||||
class ForeignTransientProvider {}
|
||||
|
||||
@Injectable()
|
||||
export class Dependant {
|
||||
constructor(
|
||||
private readonly transientProvider: TransientProvider,
|
||||
|
||||
private readonly foreignTransientProvider: ForeignTransientProvider,
|
||||
|
||||
@Inject(RequestProvider)
|
||||
private readonly requestProvider: RequestProvider,
|
||||
) {}
|
||||
|
||||
public checkDependencies() {
|
||||
expect(this.transientProvider).to.be.instanceOf(TransientProvider);
|
||||
expect(this.foreignTransientProvider).to.be.instanceOf(
|
||||
ForeignTransientProvider,
|
||||
);
|
||||
expect(this.requestProvider).to.be.instanceOf(RequestProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: ForeignTransientProvider,
|
||||
scope: Scope.TRANSIENT,
|
||||
useClass: ForeignTransientProvider,
|
||||
},
|
||||
],
|
||||
exports: [ForeignTransientProvider],
|
||||
})
|
||||
export class ModuleWithForeignTransientProvider {}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: TransientProvider,
|
||||
scope: Scope.TRANSIENT,
|
||||
useClass: TransientProvider,
|
||||
},
|
||||
{
|
||||
provide: Dependant,
|
||||
scope: Scope.DEFAULT,
|
||||
useClass: Dependant,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class GlobalModuleWithTransientProviderAndDependant {}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: RequestProvider,
|
||||
scope: Scope.REQUEST,
|
||||
useFactory: () => {
|
||||
return new RequestProvider();
|
||||
},
|
||||
},
|
||||
],
|
||||
exports: [RequestProvider],
|
||||
})
|
||||
export class GlobalModuleWithRequestProvider {}
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
/*
|
||||
* ForeginTransientProvider will be resolved quickly because its host module is imported first.
|
||||
* IMPORTANT: Do not move this module, otherwise we may not catch future regressions.
|
||||
*/
|
||||
ModuleWithForeignTransientProvider,
|
||||
|
||||
GlobalModule1,
|
||||
GlobalModule2,
|
||||
GlobalModule3,
|
||||
GlobalModule4,
|
||||
GlobalModule5,
|
||||
GlobalModule6,
|
||||
GlobalModule7,
|
||||
GlobalModule8,
|
||||
GlobalModule9,
|
||||
GlobalModule10,
|
||||
GlobalModuleWithTransientProviderAndDependant,
|
||||
GlobalModuleWithRequestProvider,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
describe('Many global modules', () => {
|
||||
it('should inject request-scoped and transient-scoped providers from different modules', async () => {
|
||||
const moduleBuilder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
const moduleRef = await moduleBuilder.compile();
|
||||
|
||||
const dependant = await moduleRef.resolve(Dependant);
|
||||
const checkDependenciesSpy = sinon.spy(dependant, 'checkDependencies');
|
||||
dependant.checkDependencies();
|
||||
|
||||
expect(checkDependenciesSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
@@ -24,7 +24,7 @@ describe('Multiple providers under the same token ("each" feature)', () => {
|
||||
});
|
||||
});
|
||||
describe('resolve()', () => {
|
||||
it('should return an array of request-scoped providers', async () => {
|
||||
it('should return an array of providers', async () => {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [MultipleProvidersModule],
|
||||
});
|
||||
@@ -43,25 +43,5 @@ describe('Multiple providers under the same token ("each" feature)', () => {
|
||||
|
||||
expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']);
|
||||
});
|
||||
|
||||
it('should return an array of default-scoped providers', async () => {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [MultipleProvidersModule],
|
||||
});
|
||||
const testingModule = await builder.compile();
|
||||
|
||||
const multiProviderInstances = await testingModule.resolve<string>(
|
||||
'MULTI_PROVIDER',
|
||||
undefined,
|
||||
{
|
||||
each: true,
|
||||
},
|
||||
);
|
||||
|
||||
// @ts-expect-error: make sure "multiProviderInstances" is string[] not string
|
||||
multiProviderInstances.charAt;
|
||||
|
||||
expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -127,8 +127,7 @@ Potential solutions:
|
||||
@Module({
|
||||
imports: [ /* the Module containing "MISSING_DEP" */ ]
|
||||
})
|
||||
|
||||
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors`);
|
||||
`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Injectable, Scope, Module } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ContextIdFactory, REQUEST } from '@nestjs/core';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('Request Scope Bubbling', () => {
|
||||
// 1. Define the "Poison" (Request Scoped Service)
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
class ChildService {
|
||||
// A random ID to verify uniqueness
|
||||
public readonly id = Math.random();
|
||||
}
|
||||
|
||||
// 2. Define the "Victim" (Singleton that depends on Request Scoped)
|
||||
@Injectable()
|
||||
class ParentService {
|
||||
constructor(public readonly child: ChildService) {}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [ChildService, ParentService],
|
||||
})
|
||||
class TestModule {}
|
||||
|
||||
it('should downgrade a Singleton to Request-Scoped if it depends on a Request-Scoped provider', async () => {
|
||||
// 3. Bootstrap the Module (using Test.createTestingModule instead of NestFactory)
|
||||
const moduleRef: TestingModule = await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile();
|
||||
|
||||
// 4. Simulate Request 1
|
||||
const contextId1 = ContextIdFactory.create();
|
||||
const parent1 = await moduleRef.resolve(ParentService, contextId1);
|
||||
|
||||
// 5. Simulate Request 2
|
||||
const contextId2 = ContextIdFactory.create();
|
||||
const parent2 = await moduleRef.resolve(ParentService, contextId2);
|
||||
|
||||
// 6. Assertions (The "Moment of Truth")
|
||||
|
||||
// The Child IDs should be different (Proof of Request Scope)
|
||||
expect(parent1.child.id).to.not.equal(parent2.child.id);
|
||||
|
||||
// The Parent instances should ALSO be different (Proof of Bubbling)
|
||||
// If Parent was a true Singleton, these would be equal.
|
||||
expect(parent1).to.not.equal(parent2);
|
||||
});
|
||||
});
|
||||
@@ -2197,22 +2197,6 @@
|
||||
},
|
||||
"id": "1976848738"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "HelloModule",
|
||||
"sourceClassName": "HelloModule",
|
||||
"targetClassName": "Meta",
|
||||
"sourceClassToken": "HelloModule",
|
||||
"targetClassToken": "META",
|
||||
"targetModuleName": "HelloModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-2105726668": {
|
||||
"source": "-1803759743",
|
||||
"target": "1010833816",
|
||||
@@ -2229,6 +2213,22 @@
|
||||
},
|
||||
"id": "-2105726668"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "HelloModule",
|
||||
"sourceClassName": "HelloModule",
|
||||
"targetClassName": "Meta",
|
||||
"sourceClassToken": "HelloModule",
|
||||
"targetClassToken": "META",
|
||||
"targetModuleName": "HelloModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-1657371464": {
|
||||
"source": "-1673986099",
|
||||
"target": "1919157847",
|
||||
@@ -2263,22 +2263,6 @@
|
||||
},
|
||||
"id": "-1303681274"
|
||||
},
|
||||
"-831049991": {
|
||||
"source": "594986539",
|
||||
"target": "-1721730431",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "InputModule",
|
||||
"sourceClassName": "InputService",
|
||||
"targetClassName": "CircularService",
|
||||
"sourceClassToken": "InputService",
|
||||
"targetClassToken": "CircularService",
|
||||
"targetModuleName": "CircularModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-831049991"
|
||||
},
|
||||
"-886102564": {
|
||||
"source": "208171089",
|
||||
"target": "671882984",
|
||||
@@ -2296,22 +2280,6 @@
|
||||
},
|
||||
"id": "-886102564"
|
||||
},
|
||||
"-2146943494": {
|
||||
"source": "-234035039",
|
||||
"target": "928565345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "RequestChainModule",
|
||||
"sourceClassName": "RequestChainService",
|
||||
"targetClassName": "HelperService",
|
||||
"sourceClassToken": "RequestChainService",
|
||||
"targetClassToken": "HelperService",
|
||||
"targetModuleName": "HelperModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-2146943494"
|
||||
},
|
||||
"-2003045613": {
|
||||
"source": "-377928898",
|
||||
"target": "-616397055",
|
||||
@@ -2344,6 +2312,38 @@
|
||||
},
|
||||
"id": "-881420795"
|
||||
},
|
||||
"-831049991": {
|
||||
"source": "594986539",
|
||||
"target": "-1721730431",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "InputModule",
|
||||
"sourceClassName": "InputService",
|
||||
"targetClassName": "CircularService",
|
||||
"sourceClassToken": "InputService",
|
||||
"targetClassToken": "CircularService",
|
||||
"targetModuleName": "CircularModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-831049991"
|
||||
},
|
||||
"-2146943494": {
|
||||
"source": "-234035039",
|
||||
"target": "928565345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "RequestChainModule",
|
||||
"sourceClassName": "RequestChainService",
|
||||
"targetClassName": "HelperService",
|
||||
"sourceClassToken": "RequestChainService",
|
||||
"targetClassToken": "HelperService",
|
||||
"targetModuleName": "HelperModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-2146943494"
|
||||
},
|
||||
"-1816180282": {
|
||||
"source": "-848516688",
|
||||
"target": "-1673986099",
|
||||
|
||||
@@ -2181,22 +2181,6 @@
|
||||
},
|
||||
"id": "1976848738"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "HelloModule",
|
||||
"sourceClassName": "HelloModule",
|
||||
"targetClassName": "Meta",
|
||||
"sourceClassToken": "HelloModule",
|
||||
"targetClassToken": "META",
|
||||
"targetModuleName": "HelloModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-2105726668": {
|
||||
"source": "-1803759743",
|
||||
"target": "1010833816",
|
||||
@@ -2213,6 +2197,22 @@
|
||||
},
|
||||
"id": "-2105726668"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "HelloModule",
|
||||
"sourceClassName": "HelloModule",
|
||||
"targetClassName": "Meta",
|
||||
"sourceClassToken": "HelloModule",
|
||||
"targetClassToken": "META",
|
||||
"targetModuleName": "HelloModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-1657371464": {
|
||||
"source": "-1673986099",
|
||||
"target": "1919157847",
|
||||
@@ -2247,22 +2247,6 @@
|
||||
},
|
||||
"id": "-1303681274"
|
||||
},
|
||||
"-831049991": {
|
||||
"source": "594986539",
|
||||
"target": "-1721730431",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "InputModule",
|
||||
"sourceClassName": "InputService",
|
||||
"targetClassName": "CircularService",
|
||||
"sourceClassToken": "InputService",
|
||||
"targetClassToken": "CircularService",
|
||||
"targetModuleName": "CircularModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-831049991"
|
||||
},
|
||||
"-886102564": {
|
||||
"source": "208171089",
|
||||
"target": "671882984",
|
||||
@@ -2280,22 +2264,6 @@
|
||||
},
|
||||
"id": "-886102564"
|
||||
},
|
||||
"-2146943494": {
|
||||
"source": "-234035039",
|
||||
"target": "928565345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "RequestChainModule",
|
||||
"sourceClassName": "RequestChainService",
|
||||
"targetClassName": "HelperService",
|
||||
"sourceClassToken": "RequestChainService",
|
||||
"targetClassToken": "HelperService",
|
||||
"targetModuleName": "HelperModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-2146943494"
|
||||
},
|
||||
"-2003045613": {
|
||||
"source": "-377928898",
|
||||
"target": "-616397055",
|
||||
@@ -2328,6 +2296,38 @@
|
||||
},
|
||||
"id": "-881420795"
|
||||
},
|
||||
"-831049991": {
|
||||
"source": "594986539",
|
||||
"target": "-1721730431",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "InputModule",
|
||||
"sourceClassName": "InputService",
|
||||
"targetClassName": "CircularService",
|
||||
"sourceClassToken": "InputService",
|
||||
"targetClassToken": "CircularService",
|
||||
"targetModuleName": "CircularModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-831049991"
|
||||
},
|
||||
"-2146943494": {
|
||||
"source": "-234035039",
|
||||
"target": "928565345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "RequestChainModule",
|
||||
"sourceClassName": "RequestChainService",
|
||||
"targetClassName": "HelperService",
|
||||
"sourceClassToken": "RequestChainService",
|
||||
"targetClassToken": "HelperService",
|
||||
"targetModuleName": "HelperModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-2146943494"
|
||||
},
|
||||
"-1816180282": {
|
||||
"source": "-848516688",
|
||||
"target": "-1673986099",
|
||||
|
||||
@@ -12,10 +12,9 @@ export interface Response<T> {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TransformInterceptor<T> implements NestInterceptor<
|
||||
T,
|
||||
Response<T>
|
||||
> {
|
||||
export class TransformInterceptor<T>
|
||||
implements NestInterceptor<T, Response<T>>
|
||||
{
|
||||
intercept(
|
||||
context: ExecutionContext,
|
||||
next: CallHandler<T>,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { LazyController } from '../src/lazy.controller';
|
||||
|
||||
describe('Lazy Requested Scoped providers', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
controllers: [LazyController],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should not recreate dependencies for default scope', async () => {
|
||||
const resultOne = await request(app.getHttpServer()).get('/lazy/request');
|
||||
|
||||
expect(resultOne.text).to.be.equal('Hi! Counter is 1');
|
||||
expect(resultOne.statusCode).to.be.equal(200);
|
||||
|
||||
const resultTwo = await request(app.getHttpServer()).get('/lazy/request');
|
||||
|
||||
expect(resultTwo.text).to.be.equal('Hi! Counter is 2');
|
||||
expect(resultTwo.statusCode).to.be.equal(200);
|
||||
});
|
||||
});
|
||||
@@ -13,16 +13,6 @@ export class LazyController {
|
||||
const { TransientService } = await import('./transient.service');
|
||||
const _service = await moduleRef.resolve(TransientService);
|
||||
|
||||
return _service.eager();
|
||||
}
|
||||
@Get('request')
|
||||
async execRequestScope() {
|
||||
const { RequestLazyModule } = await import('./request.module');
|
||||
const moduleRef = await this.lazyLoadModule.load(() => RequestLazyModule);
|
||||
|
||||
const { RequestService } = await import('./request.service');
|
||||
const _service = await moduleRef.resolve(RequestService);
|
||||
|
||||
return _service.eager();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { EagerService } from './eager.module';
|
||||
import { GlobalService } from './global.module';
|
||||
import { RequestService } from './request.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
providers: [RequestService, GlobalService, EagerService],
|
||||
exports: [RequestService],
|
||||
})
|
||||
export class RequestLazyModule {}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { EagerService } from './eager.module';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class RequestService {
|
||||
constructor(private eagerService: EagerService) {}
|
||||
|
||||
eager() {
|
||||
return this.eagerService.sayHello();
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { INestApplication, INestMicroservice } from '@nestjs/common';
|
||||
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { RMQFanoutExchangeProducerController } from '../src/rmq/fanout-exchange-producer-rmq.controller';
|
||||
import { RMQFanoutExchangeConsumerController } from '../src/rmq/fanout-exchange-consumer-rmq.controller';
|
||||
|
||||
describe('RabbitMQ transport (Fanout Exchange)', () => {
|
||||
let server: any;
|
||||
let appProducer: INestApplication;
|
||||
let appConsumer: INestMicroservice;
|
||||
|
||||
beforeEach(async () => {
|
||||
const producerModule = await Test.createTestingModule({
|
||||
controllers: [RMQFanoutExchangeProducerController],
|
||||
}).compile();
|
||||
const consumerModule = await Test.createTestingModule({
|
||||
controllers: [RMQFanoutExchangeConsumerController],
|
||||
}).compile();
|
||||
|
||||
appProducer = producerModule.createNestApplication();
|
||||
server = appProducer.getHttpAdapter().getInstance();
|
||||
|
||||
appConsumer = consumerModule.createNestMicroservice<MicroserviceOptions>({
|
||||
transport: Transport.RMQ,
|
||||
options: {
|
||||
urls: [`amqp://0.0.0.0:5672`],
|
||||
queue: '',
|
||||
exchange: 'test.fanout',
|
||||
exchangeType: 'fanout',
|
||||
queueOptions: {
|
||||
exclusive: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
await Promise.all([appProducer.init(), appConsumer.listen()]);
|
||||
});
|
||||
|
||||
it(`should send message to fanout exchange`, async () => {
|
||||
await request(server).get('/fanout-exchange').expect(200, 'ping/pong');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all([appProducer.close(), appConsumer.close()]);
|
||||
});
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { RMQTopicExchangeController } from '../src/rmq/topic-exchange-rmq.controller';
|
||||
|
||||
describe('RabbitMQ transport (Topic Exchange - wildcards)', () => {
|
||||
let server: any;
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
controllers: [RMQTopicExchangeController],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpAdapter().getInstance();
|
||||
|
||||
app.connectMicroservice<MicroserviceOptions>({
|
||||
transport: Transport.RMQ,
|
||||
options: {
|
||||
urls: [`amqp://0.0.0.0:5672`],
|
||||
queue: 'test2',
|
||||
wildcards: true,
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservices();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`should send message to wildcard topic exchange`, () => {
|
||||
return request(server).get('/topic-exchange').expect(200, 'wildcard.a.b');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import { Ctx, MessagePattern, RmqContext } from '@nestjs/microservices';
|
||||
|
||||
@Controller()
|
||||
export class RMQFanoutExchangeConsumerController {
|
||||
constructor() {}
|
||||
|
||||
@MessagePattern('ping')
|
||||
handleTopicExchange(@Ctx() ctx: RmqContext): string {
|
||||
return ctx.getPattern() + '/pong';
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import {
|
||||
ClientProxy,
|
||||
ClientProxyFactory,
|
||||
Transport,
|
||||
} from '@nestjs/microservices';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
@Controller()
|
||||
export class RMQFanoutExchangeProducerController {
|
||||
client: ClientProxy;
|
||||
|
||||
constructor() {
|
||||
this.client = ClientProxyFactory.create({
|
||||
transport: Transport.RMQ,
|
||||
options: {
|
||||
urls: [`amqp://localhost:5672`],
|
||||
exchange: 'test.fanout',
|
||||
exchangeType: 'fanout',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@Get('fanout-exchange')
|
||||
async topicExchange() {
|
||||
return lastValueFrom(this.client.send<string>('ping', 1));
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import {
|
||||
ClientProxy,
|
||||
ClientProxyFactory,
|
||||
Ctx,
|
||||
MessagePattern,
|
||||
RmqContext,
|
||||
Transport,
|
||||
} from '@nestjs/microservices';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
@Controller()
|
||||
export class RMQTopicExchangeController {
|
||||
client: ClientProxy;
|
||||
|
||||
constructor() {
|
||||
this.client = ClientProxyFactory.create({
|
||||
transport: Transport.RMQ,
|
||||
options: {
|
||||
urls: [`amqp://localhost:5672`],
|
||||
queue: 'test2',
|
||||
wildcards: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@Get('topic-exchange')
|
||||
async topicExchange() {
|
||||
return lastValueFrom(this.client.send<string>('wildcard.a.b', 1));
|
||||
}
|
||||
|
||||
@MessagePattern('wildcard.*.*')
|
||||
handleTopicExchange(@Ctx() ctx: RmqContext): string {
|
||||
return ctx.getPattern();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { EventSource } from 'eventsource';
|
||||
import * as EventSource from 'eventsource';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
describe('Sse (Express Application)', () => {
|
||||
@@ -20,14 +20,7 @@ describe('Sse (Express Application)', () => {
|
||||
const url = await app.getUrl();
|
||||
|
||||
eventSource = new EventSource(url + '/sse', {
|
||||
fetch: (input, init) =>
|
||||
fetch(input, {
|
||||
...init,
|
||||
headers: {
|
||||
...init?.headers,
|
||||
connection: 'keep-alive',
|
||||
},
|
||||
}),
|
||||
headers: { connection: 'keep-alive' },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,14 +57,7 @@ describe('Sse (Express Application)', () => {
|
||||
const url = await app.getUrl();
|
||||
|
||||
eventSource = new EventSource(url + '/sse', {
|
||||
fetch: (input, init) =>
|
||||
fetch(input, {
|
||||
...init,
|
||||
headers: {
|
||||
...init?.headers,
|
||||
connection: 'keep-alive',
|
||||
},
|
||||
}),
|
||||
headers: { connection: 'keep-alive' },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { EventSource } from 'eventsource';
|
||||
import * as EventSource from 'eventsource';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
describe('Sse (Fastify Application)', () => {
|
||||
@@ -25,14 +25,7 @@ describe('Sse (Fastify Application)', () => {
|
||||
const url = await app.getUrl();
|
||||
|
||||
eventSource = new EventSource(url + '/sse', {
|
||||
fetch: (input, init) =>
|
||||
fetch(input, {
|
||||
...init,
|
||||
headers: {
|
||||
...init?.headers,
|
||||
connection: 'keep-alive',
|
||||
},
|
||||
}),
|
||||
headers: { connection: 'keep-alive' },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,14 +64,7 @@ describe('Sse (Fastify Application)', () => {
|
||||
const url = await app.getUrl();
|
||||
|
||||
eventSource = new EventSource(url + '/sse', {
|
||||
fetch: (input, init) =>
|
||||
fetch(input, {
|
||||
...init,
|
||||
headers: {
|
||||
...init?.headers,
|
||||
connection: 'keep-alive',
|
||||
},
|
||||
}),
|
||||
headers: { connection: 'keep-alive' },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
const PROMPT = '> ';
|
||||
|
||||
describe('REPL process', function () {
|
||||
let replProcess: ReturnType<typeof spawn>;
|
||||
|
||||
function waitForReplToStart(
|
||||
process: ReturnType<typeof spawn>,
|
||||
message,
|
||||
timeout = 10000,
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
reject(new Error('REPL did not start in time'));
|
||||
}, timeout);
|
||||
|
||||
if (!process.stdout || !process.stderr) {
|
||||
return reject(new Error('REPL stdout or stderr is not available'));
|
||||
}
|
||||
process.stdout.on('data', data => {
|
||||
if (data.toString().includes(message)) {
|
||||
clearTimeout(timer);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
process.stderr.on('data', data => {
|
||||
if (data.toString().includes(message)) {
|
||||
clearTimeout(timer);
|
||||
reject(new Error(`REPL started with error: ${data}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(async function () {
|
||||
this.timeout(15000);
|
||||
replProcess = spawn('ts-node', ['../src/repl.ts'], { cwd: __dirname });
|
||||
await waitForReplToStart(replProcess, PROMPT);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (replProcess) {
|
||||
replProcess.kill(9);
|
||||
}
|
||||
});
|
||||
|
||||
it('exits on .exit', async function () {
|
||||
this.timeout(1000);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
replProcess.on('exit', _ => {
|
||||
expect(replProcess.exitCode).to.equal(0);
|
||||
resolve();
|
||||
});
|
||||
|
||||
replProcess.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
if (replProcess.stdin) {
|
||||
replProcess.stdin.write('.exit\n');
|
||||
} else {
|
||||
reject(new Error('REPL stdin is not available'));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class DatabaseConnection implements OnModuleDestroy {
|
||||
keepAlive = true;
|
||||
|
||||
static connect(): DatabaseConnection {
|
||||
const connection = new DatabaseConnection();
|
||||
connection.maintainConnection();
|
||||
return connection;
|
||||
}
|
||||
|
||||
onModuleDestroy() {
|
||||
this.keepAlive = false;
|
||||
}
|
||||
|
||||
maintainConnection() {
|
||||
setTimeout(() => {
|
||||
if (this.keepAlive) {
|
||||
this.maintainConnection();
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { DynamicModule, Module } from '@nestjs/common';
|
||||
import { DatabaseConnection } from './database.connection';
|
||||
|
||||
@Module({})
|
||||
export class DatabaseModule {
|
||||
static forRoot(): DynamicModule {
|
||||
const connectionProvider = {
|
||||
provide: DatabaseConnection,
|
||||
useFactory: () => {
|
||||
return DatabaseConnection.connect();
|
||||
},
|
||||
};
|
||||
return {
|
||||
global: true,
|
||||
module: DatabaseModule,
|
||||
providers: [connectionProvider],
|
||||
exports: [connectionProvider],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DatabaseModule } from './database/database.module';
|
||||
|
||||
@Module({
|
||||
imports: [DatabaseModule.forRoot()],
|
||||
})
|
||||
export class LongLivingAppModule {}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { repl } from '@nestjs/core';
|
||||
import { LongLivingAppModule } from './long-living-app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
await repl(LongLivingAppModule);
|
||||
}
|
||||
bootstrap().catch(err => {
|
||||
console.error('Error during bootstrap:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,8 +1,7 @@
|
||||
import { INestApplication, Injectable, Scope } from '@nestjs/common';
|
||||
import { INestApplication, Scope } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { NestedTransientModule } from '../src/nested-transient/nested-transient.module';
|
||||
import { Guard } from '../src/transient/guards/request-scoped.guard';
|
||||
import { HelloController } from '../src/transient/hello.controller';
|
||||
import { HelloModule } from '../src/transient/hello.module';
|
||||
@@ -18,220 +17,65 @@ class Meta {
|
||||
}
|
||||
|
||||
describe('Transient scope', () => {
|
||||
describe('when transient scope is used', () => {
|
||||
let server: any;
|
||||
let app: INestApplication;
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
HelloModule.forRoot({
|
||||
provide: 'META',
|
||||
useClass: Meta,
|
||||
scope: Scope.TRANSIENT,
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
HelloModule.forRoot({
|
||||
provide: 'META',
|
||||
useClass: Meta,
|
||||
scope: Scope.TRANSIENT,
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('and when one service is request scoped', () => {
|
||||
before(async () => {
|
||||
const performHttpCall = end =>
|
||||
request(server)
|
||||
.get('/hello')
|
||||
.end(err => {
|
||||
if (err) return end(err);
|
||||
end();
|
||||
});
|
||||
await new Promise<any>(resolve => performHttpCall(resolve));
|
||||
await new Promise<any>(resolve => performHttpCall(resolve));
|
||||
await new Promise<any>(resolve => performHttpCall(resolve));
|
||||
});
|
||||
|
||||
it(`should create controller for each request`, () => {
|
||||
expect(HelloController.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, () => {
|
||||
expect(UsersService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create provider for each inquirer`, () => {
|
||||
expect(Meta.COUNTER).to.be.eql(7);
|
||||
});
|
||||
|
||||
it(`should create transient pipe for each controller (3 requests, 1 static)`, () => {
|
||||
expect(UserByIdPipe.COUNTER).to.be.eql(4);
|
||||
});
|
||||
|
||||
it(`should create transient interceptor for each controller (3 requests, 1 static)`, () => {
|
||||
expect(Interceptor.COUNTER).to.be.eql(4);
|
||||
});
|
||||
|
||||
it(`should create transient guard for each controller (3 requests, 1 static)`, () => {
|
||||
expect(Guard.COUNTER).to.be.eql(4);
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('when there is a nested structure of transient providers', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
class DeepTransient {
|
||||
public initialized = false;
|
||||
|
||||
constructor() {
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
class LoggerService {
|
||||
public context?: string;
|
||||
}
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
class SecondService {
|
||||
constructor(public readonly loggerService: LoggerService) {
|
||||
this.loggerService.context = 'SecondService';
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class FirstService {
|
||||
constructor(
|
||||
public readonly secondService: SecondService,
|
||||
public readonly loggerService: LoggerService,
|
||||
public readonly deepTransient: DeepTransient,
|
||||
) {
|
||||
this.loggerService.context = 'FirstService';
|
||||
}
|
||||
}
|
||||
|
||||
describe('when one service is request scoped', () => {
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [FirstService, SecondService, LoggerService, DeepTransient],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should create a new instance of the transient provider for each provider', async () => {
|
||||
const firstService1 = app.get(FirstService);
|
||||
|
||||
expect(firstService1.secondService.loggerService.context).to.equal(
|
||||
'SecondService',
|
||||
);
|
||||
expect(firstService1.loggerService.context).to.equal('FirstService');
|
||||
expect(firstService1.deepTransient.initialized).to.be.true;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when DEFAULT scoped provider has deeply nested TRANSIENT chain', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
class DeepNestedTransient {
|
||||
public static constructorCalled = false;
|
||||
|
||||
constructor() {
|
||||
DeepNestedTransient.constructorCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
class MiddleTransient {
|
||||
constructor(public readonly nested: DeepNestedTransient) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class RootService {
|
||||
constructor(public readonly middle: MiddleTransient) {}
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
DeepNestedTransient.constructorCalled = false;
|
||||
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [RootService, MiddleTransient, DeepNestedTransient],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should call constructor of deeply nested TRANSIENT provider', () => {
|
||||
const rootService = app.get(RootService);
|
||||
|
||||
expect(DeepNestedTransient.constructorCalled).to.be.true;
|
||||
expect(rootService.middle.nested).to.be.instanceOf(DeepNestedTransient);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when nested transient providers are used in request scope', () => {
|
||||
let server: any;
|
||||
let app: INestApplication;
|
||||
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [NestedTransientModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
describe('when handling HTTP requests', () => {
|
||||
let response: any;
|
||||
|
||||
before(async () => {
|
||||
const performHttpCall = () =>
|
||||
new Promise<any>((resolve, reject) => {
|
||||
request(server)
|
||||
.get('/nested-transient')
|
||||
.end((err, res) => {
|
||||
if (err) return reject(err);
|
||||
resolve(res);
|
||||
});
|
||||
const performHttpCall = end =>
|
||||
request(server)
|
||||
.get('/hello')
|
||||
.end(err => {
|
||||
if (err) return end(err);
|
||||
end();
|
||||
});
|
||||
|
||||
response = await performHttpCall();
|
||||
});
|
||||
|
||||
it('should isolate nested transient instances for each parent service', () => {
|
||||
expect(response.body.firstServiceContext).to.equal(
|
||||
'NESTED-FirstService',
|
||||
);
|
||||
expect(response.body.secondServiceContext).to.equal(
|
||||
'NESTED-SecondService',
|
||||
);
|
||||
expect(response.body.firstServiceNestedId).to.not.equal(
|
||||
response.body.secondServiceNestedId,
|
||||
);
|
||||
});
|
||||
await new Promise<any>(resolve => performHttpCall(resolve));
|
||||
await new Promise<any>(resolve => performHttpCall(resolve));
|
||||
await new Promise<any>(resolve => performHttpCall(resolve));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
it(`should create controller for each request`, () => {
|
||||
expect(HelloController.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, () => {
|
||||
expect(UsersService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create provider for each inquirer`, () => {
|
||||
expect(Meta.COUNTER).to.be.eql(7);
|
||||
});
|
||||
|
||||
it(`should create transient pipe for each controller (3 requests, 1 static)`, () => {
|
||||
expect(UserByIdPipe.COUNTER).to.be.eql(4);
|
||||
});
|
||||
|
||||
it(`should create transient interceptor for each controller (3 requests, 1 static)`, () => {
|
||||
expect(Interceptor.COUNTER).to.be.eql(4);
|
||||
});
|
||||
|
||||
it(`should create transient guard for each controller (3 requests, 1 static)`, () => {
|
||||
expect(Guard.COUNTER).to.be.eql(4);
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { TransientLoggerService } from './transient-logger.service';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class FirstRequestService {
|
||||
static COUNTER = 0;
|
||||
|
||||
constructor(public readonly logger: TransientLoggerService) {
|
||||
FirstRequestService.COUNTER++;
|
||||
this.logger.setContext('FirstService');
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Controller, Get, Scope } from '@nestjs/common';
|
||||
import { FirstRequestService } from './first-request.service';
|
||||
import { SecondRequestService } from './second-request.service';
|
||||
|
||||
@Controller({ path: 'nested-transient', scope: Scope.REQUEST })
|
||||
export class NestedTransientController {
|
||||
static COUNTER = 0;
|
||||
|
||||
constructor(
|
||||
private readonly firstService: FirstRequestService,
|
||||
private readonly secondService: SecondRequestService,
|
||||
) {
|
||||
NestedTransientController.COUNTER++;
|
||||
}
|
||||
|
||||
@Get()
|
||||
getIsolationData() {
|
||||
return {
|
||||
firstServiceContext: this.firstService.logger.getNestedContext(),
|
||||
firstServiceNestedId: this.firstService.logger.getNestedInstanceId(),
|
||||
secondServiceContext: this.secondService.logger.getNestedContext(),
|
||||
secondServiceNestedId: this.secondService.logger.getNestedInstanceId(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { NestedTransientController } from './nested-transient.controller';
|
||||
import { FirstRequestService } from './first-request.service';
|
||||
import { SecondRequestService } from './second-request.service';
|
||||
import { TransientLoggerService } from './transient-logger.service';
|
||||
import { NestedTransientService } from './nested-transient.service';
|
||||
|
||||
@Module({
|
||||
controllers: [NestedTransientController],
|
||||
providers: [
|
||||
FirstRequestService,
|
||||
SecondRequestService,
|
||||
TransientLoggerService,
|
||||
NestedTransientService,
|
||||
],
|
||||
})
|
||||
export class NestedTransientModule {}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class NestedTransientService {
|
||||
static COUNTER = 0;
|
||||
public readonly instanceId: number;
|
||||
private context?: string;
|
||||
|
||||
constructor() {
|
||||
NestedTransientService.COUNTER++;
|
||||
this.instanceId = NestedTransientService.COUNTER;
|
||||
}
|
||||
|
||||
setContext(ctx: string) {
|
||||
this.context = ctx;
|
||||
}
|
||||
|
||||
getContext(): string | undefined {
|
||||
return this.context;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { TransientLoggerService } from './transient-logger.service';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class SecondRequestService {
|
||||
static COUNTER = 0;
|
||||
|
||||
constructor(public readonly logger: TransientLoggerService) {
|
||||
SecondRequestService.COUNTER++;
|
||||
this.logger.setContext('SecondService');
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { NestedTransientService } from './nested-transient.service';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class TransientLoggerService {
|
||||
static COUNTER = 0;
|
||||
public readonly instanceId: number;
|
||||
|
||||
constructor(public readonly nested: NestedTransientService) {
|
||||
TransientLoggerService.COUNTER++;
|
||||
this.instanceId = TransientLoggerService.COUNTER;
|
||||
}
|
||||
|
||||
setContext(ctx: string) {
|
||||
this.nested.setContext(`NESTED-${ctx}`);
|
||||
}
|
||||
|
||||
getNestedContext(): string | undefined {
|
||||
return this.nested.getContext();
|
||||
}
|
||||
|
||||
getNestedInstanceId(): number {
|
||||
return this.nested.instanceId;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { PhotoModule } from './photo/photo.module';
|
||||
database: 'test',
|
||||
entities: [Photo],
|
||||
synchronize: true,
|
||||
keepConnectionAlive: true,
|
||||
retryAttempts: 2,
|
||||
retryDelay: 1000,
|
||||
}),
|
||||
|
||||
@@ -18,6 +18,7 @@ class ConfigService implements TypeOrmOptionsFactory {
|
||||
database: 'test',
|
||||
entities: [Photo],
|
||||
synchronize: true,
|
||||
keepConnectionAlive: true,
|
||||
retryAttempts: 2,
|
||||
retryDelay: 1000,
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ class ConfigService implements TypeOrmOptionsFactory {
|
||||
database: 'test',
|
||||
entities: [Photo],
|
||||
synchronize: true,
|
||||
keepConnectionAlive: true,
|
||||
retryAttempts: 2,
|
||||
retryDelay: 1000,
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ import { PhotoModule } from './photo/photo.module';
|
||||
database: 'test',
|
||||
entities: [Photo],
|
||||
synchronize: true,
|
||||
keepConnectionAlive: true,
|
||||
retryAttempts: 2,
|
||||
retryDelay: 1000,
|
||||
}),
|
||||
|
||||
@@ -18,6 +18,7 @@ export class DatabaseModule {
|
||||
database: 'test',
|
||||
entities: [Photo],
|
||||
synchronize: true,
|
||||
keepConnectionAlive: true,
|
||||
retryAttempts: 2,
|
||||
retryDelay: 1000,
|
||||
}),
|
||||
|
||||
@@ -41,38 +41,5 @@ describe('WebSocketGateway (ack)', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle manual ack for async operations when @Ack() is used (success case)', async () => {
|
||||
app = await createNestApp(AckGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = io('http://localhost:8080');
|
||||
const payload = { shouldSucceed: true };
|
||||
|
||||
await new Promise<void>(resolve =>
|
||||
ws.emit('manual-ack', payload, response => {
|
||||
expect(response).to.eql({ status: 'success', data: payload });
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle manual ack for async operations when @Ack() is used (error case)', async () => {
|
||||
app = await createNestApp(AckGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = io('http://localhost:8080');
|
||||
const payload = { shouldSucceed: false };
|
||||
|
||||
await new Promise<void>(resolve =>
|
||||
ws.emit('manual-ack', payload, response => {
|
||||
expect(response).to.eql({
|
||||
status: 'error',
|
||||
message: 'Operation failed',
|
||||
});
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => app.close());
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { EventSource } from 'eventsource';
|
||||
import * as EventSource from 'eventsource';
|
||||
import { io } from 'socket.io-client';
|
||||
import { AppController as LongConnectionController } from '../../nest-application/sse/src/app.controller';
|
||||
import { ApplicationGateway } from '../src/app.gateway';
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
Ack,
|
||||
MessageBody,
|
||||
SubscribeMessage,
|
||||
WebSocketGateway,
|
||||
} from '@nestjs/websockets';
|
||||
import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets';
|
||||
|
||||
@WebSocketGateway(8080)
|
||||
export class AckGateway {
|
||||
@@ -11,19 +6,4 @@ export class AckGateway {
|
||||
onPush() {
|
||||
return 'pong';
|
||||
}
|
||||
|
||||
@SubscribeMessage('manual-ack')
|
||||
async handleManualAck(
|
||||
@MessageBody() data: any,
|
||||
@Ack() ack: (response: any) => void,
|
||||
) {
|
||||
await new Promise(resolve => setTimeout(resolve, 20));
|
||||
|
||||
if (data.shouldSucceed) {
|
||||
ack({ status: 'success', data });
|
||||
} else {
|
||||
ack({ status: 'error', message: 'Operation failed' });
|
||||
}
|
||||
return { status: 'ignored' };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"packages": ["packages/*"],
|
||||
"version": "11.1.14",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
"lerna": "2.4.0",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "11.0.0-next.1"
|
||||
}
|
||||
|
||||
34890
package-lock.json
generated
34890
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
185
package.json
185
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "11.1.10",
|
||||
"version": "10.4.5",
|
||||
"description": "Modern, fast, powerful node.js web framework",
|
||||
"homepage": "https://nestjs.com",
|
||||
"repository": {
|
||||
@@ -42,12 +42,12 @@
|
||||
"lint:spec": "node --max-old-space-size=4096 ./node_modules/.bin/eslint 'packages/**/**.spec.ts'",
|
||||
"lint:ci": "concurrently 'npm run lint:packages' 'npm run lint:spec'",
|
||||
"prerelease": "gulp copy-misc",
|
||||
"publish": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --force-publish --exact -m \"chore(release): publish %s release\"",
|
||||
"publish": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --force-publish --access public --exact -m \"chore(@nestjs) publish %s release\"",
|
||||
"prepublishOnly": "npm run changelog | pbcopy",
|
||||
"publish:beta": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=beta -m \"chore(release): publish %s release\"",
|
||||
"publish:next": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=next --skip-git -m \"chore(release): publish %s release\"",
|
||||
"publish:rc": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=rc -m \"chore(release): publish %s release\"",
|
||||
"publish:test": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --force-publish --npm-tag=test --skip-git -m \"chore(release): publish %s release\"",
|
||||
"publish:beta": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=beta --access public -m \"chore(@nestjs) publish %s release\"",
|
||||
"publish:next": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=next --access public --skip-git -m \"chore(@nestjs) publish %s release\"",
|
||||
"publish:rc": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=rc --access public -m \"chore(@nestjs) publish %s release\"",
|
||||
"publish:test": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --force-publish --access public --npm-tag=test --skip-git -m \"chore(@nestjs) publish %s release\"",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -59,117 +59,124 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/opencollective": "0.4.1",
|
||||
"ansis": "4.2.0",
|
||||
"@nuxtjs/opencollective": "0.3.2",
|
||||
"ansis": "3.3.2",
|
||||
"class-transformer": "0.5.1",
|
||||
"class-validator": "0.14.3",
|
||||
"cors": "2.8.6",
|
||||
"express": "5.2.1",
|
||||
"fast-json-stringify": "6.3.0",
|
||||
"class-validator": "0.14.1",
|
||||
"cors": "2.8.5",
|
||||
"express": "5.0.1",
|
||||
"fast-json-stringify": "6.0.0",
|
||||
"fast-safe-stringify": "2.1.1",
|
||||
"file-type": "21.3.0",
|
||||
"iterare": "1.2.1",
|
||||
"load-esm": "1.0.3",
|
||||
"object-hash": "3.0.0",
|
||||
"path-to-regexp": "8.3.0",
|
||||
"path-to-regexp": "8.2.0",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"rxjs": "7.8.2",
|
||||
"socket.io": "4.8.3",
|
||||
"rxjs": "7.8.1",
|
||||
"socket.io": "4.8.1",
|
||||
"tslib": "2.8.1",
|
||||
"uid": "2.0.2",
|
||||
"uuid": "13.0.0"
|
||||
"uuid": "11.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/server": "5.4.0",
|
||||
"@as-integrations/express5": "1.1.2",
|
||||
"@commitlint/cli": "20.4.1",
|
||||
"@commitlint/config-angular": "20.4.1",
|
||||
"@eslint/eslintrc": "3.3.3",
|
||||
"@eslint/js": "9.39.2",
|
||||
"@fastify/cors": "11.2.0",
|
||||
"@fastify/formbody": "8.0.2",
|
||||
"@fastify/middie": "9.1.0",
|
||||
"@fastify/multipart": "9.4.0",
|
||||
"@fastify/static": "9.0.0",
|
||||
"@fastify/view": "11.1.1",
|
||||
"@grpc/grpc-js": "1.14.3",
|
||||
"@grpc/proto-loader": "0.8.0",
|
||||
"@nestjs/apollo": "13.2.4",
|
||||
"@nestjs/graphql": "13.2.4",
|
||||
"@nestjs/mongoose": "11.0.4",
|
||||
"@nestjs/typeorm": "11.0.0",
|
||||
"@types/amqplib": "0.10.8",
|
||||
"@types/chai": "4.3.20",
|
||||
"@apollo/server": "4.11.2",
|
||||
"@codechecks/client": "0.1.12",
|
||||
"@commitlint/cli": "19.5.0",
|
||||
"@commitlint/config-angular": "19.5.0",
|
||||
"@eslint/eslintrc": "3.2.0",
|
||||
"@eslint/js": "9.15.0",
|
||||
"@fastify/cors": "10.0.1",
|
||||
"@fastify/formbody": "8.0.1",
|
||||
"@fastify/middie": "9.0.2",
|
||||
"@fastify/multipart": "9.0.1",
|
||||
"@fastify/static": "7.0.4",
|
||||
"@fastify/view": "9.1.0",
|
||||
"@grpc/grpc-js": "1.12.2",
|
||||
"@grpc/proto-loader": "0.7.13",
|
||||
"@nestjs/apollo": "12.2.1",
|
||||
"@nestjs/graphql": "12.2.1",
|
||||
"@nestjs/mongoose": "10.1.0",
|
||||
"@nestjs/typeorm": "10.0.2",
|
||||
"@types/amqplib": "0.10.5",
|
||||
"@types/bytes": "3.1.4",
|
||||
"@types/chai": "4.3.16",
|
||||
"@types/chai-as-promised": "7.1.8",
|
||||
"@types/cors": "2.8.19",
|
||||
"@types/cors": "2.8.17",
|
||||
"@types/eslint__js": "8.42.3",
|
||||
"@types/express": "5.0.6",
|
||||
"@types/gulp": "4.0.18",
|
||||
"@types/http-errors": "2.0.5",
|
||||
"@types/express": "5.0.0",
|
||||
"@types/gulp": "4.0.17",
|
||||
"@types/http-errors": "2.0.4",
|
||||
"@types/mocha": "10.0.10",
|
||||
"@types/node": "25.2.3",
|
||||
"@types/sinon": "21.0.0",
|
||||
"@types/supertest": "6.0.3",
|
||||
"@types/ws": "8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.56.0",
|
||||
"@typescript-eslint/parser": "8.56.0",
|
||||
"amqp-connection-manager": "5.0.0",
|
||||
"amqplib": "0.10.9",
|
||||
"body-parser": "2.2.2",
|
||||
"cache-manager": "7.2.8",
|
||||
"@types/node": "22.9.0",
|
||||
"@types/sinon": "17.0.3",
|
||||
"@types/supertest": "6.0.2",
|
||||
"@types/ws": "8.5.13",
|
||||
"amqp-connection-manager": "4.1.14",
|
||||
"amqplib": "0.10.5",
|
||||
"artillery": "2.0.21",
|
||||
"body-parser": "1.20.3",
|
||||
"bytes": "3.1.2",
|
||||
"cache-manager": "5.7.6",
|
||||
"cache-manager-redis-store": "3.0.1",
|
||||
"chai": "4.5.0",
|
||||
"chai-as-promised": "7.1.2",
|
||||
"concurrently": "9.2.1",
|
||||
"conventional-changelog": "7.1.1",
|
||||
"clang-format": "1.8.0",
|
||||
"concurrently": "9.1.0",
|
||||
"conventional-changelog": "6.0.0",
|
||||
"core-js": "3.39.0",
|
||||
"coveralls": "3.1.1",
|
||||
"delete-empty": "3.0.0",
|
||||
"eslint": "9.39.2",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-plugin-prettier": "5.5.5",
|
||||
"eventsource": "4.1.0",
|
||||
"engine.io-client": "6.6.2",
|
||||
"eslint": "8.57.1",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-prettier": "5.2.1",
|
||||
"eventsource": "2.0.2",
|
||||
"fancy-log": "2.0.0",
|
||||
"fastify": "5.7.4",
|
||||
"fastify-plugin": "5.1.0",
|
||||
"find-my-way": "9.4.0",
|
||||
"globals": "17.3.0",
|
||||
"graphql": "16.12.0",
|
||||
"graphql-subscriptions": "3.0.0",
|
||||
"gulp": "5.0.1",
|
||||
"fastify": "5.1.0",
|
||||
"globals": "15.12.0",
|
||||
"graphql": "16.9.0",
|
||||
"graphql-tools": "9.0.3",
|
||||
"graphql-subscriptions": "2.0.0",
|
||||
"gulp": "5.0.0",
|
||||
"gulp-clang-format": "1.0.27",
|
||||
"gulp-clean": "0.4.0",
|
||||
"gulp-sourcemaps": "3.0.0",
|
||||
"gulp-typescript": "5.0.1",
|
||||
"gulp-watch": "5.0.1",
|
||||
"http-errors": "2.0.1",
|
||||
"http-errors": "2.0.0",
|
||||
"husky": "9.1.7",
|
||||
"ioredis": "5.9.3",
|
||||
"imports-loader": "5.0.0",
|
||||
"ioredis": "5.4.1",
|
||||
"json-loader": "0.5.7",
|
||||
"kafkajs": "2.2.4",
|
||||
"lerna": "9.0.4",
|
||||
"lerna": "2.11.0",
|
||||
"lerna-changelog": "2.2.0",
|
||||
"light-my-request": "6.6.0",
|
||||
"lint-staged": "16.2.7",
|
||||
"light-my-request": "6.3.0",
|
||||
"lint-staged": "15.2.10",
|
||||
"markdown-table": "2.0.0",
|
||||
"mocha": "11.7.5",
|
||||
"mongoose": "9.2.1",
|
||||
"mqtt": "5.15.0",
|
||||
"multer": "2.0.2",
|
||||
"mysql2": "3.17.2",
|
||||
"nats": "2.29.3",
|
||||
"nyc": "14.1.1",
|
||||
"prettier": "^3.7.4",
|
||||
"redis": "5.11.0",
|
||||
"reusify": "1.1.0",
|
||||
"mocha": "10.8.2",
|
||||
"mongoose": "8.8.3",
|
||||
"mqtt": "5.10.2",
|
||||
"multer": "1.4.4",
|
||||
"mysql2": "3.11.4",
|
||||
"nats": "2.28.2",
|
||||
"nodemon": "3.1.7",
|
||||
"nyc": "17.1.0",
|
||||
"prettier": "3.3.3",
|
||||
"redis": "4.7.0",
|
||||
"rxjs-compat": "6.6.7",
|
||||
"sinon": "21.0.1",
|
||||
"sinon-chai": "3.7.0",
|
||||
"socket.io-client": "4.8.3",
|
||||
"supertest": "7.2.2",
|
||||
"ts-morph": "27.0.2",
|
||||
"sinon": "19.0.2",
|
||||
"sinon-chai": "4.0.0",
|
||||
"socket.io-client": "4.8.1",
|
||||
"subscriptions-transport-ws": "0.11.0",
|
||||
"supertest": "7.0.0",
|
||||
"ts-morph": "24.0.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typeorm": "0.3.28",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.56.0",
|
||||
"ws": "8.19.0"
|
||||
"typeorm": "0.3.20",
|
||||
"typescript": "5.6.3",
|
||||
"typescript-eslint": "8.15.0",
|
||||
"wrk": "1.2.1",
|
||||
"ws": "8.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<a href="https://nestjs.com/" target="_blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
||||
<a href="https://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
@@ -8,7 +8,7 @@
|
||||
<p align="center">A progressive <a href="https://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://github.com/nestjs/nest/blob/master/LICENSE" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
@@ -49,7 +49,7 @@ Please make sure to read the [Issue Reporting Checklist](https://github.com/nest
|
||||
|
||||
## Consulting
|
||||
|
||||
With official support, you can get expert help straight from the Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
|
||||
## Support
|
||||
|
||||
@@ -62,8 +62,9 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<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://serpapi.com/" target="_blank"><img src="https://nestjs.com/img/logos/serpapi-logo.png" width="150" 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>
|
||||
|
||||
@@ -79,7 +80,10 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
</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>
|
||||
|
||||
@@ -89,12 +93,10 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<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>
|
||||
<td align="center" valign="middle"><a href="https://crawljobs.com" target="_blank"><img src="https://nestjs.com/img/logos/crawljobs-logo.svg" width="130" valign="middle" /></a></td>
|
||||
</tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://pandektes.com" target="_blank"><img src="https://nestjs.com/img/logos/pandektes-logo.png" width="65" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -118,16 +120,19 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<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>
|
||||
<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>
|
||||
</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>
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ export function flatten<T extends Array<unknown> = any>(
|
||||
arr: T,
|
||||
): T extends Array<infer R> ? R : never {
|
||||
const flat = ([] as any[]).concat(...arr);
|
||||
return flat.some(Array.isArray)
|
||||
? flatten(flat)
|
||||
: (flat as T extends Array<infer R> ? R : never);
|
||||
return flat.some(Array.isArray) ? flatten(flat) : flat;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -525,32 +525,6 @@ export function Body(
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Route handler parameter decorator. Extracts the `rawBody` Buffer
|
||||
* property from the `req` object and populates the decorated parameter with that value.
|
||||
|
||||
@@ -6,12 +6,7 @@ import { RequestMethod } from '../../enums/request-method.enum';
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function Sse(
|
||||
path?: string,
|
||||
options: { [METHOD_METADATA]?: RequestMethod } = {
|
||||
[METHOD_METADATA]: RequestMethod.GET,
|
||||
},
|
||||
): MethodDecorator {
|
||||
export function Sse(path?: string): MethodDecorator {
|
||||
return (
|
||||
target: object,
|
||||
key: string | symbol,
|
||||
@@ -22,7 +17,7 @@ export function Sse(
|
||||
Reflect.defineMetadata(PATH_METADATA, path, descriptor.value);
|
||||
Reflect.defineMetadata(
|
||||
METHOD_METADATA,
|
||||
options[METHOD_METADATA],
|
||||
RequestMethod.GET,
|
||||
descriptor.value,
|
||||
);
|
||||
Reflect.defineMetadata(SSE_METADATA, true, descriptor.value);
|
||||
|
||||
@@ -12,5 +12,4 @@ export enum RouteParamtypes {
|
||||
HOST = 10,
|
||||
IP = 11,
|
||||
RAW_BODY = 12,
|
||||
ACK = 13,
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface StreamableFileOptions {
|
||||
/**
|
||||
* The value that will be used for the `Content-Disposition` response header.
|
||||
*/
|
||||
disposition?: string | string[];
|
||||
disposition?: string;
|
||||
/**
|
||||
* The value that will be used for the `Content-Length` response header.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Nest @common
|
||||
* Copyright(c) 2017 - 2025 Kamil Mysliwiec
|
||||
* Copyright(c) 2017 - 2023 Kamil Mysliwiec
|
||||
* https://nestjs.com
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
@@ -10,7 +10,7 @@ type StaticOrigin = boolean | string | RegExp | (string | RegExp)[];
|
||||
* @publicApi
|
||||
*/
|
||||
export type CustomOrigin = (
|
||||
requestOrigin: string | undefined,
|
||||
requestOrigin: string,
|
||||
callback: (err: Error | null, origin?: StaticOrigin) => void,
|
||||
) => void;
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { RequestMethod } from '../../enums';
|
||||
import {
|
||||
CorsOptions,
|
||||
CorsOptionsDelegate,
|
||||
} from '../../interfaces/external/cors-options.interface';
|
||||
import { NestApplicationOptions } from '../../interfaces/nest-application-options.interface';
|
||||
import { VersionValue, VersioningOptions } from '../version-options.interface';
|
||||
import { VersioningOptions, VersionValue } from '../version-options.interface';
|
||||
|
||||
export type ErrorHandler<TRequest = any, TResponse = any> = (
|
||||
error: any,
|
||||
@@ -87,7 +91,7 @@ export interface HttpServer<
|
||||
getRequestUrl?(request: TRequest): string;
|
||||
getInstance(): ServerInstance;
|
||||
registerParserMiddleware(...args: any[]): any;
|
||||
enableCors(options: any): any;
|
||||
enableCors(options: CorsOptions | CorsOptionsDelegate<TRequest>): any;
|
||||
getHttpServer(): any;
|
||||
initHttpServer(options: NestApplicationOptions): void;
|
||||
close(): any;
|
||||
|
||||
@@ -24,7 +24,6 @@ export * from './nest-application-options.interface';
|
||||
export * from './nest-application.interface';
|
||||
export * from './nest-microservice.interface';
|
||||
export * from './scope-options.interface';
|
||||
export * from './shutdown-hooks-options.interface';
|
||||
export * from './type.interface';
|
||||
export * from './version-options.interface';
|
||||
export * from './websockets/web-socket-adapter.interface';
|
||||
|
||||
@@ -3,5 +3,4 @@
|
||||
*/
|
||||
export interface NestHybridApplicationOptions {
|
||||
inheritAppConfig?: boolean;
|
||||
deferInitialization?: boolean;
|
||||
}
|
||||
|
||||
@@ -53,25 +53,4 @@ export class NestApplicationContextOptions {
|
||||
* @default 'reference'
|
||||
*/
|
||||
moduleIdGeneratorAlgorithm?: 'deep-hash' | 'reference';
|
||||
|
||||
/**
|
||||
* Instrument the application context.
|
||||
* This option allows you to add custom instrumentation to the application context.
|
||||
*/
|
||||
instrument?: {
|
||||
/**
|
||||
* Function that decorates each instance created by the application context.
|
||||
* This function can be used to add custom properties or methods to the instance.
|
||||
* @param instance The instance to decorate.
|
||||
* @returns The decorated instance.
|
||||
*/
|
||||
instanceDecorator: (instance: unknown) => unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* If enabled, will force the use of console.log/console.error instead of process.stdout/stderr.write
|
||||
* in the default ConsoleLogger. This is useful for test environments like Jest that can buffer console calls.
|
||||
* @default false
|
||||
*/
|
||||
forceConsole?: boolean;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ShutdownSignal } from '../enums/shutdown-signal.enum';
|
||||
import { LoggerService, LogLevel } from '../services/logger.service';
|
||||
import { DynamicModule } from './modules';
|
||||
import { NestApplicationContextOptions } from './nest-application-context-options.interface';
|
||||
import { ShutdownHooksOptions } from './shutdown-hooks-options.interface';
|
||||
import { Type } from './type.interface';
|
||||
|
||||
export type SelectOptions = Pick<NestApplicationContextOptions, 'abortOnError'>;
|
||||
@@ -144,15 +143,9 @@ export interface INestApplicationContext {
|
||||
* `onApplicationShutdown` function of a provider if the
|
||||
* process receives a shutdown signal.
|
||||
*
|
||||
* @param {ShutdownSignal[] | string[]} [signals] The system signals to listen to
|
||||
* @param {ShutdownHooksOptions} [options] Options for configuring shutdown hooks behavior
|
||||
*
|
||||
* @returns {this} The Nest application context instance
|
||||
*/
|
||||
enableShutdownHooks(
|
||||
signals?: ShutdownSignal[] | string[],
|
||||
options?: ShutdownHooksOptions,
|
||||
): this;
|
||||
enableShutdownHooks(signals?: ShutdownSignal[] | string[]): this;
|
||||
|
||||
/**
|
||||
* Initializes the Nest application.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
CorsOptions,
|
||||
CorsOptionsDelegate,
|
||||
} from './external/cors-options.interface';
|
||||
import { CanActivate } from './features/can-activate.interface';
|
||||
import { NestInterceptor } from './features/nest-interceptor.interface';
|
||||
import { GlobalPrefixOptions } from './global-prefix-options.interface';
|
||||
@@ -17,9 +21,8 @@ import { WebSocketAdapter } from './websockets/web-socket-adapter.interface';
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface INestApplication<
|
||||
TServer = any,
|
||||
> extends INestApplicationContext {
|
||||
export interface INestApplication<TServer = any>
|
||||
extends INestApplicationContext {
|
||||
/**
|
||||
* A wrapper function around HTTP adapter method: `adapter.use()`.
|
||||
* Example `app.use(cors())`
|
||||
@@ -33,7 +36,7 @@ export interface INestApplication<
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
enableCors(options?: any): void;
|
||||
enableCors(options?: CorsOptions | CorsOptionsDelegate<any>): void;
|
||||
|
||||
/**
|
||||
* Enables Versioning for the application.
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Options for configuring shutdown hooks behavior.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ShutdownHooksOptions {
|
||||
/**
|
||||
* If true, uses `process.exit()` instead of `process.kill(process.pid, signal)`
|
||||
* after shutdown hooks complete. This ensures the 'exit' event is properly
|
||||
* triggered, which is required for async loggers (like Pino with transports)
|
||||
* to flush their buffers before the process terminates.
|
||||
*
|
||||
* Note: Using `process.exit()` will:
|
||||
* - Change the exit code (e.g., SIGTERM: 143 → 0)
|
||||
* - May not trigger other signal handlers from third-party libraries
|
||||
* - May affect orchestrator (Kubernetes, Docker) behavior
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
useProcessExit?: boolean;
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import { Observable } from 'rxjs';
|
||||
export interface WsMessageHandler<T = string> {
|
||||
message: T;
|
||||
callback: (...args: any[]) => Observable<any> | Promise<any>;
|
||||
isAckHandledManually: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Logger } from '../services/logger.service';
|
||||
import { randomStringGenerator } from '../utils/random-string-generator.util';
|
||||
import {
|
||||
ASYNC_METHOD_SUFFIX,
|
||||
ASYNC_OPTIONS_METADATA_KEYS,
|
||||
CONFIGURABLE_MODULE_ID,
|
||||
DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
DEFAULT_METHOD_KEY,
|
||||
@@ -53,8 +52,8 @@ export interface ConfigurableModuleBuilderOptions {
|
||||
export class ConfigurableModuleBuilder<
|
||||
ModuleOptions,
|
||||
StaticMethodKey extends string = typeof DEFAULT_METHOD_KEY,
|
||||
FactoryClassMethodKey extends string =
|
||||
typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
FactoryClassMethodKey extends
|
||||
string = typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
ExtraModuleDefinitionOptions = {},
|
||||
> {
|
||||
protected staticMethodKey: StaticMethodKey;
|
||||
@@ -255,7 +254,7 @@ export class ConfigurableModuleBuilder<
|
||||
},
|
||||
{
|
||||
...self.extras,
|
||||
...this.extractExtrasFromAsyncOptions(options, self.extras),
|
||||
...options,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -278,28 +277,8 @@ export class ConfigurableModuleBuilder<
|
||||
return moduleOptions as ModuleOptions;
|
||||
}
|
||||
|
||||
private static extractExtrasFromAsyncOptions(
|
||||
input: ConfigurableModuleAsyncOptions<ModuleOptions> &
|
||||
ExtraModuleDefinitionOptions,
|
||||
extras: ExtraModuleDefinitionOptions | undefined,
|
||||
): Partial<ExtraModuleDefinitionOptions> {
|
||||
if (!extras) {
|
||||
return {};
|
||||
}
|
||||
const extrasOptions = {};
|
||||
|
||||
Object.keys(input as object)
|
||||
.filter(key => !ASYNC_OPTIONS_METADATA_KEYS.includes(key as any))
|
||||
.forEach(key => {
|
||||
extrasOptions[key] = input[key];
|
||||
});
|
||||
|
||||
return extrasOptions;
|
||||
}
|
||||
|
||||
private static createAsyncProviders(
|
||||
options: ConfigurableModuleAsyncOptions<ModuleOptions> &
|
||||
ExtraModuleDefinitionOptions,
|
||||
options: ConfigurableModuleAsyncOptions<ModuleOptions>,
|
||||
): Provider[] {
|
||||
if (options.useExisting || options.useFactory) {
|
||||
if (options.inject && options.provideInjectionTokensFrom) {
|
||||
|
||||
@@ -3,16 +3,3 @@ export const DEFAULT_FACTORY_CLASS_METHOD_KEY = 'create';
|
||||
|
||||
export const ASYNC_METHOD_SUFFIX = 'Async';
|
||||
export const CONFIGURABLE_MODULE_ID = 'CONFIGURABLE_MODULE_ID';
|
||||
|
||||
/**
|
||||
* List of keys that are specific to ConfigurableModuleAsyncOptions
|
||||
* and should be excluded when extracting user-defined extras.
|
||||
*/
|
||||
export const ASYNC_OPTIONS_METADATA_KEYS = [
|
||||
'useFactory',
|
||||
'useClass',
|
||||
'useExisting',
|
||||
'inject',
|
||||
'imports',
|
||||
'provideInjectionTokensFrom',
|
||||
] as const;
|
||||
|
||||
@@ -28,8 +28,8 @@ export type ConfigurableModuleOptionsFactory<
|
||||
*/
|
||||
export interface ConfigurableModuleAsyncOptions<
|
||||
ModuleOptions,
|
||||
FactoryClassMethodKey extends string =
|
||||
typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
FactoryClassMethodKey extends
|
||||
string = typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
> extends Pick<ModuleMetadata, 'imports'> {
|
||||
/**
|
||||
* Injection token resolving to an existing provider. The provider must implement
|
||||
@@ -47,7 +47,7 @@ export interface ConfigurableModuleAsyncOptions<
|
||||
>;
|
||||
/**
|
||||
* Function returning options (or a Promise resolving to options) to configure the
|
||||
* module.
|
||||
* cache module.
|
||||
*/
|
||||
useFactory?: (...args: any[]) => Promise<ModuleOptions> | ModuleOptions;
|
||||
/**
|
||||
|
||||
@@ -16,8 +16,8 @@ import { ConfigurableModuleAsyncOptions } from './configurable-module-async-opti
|
||||
export type ConfigurableModuleCls<
|
||||
ModuleOptions,
|
||||
MethodKey extends string = typeof DEFAULT_METHOD_KEY,
|
||||
FactoryClassMethodKey extends string =
|
||||
typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
FactoryClassMethodKey extends
|
||||
string = typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
ExtraModuleDefinitionOptions = {},
|
||||
> = {
|
||||
new (): any;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/common",
|
||||
"version": "11.1.14",
|
||||
"version": "11.0.0-next.1",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"homepage": "https://nestjs.com",
|
||||
@@ -18,15 +18,13 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"file-type": "21.3.0",
|
||||
"iterare": "1.2.1",
|
||||
"load-esm": "1.0.3",
|
||||
"tslib": "2.8.1",
|
||||
"tslib": "2.7.0",
|
||||
"uid": "2.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"class-transformer": ">=0.4.1",
|
||||
"class-validator": ">=0.13.2",
|
||||
"class-transformer": "*",
|
||||
"class-validator": "*",
|
||||
"reflect-metadata": "^0.1.12 || ^0.2.0",
|
||||
"rxjs": "^7.1.0"
|
||||
},
|
||||
@@ -37,6 +35,5 @@
|
||||
"class-transformer": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"gitHead": "bcb4747f7598a9d2655c8184a6d729ebefa07fbd"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,9 @@ import { isNil, isNumber } from '../utils/shared.utils';
|
||||
* @publicApi
|
||||
*/
|
||||
@Injectable()
|
||||
export class DefaultValuePipe<T = any, R = any> implements PipeTransform<
|
||||
T,
|
||||
T | R
|
||||
> {
|
||||
export class DefaultValuePipe<T = any, R = any>
|
||||
implements PipeTransform<T, T | R>
|
||||
{
|
||||
constructor(protected readonly defaultValue: R) {}
|
||||
|
||||
transform(value?: T, _metadata?: ArgumentMetadata): T | R {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user