servcraft 0.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 (106) hide show
  1. package/.dockerignore +45 -0
  2. package/.env.example +46 -0
  3. package/.husky/commit-msg +1 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc +11 -0
  7. package/Dockerfile +76 -0
  8. package/Dockerfile.dev +31 -0
  9. package/README.md +232 -0
  10. package/commitlint.config.js +24 -0
  11. package/dist/cli/index.cjs +3968 -0
  12. package/dist/cli/index.cjs.map +1 -0
  13. package/dist/cli/index.d.cts +1 -0
  14. package/dist/cli/index.d.ts +1 -0
  15. package/dist/cli/index.js +3945 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/index.cjs +2458 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +828 -0
  20. package/dist/index.d.ts +828 -0
  21. package/dist/index.js +2332 -0
  22. package/dist/index.js.map +1 -0
  23. package/docker-compose.prod.yml +118 -0
  24. package/docker-compose.yml +147 -0
  25. package/eslint.config.js +27 -0
  26. package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
  27. package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
  28. package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
  29. package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
  30. package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +5 -0
  31. package/npm-cache/_update-notifier-last-checked +0 -0
  32. package/package.json +112 -0
  33. package/prisma/schema.prisma +157 -0
  34. package/src/cli/commands/add-module.ts +422 -0
  35. package/src/cli/commands/db.ts +137 -0
  36. package/src/cli/commands/docs.ts +16 -0
  37. package/src/cli/commands/generate.ts +459 -0
  38. package/src/cli/commands/init.ts +640 -0
  39. package/src/cli/index.ts +32 -0
  40. package/src/cli/templates/controller.ts +67 -0
  41. package/src/cli/templates/dynamic-prisma.ts +89 -0
  42. package/src/cli/templates/dynamic-schemas.ts +232 -0
  43. package/src/cli/templates/dynamic-types.ts +60 -0
  44. package/src/cli/templates/module-index.ts +33 -0
  45. package/src/cli/templates/prisma-model.ts +17 -0
  46. package/src/cli/templates/repository.ts +104 -0
  47. package/src/cli/templates/routes.ts +70 -0
  48. package/src/cli/templates/schemas.ts +26 -0
  49. package/src/cli/templates/service.ts +58 -0
  50. package/src/cli/templates/types.ts +27 -0
  51. package/src/cli/utils/docs-generator.ts +47 -0
  52. package/src/cli/utils/field-parser.ts +315 -0
  53. package/src/cli/utils/helpers.ts +89 -0
  54. package/src/config/env.ts +80 -0
  55. package/src/config/index.ts +97 -0
  56. package/src/core/index.ts +5 -0
  57. package/src/core/logger.ts +43 -0
  58. package/src/core/server.ts +132 -0
  59. package/src/database/index.ts +7 -0
  60. package/src/database/prisma.ts +54 -0
  61. package/src/database/seed.ts +59 -0
  62. package/src/index.ts +63 -0
  63. package/src/middleware/error-handler.ts +73 -0
  64. package/src/middleware/index.ts +3 -0
  65. package/src/middleware/security.ts +116 -0
  66. package/src/modules/audit/audit.service.ts +192 -0
  67. package/src/modules/audit/index.ts +2 -0
  68. package/src/modules/audit/types.ts +37 -0
  69. package/src/modules/auth/auth.controller.ts +182 -0
  70. package/src/modules/auth/auth.middleware.ts +87 -0
  71. package/src/modules/auth/auth.routes.ts +123 -0
  72. package/src/modules/auth/auth.service.ts +142 -0
  73. package/src/modules/auth/index.ts +49 -0
  74. package/src/modules/auth/schemas.ts +52 -0
  75. package/src/modules/auth/types.ts +69 -0
  76. package/src/modules/email/email.service.ts +212 -0
  77. package/src/modules/email/index.ts +10 -0
  78. package/src/modules/email/templates.ts +213 -0
  79. package/src/modules/email/types.ts +57 -0
  80. package/src/modules/swagger/index.ts +3 -0
  81. package/src/modules/swagger/schema-builder.ts +263 -0
  82. package/src/modules/swagger/swagger.service.ts +169 -0
  83. package/src/modules/swagger/types.ts +68 -0
  84. package/src/modules/user/index.ts +30 -0
  85. package/src/modules/user/schemas.ts +49 -0
  86. package/src/modules/user/types.ts +78 -0
  87. package/src/modules/user/user.controller.ts +139 -0
  88. package/src/modules/user/user.repository.ts +156 -0
  89. package/src/modules/user/user.routes.ts +199 -0
  90. package/src/modules/user/user.service.ts +145 -0
  91. package/src/modules/validation/index.ts +18 -0
  92. package/src/modules/validation/validator.ts +104 -0
  93. package/src/types/common.ts +61 -0
  94. package/src/types/index.ts +10 -0
  95. package/src/utils/errors.ts +66 -0
  96. package/src/utils/index.ts +33 -0
  97. package/src/utils/pagination.ts +38 -0
  98. package/src/utils/response.ts +63 -0
  99. package/tests/integration/auth.test.ts +59 -0
  100. package/tests/setup.ts +17 -0
  101. package/tests/unit/modules/validation.test.ts +88 -0
  102. package/tests/unit/utils/errors.test.ts +113 -0
  103. package/tests/unit/utils/pagination.test.ts +82 -0
  104. package/tsconfig.json +33 -0
  105. package/tsup.config.ts +14 -0
  106. package/vitest.config.ts +34 -0
@@ -0,0 +1,5 @@
1
+
2
+ d6bee00ec15bdca9908a56fdc20675409c9ed366 {"key":"pacote:tarball:file:/mnt/d4169edb-8c9f-4e15-9e9d-358608611b0c/Projetcts/Node.js/servcraft","integrity":"sha512-HNADRA1QCgSHYhqtHWQCl4NAaYl2YCBG244k+gPAHubAIsabBYL5aQQtlELuh2rDXAOOlg3UJ9HmIvokuOt9ug==","time":1766018965337,"size":220811}
3
+ 9b065a6b5dd7776f840aa247a52a96b098d3280b {"key":"pacote:tarball:file:/mnt/d4169edb-8c9f-4e15-9e9d-358608611b0c/Projetcts/Node.js/servcraft","integrity":"sha512-4BLzYNyTFe5fF4RKDIwjPua/fDCDfEoC6g1Wxhx/erIcDpWOUO0sV8WfmDx2K5MFZ3jJAJsjmP/Cbe8Bg5mbEw==","time":1766019082154,"size":443837}
4
+ 793f695e6f45c908cbf359d9bb41e39e78c1827f {"key":"pacote:tarball:file:/mnt/d4169edb-8c9f-4e15-9e9d-358608611b0c/Projetcts/Node.js/servcraft","integrity":"sha512-7bD64RYZAomPTJE8Z9f2zfa+BmWuw7OJucT08KEByh2lm63xtZxOADD1IjAjuNY8/lAcRqMsIMiV1Ps/EcoiMg==","time":1766019259320,"size":888564}
5
+ d817e3cb064934d074e652dfa4e82c5a29df0bda {"key":"pacote:tarball:file:/mnt/d4169edb-8c9f-4e15-9e9d-358608611b0c/Projetcts/Node.js/servcraft","integrity":"sha512-QlUotJPKSRgz5aqw6cMQjSmrPzbCSMqI9F1GMGdPzpEwlZ5WrjCHl6wrYyj6fwmmELlVDtCcuXHQOYdtKT/GnQ==","time":1766019329526,"size":1777318}
File without changes
package/package.json ADDED
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "servcraft",
3
+ "version": "0.1.0",
4
+ "description": "A modular, production-ready Node.js backend framework",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "servcraft": "dist/cli/index.js"
9
+ },
10
+ "scripts": {
11
+ "dev": "tsx watch src/index.ts",
12
+ "build": "tsup",
13
+ "start": "node dist/index.js",
14
+ "test": "vitest",
15
+ "test:coverage": "vitest --coverage",
16
+ "lint": "eslint src --ext .ts",
17
+ "lint:fix": "eslint src --ext .ts --fix",
18
+ "format": "prettier --write \"src/**/*.ts\"",
19
+ "prepare": "command -v husky >/dev/null 2>&1 && husky install || echo \"husky not installed, skipping hooks\"",
20
+ "typecheck": "tsc --noEmit",
21
+ "docs": "tsx src/cli/index.ts docs",
22
+ "prepublishOnly": "npm run build",
23
+ "db:generate": "prisma generate",
24
+ "db:migrate": "prisma migrate dev",
25
+ "db:push": "prisma db push",
26
+ "db:studio": "prisma studio",
27
+ "db:seed": "tsx src/database/seed.ts"
28
+ },
29
+ "prisma": {
30
+ "seed": "tsx src/database/seed.ts"
31
+ },
32
+ "keywords": [
33
+ "backend",
34
+ "framework",
35
+ "nodejs",
36
+ "fastify",
37
+ "typescript",
38
+ "api",
39
+ "rest",
40
+ "modular",
41
+ "authentication",
42
+ "jwt"
43
+ ],
44
+ "author": "Le-Sourcier",
45
+ "license": "MIT",
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ },
49
+ "dependencies": {
50
+ "@fastify/cors": "^9.0.1",
51
+ "@fastify/helmet": "^11.1.1",
52
+ "@fastify/jwt": "^8.0.1",
53
+ "@fastify/rate-limit": "^9.1.0",
54
+ "@fastify/cookie": "^9.3.1",
55
+ "@fastify/swagger": "^8.15.0",
56
+ "@fastify/swagger-ui": "^4.1.0",
57
+ "@prisma/client": "^5.22.0",
58
+ "fastify": "^4.28.1",
59
+ "pino": "^9.5.0",
60
+ "pino-pretty": "^11.3.0",
61
+ "zod": "^3.23.8",
62
+ "bcryptjs": "^2.4.3",
63
+ "dotenv": "^16.4.5",
64
+ "nodemailer": "^6.9.15",
65
+ "handlebars": "^4.7.8",
66
+ "commander": "^12.1.0",
67
+ "chalk": "^5.3.0",
68
+ "inquirer": "^12.1.0",
69
+ "ora": "^8.1.1"
70
+ },
71
+ "devDependencies": {
72
+ "@types/node": "^22.10.1",
73
+ "@types/bcryptjs": "^2.4.6",
74
+ "@types/nodemailer": "^6.4.17",
75
+ "@typescript-eslint/eslint-plugin": "^8.17.0",
76
+ "@typescript-eslint/parser": "^8.17.0",
77
+ "eslint": "^9.16.0",
78
+ "eslint-config-prettier": "^9.1.0",
79
+ "eslint-plugin-prettier": "^5.2.1",
80
+ "prettier": "^3.4.2",
81
+ "husky": "^9.1.7",
82
+ "lint-staged": "^15.2.10",
83
+ "typescript": "^5.7.2",
84
+ "tsx": "^4.19.2",
85
+ "tsup": "^8.3.5",
86
+ "vitest": "^2.1.8",
87
+ "@vitest/coverage-v8": "^2.1.8",
88
+ "prisma": "^5.22.0",
89
+ "@commitlint/cli": "^19.6.0",
90
+ "@commitlint/config-conventional": "^19.6.0",
91
+ "typescript-eslint": "^8.17.0",
92
+ "@eslint/js": "^9.16.0"
93
+ },
94
+ "type": "module",
95
+ "lint-staged": {
96
+ "*.ts": [
97
+ "eslint --fix",
98
+ "prettier --write"
99
+ ]
100
+ },
101
+ "directories": {
102
+ "test": "tests"
103
+ },
104
+ "repository": {
105
+ "type": "git",
106
+ "url": "git+https://github.com/Le-Sourcier/servcraft.git"
107
+ },
108
+ "bugs": {
109
+ "url": "https://github.com/Le-Sourcier/servcraft/issues"
110
+ },
111
+ "homepage": "https://github.com/Le-Sourcier/servcraft#readme"
112
+ }
@@ -0,0 +1,157 @@
1
+ // Servcraft - Prisma Schema
2
+ // Supports: PostgreSQL, MySQL, SQLite
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ }
7
+
8
+ datasource db {
9
+ provider = env("DATABASE_PROVIDER") // "postgresql", "mysql", or "sqlite"
10
+ url = env("DATABASE_URL")
11
+ }
12
+
13
+ // ==========================================
14
+ // USER & AUTHENTICATION
15
+ // ==========================================
16
+
17
+ model User {
18
+ id String @id @default(uuid())
19
+ email String @unique
20
+ password String
21
+ name String?
22
+ role UserRole @default(USER)
23
+ status UserStatus @default(ACTIVE)
24
+ emailVerified Boolean @default(false)
25
+ lastLoginAt DateTime?
26
+ metadata Json?
27
+
28
+ createdAt DateTime @default(now())
29
+ updatedAt DateTime @updatedAt
30
+
31
+ // Relations
32
+ refreshTokens RefreshToken[]
33
+ sessions Session[]
34
+ auditLogs AuditLog[]
35
+
36
+ @@index([email])
37
+ @@index([status])
38
+ @@index([role])
39
+ @@map("users")
40
+ }
41
+
42
+ enum UserRole {
43
+ USER
44
+ MODERATOR
45
+ ADMIN
46
+ SUPER_ADMIN
47
+ }
48
+
49
+ enum UserStatus {
50
+ ACTIVE
51
+ INACTIVE
52
+ SUSPENDED
53
+ BANNED
54
+ }
55
+
56
+ model RefreshToken {
57
+ id String @id @default(uuid())
58
+ token String @unique
59
+ userId String
60
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
61
+ expiresAt DateTime
62
+ createdAt DateTime @default(now())
63
+
64
+ @@index([userId])
65
+ @@index([token])
66
+ @@map("refresh_tokens")
67
+ }
68
+
69
+ model Session {
70
+ id String @id @default(uuid())
71
+ userId String
72
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
73
+ userAgent String?
74
+ ipAddress String?
75
+ expiresAt DateTime
76
+ createdAt DateTime @default(now())
77
+
78
+ @@index([userId])
79
+ @@map("sessions")
80
+ }
81
+
82
+ // ==========================================
83
+ // PASSWORD RESET
84
+ // ==========================================
85
+
86
+ model PasswordReset {
87
+ id String @id @default(uuid())
88
+ email String
89
+ token String @unique
90
+ expiresAt DateTime
91
+ used Boolean @default(false)
92
+ createdAt DateTime @default(now())
93
+
94
+ @@index([email])
95
+ @@index([token])
96
+ @@map("password_resets")
97
+ }
98
+
99
+ // ==========================================
100
+ // EMAIL VERIFICATION
101
+ // ==========================================
102
+
103
+ model EmailVerification {
104
+ id String @id @default(uuid())
105
+ email String
106
+ token String @unique
107
+ expiresAt DateTime
108
+ verified Boolean @default(false)
109
+ createdAt DateTime @default(now())
110
+
111
+ @@index([email])
112
+ @@index([token])
113
+ @@map("email_verifications")
114
+ }
115
+
116
+ // ==========================================
117
+ // AUDIT LOGS
118
+ // ==========================================
119
+
120
+ model AuditLog {
121
+ id String @id @default(uuid())
122
+ userId String?
123
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
124
+ action String
125
+ resource String
126
+ resourceId String?
127
+ oldValue Json?
128
+ newValue Json?
129
+ ipAddress String?
130
+ userAgent String?
131
+ metadata Json?
132
+ createdAt DateTime @default(now())
133
+
134
+ @@index([userId])
135
+ @@index([action])
136
+ @@index([resource])
137
+ @@index([createdAt])
138
+ @@map("audit_logs")
139
+ }
140
+
141
+ // ==========================================
142
+ // SETTINGS / CONFIG
143
+ // ==========================================
144
+
145
+ model Setting {
146
+ id String @id @default(uuid())
147
+ key String @unique
148
+ value Json
149
+ type String @default("string")
150
+ group String @default("general")
151
+ createdAt DateTime @default(now())
152
+ updatedAt DateTime @updatedAt
153
+
154
+ @@index([key])
155
+ @@index([group])
156
+ @@map("settings")
157
+ }
@@ -0,0 +1,422 @@
1
+ import { Command } from 'commander';
2
+ import path from 'path';
3
+ import fs from 'fs/promises';
4
+ import ora from 'ora';
5
+ import chalk from 'chalk';
6
+ import { ensureDir, writeFile, fileExists, success, error, info, warn, getModulesDir, getSourceDir } from '../utils/helpers.js';
7
+
8
+ // Pre-built modules that can be added
9
+ const AVAILABLE_MODULES = {
10
+ auth: {
11
+ name: 'Authentication',
12
+ description: 'JWT authentication with access/refresh tokens',
13
+ files: ['auth.service', 'auth.controller', 'auth.routes', 'auth.middleware', 'auth.schemas', 'auth.types', 'index'],
14
+ },
15
+ users: {
16
+ name: 'User Management',
17
+ description: 'User CRUD with RBAC (roles & permissions)',
18
+ files: ['user.service', 'user.controller', 'user.repository', 'user.routes', 'user.schemas', 'user.types', 'index'],
19
+ },
20
+ email: {
21
+ name: 'Email Service',
22
+ description: 'SMTP email with templates (Handlebars)',
23
+ files: ['email.service', 'email.templates', 'email.types', 'index'],
24
+ },
25
+ audit: {
26
+ name: 'Audit Logs',
27
+ description: 'Activity logging and audit trail',
28
+ files: ['audit.service', 'audit.types', 'index'],
29
+ },
30
+ upload: {
31
+ name: 'File Upload',
32
+ description: 'File upload with local/S3 storage',
33
+ files: ['upload.service', 'upload.controller', 'upload.routes', 'upload.types', 'index'],
34
+ },
35
+ cache: {
36
+ name: 'Redis Cache',
37
+ description: 'Redis caching service',
38
+ files: ['cache.service', 'cache.types', 'index'],
39
+ },
40
+ notifications: {
41
+ name: 'Notifications',
42
+ description: 'In-app and push notifications',
43
+ files: ['notification.service', 'notification.types', 'index'],
44
+ },
45
+ settings: {
46
+ name: 'Settings',
47
+ description: 'Application settings management',
48
+ files: ['settings.service', 'settings.controller', 'settings.routes', 'settings.types', 'index'],
49
+ },
50
+ };
51
+
52
+ export const addModuleCommand = new Command('add')
53
+ .description('Add a pre-built module to your project')
54
+ .argument('[module]', 'Module to add (auth, users, email, audit, upload, cache, notifications, settings)')
55
+ .option('-l, --list', 'List available modules')
56
+ .action(async (moduleName?: string, options?: { list?: boolean }) => {
57
+ if (options?.list || !moduleName) {
58
+ console.log(chalk.bold('\n📦 Available Modules:\n'));
59
+
60
+ for (const [key, mod] of Object.entries(AVAILABLE_MODULES)) {
61
+ console.log(` ${chalk.cyan(key.padEnd(15))} ${mod.name}`);
62
+ console.log(` ${' '.repeat(15)} ${chalk.gray(mod.description)}\n`);
63
+ }
64
+
65
+ console.log(chalk.bold('Usage:'));
66
+ console.log(` ${chalk.yellow('servcraft add auth')} Add authentication module`);
67
+ console.log(` ${chalk.yellow('servcraft add users')} Add user management module`);
68
+ console.log(` ${chalk.yellow('servcraft add email')} Add email service module\n`);
69
+ return;
70
+ }
71
+
72
+ const module = AVAILABLE_MODULES[moduleName as keyof typeof AVAILABLE_MODULES];
73
+
74
+ if (!module) {
75
+ error(`Unknown module: ${moduleName}`);
76
+ info('Run "servcraft add --list" to see available modules');
77
+ return;
78
+ }
79
+
80
+ const spinner = ora(`Adding ${module.name} module...`).start();
81
+
82
+ try {
83
+ const moduleDir = path.join(getModulesDir(), moduleName);
84
+
85
+ // Check if module already exists
86
+ if (await fileExists(moduleDir)) {
87
+ spinner.stop();
88
+ warn(`Module "${moduleName}" already exists`);
89
+ return;
90
+ }
91
+
92
+ await ensureDir(moduleDir);
93
+
94
+ // Generate module files based on type
95
+ switch (moduleName) {
96
+ case 'auth':
97
+ await generateAuthModule(moduleDir);
98
+ break;
99
+ case 'users':
100
+ await generateUsersModule(moduleDir);
101
+ break;
102
+ case 'email':
103
+ await generateEmailModule(moduleDir);
104
+ break;
105
+ case 'audit':
106
+ await generateAuditModule(moduleDir);
107
+ break;
108
+ case 'upload':
109
+ await generateUploadModule(moduleDir);
110
+ break;
111
+ case 'cache':
112
+ await generateCacheModule(moduleDir);
113
+ break;
114
+ default:
115
+ await generateGenericModule(moduleDir, moduleName);
116
+ }
117
+
118
+ spinner.succeed(`${module.name} module added successfully!`);
119
+
120
+ console.log('\n📁 Files created:');
121
+ module.files.forEach((f) => success(` src/modules/${moduleName}/${f}.ts`));
122
+
123
+ console.log('\n📌 Next steps:');
124
+ info(' 1. Register the module in your main app file');
125
+ info(' 2. Configure any required environment variables');
126
+ info(' 3. Run database migrations if needed');
127
+
128
+ } catch (err) {
129
+ spinner.fail('Failed to add module');
130
+ error(err instanceof Error ? err.message : String(err));
131
+ }
132
+ });
133
+
134
+ async function generateAuthModule(dir: string): Promise<void> {
135
+ // This would copy from templates or generate inline
136
+ // For now, we'll create placeholder files
137
+ const files = {
138
+ 'auth.types.ts': `export interface JwtPayload {
139
+ sub: string;
140
+ email: string;
141
+ role: string;
142
+ type: 'access' | 'refresh';
143
+ }
144
+
145
+ export interface TokenPair {
146
+ accessToken: string;
147
+ refreshToken: string;
148
+ expiresIn: number;
149
+ }
150
+
151
+ export interface AuthUser {
152
+ id: string;
153
+ email: string;
154
+ role: string;
155
+ }
156
+ `,
157
+ 'auth.schemas.ts': `import { z } from 'zod';
158
+
159
+ export const loginSchema = z.object({
160
+ email: z.string().email(),
161
+ password: z.string().min(1),
162
+ });
163
+
164
+ export const registerSchema = z.object({
165
+ email: z.string().email(),
166
+ password: z.string().min(8),
167
+ name: z.string().min(2).optional(),
168
+ });
169
+
170
+ export const refreshTokenSchema = z.object({
171
+ refreshToken: z.string().min(1),
172
+ });
173
+ `,
174
+ 'index.ts': `export * from './auth.types.js';
175
+ export * from './auth.schemas.js';
176
+ // Export services, controllers, etc.
177
+ `,
178
+ };
179
+
180
+ for (const [name, content] of Object.entries(files)) {
181
+ await writeFile(path.join(dir, name), content);
182
+ }
183
+ }
184
+
185
+ async function generateUsersModule(dir: string): Promise<void> {
186
+ const files = {
187
+ 'user.types.ts': `export type UserStatus = 'active' | 'inactive' | 'suspended' | 'banned';
188
+ export type UserRole = 'user' | 'admin' | 'moderator' | 'super_admin';
189
+
190
+ export interface User {
191
+ id: string;
192
+ email: string;
193
+ password: string;
194
+ name?: string;
195
+ role: UserRole;
196
+ status: UserStatus;
197
+ createdAt: Date;
198
+ updatedAt: Date;
199
+ }
200
+ `,
201
+ 'user.schemas.ts': `import { z } from 'zod';
202
+
203
+ export const createUserSchema = z.object({
204
+ email: z.string().email(),
205
+ password: z.string().min(8),
206
+ name: z.string().min(2).optional(),
207
+ role: z.enum(['user', 'admin', 'moderator']).optional(),
208
+ });
209
+
210
+ export const updateUserSchema = z.object({
211
+ email: z.string().email().optional(),
212
+ name: z.string().min(2).optional(),
213
+ role: z.enum(['user', 'admin', 'moderator', 'super_admin']).optional(),
214
+ status: z.enum(['active', 'inactive', 'suspended', 'banned']).optional(),
215
+ });
216
+ `,
217
+ 'index.ts': `export * from './user.types.js';
218
+ export * from './user.schemas.js';
219
+ `,
220
+ };
221
+
222
+ for (const [name, content] of Object.entries(files)) {
223
+ await writeFile(path.join(dir, name), content);
224
+ }
225
+ }
226
+
227
+ async function generateEmailModule(dir: string): Promise<void> {
228
+ const files = {
229
+ 'email.types.ts': `export interface EmailOptions {
230
+ to: string | string[];
231
+ subject: string;
232
+ html?: string;
233
+ text?: string;
234
+ template?: string;
235
+ data?: Record<string, unknown>;
236
+ }
237
+
238
+ export interface EmailResult {
239
+ success: boolean;
240
+ messageId?: string;
241
+ error?: string;
242
+ }
243
+ `,
244
+ 'email.service.ts': `import nodemailer from 'nodemailer';
245
+ import type { EmailOptions, EmailResult } from './email.types.js';
246
+
247
+ export class EmailService {
248
+ private transporter;
249
+
250
+ constructor() {
251
+ this.transporter = nodemailer.createTransport({
252
+ host: process.env.SMTP_HOST,
253
+ port: parseInt(process.env.SMTP_PORT || '587', 10),
254
+ auth: {
255
+ user: process.env.SMTP_USER,
256
+ pass: process.env.SMTP_PASS,
257
+ },
258
+ });
259
+ }
260
+
261
+ async send(options: EmailOptions): Promise<EmailResult> {
262
+ try {
263
+ const result = await this.transporter.sendMail({
264
+ from: process.env.SMTP_FROM,
265
+ ...options,
266
+ });
267
+ return { success: true, messageId: result.messageId };
268
+ } catch (error) {
269
+ return { success: false, error: String(error) };
270
+ }
271
+ }
272
+ }
273
+
274
+ export const emailService = new EmailService();
275
+ `,
276
+ 'index.ts': `export * from './email.types.js';
277
+ export { EmailService, emailService } from './email.service.js';
278
+ `,
279
+ };
280
+
281
+ for (const [name, content] of Object.entries(files)) {
282
+ await writeFile(path.join(dir, name), content);
283
+ }
284
+ }
285
+
286
+ async function generateAuditModule(dir: string): Promise<void> {
287
+ const files = {
288
+ 'audit.types.ts': `export interface AuditLogEntry {
289
+ userId?: string;
290
+ action: string;
291
+ resource: string;
292
+ resourceId?: string;
293
+ oldValue?: Record<string, unknown>;
294
+ newValue?: Record<string, unknown>;
295
+ ipAddress?: string;
296
+ userAgent?: string;
297
+ createdAt: Date;
298
+ }
299
+ `,
300
+ 'audit.service.ts': `import type { AuditLogEntry } from './audit.types.js';
301
+
302
+ const logs: AuditLogEntry[] = [];
303
+
304
+ export class AuditService {
305
+ async log(entry: Omit<AuditLogEntry, 'createdAt'>): Promise<void> {
306
+ logs.push({ ...entry, createdAt: new Date() });
307
+ console.log('[AUDIT]', entry.action, entry.resource);
308
+ }
309
+
310
+ async query(filters: Partial<AuditLogEntry>): Promise<AuditLogEntry[]> {
311
+ return logs.filter((log) => {
312
+ for (const [key, value] of Object.entries(filters)) {
313
+ if (log[key as keyof AuditLogEntry] !== value) return false;
314
+ }
315
+ return true;
316
+ });
317
+ }
318
+ }
319
+
320
+ export const auditService = new AuditService();
321
+ `,
322
+ 'index.ts': `export * from './audit.types.js';
323
+ export { AuditService, auditService } from './audit.service.js';
324
+ `,
325
+ };
326
+
327
+ for (const [name, content] of Object.entries(files)) {
328
+ await writeFile(path.join(dir, name), content);
329
+ }
330
+ }
331
+
332
+ async function generateUploadModule(dir: string): Promise<void> {
333
+ const files = {
334
+ 'upload.types.ts': `export interface UploadedFile {
335
+ id: string;
336
+ filename: string;
337
+ originalName: string;
338
+ mimetype: string;
339
+ size: number;
340
+ path: string;
341
+ url: string;
342
+ createdAt: Date;
343
+ }
344
+
345
+ export interface UploadOptions {
346
+ maxSize?: number;
347
+ allowedTypes?: string[];
348
+ destination?: string;
349
+ }
350
+ `,
351
+ 'index.ts': `export * from './upload.types.js';
352
+ `,
353
+ };
354
+
355
+ for (const [name, content] of Object.entries(files)) {
356
+ await writeFile(path.join(dir, name), content);
357
+ }
358
+ }
359
+
360
+ async function generateCacheModule(dir: string): Promise<void> {
361
+ const files = {
362
+ 'cache.types.ts': `export interface CacheOptions {
363
+ ttl?: number;
364
+ prefix?: string;
365
+ }
366
+ `,
367
+ 'cache.service.ts': `import type { CacheOptions } from './cache.types.js';
368
+
369
+ // In-memory cache (replace with Redis in production)
370
+ const cache = new Map<string, { value: unknown; expiry: number }>();
371
+
372
+ export class CacheService {
373
+ async get<T>(key: string): Promise<T | null> {
374
+ const item = cache.get(key);
375
+ if (!item) return null;
376
+ if (Date.now() > item.expiry) {
377
+ cache.delete(key);
378
+ return null;
379
+ }
380
+ return item.value as T;
381
+ }
382
+
383
+ async set(key: string, value: unknown, ttl = 3600): Promise<void> {
384
+ cache.set(key, { value, expiry: Date.now() + ttl * 1000 });
385
+ }
386
+
387
+ async del(key: string): Promise<void> {
388
+ cache.delete(key);
389
+ }
390
+
391
+ async clear(): Promise<void> {
392
+ cache.clear();
393
+ }
394
+ }
395
+
396
+ export const cacheService = new CacheService();
397
+ `,
398
+ 'index.ts': `export * from './cache.types.js';
399
+ export { CacheService, cacheService } from './cache.service.js';
400
+ `,
401
+ };
402
+
403
+ for (const [name, content] of Object.entries(files)) {
404
+ await writeFile(path.join(dir, name), content);
405
+ }
406
+ }
407
+
408
+ async function generateGenericModule(dir: string, name: string): Promise<void> {
409
+ const files = {
410
+ [`${name}.types.ts`]: `// ${name} types
411
+ export interface ${name.charAt(0).toUpperCase() + name.slice(1)}Data {
412
+ // Define your types here
413
+ }
414
+ `,
415
+ 'index.ts': `export * from './${name}.types.js';
416
+ `,
417
+ };
418
+
419
+ for (const [fileName, content] of Object.entries(files)) {
420
+ await writeFile(path.join(dir, fileName), content);
421
+ }
422
+ }