express-genix 1.1.0

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 (47) hide show
  1. package/README.md +110 -0
  2. package/index.js +114 -0
  3. package/lib/cleanup.js +129 -0
  4. package/lib/generator.js +205 -0
  5. package/lib/utils.js +92 -0
  6. package/package.json +49 -0
  7. package/templates/config/database.mongo.js.ejs +36 -0
  8. package/templates/config/database.postgres.js.ejs +41 -0
  9. package/templates/config/swagger.json.ejs +194 -0
  10. package/templates/controllers/authController.js.ejs +129 -0
  11. package/templates/controllers/exampleController.js.ejs +152 -0
  12. package/templates/controllers/userController.js.ejs +60 -0
  13. package/templates/core/Dockerfile.ejs +32 -0
  14. package/templates/core/README.md.ejs +179 -0
  15. package/templates/core/app.js.ejs +65 -0
  16. package/templates/core/docker-compose.yml.ejs +48 -0
  17. package/templates/core/env.ejs +20 -0
  18. package/templates/core/eslintrc.json.ejs +20 -0
  19. package/templates/core/gitignore.ejs +51 -0
  20. package/templates/core/healthcheck.js.ejs +25 -0
  21. package/templates/core/index.js.ejs +24 -0
  22. package/templates/core/jest.config.js.ejs +23 -0
  23. package/templates/core/package.json.ejs +33 -0
  24. package/templates/core/prettierrc.json.ejs +12 -0
  25. package/templates/core/server.js.ejs +44 -0
  26. package/templates/middleware/auth.js.ejs +66 -0
  27. package/templates/middleware/errorHandler.js.ejs +47 -0
  28. package/templates/middleware/validation.js.ejs +48 -0
  29. package/templates/models/User.mongo.js.ejs +33 -0
  30. package/templates/models/User.postgres.js.ejs +41 -0
  31. package/templates/models/index.mongo.js.ejs +8 -0
  32. package/templates/models/index.postgres.js.ejs +12 -0
  33. package/templates/routes/authRoutes.js.ejs +14 -0
  34. package/templates/routes/exampleRoutes.js.ejs +13 -0
  35. package/templates/routes/index.js.ejs +24 -0
  36. package/templates/routes/userRoutes.js.ejs +16 -0
  37. package/templates/services/authService.js.ejs +36 -0
  38. package/templates/services/exampleService.js.ejs +113 -0
  39. package/templates/services/userService.mongo.js.ejs +34 -0
  40. package/templates/services/userService.postgres.js.ejs +31 -0
  41. package/templates/tests/auth.test.js.ejs +67 -0
  42. package/templates/tests/example.test.js.ejs +113 -0
  43. package/templates/tests/setup.js.ejs +12 -0
  44. package/templates/tests/users.test.js.ejs +43 -0
  45. package/templates/utils/errors.js.ejs +13 -0
  46. package/templates/utils/logger.js.ejs +28 -0
  47. package/templates/utils/validators.js.ejs +35 -0
@@ -0,0 +1,48 @@
1
+ // templates/middleware/validation.js.ejs
2
+ const { AppError } = require('../utils/errors');
3
+
4
+ const validateEmail = (email) => {
5
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6
+ return emailRegex.test(email);
7
+ };
8
+
9
+ const validateLogin = (req, res, next) => {
10
+ const { email, password } = req.body;
11
+
12
+ if (!email || !password) {
13
+ return next(new AppError('Email and password are required', 400));
14
+ }
15
+
16
+ if (!validateEmail(email)) {
17
+ return next(new AppError('Please provide a valid email address', 400));
18
+ }
19
+
20
+ next();
21
+ };
22
+
23
+ const validateRegister = (req, res, next) => {
24
+ const { username, email, password } = req.body;
25
+
26
+ if (!username || !email || !password) {
27
+ return next(new AppError('Username, email, and password are required', 400));
28
+ }
29
+
30
+ if (username.length < 3 || username.length > 50) {
31
+ return next(new AppError('Username must be between 3 and 50 characters', 400));
32
+ }
33
+
34
+ if (!validateEmail(email)) {
35
+ return next(new AppError('Please provide a valid email address', 400));
36
+ }
37
+
38
+ if (password.length < 6) {
39
+ return next(new AppError('Password must be at least 6 characters long', 400));
40
+ }
41
+
42
+ next();
43
+ };
44
+
45
+ module.exports = {
46
+ validateLogin,
47
+ validateRegister,
48
+ };
@@ -0,0 +1,33 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const userSchema = new mongoose.Schema({
4
+ username: {
5
+ type: String,
6
+ required: [true, 'Username is required'],
7
+ unique: true,
8
+ trim: true,
9
+ minlength: [3, 'Username must be at least 3 characters long'],
10
+ maxlength: [50, 'Username must not exceed 50 characters'],
11
+ },
12
+ email: {
13
+ type: String,
14
+ required: [true, 'Email is required'],
15
+ unique: true,
16
+ lowercase: true,
17
+ trim: true,
18
+ match: [/^\S+@\S+\.\S+$/, 'Please enter a valid email address'],
19
+ },
20
+ password: {
21
+ type: String,
22
+ required: [true, 'Password is required'],
23
+ minlength: [6, 'Password must be at least 6 characters long'],
24
+ },
25
+ }, {
26
+ timestamps: true,
27
+ });
28
+
29
+ // Index for better query performance
30
+ userSchema.index({ email: 1 });
31
+ userSchema.index({ username: 1 });
32
+
33
+ module.exports = mongoose.model('User', userSchema);
@@ -0,0 +1,41 @@
1
+ const { DataTypes } = require('sequelize');
2
+ const { sequelize } = require('../config/database');
3
+
4
+ const User = sequelize.define('User', {
5
+ id: {
6
+ type: DataTypes.UUID,
7
+ defaultValue: DataTypes.UUIDV4,
8
+ primaryKey: true,
9
+ },
10
+ username: {
11
+ type: DataTypes.STRING(50),
12
+ allowNull: false,
13
+ unique: true,
14
+ validate: {
15
+ len: [3, 50],
16
+ notEmpty: true,
17
+ },
18
+ },
19
+ email: {
20
+ type: DataTypes.STRING,
21
+ allowNull: false,
22
+ unique: true,
23
+ validate: {
24
+ isEmail: true,
25
+ notEmpty: true,
26
+ },
27
+ },
28
+ password: {
29
+ type: DataTypes.STRING,
30
+ allowNull: false,
31
+ validate: {
32
+ len: [6, 255],
33
+ notEmpty: true,
34
+ },
35
+ },
36
+ }, {
37
+ timestamps: true,
38
+ tableName: 'users',
39
+ });
40
+
41
+ module.exports = User;
@@ -0,0 +1,8 @@
1
+ // For MongoDB, we typically don't need an index file as we import models directly
2
+ // But you can use this file to define model relationships if needed
3
+
4
+ const User = require('./User');
5
+
6
+ module.exports = {
7
+ User,
8
+ };
@@ -0,0 +1,12 @@
1
+ const { sequelize } = require('../config/database');
2
+ const User = require('./User');
3
+
4
+ // Define associations here if you have multiple models
5
+ // Example:
6
+ // User.hasMany(Post);
7
+ // Post.belongsTo(User);
8
+
9
+ module.exports = {
10
+ sequelize,
11
+ User,
12
+ };
@@ -0,0 +1,14 @@
1
+ // templates/routes/authRoutes.js.ejs
2
+ const express = require('express');
3
+ const authController = require('../controllers/authController');
4
+ const { validateLogin, validateRegister } = require('../middleware/validation');
5
+
6
+ const router = express.Router();
7
+
8
+ // Authentication routes
9
+ router.post('/register', validateRegister, authController.register);
10
+ router.post('/login', validateLogin, authController.login);
11
+ router.post('/refresh', authController.refreshToken);
12
+ router.post('/logout', authController.authenticateToken, authController.logout);
13
+
14
+ module.exports = router;
@@ -0,0 +1,13 @@
1
+ const express = require('express');
2
+ const exampleController = require('../controllers/exampleController');
3
+
4
+ const router = express.Router();
5
+
6
+ // Example routes
7
+ router.get('/', exampleController.getAllExamples);
8
+ router.get('/:id', exampleController.getExampleById);
9
+ router.post('/', exampleController.createExample);
10
+ router.put('/:id', exampleController.updateExample);
11
+ router.delete('/:id', exampleController.deleteExample);
12
+
13
+ module.exports = router;
@@ -0,0 +1,24 @@
1
+ const express = require('express');<% if (hasDatabase) { %>
2
+ const authRoutes = require('./authRoutes');
3
+ const userRoutes = require('./userRoutes');<% } else { %>
4
+ const exampleRoutes = require('./exampleRoutes');<% } %>
5
+
6
+ const router = express.Router();
7
+
8
+ // Mount route modules<% if (hasDatabase) { %>
9
+ router.use('/auth', authRoutes);
10
+ router.use('/users', userRoutes);<% } else { %>
11
+ router.use('/examples', exampleRoutes);<% } %>
12
+
13
+ // API root endpoint
14
+ router.get('/', (req, res) => {
15
+ res.json({
16
+ message: 'Welcome to the API',
17
+ version: '1.0.0',
18
+ documentation: '/api-docs',<% if (hasDatabase) { %>
19
+ features: ['authentication', 'user-management', '<%= db %>-database']<% } else { %>
20
+ features: ['rate-limiting', 'logging', 'swagger-docs']<% } %>,
21
+ });
22
+ });
23
+
24
+ module.exports = router;
@@ -0,0 +1,16 @@
1
+ // templates/routes/userRoutes.js.ejs
2
+ const express = require('express');
3
+ const userController = require('../controllers/userController');
4
+ const authController = require('../controllers/authController');
5
+
6
+ const router = express.Router();
7
+
8
+ // All user routes require authentication
9
+ router.use(authController.authenticateToken);
10
+
11
+ // User routes
12
+ router.get('/profile', userController.getProfile);
13
+ router.put('/profile', userController.updateProfile);
14
+ router.delete('/profile', userController.deleteProfile);
15
+
16
+ module.exports = router;
@@ -0,0 +1,36 @@
1
+ const jwt = require('jsonwebtoken');
2
+
3
+ const jwtSecret = process.env.JWT_SECRET || 'your-secret-key';
4
+ const jwtRefreshSecret = process.env.JWT_REFRESH_SECRET || 'your-refresh-secret-key';
5
+
6
+ const generateTokens = (user) => {
7
+ const payload = {
8
+ userId: user.id,
9
+ username: user.username,
10
+ email: user.email,
11
+ };
12
+
13
+ const accessToken = jwt.sign(payload, jwtSecret, {
14
+ expiresIn: process.env.JWT_EXPIRE || '15m',
15
+ });
16
+
17
+ const refreshToken = jwt.sign({ userId: user.id }, jwtRefreshSecret, {
18
+ expiresIn: process.env.JWT_REFRESH_EXPIRE || '7d',
19
+ });
20
+
21
+ return { accessToken, refreshToken };
22
+ };
23
+
24
+ const verifyToken = (token) => {
25
+ return jwt.verify(token, jwtSecret);
26
+ };
27
+
28
+ const verifyRefreshToken = (token) => {
29
+ return jwt.verify(token, jwtRefreshSecret);
30
+ };
31
+
32
+ module.exports = {
33
+ generateTokens,
34
+ verifyToken,
35
+ verifyRefreshToken,
36
+ };
@@ -0,0 +1,113 @@
1
+ const { createLogger } = require('../utils/logger');
2
+
3
+ const logger = createLogger('ExampleService');
4
+
5
+ // In-memory storage for examples (replace with database in real applications)
6
+ let examples = [
7
+ {
8
+ id: '1',
9
+ title: 'Example 1',
10
+ description: 'This is the first example',
11
+ createdAt: new Date('2024-01-01'),
12
+ updatedAt: new Date('2024-01-01'),
13
+ },
14
+ {
15
+ id: '2',
16
+ title: 'Example 2',
17
+ description: 'This is the second example',
18
+ createdAt: new Date('2024-01-02'),
19
+ updatedAt: new Date('2024-01-02'),
20
+ },
21
+ ];
22
+
23
+ let nextId = 3;
24
+
25
+ /**
26
+ * Get all examples with pagination
27
+ */
28
+ const getAllExamples = async (page = 1, limit = 10) => {
29
+ logger.info('Fetching all examples', { page, limit });
30
+
31
+ const startIndex = (page - 1) * limit;
32
+ const endIndex = startIndex + limit;
33
+
34
+ const paginatedExamples = examples.slice(startIndex, endIndex);
35
+
36
+ return {
37
+ examples: paginatedExamples,
38
+ total: examples.length,
39
+ totalPages: Math.ceil(examples.length / limit),
40
+ currentPage: page,
41
+ };
42
+ };
43
+
44
+ /**
45
+ * Get example by ID
46
+ */
47
+ const getExampleById = async (id) => {
48
+ logger.info('Fetching example by ID', { id });
49
+
50
+ const example = examples.find(ex => ex.id === id);
51
+ return example || null;
52
+ };
53
+
54
+ /**
55
+ * Create new example
56
+ */
57
+ const createExample = async (exampleData) => {
58
+ logger.info('Creating new example', { title: exampleData.title });
59
+
60
+ const newExample = {
61
+ id: nextId.toString(),
62
+ ...exampleData,
63
+ };
64
+
65
+ examples.push(newExample);
66
+ nextId++;
67
+
68
+ return newExample;
69
+ };
70
+
71
+ /**
72
+ * Update example by ID
73
+ */
74
+ const updateExample = async (id, updateData) => {
75
+ logger.info('Updating example', { id });
76
+
77
+ const index = examples.findIndex(ex => ex.id === id);
78
+
79
+ if (index === -1) {
80
+ return null;
81
+ }
82
+
83
+ examples[index] = {
84
+ ...examples[index],
85
+ ...updateData,
86
+ };
87
+
88
+ return examples[index];
89
+ };
90
+
91
+ /**
92
+ * Delete example by ID
93
+ */
94
+ const deleteExample = async (id) => {
95
+ logger.info('Deleting example', { id });
96
+
97
+ const index = examples.findIndex(ex => ex.id === id);
98
+
99
+ if (index === -1) {
100
+ return false;
101
+ }
102
+
103
+ examples.splice(index, 1);
104
+ return true;
105
+ };
106
+
107
+ module.exports = {
108
+ getAllExamples,
109
+ getExampleById,
110
+ createExample,
111
+ updateExample,
112
+ deleteExample,
113
+ };
@@ -0,0 +1,34 @@
1
+ // templates/services/userService.mongo.js.ejs
2
+ const User = require('../models/User');
3
+
4
+ const findById = async (id) => {
5
+ return await User.findById(id).select('-password');
6
+ };
7
+
8
+ const findByEmail = async (email) => {
9
+ return await User.findOne({ email });
10
+ };
11
+
12
+ const create = async (userData) => {
13
+ const user = new User(userData);
14
+ return await user.save();
15
+ };
16
+
17
+ const updateById = async (id, updateData) => {
18
+ return await User.findByIdAndUpdate(id, updateData, {
19
+ new: true,
20
+ select: '-password'
21
+ });
22
+ };
23
+
24
+ const deleteById = async (id) => {
25
+ return await User.findByIdAndDelete(id);
26
+ };
27
+
28
+ module.exports = {
29
+ findById,
30
+ findByEmail,
31
+ create,
32
+ updateById,
33
+ deleteById,
34
+ };
@@ -0,0 +1,31 @@
1
+ // templates/services/userService.postgres.js.ejs
2
+ const { User } = require('../models');
3
+
4
+ const findById = async (id) => {
5
+ return await User.findByPk(id, { attributes: { exclude: ['password'] } });
6
+ };
7
+
8
+ const findByEmail = async (email) => {
9
+ return await User.findOne({ where: { email } });
10
+ };
11
+
12
+ const create = async (userData) => {
13
+ return await User.create(userData);
14
+ };
15
+
16
+ const updateById = async (id, updateData) => {
17
+ await User.update(updateData, { where: { id } });
18
+ return await User.findByPk(id, { attributes: { exclude: ['password'] } });
19
+ };
20
+
21
+ const deleteById = async (id) => {
22
+ return await User.destroy({ where: { id } });
23
+ };
24
+
25
+ module.exports = {
26
+ findById,
27
+ findByEmail,
28
+ create,
29
+ updateById,
30
+ deleteById,
31
+ };
@@ -0,0 +1,67 @@
1
+ // templates/tests/auth.test.js.ejs
2
+ const request = require('supertest');
3
+ const app = require('../src/app');
4
+
5
+ describe('Authentication Endpoints', () => {
6
+ describe('POST /api/auth/register', () => {
7
+ it('should register a new user', async () => {
8
+ const userData = {
9
+ username: 'testuser',
10
+ email: 'test@example.com',
11
+ password: 'password123',
12
+ };
13
+
14
+ const res = await request(app)
15
+ .post('/api/auth/register')
16
+ .send(userData);
17
+
18
+ expect(res.statusCode).toBe(201);
19
+ expect(res.body).toHaveProperty('accessToken');
20
+ expect(res.body).toHaveProperty('refreshToken');
21
+ expect(res.body.user).toHaveProperty('email', userData.email);
22
+ });
23
+
24
+ it('should return 400 for invalid email', async () => {
25
+ const userData = {
26
+ username: 'testuser',
27
+ email: 'invalid-email',
28
+ password: 'password123',
29
+ };
30
+
31
+ const res = await request(app)
32
+ .post('/api/auth/register')
33
+ .send(userData);
34
+
35
+ expect(res.statusCode).toBe(400);
36
+ });
37
+ });
38
+
39
+ describe('POST /api/auth/login', () => {
40
+ it('should login with valid credentials', async () => {
41
+ const credentials = {
42
+ email: 'test@example.com',
43
+ password: 'password123',
44
+ };
45
+
46
+ const res = await request(app)
47
+ .post('/api/auth/login')
48
+ .send(credentials);
49
+
50
+ expect(res.statusCode).toBe(200);
51
+ expect(res.body).toHaveProperty('accessToken');
52
+ });
53
+
54
+ it('should return 401 for invalid credentials', async () => {
55
+ const credentials = {
56
+ email: 'test@example.com',
57
+ password: 'wrongpassword',
58
+ };
59
+
60
+ const res = await request(app)
61
+ .post('/api/auth/login')
62
+ .send(credentials);
63
+
64
+ expect(res.statusCode).toBe(401);
65
+ });
66
+ });
67
+ });
@@ -0,0 +1,113 @@
1
+ const request = require('supertest');
2
+ const app = require('../src/app');
3
+
4
+ describe('Example Endpoints', () => {
5
+ describe('GET /api/examples', () => {
6
+ it('should get all examples', async () => {
7
+ const res = await request(app)
8
+ .get('/api/examples');
9
+
10
+ expect(res.statusCode).toBe(200);
11
+ expect(res.body.success).toBe(true);
12
+ expect(res.body.data).toHaveProperty('examples');
13
+ expect(Array.isArray(res.body.data.examples)).toBe(true);
14
+ });
15
+
16
+ it('should support pagination', async () => {
17
+ const res = await request(app)
18
+ .get('/api/examples?page=1&limit=1');
19
+
20
+ expect(res.statusCode).toBe(200);
21
+ expect(res.body.pagination).toEqual({
22
+ page: 1,
23
+ limit: 1,
24
+ });
25
+ });
26
+ });
27
+
28
+ describe('GET /api/examples/:id', () => {
29
+ it('should get example by id', async () => {
30
+ const res = await request(app)
31
+ .get('/api/examples/1');
32
+
33
+ expect(res.statusCode).toBe(200);
34
+ expect(res.body.success).toBe(true);
35
+ expect(res.body.data).toHaveProperty('id', '1');
36
+ });
37
+
38
+ it('should return 404 for non-existent example', async () => {
39
+ const res = await request(app)
40
+ .get('/api/examples/999');
41
+
42
+ expect(res.statusCode).toBe(404);
43
+ });
44
+ });
45
+
46
+ describe('POST /api/examples', () => {
47
+ it('should create new example', async () => {
48
+ const exampleData = {
49
+ title: 'Test Example',
50
+ description: 'This is a test example',
51
+ };
52
+
53
+ const res = await request(app)
54
+ .post('/api/examples')
55
+ .send(exampleData);
56
+
57
+ expect(res.statusCode).toBe(201);
58
+ expect(res.body.success).toBe(true);
59
+ expect(res.body.data).toHaveProperty('title', exampleData.title);
60
+ expect(res.body.data).toHaveProperty('description', exampleData.description);
61
+ });
62
+
63
+ it('should return 400 for missing required fields', async () => {
64
+ const res = await request(app)
65
+ .post('/api/examples')
66
+ .send({ title: 'Only title' });
67
+
68
+ expect(res.statusCode).toBe(400);
69
+ });
70
+ });
71
+
72
+ describe('PUT /api/examples/:id', () => {
73
+ it('should update existing example', async () => {
74
+ const updateData = {
75
+ title: 'Updated Example',
76
+ description: 'Updated description',
77
+ };
78
+
79
+ const res = await request(app)
80
+ .put('/api/examples/1')
81
+ .send(updateData);
82
+
83
+ expect(res.statusCode).toBe(200);
84
+ expect(res.body.success).toBe(true);
85
+ expect(res.body.data).toHaveProperty('title', updateData.title);
86
+ });
87
+
88
+ it('should return 404 for non-existent example', async () => {
89
+ const res = await request(app)
90
+ .put('/api/examples/999')
91
+ .send({ title: 'Updated' });
92
+
93
+ expect(res.statusCode).toBe(404);
94
+ });
95
+ });
96
+
97
+ describe('DELETE /api/examples/:id', () => {
98
+ it('should delete existing example', async () => {
99
+ const res = await request(app)
100
+ .delete('/api/examples/2');
101
+
102
+ expect(res.statusCode).toBe(200);
103
+ expect(res.body.success).toBe(true);
104
+ });
105
+
106
+ it('should return 404 for non-existent example', async () => {
107
+ const res = await request(app)
108
+ .delete('/api/examples/999');
109
+
110
+ expect(res.statusCode).toBe(404);
111
+ });
112
+ });
113
+ });
@@ -0,0 +1,12 @@
1
+ // templates/tests/setup.js.ejs
2
+ // Global test setup
3
+ process.env.NODE_ENV = 'test';
4
+ <% if (hasDatabase) { %>process.env.JWT_SECRET = 'test-secret';
5
+ process.env.JWT_REFRESH_SECRET = 'test-refresh-secret';<% } %>
6
+
7
+ // Suppress console logs during tests
8
+ if (process.env.NODE_ENV === 'test') {
9
+ console.log = jest.fn();
10
+ console.error = jest.fn();
11
+ console.warn = jest.fn();
12
+ }
@@ -0,0 +1,43 @@
1
+ // templates/tests/users.test.js.ejs
2
+ const request = require('supertest');
3
+ const jwt = require('jsonwebtoken');
4
+ const app = require('../src/app');
5
+
6
+ describe('User Endpoints', () => {
7
+ let authToken;
8
+
9
+ beforeAll(() => {
10
+ // Create a test token
11
+ authToken = jwt.sign(
12
+ { userId: 'test-user-id', username: 'testuser', email: 'test@example.com' },
13
+ process.env.JWT_SECRET,
14
+ { expiresIn: '1h' }
15
+ );
16
+ });
17
+
18
+ describe('GET /api/users/profile', () => {
19
+ it('should get user profile with valid token', async () => {
20
+ const res = await request(app)
21
+ .get('/api/users/profile')
22
+ .set('Authorization', `Bearer ${authToken}`);
23
+
24
+ expect(res.statusCode).toBe(200);
25
+ expect(res.body).toHaveProperty('user');
26
+ });
27
+
28
+ it('should return 401 without token', async () => {
29
+ const res = await request(app)
30
+ .get('/api/users/profile');
31
+
32
+ expect(res.statusCode).toBe(401);
33
+ });
34
+
35
+ it('should return 403 with invalid token', async () => {
36
+ const res = await request(app)
37
+ .get('/api/users/profile')
38
+ .set('Authorization', 'Bearer invalid-token');
39
+
40
+ expect(res.statusCode).toBe(403);
41
+ });
42
+ });
43
+ });
@@ -0,0 +1,13 @@
1
+ // templates/utils/errors.js.ejs
2
+ class AppError extends Error {
3
+ constructor(message, statusCode) {
4
+ super(message);
5
+ this.statusCode = statusCode;
6
+ this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
7
+ this.isOperational = true;
8
+
9
+ Error.captureStackTrace(this, this.constructor);
10
+ }
11
+ }
12
+
13
+ module.exports = { AppError };