@intrig/plugin-nest 0.0.2-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.
@@ -0,0 +1,2 @@
1
+ export * from "./src/index";
2
+ export { default } from "./src/index";
package/dist/index.js ADDED
@@ -0,0 +1,694 @@
1
+ import { StatsCounter, jsonLiteral, typescript, pascalCase, camelCase, markdown } from '@intrig/plugin-sdk';
2
+ import * as path from 'path';
3
+ import * as fsx from 'fs-extra';
4
+ import chalk from 'chalk';
5
+
6
+ class InternalGeneratorContext {
7
+ constructor(){
8
+ this.codeGenerationBreakdown = {};
9
+ }
10
+ getCounter(sourceId) {
11
+ this.codeGenerationBreakdown[sourceId] = this.codeGenerationBreakdown[sourceId] || new StatsCounter(sourceId);
12
+ return this.codeGenerationBreakdown[sourceId];
13
+ }
14
+ getCounters() {
15
+ return [
16
+ ...Object.values(this.codeGenerationBreakdown)
17
+ ];
18
+ }
19
+ }
20
+
21
+ function packageJsonTemplate(ctx) {
22
+ const projectDir = ctx.rootDir ?? process.cwd();
23
+ const packageJson = fsx.readJsonSync(path.resolve(projectDir, 'package.json'));
24
+ const json = jsonLiteral(path.resolve('package.json'));
25
+ return json`
26
+ {
27
+ "name": "@intrig/generated",
28
+ "version": "d${Date.now()}",
29
+ "private": true,
30
+ "main": "dist/index.js",
31
+ "types": "dist/index.d.ts",
32
+ "scripts": {
33
+ "build": "swc src -d dist --copy-files --strip-leading-paths && tsc --emitDeclarationOnly"
34
+ },
35
+ "dependencies": {
36
+ "@nestjs/common": "^10.0.0",
37
+ "@nestjs/axios": "^3.0.0",
38
+ "axios": "^1.7.7",
39
+ "rxjs": "^7.8.0",
40
+ "zod": "^3.23.8"
41
+ },
42
+ "devDependencies": {
43
+ "@swc/cli": "^0.7.7",
44
+ "@swc/core": "^1.12.6",
45
+ "@types/node": "^24.0.4",
46
+ "typescript": "${packageJson.devDependencies?.typescript ?? packageJson.dependencies?.typescript ?? '^5.0.0'}"
47
+ },
48
+ "peerDependencies": {
49
+ "@nestjs/common": "^10.0.0",
50
+ "@nestjs/axios": "^3.0.0",
51
+ "rxjs": "^7.0.0"
52
+ },
53
+ "_moduleAliases": {
54
+ "@intrig/nest": "./src"
55
+ },
56
+ "type": "module",
57
+ "exports": {
58
+ ".": {
59
+ "import": "./src/index.js",
60
+ "require": "./src/index.js",
61
+ "types": "./src/index.d.ts"
62
+ },
63
+ "./*": {
64
+ "import": "./src/*.js",
65
+ "require": "./src/*.js",
66
+ "types": "./src/*.d.ts"
67
+ }
68
+ },
69
+ "typesVersions": {
70
+ "*": {
71
+ "*": ["src/*"]
72
+ }
73
+ }
74
+ }
75
+ `;
76
+ }
77
+
78
+ function indexTemplate(serviceNames) {
79
+ const ts = typescript(path.resolve("src", "index.ts"));
80
+ return ts`
81
+ export * from './intrig.module';
82
+ ${serviceNames.map((name)=>`export * from './${name}/${name}.service';`).join('\n')}
83
+ `;
84
+ }
85
+
86
+ function nestTsConfigTemplate() {
87
+ const json = jsonLiteral(path.resolve('tsconfig.json'));
88
+ return json`
89
+ {
90
+ "compilerOptions": {
91
+ "target": "ES2021",
92
+ "module": "ESNext",
93
+ "declaration": true,
94
+ "outDir": "./dist",
95
+ "strict": true,
96
+ "esModuleInterop": true,
97
+ "noImplicitAny": false,
98
+ "moduleResolution": "node",
99
+ "baseUrl": "./",
100
+ "paths": {
101
+ "@intrig/nest": [
102
+ "./src"
103
+ ],
104
+ "@intrig/nest/*": [
105
+ "./src/*"
106
+ ]
107
+ },
108
+ "skipLibCheck": true,
109
+ "experimentalDecorators": true,
110
+ "emitDecoratorMetadata": true
111
+ },
112
+ "exclude": [
113
+ "node_modules",
114
+ "../../node_modules",
115
+ "**/__tests__/*"
116
+ ]
117
+ }
118
+ `;
119
+ }
120
+
121
+ function moduleTemplate(serviceNames, sources) {
122
+ const ts = typescript(path.resolve("src", "intrig.module.ts"));
123
+ return ts`
124
+ import { Module } from '@nestjs/common';
125
+ import { HttpModule } from '@nestjs/axios';
126
+ ${serviceNames.map((name)=>`import { ${pascalCase(name)}Service } from './${name}/${name}.service';`).join('\n')}
127
+
128
+ @Module({
129
+ imports: [
130
+ HttpModule.register({
131
+ timeout: 60000,
132
+ maxRedirects: 5,
133
+ }),
134
+ ],
135
+ providers: [${serviceNames.map((name)=>`${pascalCase(name)}Service`).join(', ')}],
136
+ exports: [${serviceNames.map((name)=>`${pascalCase(name)}Service`).join(', ')}],
137
+ })
138
+ export class IntrigModule {}
139
+ `;
140
+ }
141
+
142
+ function extractMethodSignature(descriptor, imports, source) {
143
+ const { data } = descriptor;
144
+ const params = [];
145
+ // Path and query parameters
146
+ if (data.variables && data.variables.length > 0) {
147
+ const pathParams = data.variables.filter((v)=>v.in === 'path');
148
+ const queryParams = data.variables.filter((v)=>v.in === 'query');
149
+ pathParams.forEach((param)=>{
150
+ const typeName = param.ref.split('/').pop() || 'any';
151
+ imports.add(`import { ${typeName} } from '../components/schemas/${typeName}';`);
152
+ params.push(`${camelCase(param.name)}: ${typeName}`);
153
+ });
154
+ queryParams.forEach((param)=>{
155
+ const typeName = param.ref.split('/').pop() || 'any';
156
+ imports.add(`import { ${typeName} } from '../components/schemas/${typeName}';`);
157
+ params.push(`${camelCase(param.name)}?: ${typeName}`);
158
+ });
159
+ }
160
+ // Request body
161
+ let bodyParam;
162
+ if (data.requestBody) {
163
+ const bodyType = data.requestBody;
164
+ imports.add(`import { ${bodyType} } from '../components/schemas/${bodyType}';`);
165
+ bodyParam = `data: ${bodyType}`;
166
+ params.push(bodyParam);
167
+ }
168
+ // Return type
169
+ let returnType = 'void';
170
+ if (data.response) {
171
+ returnType = data.response;
172
+ imports.add(`import { ${returnType} } from '../components/schemas/${returnType}';`);
173
+ }
174
+ return {
175
+ params,
176
+ returnType,
177
+ bodyParam
178
+ };
179
+ }
180
+ function buildUrlExpression(descriptor) {
181
+ const { data } = descriptor;
182
+ const urlPath = data.paths[0] || '';
183
+ if (!data.variables || data.variables.length === 0) {
184
+ return `'${urlPath}'`;
185
+ }
186
+ const pathParams = data.variables.filter((v)=>v.in === 'path');
187
+ if (pathParams.length === 0) {
188
+ return `'${urlPath}'`;
189
+ }
190
+ // Replace {param} with ${param}
191
+ let urlTemplate = urlPath;
192
+ pathParams.forEach((param)=>{
193
+ urlTemplate = urlTemplate.replace(`{${param.name}}`, `\${${camelCase(param.name)}}`);
194
+ });
195
+ return `\`${urlTemplate}\``;
196
+ }
197
+ function buildRequestConfig(descriptor) {
198
+ const { data } = descriptor;
199
+ const configParts = [];
200
+ // Query parameters
201
+ if (data.variables && data.variables.some((v)=>v.in === 'query')) {
202
+ const queryParams = data.variables.filter((v)=>v.in === 'query');
203
+ const paramsObj = queryParams.map((p)=>`${p.name}: ${camelCase(p.name)}`).join(', ');
204
+ configParts.push(`params: { ${paramsObj} }`);
205
+ }
206
+ // Headers
207
+ if (data.contentType) {
208
+ configParts.push(`headers: { 'Content-Type': '${data.contentType}' }`);
209
+ }
210
+ if (configParts.length === 0) {
211
+ return '';
212
+ }
213
+ return `, { ${configParts.join(', ')} }`;
214
+ }
215
+ function generateMethodTemplate(descriptor, imports, source) {
216
+ const { data } = descriptor;
217
+ const methodName = camelCase(data.operationId);
218
+ const signature = extractMethodSignature(descriptor, imports);
219
+ const urlExpression = buildUrlExpression(descriptor);
220
+ const configExpression = buildRequestConfig(descriptor);
221
+ const httpMethod = data.method.toLowerCase();
222
+ const hasBody = data.requestBody !== undefined;
223
+ const hasResponse = data.response !== undefined;
224
+ let methodBody;
225
+ if (hasResponse) {
226
+ if (hasBody) {
227
+ // POST/PUT/PATCH with body and response
228
+ methodBody = `
229
+ async ${methodName}(${signature.params.join(', ')}): Promise<${signature.returnType}> {
230
+ const response = await firstValueFrom(
231
+ this.httpService.${httpMethod}<${signature.returnType}>(${urlExpression}, data${configExpression})
232
+ );
233
+ return response.data;
234
+ }`;
235
+ } else {
236
+ // GET/DELETE with response
237
+ methodBody = `
238
+ async ${methodName}(${signature.params.join(', ')}): Promise<${signature.returnType}> {
239
+ const response = await firstValueFrom(
240
+ this.httpService.${httpMethod}<${signature.returnType}>(${urlExpression}${configExpression})
241
+ );
242
+ return response.data;
243
+ }`;
244
+ }
245
+ } else {
246
+ if (hasBody) {
247
+ // POST/PUT/PATCH with body but no response
248
+ methodBody = `
249
+ async ${methodName}(${signature.params.join(', ')}): Promise<void> {
250
+ await firstValueFrom(
251
+ this.httpService.${httpMethod}(${urlExpression}, data${configExpression})
252
+ );
253
+ }`;
254
+ } else {
255
+ // GET/DELETE with no response
256
+ methodBody = `
257
+ async ${methodName}(${signature.params.join(', ')}): Promise<void> {
258
+ await firstValueFrom(
259
+ this.httpService.${httpMethod}(${urlExpression}${configExpression})
260
+ );
261
+ }`;
262
+ }
263
+ }
264
+ return methodBody;
265
+ }
266
+
267
+ function serviceTemplate(serviceName, descriptors, source) {
268
+ const ts = typescript(path.resolve('src', serviceName, `${serviceName}.service.ts`));
269
+ const imports = new Set();
270
+ const methods = [];
271
+ for (const descriptor of descriptors){
272
+ const methodCode = generateMethodTemplate(descriptor, imports);
273
+ methods.push(methodCode);
274
+ }
275
+ return ts`
276
+ import { Injectable } from '@nestjs/common';
277
+ import { HttpService } from '@nestjs/axios';
278
+ import { firstValueFrom } from 'rxjs';
279
+ ${[
280
+ ...imports
281
+ ].join('\n')}
282
+
283
+ @Injectable()
284
+ export class ${pascalCase(serviceName)}Service {
285
+ constructor(private readonly httpService: HttpService) {}
286
+
287
+ ${methods.join('\n\n')}
288
+ }
289
+ `;
290
+ }
291
+
292
+ async function typeTemplate(descriptor) {
293
+ const { data: { schema, name: typeName }, source } = descriptor;
294
+ const { imports, tsType } = openApiSchemaToTypeScript(schema);
295
+ const ts = typescript(path.resolve('src', 'components', 'schemas', `${typeName}.ts`));
296
+ const simpleType = (await jsonLiteral('')`${JSON.stringify(schema)}`).content;
297
+ return ts`
298
+ ${[
299
+ ...imports
300
+ ].join('\n')}
301
+
302
+ //--- TypeScript Type ---//
303
+
304
+ export type ${typeName} = ${tsType}
305
+
306
+ //--- JSON Schema ---//
307
+
308
+ export const ${typeName}_jsonschema = ${JSON.stringify(schema) ?? "{}"}
309
+
310
+ //--- Simple Type ---//
311
+ /*[${simpleType}]*/
312
+ `;
313
+ }
314
+ function isRef(schema) {
315
+ return '$ref' in (schema ?? {});
316
+ }
317
+ // Helper function to convert OpenAPI schema types to TypeScript types
318
+ function openApiSchemaToTypeScript(schema, imports = new Set()) {
319
+ if (!schema) {
320
+ return {
321
+ tsType: 'any',
322
+ imports: new Set()
323
+ };
324
+ }
325
+ if (isRef(schema)) {
326
+ return handleRefSchema(schema.$ref, imports);
327
+ }
328
+ if (!schema.type) {
329
+ if ('properties' in schema) {
330
+ schema.type = 'object';
331
+ } else if ('items' in schema) {
332
+ schema.type = 'array';
333
+ }
334
+ }
335
+ switch(schema.type){
336
+ case 'string':
337
+ return handleStringSchema(schema);
338
+ case 'number':
339
+ return handleNumberSchema();
340
+ case 'integer':
341
+ return handleIntegerSchema();
342
+ case 'boolean':
343
+ return handleBooleanSchema();
344
+ case 'array':
345
+ return handleArraySchema(schema, imports);
346
+ case 'object':
347
+ return handleObjectSchema(schema, imports);
348
+ default:
349
+ return handleComplexSchema(schema, imports);
350
+ }
351
+ }
352
+ function handleRefSchema(ref, imports) {
353
+ const refParts = ref.split('/');
354
+ const refName = refParts[refParts.length - 1];
355
+ imports.add(`import { ${refName} } from './${refName}';`);
356
+ return {
357
+ tsType: refName,
358
+ imports
359
+ };
360
+ }
361
+ function handleStringSchema(schema) {
362
+ if (schema.enum) {
363
+ const enumValues = schema.enum.map((value)=>`'${value}'`).join(' | ');
364
+ return {
365
+ tsType: enumValues,
366
+ imports: new Set()
367
+ };
368
+ }
369
+ let tsType = 'string';
370
+ if (schema.format === 'date' || schema.format === 'date-time') {
371
+ tsType = 'Date';
372
+ } else if (schema.format === 'binary') {
373
+ tsType = 'Buffer';
374
+ } else if (schema.format === 'byte') {
375
+ tsType = 'string'; // base64 encoded string
376
+ }
377
+ return {
378
+ tsType,
379
+ imports: new Set()
380
+ };
381
+ }
382
+ function handleNumberSchema() {
383
+ return {
384
+ tsType: 'number',
385
+ imports: new Set()
386
+ };
387
+ }
388
+ function handleIntegerSchema() {
389
+ return {
390
+ tsType: 'number',
391
+ imports: new Set()
392
+ };
393
+ }
394
+ function handleBooleanSchema() {
395
+ return {
396
+ tsType: 'boolean',
397
+ imports: new Set()
398
+ };
399
+ }
400
+ function handleArraySchema(schema, imports) {
401
+ if (!schema.items) {
402
+ throw new Error('Array schema must have an items property');
403
+ }
404
+ const { tsType, imports: itemImports } = openApiSchemaToTypeScript(schema.items, imports);
405
+ return {
406
+ tsType: `(${tsType})[]`,
407
+ imports: new Set([
408
+ ...imports,
409
+ ...itemImports
410
+ ])
411
+ };
412
+ }
413
+ function handleObjectSchema(schema, imports) {
414
+ const updatedRequiredFields = schema.required || [];
415
+ if (schema.properties) {
416
+ const propertiesTs = Object.entries(schema.properties).map(([key, value])=>{
417
+ const { tsType, optional } = openApiSchemaToTypeScript(value);
418
+ const isRequired = !optional && updatedRequiredFields.includes(key);
419
+ return `${key}${isRequired ? '' : '?'}: ${tsType} ${isRequired ? '' : ' | null'};`;
420
+ });
421
+ return {
422
+ tsType: `{ ${propertiesTs.join(' ')} }`,
423
+ imports
424
+ };
425
+ }
426
+ return {
427
+ tsType: 'any',
428
+ imports: new Set(),
429
+ optional: true
430
+ };
431
+ }
432
+ function handleComplexSchema(schema, imports) {
433
+ if (schema.oneOf) {
434
+ const options = schema.oneOf.map((subSchema)=>openApiSchemaToTypeScript(subSchema));
435
+ const tsTypes = options.map((option)=>option.tsType);
436
+ return {
437
+ tsType: tsTypes.join(' | '),
438
+ imports: new Set([
439
+ ...imports,
440
+ ...options.flatMap((option)=>Array.from(option.imports))
441
+ ])
442
+ };
443
+ }
444
+ if (schema.anyOf) {
445
+ const options = schema.anyOf.map((subSchema)=>openApiSchemaToTypeScript(subSchema));
446
+ const tsTypes = options.map((option)=>option.tsType);
447
+ return {
448
+ tsType: tsTypes.join(' | '),
449
+ imports: new Set([
450
+ ...imports,
451
+ ...options.flatMap((option)=>Array.from(option.imports))
452
+ ])
453
+ };
454
+ }
455
+ if (schema.allOf) {
456
+ const options = schema.allOf.map((subSchema)=>openApiSchemaToTypeScript(subSchema));
457
+ const tsTypes = options.map((option)=>option.tsType);
458
+ return {
459
+ tsType: tsTypes.join(' & '),
460
+ imports: new Set([
461
+ ...imports,
462
+ ...options.flatMap((option)=>Array.from(option.imports))
463
+ ])
464
+ };
465
+ }
466
+ return {
467
+ tsType: 'any',
468
+ imports
469
+ };
470
+ }
471
+
472
+ /**
473
+ * Extract service name from operationId or path
474
+ * Examples:
475
+ * - getUser -> users
476
+ * - createPost -> posts
477
+ * - listProducts -> products
478
+ * - /api/users/{id} -> users
479
+ */ function extractServiceName(descriptor) {
480
+ const operationId = descriptor.data.operationId;
481
+ // Try to extract from operationId first
482
+ // Common patterns: getUser, createUser, listUsers, getUserById
483
+ const operationPrefixes = [
484
+ 'get',
485
+ 'post',
486
+ 'put',
487
+ 'patch',
488
+ 'delete',
489
+ 'create',
490
+ 'update',
491
+ 'list',
492
+ 'fetch',
493
+ 'remove'
494
+ ];
495
+ for (const prefix of operationPrefixes){
496
+ if (operationId.toLowerCase().startsWith(prefix)) {
497
+ const rest = operationId.substring(prefix.length);
498
+ if (rest.length > 0) {
499
+ // Extract the resource name (e.g., "User" from "getUser")
500
+ const resourceName = rest.replace(/By.*$/, ''); // Remove "ById", "ByName", etc.
501
+ return camelCase(resourceName.endsWith('s') ? resourceName : resourceName + 's');
502
+ }
503
+ }
504
+ }
505
+ // Fallback: extract from path
506
+ const path = descriptor.data.paths[0] || '';
507
+ const pathSegments = path.split('/').filter((s)=>s && !s.startsWith('{'));
508
+ if (pathSegments.length > 0) {
509
+ // Take the first meaningful segment
510
+ const segment = pathSegments[0];
511
+ return camelCase(segment);
512
+ }
513
+ // Ultimate fallback: use source name
514
+ return camelCase(descriptor.source);
515
+ }
516
+ /**
517
+ * Group REST descriptors by service name
518
+ */ function groupDescriptorsByService(descriptors) {
519
+ const grouped = {};
520
+ for (const descriptor of descriptors){
521
+ const serviceName = extractServiceName(descriptor);
522
+ if (!grouped[serviceName]) {
523
+ grouped[serviceName] = [];
524
+ }
525
+ grouped[serviceName].push(descriptor);
526
+ }
527
+ return grouped;
528
+ }
529
+ async function generateCode(ctx) {
530
+ // Root/project files
531
+ await ctx.dump(packageJsonTemplate(ctx));
532
+ await ctx.dump(nestTsConfigTemplate());
533
+ const internalGeneratorContext = new InternalGeneratorContext();
534
+ // Group REST descriptors by service
535
+ const groupedDescriptors = groupDescriptorsByService(ctx.restDescriptors);
536
+ const serviceNames = Object.keys(groupedDescriptors).sort();
537
+ // Generate module and index files
538
+ await ctx.dump(moduleTemplate(serviceNames, ctx.sources));
539
+ await ctx.dump(indexTemplate(serviceNames));
540
+ // Generate service files
541
+ for (const [serviceName, descriptors] of Object.entries(groupedDescriptors)){
542
+ const source = descriptors[0]?.source || 'default';
543
+ await ctx.dump(serviceTemplate(serviceName, descriptors));
544
+ // Track statistics
545
+ const counter = internalGeneratorContext.getCounter(source);
546
+ counter.inc('services');
547
+ descriptors.forEach(()=>counter.inc('methods'));
548
+ }
549
+ // Generate type files
550
+ for (const schemaDescriptor of ctx.schemaDescriptors){
551
+ await ctx.dump(typeTemplate(schemaDescriptor));
552
+ // Track statistics
553
+ const counter = internalGeneratorContext.getCounter(schemaDescriptor.source);
554
+ counter.inc('types');
555
+ }
556
+ return internalGeneratorContext.getCounters();
557
+ }
558
+
559
+ async function schemaTypescriptDoc(result) {
560
+ const { data: { name, schema }, source } = result;
561
+ const { tsType } = openApiSchemaToTypeScript(result.data.schema);
562
+ const ts = typescript(path.resolve('src', source, 'temp', name, `${name}.ts`));
563
+ const importContent = await ts`
564
+ import { ${name} } from '@intrig/nest/components/schemas/${name}';
565
+ `;
566
+ const md = markdown('');
567
+ return md`
568
+ \`\`\`typescript
569
+ ${importContent.content}
570
+
571
+ type Example = ${tsType}
572
+ \`\`\`
573
+ `;
574
+ }
575
+ async function schemaJsonSchemaDoc(result) {
576
+ const { data: { name, schema }, source } = result;
577
+ const ts = typescript(path.resolve('src', source, 'temp', name, `${name}.ts`));
578
+ const importContent = await ts`
579
+ import { ${name}_jsonschema } from '@intrig/nest/components/schemas/${name}';
580
+ `;
581
+ const md = markdown('');
582
+ return md`
583
+ \`\`\`typescript
584
+ ${importContent.content}
585
+
586
+ console.log(${name}_jsonschema)
587
+ \`\`\`
588
+
589
+ \`\`\`json
590
+ ${JSON.stringify(schema, null, 2)}
591
+ \`\`\`
592
+ `;
593
+ }
594
+
595
+ async function getSchemaDocumentation(result) {
596
+ const tabs = [];
597
+ tabs.push({
598
+ name: 'TypeScript Type',
599
+ content: (await schemaTypescriptDoc(result)).content
600
+ });
601
+ tabs.push({
602
+ name: 'JSON Schema',
603
+ content: (await schemaJsonSchemaDoc(result)).content
604
+ });
605
+ return tabs;
606
+ }
607
+
608
+ async function nestServiceMethodDocs(result) {
609
+ const { data: { operationId, method, paths, requestBody, response, variables }, source } = result;
610
+ const methodName = camelCase(operationId);
611
+ const ts = typescript(path.resolve('src', source, 'temp', operationId, `${operationId}.ts`));
612
+ // Build example usage
613
+ let exampleParams = [];
614
+ let exampleCall = '';
615
+ if (variables && variables.length > 0) {
616
+ variables.forEach((v)=>{
617
+ exampleParams.push(`// ${v.in} parameter\nconst ${camelCase(v.name)} = ...;`);
618
+ });
619
+ }
620
+ if (requestBody) {
621
+ exampleParams.push(`// request body\nconst data = ...;`);
622
+ }
623
+ const callParams = [
624
+ ...variables?.map((v)=>camelCase(v.name)) || [],
625
+ requestBody ? 'data' : ''
626
+ ].filter(Boolean);
627
+ exampleCall = `const result = await service.${methodName}(${callParams.join(', ')});`;
628
+ const importContent = await ts`
629
+ import { SomeService } from '@intrig/nest';
630
+
631
+ // Inject the service in your controller or another service
632
+ constructor(private readonly service: SomeService) {}
633
+
634
+ // Usage example
635
+ ${exampleParams.join('\n')}
636
+ ${exampleCall}
637
+ `;
638
+ const md = markdown('');
639
+ return md`
640
+ \`\`\`typescript
641
+ ${importContent.content}
642
+ \`\`\`
643
+
644
+ **Endpoint Details:**
645
+ - Method: \`${method.toUpperCase()}\`
646
+ - Path: \`${paths[0] || ''}\`
647
+ ${requestBody ? `- Request Body Type: \`${requestBody}\`` : ''}
648
+ ${response ? `- Response Type: \`${response}\`` : ''}
649
+ `;
650
+ }
651
+
652
+ async function getEndpointDocumentation(result) {
653
+ const tabs = [];
654
+ tabs.push({
655
+ name: 'Service Method',
656
+ content: (await nestServiceMethodDocs(result)).content
657
+ });
658
+ return tabs;
659
+ }
660
+
661
+ async function initPlugin(ctx) {
662
+ // NestJS plugin doesn't need any specific initialization tasks currently
663
+ // But we return a postInit function to show instructions to the user
664
+ return {
665
+ postInit: ()=>{
666
+ console.log(chalk.blue('\n📋 Next Steps:'));
667
+ console.log(chalk.white('To complete your NestJS setup, import the generated IntrigModule into your app.module.ts:'));
668
+ console.log(chalk.cyan('\n import { IntrigModule } from \'@intrig/nest\';'));
669
+ console.log(chalk.cyan('\n @Module({'));
670
+ console.log(chalk.cyan(' imports: [IntrigModule],'));
671
+ console.log(chalk.cyan(' })'));
672
+ console.log(chalk.gray('\nThe generated services will be available for dependency injection.\n'));
673
+ }
674
+ };
675
+ }
676
+
677
+ function createPlugin() {
678
+ return {
679
+ meta () {
680
+ return {
681
+ name: 'intrig-binding',
682
+ version: '0.0.1',
683
+ compat: '^0.0.15',
684
+ generator: 'nest'
685
+ };
686
+ },
687
+ generate: generateCode,
688
+ getSchemaDocumentation,
689
+ getEndpointDocumentation,
690
+ init: initPlugin
691
+ };
692
+ }
693
+
694
+ export { createPlugin, createPlugin as default };