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