archetype-engine 2.0.0 → 2.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 (27) hide show
  1. package/README.md +1 -1
  2. package/dist/src/cli.js +6 -0
  3. package/dist/src/fields.d.ts +9 -9
  4. package/dist/src/fields.d.ts.map +1 -1
  5. package/dist/src/fields.js +8 -8
  6. package/dist/src/init/templates.d.ts +2 -0
  7. package/dist/src/init/templates.d.ts.map +1 -1
  8. package/dist/src/init/templates.js +418 -0
  9. package/dist/src/mcp-server.d.ts +15 -0
  10. package/dist/src/mcp-server.d.ts.map +1 -0
  11. package/dist/src/mcp-server.js +271 -0
  12. package/dist/src/templates/nextjs-drizzle-trpc/generators/index.d.ts +3 -0
  13. package/dist/src/templates/nextjs-drizzle-trpc/generators/index.d.ts.map +1 -1
  14. package/dist/src/templates/nextjs-drizzle-trpc/generators/index.js +7 -1
  15. package/dist/src/templates/nextjs-drizzle-trpc/generators/openapi.d.ts +26 -0
  16. package/dist/src/templates/nextjs-drizzle-trpc/generators/openapi.d.ts.map +1 -0
  17. package/dist/src/templates/nextjs-drizzle-trpc/generators/openapi.js +690 -0
  18. package/dist/src/templates/nextjs-drizzle-trpc/generators/seed.d.ts +27 -0
  19. package/dist/src/templates/nextjs-drizzle-trpc/generators/seed.d.ts.map +1 -0
  20. package/dist/src/templates/nextjs-drizzle-trpc/generators/seed.js +407 -0
  21. package/dist/src/templates/nextjs-drizzle-trpc/generators/test.d.ts +27 -0
  22. package/dist/src/templates/nextjs-drizzle-trpc/generators/test.d.ts.map +1 -0
  23. package/dist/src/templates/nextjs-drizzle-trpc/generators/test.js +520 -0
  24. package/dist/src/templates/nextjs-drizzle-trpc/index.d.ts +5 -1
  25. package/dist/src/templates/nextjs-drizzle-trpc/index.d.ts.map +1 -1
  26. package/dist/src/templates/nextjs-drizzle-trpc/index.js +11 -1
  27. package/package.json +3 -2
@@ -0,0 +1,690 @@
1
+ "use strict";
2
+ /**
3
+ * OpenAPI/Swagger Documentation Generator
4
+ *
5
+ * Generates OpenAPI 3.0 specification from entity definitions and tRPC routers.
6
+ * Provides interactive API documentation via Swagger UI.
7
+ *
8
+ * Generated files:
9
+ * - docs/openapi.json - OpenAPI 3.0 specification
10
+ * - docs/swagger.html - Interactive Swagger UI
11
+ *
12
+ * Features:
13
+ * - Complete API documentation for all CRUD endpoints
14
+ * - Request/response schemas derived from Zod validation
15
+ * - Authentication/authorization documentation
16
+ * - Filter, search, and pagination parameter docs
17
+ * - Batch operation endpoints
18
+ * - Interactive testing via Swagger UI
19
+ *
20
+ * @module generators/openapi
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.openapiGenerator = void 0;
24
+ const utils_1 = require("../../../core/utils");
25
+ /**
26
+ * Map field type to OpenAPI type
27
+ */
28
+ function getOpenAPIType(field) {
29
+ switch (field.type) {
30
+ case 'text':
31
+ if (field.validations.some(v => v.type === 'email')) {
32
+ return { type: 'string', format: 'email' };
33
+ }
34
+ if (field.validations.some(v => v.type === 'url')) {
35
+ return { type: 'string', format: 'uri' };
36
+ }
37
+ if (field.enumValues) {
38
+ return { type: 'string' };
39
+ }
40
+ return { type: 'string' };
41
+ case 'number':
42
+ if (field.validations.some(v => v.type === 'integer')) {
43
+ return { type: 'integer', format: 'int32' };
44
+ }
45
+ return { type: 'number', format: 'double' };
46
+ case 'boolean':
47
+ return { type: 'boolean' };
48
+ case 'date':
49
+ return { type: 'string', format: 'date-time' };
50
+ case 'enum':
51
+ return { type: 'string' };
52
+ default:
53
+ return { type: 'string' };
54
+ }
55
+ }
56
+ /**
57
+ * Generate OpenAPI schema for a field
58
+ */
59
+ function generateFieldSchema(fieldName, field) {
60
+ const schema = getOpenAPIType(field);
61
+ // Add enum values
62
+ if (field.enumValues) {
63
+ schema.enum = field.enumValues;
64
+ }
65
+ // Add description from label
66
+ if (field.label) {
67
+ schema.description = field.label;
68
+ }
69
+ // Add validation constraints
70
+ if (field.type === 'text') {
71
+ const minLength = field.validations.find(v => v.type === 'minLength');
72
+ const maxLength = field.validations.find(v => v.type === 'maxLength');
73
+ if (minLength)
74
+ schema.minLength = minLength.value;
75
+ if (maxLength)
76
+ schema.maxLength = maxLength.value;
77
+ }
78
+ if (field.type === 'number') {
79
+ const min = field.validations.find(v => v.type === 'min');
80
+ const max = field.validations.find(v => v.type === 'max');
81
+ if (min)
82
+ schema.minimum = min.value;
83
+ if (max)
84
+ schema.maximum = max.value;
85
+ }
86
+ // Add default value
87
+ if (field.default !== undefined) {
88
+ schema.default = field.default;
89
+ }
90
+ return schema;
91
+ }
92
+ /**
93
+ * Generate OpenAPI schema for entity create input
94
+ */
95
+ function generateCreateSchema(entity) {
96
+ const properties = {};
97
+ const required = [];
98
+ for (const [fieldName, field] of Object.entries(entity.fields)) {
99
+ // Skip computed fields
100
+ if (field.type === 'computed')
101
+ continue;
102
+ properties[fieldName] = generateFieldSchema(fieldName, field);
103
+ if (field.required) {
104
+ required.push(fieldName);
105
+ }
106
+ }
107
+ return {
108
+ type: 'object',
109
+ properties,
110
+ required: required.length > 0 ? required : undefined,
111
+ };
112
+ }
113
+ /**
114
+ * Generate OpenAPI schema for entity (full model with ID and timestamps)
115
+ */
116
+ function generateEntitySchema(entity) {
117
+ const properties = {
118
+ id: { type: 'string', description: 'Unique identifier' },
119
+ };
120
+ // Add all fields including computed
121
+ for (const [fieldName, field] of Object.entries(entity.fields)) {
122
+ properties[fieldName] = generateFieldSchema(fieldName, field);
123
+ }
124
+ // Add timestamps if enabled
125
+ if (entity.behaviors.timestamps) {
126
+ properties.createdAt = { type: 'string', format: 'date-time' };
127
+ properties.updatedAt = { type: 'string', format: 'date-time' };
128
+ }
129
+ // Add soft delete field
130
+ if (entity.behaviors.softDelete) {
131
+ properties.deletedAt = {
132
+ type: 'string',
133
+ format: 'date-time',
134
+ nullable: true,
135
+ description: 'Soft delete timestamp'
136
+ };
137
+ }
138
+ return {
139
+ type: 'object',
140
+ properties,
141
+ required: ['id'],
142
+ };
143
+ }
144
+ /**
145
+ * Generate filter parameter schema for a field
146
+ */
147
+ function generateFilterSchema(fieldName, field) {
148
+ const baseType = getOpenAPIType(field);
149
+ switch (field.type) {
150
+ case 'text':
151
+ return {
152
+ oneOf: [
153
+ { type: 'string' },
154
+ {
155
+ type: 'object',
156
+ properties: {
157
+ eq: { type: 'string' },
158
+ ne: { type: 'string' },
159
+ contains: { type: 'string' },
160
+ startsWith: { type: 'string' },
161
+ endsWith: { type: 'string' },
162
+ }
163
+ }
164
+ ]
165
+ };
166
+ case 'number':
167
+ return {
168
+ oneOf: [
169
+ { type: 'number' },
170
+ {
171
+ type: 'object',
172
+ properties: {
173
+ eq: { type: 'number' },
174
+ ne: { type: 'number' },
175
+ gt: { type: 'number' },
176
+ gte: { type: 'number' },
177
+ lt: { type: 'number' },
178
+ lte: { type: 'number' },
179
+ }
180
+ }
181
+ ]
182
+ };
183
+ case 'boolean':
184
+ return { type: 'boolean' };
185
+ case 'date':
186
+ return {
187
+ oneOf: [
188
+ { type: 'string', format: 'date-time' },
189
+ {
190
+ type: 'object',
191
+ properties: {
192
+ eq: { type: 'string', format: 'date-time' },
193
+ ne: { type: 'string', format: 'date-time' },
194
+ gt: { type: 'string', format: 'date-time' },
195
+ gte: { type: 'string', format: 'date-time' },
196
+ lt: { type: 'string', format: 'date-time' },
197
+ lte: { type: 'string', format: 'date-time' },
198
+ }
199
+ }
200
+ ]
201
+ };
202
+ default:
203
+ return baseType;
204
+ }
205
+ }
206
+ /**
207
+ * Generate security requirement for operation
208
+ */
209
+ function getSecurityRequirement(isProtected) {
210
+ return isProtected ? [{ bearerAuth: [] }] : [];
211
+ }
212
+ /**
213
+ * Generate OpenAPI paths for an entity
214
+ */
215
+ function generateEntityPaths(entity, baseUrl) {
216
+ const routerName = (0, utils_1.toCamelCase)(entity.name);
217
+ const paths = {};
218
+ // List operation
219
+ paths[`${baseUrl}/${routerName}.list`] = {
220
+ get: {
221
+ summary: `List ${entity.name} records`,
222
+ description: `Retrieve a paginated list of ${entity.name} records with optional filtering and search`,
223
+ operationId: `${routerName}List`,
224
+ tags: [entity.name],
225
+ security: getSecurityRequirement(entity.protected.list),
226
+ parameters: [
227
+ {
228
+ name: 'page',
229
+ in: 'query',
230
+ schema: { type: 'integer', minimum: 1, default: 1 },
231
+ description: 'Page number for pagination'
232
+ },
233
+ {
234
+ name: 'limit',
235
+ in: 'query',
236
+ schema: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
237
+ description: 'Number of items per page'
238
+ },
239
+ {
240
+ name: 'search',
241
+ in: 'query',
242
+ schema: { type: 'string' },
243
+ description: 'Search across all text fields'
244
+ },
245
+ ],
246
+ responses: {
247
+ '200': {
248
+ description: 'Successful response',
249
+ content: {
250
+ 'application/json': {
251
+ schema: {
252
+ type: 'object',
253
+ properties: {
254
+ items: {
255
+ type: 'array',
256
+ items: { $ref: `#/components/schemas/${entity.name}` }
257
+ },
258
+ total: { type: 'integer', description: 'Total number of records' },
259
+ page: { type: 'integer', description: 'Current page number' },
260
+ limit: { type: 'integer', description: 'Items per page' },
261
+ hasMore: { type: 'boolean', description: 'Whether more pages exist' },
262
+ }
263
+ }
264
+ }
265
+ }
266
+ },
267
+ '401': entity.protected.list ? { description: 'Unauthorized' } : undefined,
268
+ }
269
+ }
270
+ };
271
+ // Get operation
272
+ paths[`${baseUrl}/${routerName}.get`] = {
273
+ get: {
274
+ summary: `Get ${entity.name} by ID`,
275
+ description: `Retrieve a single ${entity.name} record by its unique identifier`,
276
+ operationId: `${routerName}Get`,
277
+ tags: [entity.name],
278
+ security: getSecurityRequirement(entity.protected.get),
279
+ parameters: [
280
+ {
281
+ name: 'id',
282
+ in: 'query',
283
+ required: true,
284
+ schema: { type: 'string' },
285
+ description: 'Unique identifier'
286
+ }
287
+ ],
288
+ responses: {
289
+ '200': {
290
+ description: 'Successful response',
291
+ content: {
292
+ 'application/json': {
293
+ schema: { $ref: `#/components/schemas/${entity.name}` }
294
+ }
295
+ }
296
+ },
297
+ '401': entity.protected.get ? { description: 'Unauthorized' } : undefined,
298
+ '404': { description: 'Record not found' },
299
+ }
300
+ }
301
+ };
302
+ // Create operation
303
+ paths[`${baseUrl}/${routerName}.create`] = {
304
+ post: {
305
+ summary: `Create ${entity.name}`,
306
+ description: `Create a new ${entity.name} record`,
307
+ operationId: `${routerName}Create`,
308
+ tags: [entity.name],
309
+ security: getSecurityRequirement(entity.protected.create),
310
+ requestBody: {
311
+ required: true,
312
+ content: {
313
+ 'application/json': {
314
+ schema: { $ref: `#/components/schemas/${entity.name}CreateInput` }
315
+ }
316
+ }
317
+ },
318
+ responses: {
319
+ '201': {
320
+ description: 'Successfully created',
321
+ content: {
322
+ 'application/json': {
323
+ schema: { $ref: `#/components/schemas/${entity.name}` }
324
+ }
325
+ }
326
+ },
327
+ '400': { description: 'Validation error' },
328
+ '401': entity.protected.create ? { description: 'Unauthorized' } : undefined,
329
+ }
330
+ }
331
+ };
332
+ // Update operation
333
+ paths[`${baseUrl}/${routerName}.update`] = {
334
+ patch: {
335
+ summary: `Update ${entity.name}`,
336
+ description: `Update an existing ${entity.name} record`,
337
+ operationId: `${routerName}Update`,
338
+ tags: [entity.name],
339
+ security: getSecurityRequirement(entity.protected.update),
340
+ requestBody: {
341
+ required: true,
342
+ content: {
343
+ 'application/json': {
344
+ schema: {
345
+ type: 'object',
346
+ properties: {
347
+ id: { type: 'string', description: 'Record ID to update' },
348
+ data: { $ref: `#/components/schemas/${entity.name}UpdateInput` }
349
+ },
350
+ required: ['id', 'data']
351
+ }
352
+ }
353
+ }
354
+ },
355
+ responses: {
356
+ '200': {
357
+ description: 'Successfully updated',
358
+ content: {
359
+ 'application/json': {
360
+ schema: { $ref: `#/components/schemas/${entity.name}` }
361
+ }
362
+ }
363
+ },
364
+ '400': { description: 'Validation error' },
365
+ '401': entity.protected.update ? { description: 'Unauthorized' } : undefined,
366
+ '404': { description: 'Record not found' },
367
+ }
368
+ }
369
+ };
370
+ // Remove operation
371
+ paths[`${baseUrl}/${routerName}.remove`] = {
372
+ delete: {
373
+ summary: `Delete ${entity.name}`,
374
+ description: entity.behaviors.softDelete
375
+ ? `Soft delete a ${entity.name} record (sets deletedAt timestamp)`
376
+ : `Permanently delete a ${entity.name} record`,
377
+ operationId: `${routerName}Remove`,
378
+ tags: [entity.name],
379
+ security: getSecurityRequirement(entity.protected.remove),
380
+ parameters: [
381
+ {
382
+ name: 'id',
383
+ in: 'query',
384
+ required: true,
385
+ schema: { type: 'string' },
386
+ description: 'Record ID to delete'
387
+ }
388
+ ],
389
+ responses: {
390
+ '200': {
391
+ description: 'Successfully deleted',
392
+ content: {
393
+ 'application/json': {
394
+ schema: { $ref: `#/components/schemas/${entity.name}` }
395
+ }
396
+ }
397
+ },
398
+ '401': entity.protected.remove ? { description: 'Unauthorized' } : undefined,
399
+ '404': { description: 'Record not found' },
400
+ }
401
+ }
402
+ };
403
+ // Batch operations
404
+ paths[`${baseUrl}/${routerName}.createMany`] = {
405
+ post: {
406
+ summary: `Bulk create ${entity.name} records`,
407
+ description: `Create multiple ${entity.name} records in a single request (max 100)`,
408
+ operationId: `${routerName}CreateMany`,
409
+ tags: [entity.name],
410
+ security: getSecurityRequirement(entity.protected.create),
411
+ requestBody: {
412
+ required: true,
413
+ content: {
414
+ 'application/json': {
415
+ schema: {
416
+ type: 'object',
417
+ properties: {
418
+ items: {
419
+ type: 'array',
420
+ items: { $ref: `#/components/schemas/${entity.name}CreateInput` },
421
+ maxItems: 100
422
+ }
423
+ },
424
+ required: ['items']
425
+ }
426
+ }
427
+ }
428
+ },
429
+ responses: {
430
+ '201': {
431
+ description: 'Successfully created',
432
+ content: {
433
+ 'application/json': {
434
+ schema: {
435
+ type: 'object',
436
+ properties: {
437
+ created: {
438
+ type: 'array',
439
+ items: { $ref: `#/components/schemas/${entity.name}` }
440
+ },
441
+ count: { type: 'integer' }
442
+ }
443
+ }
444
+ }
445
+ }
446
+ },
447
+ '400': { description: 'Validation error' },
448
+ '401': entity.protected.create ? { description: 'Unauthorized' } : undefined,
449
+ }
450
+ }
451
+ };
452
+ return paths;
453
+ }
454
+ /**
455
+ * Generate complete OpenAPI specification
456
+ */
457
+ function generateOpenAPISpec(manifest) {
458
+ const baseUrl = '/api/trpc';
459
+ const spec = {
460
+ openapi: '3.0.0',
461
+ info: {
462
+ title: 'Generated API Documentation',
463
+ description: 'Auto-generated API documentation from Archetype entity definitions',
464
+ version: '1.0.0',
465
+ },
466
+ servers: [
467
+ { url: 'http://localhost:3000', description: 'Development server' },
468
+ { url: 'https://api.example.com', description: 'Production server' },
469
+ ],
470
+ paths: {},
471
+ components: {
472
+ schemas: {},
473
+ },
474
+ };
475
+ // Add security schemes if auth is enabled
476
+ if (manifest.auth.enabled) {
477
+ spec.components.securitySchemes = {
478
+ bearerAuth: {
479
+ type: 'http',
480
+ scheme: 'bearer',
481
+ bearerFormat: 'JWT',
482
+ description: 'JWT token from authentication'
483
+ }
484
+ };
485
+ }
486
+ // Generate schemas and paths for each entity
487
+ for (const entity of manifest.entities) {
488
+ // Add entity schemas
489
+ spec.components.schemas[entity.name] = generateEntitySchema(entity);
490
+ spec.components.schemas[`${entity.name}CreateInput`] = generateCreateSchema(entity);
491
+ spec.components.schemas[`${entity.name}UpdateInput`] = {
492
+ ...generateCreateSchema(entity),
493
+ required: undefined, // All fields optional for updates
494
+ };
495
+ // Add entity paths
496
+ const entityPaths = generateEntityPaths(entity, baseUrl);
497
+ Object.assign(spec.paths, entityPaths);
498
+ }
499
+ return spec;
500
+ }
501
+ /**
502
+ * Generate Swagger UI HTML page
503
+ */
504
+ function generateSwaggerUI() {
505
+ return `<!DOCTYPE html>
506
+ <html lang="en">
507
+ <head>
508
+ <meta charset="UTF-8">
509
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
510
+ <title>API Documentation - Swagger UI</title>
511
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" />
512
+ <style>
513
+ body { margin: 0; padding: 0; }
514
+ </style>
515
+ </head>
516
+ <body>
517
+ <div id="swagger-ui"></div>
518
+
519
+ <script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js"></script>
520
+ <script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-standalone-preset.js"></script>
521
+ <script>
522
+ window.onload = function() {
523
+ window.ui = SwaggerUIBundle({
524
+ url: './openapi.json',
525
+ dom_id: '#swagger-ui',
526
+ deepLinking: true,
527
+ presets: [
528
+ SwaggerUIBundle.presets.apis,
529
+ SwaggerUIStandalonePreset
530
+ ],
531
+ plugins: [
532
+ SwaggerUIBundle.plugins.DownloadUrl
533
+ ],
534
+ layout: 'StandaloneLayout',
535
+ defaultModelsExpandDepth: 1,
536
+ defaultModelExpandDepth: 1,
537
+ });
538
+ };
539
+ </script>
540
+ </body>
541
+ </html>
542
+ `;
543
+ }
544
+ /**
545
+ * Generate Markdown documentation as alternative to Swagger UI
546
+ */
547
+ function generateMarkdownDocs(manifest) {
548
+ const lines = [];
549
+ lines.push('# API Documentation');
550
+ lines.push('');
551
+ lines.push('Auto-generated API documentation from Archetype entity definitions.');
552
+ lines.push('');
553
+ lines.push('## Base URL');
554
+ lines.push('');
555
+ lines.push('```');
556
+ lines.push('http://localhost:3000/api/trpc');
557
+ lines.push('```');
558
+ lines.push('');
559
+ if (manifest.auth.enabled) {
560
+ lines.push('## Authentication');
561
+ lines.push('');
562
+ lines.push('Protected endpoints require a JWT token in the Authorization header:');
563
+ lines.push('');
564
+ lines.push('```');
565
+ lines.push('Authorization: Bearer <your-jwt-token>');
566
+ lines.push('```');
567
+ lines.push('');
568
+ }
569
+ lines.push('## Entities');
570
+ lines.push('');
571
+ for (const entity of manifest.entities) {
572
+ const routerName = (0, utils_1.toCamelCase)(entity.name);
573
+ lines.push(`### ${entity.name}`);
574
+ lines.push('');
575
+ // Fields
576
+ lines.push('#### Fields');
577
+ lines.push('');
578
+ lines.push('| Field | Type | Required | Description |');
579
+ lines.push('|-------|------|----------|-------------|');
580
+ for (const [fieldName, field] of Object.entries(entity.fields)) {
581
+ const type = getOpenAPIType(field);
582
+ const required = field.required ? 'Yes' : 'No';
583
+ const description = field.label || '-';
584
+ lines.push(`| ${fieldName} | ${type.type} | ${required} | ${description} |`);
585
+ }
586
+ lines.push('');
587
+ // Endpoints
588
+ lines.push('#### Endpoints');
589
+ lines.push('');
590
+ // List
591
+ const listAuth = entity.protected.list ? '🔒 Protected' : '🌐 Public';
592
+ lines.push(`**List ${entity.name}s** ${listAuth}`);
593
+ lines.push('');
594
+ lines.push('```');
595
+ lines.push(`GET /api/trpc/${routerName}.list?page=1&limit=10`);
596
+ lines.push('```');
597
+ lines.push('');
598
+ // Get
599
+ const getAuth = entity.protected.get ? '🔒 Protected' : '🌐 Public';
600
+ lines.push(`**Get ${entity.name} by ID** ${getAuth}`);
601
+ lines.push('');
602
+ lines.push('```');
603
+ lines.push(`GET /api/trpc/${routerName}.get?id=<id>`);
604
+ lines.push('```');
605
+ lines.push('');
606
+ // Create
607
+ const createAuth = entity.protected.create ? '🔒 Protected' : '🌐 Public';
608
+ lines.push(`**Create ${entity.name}** ${createAuth}`);
609
+ lines.push('');
610
+ lines.push('```');
611
+ lines.push(`POST /api/trpc/${routerName}.create`);
612
+ lines.push('Content-Type: application/json');
613
+ lines.push('');
614
+ // Generate example
615
+ const exampleFields = [];
616
+ for (const [fieldName, field] of Object.entries(entity.fields)) {
617
+ if (field.type === 'computed')
618
+ continue;
619
+ if (!field.required)
620
+ continue;
621
+ let exampleValue = 'value';
622
+ if (field.type === 'text')
623
+ exampleValue = `"example ${fieldName}"`;
624
+ if (field.type === 'number')
625
+ exampleValue = '42';
626
+ if (field.type === 'boolean')
627
+ exampleValue = 'true';
628
+ if (field.enumValues)
629
+ exampleValue = `"${field.enumValues[0]}"`;
630
+ exampleFields.push(` "${fieldName}": ${exampleValue}`);
631
+ }
632
+ lines.push('{');
633
+ lines.push(exampleFields.join(',\n'));
634
+ lines.push('}');
635
+ lines.push('```');
636
+ lines.push('');
637
+ // Update
638
+ const updateAuth = entity.protected.update ? '🔒 Protected' : '🌐 Public';
639
+ lines.push(`**Update ${entity.name}** ${updateAuth}`);
640
+ lines.push('');
641
+ lines.push('```');
642
+ lines.push(`PATCH /api/trpc/${routerName}.update`);
643
+ lines.push('Content-Type: application/json');
644
+ lines.push('');
645
+ lines.push('{');
646
+ lines.push(' "id": "<id>",');
647
+ lines.push(' "data": { /* fields to update */ }');
648
+ lines.push('}');
649
+ lines.push('```');
650
+ lines.push('');
651
+ // Delete
652
+ const deleteAuth = entity.protected.remove ? '🔒 Protected' : '🌐 Public';
653
+ lines.push(`**Delete ${entity.name}** ${deleteAuth}`);
654
+ lines.push('');
655
+ lines.push('```');
656
+ lines.push(`DELETE /api/trpc/${routerName}.remove?id=<id>`);
657
+ lines.push('```');
658
+ lines.push('');
659
+ lines.push('---');
660
+ lines.push('');
661
+ }
662
+ return lines.join('\n');
663
+ }
664
+ /**
665
+ * OpenAPI documentation generator
666
+ */
667
+ exports.openapiGenerator = {
668
+ name: 'openapi-docs',
669
+ description: 'Generates OpenAPI specification and interactive Swagger UI documentation',
670
+ generate(manifest, ctx) {
671
+ const files = [];
672
+ // Generate OpenAPI JSON spec
673
+ const spec = generateOpenAPISpec(manifest);
674
+ files.push({
675
+ path: 'docs/openapi.json',
676
+ content: JSON.stringify(spec, null, 2),
677
+ });
678
+ // Generate Swagger UI HTML
679
+ files.push({
680
+ path: 'docs/swagger.html',
681
+ content: generateSwaggerUI(),
682
+ });
683
+ // Generate Markdown documentation
684
+ files.push({
685
+ path: 'docs/API.md',
686
+ content: generateMarkdownDocs(manifest),
687
+ });
688
+ return files;
689
+ },
690
+ };