create-charcole 2.0.4 → 2.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +290 -14
  2. package/README.md +258 -312
  3. package/bin/index.js +392 -55
  4. package/bin/lib/pkgManager.js +8 -25
  5. package/bin/lib/templateHandler.js +5 -42
  6. package/create-charcole-2.1.0.tgz +0 -0
  7. package/package.json +2 -2
  8. package/packages/swagger/BACKWARD_COMPATIBILITY.md +145 -0
  9. package/packages/swagger/CHANGELOG.md +404 -0
  10. package/packages/swagger/README.md +578 -0
  11. package/packages/swagger/charcole-swagger-1.0.0.tgz +0 -0
  12. package/packages/swagger/package-lock.json +1715 -0
  13. package/packages/swagger/package.json +44 -0
  14. package/packages/swagger/src/helpers.js +427 -0
  15. package/packages/swagger/src/index.d.ts +126 -0
  16. package/packages/swagger/src/index.js +12 -0
  17. package/packages/swagger/src/setup.js +100 -0
  18. package/template/js/.env.example +8 -0
  19. package/template/js/README.md +128 -5
  20. package/template/js/basePackage.json +11 -13
  21. package/template/js/src/app.js +8 -2
  22. package/template/js/src/config/swagger.config.js +15 -0
  23. package/template/js/src/lib/swagger/SWAGGER_GUIDE.md +561 -0
  24. package/template/js/src/modules/auth/auth.constants.js +3 -0
  25. package/template/js/src/modules/auth/auth.controller.js +29 -0
  26. package/template/js/src/modules/auth/auth.middlewares.js +19 -0
  27. package/template/js/src/modules/auth/auth.routes.js +131 -0
  28. package/template/js/src/modules/auth/auth.schemas.js +60 -0
  29. package/template/js/src/modules/auth/auth.service.js +67 -0
  30. package/template/js/src/modules/auth/package.json +6 -0
  31. package/template/js/src/modules/health/controller.js +104 -3
  32. package/template/js/src/modules/swagger/charcole-swagger-1.0.0.tgz +0 -0
  33. package/template/js/src/modules/swagger/package.json +5 -0
  34. package/template/js/src/repositories/user.repo.js +19 -0
  35. package/template/js/src/routes/index.js +25 -0
  36. package/template/js/src/routes/protected.js +57 -0
  37. package/template/ts/.env.example +8 -0
  38. package/template/ts/README.md +128 -5
  39. package/template/ts/basePackage.json +19 -15
  40. package/template/ts/build.js +46 -0
  41. package/template/ts/src/app.ts +12 -7
  42. package/template/ts/src/config/swagger.config.ts +30 -0
  43. package/template/ts/src/lib/swagger/SWAGGER_GUIDE.md +561 -0
  44. package/template/ts/src/middlewares/errorHandler.ts +15 -23
  45. package/template/ts/src/middlewares/requestLogger.ts +1 -1
  46. package/template/ts/src/middlewares/validateRequest.ts +1 -1
  47. package/template/ts/src/modules/auth/auth.constants.ts +6 -0
  48. package/template/ts/src/modules/auth/auth.controller.ts +32 -0
  49. package/template/ts/src/modules/auth/auth.middlewares.ts +46 -0
  50. package/template/ts/src/modules/auth/auth.routes.ts +52 -0
  51. package/template/ts/src/modules/auth/auth.schemas.ts +73 -0
  52. package/template/ts/src/modules/auth/auth.service.ts +106 -0
  53. package/template/ts/src/modules/auth/package.json +10 -0
  54. package/template/ts/src/modules/health/controller.ts +61 -45
  55. package/template/ts/src/modules/swagger/charcole-swagger-1.0.0.tgz +0 -0
  56. package/template/ts/src/modules/swagger/package.json +5 -0
  57. package/template/ts/src/repositories/user.repo.ts +33 -0
  58. package/template/ts/src/routes/index.ts +24 -0
  59. package/template/ts/src/routes/protected.ts +46 -0
  60. package/template/ts/src/server.ts +3 -4
  61. package/template/ts/src/utils/logger.ts +1 -1
  62. package/template/ts/tsconfig.json +14 -7
  63. package/tmpclaude-1049-cwd +1 -0
  64. package/tmpclaude-3e37-cwd +1 -0
  65. package/tmpclaude-4d73-cwd +1 -0
  66. package/tmpclaude-8a8e-cwd +1 -0
  67. package/template/js/ARCHITECTURE_DIAGRAMS.md +0 -283
  68. package/template/js/CHECKLIST.md +0 -279
  69. package/template/js/COMPLETE.md +0 -405
  70. package/template/js/ERROR_HANDLING.md +0 -393
  71. package/template/js/IMPLEMENTATION.md +0 -368
  72. package/template/js/IMPLEMENTATION_COMPLETE.md +0 -363
  73. package/template/js/INDEX.md +0 -290
  74. package/template/js/QUICK_REFERENCE.md +0 -270
  75. package/template/js/package.json +0 -28
  76. package/template/js/src/routes.js +0 -17
  77. package/template/js/test-api.js +0 -100
  78. package/template/ts/ARCHITECTURE_DIAGRAMS.md +0 -283
  79. package/template/ts/CHECKLIST.md +0 -279
  80. package/template/ts/COMPLETE.md +0 -405
  81. package/template/ts/ERROR_HANDLING.md +0 -393
  82. package/template/ts/IMPLEMENTATION.md +0 -368
  83. package/template/ts/IMPLEMENTATION_COMPLETE.md +0 -363
  84. package/template/ts/INDEX.md +0 -290
  85. package/template/ts/QUICK_REFERENCE.md +0 -270
  86. package/template/ts/package.json +0 -32
  87. package/template/ts/src/app.js +0 -75
  88. package/template/ts/src/config/constants.js +0 -20
  89. package/template/ts/src/config/env.js +0 -26
  90. package/template/ts/src/middlewares/errorHandler.js +0 -180
  91. package/template/ts/src/middlewares/requestLogger.js +0 -33
  92. package/template/ts/src/middlewares/validateRequest.js +0 -42
  93. package/template/ts/src/modules/health/controller.js +0 -50
  94. package/template/ts/src/routes.js +0 -17
  95. package/template/ts/src/routes.ts +0 -16
  96. package/template/ts/src/server.js +0 -38
  97. package/template/ts/src/utils/AppError.js +0 -182
  98. package/template/ts/src/utils/logger.js +0 -73
  99. package/template/ts/src/utils/response.js +0 -51
  100. package/template/ts/test-api.js +0 -100
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@charcoles/swagger",
3
+ "version": "1.0.0",
4
+ "description": "Auto-generated Swagger documentation for Charcole APIs",
5
+ "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.d.ts",
11
+ "default": "./src/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "test": "echo \"No tests yet\" && exit 0",
16
+ "prepare": "echo 'Ready for npm link'"
17
+ },
18
+ "dependencies": {
19
+ "swagger-ui-express": "^4.6.3",
20
+ "swagger-jsdoc": "^6.2.8",
21
+ "glob": "^10.3.10",
22
+ "zod-to-json-schema": "^3.22.4"
23
+ },
24
+ "peerDependencies": {
25
+ "zod": "^3.0.0"
26
+ },
27
+ "peerDependenciesMeta": {
28
+ "zod": {
29
+ "optional": true
30
+ }
31
+ },
32
+ "keywords": [
33
+ "charcole",
34
+ "swagger",
35
+ "openapi",
36
+ "documentation",
37
+ "express"
38
+ ],
39
+ "author": "Sheraz Manzoor",
40
+ "license": "ISC",
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }
@@ -0,0 +1,427 @@
1
+ import { zodToJsonSchema } from "zod-to-json-schema";
2
+
3
+ /**
4
+ * Convert Zod schema to OpenAPI JSON Schema
5
+ * @param {import('zod').ZodType} zodSchema - Zod schema to convert
6
+ * @param {string} name - Schema name for reference
7
+ * @returns {Object} OpenAPI-compatible JSON Schema
8
+ */
9
+ export function convertZodToOpenAPI(zodSchema, name) {
10
+ if (!zodSchema) return null;
11
+
12
+ try {
13
+ // Convert Zod to JSON Schema
14
+ const jsonSchema = zodToJsonSchema(zodSchema, {
15
+ name,
16
+ target: "openApi3",
17
+ $refStrategy: "none",
18
+ });
19
+
20
+ // Remove $schema property as it's not needed in OpenAPI components
21
+ if (jsonSchema.$schema) {
22
+ delete jsonSchema.$schema;
23
+ }
24
+
25
+ // Handle case where zodToJsonSchema returns a $ref with definitions
26
+ // This happens with complex nested schemas
27
+ if (jsonSchema.$ref && jsonSchema.definitions) {
28
+ // Extract the actual schema from definitions
29
+ const refName = jsonSchema.$ref.split("/").pop();
30
+ if (jsonSchema.definitions[refName]) {
31
+ const actualSchema = jsonSchema.definitions[refName];
32
+ // Remove $schema from the extracted definition too
33
+ if (actualSchema.$schema) {
34
+ delete actualSchema.$schema;
35
+ }
36
+ return actualSchema;
37
+ }
38
+ }
39
+
40
+ // Remove definitions if present (OpenAPI handles these at component level)
41
+ if (jsonSchema.definitions) {
42
+ delete jsonSchema.definitions;
43
+ }
44
+
45
+ return jsonSchema;
46
+ } catch (error) {
47
+ console.warn(`Failed to convert Zod schema "${name}":`, error.message);
48
+ return null;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Extract body schema from a Zod schema that has a .body property
54
+ * Common pattern: z.object({ body: z.object({ ... }) })
55
+ * @param {import('zod').ZodType} schema - Zod schema
56
+ * @returns {import('zod').ZodType|null} Body schema if found
57
+ */
58
+ export function extractBodySchema(schema) {
59
+ if (!schema) return null;
60
+
61
+ try {
62
+ // Check if schema has a .body property (common pattern)
63
+ if (schema._def && schema._def.shape && schema._def.shape().body) {
64
+ return schema._def.shape().body;
65
+ }
66
+ } catch (error) {
67
+ // If extraction fails, return original schema
68
+ }
69
+
70
+ return schema;
71
+ }
72
+
73
+ /**
74
+ * Generate common response schemas
75
+ * @returns {Object} OpenAPI response components
76
+ */
77
+ export function getCommonResponses() {
78
+ return {
79
+ Success: {
80
+ description: "Success",
81
+ content: {
82
+ "application/json": {
83
+ schema: {
84
+ type: "object",
85
+ properties: {
86
+ success: {
87
+ type: "boolean",
88
+ example: true,
89
+ },
90
+ message: {
91
+ type: "string",
92
+ example: "Operation successful",
93
+ },
94
+ data: {
95
+ type: "object",
96
+ },
97
+ },
98
+ },
99
+ },
100
+ },
101
+ },
102
+ ValidationError: {
103
+ description: "Validation Error",
104
+ content: {
105
+ "application/json": {
106
+ schema: {
107
+ type: "object",
108
+ properties: {
109
+ success: {
110
+ type: "boolean",
111
+ example: false,
112
+ },
113
+ message: {
114
+ type: "string",
115
+ example: "Validation failed",
116
+ },
117
+ errors: {
118
+ type: "array",
119
+ items: {
120
+ type: "object",
121
+ properties: {
122
+ field: {
123
+ type: "string",
124
+ },
125
+ message: {
126
+ type: "string",
127
+ },
128
+ },
129
+ },
130
+ },
131
+ },
132
+ },
133
+ },
134
+ },
135
+ },
136
+ Unauthorized: {
137
+ description: "Unauthorized - Invalid or missing token",
138
+ content: {
139
+ "application/json": {
140
+ schema: {
141
+ type: "object",
142
+ properties: {
143
+ success: {
144
+ type: "boolean",
145
+ example: false,
146
+ },
147
+ message: {
148
+ type: "string",
149
+ example: "Unauthorized",
150
+ },
151
+ },
152
+ },
153
+ },
154
+ },
155
+ },
156
+ Forbidden: {
157
+ description: "Forbidden - Insufficient permissions",
158
+ content: {
159
+ "application/json": {
160
+ schema: {
161
+ type: "object",
162
+ properties: {
163
+ success: {
164
+ type: "boolean",
165
+ example: false,
166
+ },
167
+ message: {
168
+ type: "string",
169
+ example: "Forbidden",
170
+ },
171
+ },
172
+ },
173
+ },
174
+ },
175
+ },
176
+ NotFound: {
177
+ description: "Resource not found",
178
+ content: {
179
+ "application/json": {
180
+ schema: {
181
+ type: "object",
182
+ properties: {
183
+ success: {
184
+ type: "boolean",
185
+ example: false,
186
+ },
187
+ message: {
188
+ type: "string",
189
+ example: "Resource not found",
190
+ },
191
+ },
192
+ },
193
+ },
194
+ },
195
+ },
196
+ InternalError: {
197
+ description: "Internal server error",
198
+ content: {
199
+ "application/json": {
200
+ schema: {
201
+ type: "object",
202
+ properties: {
203
+ success: {
204
+ type: "boolean",
205
+ example: false,
206
+ },
207
+ message: {
208
+ type: "string",
209
+ example: "Internal server error",
210
+ },
211
+ },
212
+ },
213
+ },
214
+ },
215
+ },
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Detect security requirements from middleware chain
221
+ * @param {Array} middlewares - Array of middleware functions
222
+ * @returns {Array} Security requirements
223
+ */
224
+ export function detectSecurity(middlewares) {
225
+ if (!Array.isArray(middlewares)) return [];
226
+
227
+ const securityRequirements = [];
228
+
229
+ for (const middleware of middlewares) {
230
+ const name = middleware.name || "";
231
+
232
+ // Check for authentication middleware
233
+ if (
234
+ name.includes("auth") ||
235
+ name.includes("Auth") ||
236
+ name.includes("requireAuth") ||
237
+ name.includes("authenticate") ||
238
+ name.includes("jwt") ||
239
+ name.includes("JWT")
240
+ ) {
241
+ securityRequirements.push({ bearerAuth: [] });
242
+ break; // Only add once
243
+ }
244
+ }
245
+
246
+ return securityRequirements;
247
+ }
248
+
249
+ /**
250
+ * Create a minimal Swagger doc helper
251
+ * Generates a @swagger comment block from simple options
252
+ * @param {Object} options - Documentation options
253
+ * @returns {string} JSDoc comment block
254
+ */
255
+ export function createSwaggerDoc(options) {
256
+ const {
257
+ method = "get",
258
+ path,
259
+ summary,
260
+ description,
261
+ tags = [],
262
+ requestSchema,
263
+ responseSchemaName,
264
+ security = false,
265
+ parameters = [],
266
+ } = options;
267
+
268
+ let doc = `/**\n * @swagger\n * ${path}:\n * ${method.toLowerCase()}:\n`;
269
+
270
+ if (summary) {
271
+ doc += ` * summary: ${summary}\n`;
272
+ }
273
+
274
+ if (description) {
275
+ doc += ` * description: ${description}\n`;
276
+ }
277
+
278
+ if (tags.length > 0) {
279
+ doc += ` * tags:\n`;
280
+ tags.forEach((tag) => {
281
+ doc += ` * - ${tag}\n`;
282
+ });
283
+ }
284
+
285
+ if (security) {
286
+ doc += ` * security:\n * - bearerAuth: []\n`;
287
+ }
288
+
289
+ if (parameters.length > 0) {
290
+ doc += ` * parameters:\n`;
291
+ parameters.forEach((param) => {
292
+ doc += ` * - in: ${param.in}\n`;
293
+ doc += ` * name: ${param.name}\n`;
294
+ if (param.required) {
295
+ doc += ` * required: true\n`;
296
+ }
297
+ doc += ` * schema:\n`;
298
+ doc += ` * type: ${param.type || "string"}\n`;
299
+ if (param.description) {
300
+ doc += ` * description: ${param.description}\n`;
301
+ }
302
+ });
303
+ }
304
+
305
+ if (
306
+ requestSchema &&
307
+ (method === "post" || method === "put" || method === "patch")
308
+ ) {
309
+ doc += ` * requestBody:\n`;
310
+ doc += ` * required: true\n`;
311
+ doc += ` * content:\n`;
312
+ doc += ` * application/json:\n`;
313
+ doc += ` * schema:\n`;
314
+ doc += ` * $ref: '#/components/schemas/${requestSchema}'\n`;
315
+ }
316
+
317
+ doc += ` * responses:\n`;
318
+ if (responseSchemaName) {
319
+ doc += ` * 200:\n`;
320
+ doc += ` * $ref: '#/components/responses/${responseSchemaName}'\n`;
321
+ } else {
322
+ doc += ` * 200:\n`;
323
+ doc += ` * description: Success\n`;
324
+ }
325
+
326
+ if (method === "post" || method === "put" || method === "patch") {
327
+ doc += ` * 400:\n`;
328
+ doc += ` * $ref: '#/components/responses/ValidationError'\n`;
329
+ }
330
+
331
+ if (security) {
332
+ doc += ` * 401:\n`;
333
+ doc += ` * $ref: '#/components/responses/Unauthorized'\n`;
334
+ }
335
+
336
+ doc += ` */`;
337
+
338
+ return doc;
339
+ }
340
+
341
+ /**
342
+ * Register Zod schemas for auto-documentation
343
+ * @param {Object} schemas - Object containing Zod schemas
344
+ * @returns {Object} OpenAPI components schemas
345
+ */
346
+ export function registerSchemas(schemas) {
347
+ const components = {};
348
+
349
+ for (const [name, schema] of Object.entries(schemas)) {
350
+ if (!schema) continue;
351
+
352
+ // Extract body schema if it exists
353
+ const actualSchema = extractBodySchema(schema) || schema;
354
+
355
+ // Convert to OpenAPI
356
+ const converted = convertZodToOpenAPI(actualSchema, name);
357
+ if (converted) {
358
+ components[name] = converted;
359
+ }
360
+ }
361
+
362
+ return components;
363
+ }
364
+
365
+ /**
366
+ * Simplified API for quick endpoint documentation
367
+ * @param {string} method - HTTP method
368
+ * @param {string} path - Endpoint path
369
+ * @param {Object} options - Documentation options
370
+ * @returns {Object} OpenAPI path object
371
+ */
372
+ export function endpoint(method, path, options = {}) {
373
+ const {
374
+ summary,
375
+ description,
376
+ tags = [],
377
+ schema,
378
+ responseSchema,
379
+ security = false,
380
+ params = [],
381
+ } = options;
382
+
383
+ const pathItem = {
384
+ summary,
385
+ description,
386
+ tags,
387
+ };
388
+
389
+ if (security) {
390
+ pathItem.security = [{ bearerAuth: [] }];
391
+ }
392
+
393
+ if (params.length > 0) {
394
+ pathItem.parameters = params;
395
+ }
396
+
397
+ if (schema && (method === "post" || method === "put" || method === "patch")) {
398
+ pathItem.requestBody = {
399
+ required: true,
400
+ content: {
401
+ "application/json": {
402
+ schema: { $ref: `#/components/schemas/${schema}` },
403
+ },
404
+ },
405
+ };
406
+ }
407
+
408
+ pathItem.responses = {
409
+ 200: responseSchema
410
+ ? { $ref: `#/components/responses/${responseSchema}` }
411
+ : { description: "Success" },
412
+ };
413
+
414
+ if (method === "post" || method === "put" || method === "patch") {
415
+ pathItem.responses["400"] = {
416
+ $ref: "#/components/responses/ValidationError",
417
+ };
418
+ }
419
+
420
+ if (security) {
421
+ pathItem.responses["401"] = {
422
+ $ref: "#/components/responses/Unauthorized",
423
+ };
424
+ }
425
+
426
+ return { [path]: { [method.toLowerCase()]: pathItem } };
427
+ }
@@ -0,0 +1,126 @@
1
+ import { Application } from "express";
2
+ import { ZodType, ZodSchema } from "zod";
3
+
4
+ export interface SwaggerServer {
5
+ url: string;
6
+ description: string;
7
+ }
8
+
9
+ export interface SwaggerOptions {
10
+ title?: string;
11
+ version?: string;
12
+ description?: string;
13
+ path?: string;
14
+ servers?: SwaggerServer[];
15
+ // NEW: Auto-register Zod schemas
16
+ schemas?: Record<string, ZodType<any>>;
17
+ // NEW: Include common response templates (default: true)
18
+ includeCommonResponses?: boolean;
19
+ // NEW: Custom response schemas
20
+ customResponses?: Record<string, any>;
21
+ }
22
+
23
+ export interface OpenAPISpec {
24
+ openapi: string;
25
+ info: {
26
+ title: string;
27
+ version: string;
28
+ description: string;
29
+ };
30
+ servers: SwaggerServer[];
31
+ components: {
32
+ securitySchemes: {
33
+ bearerAuth: {
34
+ type: string;
35
+ scheme: string;
36
+ bearerFormat: string;
37
+ description: string;
38
+ };
39
+ };
40
+ schemas?: Record<string, any>;
41
+ responses?: Record<string, any>;
42
+ };
43
+ paths?: Record<string, any>;
44
+ }
45
+
46
+ export function setupSwagger(
47
+ app: Application,
48
+ options?: SwaggerOptions,
49
+ ): OpenAPISpec;
50
+
51
+ // Helper functions
52
+ export interface EndpointParameter {
53
+ in: "path" | "query" | "header";
54
+ name: string;
55
+ required?: boolean;
56
+ type?: string;
57
+ description?: string;
58
+ }
59
+
60
+ export interface EndpointOptions {
61
+ summary: string;
62
+ description?: string;
63
+ tags?: string[];
64
+ schema?: string;
65
+ responseSchema?: string;
66
+ security?: boolean;
67
+ params?: EndpointParameter[];
68
+ }
69
+
70
+ export interface SwaggerDocOptions {
71
+ method: string;
72
+ path: string;
73
+ summary: string;
74
+ description?: string;
75
+ tags?: string[];
76
+ requestSchema?: string;
77
+ responseSchemaName?: string;
78
+ security?: boolean;
79
+ parameters?: EndpointParameter[];
80
+ }
81
+
82
+ /**
83
+ * Convert Zod schema to OpenAPI JSON Schema
84
+ */
85
+ export function convertZodToOpenAPI(
86
+ zodSchema: ZodType<any>,
87
+ name: string,
88
+ ): object | null;
89
+
90
+ /**
91
+ * Extract body schema from a Zod schema that has a .body property
92
+ */
93
+ export function extractBodySchema(schema: ZodType<any>): ZodType<any> | null;
94
+
95
+ /**
96
+ * Get common response schemas (Success, ValidationError, Unauthorized, etc.)
97
+ */
98
+ export function getCommonResponses(): Record<string, any>;
99
+
100
+ /**
101
+ * Detect security requirements from middleware chain
102
+ */
103
+ export function detectSecurity(
104
+ middlewares: Function[],
105
+ ): Array<{ bearerAuth: [] }>;
106
+
107
+ /**
108
+ * Create a Swagger documentation comment block
109
+ */
110
+ export function createSwaggerDoc(options: SwaggerDocOptions): string;
111
+
112
+ /**
113
+ * Register Zod schemas for auto-documentation
114
+ */
115
+ export function registerSchemas(
116
+ schemas: Record<string, ZodType<any>>,
117
+ ): Record<string, any>;
118
+
119
+ /**
120
+ * Simplified API for quick endpoint documentation
121
+ */
122
+ export function endpoint(
123
+ method: string,
124
+ path: string,
125
+ options?: EndpointOptions,
126
+ ): Record<string, any>;
@@ -0,0 +1,12 @@
1
+ export { setupSwagger } from "./setup.js";
2
+
3
+ // Export helper functions for advanced usage
4
+ export {
5
+ convertZodToOpenAPI,
6
+ extractBodySchema,
7
+ getCommonResponses,
8
+ detectSecurity,
9
+ createSwaggerDoc,
10
+ registerSchemas,
11
+ endpoint,
12
+ } from "./helpers.js";
@@ -0,0 +1,100 @@
1
+ import swaggerUi from "swagger-ui-express";
2
+ import swaggerJSDoc from "swagger-jsdoc";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { registerSchemas, getCommonResponses } from "./helpers.js";
6
+
7
+ export function setupSwagger(app, options = {}) {
8
+ const defaultOptions = {
9
+ title: "Charcole API",
10
+ version: "1.0.0",
11
+ description: "Auto-generated API documentation",
12
+ path: "/api-docs",
13
+ servers: [{ url: "http://localhost:3000", description: "Local server" }],
14
+ // NEW: Auto-register Zod schemas
15
+ schemas: {},
16
+ // NEW: Include common response templates
17
+ includeCommonResponses: true,
18
+ // NEW: Custom response schemas
19
+ customResponses: {},
20
+ };
21
+
22
+ const config = { ...defaultOptions, ...options };
23
+
24
+ // Detect if running TypeScript or JavaScript by checking if src directory has .ts files
25
+ const srcPath = path.join(process.cwd(), "src");
26
+ const hasSrcDir = fs.existsSync(srcPath);
27
+
28
+ let isTypeScript = false;
29
+ if (hasSrcDir) {
30
+ // Check if there are any .ts files in src directory
31
+ const files = fs.readdirSync(srcPath);
32
+ isTypeScript = files.some((file) => file.endsWith(".ts"));
33
+ }
34
+
35
+ // Determine file extensions to scan
36
+ const fileExtension = isTypeScript ? "ts" : "js";
37
+
38
+ // Build API paths based on project structure
39
+ const apiPaths = [
40
+ `${process.cwd()}/src/modules/**/*.${fileExtension}`,
41
+ `${process.cwd()}/src/routes/**/*.${fileExtension}`,
42
+ ];
43
+
44
+ // NEW: Build components with auto-registered schemas
45
+ const components = {
46
+ securitySchemes: {
47
+ bearerAuth: {
48
+ type: "http",
49
+ scheme: "bearer",
50
+ bearerFormat: "JWT",
51
+ description: "Enter your JWT token in the format: your-token-here",
52
+ },
53
+ },
54
+ schemas: {},
55
+ responses: {},
56
+ };
57
+
58
+ // NEW: Register Zod schemas if provided
59
+ if (config.schemas && Object.keys(config.schemas).length > 0) {
60
+ try {
61
+ const registeredSchemas = registerSchemas(config.schemas);
62
+ components.schemas = { ...registeredSchemas };
63
+ console.log(
64
+ `✅ Auto-registered ${Object.keys(registeredSchemas).length} Zod schemas`,
65
+ );
66
+ } catch (error) {
67
+ console.warn("⚠️ Failed to register some Zod schemas:", error.message);
68
+ }
69
+ }
70
+
71
+ // NEW: Add common response templates
72
+ if (config.includeCommonResponses) {
73
+ components.responses = {
74
+ ...getCommonResponses(),
75
+ ...config.customResponses,
76
+ };
77
+ }
78
+
79
+ const openApiSpec = swaggerJSDoc({
80
+ definition: {
81
+ openapi: "3.0.0",
82
+ info: {
83
+ title: config.title,
84
+ version: config.version,
85
+ description: config.description,
86
+ },
87
+ servers: config.servers,
88
+ components,
89
+ },
90
+ apis: apiPaths,
91
+ });
92
+
93
+ app.use(config.path, swaggerUi.serve, swaggerUi.setup(openApiSpec));
94
+
95
+ console.log(
96
+ `✅ Swagger UI available at http://localhost:${process.env.PORT || 3000}${config.path}`,
97
+ );
98
+
99
+ return openApiSpec;
100
+ }