micro-generate 1.0.1

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 +104 -0
  2. package/dist/cli/generators/feature-generator.d.ts +2 -0
  3. package/dist/cli/generators/feature-generator.js +164 -0
  4. package/dist/cli/generators/feature-generator.js.map +1 -0
  5. package/dist/cli/generators/graphql-generator.d.ts +2 -0
  6. package/dist/cli/generators/graphql-generator.js +117 -0
  7. package/dist/cli/generators/graphql-generator.js.map +1 -0
  8. package/dist/cli/generators/init-generator.d.ts +2 -0
  9. package/dist/cli/generators/init-generator.js +400 -0
  10. package/dist/cli/generators/init-generator.js.map +1 -0
  11. package/dist/cli/index.d.ts +2 -0
  12. package/dist/cli/index.js +87 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/cli/utils/types.d.ts +10 -0
  15. package/dist/cli/utils/types.js +2 -0
  16. package/dist/cli/utils/types.js.map +1 -0
  17. package/dist/config/database.d.ts +2 -0
  18. package/dist/config/database.js +27 -0
  19. package/dist/config/database.js.map +1 -0
  20. package/dist/config/env.d.ts +12 -0
  21. package/dist/config/env.js +17 -0
  22. package/dist/config/env.js.map +1 -0
  23. package/dist/core/MicroserviceServer.d.ts +24 -0
  24. package/dist/core/MicroserviceServer.js +85 -0
  25. package/dist/core/MicroserviceServer.js.map +1 -0
  26. package/dist/database/redis.d.ts +31 -0
  27. package/dist/database/redis.js +115 -0
  28. package/dist/database/redis.js.map +1 -0
  29. package/dist/features/hello/resolvers.d.ts +5 -0
  30. package/dist/features/hello/resolvers.js +6 -0
  31. package/dist/features/hello/resolvers.js.map +1 -0
  32. package/dist/features/hello/typeDefs.d.ts +1 -0
  33. package/dist/features/hello/typeDefs.js +6 -0
  34. package/dist/features/hello/typeDefs.js.map +1 -0
  35. package/dist/features/index.d.ts +6 -0
  36. package/dist/features/index.js +5 -0
  37. package/dist/features/index.js.map +1 -0
  38. package/dist/index.d.ts +5 -0
  39. package/dist/index.js +6 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/server.d.ts +1 -0
  42. package/dist/server.js +43 -0
  43. package/dist/server.js.map +1 -0
  44. package/dist/utils/logger.d.ts +3 -0
  45. package/dist/utils/logger.js +21 -0
  46. package/dist/utils/logger.js.map +1 -0
  47. package/package.json +62 -0
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # 🚀 Microservice Generator (`micro-generate`)
2
+
3
+ **`micro-generate`** es un CLI potente y ligero para crear microservicios listos para producción con **Node.js, TypeScript, GraphQL (Apollo Server), MongoDB y Redis**.
4
+
5
+ Olvídate del boilerplate. Genera infraestructuras completas, features modulares, queries y mutaciones en segundos con una arquitectura escalable y las mejores prácticas integradas.
6
+
7
+ ---
8
+
9
+ ## 📦 Instalación y Uso
10
+
11
+ No necesitas instalarlo globalmente. Recomendamos usar `npx` para ejecutar siempre la última versión:
12
+
13
+ ```bash
14
+ npx micro-generate <comando>
15
+ ```
16
+
17
+ ---
18
+
19
+ ## 🛠️ Comandos Disponibles
20
+
21
+ ### 1. Inicializar un Nuevo Proyecto
22
+ Crea la estructura base del microservicio, configurando TypeScript, Docker, Variables de Entorno y el Servidor.
23
+
24
+ ```bash
25
+ npx micro-generate init
26
+ ```
27
+ * **Interactivo:** Te preguntará el nombre del proyecto y si deseas habilitar MongoDB y/o Redis.
28
+ * **Resultado:** Un proyecto completo con `src/core`, `src/config`, y todo listo para correr `npm run dev`.
29
+
30
+ ### 2. Crear una Nueva Feature
31
+ Genera un módulo de dominio completo (Vertical Slice Architecture).
32
+
33
+ ```bash
34
+ npx micro-generate feature
35
+ ```
36
+ * **Te pedirá:** Nombre del feature (ej: `users`, `orders`).
37
+ * **Genera:** Carpeta en `src/features/<nombre>` con:
38
+ * `typeDefs.ts`: Esquemas GraphQL.
39
+ * `resolvers.ts`: Controladores.
40
+ * `service.ts`: Lógica de negocio.
41
+ * `model.ts`: Modelo Mongoose (si Mongo está activo).
42
+ * **Automático:** Registra la feature en el `index.ts` principal.
43
+
44
+ ### 3. Agregar una Query (Consulta)
45
+ Agrega una nueva consulta GraphQL a una feature existente sin tocar el código manualmente.
46
+
47
+ ```bash
48
+ npx micro-generate query
49
+ ```
50
+ * **Flujo:** Seleccionas la feature de una lista -> Escribes el nombre -> Defines el tipo de retorno.
51
+ * **Magia:** Inyecta automáticamente la definición en `typeDefs` y el esqueleto de la función en `resolvers`.
52
+
53
+ ### 4. Agregar una Mutation (Mutación)
54
+ Agrega una nueva mutación para modificar datos.
55
+
56
+ ```bash
57
+ npx micro-generate mutation
58
+ ```
59
+ * Similar a `query`, ideal para operaciones de creación, actualización o eliminación (CRUD).
60
+
61
+ ---
62
+
63
+ ## 🔥 Características Principales
64
+
65
+ * **⚡ Stack Moderno**: Node.js 20+, TypeScript 5, Apollo Server 4.
66
+ * **🧠 GraphQL Modular**: Arquitectura basada en features autotencidos.
67
+ * **🛡️ Type-Safety**: Validación de variables de entorno con `Zod`.
68
+ * **🗄️ Bases de Datos**:
69
+ * **MongoDB** (Mongoose) con conexión robusta.
70
+ * **Redis** para caché y pub/sub (cliente inteligente).
71
+ * **📝 Logging**: Implementación de alto rendimiento con **Pino** (JSON logs).
72
+ * **🛑 Graceful Shutdown**: Manejo correcto de señales SIGINT/SIGTERM para evitar conexiones colgadas.
73
+ * **🎮 Playground Control**: Habilita/deshabilita el Apollo Sandbox vía variables de entorno.
74
+
75
+ ## 📂 Estructura Generada
76
+
77
+ ```
78
+ my-service/
79
+ ├── src/
80
+ │ ├── config/ # Configuración de entorno y DBs
81
+ │ ├── core/ # Servidor central (MicroserviceServer)
82
+ │ ├── features/ # Tu lógica de negocio aquí
83
+ │ │ ├── users/ # Ejemplo de Feature
84
+ │ │ │ ├── typeDefs.ts
85
+ │ │ │ ├── resolvers.ts
86
+ │ │ │ └── service.ts
87
+ │ │ └── index.ts # Auto-registro
88
+ │ ├── utils/ # Loggers y helpers
89
+ │ └── index.ts # Punto de entrada
90
+ ├── .env # Variables de entorno
91
+ ├── package.json
92
+ └── tsconfig.json
93
+ ```
94
+
95
+ ## 🤝 Contribuyendo
96
+
97
+ 1. Haz un Fork.
98
+ 2. Crea tu rama de feature (`git checkout -b feature/amazing-feature`).
99
+ 3. Commit a tus cambios (`git commit -m 'feat: Add amazing feature'`).
100
+ 4. Push a la rama (`git push origin feature/amazing-feature`).
101
+ 5. Abre un Pull Request.
102
+
103
+ ---
104
+ Generado con ❤️ por `micro-generate`.
@@ -0,0 +1,2 @@
1
+ import { GenerateFeatureOptions } from '../utils/types.js';
2
+ export declare const generateFeature: (options: GenerateFeatureOptions) => Promise<void>;
@@ -0,0 +1,164 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ export const generateFeature = async (options) => {
5
+ const { name, useMongo = true, useRedis = true } = options;
6
+ const featurePath = path.join(process.cwd(), 'src', 'features', name);
7
+ if (await fs.pathExists(featurePath)) {
8
+ console.error(chalk.red(`Feature ${name} already exists.`));
9
+ return;
10
+ }
11
+ await fs.ensureDir(featurePath);
12
+ const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
13
+ const pascalName = capitalize(name);
14
+ // TypeDefs
15
+ const typeDefsContent = `export const typeDefs = \`#graphql
16
+ type ${pascalName} {
17
+ id: ID!
18
+ name: String
19
+ }
20
+
21
+ type ${pascalName}Response {
22
+ success: Boolean!
23
+ message: String!
24
+ data: ${pascalName}
25
+ }
26
+
27
+ type Query {
28
+ ${name}s: [${pascalName}]
29
+ ${name}(id: ID!): ${pascalName}
30
+ }
31
+
32
+ type Mutation {
33
+ create${pascalName}(name: String!): ${pascalName}Response
34
+ }
35
+ \`;`;
36
+ // Model (Only if Mongo is enabled)
37
+ if (useMongo) {
38
+ const modelContent = `import mongoose, { Schema, Document } from 'mongoose';
39
+
40
+ export interface I${pascalName} extends Document {
41
+ name: string;
42
+ }
43
+
44
+ const ${pascalName}Schema: Schema = new Schema({
45
+ name: { type: String, required: true },
46
+ }, { timestamps: true, collection: '${pascalName}' });
47
+
48
+ export const ${pascalName}Model = mongoose.model<I${pascalName}>('${pascalName}', ${pascalName}Schema);`;
49
+ // Save model in src/db/mongo/
50
+ const mongoPath = path.join(process.cwd(), 'src', 'db', 'mongo');
51
+ if (await fs.pathExists(mongoPath)) {
52
+ await fs.writeFile(path.join(mongoPath, `${name}.model.ts`), modelContent);
53
+ }
54
+ else {
55
+ console.warn(chalk.yellow(`Warning: src/db/mongo does not exist. Saving model in feature folder.`));
56
+ await fs.writeFile(path.join(featurePath, 'model.ts'), modelContent);
57
+ }
58
+ }
59
+ // Service
60
+ let serviceImports = '';
61
+ let serviceClassBody = '';
62
+ if (useMongo) {
63
+ // Import from centralized location: ../../db/mongo/NAME.model.js
64
+ // If it was saved locally (fallback), import from ./model.js
65
+ const mongoPath = path.join(process.cwd(), 'src', 'db', 'mongo');
66
+ if (await fs.pathExists(mongoPath)) {
67
+ serviceImports += `import { ${pascalName}Model, I${pascalName} } from '../../db/mongo/${name}.model.js';\n`;
68
+ }
69
+ else {
70
+ serviceImports += `import { ${pascalName}Model, I${pascalName} } from './model.js';\n`;
71
+ }
72
+ serviceClassBody += `
73
+ async getAll(): Promise<I${pascalName}[]> {
74
+ return ${pascalName}Model.find();
75
+ }
76
+
77
+ async create(name: string): Promise<I${pascalName}> {
78
+ return ${pascalName}Model.create({ name });
79
+ }
80
+ `;
81
+ }
82
+ else {
83
+ // In-memory or Redis-only fallback for types
84
+ serviceClassBody += `
85
+ async getAll(): Promise<any[]> {
86
+ return [{ id: '1', name: 'Test (No Mongo)' }];
87
+ }
88
+
89
+ async create(name: string): Promise<any> {
90
+ return { id: Math.random().toString(), name };
91
+ }
92
+ `;
93
+ }
94
+ if (useRedis) {
95
+ // Assuming client.ts and redis.ts are at src/db/redis/ relative to src/features/FEATURE/service.ts
96
+ serviceImports += `import { getRedisClient } from '../../db/redis/client.js';\n`;
97
+ serviceImports += `import { createRedisRepository } from '../../db/redis/redis.js';\n`;
98
+ // Add repository property to class
99
+ serviceClassBody = `
100
+ private redis = createRedisRepository(getRedisClient, '${name}:');
101
+ ` + serviceClassBody;
102
+ serviceClassBody += `
103
+ async getCache(key: string): Promise<any> {
104
+ return this.redis.find(key);
105
+ }
106
+
107
+ async setCache(key: string, value: string): Promise<any> {
108
+ return this.redis.saveTTL(key, value, 3600);
109
+ }
110
+ `;
111
+ }
112
+ const serviceContent = `${serviceImports}
113
+ export class ${pascalName}Service {${serviceClassBody}}
114
+
115
+ export const ${name}Service = new ${pascalName}Service();`;
116
+ // Resolvers
117
+ const resolversContent = `import { ${name}Service } from './service.js';
118
+
119
+ export const resolvers = {
120
+ Query: {
121
+ ${name}s: () => ${name}Service.getAll(),
122
+ ${name}: (_: any, { id }: { id: string }) => ({ id, name: 'Test' }),
123
+ },
124
+ Mutation: {
125
+ create${pascalName}: async (_: any, { name }: { name: string }) => {
126
+ const result = await ${name}Service.create(name);
127
+ return {
128
+ success: true,
129
+ message: '${pascalName} created successfully',
130
+ data: result,
131
+ };
132
+ },
133
+ },
134
+ };`;
135
+ await fs.writeFile(path.join(featurePath, 'typeDefs.ts'), typeDefsContent);
136
+ await fs.writeFile(path.join(featurePath, 'resolvers.ts'), resolversContent);
137
+ await fs.writeFile(path.join(featurePath, 'service.ts'), serviceContent);
138
+ console.log(chalk.green(`Feature ${name} created successfully at ${featurePath}`));
139
+ // Register feature in src/features/index.ts
140
+ const featuresIndexPath = path.join(process.cwd(), 'src', 'features', 'index.ts');
141
+ if (await fs.pathExists(featuresIndexPath)) {
142
+ let featuresIndexContent = await fs.readFile(featuresIndexPath, 'utf-8');
143
+ const typeDefsImport = `import { typeDefs as ${name}TypeDefs } from './${name}/typeDefs.js';\n`;
144
+ const resolversImport = `import { resolvers as ${name}Resolvers } from './${name}/resolvers.js';\n`;
145
+ if (!featuresIndexContent.includes(`${name}/typeDefs.js`)) {
146
+ // Add imports
147
+ const lastImportIndex = featuresIndexContent.lastIndexOf('import ');
148
+ const insertImportPosition = lastImportIndex !== -1 ? featuresIndexContent.indexOf('\n', lastImportIndex) + 1 : 0;
149
+ featuresIndexContent = featuresIndexContent.slice(0, insertImportPosition) + typeDefsImport + resolversImport + featuresIndexContent.slice(insertImportPosition);
150
+ // Add to exports
151
+ featuresIndexContent = featuresIndexContent.replace('export const typeDefs = [', `export const typeDefs = [${name}TypeDefs, `);
152
+ featuresIndexContent = featuresIndexContent.replace('export const resolvers = [', `export const resolvers = [${name}Resolvers, `);
153
+ await fs.writeFile(featuresIndexPath, featuresIndexContent);
154
+ console.log(chalk.blue(`Feature ${name} registered in src/features/index.ts`));
155
+ }
156
+ else {
157
+ console.log(chalk.yellow(`Feature ${name} already registered in src/features/index.ts`));
158
+ }
159
+ }
160
+ else {
161
+ console.warn(chalk.yellow('Remember to register the feature in src/features/index.ts (indexes file not found)'));
162
+ }
163
+ };
164
+ //# sourceMappingURL=feature-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-generator.js","sourceRoot":"","sources":["../../../src/cli/generators/feature-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const generateQuery: () => Promise<void>;
2
+ export declare const generateMutation: () => Promise<void>;
@@ -0,0 +1,117 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import inquirer from 'inquirer';
4
+ import chalk from 'chalk';
5
+ export const generateQuery = async () => {
6
+ await generateGraphQLOperation('query');
7
+ };
8
+ export const generateMutation = async () => {
9
+ await generateGraphQLOperation('mutation');
10
+ };
11
+ const generateGraphQLOperation = async (type) => {
12
+ try {
13
+ // 1. Get available features
14
+ const featuresPath = path.join(process.cwd(), 'src', 'features');
15
+ if (!await fs.pathExists(featuresPath)) {
16
+ console.error(chalk.red('❌ src/features directory not found. Are you in the root of the project?'));
17
+ return;
18
+ }
19
+ const files = await fs.readdir(featuresPath);
20
+ const features = files.filter(f => {
21
+ const fullPath = path.join(featuresPath, f);
22
+ return fs.statSync(fullPath).isDirectory();
23
+ });
24
+ if (features.length === 0) {
25
+ console.error(chalk.red('❌ No features found. Generate a feature first using generate:feature'));
26
+ return;
27
+ }
28
+ // 2. Prompt user
29
+ const answers = await inquirer.prompt([
30
+ {
31
+ type: 'rawlist',
32
+ name: 'feature',
33
+ message: 'Select a feature:',
34
+ choices: features.map(f => ({ name: f, value: f }))
35
+ },
36
+ {
37
+ type: 'input',
38
+ name: 'name',
39
+ message: `${type === 'query' ? 'Query' : 'Mutation'} name:`,
40
+ validate: (input) => input ? true : 'Name is required'
41
+ },
42
+ {
43
+ type: 'input',
44
+ name: 'returnType',
45
+ message: 'Return type:',
46
+ default: 'String'
47
+ }
48
+ ]);
49
+ const { feature, name, returnType } = answers;
50
+ if (!feature) {
51
+ console.error(chalk.red('❌ No feature selected.'));
52
+ return;
53
+ }
54
+ const featurePath = path.join(featuresPath, feature);
55
+ const typeDefsPath = path.join(featurePath, 'typeDefs.ts');
56
+ const resolversPath = path.join(featurePath, 'resolvers.ts');
57
+ if (!await fs.pathExists(typeDefsPath) || !await fs.pathExists(resolversPath)) {
58
+ console.error(chalk.red(`❌ typeDefs.ts or resolvers.ts not found in ${featurePath}`));
59
+ return;
60
+ }
61
+ // 3. Update typeDefs.ts
62
+ let typeDefsContent = await fs.readFile(typeDefsPath, 'utf-8');
63
+ const opType = type === 'query' ? 'Query' : 'Mutation';
64
+ const fieldDefinition = ` ${name}: ${returnType}\n`;
65
+ // Check if "extend type Query/Mutation" exists
66
+ const typeExtensionRegex = new RegExp(`extend\\s+type\\s+${opType}\\s*{`);
67
+ if (typeDefsContent.match(typeExtensionRegex)) {
68
+ // Insert into existing extension
69
+ typeDefsContent = typeDefsContent.replace(typeExtensionRegex, (match) => `${match}\n${fieldDefinition}`);
70
+ }
71
+ else {
72
+ // Create new extension, insert before the end of the template string
73
+ // Assuming format: export const typeDefs = `#graphql ... `;
74
+ const lastBacktickIndex = typeDefsContent.lastIndexOf('`');
75
+ if (lastBacktickIndex !== -1) {
76
+ const newExtension = `\n extend type ${opType} {\n${fieldDefinition} }\n`;
77
+ typeDefsContent = typeDefsContent.slice(0, lastBacktickIndex) + newExtension + typeDefsContent.slice(lastBacktickIndex);
78
+ }
79
+ else {
80
+ console.error(chalk.red('❌ Could not parse typeDefs.ts structure.'));
81
+ return;
82
+ }
83
+ }
84
+ await fs.writeFile(typeDefsPath, typeDefsContent);
85
+ // 4. Update resolvers.ts
86
+ let resolversContent = await fs.readFile(resolversPath, 'utf-8');
87
+ // Check if keys "Query:" or "Mutation:" exist
88
+ const resolverKeyRegex = new RegExp(`${opType}\\s*:\\s*{`);
89
+ const resolverFunction = ` ${name}: async (_: any, args: any) => {
90
+ // TODO: Implement logic
91
+ return null;
92
+ },`;
93
+ if (resolversContent.match(resolverKeyRegex)) {
94
+ // Insert into existing resolver object
95
+ resolversContent = resolversContent.replace(resolverKeyRegex, (match) => `${match}\n${resolverFunction}`);
96
+ }
97
+ else {
98
+ // Create new key in export const resolvers = { ... }
99
+ // We look for the export declaration and append the new key
100
+ const exportRegex = /export\s+const\s+resolvers\s*=\s*{/;
101
+ if (resolversContent.match(exportRegex)) {
102
+ const newResolverBlock = `\n ${opType}: {\n${resolverFunction}\n },`;
103
+ resolversContent = resolversContent.replace(exportRegex, (match) => `${match}${newResolverBlock}`);
104
+ }
105
+ else {
106
+ console.error(chalk.red('❌ Could not parse resolvers.ts structure.'));
107
+ return;
108
+ }
109
+ }
110
+ await fs.writeFile(resolversPath, resolversContent);
111
+ console.log(chalk.green(`✅ ${opType} '${name}' added to feature '${feature}' successfully!`));
112
+ }
113
+ catch (error) {
114
+ console.error(chalk.red('An error occurred during generation:'), error);
115
+ }
116
+ };
117
+ //# sourceMappingURL=graphql-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-generator.js","sourceRoot":"","sources":["../../../src/cli/generators/graphql-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { GenerateProjectOptions } from '../utils/types.js';
2
+ export declare const generateInit: (options: GenerateProjectOptions) => Promise<void>;