create-pnpm-custom-app 1.0.2 → 1.0.4

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 (34) hide show
  1. package/README.md +11 -9
  2. package/bin/cli.js +113 -82
  3. package/package.json +13 -5
  4. package/templates/.github/copilot-instructions.md +1 -0
  5. package/templates/CONTRIBUTING.md +8 -1
  6. package/templates/README.md +3 -3
  7. package/templates/apps/api/package.json +25 -25
  8. package/templates/apps/api/src/app.ts +4 -6
  9. package/templates/apps/api/src/config/config.ts +2 -2
  10. package/templates/apps/api/src/config/logger.ts +3 -3
  11. package/templates/apps/api/src/db/mongo.ts +3 -3
  12. package/templates/apps/api/src/index.ts +2 -2
  13. package/templates/apps/api/src/middlewares/middleware.ts +8 -14
  14. package/templates/apps/api/src/models/example.model.ts +7 -7
  15. package/templates/apps/api/src/routes/routes.ts +3 -3
  16. package/templates/apps/api/src/schemas/swagger.schema.ts +5 -5
  17. package/templates/apps/api/src/services/example.service.ts +9 -9
  18. package/templates/apps/api/src/tests/health.test.ts +9 -9
  19. package/templates/apps/api/src/tests/helpers/test-helpers.ts +4 -4
  20. package/templates/apps/api/src/tests/mocks/mocks.ts +3 -3
  21. package/templates/apps/api/src/tests/setup.ts +1 -1
  22. package/templates/apps/api/src/types/fastify.d.ts +3 -3
  23. package/templates/apps/web/app/[locale]/(routes)/page.tsx +61 -33
  24. package/templates/apps/web/app/[locale]/layout.tsx +1 -3
  25. package/templates/apps/web/app/components/layout/Footer.component.tsx +3 -5
  26. package/templates/apps/web/app/components/layout/Nav.component.tsx +4 -14
  27. package/templates/apps/web/app/globals.css +44 -2
  28. package/templates/apps/web/eslint.config.mjs +1 -7
  29. package/templates/apps/web/lib/utils.ts +2 -2
  30. package/templates/apps/web/package.json +12 -12
  31. package/templates/apps/web/tsconfig.json +7 -1
  32. package/templates/package.json +2 -2
  33. package/templates/packages/shared/eslint.config.js +11 -11
  34. package/templates/packages/shared/src/index.ts +2 -3
package/README.md CHANGED
@@ -19,6 +19,8 @@
19
19
 
20
20
  ### Recommended: Use npx (always latest version)
21
21
 
22
+ [![npm version](https://img.shields.io/npm/v/create-pnpm-custom-app.svg)](https://www.npmjs.com/package/create-pnpm-custom-app)
23
+
22
24
  ```bash
23
25
  npx create-pnpm-custom-app@latest my-project
24
26
  ```
@@ -104,7 +106,7 @@ my-project/
104
106
  ```bash
105
107
  # Frontend
106
108
  cp apps/web/.env.example apps/web/.env.local
107
-
109
+
108
110
  # Backend
109
111
  cp apps/api/.env.example apps/api/.env
110
112
  ```
@@ -118,7 +120,7 @@ my-project/
118
120
  ```bash
119
121
  # Terminal 1: Frontend (http://localhost:3000)
120
122
  pnpm --filter web dev
121
-
123
+
122
124
  # Terminal 2: Backend (http://localhost:3002)
123
125
  pnpm --filter api dev
124
126
  ```
@@ -160,14 +162,14 @@ pnpm --filter api lint # Lint code
160
162
 
161
163
  ## Tech Stack
162
164
 
163
- | Category | Technologies |
164
- | ---------- | ------------- |
165
+ | Category | Technologies |
166
+ | ------------ | ----------------------------------------------------------- |
165
167
  | **Frontend** | Next.js 16, React 19, TypeScript, Tailwind CSS 4, next-intl |
166
- | **Backend** | Fastify 5, TypeScript, MongoDB, Mongoose, Pino |
167
- | **Auth** | @fastify/jwt, bcrypt |
168
- | **Testing** | Jest, Supertest |
169
- | **Tooling** | pnpm, ESLint, Prettier, TypeScript |
170
- | **Docs** | Swagger/OpenAPI, JSDoc |
168
+ | **Backend** | Fastify 5, TypeScript, MongoDB, Mongoose, Pino |
169
+ | **Auth** | @fastify/jwt, bcrypt |
170
+ | **Testing** | Jest, Supertest |
171
+ | **Tooling** | pnpm, ESLint, Prettier, TypeScript |
172
+ | **Docs** | Swagger/OpenAPI, JSDoc |
171
173
 
172
174
  ## Requirements
173
175
 
package/bin/cli.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { Command } from 'commander';
4
- import prompts from 'prompts';
5
- import chalk from 'chalk';
6
- import ora from 'ora';
7
- import fs from 'fs-extra';
8
- import path from 'path';
9
- import { fileURLToPath } from 'url';
10
- import { execSync } from 'child_process';
3
+ import { Command } from "commander";
4
+ import prompts from "prompts";
5
+ import chalk from "chalk";
6
+ import ora from "ora";
7
+ import fs from "fs-extra";
8
+ import path from "path";
9
+ import { fileURLToPath } from "url";
10
+ import { execSync } from "child_process";
11
11
 
12
12
  const __filename = fileURLToPath(import.meta.url);
13
13
  const __dirname = path.dirname(__filename);
@@ -15,31 +15,34 @@ const __dirname = path.dirname(__filename);
15
15
  const program = new Command();
16
16
 
17
17
  program
18
- .name('create-pnpm-custom-app')
19
- .description('Scaffold a professional full-stack monorepo with Next.js, Fastify, and pnpm workspaces')
20
- .version('1.0.0')
21
- .argument('[project-name]', 'Name of the project')
18
+ .name("create-pnpm-custom-app")
19
+ .description(
20
+ "Scaffold a professional full-stack monorepo with Next.js, Fastify, and pnpm workspaces",
21
+ )
22
+ .version("1.0.0")
23
+ .argument("[project-name]", "Name of the project")
22
24
  .parse(process.argv);
23
25
 
24
26
  const args = program.args;
25
27
 
26
28
  async function main() {
27
- console.log(chalk.bold.cyan('\n🚀 Create PNPM Custom App\n'));
29
+ console.log(chalk.bold.cyan("\n🚀 Create PNPM Custom App\n"));
28
30
 
29
31
  let projectName = args[0];
30
- let githubUser = '';
32
+ let githubUser = "";
31
33
 
32
34
  if (!projectName) {
33
35
  const response = await prompts({
34
- type: 'text',
35
- name: 'projectName',
36
- message: 'What is your project name?',
37
- initial: 'my-app',
38
- validate: (value) => (value.length > 0 ? true : 'Project name is required'),
36
+ type: "text",
37
+ name: "projectName",
38
+ message: "What is your project name?",
39
+ initial: "my-app",
40
+ validate: (value) =>
41
+ value.length > 0 ? true : "Project name is required",
39
42
  });
40
43
 
41
44
  if (!response.projectName) {
42
- console.log(chalk.red('\n❌ Project creation cancelled.'));
45
+ console.log(chalk.red("\n❌ Project creation cancelled."));
43
46
  process.exit(1);
44
47
  }
45
48
 
@@ -48,9 +51,10 @@ async function main() {
48
51
 
49
52
  // Ask for GitHub username
50
53
  const githubResponse = await prompts({
51
- type: 'text',
52
- name: 'githubUser',
53
- message: 'What is your GitHub username? (optional, used only for repository links)',
54
+ type: "text",
55
+ name: "githubUser",
56
+ message:
57
+ "What is your GitHub username? (optional, used only for repository links)",
54
58
  initial: projectName,
55
59
  validate: (value) => true, // Optional field
56
60
  });
@@ -66,43 +70,49 @@ async function main() {
66
70
 
67
71
  console.log(chalk.gray(`\n📁 Creating project at: ${projectPath}\n`));
68
72
 
69
- const spinner = ora('Creating project structure...').start();
73
+ const spinner = ora("Creating project structure...").start();
70
74
 
71
75
  try {
72
76
  // Create project directory
73
77
  fs.mkdirSync(projectPath, { recursive: true });
74
78
 
75
79
  // Copy all templates
76
- const templatesDir = path.join(__dirname, '../templates');
80
+ const templatesDir = path.join(__dirname, "../templates");
77
81
  fs.copySync(templatesDir, projectPath);
78
82
 
79
83
  // Rename gitignore files (npm publish removes .gitignore)
80
- const gitignoreRootPath = path.join(projectPath, 'gitignore-root');
84
+ const gitignoreRootPath = path.join(projectPath, "gitignore-root");
81
85
  if (fs.existsSync(gitignoreRootPath)) {
82
- fs.renameSync(gitignoreRootPath, path.join(projectPath, '.gitignore'));
86
+ fs.renameSync(gitignoreRootPath, path.join(projectPath, ".gitignore"));
83
87
  }
84
88
 
85
- const webGitignorePath = path.join(projectPath, 'apps/web/gitignore');
89
+ const webGitignorePath = path.join(projectPath, "apps/web/gitignore");
86
90
  if (fs.existsSync(webGitignorePath)) {
87
- fs.renameSync(webGitignorePath, path.join(projectPath, 'apps/web/.gitignore'));
91
+ fs.renameSync(
92
+ webGitignorePath,
93
+ path.join(projectPath, "apps/web/.gitignore"),
94
+ );
88
95
  }
89
96
 
90
- const apiGitignorePath = path.join(projectPath, 'apps/api/gitignore');
97
+ const apiGitignorePath = path.join(projectPath, "apps/api/gitignore");
91
98
  if (fs.existsSync(apiGitignorePath)) {
92
- fs.renameSync(apiGitignorePath, path.join(projectPath, 'apps/api/.gitignore'));
99
+ fs.renameSync(
100
+ apiGitignorePath,
101
+ path.join(projectPath, "apps/api/.gitignore"),
102
+ );
93
103
  }
94
104
 
95
105
  // Replace project name in package.json files
96
106
  const packageJsonPaths = [
97
- path.join(projectPath, 'package.json'),
98
- path.join(projectPath, 'apps/web/package.json'),
99
- path.join(projectPath, 'apps/api/package.json'),
100
- path.join(projectPath, 'packages/shared/package.json'),
107
+ path.join(projectPath, "package.json"),
108
+ path.join(projectPath, "apps/web/package.json"),
109
+ path.join(projectPath, "apps/api/package.json"),
110
+ path.join(projectPath, "packages/shared/package.json"),
101
111
  ];
102
112
 
103
113
  packageJsonPaths.forEach((pkgPath) => {
104
114
  if (fs.existsSync(pkgPath)) {
105
- const content = fs.readFileSync(pkgPath, 'utf-8');
115
+ const content = fs.readFileSync(pkgPath, "utf-8");
106
116
  const updated = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
107
117
  fs.writeFileSync(pkgPath, updated);
108
118
  }
@@ -110,76 +120,97 @@ async function main() {
110
120
 
111
121
  // Replace project name in all other files (tsx, ts, md, json, etc.)
112
122
  const filesToReplace = [
113
- path.join(projectPath, 'README.md'),
114
- path.join(projectPath, 'CONTRIBUTING.md'),
115
- path.join(projectPath, 'apps/web/app/layout.tsx'),
116
- path.join(projectPath, 'apps/web/app/manifest.json'),
117
- path.join(projectPath, 'apps/web/app/components/layout/Nav.component.tsx'),
118
- path.join(projectPath, 'apps/web/app/components/layout/Footer.component.tsx'),
119
- path.join(projectPath, 'apps/web/app/[locale]/(routes)/page.tsx'),
120
- path.join(projectPath, 'apps/web/app/opengraph-image.tsx'),
121
- path.join(projectPath, 'apps/web/app/twitter-image.tsx'),
122
- path.join(projectPath, 'apps/web/app/apple-icon.tsx'),
123
- path.join(projectPath, 'apps/web/app/icon.tsx'),
124
- path.join(projectPath, 'apps/web/messages/en.json'),
125
- path.join(projectPath, 'apps/web/messages/es.json'),
126
- path.join(projectPath, 'apps/api/src/app.ts'),
127
- path.join(projectPath, 'packages/shared/src/index.ts'),
128
- path.join(projectPath, '.github/copilot-instructions.md'),
123
+ path.join(projectPath, "README.md"),
124
+ path.join(projectPath, "CONTRIBUTING.md"),
125
+ path.join(projectPath, "apps/web/app/layout.tsx"),
126
+ path.join(projectPath, "apps/web/app/manifest.json"),
127
+ path.join(
128
+ projectPath,
129
+ "apps/web/app/components/layout/Nav.component.tsx",
130
+ ),
131
+ path.join(
132
+ projectPath,
133
+ "apps/web/app/components/layout/Footer.component.tsx",
134
+ ),
135
+ path.join(projectPath, "apps/web/app/[locale]/(routes)/page.tsx"),
136
+ path.join(projectPath, "apps/web/app/opengraph-image.tsx"),
137
+ path.join(projectPath, "apps/web/app/twitter-image.tsx"),
138
+ path.join(projectPath, "apps/web/app/apple-icon.tsx"),
139
+ path.join(projectPath, "apps/web/app/icon.tsx"),
140
+ path.join(projectPath, "apps/web/messages/en.json"),
141
+ path.join(projectPath, "apps/web/messages/es.json"),
142
+ path.join(projectPath, "apps/api/src/app.ts"),
143
+ path.join(projectPath, "packages/shared/src/index.ts"),
144
+ path.join(projectPath, ".github/copilot-instructions.md"),
129
145
  ];
130
146
 
131
147
  filesToReplace.forEach((filePath) => {
132
148
  if (fs.existsSync(filePath)) {
133
- let content = fs.readFileSync(filePath, 'utf-8');
149
+ let content = fs.readFileSync(filePath, "utf-8");
134
150
  content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
135
151
  content = content.replace(/\{\{GITHUB_USER\}\}/g, githubUser);
136
152
  fs.writeFileSync(filePath, content);
137
153
  }
138
154
  });
139
155
 
140
- spinner.succeed(chalk.green('Project structure created!'));
156
+ spinner.succeed(chalk.green("Project structure created!"));
141
157
 
142
158
  // Install dependencies
143
- spinner.start('Installing dependencies with pnpm... This may take a few minutes.');
144
-
159
+ spinner.start(
160
+ "Installing dependencies with pnpm... This may take a few minutes.",
161
+ );
162
+
145
163
  try {
146
- execSync('pnpm install', { cwd: projectPath, stdio: 'ignore' });
147
- spinner.succeed(chalk.green('Dependencies installed successfully!'));
164
+ execSync("pnpm install", { cwd: projectPath, stdio: "ignore" });
165
+ spinner.succeed(chalk.green("Dependencies installed successfully!"));
148
166
  } catch (error) {
149
- spinner.warn(chalk.yellow('Could not install dependencies automatically.'));
150
- console.log(chalk.gray(' Run `pnpm install` manually inside the project.'));
167
+ spinner.warn(
168
+ chalk.yellow("Could not install dependencies automatically."),
169
+ );
170
+ console.log(
171
+ chalk.gray(" Run `pnpm install` manually inside the project."),
172
+ );
151
173
  }
152
174
 
153
175
  // Success message
154
- console.log(chalk.bold.green(`\n✅ Project "${projectName}" created successfully!\n`));
155
- console.log(chalk.cyan('📦 Project structure:'));
156
- console.log(chalk.gray(' ├── apps/'));
157
- console.log(chalk.gray(' │ ├── web/ (Next.js 16 + TypeScript + Tailwind 4)'));
158
- console.log(chalk.gray(' │ └── api/ (Fastify + MongoDB + TypeScript)'));
159
- console.log(chalk.gray(' ├── packages/'));
160
- console.log(chalk.gray(' │ └── shared/ (Shared types and interfaces)'));
161
- console.log(chalk.gray(' └── docs/ (Project documentation)\n'));
162
-
163
- console.log(chalk.cyan('🚀 Next steps:'));
176
+ console.log(
177
+ chalk.bold.green(`\n✅ Project "${projectName}" created successfully!\n`),
178
+ );
179
+ console.log(chalk.cyan("📦 Project structure:"));
180
+ console.log(chalk.gray(" ├── apps/"));
181
+ console.log(
182
+ chalk.gray(
183
+ " │ ├── web/ (Next.js 16 + TypeScript + Tailwind 4)",
184
+ ),
185
+ );
186
+ console.log(
187
+ chalk.gray(" │ └── api/ (Fastify + MongoDB + TypeScript)"),
188
+ );
189
+ console.log(chalk.gray(" ├── packages/"));
190
+ console.log(
191
+ chalk.gray(" │ └── shared/ (Shared types and interfaces)"),
192
+ );
193
+ console.log(chalk.gray(" └── docs/ (Project documentation)\n"));
194
+
195
+ console.log(chalk.cyan("🚀 Next steps:"));
164
196
  console.log(chalk.white(` 1. cd ${projectName}`));
165
- console.log(chalk.white(' 2. Copy .env.example files and configure:'));
166
- console.log(chalk.gray(' - apps/web/.env.local'));
167
- console.log(chalk.gray(' - apps/api/.env'));
168
- console.log(chalk.white(' 3. Start development:'));
169
- console.log(chalk.gray(' - Frontend: pnpm --filter web dev'));
170
- console.log(chalk.gray(' - Backend: pnpm --filter api dev'));
171
- console.log(chalk.white(' 4. Read the README.md for more information\n'));
172
-
173
- console.log(chalk.bold.magenta('Happy coding! 🎉\n'));
174
-
197
+ console.log(chalk.white(" 2. Copy .env.example files and configure:"));
198
+ console.log(chalk.gray(" - apps/web/.env.local"));
199
+ console.log(chalk.gray(" - apps/api/.env"));
200
+ console.log(chalk.white(" 3. Start development:"));
201
+ console.log(chalk.gray(" - Frontend: pnpm --filter web dev"));
202
+ console.log(chalk.gray(" - Backend: pnpm --filter api dev"));
203
+ console.log(chalk.white(" 4. Read the README.md for more information\n"));
204
+
205
+ console.log(chalk.bold.magenta("Happy coding! 🎉\n"));
175
206
  } catch (error) {
176
- spinner.fail(chalk.red('Failed to create project'));
207
+ spinner.fail(chalk.red("Failed to create project"));
177
208
  console.error(error);
178
209
  process.exit(1);
179
210
  }
180
211
  }
181
212
 
182
213
  main().catch((error) => {
183
- console.error(chalk.red('An error occurred:'), error);
214
+ console.error(chalk.red("An error occurred:"), error);
184
215
  process.exit(1);
185
216
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pnpm-custom-app",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "A professional CLI to scaffold full-stack monorepo projects with Next.js, Fastify, and pnpm workspaces",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,12 +26,20 @@
26
26
  "node": ">=20.0.0"
27
27
  },
28
28
  "dependencies": {
29
- "chalk": "^5.3.0",
30
- "commander": "^12.1.0",
31
- "fs-extra": "^11.2.0",
32
- "ora": "^8.1.1",
29
+ "chalk": "^5.6.2",
30
+ "commander": "^14.0.2",
31
+ "fs-extra": "^11.3.3",
32
+ "ora": "^9.1.0",
33
33
  "prompts": "^2.4.2"
34
34
  },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/pelayotrives/create-pnpm-custom-app"
38
+ },
39
+ "homepage": "https://github.com/pelayotrives/create-pnpm-custom-app#readme",
40
+ "bugs": {
41
+ "url": "https://github.com/pelayotrives/create-pnpm-custom-app/issues"
42
+ },
35
43
  "files": [
36
44
  "bin",
37
45
  "templates"
@@ -5,6 +5,7 @@
5
5
  [Brief description of your project]
6
6
 
7
7
  **Tech Stack:**
8
+
8
9
  - **Frontend**: Next.js 16+ (App Router), React 19+, TypeScript
9
10
  - **Backend**: Fastify API with MongoDB
10
11
  - **Monorepo**: pnpm workspace (apps/web, apps/api, packages/shared)
@@ -77,7 +77,7 @@ pnpm --filter api test:watch
77
77
 
78
78
  We follow [Conventional Commits](https://www.conventionalcommits.org/):
79
79
 
80
- ```
80
+ ```txt
81
81
  <type>(<scope>): <subject>
82
82
 
83
83
  <body>
@@ -108,11 +108,13 @@ docs(readme): update installation instructions
108
108
  1. **Update documentation** - Update README.md if needed
109
109
  2. **Add tests** - Ensure new features have test coverage
110
110
  3. **Run quality checks**:
111
+
111
112
  ```bash
112
113
  pnpm run lint
113
114
  pnpm run build
114
115
  pnpm run test
115
116
  ```
117
+
116
118
  4. **Create pull request** - Use a clear title and description
117
119
  5. **Link issues** - Reference related issues in the PR description
118
120
  6. **Wait for review** - Address feedback from maintainers
@@ -121,18 +123,22 @@ docs(readme): update installation instructions
121
123
 
122
124
  ```markdown
123
125
  ## Description
126
+
124
127
  Brief description of changes
125
128
 
126
129
  ## Type of Change
130
+
127
131
  - [ ] Bug fix
128
132
  - [ ] New feature
129
133
  - [ ] Breaking change
130
134
  - [ ] Documentation update
131
135
 
132
136
  ## Testing
137
+
133
138
  Describe the tests you ran
134
139
 
135
140
  ## Checklist
141
+
136
142
  - [ ] Code follows project style guidelines
137
143
  - [ ] Self-review completed
138
144
  - [ ] Comments added for complex code
@@ -177,6 +183,7 @@ Describe the tests you ran
177
183
  ## Questions?
178
184
 
179
185
  If you have questions, please:
186
+
180
187
  1. Check existing documentation
181
188
  2. Search existing issues
182
189
  3. Create a new issue with the `question` label
@@ -281,10 +281,10 @@ pnpm --filter api test:coverage # With coverage
281
281
  Example test structure:
282
282
 
283
283
  ```typescript
284
- import { describe, it, expect } from '@jest/globals';
284
+ import { describe, it, expect } from "@jest/globals";
285
285
 
286
- describe('Feature Name', () => {
287
- it('should do something', () => {
286
+ describe("Feature Name", () => {
287
+ it("should do something", () => {
288
288
  expect(true).toBe(true);
289
289
  });
290
290
  });
@@ -13,35 +13,35 @@
13
13
  "lint": "eslint src --ext .ts"
14
14
  },
15
15
  "dependencies": {
16
- "@fastify/cors": "^10.0.1",
17
- "@fastify/jwt": "^9.0.1",
18
- "@fastify/multipart": "^9.0.1",
19
- "@fastify/rate-limit": "^10.1.1",
20
- "@fastify/swagger": "^9.3.0",
21
- "@fastify/swagger-ui": "^5.0.1",
16
+ "@fastify/cors": "^11.2.0",
17
+ "@fastify/jwt": "^10.0.0",
18
+ "@fastify/multipart": "^9.4.0",
19
+ "@fastify/rate-limit": "^10.3.0",
20
+ "@fastify/swagger": "^9.6.1",
21
+ "@fastify/swagger-ui": "^5.2.4",
22
22
  "@{{PROJECT_NAME}}/shared": "workspace:*",
23
- "bcrypt": "^5.1.1",
24
- "dotenv": "^16.4.7",
25
- "fastify": "^5.2.0",
26
- "mongoose": "^8.9.3",
27
- "pino": "^9.5.0",
28
- "pino-pretty": "^13.0.0"
23
+ "bcrypt": "^6.0.0",
24
+ "dotenv": "^17.2.3",
25
+ "fastify": "^5.7.2",
26
+ "mongoose": "^9.1.5",
27
+ "pino": "^10.3.0",
28
+ "pino-pretty": "^13.1.3"
29
29
  },
30
30
  "devDependencies": {
31
- "@eslint/js": "^9.18.0",
32
- "@jest/globals": "^29.7.0",
33
- "@types/bcrypt": "^5.0.2",
34
- "@types/jest": "^29.5.14",
35
- "@types/node": "^22.10.5",
36
- "@types/supertest": "^6.0.2",
37
- "eslint": "^9.18.0",
38
- "jest": "^29.7.0",
39
- "supertest": "^7.0.0",
40
- "ts-jest": "^29.2.5",
31
+ "@eslint/js": "^9.39.2",
32
+ "@jest/globals": "^30.2.0",
33
+ "@types/bcrypt": "^6.0.0",
34
+ "@types/jest": "^30.0.0",
35
+ "@types/node": "^22.19.7",
36
+ "@types/supertest": "^6.0.3",
37
+ "eslint": "^9.39.2",
38
+ "jest": "^30.2.0",
39
+ "supertest": "^7.2.2",
40
+ "ts-jest": "^29.4.6",
41
41
  "ts-node": "^10.9.2",
42
- "tsx": "^4.19.2",
43
- "typescript": "^5.7.3",
44
- "typescript-eslint": "^8.21.0"
42
+ "tsx": "^4.21.0",
43
+ "typescript": "^5.9.3",
44
+ "typescript-eslint": "^8.54.0"
45
45
  },
46
46
  "engines": {
47
47
  "node": ">=20.0.0"
@@ -30,7 +30,7 @@ function registerJWT(app: FastifyInstance) {
30
30
  'JWT_SECRET is not defined. Please copy apps/api/.env.example to apps/api/.env and set JWT_SECRET'
31
31
  );
32
32
  }
33
-
33
+
34
34
  app.register(jwt, {
35
35
  secret: config.jwtSecret,
36
36
  sign: {
@@ -46,7 +46,7 @@ function registerRateLimit(app: FastifyInstance) {
46
46
  if (config.env === 'test') {
47
47
  return;
48
48
  }
49
-
49
+
50
50
  app.register(rateLimit, {
51
51
  global: false,
52
52
  });
@@ -70,9 +70,7 @@ async function registerSwagger(app: FastifyInstance) {
70
70
  description: 'Development server',
71
71
  },
72
72
  ],
73
- tags: [
74
- { name: 'Health', description: 'Health check endpoints' },
75
- ],
73
+ tags: [{ name: 'Health', description: 'Health check endpoints' }],
76
74
  },
77
75
  });
78
76
 
@@ -99,7 +97,7 @@ function registerMultipart(app: FastifyInstance) {
99
97
 
100
98
  /**
101
99
  * Builds and configures the Fastify application
102
- *
100
+ *
103
101
  * @returns Configured Fastify instance
104
102
  */
105
103
  export async function buildApp(): Promise<FastifyInstance> {
@@ -15,12 +15,12 @@ const env = process.env.NODE_ENV ?? 'development';
15
15
 
16
16
  /**
17
17
  * Application configuration loaded from environment variables
18
- *
18
+ *
19
19
  * @remarks
20
20
  * Required environment variables:
21
21
  * - MONGODB_URI: MongoDB connection string
22
22
  * - JWT_SECRET: Secret for JWT token signing
23
- *
23
+ *
24
24
  * Optional environment variables:
25
25
  * - PORT: Server port (default: 3002)
26
26
  * - CORS_ORIGIN: Allowed CORS origin (default: *)
@@ -3,9 +3,9 @@ import pino from 'pino';
3
3
 
4
4
  /**
5
5
  * Returns logger configuration based on environment
6
- *
6
+ *
7
7
  * @returns Logger config or false to disable logging in test
8
- *
8
+ *
9
9
  * @remarks
10
10
  * - Test/E2E: Logging disabled
11
11
  * - Production: Structured JSON logs at 'info' level
@@ -46,7 +46,7 @@ export function getLoggerConfig(): false | object {
46
46
 
47
47
  /**
48
48
  * Application-wide logger instance using Pino
49
- *
49
+ *
50
50
  * @example
51
51
  * ```typescript
52
52
  * logger.info({ userId: '123' }, 'User logged in');
@@ -2,11 +2,11 @@ import mongoose from 'mongoose';
2
2
 
3
3
  /**
4
4
  * Establishes connection to MongoDB
5
- *
5
+ *
6
6
  * @param uri - MongoDB connection URI
7
7
  * @returns Mongoose connection object
8
8
  * @throws Error if connection fails
9
- *
9
+ *
10
10
  * @example
11
11
  * ```typescript
12
12
  * await connectMongo('mongodb://localhost:27017/mydb');
@@ -20,7 +20,7 @@ export async function connectMongo(uri: string) {
20
20
 
21
21
  /**
22
22
  * Disconnects from MongoDB
23
- *
23
+ *
24
24
  * @remarks
25
25
  * Call this when shutting down the application or when database
26
26
  * connection is no longer needed.
@@ -4,14 +4,14 @@ import config from './config/config.js';
4
4
 
5
5
  /**
6
6
  * Starts the API server
7
- *
7
+ *
8
8
  * - Connects to MongoDB
9
9
  * - Starts the Fastify server on configured port
10
10
  * - Logs server URL when ready
11
11
  */
12
12
  async function start() {
13
13
  const app = await buildApp();
14
-
14
+
15
15
  try {
16
16
  if (!config.mongoUri) {
17
17
  app.log.error(