@gyxer-studio/generator 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 (50) hide show
  1. package/README.md +63 -0
  2. package/dist/generators/app.generator.d.ts +19 -0
  3. package/dist/generators/app.generator.d.ts.map +1 -0
  4. package/dist/generators/app.generator.js +237 -0
  5. package/dist/generators/app.generator.js.map +1 -0
  6. package/dist/generators/controller.generator.d.ts +10 -0
  7. package/dist/generators/controller.generator.d.ts.map +1 -0
  8. package/dist/generators/controller.generator.js +88 -0
  9. package/dist/generators/controller.generator.js.map +1 -0
  10. package/dist/generators/docker.generator.d.ts +15 -0
  11. package/dist/generators/docker.generator.d.ts.map +1 -0
  12. package/dist/generators/docker.generator.js +82 -0
  13. package/dist/generators/docker.generator.js.map +1 -0
  14. package/dist/generators/dto.generator.d.ts +23 -0
  15. package/dist/generators/dto.generator.d.ts.map +1 -0
  16. package/dist/generators/dto.generator.js +271 -0
  17. package/dist/generators/dto.generator.js.map +1 -0
  18. package/dist/generators/module.generator.d.ts +6 -0
  19. package/dist/generators/module.generator.d.ts.map +1 -0
  20. package/dist/generators/module.generator.js +20 -0
  21. package/dist/generators/module.generator.js.map +1 -0
  22. package/dist/generators/prisma.generator.d.ts +6 -0
  23. package/dist/generators/prisma.generator.d.ts.map +1 -0
  24. package/dist/generators/prisma.generator.js +243 -0
  25. package/dist/generators/prisma.generator.js.map +1 -0
  26. package/dist/generators/service.generator.d.ts +6 -0
  27. package/dist/generators/service.generator.d.ts.map +1 -0
  28. package/dist/generators/service.generator.js +97 -0
  29. package/dist/generators/service.generator.js.map +1 -0
  30. package/dist/index.d.ts +14 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +16 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/modules/auth-jwt.generator.d.ts +20 -0
  35. package/dist/modules/auth-jwt.generator.d.ts.map +1 -0
  36. package/dist/modules/auth-jwt.generator.js +431 -0
  37. package/dist/modules/auth-jwt.generator.js.map +1 -0
  38. package/dist/project-generator.d.ts +16 -0
  39. package/dist/project-generator.d.ts.map +1 -0
  40. package/dist/project-generator.js +199 -0
  41. package/dist/project-generator.js.map +1 -0
  42. package/dist/security/report.d.ts +24 -0
  43. package/dist/security/report.d.ts.map +1 -0
  44. package/dist/security/report.js +108 -0
  45. package/dist/security/report.js.map +1 -0
  46. package/dist/utils.d.ts +16 -0
  47. package/dist/utils.d.ts.map +1 -0
  48. package/dist/utils.js +44 -0
  49. package/dist/utils.js.map +1 -0
  50. package/package.json +58 -0
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @gyxer-studio/generator
2
+
3
+ NestJS project code generator. Takes a Gyxer schema and generates a complete, production-ready NestJS application with Prisma ORM.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @gyxer-studio/generator
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { generateProject } from '@gyxer-studio/generator';
15
+ import type { GyxerProject } from '@gyxer-studio/schema';
16
+
17
+ const project: GyxerProject = {
18
+ name: 'my-app',
19
+ version: '0.1.0',
20
+ entities: [/* ... */],
21
+ modules: [],
22
+ settings: {
23
+ port: 3000,
24
+ database: 'postgresql',
25
+ // ...
26
+ },
27
+ };
28
+
29
+ const files = generateProject(project);
30
+ // files is a Map<string, string> of filepath → content
31
+
32
+ for (const [path, content] of files) {
33
+ // write files to disk
34
+ }
35
+ ```
36
+
37
+ ## What Gets Generated
38
+
39
+ - **Prisma schema** with models, relations, and enums
40
+ - **NestJS modules** with controllers, services, and DTOs
41
+ - **CRUD endpoints** with Swagger decorators and validation
42
+ - **JWT auth** (when `auth-jwt` module is enabled)
43
+ - **Docker** files (Dockerfile + docker-compose.yml)
44
+ - **Security report** with best practices assessment
45
+
46
+ ## Individual Generators
47
+
48
+ ```typescript
49
+ import {
50
+ generatePrismaSchema,
51
+ generateCreateDto,
52
+ generateUpdateDto,
53
+ generateController,
54
+ generateService,
55
+ generateModule,
56
+ generateDockerfile,
57
+ generateDockerCompose,
58
+ } from '@gyxer-studio/generator';
59
+ ```
60
+
61
+ ## Part of [Gyxer Studio](https://github.com/Gyxer513/gyxer-studio)
62
+
63
+ MIT License
@@ -0,0 +1,19 @@
1
+ import type { GyxerProject } from '@gyxer-studio/schema';
2
+ /**
3
+ * Generate main.ts bootstrap file.
4
+ */
5
+ export declare function generateMain(project: GyxerProject): string;
6
+ /**
7
+ * Generate app.module.ts with all entity modules imported.
8
+ */
9
+ export declare function generateAppModule(project: GyxerProject): string;
10
+ /**
11
+ * Generate PrismaService and PrismaModule.
12
+ */
13
+ export declare function generatePrismaService(): string;
14
+ export declare function generatePrismaModule(): string;
15
+ /**
16
+ * Generate Prisma exception filter that converts Prisma errors to proper HTTP responses.
17
+ */
18
+ export declare function generatePrismaExceptionFilter(): string;
19
+ //# sourceMappingURL=app.generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.generator.d.ts","sourceRoot":"","sources":["../../src/generators/app.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAgF1D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CA+E/D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAe9C;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAW7C;AAED;;GAEG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,CA6DtD"}
@@ -0,0 +1,237 @@
1
+ import { toKebabCase } from '../utils.js';
2
+ /**
3
+ * Generate main.ts bootstrap file.
4
+ */
5
+ export function generateMain(project) {
6
+ const lines = [];
7
+ lines.push(`import { NestFactory } from '@nestjs/core';`);
8
+ lines.push(`import { ValidationPipe } from '@nestjs/common';`);
9
+ if (project.settings.enableSwagger) {
10
+ lines.push(`import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';`);
11
+ }
12
+ if (project.settings.enableHelmet) {
13
+ lines.push(`import helmet from 'helmet';`);
14
+ }
15
+ lines.push(`import { AppModule } from './app.module';`);
16
+ lines.push(`import { PrismaExceptionFilter } from './prisma/prisma-exception.filter';`);
17
+ lines.push('');
18
+ lines.push('async function bootstrap() {');
19
+ lines.push(' const app = await NestFactory.create(AppModule);');
20
+ lines.push('');
21
+ // Helmet
22
+ if (project.settings.enableHelmet) {
23
+ lines.push(' // Security headers');
24
+ lines.push(' app.use(helmet());');
25
+ lines.push('');
26
+ }
27
+ // CORS
28
+ if (project.settings.enableCors) {
29
+ lines.push(' // CORS');
30
+ lines.push(' app.enableCors();');
31
+ lines.push('');
32
+ }
33
+ // Validation
34
+ lines.push(' // Global validation pipe');
35
+ lines.push(' app.useGlobalPipes(');
36
+ lines.push(' new ValidationPipe({');
37
+ lines.push(' whitelist: true,');
38
+ lines.push(' forbidNonWhitelisted: true,');
39
+ lines.push(' transform: true,');
40
+ lines.push(' }),');
41
+ lines.push(' );');
42
+ lines.push('');
43
+ lines.push(' // Prisma exception filter — converts DB errors to proper HTTP responses');
44
+ lines.push(' app.useGlobalFilters(new PrismaExceptionFilter());');
45
+ lines.push('');
46
+ // Swagger
47
+ if (project.settings.enableSwagger) {
48
+ lines.push(' // Swagger API documentation');
49
+ const hasAuthJwt = project.modules?.some((m) => m.name === 'auth-jwt' && m.enabled !== false) ?? false;
50
+ lines.push(' const config = new DocumentBuilder()');
51
+ lines.push(` .setTitle('${project.name}')`);
52
+ lines.push(` .setDescription('${project.description || 'API documentation'}')`);
53
+ lines.push(` .setVersion('${project.version}')`);
54
+ if (hasAuthJwt) {
55
+ lines.push(" .addBearerAuth()");
56
+ }
57
+ lines.push(' .build();');
58
+ lines.push(' const document = SwaggerModule.createDocument(app, config);');
59
+ lines.push(" SwaggerModule.setup('api/docs', app, document);");
60
+ lines.push('');
61
+ }
62
+ lines.push(` await app.listen(process.env.PORT ?? ${project.settings.port});`);
63
+ lines.push(` console.log(\`Application is running on: \${await app.getUrl()}\`);`);
64
+ if (project.settings.enableSwagger) {
65
+ lines.push(` console.log(\`Swagger docs: \${await app.getUrl()}/api/docs\`);`);
66
+ }
67
+ lines.push('}');
68
+ lines.push('bootstrap();');
69
+ return lines.join('\n') + '\n';
70
+ }
71
+ /**
72
+ * Generate app.module.ts with all entity modules imported.
73
+ */
74
+ export function generateAppModule(project) {
75
+ const hasAuthJwt = project.modules?.some((m) => m.name === 'auth-jwt' && m.enabled !== false) ?? false;
76
+ const needsAppGuard = project.settings.enableRateLimit || hasAuthJwt;
77
+ const imports = [];
78
+ const moduleNames = [];
79
+ // Prisma module
80
+ imports.push(`import { PrismaModule } from './prisma/prisma.module';`);
81
+ moduleNames.push('PrismaModule');
82
+ // Rate limiting
83
+ if (project.settings.enableRateLimit) {
84
+ imports.push(`import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';`);
85
+ }
86
+ // Auth JWT
87
+ if (hasAuthJwt) {
88
+ imports.push(`import { AuthModule } from './auth/auth.module';`);
89
+ imports.push(`import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';`);
90
+ moduleNames.push('AuthModule');
91
+ }
92
+ if (needsAppGuard) {
93
+ imports.push(`import { APP_GUARD } from '@nestjs/core';`);
94
+ }
95
+ // Entity modules
96
+ for (const entity of project.entities) {
97
+ const kebab = toKebabCase(entity.name);
98
+ imports.push(`import { ${entity.name}Module } from './${kebab}/${kebab}.module';`);
99
+ moduleNames.push(`${entity.name}Module`);
100
+ }
101
+ const lines = [];
102
+ lines.push(`import { Module } from '@nestjs/common';`);
103
+ for (const imp of imports) {
104
+ lines.push(imp);
105
+ }
106
+ lines.push('');
107
+ lines.push('@Module({');
108
+ lines.push(' imports: [');
109
+ if (project.settings.enableRateLimit) {
110
+ lines.push(' ThrottlerModule.forRoot([{');
111
+ lines.push(` ttl: ${(project.settings.rateLimitTtl ?? 60) * 1000},`);
112
+ lines.push(` limit: ${project.settings.rateLimitMax ?? 100},`);
113
+ lines.push(' }]),');
114
+ }
115
+ for (const mod of moduleNames) {
116
+ lines.push(` ${mod},`);
117
+ }
118
+ lines.push(' ],');
119
+ if (needsAppGuard) {
120
+ lines.push(' providers: [');
121
+ if (project.settings.enableRateLimit) {
122
+ lines.push(' {');
123
+ lines.push(' provide: APP_GUARD,');
124
+ lines.push(' useClass: ThrottlerGuard,');
125
+ lines.push(' },');
126
+ }
127
+ if (hasAuthJwt) {
128
+ lines.push(' // Global JWT guard — all routes protected by default');
129
+ lines.push(' // Use @Public() decorator to make specific routes public');
130
+ lines.push(' {');
131
+ lines.push(' provide: APP_GUARD,');
132
+ lines.push(' useClass: JwtAuthGuard,');
133
+ lines.push(' },');
134
+ }
135
+ lines.push(' ],');
136
+ }
137
+ lines.push('})');
138
+ lines.push('export class AppModule {}');
139
+ return lines.join('\n') + '\n';
140
+ }
141
+ /**
142
+ * Generate PrismaService and PrismaModule.
143
+ */
144
+ export function generatePrismaService() {
145
+ return `import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
146
+ import { PrismaClient } from '@prisma/client';
147
+
148
+ @Injectable()
149
+ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
150
+ async onModuleInit() {
151
+ await this.$connect();
152
+ }
153
+
154
+ async onModuleDestroy() {
155
+ await this.$disconnect();
156
+ }
157
+ }
158
+ `;
159
+ }
160
+ export function generatePrismaModule() {
161
+ return `import { Global, Module } from '@nestjs/common';
162
+ import { PrismaService } from './prisma.service';
163
+
164
+ @Global()
165
+ @Module({
166
+ providers: [PrismaService],
167
+ exports: [PrismaService],
168
+ })
169
+ export class PrismaModule {}
170
+ `;
171
+ }
172
+ /**
173
+ * Generate Prisma exception filter that converts Prisma errors to proper HTTP responses.
174
+ */
175
+ export function generatePrismaExceptionFilter() {
176
+ return `import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common';
177
+ import { Prisma } from '@prisma/client';
178
+ import { Response } from 'express';
179
+
180
+ @Catch(Prisma.PrismaClientKnownRequestError)
181
+ export class PrismaExceptionFilter implements ExceptionFilter {
182
+ catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) {
183
+ const ctx = host.switchToHttp();
184
+ const response = ctx.getResponse<Response>();
185
+
186
+ switch (exception.code) {
187
+ case 'P2002': {
188
+ // Unique constraint violation
189
+ const fields = (exception.meta?.target as string[]) || [];
190
+ response.status(HttpStatus.CONFLICT).json({
191
+ statusCode: HttpStatus.CONFLICT,
192
+ message: \`Unique constraint violation on: \${fields.join(', ')}\`,
193
+ error: 'Conflict',
194
+ });
195
+ break;
196
+ }
197
+ case 'P2003': {
198
+ // Foreign key constraint violation
199
+ const field = (exception.meta?.field_name as string) || 'unknown';
200
+ response.status(HttpStatus.BAD_REQUEST).json({
201
+ statusCode: HttpStatus.BAD_REQUEST,
202
+ message: \`Foreign key constraint violated on: \${field}\`,
203
+ error: 'Bad Request',
204
+ });
205
+ break;
206
+ }
207
+ case 'P2025': {
208
+ // Record not found
209
+ response.status(HttpStatus.NOT_FOUND).json({
210
+ statusCode: HttpStatus.NOT_FOUND,
211
+ message: (exception.meta?.cause as string) || 'Record not found',
212
+ error: 'Not Found',
213
+ });
214
+ break;
215
+ }
216
+ case 'P2014': {
217
+ // Required relation violation
218
+ response.status(HttpStatus.BAD_REQUEST).json({
219
+ statusCode: HttpStatus.BAD_REQUEST,
220
+ message: 'Required relation violation',
221
+ error: 'Bad Request',
222
+ });
223
+ break;
224
+ }
225
+ default: {
226
+ response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
227
+ statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
228
+ message: \`Database error: \${exception.code}\`,
229
+ error: 'Internal Server Error',
230
+ });
231
+ }
232
+ }
233
+ }
234
+ }
235
+ `;
236
+ }
237
+ //# sourceMappingURL=app.generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.generator.js","sourceRoot":"","sources":["../../src/generators/app.generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Entity, GyxerProject } from '@gyxer-studio/schema';
2
+ /**
3
+ * Generate a NestJS controller for an entity with full CRUD + Swagger.
4
+ * When auth-jwt is enabled:
5
+ * - GET endpoints are @Public() (no auth required)
6
+ * - POST/PATCH/DELETE require JWT (global guard)
7
+ * - @ApiBearerAuth() on protected endpoints for Swagger UI
8
+ */
9
+ export declare function generateController(entity: Entity, project: GyxerProject): string;
10
+ //# sourceMappingURL=controller.generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.generator.d.ts","sourceRoot":"","sources":["../../src/generators/controller.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGjE;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAsFhF"}
@@ -0,0 +1,88 @@
1
+ import { toCamelCase, toKebabCase, pluralize } from '../utils.js';
2
+ /**
3
+ * Generate a NestJS controller for an entity with full CRUD + Swagger.
4
+ * When auth-jwt is enabled:
5
+ * - GET endpoints are @Public() (no auth required)
6
+ * - POST/PATCH/DELETE require JWT (global guard)
7
+ * - @ApiBearerAuth() on protected endpoints for Swagger UI
8
+ */
9
+ export function generateController(entity, project) {
10
+ const name = entity.name;
11
+ const camel = toCamelCase(name);
12
+ const kebab = toKebabCase(name);
13
+ const route = pluralize(kebab);
14
+ const serviceName = `${camel}Service`;
15
+ const className = `${name}Controller`;
16
+ const hasAuthJwt = project.modules?.some((m) => m.name === 'auth-jwt' && m.enabled !== false) ?? false;
17
+ const importLines = [
18
+ 'import {',
19
+ ' Controller,',
20
+ ' Get,',
21
+ ' Post,',
22
+ ' Body,',
23
+ ' Patch,',
24
+ ' Param,',
25
+ ' Delete,',
26
+ ' ParseIntPipe,',
27
+ "} from '@nestjs/common';",
28
+ ];
29
+ const swaggerImports = hasAuthJwt
30
+ ? "import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';"
31
+ : "import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';";
32
+ importLines.push(swaggerImports);
33
+ if (hasAuthJwt) {
34
+ importLines.push("import { Public } from '../auth/decorators/public.decorator';");
35
+ }
36
+ importLines.push(`import { ${name}Service } from './${kebab}.service';`);
37
+ importLines.push(`import { Create${name}Dto } from './dto/create-${kebab}.dto';`);
38
+ importLines.push(`import { Update${name}Dto } from './dto/update-${kebab}.dto';`);
39
+ const protectedDecorator = hasAuthJwt ? '\n @ApiBearerAuth()' : '';
40
+ const publicDecorator = hasAuthJwt ? '\n @Public()' : '';
41
+ return `${importLines.join('\n')}
42
+
43
+ @ApiTags('${route}')
44
+ @Controller('${route}')
45
+ export class ${className} {
46
+ constructor(private readonly ${serviceName}: ${name}Service) {}
47
+
48
+ @Post()${protectedDecorator}
49
+ @ApiOperation({ summary: 'Create a new ${camel}' })
50
+ @ApiResponse({ status: 201, description: '${name} created successfully' })
51
+ create(@Body() dto: Create${name}Dto) {
52
+ return this.${serviceName}.create(dto);
53
+ }
54
+
55
+ @Get()${publicDecorator}
56
+ @ApiOperation({ summary: 'Get all ${pluralize(camel)}' })
57
+ @ApiResponse({ status: 200, description: 'List of ${pluralize(camel)}' })
58
+ findAll() {
59
+ return this.${serviceName}.findAll();
60
+ }
61
+
62
+ @Get(':id')${publicDecorator}
63
+ @ApiOperation({ summary: 'Get a ${camel} by id' })
64
+ @ApiResponse({ status: 200, description: '${name} found' })
65
+ @ApiResponse({ status: 404, description: '${name} not found' })
66
+ findOne(@Param('id', ParseIntPipe) id: number) {
67
+ return this.${serviceName}.findOne(id);
68
+ }
69
+
70
+ @Patch(':id')${protectedDecorator}
71
+ @ApiOperation({ summary: 'Update a ${camel}' })
72
+ @ApiResponse({ status: 200, description: '${name} updated successfully' })
73
+ @ApiResponse({ status: 404, description: '${name} not found' })
74
+ update(@Param('id', ParseIntPipe) id: number, @Body() dto: Update${name}Dto) {
75
+ return this.${serviceName}.update(id, dto);
76
+ }
77
+
78
+ @Delete(':id')${protectedDecorator}
79
+ @ApiOperation({ summary: 'Delete a ${camel}' })
80
+ @ApiResponse({ status: 200, description: '${name} deleted successfully' })
81
+ @ApiResponse({ status: 404, description: '${name} not found' })
82
+ remove(@Param('id', ParseIntPipe) id: number) {
83
+ return this.${serviceName}.remove(id);
84
+ }
85
+ }
86
+ `;
87
+ }
88
+ //# sourceMappingURL=controller.generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.generator.js","sourceRoot":"","sources":["../../src/generators/controller.generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,OAAqB;IACtE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,WAAW,GAAG,GAAG,KAAK,SAAS,CAAC;IACtC,MAAM,SAAS,GAAG,GAAG,IAAI,YAAY,CAAC;IAEtC,MAAM,UAAU,GACd,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC;IAEtF,MAAM,WAAW,GAAG;QAClB,UAAU;QACV,eAAe;QACf,QAAQ;QACR,SAAS;QACT,SAAS;QACT,UAAU;QACV,UAAU;QACV,WAAW;QACX,iBAAiB;QACjB,0BAA0B;KAC3B,CAAC;IAEF,MAAM,cAAc,GAAG,UAAU;QAC/B,CAAC,CAAC,sFAAsF;QACxF,CAAC,CAAC,uEAAuE,CAAC;IAC5E,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjC,IAAI,UAAU,EAAE,CAAC;QACf,WAAW,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IACpF,CAAC;IAED,WAAW,CAAC,IAAI,CAAC,YAAY,IAAI,qBAAqB,KAAK,YAAY,CAAC,CAAC;IACzE,WAAW,CAAC,IAAI,CAAC,kBAAkB,IAAI,4BAA4B,KAAK,QAAQ,CAAC,CAAC;IAClF,WAAW,CAAC,IAAI,CAAC,kBAAkB,IAAI,4BAA4B,KAAK,QAAQ,CAAC,CAAC;IAElF,MAAM,kBAAkB,GAAG,UAAU,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;IACpE,MAAM,eAAe,GAAG,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1D,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;;YAEtB,KAAK;eACF,KAAK;eACL,SAAS;iCACS,WAAW,KAAK,IAAI;;WAE1C,kBAAkB;2CACc,KAAK;8CACF,IAAI;8BACpB,IAAI;kBAChB,WAAW;;;UAGnB,eAAe;sCACa,SAAS,CAAC,KAAK,CAAC;sDACA,SAAS,CAAC,KAAK,CAAC;;kBAEpD,WAAW;;;eAGd,eAAe;oCACM,KAAK;8CACK,IAAI;8CACJ,IAAI;;kBAEhC,WAAW;;;iBAGZ,kBAAkB;uCACI,KAAK;8CACE,IAAI;8CACJ,IAAI;qEACmB,IAAI;kBACvD,WAAW;;;kBAGX,kBAAkB;uCACG,KAAK;8CACE,IAAI;8CACJ,IAAI;;kBAEhC,WAAW;;;CAG5B,CAAC;AACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { GyxerProject } from '@gyxer-studio/schema';
2
+ /**
3
+ * Generate Dockerfile for the NestJS app.
4
+ */
5
+ export declare function generateDockerfile(): string;
6
+ /**
7
+ * Generate docker-compose.yml with app + PostgreSQL.
8
+ */
9
+ export declare function generateDockerCompose(project: GyxerProject): string;
10
+ /**
11
+ * Generate .env and .env.example files.
12
+ */
13
+ export declare function generateEnvFile(project: GyxerProject): string;
14
+ export declare function generateEnvExample(project: GyxerProject): string;
15
+ //# sourceMappingURL=docker.generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.generator.d.ts","sourceRoot":"","sources":["../../src/generators/docker.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAkB3C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAsCnE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAO7D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAMhE"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Generate Dockerfile for the NestJS app.
3
+ */
4
+ export function generateDockerfile() {
5
+ return `FROM node:20-alpine AS builder
6
+ WORKDIR /app
7
+ COPY package*.json ./
8
+ RUN npm install
9
+ COPY . .
10
+ RUN npx prisma generate
11
+ RUN npm run build
12
+
13
+ FROM node:20-alpine AS runner
14
+ WORKDIR /app
15
+ COPY --from=builder /app/dist ./dist
16
+ COPY --from=builder /app/node_modules ./node_modules
17
+ COPY --from=builder /app/package*.json ./
18
+ COPY --from=builder /app/prisma ./prisma
19
+ EXPOSE 3000
20
+ CMD ["sh", "-c", "npx prisma db push --skip-generate && node dist/main.js"]
21
+ `;
22
+ }
23
+ /**
24
+ * Generate docker-compose.yml with app + PostgreSQL.
25
+ */
26
+ export function generateDockerCompose(project) {
27
+ const dbName = project.name.replace(/-/g, '_');
28
+ return `version: '3.8'
29
+
30
+ services:
31
+ app:
32
+ build: .
33
+ ports:
34
+ - "\${PORT:-${project.settings.port}}:${project.settings.port}"
35
+ environment:
36
+ - DATABASE_URL=postgresql://postgres:\${DB_PASSWORD:-postgres}@db:5432/${dbName}
37
+ - PORT=${project.settings.port}
38
+ depends_on:
39
+ db:
40
+ condition: service_healthy
41
+ restart: unless-stopped
42
+
43
+ db:
44
+ image: postgres:16-alpine
45
+ environment:
46
+ POSTGRES_DB: ${dbName}
47
+ POSTGRES_USER: postgres
48
+ POSTGRES_PASSWORD: \${DB_PASSWORD:-postgres}
49
+ ports:
50
+ - "\${DB_PORT:-5432}:5432"
51
+ volumes:
52
+ - pgdata:/var/lib/postgresql/data
53
+ healthcheck:
54
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
55
+ interval: 5s
56
+ timeout: 5s
57
+ retries: 5
58
+ restart: unless-stopped
59
+
60
+ volumes:
61
+ pgdata:
62
+ `;
63
+ }
64
+ /**
65
+ * Generate .env and .env.example files.
66
+ */
67
+ export function generateEnvFile(project) {
68
+ const dbName = project.name.replace(/-/g, '_');
69
+ return `DATABASE_URL=postgresql://postgres:postgres@localhost:5432/${dbName}
70
+ PORT=${project.settings.port}
71
+ DB_PASSWORD=postgres
72
+ DB_PORT=5432
73
+ `;
74
+ }
75
+ export function generateEnvExample(project) {
76
+ return `DATABASE_URL=postgresql://postgres:YOUR_PASSWORD@localhost:5432/YOUR_DB_NAME
77
+ PORT=${project.settings.port}
78
+ DB_PASSWORD=YOUR_PASSWORD
79
+ DB_PORT=5432
80
+ `;
81
+ }
82
+ //# sourceMappingURL=docker.generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.generator.js","sourceRoot":"","sources":["../../src/generators/docker.generator.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO;;;;;;;;;;;;;;;;CAgBR,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAqB;IACzD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAE/C,OAAO;;;;;;oBAMW,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,IAAI;;+EAEY,MAAM;eACtE,OAAO,CAAC,QAAQ,CAAC,IAAI;;;;;;;;;qBASf,MAAM;;;;;;;;;;;;;;;;CAgB1B,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,OAAqB;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/C,OAAO,8DAA8D,MAAM;OACtE,OAAO,CAAC,QAAQ,CAAC,IAAI;;;CAG3B,CAAC;AACF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAqB;IACtD,OAAO;OACF,OAAO,CAAC,QAAQ,CAAC,IAAI;;;CAG3B,CAAC;AACF,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { Entity, GyxerProject } from '@gyxer-studio/schema';
2
+ /** FK field descriptor for DTO generation. */
3
+ interface FkFieldInfo {
4
+ name: string;
5
+ targetEntity: string;
6
+ }
7
+ /**
8
+ * Collect all foreign key fields that this entity needs in its DTO.
9
+ * Sources:
10
+ * 1. Entity's own relations that define a foreignKey (explicit FK).
11
+ * 2. Inverse relations from other entities that create FK on this entity.
12
+ */
13
+ export declare function collectFkFields(entity: Entity, project: GyxerProject): FkFieldInfo[];
14
+ /**
15
+ * Generate Create DTO for an entity.
16
+ */
17
+ export declare function generateCreateDto(entity: Entity, project: GyxerProject): string;
18
+ /**
19
+ * Generate Update DTO for an entity (all fields optional).
20
+ */
21
+ export declare function generateUpdateDto(entity: Entity, project: GyxerProject): string;
22
+ export {};
23
+ //# sourceMappingURL=dto.generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dto.generator.d.ts","sourceRoot":"","sources":["../../src/generators/dto.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAS,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGxE,8CAA8C;AAC9C,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,WAAW,EAAE,CAgDpF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAkD/E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAgC/E"}