servcraft 0.2.0 → 0.3.1

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.
@@ -6,13 +6,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __esm = (fn, res) => function __init() {
10
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
- };
12
- var __export = (target, all) => {
13
- for (var name in all)
14
- __defProp(target, name, { get: all[name], enumerable: true });
15
- };
16
9
  var __copyProps = (to, from, except, desc) => {
17
10
  if (from && typeof from === "object" || typeof from === "function") {
18
11
  for (let key of __getOwnPropNames(from))
@@ -29,197 +22,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
22
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
23
  mod
31
24
  ));
32
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
25
 
34
26
  // node_modules/tsup/assets/cjs_shims.js
35
- var getImportMetaUrl, importMetaUrl;
36
- var init_cjs_shims = __esm({
37
- "node_modules/tsup/assets/cjs_shims.js"() {
38
- "use strict";
39
- getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
40
- importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
41
- }
42
- });
43
-
44
- // src/cli/utils/error-handler.ts
45
- var error_handler_exports = {};
46
- __export(error_handler_exports, {
47
- ErrorTypes: () => ErrorTypes,
48
- ServCraftError: () => ServCraftError,
49
- displayError: () => displayError,
50
- handleSystemError: () => handleSystemError,
51
- validateProject: () => validateProject
52
- });
53
- function displayError(error2) {
54
- console.error("\n" + import_chalk4.default.red.bold("\u2717 Error: ") + import_chalk4.default.red(error2.message));
55
- if (error2 instanceof ServCraftError) {
56
- if (error2.suggestions.length > 0) {
57
- console.log("\n" + import_chalk4.default.yellow.bold("\u{1F4A1} Suggestions:"));
58
- error2.suggestions.forEach((suggestion) => {
59
- console.log(import_chalk4.default.yellow(" \u2022 ") + suggestion);
60
- });
61
- }
62
- if (error2.docsLink) {
63
- console.log("\n" + import_chalk4.default.blue.bold("\u{1F4DA} Documentation: ") + import_chalk4.default.blue.underline(error2.docsLink));
64
- }
65
- }
66
- console.log();
67
- }
68
- function handleSystemError(err) {
69
- switch (err.code) {
70
- case "ENOENT":
71
- return new ServCraftError(
72
- `File or directory not found: ${err.path}`,
73
- [`Check if the path exists`, `Create the directory first`]
74
- );
75
- case "EACCES":
76
- case "EPERM":
77
- return new ServCraftError(
78
- `Permission denied: ${err.path}`,
79
- [
80
- `Check file permissions`,
81
- `Try running with elevated privileges (not recommended)`,
82
- `Change ownership of the directory`
83
- ]
84
- );
85
- case "EEXIST":
86
- return new ServCraftError(
87
- `File or directory already exists: ${err.path}`,
88
- [`Use a different name`, `Remove the existing file first`, `Use ${import_chalk4.default.cyan("--force")} to overwrite`]
89
- );
90
- case "ENOTDIR":
91
- return new ServCraftError(
92
- `Not a directory: ${err.path}`,
93
- [`Check the path`, `A file exists where a directory is expected`]
94
- );
95
- case "EISDIR":
96
- return new ServCraftError(
97
- `Is a directory: ${err.path}`,
98
- [`Cannot perform this operation on a directory`, `Did you mean to target a file?`]
99
- );
100
- default:
101
- return new ServCraftError(
102
- err.message,
103
- [`Check system error code: ${err.code}`, `Review the error details above`]
104
- );
105
- }
106
- }
107
- function validateProject() {
108
- try {
109
- const fs10 = require("fs");
110
- const path11 = require("path");
111
- if (!fs10.existsSync("package.json")) {
112
- return ErrorTypes.NOT_IN_PROJECT();
113
- }
114
- const packageJson = JSON.parse(fs10.readFileSync("package.json", "utf-8"));
115
- if (!packageJson.dependencies?.fastify) {
116
- return new ServCraftError(
117
- "This does not appear to be a ServCraft project",
118
- [
119
- `ServCraft projects require Fastify`,
120
- `Run ${import_chalk4.default.cyan("servcraft init")} to create a new project`
121
- ]
122
- );
123
- }
124
- return null;
125
- } catch (err) {
126
- return new ServCraftError(
127
- "Failed to validate project",
128
- [`Ensure you are in the project root directory`, `Check if ${import_chalk4.default.yellow("package.json")} is valid`]
129
- );
130
- }
131
- }
132
- var import_chalk4, ServCraftError, ErrorTypes;
133
- var init_error_handler = __esm({
134
- "src/cli/utils/error-handler.ts"() {
135
- "use strict";
136
- init_cjs_shims();
137
- import_chalk4 = __toESM(require("chalk"), 1);
138
- ServCraftError = class extends Error {
139
- suggestions;
140
- docsLink;
141
- constructor(message, suggestions = [], docsLink) {
142
- super(message);
143
- this.name = "ServCraftError";
144
- this.suggestions = suggestions;
145
- this.docsLink = docsLink;
146
- }
147
- };
148
- ErrorTypes = {
149
- MODULE_NOT_FOUND: (moduleName) => new ServCraftError(
150
- `Module "${moduleName}" not found`,
151
- [
152
- `Run ${import_chalk4.default.cyan("servcraft list")} to see available modules`,
153
- `Check the spelling of the module name`,
154
- `Visit ${import_chalk4.default.blue("https://github.com/Le-Sourcier/servcraft#modules")} for module list`
155
- ],
156
- "https://github.com/Le-Sourcier/servcraft#add-pre-built-modules"
157
- ),
158
- MODULE_ALREADY_EXISTS: (moduleName) => new ServCraftError(
159
- `Module "${moduleName}" already exists`,
160
- [
161
- `Use ${import_chalk4.default.cyan("servcraft add " + moduleName + " --force")} to overwrite`,
162
- `Use ${import_chalk4.default.cyan("servcraft add " + moduleName + " --update")} to update`,
163
- `Use ${import_chalk4.default.cyan("servcraft add " + moduleName + " --skip-existing")} to skip`
164
- ]
165
- ),
166
- NOT_IN_PROJECT: () => new ServCraftError(
167
- "Not in a ServCraft project directory",
168
- [
169
- `Run ${import_chalk4.default.cyan("servcraft init")} to create a new project`,
170
- `Navigate to your ServCraft project directory`,
171
- `Check if ${import_chalk4.default.yellow("package.json")} exists`
172
- ],
173
- "https://github.com/Le-Sourcier/servcraft#initialize-project"
174
- ),
175
- FILE_ALREADY_EXISTS: (fileName) => new ServCraftError(
176
- `File "${fileName}" already exists`,
177
- [
178
- `Use ${import_chalk4.default.cyan("--force")} flag to overwrite`,
179
- `Choose a different name`,
180
- `Delete the existing file first`
181
- ]
182
- ),
183
- INVALID_DATABASE: (database) => new ServCraftError(
184
- `Invalid database type: "${database}"`,
185
- [
186
- `Valid options: ${import_chalk4.default.cyan("postgresql, mysql, sqlite, mongodb, none")}`,
187
- `Use ${import_chalk4.default.cyan("servcraft init --db postgresql")} for PostgreSQL`
188
- ]
189
- ),
190
- INVALID_VALIDATOR: (validator) => new ServCraftError(
191
- `Invalid validator type: "${validator}"`,
192
- [`Valid options: ${import_chalk4.default.cyan("zod, joi, yup")}`, `Default is ${import_chalk4.default.cyan("zod")}`]
193
- ),
194
- MISSING_DEPENDENCY: (dependency, command) => new ServCraftError(
195
- `Missing dependency: "${dependency}"`,
196
- [`Run ${import_chalk4.default.cyan(command)} to install`, `Check your ${import_chalk4.default.yellow("package.json")}`]
197
- ),
198
- INVALID_FIELD_FORMAT: (field) => new ServCraftError(
199
- `Invalid field format: "${field}"`,
200
- [
201
- `Expected format: ${import_chalk4.default.cyan("name:type")}`,
202
- `Example: ${import_chalk4.default.cyan("name:string age:number isActive:boolean")}`,
203
- `Supported types: string, number, boolean, date`
204
- ]
205
- ),
206
- GIT_NOT_INITIALIZED: () => new ServCraftError(
207
- "Git repository not initialized",
208
- [
209
- `Run ${import_chalk4.default.cyan("git init")} to initialize git`,
210
- `This is required for some ServCraft features`
211
- ]
212
- )
213
- };
214
- }
215
- });
27
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
28
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
216
29
 
217
30
  // src/cli/index.ts
218
- init_cjs_shims();
219
- var import_commander9 = require("commander");
31
+ var import_commander11 = require("commander");
220
32
 
221
33
  // src/cli/commands/init.ts
222
- init_cjs_shims();
223
34
  var import_commander = require("commander");
224
35
  var import_path3 = __toESM(require("path"), 1);
225
36
  var import_promises2 = __toESM(require("fs/promises"), 1);
@@ -229,13 +40,11 @@ var import_chalk3 = __toESM(require("chalk"), 1);
229
40
  var import_child_process = require("child_process");
230
41
 
231
42
  // src/cli/utils/helpers.ts
232
- init_cjs_shims();
233
43
  var import_promises = __toESM(require("fs/promises"), 1);
234
44
  var import_path2 = __toESM(require("path"), 1);
235
45
  var import_chalk2 = __toESM(require("chalk"), 1);
236
46
 
237
47
  // src/cli/utils/dry-run.ts
238
- init_cjs_shims();
239
48
  var import_chalk = __toESM(require("chalk"), 1);
240
49
  var import_path = __toESM(require("path"), 1);
241
50
  var DryRunManager = class _DryRunManager {
@@ -1416,7 +1225,6 @@ declare module 'fastify' {
1416
1225
  }
1417
1226
 
1418
1227
  // src/cli/commands/generate.ts
1419
- init_cjs_shims();
1420
1228
  var import_commander2 = require("commander");
1421
1229
  var import_path4 = __toESM(require("path"), 1);
1422
1230
  var import_ora2 = __toESM(require("ora"), 1);
@@ -1424,7 +1232,6 @@ var import_inquirer2 = __toESM(require("inquirer"), 1);
1424
1232
  var import_chalk5 = __toESM(require("chalk"), 1);
1425
1233
 
1426
1234
  // src/cli/utils/field-parser.ts
1427
- init_cjs_shims();
1428
1235
  var tsTypeMap = {
1429
1236
  string: "string",
1430
1237
  number: "number",
@@ -1566,11 +1373,109 @@ function parseFields(fieldsStr) {
1566
1373
  return fieldsStr.split(/\s+/).filter(Boolean).map(parseField);
1567
1374
  }
1568
1375
 
1569
- // src/cli/commands/generate.ts
1570
- init_error_handler();
1376
+ // src/cli/utils/error-handler.ts
1377
+ var import_chalk4 = __toESM(require("chalk"), 1);
1378
+ var ServCraftError = class extends Error {
1379
+ suggestions;
1380
+ docsLink;
1381
+ constructor(message, suggestions = [], docsLink) {
1382
+ super(message);
1383
+ this.name = "ServCraftError";
1384
+ this.suggestions = suggestions;
1385
+ this.docsLink = docsLink;
1386
+ }
1387
+ };
1388
+ var ErrorTypes = {
1389
+ MODULE_NOT_FOUND: (moduleName) => new ServCraftError(
1390
+ `Module "${moduleName}" not found`,
1391
+ [
1392
+ `Run ${import_chalk4.default.cyan("servcraft list")} to see available modules`,
1393
+ `Check the spelling of the module name`,
1394
+ `Visit ${import_chalk4.default.blue("https://github.com/Le-Sourcier/servcraft#modules")} for module list`
1395
+ ],
1396
+ "https://github.com/Le-Sourcier/servcraft#add-pre-built-modules"
1397
+ ),
1398
+ MODULE_ALREADY_EXISTS: (moduleName) => new ServCraftError(`Module "${moduleName}" already exists`, [
1399
+ `Use ${import_chalk4.default.cyan("servcraft add " + moduleName + " --force")} to overwrite`,
1400
+ `Use ${import_chalk4.default.cyan("servcraft add " + moduleName + " --update")} to update`,
1401
+ `Use ${import_chalk4.default.cyan("servcraft add " + moduleName + " --skip-existing")} to skip`
1402
+ ]),
1403
+ NOT_IN_PROJECT: () => new ServCraftError(
1404
+ "Not in a ServCraft project directory",
1405
+ [
1406
+ `Run ${import_chalk4.default.cyan("servcraft init")} to create a new project`,
1407
+ `Navigate to your ServCraft project directory`,
1408
+ `Check if ${import_chalk4.default.yellow("package.json")} exists`
1409
+ ],
1410
+ "https://github.com/Le-Sourcier/servcraft#initialize-project"
1411
+ ),
1412
+ FILE_ALREADY_EXISTS: (fileName) => new ServCraftError(`File "${fileName}" already exists`, [
1413
+ `Use ${import_chalk4.default.cyan("--force")} flag to overwrite`,
1414
+ `Choose a different name`,
1415
+ `Delete the existing file first`
1416
+ ]),
1417
+ INVALID_DATABASE: (database) => new ServCraftError(`Invalid database type: "${database}"`, [
1418
+ `Valid options: ${import_chalk4.default.cyan("postgresql, mysql, sqlite, mongodb, none")}`,
1419
+ `Use ${import_chalk4.default.cyan("servcraft init --db postgresql")} for PostgreSQL`
1420
+ ]),
1421
+ INVALID_VALIDATOR: (validator) => new ServCraftError(`Invalid validator type: "${validator}"`, [
1422
+ `Valid options: ${import_chalk4.default.cyan("zod, joi, yup")}`,
1423
+ `Default is ${import_chalk4.default.cyan("zod")}`
1424
+ ]),
1425
+ MISSING_DEPENDENCY: (dependency, command) => new ServCraftError(`Missing dependency: "${dependency}"`, [
1426
+ `Run ${import_chalk4.default.cyan(command)} to install`,
1427
+ `Check your ${import_chalk4.default.yellow("package.json")}`
1428
+ ]),
1429
+ INVALID_FIELD_FORMAT: (field) => new ServCraftError(`Invalid field format: "${field}"`, [
1430
+ `Expected format: ${import_chalk4.default.cyan("name:type")}`,
1431
+ `Example: ${import_chalk4.default.cyan("name:string age:number isActive:boolean")}`,
1432
+ `Supported types: string, number, boolean, date`
1433
+ ]),
1434
+ GIT_NOT_INITIALIZED: () => new ServCraftError("Git repository not initialized", [
1435
+ `Run ${import_chalk4.default.cyan("git init")} to initialize git`,
1436
+ `This is required for some ServCraft features`
1437
+ ])
1438
+ };
1439
+ function displayError(error2) {
1440
+ console.error("\n" + import_chalk4.default.red.bold("\u2717 Error: ") + import_chalk4.default.red(error2.message));
1441
+ if (error2 instanceof ServCraftError) {
1442
+ if (error2.suggestions.length > 0) {
1443
+ console.log("\n" + import_chalk4.default.yellow.bold("\u{1F4A1} Suggestions:"));
1444
+ error2.suggestions.forEach((suggestion) => {
1445
+ console.log(import_chalk4.default.yellow(" \u2022 ") + suggestion);
1446
+ });
1447
+ }
1448
+ if (error2.docsLink) {
1449
+ console.log(
1450
+ "\n" + import_chalk4.default.blue.bold("\u{1F4DA} Documentation: ") + import_chalk4.default.blue.underline(error2.docsLink)
1451
+ );
1452
+ }
1453
+ }
1454
+ console.log();
1455
+ }
1456
+ function validateProject() {
1457
+ try {
1458
+ const fs12 = require("fs");
1459
+ if (!fs12.existsSync("package.json")) {
1460
+ return ErrorTypes.NOT_IN_PROJECT();
1461
+ }
1462
+ const packageJson = JSON.parse(fs12.readFileSync("package.json", "utf-8"));
1463
+ if (!packageJson.dependencies?.fastify) {
1464
+ return new ServCraftError("This does not appear to be a ServCraft project", [
1465
+ `ServCraft projects require Fastify`,
1466
+ `Run ${import_chalk4.default.cyan("servcraft init")} to create a new project`
1467
+ ]);
1468
+ }
1469
+ return null;
1470
+ } catch {
1471
+ return new ServCraftError("Failed to validate project", [
1472
+ `Ensure you are in the project root directory`,
1473
+ `Check if ${import_chalk4.default.yellow("package.json")} is valid`
1474
+ ]);
1475
+ }
1476
+ }
1571
1477
 
1572
1478
  // src/cli/templates/controller.ts
1573
- init_cjs_shims();
1574
1479
  function controllerTemplate(name, pascalName, camelName) {
1575
1480
  return `import type { FastifyRequest, FastifyReply } from 'fastify';
1576
1481
  import type { ${pascalName}Service } from './${name}.service.js';
@@ -1640,7 +1545,6 @@ export function create${pascalName}Controller(${camelName}Service: ${pascalName}
1640
1545
  }
1641
1546
 
1642
1547
  // src/cli/templates/service.ts
1643
- init_cjs_shims();
1644
1548
  function serviceTemplate(name, pascalName, camelName) {
1645
1549
  return `import type { PaginatedResult, PaginationParams } from '../../types/index.js';
1646
1550
  import { NotFoundError, ConflictError } from '../../utils/errors.js';
@@ -1701,7 +1605,6 @@ export function create${pascalName}Service(repository?: ${pascalName}Repository)
1701
1605
  }
1702
1606
 
1703
1607
  // src/cli/templates/repository.ts
1704
- init_cjs_shims();
1705
1608
  function repositoryTemplate(name, pascalName, camelName, pluralName) {
1706
1609
  return `import { randomUUID } from 'crypto';
1707
1610
  import type { PaginatedResult, PaginationParams } from '../../types/index.js';
@@ -1808,7 +1711,6 @@ export function create${pascalName}Repository(): ${pascalName}Repository {
1808
1711
  }
1809
1712
 
1810
1713
  // src/cli/templates/types.ts
1811
- init_cjs_shims();
1812
1714
  function typesTemplate(name, pascalName) {
1813
1715
  return `import type { BaseEntity } from '../../types/index.js';
1814
1716
 
@@ -1838,7 +1740,6 @@ export interface ${pascalName}Filters {
1838
1740
  }
1839
1741
 
1840
1742
  // src/cli/templates/schemas.ts
1841
- init_cjs_shims();
1842
1743
  function schemasTemplate(name, pascalName, camelName) {
1843
1744
  return `import { z } from 'zod';
1844
1745
 
@@ -1867,7 +1768,6 @@ export type ${pascalName}QueryInput = z.infer<typeof ${camelName}QuerySchema>;
1867
1768
  }
1868
1769
 
1869
1770
  // src/cli/templates/routes.ts
1870
- init_cjs_shims();
1871
1771
  function routesTemplate(name, pascalName, camelName, pluralName) {
1872
1772
  return `import type { FastifyInstance } from 'fastify';
1873
1773
  import type { ${pascalName}Controller } from './${name}.controller.js';
@@ -1920,7 +1820,6 @@ export function register${pascalName}Routes(
1920
1820
  }
1921
1821
 
1922
1822
  // src/cli/templates/module-index.ts
1923
- init_cjs_shims();
1924
1823
  function moduleIndexTemplate(name, pascalName, camelName) {
1925
1824
  return `import type { FastifyInstance } from 'fastify';
1926
1825
  import { logger } from '../../core/logger.js';
@@ -1956,7 +1855,6 @@ export * from './${name}.schemas.js';
1956
1855
  }
1957
1856
 
1958
1857
  // src/cli/templates/prisma-model.ts
1959
- init_cjs_shims();
1960
1858
  function prismaModelTemplate(name, pascalName, tableName) {
1961
1859
  return `
1962
1860
  // Add this model to your prisma/schema.prisma file
@@ -1976,7 +1874,6 @@ model ${pascalName} {
1976
1874
  }
1977
1875
 
1978
1876
  // src/cli/templates/dynamic-types.ts
1979
- init_cjs_shims();
1980
1877
  function dynamicTypesTemplate(name, pascalName, fields) {
1981
1878
  const fieldLines = fields.map((field) => {
1982
1879
  const tsType = tsTypeMap[field.type];
@@ -2021,7 +1918,6 @@ ${fields.filter((f) => ["string", "enum", "boolean"].includes(f.type)).map((f) =
2021
1918
  }
2022
1919
 
2023
1920
  // src/cli/templates/dynamic-schemas.ts
2024
- init_cjs_shims();
2025
1921
  function dynamicSchemasTemplate(name, pascalName, camelName, fields, validator = "zod") {
2026
1922
  switch (validator) {
2027
1923
  case "joi":
@@ -2200,7 +2096,6 @@ function getJsType(field) {
2200
2096
  }
2201
2097
 
2202
2098
  // src/cli/templates/dynamic-prisma.ts
2203
- init_cjs_shims();
2204
2099
  function dynamicPrismaTemplate(modelName, tableName, fields) {
2205
2100
  const fieldLines = [];
2206
2101
  for (const field of fields) {
@@ -2268,6 +2163,353 @@ ${indexLines.join("\n")}
2268
2163
  `;
2269
2164
  }
2270
2165
 
2166
+ // src/cli/templates/controller-test.ts
2167
+ function controllerTestTemplate(name, pascalName, camelName) {
2168
+ return `import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2169
+ import { build } from '../../app.js';
2170
+ import { FastifyInstance } from 'fastify';
2171
+
2172
+ describe('${pascalName}Controller', () => {
2173
+ let app: FastifyInstance;
2174
+
2175
+ beforeAll(async () => {
2176
+ app = await build();
2177
+ await app.ready();
2178
+ });
2179
+
2180
+ afterAll(async () => {
2181
+ await app.close();
2182
+ });
2183
+
2184
+ describe('GET /${name}', () => {
2185
+ it('should return list of ${name}', async () => {
2186
+ const response = await app.inject({
2187
+ method: 'GET',
2188
+ url: '/${name}',
2189
+ });
2190
+
2191
+ expect(response.statusCode).toBe(200);
2192
+ expect(response.json()).toHaveProperty('data');
2193
+ });
2194
+ });
2195
+
2196
+ describe('GET /${name}/:id', () => {
2197
+ it('should return a single ${camelName}', async () => {
2198
+ // TODO: Create test ${camelName} first
2199
+ const response = await app.inject({
2200
+ method: 'GET',
2201
+ url: '/${name}/1',
2202
+ });
2203
+
2204
+ expect(response.statusCode).toBe(200);
2205
+ expect(response.json()).toHaveProperty('data');
2206
+ });
2207
+
2208
+ it('should return 404 for non-existent ${camelName}', async () => {
2209
+ const response = await app.inject({
2210
+ method: 'GET',
2211
+ url: '/${name}/999999',
2212
+ });
2213
+
2214
+ expect(response.statusCode).toBe(404);
2215
+ });
2216
+ });
2217
+
2218
+ describe('POST /${name}', () => {
2219
+ it('should create a new ${camelName}', async () => {
2220
+ const response = await app.inject({
2221
+ method: 'POST',
2222
+ url: '/${name}',
2223
+ payload: {
2224
+ // TODO: Add required fields
2225
+ },
2226
+ });
2227
+
2228
+ expect(response.statusCode).toBe(201);
2229
+ expect(response.json()).toHaveProperty('data');
2230
+ });
2231
+
2232
+ it('should return 400 for invalid data', async () => {
2233
+ const response = await app.inject({
2234
+ method: 'POST',
2235
+ url: '/${name}',
2236
+ payload: {},
2237
+ });
2238
+
2239
+ expect(response.statusCode).toBe(400);
2240
+ });
2241
+ });
2242
+
2243
+ describe('PUT /${name}/:id', () => {
2244
+ it('should update a ${camelName}', async () => {
2245
+ // TODO: Create test ${camelName} first
2246
+ const response = await app.inject({
2247
+ method: 'PUT',
2248
+ url: '/${name}/1',
2249
+ payload: {
2250
+ // TODO: Add fields to update
2251
+ },
2252
+ });
2253
+
2254
+ expect(response.statusCode).toBe(200);
2255
+ expect(response.json()).toHaveProperty('data');
2256
+ });
2257
+ });
2258
+
2259
+ describe('DELETE /${name}/:id', () => {
2260
+ it('should delete a ${camelName}', async () => {
2261
+ // TODO: Create test ${camelName} first
2262
+ const response = await app.inject({
2263
+ method: 'DELETE',
2264
+ url: '/${name}/1',
2265
+ });
2266
+
2267
+ expect(response.statusCode).toBe(204);
2268
+ });
2269
+ });
2270
+ });
2271
+ `;
2272
+ }
2273
+
2274
+ // src/cli/templates/service-test.ts
2275
+ function serviceTestTemplate(name, pascalName, camelName) {
2276
+ return `import { describe, it, expect, beforeEach } from 'vitest';
2277
+ import { ${pascalName}Service } from '../${name}.service.js';
2278
+
2279
+ describe('${pascalName}Service', () => {
2280
+ let service: ${pascalName}Service;
2281
+
2282
+ beforeEach(() => {
2283
+ service = new ${pascalName}Service();
2284
+ });
2285
+
2286
+ describe('getAll', () => {
2287
+ it('should return all ${name}', async () => {
2288
+ const result = await service.getAll();
2289
+
2290
+ expect(result).toBeDefined();
2291
+ expect(Array.isArray(result)).toBe(true);
2292
+ });
2293
+
2294
+ it('should apply pagination', async () => {
2295
+ const result = await service.getAll({ page: 1, limit: 10 });
2296
+
2297
+ expect(result).toBeDefined();
2298
+ expect(result.length).toBeLessThanOrEqual(10);
2299
+ });
2300
+ });
2301
+
2302
+ describe('getById', () => {
2303
+ it('should return a ${camelName} by id', async () => {
2304
+ // TODO: Create test ${camelName} first
2305
+ const id = '1';
2306
+ const result = await service.getById(id);
2307
+
2308
+ expect(result).toBeDefined();
2309
+ expect(result.id).toBe(id);
2310
+ });
2311
+
2312
+ it('should return null for non-existent id', async () => {
2313
+ const result = await service.getById('999999');
2314
+
2315
+ expect(result).toBeNull();
2316
+ });
2317
+ });
2318
+
2319
+ describe('create', () => {
2320
+ it('should create a new ${camelName}', async () => {
2321
+ const data = {
2322
+ // TODO: Add required fields
2323
+ };
2324
+
2325
+ const result = await service.create(data);
2326
+
2327
+ expect(result).toBeDefined();
2328
+ expect(result.id).toBeDefined();
2329
+ });
2330
+
2331
+ it('should throw error for invalid data', async () => {
2332
+ await expect(service.create({} as any)).rejects.toThrow();
2333
+ });
2334
+ });
2335
+
2336
+ describe('update', () => {
2337
+ it('should update a ${camelName}', async () => {
2338
+ // TODO: Create test ${camelName} first
2339
+ const id = '1';
2340
+ const updates = {
2341
+ // TODO: Add fields to update
2342
+ };
2343
+
2344
+ const result = await service.update(id, updates);
2345
+
2346
+ expect(result).toBeDefined();
2347
+ expect(result.id).toBe(id);
2348
+ });
2349
+
2350
+ it('should return null for non-existent id', async () => {
2351
+ const result = await service.update('999999', {});
2352
+
2353
+ expect(result).toBeNull();
2354
+ });
2355
+ });
2356
+
2357
+ describe('delete', () => {
2358
+ it('should delete a ${camelName}', async () => {
2359
+ // TODO: Create test ${camelName} first
2360
+ const id = '1';
2361
+ const result = await service.delete(id);
2362
+
2363
+ expect(result).toBe(true);
2364
+ });
2365
+
2366
+ it('should return false for non-existent id', async () => {
2367
+ const result = await service.delete('999999');
2368
+
2369
+ expect(result).toBe(false);
2370
+ });
2371
+ });
2372
+ });
2373
+ `;
2374
+ }
2375
+
2376
+ // src/cli/templates/integration-test.ts
2377
+ function integrationTestTemplate(name, pascalName, camelName) {
2378
+ return `import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
2379
+ import { build } from '../../app.js';
2380
+ import { FastifyInstance } from 'fastify';
2381
+ import { prisma } from '../../lib/prisma.js';
2382
+
2383
+ describe('${pascalName} Integration Tests', () => {
2384
+ let app: FastifyInstance;
2385
+
2386
+ beforeAll(async () => {
2387
+ app = await build();
2388
+ await app.ready();
2389
+ });
2390
+
2391
+ afterAll(async () => {
2392
+ await app.close();
2393
+ await prisma.$disconnect();
2394
+ });
2395
+
2396
+ beforeEach(async () => {
2397
+ // Clean up test data
2398
+ // await prisma.${camelName}.deleteMany();
2399
+ });
2400
+
2401
+ describe('Full CRUD workflow', () => {
2402
+ it('should create, read, update, and delete a ${camelName}', async () => {
2403
+ // Create
2404
+ const createResponse = await app.inject({
2405
+ method: 'POST',
2406
+ url: '/${name}',
2407
+ payload: {
2408
+ // TODO: Add required fields
2409
+ },
2410
+ });
2411
+
2412
+ expect(createResponse.statusCode).toBe(201);
2413
+ const created = createResponse.json().data;
2414
+ expect(created.id).toBeDefined();
2415
+
2416
+ // Read
2417
+ const readResponse = await app.inject({
2418
+ method: 'GET',
2419
+ url: \`/${name}/\${created.id}\`,
2420
+ });
2421
+
2422
+ expect(readResponse.statusCode).toBe(200);
2423
+ const read = readResponse.json().data;
2424
+ expect(read.id).toBe(created.id);
2425
+
2426
+ // Update
2427
+ const updateResponse = await app.inject({
2428
+ method: 'PUT',
2429
+ url: \`/${name}/\${created.id}\`,
2430
+ payload: {
2431
+ // TODO: Add fields to update
2432
+ },
2433
+ });
2434
+
2435
+ expect(updateResponse.statusCode).toBe(200);
2436
+ const updated = updateResponse.json().data;
2437
+ expect(updated.id).toBe(created.id);
2438
+
2439
+ // Delete
2440
+ const deleteResponse = await app.inject({
2441
+ method: 'DELETE',
2442
+ url: \`/${name}/\${created.id}\`,
2443
+ });
2444
+
2445
+ expect(deleteResponse.statusCode).toBe(204);
2446
+
2447
+ // Verify deletion
2448
+ const verifyResponse = await app.inject({
2449
+ method: 'GET',
2450
+ url: \`/${name}/\${created.id}\`,
2451
+ });
2452
+
2453
+ expect(verifyResponse.statusCode).toBe(404);
2454
+ });
2455
+ });
2456
+
2457
+ describe('List and pagination', () => {
2458
+ it('should list ${name} with pagination', async () => {
2459
+ // Create multiple ${name}
2460
+ const count = 5;
2461
+ for (let i = 0; i < count; i++) {
2462
+ await app.inject({
2463
+ method: 'POST',
2464
+ url: '/${name}',
2465
+ payload: {
2466
+ // TODO: Add required fields
2467
+ },
2468
+ });
2469
+ }
2470
+
2471
+ // Test pagination
2472
+ const response = await app.inject({
2473
+ method: 'GET',
2474
+ url: '/${name}?page=1&limit=3',
2475
+ });
2476
+
2477
+ expect(response.statusCode).toBe(200);
2478
+ const result = response.json();
2479
+ expect(result.data).toBeDefined();
2480
+ expect(result.data.length).toBeLessThanOrEqual(3);
2481
+ expect(result.total).toBeGreaterThanOrEqual(count);
2482
+ });
2483
+ });
2484
+
2485
+ describe('Validation', () => {
2486
+ it('should validate required fields on create', async () => {
2487
+ const response = await app.inject({
2488
+ method: 'POST',
2489
+ url: '/${name}',
2490
+ payload: {},
2491
+ });
2492
+
2493
+ expect(response.statusCode).toBe(400);
2494
+ expect(response.json()).toHaveProperty('error');
2495
+ });
2496
+
2497
+ it('should validate data types', async () => {
2498
+ const response = await app.inject({
2499
+ method: 'POST',
2500
+ url: '/${name}',
2501
+ payload: {
2502
+ // TODO: Add invalid field types
2503
+ },
2504
+ });
2505
+
2506
+ expect(response.statusCode).toBe(400);
2507
+ });
2508
+ });
2509
+ });
2510
+ `;
2511
+ }
2512
+
2271
2513
  // src/cli/commands/generate.ts
2272
2514
  function enableDryRunIfNeeded(options) {
2273
2515
  const dryRun = DryRunManager.getInstance();
@@ -2284,7 +2526,7 @@ function showDryRunSummary(options) {
2284
2526
  var generateCommand = new import_commander2.Command("generate").alias("g").description("Generate resources (module, controller, service, etc.)");
2285
2527
  generateCommand.command("module <name> [fields...]").alias("m").description(
2286
2528
  "Generate a complete module with controller, service, repository, types, schemas, and routes"
2287
- ).option("--no-routes", "Skip routes generation").option("--no-repository", "Skip repository generation").option("--prisma", "Generate Prisma model suggestion").option("--validator <type>", "Validator type: zod, joi, yup", "zod").option("-i, --interactive", "Interactive mode to define fields").option("--dry-run", "Preview changes without writing files").action(async (name, fieldsArgs, options) => {
2529
+ ).option("--no-routes", "Skip routes generation").option("--no-repository", "Skip repository generation").option("--prisma", "Generate Prisma model suggestion").option("--validator <type>", "Validator type: zod, joi, yup", "zod").option("-i, --interactive", "Interactive mode to define fields").option("--with-tests", "Generate test files (__tests__ directory)").option("--dry-run", "Preview changes without writing files").action(async (name, fieldsArgs, options) => {
2288
2530
  enableDryRunIfNeeded(options);
2289
2531
  let fields = [];
2290
2532
  if (options.interactive) {
@@ -2341,6 +2583,21 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
2341
2583
  for (const file of files) {
2342
2584
  await writeFile(import_path4.default.join(moduleDir, file.name), file.content);
2343
2585
  }
2586
+ if (options.withTests) {
2587
+ const testDir = import_path4.default.join(moduleDir, "__tests__");
2588
+ await writeFile(
2589
+ import_path4.default.join(testDir, `${kebabName}.controller.test.ts`),
2590
+ controllerTestTemplate(kebabName, pascalName, camelName)
2591
+ );
2592
+ await writeFile(
2593
+ import_path4.default.join(testDir, `${kebabName}.service.test.ts`),
2594
+ serviceTestTemplate(kebabName, pascalName, camelName)
2595
+ );
2596
+ await writeFile(
2597
+ import_path4.default.join(testDir, `${kebabName}.integration.test.ts`),
2598
+ integrationTestTemplate(kebabName, pascalName, camelName)
2599
+ );
2600
+ }
2344
2601
  spinner.succeed(`Module "${pascalName}" generated successfully!`);
2345
2602
  if (options.prisma || hasFields) {
2346
2603
  console.log("\n" + "\u2500".repeat(50));
@@ -2364,6 +2621,11 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
2364
2621
  }
2365
2622
  console.log("\n\u{1F4C1} Files created:");
2366
2623
  files.forEach((f) => success(` src/modules/${kebabName}/${f.name}`));
2624
+ if (options.withTests) {
2625
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.controller.test.ts`);
2626
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.service.test.ts`);
2627
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.integration.test.ts`);
2628
+ }
2367
2629
  console.log("\n\u{1F4CC} Next steps:");
2368
2630
  if (!hasFields) {
2369
2631
  info(` 1. Update the types in ${kebabName}.types.ts`);
@@ -2600,7 +2862,6 @@ async function promptForFields() {
2600
2862
  }
2601
2863
 
2602
2864
  // src/cli/commands/add-module.ts
2603
- init_cjs_shims();
2604
2865
  var import_commander3 = require("commander");
2605
2866
  var import_path5 = __toESM(require("path"), 1);
2606
2867
  var import_ora3 = __toESM(require("ora"), 1);
@@ -2608,7 +2869,6 @@ var import_chalk7 = __toESM(require("chalk"), 1);
2608
2869
  var fs5 = __toESM(require("fs/promises"), 1);
2609
2870
 
2610
2871
  // src/cli/utils/env-manager.ts
2611
- init_cjs_shims();
2612
2872
  var fs3 = __toESM(require("fs/promises"), 1);
2613
2873
  var path5 = __toESM(require("path"), 1);
2614
2874
  var import_fs = require("fs");
@@ -3267,7 +3527,6 @@ var EnvManager = class {
3267
3527
  };
3268
3528
 
3269
3529
  // src/cli/utils/template-manager.ts
3270
- init_cjs_shims();
3271
3530
  var fs4 = __toESM(require("fs/promises"), 1);
3272
3531
  var path6 = __toESM(require("path"), 1);
3273
3532
  var import_crypto = require("crypto");
@@ -3510,7 +3769,6 @@ var TemplateManager = class {
3510
3769
  };
3511
3770
 
3512
3771
  // src/cli/utils/interactive-prompt.ts
3513
- init_cjs_shims();
3514
3772
  var import_inquirer3 = __toESM(require("inquirer"), 1);
3515
3773
  var import_chalk6 = __toESM(require("chalk"), 1);
3516
3774
  var InteractivePrompt = class {
@@ -3696,7 +3954,6 @@ var InteractivePrompt = class {
3696
3954
  };
3697
3955
 
3698
3956
  // src/cli/commands/add-module.ts
3699
- init_error_handler();
3700
3957
  var AVAILABLE_MODULES = {
3701
3958
  auth: {
3702
3959
  name: "Authentication",
@@ -4438,7 +4695,6 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
4438
4695
  }
4439
4696
 
4440
4697
  // src/cli/commands/db.ts
4441
- init_cjs_shims();
4442
4698
  var import_commander4 = require("commander");
4443
4699
  var import_child_process2 = require("child_process");
4444
4700
  var import_ora4 = __toESM(require("ora"), 1);
@@ -4532,7 +4788,6 @@ dbCommand.command("status").description("Show migration status").action(async ()
4532
4788
  });
4533
4789
 
4534
4790
  // src/cli/commands/docs.ts
4535
- init_cjs_shims();
4536
4791
  var import_commander5 = require("commander");
4537
4792
  var import_path7 = __toESM(require("path"), 1);
4538
4793
  var import_promises4 = __toESM(require("fs/promises"), 1);
@@ -4540,17 +4795,14 @@ var import_ora6 = __toESM(require("ora"), 1);
4540
4795
  var import_chalk9 = __toESM(require("chalk"), 1);
4541
4796
 
4542
4797
  // src/cli/utils/docs-generator.ts
4543
- init_cjs_shims();
4544
4798
  var import_promises3 = __toESM(require("fs/promises"), 1);
4545
4799
  var import_path6 = __toESM(require("path"), 1);
4546
4800
  var import_ora5 = __toESM(require("ora"), 1);
4547
4801
 
4548
4802
  // src/core/server.ts
4549
- init_cjs_shims();
4550
4803
  var import_fastify = __toESM(require("fastify"), 1);
4551
4804
 
4552
4805
  // src/core/logger.ts
4553
- init_cjs_shims();
4554
4806
  var import_pino = __toESM(require("pino"), 1);
4555
4807
  var defaultConfig = {
4556
4808
  level: process.env.LOG_LEVEL || "info",
@@ -4683,14 +4935,7 @@ function createServer(config2 = {}) {
4683
4935
  return new Server(config2);
4684
4936
  }
4685
4937
 
4686
- // src/middleware/index.ts
4687
- init_cjs_shims();
4688
-
4689
- // src/middleware/error-handler.ts
4690
- init_cjs_shims();
4691
-
4692
4938
  // src/utils/errors.ts
4693
- init_cjs_shims();
4694
4939
  var AppError = class _AppError extends Error {
4695
4940
  statusCode;
4696
4941
  isOperational;
@@ -4744,11 +4989,7 @@ function isAppError(error2) {
4744
4989
  return error2 instanceof AppError;
4745
4990
  }
4746
4991
 
4747
- // src/config/index.ts
4748
- init_cjs_shims();
4749
-
4750
4992
  // src/config/env.ts
4751
- init_cjs_shims();
4752
4993
  var import_zod = require("zod");
4753
4994
  var import_dotenv = __toESM(require("dotenv"), 1);
4754
4995
  import_dotenv.default.config();
@@ -4894,7 +5135,6 @@ function registerErrorHandler(app) {
4894
5135
  }
4895
5136
 
4896
5137
  // src/middleware/security.ts
4897
- init_cjs_shims();
4898
5138
  var import_helmet = __toESM(require("@fastify/helmet"), 1);
4899
5139
  var import_cors = __toESM(require("@fastify/cors"), 1);
4900
5140
  var import_rate_limit = __toESM(require("@fastify/rate-limit"), 1);
@@ -4954,11 +5194,7 @@ async function registerSecurity(app, options = {}) {
4954
5194
  }
4955
5195
  }
4956
5196
 
4957
- // src/modules/swagger/index.ts
4958
- init_cjs_shims();
4959
-
4960
5197
  // src/modules/swagger/swagger.service.ts
4961
- init_cjs_shims();
4962
5198
  var import_swagger = __toESM(require("@fastify/swagger"), 1);
4963
5199
  var import_swagger_ui = __toESM(require("@fastify/swagger-ui"), 1);
4964
5200
  var defaultConfig3 = {
@@ -5018,16 +5254,11 @@ async function registerSwagger(app, customConfig) {
5018
5254
  logger.info("Swagger documentation registered at /docs");
5019
5255
  }
5020
5256
 
5021
- // src/modules/swagger/schema-builder.ts
5022
- init_cjs_shims();
5023
-
5024
5257
  // src/modules/auth/index.ts
5025
- init_cjs_shims();
5026
5258
  var import_jwt = __toESM(require("@fastify/jwt"), 1);
5027
5259
  var import_cookie = __toESM(require("@fastify/cookie"), 1);
5028
5260
 
5029
5261
  // src/modules/auth/auth.service.ts
5030
- init_cjs_shims();
5031
5262
  var import_bcryptjs = __toESM(require("bcryptjs"), 1);
5032
5263
  var import_ioredis = require("ioredis");
5033
5264
  var AuthService = class {
@@ -5226,11 +5457,7 @@ function createAuthService(app) {
5226
5457
  return new AuthService(app);
5227
5458
  }
5228
5459
 
5229
- // src/modules/auth/auth.controller.ts
5230
- init_cjs_shims();
5231
-
5232
5460
  // src/modules/auth/schemas.ts
5233
- init_cjs_shims();
5234
5461
  var import_zod2 = require("zod");
5235
5462
  var loginSchema = import_zod2.z.object({
5236
5463
  email: import_zod2.z.string().email("Invalid email address"),
@@ -5257,7 +5484,6 @@ var changePasswordSchema = import_zod2.z.object({
5257
5484
  });
5258
5485
 
5259
5486
  // src/utils/response.ts
5260
- init_cjs_shims();
5261
5487
  function success2(reply, data, statusCode = 200) {
5262
5488
  const response = {
5263
5489
  success: true,
@@ -5273,7 +5499,6 @@ function noContent(reply) {
5273
5499
  }
5274
5500
 
5275
5501
  // src/modules/validation/validator.ts
5276
- init_cjs_shims();
5277
5502
  var import_zod3 = require("zod");
5278
5503
  function validateBody(schema, data) {
5279
5504
  const result = schema.safeParse(data);
@@ -5292,11 +5517,11 @@ function validateQuery(schema, data) {
5292
5517
  function formatZodErrors(error2) {
5293
5518
  const errors = {};
5294
5519
  for (const issue of error2.issues) {
5295
- const path11 = issue.path.join(".") || "root";
5296
- if (!errors[path11]) {
5297
- errors[path11] = [];
5520
+ const path12 = issue.path.join(".") || "root";
5521
+ if (!errors[path12]) {
5522
+ errors[path12] = [];
5298
5523
  }
5299
- errors[path11].push(issue.message);
5524
+ errors[path12].push(issue.message);
5300
5525
  }
5301
5526
  return errors;
5302
5527
  }
@@ -5444,11 +5669,7 @@ function createAuthController(authService, userService) {
5444
5669
  return new AuthController(authService, userService);
5445
5670
  }
5446
5671
 
5447
- // src/modules/auth/auth.routes.ts
5448
- init_cjs_shims();
5449
-
5450
5672
  // src/modules/auth/auth.middleware.ts
5451
- init_cjs_shims();
5452
5673
  function createAuthMiddleware(authService) {
5453
5674
  return async function authenticate(request, _reply) {
5454
5675
  const authHeader = request.headers.authorization;
@@ -5491,14 +5712,7 @@ function registerAuthRoutes(app, controller, authService) {
5491
5712
  );
5492
5713
  }
5493
5714
 
5494
- // src/modules/user/user.service.ts
5495
- init_cjs_shims();
5496
-
5497
- // src/modules/user/user.repository.ts
5498
- init_cjs_shims();
5499
-
5500
5715
  // src/database/prisma.ts
5501
- init_cjs_shims();
5502
5716
  var import_client = require("@prisma/client");
5503
5717
  var prismaClientSingleton = () => {
5504
5718
  return new import_client.PrismaClient({
@@ -5512,7 +5726,6 @@ if (!isProduction()) {
5512
5726
  }
5513
5727
 
5514
5728
  // src/utils/pagination.ts
5515
- init_cjs_shims();
5516
5729
  var DEFAULT_PAGE = 1;
5517
5730
  var DEFAULT_LIMIT = 20;
5518
5731
  var MAX_LIMIT = 100;
@@ -5777,7 +5990,6 @@ function createUserRepository() {
5777
5990
  }
5778
5991
 
5779
5992
  // src/modules/user/types.ts
5780
- init_cjs_shims();
5781
5993
  var DEFAULT_ROLE_PERMISSIONS = {
5782
5994
  user: ["profile:read", "profile:update"],
5783
5995
  moderator: [
@@ -5908,9 +6120,6 @@ function createUserService(repository) {
5908
6120
  return new UserService(repository || createUserRepository());
5909
6121
  }
5910
6122
 
5911
- // src/modules/auth/types.ts
5912
- init_cjs_shims();
5913
-
5914
6123
  // src/modules/auth/index.ts
5915
6124
  async function registerAuthModule(app) {
5916
6125
  await app.register(import_jwt.default, {
@@ -5930,14 +6139,7 @@ async function registerAuthModule(app) {
5930
6139
  logger.info("Auth module registered");
5931
6140
  }
5932
6141
 
5933
- // src/modules/user/index.ts
5934
- init_cjs_shims();
5935
-
5936
- // src/modules/user/user.controller.ts
5937
- init_cjs_shims();
5938
-
5939
6142
  // src/modules/user/schemas.ts
5940
- init_cjs_shims();
5941
6143
  var import_zod4 = require("zod");
5942
6144
  var userStatusEnum = import_zod4.z.enum(["active", "inactive", "suspended", "banned"]);
5943
6145
  var userRoleEnum = import_zod4.z.enum(["user", "admin", "moderator", "super_admin"]);
@@ -6064,7 +6266,6 @@ function createUserController(userService) {
6064
6266
  }
6065
6267
 
6066
6268
  // src/modules/user/user.routes.ts
6067
- init_cjs_shims();
6068
6269
  var idParamsSchema = {
6069
6270
  type: "object",
6070
6271
  properties: {
@@ -6356,7 +6557,6 @@ function formatDate(date) {
6356
6557
  }
6357
6558
 
6358
6559
  // src/cli/commands/list.ts
6359
- init_cjs_shims();
6360
6560
  var import_commander6 = require("commander");
6361
6561
  var import_chalk10 = __toESM(require("chalk"), 1);
6362
6562
  var import_promises5 = __toESM(require("fs/promises"), 1);
@@ -6514,7 +6714,7 @@ var listCommand = new import_commander6.Command("list").alias("ls").description(
6514
6714
  if (!byCategory[mod.category]) {
6515
6715
  byCategory[mod.category] = [];
6516
6716
  }
6517
- byCategory[mod.category].push({
6717
+ byCategory[mod.category]?.push({
6518
6718
  id: key,
6519
6719
  name: mod.name,
6520
6720
  description: mod.description,
@@ -6582,14 +6782,12 @@ var listCommand = new import_commander6.Command("list").alias("ls").description(
6582
6782
  );
6583
6783
 
6584
6784
  // src/cli/commands/remove.ts
6585
- init_cjs_shims();
6586
6785
  var import_commander7 = require("commander");
6587
6786
  var import_path8 = __toESM(require("path"), 1);
6588
6787
  var import_ora7 = __toESM(require("ora"), 1);
6589
6788
  var import_chalk11 = __toESM(require("chalk"), 1);
6590
6789
  var import_promises6 = __toESM(require("fs/promises"), 1);
6591
6790
  var import_inquirer4 = __toESM(require("inquirer"), 1);
6592
- init_error_handler();
6593
6791
  var removeCommand = new import_commander7.Command("remove").alias("rm").description("Remove an installed module from your project").argument("<module>", "Module to remove").option("-y, --yes", "Skip confirmation prompt").option("--keep-env", "Keep environment variables").action(async (moduleName, options) => {
6594
6792
  const projectError = validateProject();
6595
6793
  if (projectError) {
@@ -6602,13 +6800,10 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
6602
6800
  const exists = await import_promises6.default.access(moduleDir).then(() => true).catch(() => false);
6603
6801
  if (!exists) {
6604
6802
  displayError(
6605
- new (init_error_handler(), __toCommonJS(error_handler_exports)).ServCraftError(
6606
- `Module "${moduleName}" is not installed`,
6607
- [
6608
- `Run ${import_chalk11.default.cyan("servcraft list --installed")} to see installed modules`,
6609
- `Check the spelling of the module name`
6610
- ]
6611
- )
6803
+ new ServCraftError(`Module "${moduleName}" is not installed`, [
6804
+ `Run ${import_chalk11.default.cyan("servcraft list --installed")} to see installed modules`,
6805
+ `Check the spelling of the module name`
6806
+ ])
6612
6807
  );
6613
6808
  return;
6614
6809
  }
@@ -6657,15 +6852,430 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
6657
6852
  });
6658
6853
 
6659
6854
  // src/cli/commands/doctor.ts
6660
- init_cjs_shims();
6661
6855
  var import_commander8 = require("commander");
6662
6856
  var import_chalk12 = __toESM(require("chalk"), 1);
6857
+ var import_promises7 = __toESM(require("fs/promises"), 1);
6858
+ async function checkNodeVersion() {
6859
+ const version = process.version;
6860
+ const major = parseInt(version.slice(1).split(".")[0] || "0", 10);
6861
+ if (major >= 18) {
6862
+ return { name: "Node.js", status: "pass", message: `${version} \u2713` };
6863
+ }
6864
+ return {
6865
+ name: "Node.js",
6866
+ status: "fail",
6867
+ message: `${version} (< 18)`,
6868
+ suggestion: "Upgrade to Node.js 18+"
6869
+ };
6870
+ }
6871
+ async function checkPackageJson() {
6872
+ const checks = [];
6873
+ try {
6874
+ const content = await import_promises7.default.readFile("package.json", "utf-8");
6875
+ const pkg = JSON.parse(content);
6876
+ checks.push({ name: "package.json", status: "pass", message: "Found" });
6877
+ if (pkg.dependencies?.fastify) {
6878
+ checks.push({ name: "Fastify", status: "pass", message: "Installed" });
6879
+ } else {
6880
+ checks.push({
6881
+ name: "Fastify",
6882
+ status: "fail",
6883
+ message: "Missing",
6884
+ suggestion: "npm install fastify"
6885
+ });
6886
+ }
6887
+ } catch {
6888
+ checks.push({
6889
+ name: "package.json",
6890
+ status: "fail",
6891
+ message: "Not found",
6892
+ suggestion: "Run servcraft init"
6893
+ });
6894
+ }
6895
+ return checks;
6896
+ }
6897
+ async function checkDirectories() {
6898
+ const checks = [];
6899
+ const dirs = ["src", "node_modules", ".git", ".env"];
6900
+ for (const dir of dirs) {
6901
+ try {
6902
+ await import_promises7.default.access(dir);
6903
+ checks.push({ name: dir, status: "pass", message: "Exists" });
6904
+ } catch {
6905
+ const isCritical = dir === "src" || dir === "node_modules";
6906
+ checks.push({
6907
+ name: dir,
6908
+ status: isCritical ? "fail" : "warn",
6909
+ message: "Not found",
6910
+ suggestion: dir === "node_modules" ? "npm install" : dir === ".env" ? "Create .env file" : void 0
6911
+ });
6912
+ }
6913
+ }
6914
+ return checks;
6915
+ }
6663
6916
  var doctorCommand = new import_commander8.Command("doctor").description("Diagnose project configuration and dependencies").action(async () => {
6664
- console.log(import_chalk12.default.bold.cyan("\nServCraft Doctor - Coming soon!\n"));
6917
+ console.log(import_chalk12.default.bold.cyan("\n\u{1F50D} ServCraft Doctor\n"));
6918
+ const allChecks = [];
6919
+ allChecks.push(await checkNodeVersion());
6920
+ allChecks.push(...await checkPackageJson());
6921
+ allChecks.push(...await checkDirectories());
6922
+ allChecks.forEach((check) => {
6923
+ const icon = check.status === "pass" ? import_chalk12.default.green("\u2713") : check.status === "warn" ? import_chalk12.default.yellow("\u26A0") : import_chalk12.default.red("\u2717");
6924
+ const color = check.status === "pass" ? import_chalk12.default.green : check.status === "warn" ? import_chalk12.default.yellow : import_chalk12.default.red;
6925
+ console.log(`${icon} ${check.name.padEnd(20)} ${color(check.message)}`);
6926
+ if (check.suggestion) {
6927
+ console.log(import_chalk12.default.gray(` \u2192 ${check.suggestion}`));
6928
+ }
6929
+ });
6930
+ const pass = allChecks.filter((c) => c.status === "pass").length;
6931
+ const warn2 = allChecks.filter((c) => c.status === "warn").length;
6932
+ const fail = allChecks.filter((c) => c.status === "fail").length;
6933
+ console.log(import_chalk12.default.gray("\n" + "\u2500".repeat(60)));
6934
+ console.log(
6935
+ `
6936
+ ${import_chalk12.default.green(pass + " passed")} | ${import_chalk12.default.yellow(warn2 + " warnings")} | ${import_chalk12.default.red(fail + " failed")}
6937
+ `
6938
+ );
6939
+ if (fail === 0 && warn2 === 0) {
6940
+ console.log(import_chalk12.default.green.bold("\u2728 Everything looks good!\n"));
6941
+ } else if (fail > 0) {
6942
+ console.log(import_chalk12.default.red.bold("\u2717 Fix critical issues before using ServCraft.\n"));
6943
+ } else {
6944
+ console.log(import_chalk12.default.yellow.bold("\u26A0 Some warnings, but should work.\n"));
6945
+ }
6946
+ });
6947
+
6948
+ // src/cli/commands/update.ts
6949
+ var import_commander9 = require("commander");
6950
+ var import_chalk13 = __toESM(require("chalk"), 1);
6951
+ var import_promises8 = __toESM(require("fs/promises"), 1);
6952
+ var import_path9 = __toESM(require("path"), 1);
6953
+ var import_url = require("url");
6954
+ var import_inquirer5 = __toESM(require("inquirer"), 1);
6955
+ var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
6956
+ var __dirname = import_path9.default.dirname(__filename2);
6957
+ var AVAILABLE_MODULES3 = [
6958
+ "auth",
6959
+ "users",
6960
+ "email",
6961
+ "mfa",
6962
+ "oauth",
6963
+ "rate-limit",
6964
+ "cache",
6965
+ "upload",
6966
+ "search",
6967
+ "notification",
6968
+ "webhook",
6969
+ "websocket",
6970
+ "queue",
6971
+ "payment",
6972
+ "i18n",
6973
+ "feature-flag",
6974
+ "analytics",
6975
+ "media-processing",
6976
+ "api-versioning",
6977
+ "audit",
6978
+ "swagger",
6979
+ "validation"
6980
+ ];
6981
+ async function getInstalledModules2() {
6982
+ try {
6983
+ const modulesDir = getModulesDir();
6984
+ const entries = await import_promises8.default.readdir(modulesDir, { withFileTypes: true });
6985
+ const installedModules = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => AVAILABLE_MODULES3.includes(name));
6986
+ return installedModules;
6987
+ } catch {
6988
+ return [];
6989
+ }
6990
+ }
6991
+ async function copyModuleFiles(moduleName, _projectRoot) {
6992
+ const cliRoot = import_path9.default.resolve(__dirname, "../../../");
6993
+ const sourceModulePath = import_path9.default.join(cliRoot, "src", "modules", moduleName);
6994
+ const targetModulesDir = getModulesDir();
6995
+ const targetModulePath = import_path9.default.join(targetModulesDir, moduleName);
6996
+ try {
6997
+ await import_promises8.default.access(sourceModulePath);
6998
+ } catch {
6999
+ throw new Error(`Module source not found: ${moduleName}`);
7000
+ }
7001
+ await import_promises8.default.cp(sourceModulePath, targetModulePath, { recursive: true });
7002
+ }
7003
+ async function updateModule(moduleName, options) {
7004
+ const projectError = validateProject();
7005
+ if (projectError) {
7006
+ displayError(projectError);
7007
+ return;
7008
+ }
7009
+ const projectRoot = getProjectRoot();
7010
+ const installedModules = await getInstalledModules2();
7011
+ if (!installedModules.includes(moduleName)) {
7012
+ console.log(import_chalk13.default.yellow(`
7013
+ \u26A0 Module "${moduleName}" is not installed
7014
+ `));
7015
+ console.log(
7016
+ import_chalk13.default.gray(`Run ${import_chalk13.default.cyan(`servcraft add ${moduleName}`)} to install it first.
7017
+ `)
7018
+ );
7019
+ return;
7020
+ }
7021
+ if (options.check) {
7022
+ console.log(import_chalk13.default.cyan(`
7023
+ \u{1F4E6} Checking updates for "${moduleName}"...
7024
+ `));
7025
+ console.log(import_chalk13.default.gray("Note: Version tracking will be implemented in a future release."));
7026
+ console.log(import_chalk13.default.gray("Currently, update will always reinstall the latest version.\n"));
7027
+ return;
7028
+ }
7029
+ const { confirmed } = await import_inquirer5.default.prompt([
7030
+ {
7031
+ type: "confirm",
7032
+ name: "confirmed",
7033
+ message: `Update "${moduleName}" module? This will overwrite existing files.`,
7034
+ default: false
7035
+ }
7036
+ ]);
7037
+ if (!confirmed) {
7038
+ console.log(import_chalk13.default.yellow("\n\u26A0 Update cancelled\n"));
7039
+ return;
7040
+ }
7041
+ console.log(import_chalk13.default.cyan(`
7042
+ \u{1F504} Updating "${moduleName}" module...
7043
+ `));
7044
+ try {
7045
+ await copyModuleFiles(moduleName, projectRoot);
7046
+ console.log(import_chalk13.default.green(`\u2714 Module "${moduleName}" updated successfully!
7047
+ `));
7048
+ console.log(
7049
+ import_chalk13.default.gray("Note: Remember to review any breaking changes in the documentation.\n")
7050
+ );
7051
+ } catch (error2) {
7052
+ if (error2 instanceof Error) {
7053
+ console.error(import_chalk13.default.red(`
7054
+ \u2717 Failed to update module: ${error2.message}
7055
+ `));
7056
+ }
7057
+ }
7058
+ }
7059
+ async function updateAllModules(options) {
7060
+ const projectError = validateProject();
7061
+ if (projectError) {
7062
+ displayError(projectError);
7063
+ return;
7064
+ }
7065
+ const installedModules = await getInstalledModules2();
7066
+ if (installedModules.length === 0) {
7067
+ console.log(import_chalk13.default.yellow("\n\u26A0 No modules installed\n"));
7068
+ console.log(import_chalk13.default.gray(`Run ${import_chalk13.default.cyan("servcraft list")} to see available modules.
7069
+ `));
7070
+ return;
7071
+ }
7072
+ if (options.check) {
7073
+ console.log(import_chalk13.default.cyan("\n\u{1F4E6} Checking updates for all modules...\n"));
7074
+ console.log(import_chalk13.default.bold("Installed modules:"));
7075
+ installedModules.forEach((mod) => {
7076
+ console.log(` \u2022 ${import_chalk13.default.cyan(mod)}`);
7077
+ });
7078
+ console.log();
7079
+ console.log(import_chalk13.default.gray("Note: Version tracking will be implemented in a future release."));
7080
+ console.log(import_chalk13.default.gray("Currently, update will always reinstall the latest version.\n"));
7081
+ return;
7082
+ }
7083
+ console.log(import_chalk13.default.cyan(`
7084
+ \u{1F4E6} Found ${installedModules.length} installed module(s):
7085
+ `));
7086
+ installedModules.forEach((mod) => {
7087
+ console.log(` \u2022 ${import_chalk13.default.cyan(mod)}`);
7088
+ });
7089
+ console.log();
7090
+ const { confirmed } = await import_inquirer5.default.prompt([
7091
+ {
7092
+ type: "confirm",
7093
+ name: "confirmed",
7094
+ message: "Update all modules? This will overwrite existing files.",
7095
+ default: false
7096
+ }
7097
+ ]);
7098
+ if (!confirmed) {
7099
+ console.log(import_chalk13.default.yellow("\n\u26A0 Update cancelled\n"));
7100
+ return;
7101
+ }
7102
+ console.log(import_chalk13.default.cyan("\n\u{1F504} Updating all modules...\n"));
7103
+ const projectRoot = getProjectRoot();
7104
+ let successCount = 0;
7105
+ let failCount = 0;
7106
+ for (const moduleName of installedModules) {
7107
+ try {
7108
+ await copyModuleFiles(moduleName, projectRoot);
7109
+ console.log(import_chalk13.default.green(`\u2714 Updated: ${moduleName}`));
7110
+ successCount++;
7111
+ } catch {
7112
+ console.error(import_chalk13.default.red(`\u2717 Failed: ${moduleName}`));
7113
+ failCount++;
7114
+ }
7115
+ }
7116
+ console.log();
7117
+ console.log(
7118
+ import_chalk13.default.bold(
7119
+ `
7120
+ \u2714 Update complete: ${import_chalk13.default.green(successCount)} succeeded, ${import_chalk13.default.red(failCount)} failed
7121
+ `
7122
+ )
7123
+ );
7124
+ if (successCount > 0) {
7125
+ console.log(
7126
+ import_chalk13.default.gray("Note: Remember to review any breaking changes in the documentation.\n")
7127
+ );
7128
+ }
7129
+ }
7130
+ var updateCommand = new import_commander9.Command("update").description("Update installed modules to latest version").argument("[module]", "Specific module to update").option("--check", "Check for updates without applying").option("-y, --yes", "Skip confirmation prompt").action(async (moduleName, options) => {
7131
+ if (moduleName) {
7132
+ await updateModule(moduleName, { check: options?.check });
7133
+ } else {
7134
+ await updateAllModules({ check: options?.check });
7135
+ }
7136
+ });
7137
+
7138
+ // src/cli/commands/completion.ts
7139
+ var import_commander10 = require("commander");
7140
+ var bashScript = `
7141
+ # servcraft bash completion script
7142
+ _servcraft_completions() {
7143
+ local cur prev words cword
7144
+ _init_completion || return
7145
+
7146
+ # Main commands
7147
+ local commands="init add generate list remove doctor update completion docs --version --help"
7148
+
7149
+ # Generate subcommands
7150
+ local generate_subcommands="module controller service repository types schema routes m c s r t"
7151
+
7152
+ case "\${words[1]}" in
7153
+ generate|g)
7154
+ if [[ \${cword} -eq 2 ]]; then
7155
+ COMPREPLY=( $(compgen -W "\${generate_subcommands}" -- "\${cur}") )
7156
+ fi
7157
+ ;;
7158
+ add|remove|rm|update)
7159
+ if [[ \${cword} -eq 2 ]]; then
7160
+ # Get available modules
7161
+ local modules="auth cache rate-limit notification payment oauth mfa queue websocket upload"
7162
+ COMPREPLY=( $(compgen -W "\${modules}" -- "\${cur}") )
7163
+ fi
7164
+ ;;
7165
+ completion)
7166
+ if [[ \${cword} -eq 2 ]]; then
7167
+ COMPREPLY=( $(compgen -W "bash zsh" -- "\${cur}") )
7168
+ fi
7169
+ ;;
7170
+ *)
7171
+ if [[ \${cword} -eq 1 ]]; then
7172
+ COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
7173
+ fi
7174
+ ;;
7175
+ esac
7176
+ }
7177
+
7178
+ complete -F _servcraft_completions servcraft
7179
+ `;
7180
+ var zshScript = `
7181
+ #compdef servcraft
7182
+
7183
+ _servcraft() {
7184
+ local context state state_descr line
7185
+ typeset -A opt_args
7186
+
7187
+ _arguments -C \\
7188
+ '1: :_servcraft_commands' \\
7189
+ '*::arg:->args'
7190
+
7191
+ case $state in
7192
+ args)
7193
+ case $line[1] in
7194
+ generate|g)
7195
+ _servcraft_generate
7196
+ ;;
7197
+ add|remove|rm|update)
7198
+ _servcraft_modules
7199
+ ;;
7200
+ completion)
7201
+ _arguments '1: :(bash zsh)'
7202
+ ;;
7203
+ esac
7204
+ ;;
7205
+ esac
7206
+ }
7207
+
7208
+ _servcraft_commands() {
7209
+ local commands
7210
+ commands=(
7211
+ 'init:Initialize a new ServCraft project'
7212
+ 'add:Add a pre-built module to your project'
7213
+ 'generate:Generate code files (controller, service, etc.)'
7214
+ 'list:List available and installed modules'
7215
+ 'remove:Remove an installed module'
7216
+ 'doctor:Diagnose project configuration'
7217
+ 'update:Update installed modules'
7218
+ 'completion:Generate shell completion scripts'
7219
+ 'docs:Open documentation'
7220
+ '--version:Show version'
7221
+ '--help:Show help'
7222
+ )
7223
+ _describe 'command' commands
7224
+ }
7225
+
7226
+ _servcraft_generate() {
7227
+ local subcommands
7228
+ subcommands=(
7229
+ 'module:Generate a complete module (controller + service + routes)'
7230
+ 'controller:Generate a controller'
7231
+ 'service:Generate a service'
7232
+ 'repository:Generate a repository'
7233
+ 'types:Generate TypeScript types'
7234
+ 'schema:Generate validation schema'
7235
+ 'routes:Generate routes file'
7236
+ 'm:Alias for module'
7237
+ 'c:Alias for controller'
7238
+ 's:Alias for service'
7239
+ 'r:Alias for repository'
7240
+ 't:Alias for types'
7241
+ )
7242
+ _describe 'subcommand' subcommands
7243
+ }
7244
+
7245
+ _servcraft_modules() {
7246
+ local modules
7247
+ modules=(
7248
+ 'auth:Authentication & Authorization'
7249
+ 'cache:Redis caching'
7250
+ 'rate-limit:Rate limiting'
7251
+ 'notification:Email/SMS notifications'
7252
+ 'payment:Payment integration'
7253
+ 'oauth:OAuth providers'
7254
+ 'mfa:Multi-factor authentication'
7255
+ 'queue:Background jobs'
7256
+ 'websocket:WebSocket support'
7257
+ 'upload:File upload handling'
7258
+ )
7259
+ _describe 'module' modules
7260
+ }
7261
+
7262
+ _servcraft "$@"
7263
+ `;
7264
+ var completionCommand = new import_commander10.Command("completion").description("Generate shell completion scripts").argument("<shell>", "Shell type (bash or zsh)").action((shell) => {
7265
+ const shellLower = shell.toLowerCase();
7266
+ if (shellLower === "bash") {
7267
+ console.log(bashScript);
7268
+ } else if (shellLower === "zsh") {
7269
+ console.log(zshScript);
7270
+ } else {
7271
+ console.error(`Unsupported shell: ${shell}`);
7272
+ console.error("Supported shells: bash, zsh");
7273
+ process.exit(1);
7274
+ }
6665
7275
  });
6666
7276
 
6667
7277
  // src/cli/index.ts
6668
- var program = new import_commander9.Command();
7278
+ var program = new import_commander11.Command();
6669
7279
  program.name("servcraft").description("Servcraft - A modular Node.js backend framework CLI").version("0.1.0");
6670
7280
  program.addCommand(initCommand);
6671
7281
  program.addCommand(generateCommand);
@@ -6675,5 +7285,7 @@ program.addCommand(docsCommand);
6675
7285
  program.addCommand(listCommand);
6676
7286
  program.addCommand(removeCommand);
6677
7287
  program.addCommand(doctorCommand);
7288
+ program.addCommand(updateCommand);
7289
+ program.addCommand(completionCommand);
6678
7290
  program.parse();
6679
7291
  //# sourceMappingURL=index.cjs.map