nodejs-quickstart-structure 2.1.0 β†’ 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +40 -46
  3. package/lib/modules/config-files.js +6 -0
  4. package/package.json +3 -2
  5. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +11 -1
  6. package/templates/clean-architecture/js/src/infrastructure/webserver/server.spec.js.ejs +2 -0
  7. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +1 -1
  8. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +19 -3
  9. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +9 -1
  10. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +9 -1
  11. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +9 -1
  12. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +9 -1
  13. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +16 -1
  14. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +17 -3
  15. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +21 -3
  16. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +9 -1
  17. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +9 -1
  18. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +9 -1
  19. package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +9 -1
  20. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +9 -1
  21. package/templates/common/auth/js/controllers/authController.js.ejs +11 -9
  22. package/templates/common/auth/ts/controllers/authController.ts.ejs +7 -5
  23. package/templates/common/babel.config.js.ejs +5 -0
  24. package/templates/common/caching/js/memoryCache.spec.js.ejs +2 -0
  25. package/templates/common/caching/js/redisClient.spec.js.ejs +2 -0
  26. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +2 -0
  27. package/templates/common/caching/ts/redisClient.spec.ts.ejs +2 -0
  28. package/templates/common/database/js/mongoose.spec.js.ejs +2 -0
  29. package/templates/common/database/ts/mongoose.spec.ts.ejs +2 -0
  30. package/templates/common/jest.config.js.ejs +2 -0
  31. package/templates/common/package.json.ejs +5 -3
  32. package/templates/common/tsconfig.json +1 -0
package/CHANGELOG.md CHANGED
@@ -1,7 +1,23 @@
1
1
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
2
2
 
3
+ ## [2.1.2] - 2026-04-27
4
+
5
+ ### Changed
6
+ - **Security Hardening**: Updated `postcss` to `^8.5.10` in project overrides to resolve potential security vulnerabilities and improve build stability.
7
+
8
+ ## [2.1.1] - 2026-04-24
9
+
10
+ ### Fixed
11
+ - **Template Security Hardening**: Resolved missing buffer bounds check vulnerability in `uuid` (GHSA-w5hq-g745-h8pq) by adding `"uuid": "^14.0.0"` to template `overrides`.
12
+ - **Template Formatting**: Fixed an issue in `package.json.ejs` causing unexpected empty lines between generated dependencies.
13
+ - **Unit Test Stability**: Resolved persistent `SyntaxError` and `TypeError` in Jest unit tests across all architecture permutations by implementing ESM-compatible transformation patterns for `uuid` and standardizing on `jest.clearAllMocks()` with factory mocks.
14
+
15
+ ### Changed
16
+ - **Documentation Refinement**: Remade `README.md` and removed emojis for a cleaner, more professional, text-only presentation across the VitePress-based site.
17
+
3
18
  ## [2.1.0] - 2026-04-20
4
19
 
20
+
5
21
  ### Added
6
22
  - **Pluggable JWT Authentication**: Introduced a robust, production-ready authentication system.
7
23
  - Supports **Access & Refresh Token Rotation** for enhanced security.
package/README.md CHANGED
@@ -6,41 +6,35 @@
6
6
  [![npm monthly downloads](https://img.shields.io/npm/dm/nodejs-quickstart-structure.svg?style=flat-square)](https://www.npmjs.com/package/nodejs-quickstart-structure)
7
7
  [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg?style=flat-square)](https://opensource.org/licenses/ISC)
8
8
 
9
- ### πŸ“ˆ Real-world Adoption
10
- | **Metric** | **Insight** |
11
- | :--- | :--- |
12
- | πŸ”₯ **4,000+** | Downloads on npm |
13
- | πŸš€ **1,500+** | Recent GitHub Clones |
14
- | 🌍 **Trusted by** | Devs from **Google**, **Viblo**, and global tech teams |
15
9
 
16
10
  ---
17
11
 
18
12
  A powerful ecosystem to scaffold production-ready Node.js microservices with built-in best practices. Choose between **MVC** or **Clean Architecture**, **JavaScript** or **TypeScript**, and your preferred tech stack in seconds.
19
13
 
20
- ## πŸ“Œ Table of Contents
14
+ ## Table of Contents
21
15
 
22
- - [πŸš€ Quick Start](#-choose-your-journey)
23
- - [πŸ†• What's New](#-whats-new-in-v21-the-authentication-release)
24
- - [✨ Key Features](#-key-features)
25
- - [πŸ›‘οΈ Professional Standards](#-professional-standards)
26
- - [🧩 5,280+ Project Combinations](#-5280-project-combinations)
27
- - [βš™οΈ Configuration Options](#-configuration-options)
28
- - [πŸ—οΈ Generated Project Structure](#-generated-project-structure)
29
- - [πŸ“– Documentation](#-documentation)
30
- - [πŸ—ΊοΈ Support & Roadmap](#️-roadmap--support)
16
+ - [Quick Start](#choose-your-journey)
17
+ - [What's New](#whats-new-in-v21-the-authentication-release)
18
+ - [Key Features](#key-features)
19
+ - [Professional Standards](#professional-standards)
20
+ - [5,280+ Project Combinations](#5280-project-combinations)
21
+ - [Configuration Options](#configuration-options)
22
+ - [Generated Project Structure](#generated-project-structure)
23
+ - [Documentation](#documentation)
24
+ - [Support & Roadmap](#support--roadmap)
31
25
 
32
- ## πŸš€ Choose Your Journey
26
+ ## Choose Your Journey
33
27
 
34
- | **Path A: Next-Gen Web UI** (Recommended ⭐️) | **Path B: Interactive CLI** |
28
+ | **Path A: Next-Gen Web UI** (Recommended) | **Path B: Interactive CLI** |
35
29
  | :--- | :--- |
36
30
  | <a href="https://paudang.github.io/nodejs-quickstart-structure/#configurator"><img src="docs/public/v2-preview.png" width="100%" alt="UI Preview"></a> | <img src="docs/demo.gif" width="100%" alt="CLI Demo"> |
37
- | [Try Visual Configurator β†’](https://paudang.github.io/nodejs-quickstart-structure/#configurator) | [See CLI Commands ↓](#-path-b-interactive-cli) |
38
- | ✨ **Visual Preview**: Real-time folder simulation. | ⚑ **Fast & Direct**: Quickly scaffold in terminal. |
39
- | πŸ› οΈ **Zero-Prompt**: Paste a tailored command. | 🦾 **AI-Ready**: Generates `.cursorrules`. |
31
+ | [Try Visual Configurator β†’](https://paudang.github.io/nodejs-quickstart-structure/#configurator) | [See CLI Commands ↓](#path-b-interactive-cli) |
32
+ | **Visual Preview**: Real-time folder simulation. | **Fast & Direct**: Quickly scaffold in terminal. |
33
+ | **Zero-Prompt**: Paste a tailored command. | **AI-Ready**: Generates `.cursorrules`. |
40
34
 
41
35
  ---
42
36
 
43
- ### 🦾 Path B: Interactive CLI
37
+ ### Path B: Interactive CLI
44
38
  **Scaffold your project directly from your terminal in seconds.**
45
39
 
46
40
  ```bash
@@ -56,21 +50,21 @@ nodejs-quickstart init
56
50
 
57
51
  ---
58
52
 
59
- ## πŸ†• What's New in v2.1 (The Authentication Release)
53
+ ## What's New in v2.1 (The Authentication Release)
60
54
 
61
55
  The v2.1.0 release is a major leap forward, turning the generator into a **Community Standard**:
62
56
 
63
- - **πŸ” Pluggable JWT Authentication**: Production-ready access & refresh token patterns with automatic PM2/Environment configuration.
64
- - **🦾 AI-Native Foundation**: Built-in `.cursorrules` optimized for **Cursor** & AI agentsβ€”projects are "Born to be Autonomously Coded."
65
- - **πŸ–ΌοΈ Next-Gen Web UI**: A browser-based visual project simulator with real-time folder previews.
66
- - **πŸ—οΈ Enterprise Clean Architecture**: High-fidelity structure for professional Microservices (TS/JS).
67
- - **πŸ›‘οΈ Hardened Security**: Integrated Snyk & SonarCloud logic in core templates.
68
- - **⚑ Zero-Prompt Workflow**: Generate projects with a single CLI command.
57
+ - **Pluggable JWT Authentication**: Production-ready access & refresh token patterns with automatic PM2/Environment configuration.
58
+ - **AI-Native Foundation**: Built-in `.cursorrules` optimized for **Cursor** & AI agentsβ€”projects are "Born to be Autonomously Coded."
59
+ - **Next-Gen Web UI**: A browser-based visual project simulator with real-time folder previews.
60
+ - **Enterprise Clean Architecture**: High-fidelity structure for professional Microservices (TS/JS).
61
+ - **Hardened Security**: Integrated Snyk & SonarCloud logic in core templates.
62
+ - **Zero-Prompt Workflow**: Generate projects with a single CLI command.
69
63
 
70
64
  ---
71
65
 
72
66
 
73
- ## ✨ Key Features
67
+ ## Key Features
74
68
 
75
69
  - **Interactive CLI**: Smooth, guided configuration process.
76
70
  - **Multiple Architectures**: Supports both **MVC** and **Clean Architecture**.
@@ -79,24 +73,24 @@ nodejs-quickstart init
79
73
  - **Communication Patterns**: Supports **REST**, **GraphQL** (Apollo), and **Kafka** (Event-driven).
80
74
  - **Multi-layer Caching**: Integrated **Redis** or built-in **Memory Cache**.
81
75
  - **Pluggable Authentication**: Built-in **JWT** support (Refresh/Access tokens).
82
- - **AI-Native Optimized**: specifically designed for **Cursor** and AI agents, including built-in `.cursorrules` and Agent Skill prompts. πŸš€
76
+ - **AI-Native Optimized**: specifically designed for **Cursor** and AI agents, including built-in `.cursorrules` and Agent Skill prompts.
83
77
 
84
78
  ---
85
79
 
86
- ## πŸ›‘οΈ Professional Standards
80
+ ## Professional Standards
87
81
 
88
82
  We don't just generate boilerplate; we generate **production-ready** foundations. Every project includes:
89
83
 
90
- - **πŸ” Code Quality**: Pre-configured `Eslint` and `Prettier`.
91
- - **πŸ›‘οΈ Enterprise Security**: Integrated **Snyk (SCA)**, **SonarCloud (SAST)**, `Helmet`, `HPP`, and Rate-Limiting.
92
- - **🚨 Robust Error Handling**: Centralized global error middleware with custom error classes (`ApiError`, `NotFoundError`, etc.) β€” consistent across REST & GraphQL.
93
- - **πŸ§ͺ Testing Excellence**: Integrated `Jest` and `Supertest` with **>80% Unit Test coverage** out of the box.
94
- - **πŸ”„ DevOps & CI/CD**: Optimized **Multi-Stage Dockerfiles**, health checks, infrastructure retry logic, and workflows for **GitHub Actions**, **Jenkins**, **GitLab CI**, **CircleCI**, and **Bitbucket Pipelines**.
95
- - **πŸš€ Scalable Deployment**: Integrated **PM2 Ecosystem** config for zero-downtime reloads.
84
+ - **Code Quality**: Pre-configured `Eslint` and `Prettier`.
85
+ - **Enterprise Security**: Integrated **Snyk (SCA)**, **SonarCloud (SAST)**, `Helmet`, `HPP`, and Rate-Limiting.
86
+ - **Robust Error Handling**: Centralized global error middleware with custom error classes (`ApiError`, `NotFoundError`, etc.) β€” consistent across REST & GraphQL.
87
+ - **Testing Excellence**: Integrated `Jest` and `Supertest` with **>80% Unit Test coverage** out of the box.
88
+ - **DevOps & CI/CD**: Optimized **Multi-Stage Dockerfiles**, health checks, infrastructure retry logic, and workflows for **GitHub Actions**, **Jenkins**, **GitLab CI**, **CircleCI**, and **Bitbucket Pipelines**.
89
+ - **Scalable Deployment**: Integrated **PM2 Ecosystem** config for zero-downtime reloads.
96
90
 
97
91
  ---
98
92
 
99
- ## 🧩 5,280+ Project Combinations
93
+ ## 5,280+ Project Combinations
100
94
 
101
95
  The CLI supports a massive number of configurations to fit your exact needs:
102
96
 
@@ -110,7 +104,7 @@ The CLI supports a massive number of configurations to fit your exact needs:
110
104
 
111
105
  ---
112
106
 
113
- ## βš™οΈ Configuration Options
107
+ ## Configuration Options
114
108
 
115
109
  The CLI will guide you through:
116
110
  1. **Project Name**
@@ -126,7 +120,7 @@ The CLI will guide you through:
126
120
 
127
121
  ---
128
122
 
129
- ## πŸ—οΈ Generated Project Structure
123
+ ## Generated Project Structure
130
124
  Depending on your choices, the structure adapts. Here is a **TypeScript + Clean Architecture** preview:
131
125
 
132
126
  ```text
@@ -148,13 +142,13 @@ Depending on your choices, the structure adapts. Here is a **TypeScript + Clean
148
142
 
149
143
  ---
150
144
 
151
- ## πŸ“– Documentation
145
+ ## Documentation
152
146
 
153
147
  For full guides, architecture deep-dives, and feature references, visit our **[Official Documentation Site](https://paudang.github.io/nodejs-quickstart-structure/guide/getting-started.html)**.
154
148
 
155
149
  ---
156
150
 
157
- ## ❀️ Support & πŸ—ΊοΈ Roadmap
151
+ ## Support & Roadmap
158
152
 
159
153
  ### Support the Project
160
154
  If this tool helped you build your project faster, please support us:
@@ -163,11 +157,11 @@ If this tool helped you build your project faster, please support us:
163
157
 
164
158
  ### Roadmap
165
159
  Track our progress and vote for features on our public board:
166
- πŸ‘‰ **[View our Public Roadmap on Trello](https://trello.com/b/TPTo8ylF/nodejs-quickstart-structure-product)**
160
+ **[View our Public Roadmap on Trello](https://trello.com/b/TPTo8ylF/nodejs-quickstart-structure-product)**
167
161
 
168
162
  ---
169
163
 
170
- ## ⭐ Why Star us?
164
+ ## Why Star us?
171
165
 
172
166
  We are on a mission to build the best AI-Native Node.js scaffolding experience. Your star is not just a "like"β€”it's a vote of confidence that helps us:
173
167
 
@@ -175,7 +169,7 @@ We are on a mission to build the best AI-Native Node.js scaffolding experience.
175
169
  2. **AI Model Awareness**: Popular repositories are weighted higher by AI coding assistants (Cursor, Copilot, etc.), making the generated code even better.
176
170
  3. **Open Source Sustainability**: It motivates us to keep building and shipping "Enterprise-Grade" features for free.
177
171
 
178
- If this tool saved you hours of work, **[please give us a Star!](https://github.com/paudang/nodejs-quickstart-structure)** πŸš€
172
+ If this tool saved you hours of work, **[please give us a Star!](https://github.com/paudang/nodejs-quickstart-structure)**
179
173
 
180
174
  ---
181
175
 
@@ -53,6 +53,12 @@ export const renderProfessionalConfig = async (templatesDir, targetDir, config)
53
53
  const jestE2eContent = ejs.render(jestE2eTemplate, { ...config });
54
54
  await fs.writeFile(path.join(targetDir, 'jest.e2e.config.js'), jestE2eContent);
55
55
 
56
+ if (config.language === 'JavaScript') {
57
+ const babelTemplate = await fs.readFile(path.join(templatesDir, 'common', 'babel.config.js.ejs'), 'utf-8');
58
+ const babelContent = ejs.render(babelTemplate, { ...config });
59
+ await fs.writeFile(path.join(targetDir, 'babel.config.js'), babelContent);
60
+ }
61
+
56
62
  // 1. Setup Husky pre-commit (Always for Professional Standard)
57
63
  const huskyDir = path.join(targetDir, '.husky');
58
64
  await fs.ensureDir(huskyDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-quickstart-structure",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "type": "module",
5
5
  "description": "The ultimate nodejs quickstart structure CLI to scaffold Node.js microservices with MVC or Clean Architecture",
6
6
  "main": "bin/index.js",
@@ -64,7 +64,8 @@
64
64
  },
65
65
  "overrides": {
66
66
  "esbuild": "^0.25.0",
67
- "vite": "^6.4.2"
67
+ "vite": "^6.4.2",
68
+ "postcss": "^8.5.10"
68
69
  },
69
70
  "devDependencies": {
70
71
  "snyk": "^1.1303.2",
@@ -1,7 +1,17 @@
1
1
  const UserRepository = require('@/infrastructure/repositories/UserRepository');
2
2
  const UserModel = require('@/infrastructure/database/models/User');
3
3
 
4
- jest.mock('@/infrastructure/database/models/User');
4
+ jest.mock('@/infrastructure/database/models/User', () => ({
5
+ create: jest.fn(),
6
+ findAll: jest.fn(),
7
+ findByPk: jest.fn(),
8
+ update: jest.fn(),
9
+ destroy: jest.fn(),
10
+ find: jest.fn(),
11
+ findById: jest.fn(),
12
+ findByIdAndUpdate: jest.fn(),
13
+ findByIdAndDelete: jest.fn(),
14
+ }));
5
15
 
6
16
  describe('UserRepository', () => {
7
17
  let userRepository;
@@ -28,6 +28,8 @@ jest.mock('express', () => {
28
28
  jest.mock('@/infrastructure/log/logger', () => ({
29
29
  info: jest.fn(),
30
30
  error: jest.fn(),
31
+ warn: jest.fn(),
32
+ debug: jest.fn(),
31
33
  }));
32
34
 
33
35
  jest.mock('@/utils/gracefulShutdown', () => jest.fn());
@@ -33,7 +33,7 @@ describe('UserController (Clean Architecture)', () => {
33
33
  <%_ } -%>
34
34
 
35
35
  beforeEach(() => {
36
- jest.resetAllMocks();
36
+ jest.clearAllMocks();
37
37
 
38
38
  userController = new UserController();
39
39
 
@@ -1,10 +1,26 @@
1
- <% if (auth.includes('JWT')) { %>const JwtService = require('@/infrastructure/auth/jwtService');
2
- jest.mock('@/infrastructure/auth/jwtService');<% } %>
1
+ <% if (auth.includes('JWT')) { %>
2
+ const JwtService = require('@/infrastructure/auth/jwtService');
3
+ jest.mock('@/infrastructure/auth/jwtService', () => ({
4
+ verifyToken: jest.fn(),
5
+ generateToken: jest.fn(),
6
+ generateRefreshToken: jest.fn(),
7
+ verifyRefreshToken: jest.fn(),
8
+ decodeToken: jest.fn(),
9
+ }));
10
+ <% } %>
3
11
  const { gqlContext } = require('@/interfaces/graphql/context');
4
12
  const { resolvers } = require('@/interfaces/graphql/index');
5
13
  const { typeDefs } = require('@/interfaces/graphql/typeDefs/index');
6
14
 
7
- jest.mock('@/infrastructure/repositories/UserRepository');
15
+ jest.mock('@/infrastructure/repositories/UserRepository', () => {
16
+ return jest.fn().mockImplementation(() => ({
17
+ save: jest.fn(),
18
+ findById: jest.fn(),
19
+ getUsers: jest.fn(),
20
+ update: jest.fn(),
21
+ delete: jest.fn()
22
+ }));
23
+ });
8
24
 
9
25
  describe('GraphQL Context', () => {
10
26
  afterEach(() => {
@@ -4,7 +4,15 @@ const UserRepository = require('@/infrastructure/repositories/UserRepository');
4
4
  const cacheService = require('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>');
5
5
  <%_ } -%>
6
6
 
7
- jest.mock('@/infrastructure/repositories/UserRepository');
7
+ jest.mock('@/infrastructure/repositories/UserRepository', () => {
8
+ return jest.fn().mockImplementation(() => ({
9
+ save: jest.fn(),
10
+ findById: jest.fn(),
11
+ getUsers: jest.fn(),
12
+ update: jest.fn(),
13
+ delete: jest.fn()
14
+ }));
15
+ });
8
16
  <%_ if (caching !== 'None') { -%>
9
17
  jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
10
18
  get: jest.fn(),
@@ -4,7 +4,15 @@ const UserRepository = require('@/infrastructure/repositories/UserRepository');
4
4
  const cacheService = require('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>');
5
5
  <%_ } -%>
6
6
 
7
- jest.mock('@/infrastructure/repositories/UserRepository');
7
+ jest.mock('@/infrastructure/repositories/UserRepository', () => {
8
+ return jest.fn().mockImplementation(() => ({
9
+ save: jest.fn(),
10
+ findById: jest.fn(),
11
+ getUsers: jest.fn(),
12
+ update: jest.fn(),
13
+ delete: jest.fn()
14
+ }));
15
+ });
8
16
  <%_ if (caching !== 'None') { -%>
9
17
  jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
10
18
  get: jest.fn(),
@@ -18,7 +18,15 @@ const UserRepository = require('@/infrastructure/repositories/UserRepository');
18
18
  const cacheService = require('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>');
19
19
  <%_ } -%>
20
20
 
21
- jest.mock('@/infrastructure/repositories/UserRepository');
21
+ jest.mock('@/infrastructure/repositories/UserRepository', () => {
22
+ return jest.fn().mockImplementation(() => ({
23
+ save: jest.fn(),
24
+ findById: jest.fn(),
25
+ getUsers: jest.fn(),
26
+ update: jest.fn(),
27
+ delete: jest.fn()
28
+ }));
29
+ });
22
30
  <%_ if (caching !== 'None') { -%>
23
31
  jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
24
32
  get: jest.fn(),
@@ -4,7 +4,15 @@ const UserRepository = require('@/infrastructure/repositories/UserRepository');
4
4
  const cacheService = require('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>');
5
5
  <%_ } -%>
6
6
 
7
- jest.mock('@/infrastructure/repositories/UserRepository');
7
+ jest.mock('@/infrastructure/repositories/UserRepository', () => {
8
+ return jest.fn().mockImplementation(() => ({
9
+ save: jest.fn(),
10
+ findById: jest.fn(),
11
+ getUsers: jest.fn(),
12
+ update: jest.fn(),
13
+ delete: jest.fn()
14
+ }));
15
+ });
8
16
  <%_ if (caching !== 'None') { -%>
9
17
  jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
10
18
  get: jest.fn(),
@@ -2,7 +2,22 @@ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
2
2
  import UserModel from '@/infrastructure/database/models/User';
3
3
 
4
4
  // Mock DB Model Database Layer
5
- jest.mock('@/infrastructure/database/models/User');
5
+ jest.mock('@/infrastructure/database/models/User', () => {
6
+ return {
7
+ __esModule: true,
8
+ default: {
9
+ create: jest.fn(),
10
+ findAll: jest.fn(),
11
+ findByPk: jest.fn(),
12
+ update: jest.fn(),
13
+ destroy: jest.fn(),
14
+ find: jest.fn(),
15
+ findById: jest.fn(),
16
+ findByIdAndUpdate: jest.fn(),
17
+ findByIdAndDelete: jest.fn(),
18
+ }
19
+ };
20
+ });
6
21
 
7
22
  describe('UserRepository', () => {
8
23
  let userRepository: UserRepository;
@@ -12,12 +12,26 @@ import UpdateUser from '@/usecases/updateUser';
12
12
  import DeleteUser from '@/usecases/deleteUser';
13
13
 
14
14
  // Mock dependencies
15
- jest.mock('@/infrastructure/repositories/UserRepository');
15
+ jest.mock('@/infrastructure/repositories/UserRepository', () => ({
16
+ UserRepository: jest.fn().mockImplementation(() => ({
17
+ save: jest.fn(),
18
+ findById: jest.fn(),
19
+ getUsers: jest.fn(),
20
+ update: jest.fn(),
21
+ delete: jest.fn()
22
+ }))
23
+ }));
16
24
  jest.mock('@/usecases/createUser');
17
25
  jest.mock('@/usecases/getAllUsers');
18
26
  jest.mock('@/usecases/updateUser');
19
27
  jest.mock('@/usecases/deleteUser');
20
- jest.mock('@/infrastructure/log/logger');
28
+ jest.mock('@/usecases/getUserById');
29
+ jest.mock('@/infrastructure/log/logger', () => ({
30
+ info: jest.fn(),
31
+ error: jest.fn(),
32
+ warn: jest.fn(),
33
+ debug: jest.fn()
34
+ }));
21
35
  <%_ if (communication === 'Kafka') { -%>
22
36
  jest.mock('@/infrastructure/messaging/kafkaClient', () => ({
23
37
  kafkaService: {
@@ -45,7 +59,7 @@ describe('UserController (Clean Architecture)', () => {
45
59
 
46
60
  beforeEach(() => {
47
61
  // Clear all mocks
48
- jest.resetAllMocks();
62
+ jest.clearAllMocks();
49
63
 
50
64
  userController = new UserController();
51
65
 
@@ -1,9 +1,27 @@
1
1
  import { Request } from 'express';
2
- <% if (auth.includes('JWT')) { %>import { JwtService } from '@/infrastructure/auth/jwtService';
3
- jest.mock('@/infrastructure/auth/jwtService');<% } %>
2
+ <% if (auth.includes('JWT')) { %>
3
+ import { JwtService } from '@/infrastructure/auth/jwtService';
4
+ jest.mock('@/infrastructure/auth/jwtService', () => ({
5
+ JwtService: {
6
+ generateToken: jest.fn(),
7
+ verifyToken: jest.fn(),
8
+ generateRefreshToken: jest.fn(),
9
+ verifyRefreshToken: jest.fn(),
10
+ decodeToken: jest.fn(),
11
+ }
12
+ }));
13
+ <% } %>
4
14
  import { gqlContext } from '@/interfaces/graphql/context';
5
15
 
6
- jest.mock('@/infrastructure/repositories/UserRepository');
16
+ jest.mock('@/infrastructure/repositories/UserRepository', () => ({
17
+ UserRepository: jest.fn().mockImplementation(() => ({
18
+ save: jest.fn(),
19
+ findById: jest.fn(),
20
+ getUsers: jest.fn(),
21
+ update: jest.fn(),
22
+ delete: jest.fn()
23
+ }))
24
+ }));
7
25
 
8
26
  describe('GraphQL Context', () => {
9
27
  afterEach(() => {
@@ -4,7 +4,15 @@ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
4
4
  import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
5
5
  <%_ } -%>
6
6
 
7
- jest.mock('@/infrastructure/repositories/UserRepository');
7
+ jest.mock('@/infrastructure/repositories/UserRepository', () => ({
8
+ UserRepository: jest.fn().mockImplementation(() => ({
9
+ save: jest.fn(),
10
+ findById: jest.fn(),
11
+ getUsers: jest.fn(),
12
+ update: jest.fn(),
13
+ delete: jest.fn()
14
+ }))
15
+ }));
8
16
  <%_ if (caching !== 'None') { -%>
9
17
  jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
10
18
  get: jest.fn(),
@@ -4,7 +4,15 @@ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
4
4
  import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
5
5
  <%_ } -%>
6
6
 
7
- jest.mock('@/infrastructure/repositories/UserRepository');
7
+ jest.mock('@/infrastructure/repositories/UserRepository', () => ({
8
+ UserRepository: jest.fn().mockImplementation(() => ({
9
+ save: jest.fn(),
10
+ findById: jest.fn(),
11
+ getUsers: jest.fn(),
12
+ update: jest.fn(),
13
+ delete: jest.fn()
14
+ }))
15
+ }));
8
16
  <%_ if (caching !== 'None') { -%>
9
17
  jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
10
18
  get: jest.fn(),
@@ -4,7 +4,15 @@ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
4
4
  import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
5
5
  <%_ } -%>
6
6
 
7
- jest.mock('@/infrastructure/repositories/UserRepository');
7
+ jest.mock('@/infrastructure/repositories/UserRepository', () => ({
8
+ UserRepository: jest.fn().mockImplementation(() => ({
9
+ save: jest.fn(),
10
+ findById: jest.fn(),
11
+ getUsers: jest.fn(),
12
+ update: jest.fn(),
13
+ delete: jest.fn()
14
+ }))
15
+ }));
8
16
  <%_ if (caching !== 'None') { -%>
9
17
  jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
10
18
  get: jest.fn(),
@@ -1,7 +1,15 @@
1
1
  import GetUserById from '@/usecases/getUserById';
2
2
  import { UserRepository } from '@/infrastructure/repositories/UserRepository';
3
3
 
4
- jest.mock('@/infrastructure/repositories/UserRepository');
4
+ jest.mock('@/infrastructure/repositories/UserRepository', () => ({
5
+ UserRepository: jest.fn().mockImplementation(() => ({
6
+ save: jest.fn(),
7
+ findById: jest.fn(),
8
+ getUsers: jest.fn(),
9
+ update: jest.fn(),
10
+ delete: jest.fn()
11
+ }))
12
+ }));
5
13
 
6
14
  <% if (caching === 'Redis') { -%>
7
15
  jest.mock('@/infrastructure/caching/redisClient', () => ({
@@ -4,7 +4,15 @@ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
4
4
  import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
5
5
  <%_ } -%>
6
6
 
7
- jest.mock('@/infrastructure/repositories/UserRepository');
7
+ jest.mock('@/infrastructure/repositories/UserRepository', () => ({
8
+ UserRepository: jest.fn().mockImplementation(() => ({
9
+ save: jest.fn(),
10
+ findById: jest.fn(),
11
+ getUsers: jest.fn(),
12
+ update: jest.fn(),
13
+ delete: jest.fn()
14
+ }))
15
+ }));
8
16
  <%_ if (caching !== 'None') { -%>
9
17
  jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
10
18
  get: jest.fn(),
@@ -45,16 +45,16 @@ class AuthController {
45
45
  const refreshJti = JwtService.decodeToken(refreshToken)?.jti;
46
46
  const accessToken = JwtService.generateToken({ id: userId, email: user.email, sid: refreshJti });
47
47
 
48
- <% if (caching !== 'None') { %>
48
+ <%_ if (caching !== 'None') { -%>
49
49
  const cacheKey = `refresh_tokens:${userId}`;
50
50
  const activeTokens = await cacheService.get(cacheKey) || [];
51
51
  activeTokens.push(refreshJti);
52
52
  await cacheService.set(cacheKey, activeTokens, 7 * 24 * 60 * 60);
53
- <% } else { -%>
53
+ <% } else { %>
54
54
  const activeTokens = JwtService.activeRefreshTokens.get(userId) || [];
55
55
  activeTokens.push(refreshJti);
56
56
  JwtService.activeRefreshTokens.set(userId, activeTokens);
57
- <% } %>
57
+ <%_ } -%>
58
58
 
59
59
  res.json({ token: accessToken, accessToken, refreshToken });
60
60
  } catch (error) {
@@ -78,7 +78,7 @@ class AuthController {
78
78
  const userId = String(decoded.id);
79
79
  const incomingJti = decoded.jti;
80
80
 
81
- <% if (caching !== 'None') { %>
81
+ <%_ if (caching !== 'None') { -%>
82
82
  const cacheKey = `refresh_tokens:${userId}`;
83
83
  let activeTokens = await cacheService.get(cacheKey) || [];
84
84
 
@@ -95,7 +95,7 @@ class AuthController {
95
95
 
96
96
  activeTokens.push(newRefreshJti);
97
97
  await cacheService.set(cacheKey, activeTokens, 7 * 24 * 60 * 60);
98
- <% } else { -%>
98
+ <% } else { %>
99
99
  let activeTokens = JwtService.activeRefreshTokens.get(userId) || [];
100
100
 
101
101
  if (!activeTokens.includes(incomingJti)) {
@@ -111,7 +111,7 @@ class AuthController {
111
111
 
112
112
  activeTokens.push(newRefreshJti);
113
113
  JwtService.activeRefreshTokens.set(userId, activeTokens);
114
- <% } %>
114
+ <%_ } -%>
115
115
  res.json({ accessToken: newAccessToken, refreshToken: newRefreshToken });
116
116
  } catch (error) {
117
117
  logger.error('Refresh token error:', error);
@@ -131,13 +131,15 @@ class AuthController {
131
131
 
132
132
  if (decodedAccess && decodedAccess.jti && decodedAccess.exp) {
133
133
  const remainingTime = Math.max(0, decodedAccess.exp - Math.floor(Date.now() / 1000));
134
- <%_ if (caching !== 'None') { -%>
134
+ <%_ if (caching !== 'None') { -%>
135
135
  if (remainingTime > 0) {
136
136
  await cacheService.set(`blacklist:${decodedAccess.jti}`, true, remainingTime);
137
- }<%_ } else { -%>
137
+ }
138
+ <% } else { %>
138
139
  if (remainingTime > 0) {
139
140
  JwtService.blacklistedTokens.set(decodedAccess.jti, Date.now() + remainingTime * 1000);
140
- }<%_ } -%>
141
+ }
142
+ <%_ } -%>
141
143
  }
142
144
 
143
145
  const { refreshToken } = req.body;
@@ -93,7 +93,7 @@ export class AuthController {
93
93
 
94
94
  activeTokens.push(newRefreshJti!);
95
95
  await cacheService.set(cacheKey, activeTokens, 7 * 24 * 60 * 60);
96
- <% } else { -%>
96
+ <% } else { %>
97
97
  let activeTokens = JwtService.activeRefreshTokens.get(userId) || [];
98
98
 
99
99
  if (!activeTokens.includes(incomingJti)) {
@@ -130,13 +130,15 @@ export class AuthController {
130
130
 
131
131
  if (decodedAccess && decodedAccess.jti && decodedAccess.exp) {
132
132
  const remainingTime = Math.max(0, decodedAccess.exp - Math.floor(Date.now() / 1000));
133
- <%_ if (caching !== 'None') { -%>
133
+ <%_ if (caching !== 'None') { -%>
134
134
  if (remainingTime > 0) {
135
135
  await cacheService.set(`blacklist:${decodedAccess.jti}`, true, remainingTime);
136
- }<%_ } else { -%>
136
+ }
137
+ <% } else { %>
137
138
  if (remainingTime > 0) {
138
139
  JwtService.blacklistedTokens.set(decodedAccess.jti, Date.now() + remainingTime * 1000);
139
- }<%_ } -%>
140
+ }
141
+ <%_ } -%>
140
142
  }
141
143
 
142
144
  const { refreshToken } = req.body;
@@ -149,7 +151,7 @@ export class AuthController {
149
151
  let activeTokens = await cacheService.get<string[]>(cacheKey) || [];
150
152
  activeTokens = activeTokens.filter(t => t !== decodedRefresh.jti);
151
153
  await cacheService.set(cacheKey, activeTokens, 7 * 24 * 60 * 60);
152
- <%_ } else { -%>
154
+ <% } else { %>
153
155
  let activeTokens = JwtService.activeRefreshTokens.get(userId) || [];
154
156
  activeTokens = activeTokens.filter(t => t !== decodedRefresh.jti);
155
157
  JwtService.activeRefreshTokens.set(userId, activeTokens);<% } %>
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ presets: [
3
+ ['@babel/preset-env', { targets: { node: 'current' } }]
4
+ ]
5
+ };
@@ -9,6 +9,8 @@ jest.mock('node-cache', () => {
9
9
  jest.mock('<%- loggerPath %>', () => ({
10
10
  info: jest.fn(),
11
11
  error: jest.fn(),
12
+ warn: jest.fn(),
13
+ debug: jest.fn(),
12
14
  }));
13
15
 
14
16
  describe('Memory Cache Client', () => {
@@ -16,6 +16,8 @@ jest.mock('dotenv', () => ({
16
16
  const mockLogger = {
17
17
  info: jest.fn(),
18
18
  error: jest.fn(),
19
+ warn: jest.fn(),
20
+ debug: jest.fn(),
19
21
  };
20
22
 
21
23
  jest.mock('<%- loggerPath %>', () => mockLogger);
@@ -9,6 +9,8 @@ jest.mock('node-cache', () => {
9
9
  jest.mock('<%- loggerPath %>', () => ({
10
10
  info: jest.fn(),
11
11
  error: jest.fn(),
12
+ warn: jest.fn(),
13
+ debug: jest.fn(),
12
14
  }));
13
15
 
14
16
  describe('Memory Cache Client', () => {
@@ -16,6 +16,8 @@ jest.mock('dotenv', () => ({
16
16
  const mockLogger = {
17
17
  info: jest.fn(),
18
18
  error: jest.fn(),
19
+ warn: jest.fn(),
20
+ debug: jest.fn(),
19
21
  };
20
22
 
21
23
  jest.mock('<%- loggerPath %>', () => mockLogger);
@@ -5,6 +5,8 @@ jest.mock('mongoose', () => ({
5
5
  const mockLogger = {
6
6
  info: jest.fn(),
7
7
  error: jest.fn(),
8
+ warn: jest.fn(),
9
+ debug: jest.fn(),
8
10
  };
9
11
 
10
12
  jest.mock('<% if (architecture === "MVC") { %>@/utils/logger<% } else { %>@/infrastructure/log/logger<% } %>', () => mockLogger);
@@ -5,6 +5,8 @@ jest.mock('mongoose', () => ({
5
5
  const logger = {
6
6
  info: jest.fn(),
7
7
  error: jest.fn(),
8
+ warn: jest.fn(),
9
+ debug: jest.fn(),
8
10
  };
9
11
 
10
12
  jest.mock('<% if (architecture === "MVC") { %>@/utils/logger<% } else { %>@/infrastructure/log/logger<% } %>', () => logger);
@@ -1,8 +1,10 @@
1
1
  module.exports = {
2
2
  testEnvironment: 'node',
3
+ testTimeout: 30000,
3
4
  coverageDirectory: 'coverage',
4
5
  collectCoverageFrom: ['src/**/*.{js,ts}'],
5
6
  testMatch: ['**/*.test.ts', '**/*.test.js', '**/*.spec.ts', '**/*.spec.js'],
7
+ transformIgnorePatterns: ['/node_modules/(?!.*uuid)'],
6
8
  testPathIgnorePatterns: ['/node_modules/', '/tests/e2e/'],
7
9
  <% if (language === 'TypeScript') { %>preset: 'ts-jest',<% } %>
8
10
  moduleNameMapper: {
@@ -29,7 +29,8 @@
29
29
  "braces": "^3.0.3",
30
30
  "picomatch": "^4.0.4",
31
31
  "lodash": "^4.17.23",
32
- "debounce": "^1.2.1"
32
+ "debounce": "^1.2.1",
33
+ "uuid": "^14.0.0"
33
34
  },
34
35
  "dependencies": {
35
36
  "express": "^4.18.2",
@@ -58,7 +59,6 @@
58
59
  "bcryptjs": "^2.4.3",
59
60
  <% } -%>
60
61
  "cors": "^2.8.5",
61
-
62
62
  "helmet": "^7.1.0",
63
63
  "hpp": "^0.2.3",
64
64
  "express-rate-limit": "^7.1.5",
@@ -92,7 +92,6 @@
92
92
  "cpx2": "^8.0.0"<% } %><% if (auth.includes('JWT')) { %>,
93
93
  "@types/jsonwebtoken": "^9.0.6",
94
94
  "@types/bcryptjs": "^2.4.6"<% } %><% } %>,
95
-
96
95
  "eslint": "^10.1.0",
97
96
  "@eslint/js": "^9.20.0",
98
97
  "globals": "^15.14.0",
@@ -115,6 +114,9 @@
115
114
  "tsc-alias": "^1.8.10",
116
115
  "@types/supertest": "^6.0.2"<% } else { %>,
117
116
  "jest": "^29.7.0",
117
+ "babel-jest": "^29.7.0",
118
+ "@babel/core": "^7.24.0",
119
+ "@babel/preset-env": "^7.24.0",
118
120
  "wait-on": "^7.2.0",
119
121
  "supertest": "^7.1.3"<% } %>
120
122
  },
@@ -8,6 +8,7 @@
8
8
  "esModuleInterop": true,
9
9
  "skipLibCheck": true,
10
10
  "forceConsistentCasingInFileNames": true,
11
+ "allowJs": true,
11
12
  "baseUrl": ".",
12
13
  "paths": {
13
14
  "@/*": ["src/*"],