servcraft 0.2.0 → 0.4.2

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_commander13 = 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,15 +1225,13 @@ 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
- var import_path4 = __toESM(require("path"), 1);
1229
+ var import_path5 = __toESM(require("path"), 1);
1422
1230
  var import_ora2 = __toESM(require("ora"), 1);
1423
1231
  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 fs14 = require("fs");
1459
+ if (!fs14.existsSync("package.json")) {
1460
+ return ErrorTypes.NOT_IN_PROJECT();
1461
+ }
1462
+ const packageJson = JSON.parse(fs14.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,386 @@ ${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
+
2513
+ // src/cli/utils/template-loader.ts
2514
+ var import_promises3 = __toESM(require("fs/promises"), 1);
2515
+ var import_path4 = __toESM(require("path"), 1);
2516
+ async function loadCustomTemplate(templateType) {
2517
+ const projectRoot = getProjectRoot();
2518
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
2519
+ const locations = [
2520
+ import_path4.default.join(projectRoot, ".servcraft", "templates", `${templateType}.ts`),
2521
+ import_path4.default.join(homeDir, ".servcraft", "templates", `${templateType}.ts`)
2522
+ ];
2523
+ for (const location of locations) {
2524
+ try {
2525
+ await import_promises3.default.access(location);
2526
+ const templateModule = await import(`file://${location}`);
2527
+ const functionName = `${templateType.replace(/-/g, "")}Template`;
2528
+ if (templateModule[functionName]) {
2529
+ return templateModule;
2530
+ }
2531
+ } catch {
2532
+ continue;
2533
+ }
2534
+ }
2535
+ return null;
2536
+ }
2537
+ async function getTemplate(templateType, builtInTemplate) {
2538
+ const customTemplate = await loadCustomTemplate(templateType);
2539
+ if (customTemplate) {
2540
+ const functionName = `${templateType.replace(/-/g, "")}Template`;
2541
+ return customTemplate[functionName];
2542
+ }
2543
+ return builtInTemplate;
2544
+ }
2545
+
2271
2546
  // src/cli/commands/generate.ts
2272
2547
  function enableDryRunIfNeeded(options) {
2273
2548
  const dryRun = DryRunManager.getInstance();
@@ -2284,7 +2559,7 @@ function showDryRunSummary(options) {
2284
2559
  var generateCommand = new import_commander2.Command("generate").alias("g").description("Generate resources (module, controller, service, etc.)");
2285
2560
  generateCommand.command("module <name> [fields...]").alias("m").description(
2286
2561
  "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) => {
2562
+ ).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
2563
  enableDryRunIfNeeded(options);
2289
2564
  let fields = [];
2290
2565
  if (options.interactive) {
@@ -2300,46 +2575,71 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
2300
2575
  const pluralName = pluralize(kebabName);
2301
2576
  const tableName = pluralize(kebabName.replace(/-/g, "_"));
2302
2577
  const validatorType = options.validator || "zod";
2303
- const moduleDir = import_path4.default.join(getModulesDir(), kebabName);
2578
+ const moduleDir = import_path5.default.join(getModulesDir(), kebabName);
2304
2579
  if (await fileExists(moduleDir)) {
2305
2580
  spinner.stop();
2306
2581
  error(`Module "${kebabName}" already exists`);
2307
2582
  return;
2308
2583
  }
2309
2584
  const hasFields = fields.length > 0;
2585
+ const controllerTpl = await getTemplate("controller", controllerTemplate);
2586
+ const serviceTpl = await getTemplate("service", serviceTemplate);
2587
+ const repositoryTpl = await getTemplate("repository", repositoryTemplate);
2588
+ const typesTpl = await getTemplate("types", typesTemplate);
2589
+ const schemasTpl = await getTemplate("schemas", schemasTemplate);
2590
+ const routesTpl = await getTemplate("routes", routesTemplate);
2591
+ const moduleIndexTpl = await getTemplate("module-index", moduleIndexTemplate);
2310
2592
  const files = [
2311
2593
  {
2312
2594
  name: `${kebabName}.types.ts`,
2313
- content: hasFields ? dynamicTypesTemplate(kebabName, pascalName, fields) : typesTemplate(kebabName, pascalName)
2595
+ content: hasFields ? dynamicTypesTemplate(kebabName, pascalName, fields) : typesTpl(kebabName, pascalName)
2314
2596
  },
2315
2597
  {
2316
2598
  name: `${kebabName}.schemas.ts`,
2317
- content: hasFields ? dynamicSchemasTemplate(kebabName, pascalName, camelName, fields, validatorType) : schemasTemplate(kebabName, pascalName, camelName)
2599
+ content: hasFields ? dynamicSchemasTemplate(kebabName, pascalName, camelName, fields, validatorType) : schemasTpl(kebabName, pascalName, camelName)
2318
2600
  },
2319
2601
  {
2320
2602
  name: `${kebabName}.service.ts`,
2321
- content: serviceTemplate(kebabName, pascalName, camelName)
2603
+ content: serviceTpl(kebabName, pascalName, camelName)
2322
2604
  },
2323
2605
  {
2324
2606
  name: `${kebabName}.controller.ts`,
2325
- content: controllerTemplate(kebabName, pascalName, camelName)
2607
+ content: controllerTpl(kebabName, pascalName, camelName)
2326
2608
  },
2327
- { name: "index.ts", content: moduleIndexTemplate(kebabName, pascalName, camelName) }
2609
+ { name: "index.ts", content: moduleIndexTpl(kebabName, pascalName, camelName) }
2328
2610
  ];
2329
2611
  if (options.repository !== false) {
2330
2612
  files.push({
2331
2613
  name: `${kebabName}.repository.ts`,
2332
- content: repositoryTemplate(kebabName, pascalName, camelName, pluralName)
2614
+ content: repositoryTpl(kebabName, pascalName, camelName, pluralName)
2333
2615
  });
2334
2616
  }
2335
2617
  if (options.routes !== false) {
2336
2618
  files.push({
2337
2619
  name: `${kebabName}.routes.ts`,
2338
- content: routesTemplate(kebabName, pascalName, camelName, pluralName)
2620
+ content: routesTpl(kebabName, pascalName, camelName, pluralName)
2339
2621
  });
2340
2622
  }
2341
2623
  for (const file of files) {
2342
- await writeFile(import_path4.default.join(moduleDir, file.name), file.content);
2624
+ await writeFile(import_path5.default.join(moduleDir, file.name), file.content);
2625
+ }
2626
+ if (options.withTests) {
2627
+ const testDir = import_path5.default.join(moduleDir, "__tests__");
2628
+ const controllerTestTpl = await getTemplate("controller-test", controllerTestTemplate);
2629
+ const serviceTestTpl = await getTemplate("service-test", serviceTestTemplate);
2630
+ const integrationTestTpl = await getTemplate("integration-test", integrationTestTemplate);
2631
+ await writeFile(
2632
+ import_path5.default.join(testDir, `${kebabName}.controller.test.ts`),
2633
+ controllerTestTpl(kebabName, pascalName, camelName)
2634
+ );
2635
+ await writeFile(
2636
+ import_path5.default.join(testDir, `${kebabName}.service.test.ts`),
2637
+ serviceTestTpl(kebabName, pascalName, camelName)
2638
+ );
2639
+ await writeFile(
2640
+ import_path5.default.join(testDir, `${kebabName}.integration.test.ts`),
2641
+ integrationTestTpl(kebabName, pascalName, camelName)
2642
+ );
2343
2643
  }
2344
2644
  spinner.succeed(`Module "${pascalName}" generated successfully!`);
2345
2645
  if (options.prisma || hasFields) {
@@ -2364,6 +2664,11 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
2364
2664
  }
2365
2665
  console.log("\n\u{1F4C1} Files created:");
2366
2666
  files.forEach((f) => success(` src/modules/${kebabName}/${f.name}`));
2667
+ if (options.withTests) {
2668
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.controller.test.ts`);
2669
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.service.test.ts`);
2670
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.integration.test.ts`);
2671
+ }
2367
2672
  console.log("\n\u{1F4CC} Next steps:");
2368
2673
  if (!hasFields) {
2369
2674
  info(` 1. Update the types in ${kebabName}.types.ts`);
@@ -2391,8 +2696,8 @@ generateCommand.command("controller <name>").alias("c").description("Generate a
2391
2696
  const pascalName = toPascalCase(name);
2392
2697
  const camelName = toCamelCase(name);
2393
2698
  const moduleName = options.module ? toKebabCase(options.module) : kebabName;
2394
- const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
2395
- const filePath = import_path4.default.join(moduleDir, `${kebabName}.controller.ts`);
2699
+ const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
2700
+ const filePath = import_path5.default.join(moduleDir, `${kebabName}.controller.ts`);
2396
2701
  if (await fileExists(filePath)) {
2397
2702
  spinner.stop();
2398
2703
  displayError(ErrorTypes.FILE_ALREADY_EXISTS(`${kebabName}.controller.ts`));
@@ -2415,8 +2720,8 @@ generateCommand.command("service <name>").alias("s").description("Generate a ser
2415
2720
  const pascalName = toPascalCase(name);
2416
2721
  const camelName = toCamelCase(name);
2417
2722
  const moduleName = options.module ? toKebabCase(options.module) : kebabName;
2418
- const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
2419
- const filePath = import_path4.default.join(moduleDir, `${kebabName}.service.ts`);
2723
+ const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
2724
+ const filePath = import_path5.default.join(moduleDir, `${kebabName}.service.ts`);
2420
2725
  if (await fileExists(filePath)) {
2421
2726
  spinner.stop();
2422
2727
  error(`Service "${kebabName}" already exists`);
@@ -2440,8 +2745,8 @@ generateCommand.command("repository <name>").alias("r").description("Generate a
2440
2745
  const camelName = toCamelCase(name);
2441
2746
  const pluralName = pluralize(kebabName);
2442
2747
  const moduleName = options.module ? toKebabCase(options.module) : kebabName;
2443
- const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
2444
- const filePath = import_path4.default.join(moduleDir, `${kebabName}.repository.ts`);
2748
+ const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
2749
+ const filePath = import_path5.default.join(moduleDir, `${kebabName}.repository.ts`);
2445
2750
  if (await fileExists(filePath)) {
2446
2751
  spinner.stop();
2447
2752
  error(`Repository "${kebabName}" already exists`);
@@ -2463,8 +2768,8 @@ generateCommand.command("types <name>").alias("t").description("Generate types/i
2463
2768
  const kebabName = toKebabCase(name);
2464
2769
  const pascalName = toPascalCase(name);
2465
2770
  const moduleName = options.module ? toKebabCase(options.module) : kebabName;
2466
- const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
2467
- const filePath = import_path4.default.join(moduleDir, `${kebabName}.types.ts`);
2771
+ const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
2772
+ const filePath = import_path5.default.join(moduleDir, `${kebabName}.types.ts`);
2468
2773
  if (await fileExists(filePath)) {
2469
2774
  spinner.stop();
2470
2775
  error(`Types file "${kebabName}.types.ts" already exists`);
@@ -2487,8 +2792,8 @@ generateCommand.command("schema <name>").alias("v").description("Generate valida
2487
2792
  const pascalName = toPascalCase(name);
2488
2793
  const camelName = toCamelCase(name);
2489
2794
  const moduleName = options.module ? toKebabCase(options.module) : kebabName;
2490
- const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
2491
- const filePath = import_path4.default.join(moduleDir, `${kebabName}.schemas.ts`);
2795
+ const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
2796
+ const filePath = import_path5.default.join(moduleDir, `${kebabName}.schemas.ts`);
2492
2797
  if (await fileExists(filePath)) {
2493
2798
  spinner.stop();
2494
2799
  error(`Schemas file "${kebabName}.schemas.ts" already exists`);
@@ -2512,8 +2817,8 @@ generateCommand.command("routes <name>").description("Generate routes").option("
2512
2817
  const camelName = toCamelCase(name);
2513
2818
  const pluralName = pluralize(kebabName);
2514
2819
  const moduleName = options.module ? toKebabCase(options.module) : kebabName;
2515
- const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
2516
- const filePath = import_path4.default.join(moduleDir, `${kebabName}.routes.ts`);
2820
+ const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
2821
+ const filePath = import_path5.default.join(moduleDir, `${kebabName}.routes.ts`);
2517
2822
  if (await fileExists(filePath)) {
2518
2823
  spinner.stop();
2519
2824
  error(`Routes file "${kebabName}.routes.ts" already exists`);
@@ -2600,24 +2905,22 @@ async function promptForFields() {
2600
2905
  }
2601
2906
 
2602
2907
  // src/cli/commands/add-module.ts
2603
- init_cjs_shims();
2604
2908
  var import_commander3 = require("commander");
2605
- var import_path5 = __toESM(require("path"), 1);
2909
+ var import_path6 = __toESM(require("path"), 1);
2606
2910
  var import_ora3 = __toESM(require("ora"), 1);
2607
2911
  var import_chalk7 = __toESM(require("chalk"), 1);
2608
- var fs5 = __toESM(require("fs/promises"), 1);
2912
+ var fs6 = __toESM(require("fs/promises"), 1);
2609
2913
 
2610
2914
  // src/cli/utils/env-manager.ts
2611
- init_cjs_shims();
2612
- var fs3 = __toESM(require("fs/promises"), 1);
2613
- var path5 = __toESM(require("path"), 1);
2915
+ var fs4 = __toESM(require("fs/promises"), 1);
2916
+ var path6 = __toESM(require("path"), 1);
2614
2917
  var import_fs = require("fs");
2615
2918
  var EnvManager = class {
2616
2919
  envPath;
2617
2920
  envExamplePath;
2618
2921
  constructor(projectRoot) {
2619
- this.envPath = path5.join(projectRoot, ".env");
2620
- this.envExamplePath = path5.join(projectRoot, ".env.example");
2922
+ this.envPath = path6.join(projectRoot, ".env");
2923
+ this.envExamplePath = path6.join(projectRoot, ".env.example");
2621
2924
  }
2622
2925
  /**
2623
2926
  * Add environment variables to .env file
@@ -2628,7 +2931,7 @@ var EnvManager = class {
2628
2931
  let created2 = false;
2629
2932
  let envContent = "";
2630
2933
  if ((0, import_fs.existsSync)(this.envPath)) {
2631
- envContent = await fs3.readFile(this.envPath, "utf-8");
2934
+ envContent = await fs4.readFile(this.envPath, "utf-8");
2632
2935
  } else {
2633
2936
  created2 = true;
2634
2937
  }
@@ -2657,7 +2960,7 @@ var EnvManager = class {
2657
2960
  }
2658
2961
  newContent += "\n";
2659
2962
  }
2660
- await fs3.writeFile(this.envPath, newContent, "utf-8");
2963
+ await fs4.writeFile(this.envPath, newContent, "utf-8");
2661
2964
  if ((0, import_fs.existsSync)(this.envExamplePath)) {
2662
2965
  await this.updateEnvExample(sections);
2663
2966
  }
@@ -2669,7 +2972,7 @@ var EnvManager = class {
2669
2972
  async updateEnvExample(sections) {
2670
2973
  let exampleContent = "";
2671
2974
  if ((0, import_fs.existsSync)(this.envExamplePath)) {
2672
- exampleContent = await fs3.readFile(this.envExamplePath, "utf-8");
2975
+ exampleContent = await fs4.readFile(this.envExamplePath, "utf-8");
2673
2976
  }
2674
2977
  const existingKeys = this.parseExistingKeys(exampleContent);
2675
2978
  let newContent = exampleContent;
@@ -2693,7 +2996,7 @@ var EnvManager = class {
2693
2996
  }
2694
2997
  newContent += "\n";
2695
2998
  }
2696
- await fs3.writeFile(this.envExamplePath, newContent, "utf-8");
2999
+ await fs4.writeFile(this.envExamplePath, newContent, "utf-8");
2697
3000
  }
2698
3001
  /**
2699
3002
  * Parse existing environment variable keys
@@ -3267,35 +3570,34 @@ var EnvManager = class {
3267
3570
  };
3268
3571
 
3269
3572
  // src/cli/utils/template-manager.ts
3270
- init_cjs_shims();
3271
- var fs4 = __toESM(require("fs/promises"), 1);
3272
- var path6 = __toESM(require("path"), 1);
3573
+ var fs5 = __toESM(require("fs/promises"), 1);
3574
+ var path7 = __toESM(require("path"), 1);
3273
3575
  var import_crypto = require("crypto");
3274
3576
  var import_fs2 = require("fs");
3275
3577
  var TemplateManager = class {
3276
3578
  templatesDir;
3277
3579
  manifestsDir;
3278
3580
  constructor(projectRoot) {
3279
- this.templatesDir = path6.join(projectRoot, ".servcraft", "templates");
3280
- this.manifestsDir = path6.join(projectRoot, ".servcraft", "manifests");
3581
+ this.templatesDir = path7.join(projectRoot, ".servcraft", "templates");
3582
+ this.manifestsDir = path7.join(projectRoot, ".servcraft", "manifests");
3281
3583
  }
3282
3584
  /**
3283
3585
  * Initialize template system
3284
3586
  */
3285
3587
  async initialize() {
3286
- await fs4.mkdir(this.templatesDir, { recursive: true });
3287
- await fs4.mkdir(this.manifestsDir, { recursive: true });
3588
+ await fs5.mkdir(this.templatesDir, { recursive: true });
3589
+ await fs5.mkdir(this.manifestsDir, { recursive: true });
3288
3590
  }
3289
3591
  /**
3290
3592
  * Save module template
3291
3593
  */
3292
3594
  async saveTemplate(moduleName, files) {
3293
3595
  await this.initialize();
3294
- const moduleTemplateDir = path6.join(this.templatesDir, moduleName);
3295
- await fs4.mkdir(moduleTemplateDir, { recursive: true });
3596
+ const moduleTemplateDir = path7.join(this.templatesDir, moduleName);
3597
+ await fs5.mkdir(moduleTemplateDir, { recursive: true });
3296
3598
  for (const [fileName, content] of Object.entries(files)) {
3297
- const filePath = path6.join(moduleTemplateDir, fileName);
3298
- await fs4.writeFile(filePath, content, "utf-8");
3599
+ const filePath = path7.join(moduleTemplateDir, fileName);
3600
+ await fs5.writeFile(filePath, content, "utf-8");
3299
3601
  }
3300
3602
  }
3301
3603
  /**
@@ -3303,8 +3605,8 @@ var TemplateManager = class {
3303
3605
  */
3304
3606
  async getTemplate(moduleName, fileName) {
3305
3607
  try {
3306
- const filePath = path6.join(this.templatesDir, moduleName, fileName);
3307
- return await fs4.readFile(filePath, "utf-8");
3608
+ const filePath = path7.join(this.templatesDir, moduleName, fileName);
3609
+ return await fs5.readFile(filePath, "utf-8");
3308
3610
  } catch {
3309
3611
  return null;
3310
3612
  }
@@ -3328,16 +3630,16 @@ var TemplateManager = class {
3328
3630
  installedAt: /* @__PURE__ */ new Date(),
3329
3631
  updatedAt: /* @__PURE__ */ new Date()
3330
3632
  };
3331
- const manifestPath = path6.join(this.manifestsDir, `${moduleName}.json`);
3332
- await fs4.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
3633
+ const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
3634
+ await fs5.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
3333
3635
  }
3334
3636
  /**
3335
3637
  * Get module manifest
3336
3638
  */
3337
3639
  async getManifest(moduleName) {
3338
3640
  try {
3339
- const manifestPath = path6.join(this.manifestsDir, `${moduleName}.json`);
3340
- const content = await fs4.readFile(manifestPath, "utf-8");
3641
+ const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
3642
+ const content = await fs5.readFile(manifestPath, "utf-8");
3341
3643
  return JSON.parse(content);
3342
3644
  } catch {
3343
3645
  return null;
@@ -3365,7 +3667,7 @@ var TemplateManager = class {
3365
3667
  }
3366
3668
  const results = [];
3367
3669
  for (const [fileName, fileInfo] of Object.entries(manifest.files)) {
3368
- const filePath = path6.join(moduleDir, fileName);
3670
+ const filePath = path7.join(moduleDir, fileName);
3369
3671
  if (!(0, import_fs2.existsSync)(filePath)) {
3370
3672
  results.push({
3371
3673
  fileName,
@@ -3375,7 +3677,7 @@ var TemplateManager = class {
3375
3677
  });
3376
3678
  continue;
3377
3679
  }
3378
- const currentContent = await fs4.readFile(filePath, "utf-8");
3680
+ const currentContent = await fs5.readFile(filePath, "utf-8");
3379
3681
  const currentHash = this.hashContent(currentContent);
3380
3682
  results.push({
3381
3683
  fileName,
@@ -3391,7 +3693,7 @@ var TemplateManager = class {
3391
3693
  */
3392
3694
  async createBackup(moduleName, moduleDir) {
3393
3695
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
3394
- const backupDir = path6.join(path6.dirname(moduleDir), `${moduleName}.backup-${timestamp}`);
3696
+ const backupDir = path7.join(path7.dirname(moduleDir), `${moduleName}.backup-${timestamp}`);
3395
3697
  await this.copyDirectory(moduleDir, backupDir);
3396
3698
  return backupDir;
3397
3699
  }
@@ -3399,15 +3701,15 @@ var TemplateManager = class {
3399
3701
  * Copy directory recursively
3400
3702
  */
3401
3703
  async copyDirectory(src, dest) {
3402
- await fs4.mkdir(dest, { recursive: true });
3403
- const entries = await fs4.readdir(src, { withFileTypes: true });
3704
+ await fs5.mkdir(dest, { recursive: true });
3705
+ const entries = await fs5.readdir(src, { withFileTypes: true });
3404
3706
  for (const entry of entries) {
3405
- const srcPath = path6.join(src, entry.name);
3406
- const destPath = path6.join(dest, entry.name);
3707
+ const srcPath = path7.join(src, entry.name);
3708
+ const destPath = path7.join(dest, entry.name);
3407
3709
  if (entry.isDirectory()) {
3408
3710
  await this.copyDirectory(srcPath, destPath);
3409
3711
  } else {
3410
- await fs4.copyFile(srcPath, destPath);
3712
+ await fs5.copyFile(srcPath, destPath);
3411
3713
  }
3412
3714
  }
3413
3715
  }
@@ -3504,13 +3806,12 @@ var TemplateManager = class {
3504
3806
  }
3505
3807
  manifest.files = fileHashes;
3506
3808
  manifest.updatedAt = /* @__PURE__ */ new Date();
3507
- const manifestPath = path6.join(this.manifestsDir, `${moduleName}.json`);
3508
- await fs4.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
3809
+ const manifestPath = path7.join(this.manifestsDir, `${moduleName}.json`);
3810
+ await fs5.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
3509
3811
  }
3510
3812
  };
3511
3813
 
3512
3814
  // src/cli/utils/interactive-prompt.ts
3513
- init_cjs_shims();
3514
3815
  var import_inquirer3 = __toESM(require("inquirer"), 1);
3515
3816
  var import_chalk6 = __toESM(require("chalk"), 1);
3516
3817
  var InteractivePrompt = class {
@@ -3696,7 +3997,6 @@ var InteractivePrompt = class {
3696
3997
  };
3697
3998
 
3698
3999
  // src/cli/commands/add-module.ts
3699
- init_error_handler();
3700
4000
  var AVAILABLE_MODULES = {
3701
4001
  auth: {
3702
4002
  name: "Authentication",
@@ -3864,7 +4164,7 @@ var addModuleCommand = new import_commander3.Command("add").description("Add a p
3864
4164
  }
3865
4165
  const spinner = (0, import_ora3.default)(`Adding ${module2.name} module...`).start();
3866
4166
  try {
3867
- const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
4167
+ const moduleDir = import_path6.default.join(getModulesDir(), moduleName);
3868
4168
  const templateManager = new TemplateManager(process.cwd());
3869
4169
  const moduleExists = await fileExists(moduleDir);
3870
4170
  if (moduleExists) {
@@ -3897,7 +4197,7 @@ var addModuleCommand = new import_commander3.Command("add").description("Add a p
3897
4197
  const backupPath = await templateManager.createBackup(moduleName, moduleDir);
3898
4198
  InteractivePrompt.showBackupCreated(backupPath);
3899
4199
  }
3900
- await fs5.rm(moduleDir, { recursive: true, force: true });
4200
+ await fs6.rm(moduleDir, { recursive: true, force: true });
3901
4201
  await ensureDir(moduleDir);
3902
4202
  await generateModuleFiles(moduleName, moduleDir);
3903
4203
  const files = await getModuleFiles(moduleName, moduleDir);
@@ -4008,7 +4308,7 @@ export * from './auth.schemas.js';
4008
4308
  `
4009
4309
  };
4010
4310
  for (const [name, content] of Object.entries(files)) {
4011
- await writeFile(import_path5.default.join(dir, name), content);
4311
+ await writeFile(import_path6.default.join(dir, name), content);
4012
4312
  }
4013
4313
  }
4014
4314
  async function generateUsersModule(dir) {
@@ -4048,7 +4348,7 @@ export * from './user.schemas.js';
4048
4348
  `
4049
4349
  };
4050
4350
  for (const [name, content] of Object.entries(files)) {
4051
- await writeFile(import_path5.default.join(dir, name), content);
4351
+ await writeFile(import_path6.default.join(dir, name), content);
4052
4352
  }
4053
4353
  }
4054
4354
  async function generateEmailModule(dir) {
@@ -4105,7 +4405,7 @@ export { EmailService, emailService } from './email.service.js';
4105
4405
  `
4106
4406
  };
4107
4407
  for (const [name, content] of Object.entries(files)) {
4108
- await writeFile(import_path5.default.join(dir, name), content);
4408
+ await writeFile(import_path6.default.join(dir, name), content);
4109
4409
  }
4110
4410
  }
4111
4411
  async function generateAuditModule(dir) {
@@ -4149,7 +4449,7 @@ export { AuditService, auditService } from './audit.service.js';
4149
4449
  `
4150
4450
  };
4151
4451
  for (const [name, content] of Object.entries(files)) {
4152
- await writeFile(import_path5.default.join(dir, name), content);
4452
+ await writeFile(import_path6.default.join(dir, name), content);
4153
4453
  }
4154
4454
  }
4155
4455
  async function generateUploadModule(dir) {
@@ -4175,7 +4475,7 @@ export interface UploadOptions {
4175
4475
  `
4176
4476
  };
4177
4477
  for (const [name, content] of Object.entries(files)) {
4178
- await writeFile(import_path5.default.join(dir, name), content);
4478
+ await writeFile(import_path6.default.join(dir, name), content);
4179
4479
  }
4180
4480
  }
4181
4481
  async function generateCacheModule(dir) {
@@ -4221,7 +4521,7 @@ export { CacheService, cacheService } from './cache.service.js';
4221
4521
  `
4222
4522
  };
4223
4523
  for (const [name, content] of Object.entries(files)) {
4224
- await writeFile(import_path5.default.join(dir, name), content);
4524
+ await writeFile(import_path6.default.join(dir, name), content);
4225
4525
  }
4226
4526
  }
4227
4527
  async function generateGenericModule(dir, name) {
@@ -4235,22 +4535,22 @@ export interface ${name.charAt(0).toUpperCase() + name.slice(1)}Data {
4235
4535
  `
4236
4536
  };
4237
4537
  for (const [fileName, content] of Object.entries(files)) {
4238
- await writeFile(import_path5.default.join(dir, fileName), content);
4538
+ await writeFile(import_path6.default.join(dir, fileName), content);
4239
4539
  }
4240
4540
  }
4241
4541
  async function findServercraftModules() {
4242
- const scriptDir = import_path5.default.dirname(new URL(importMetaUrl).pathname);
4542
+ const scriptDir = import_path6.default.dirname(new URL(importMetaUrl).pathname);
4243
4543
  const possiblePaths = [
4244
4544
  // Local node_modules (when servcraft is a dependency)
4245
- import_path5.default.join(process.cwd(), "node_modules", "servcraft", "src", "modules"),
4545
+ import_path6.default.join(process.cwd(), "node_modules", "servcraft", "src", "modules"),
4246
4546
  // From dist/cli/index.js -> src/modules (npx or global install)
4247
- import_path5.default.resolve(scriptDir, "..", "..", "src", "modules"),
4547
+ import_path6.default.resolve(scriptDir, "..", "..", "src", "modules"),
4248
4548
  // From src/cli/commands/add-module.ts -> src/modules (development)
4249
- import_path5.default.resolve(scriptDir, "..", "..", "modules")
4549
+ import_path6.default.resolve(scriptDir, "..", "..", "modules")
4250
4550
  ];
4251
4551
  for (const p of possiblePaths) {
4252
4552
  try {
4253
- const stats = await fs5.stat(p);
4553
+ const stats = await fs6.stat(p);
4254
4554
  if (stats.isDirectory()) {
4255
4555
  return p;
4256
4556
  }
@@ -4270,7 +4570,7 @@ async function generateModuleFiles(moduleName, moduleDir) {
4270
4570
  const sourceDirName = moduleNameMap[moduleName] || moduleName;
4271
4571
  const servercraftModulesDir = await findServercraftModules();
4272
4572
  if (servercraftModulesDir) {
4273
- const sourceModuleDir = import_path5.default.join(servercraftModulesDir, sourceDirName);
4573
+ const sourceModuleDir = import_path6.default.join(servercraftModulesDir, sourceDirName);
4274
4574
  if (await fileExists(sourceModuleDir)) {
4275
4575
  await copyModuleFromSource(sourceModuleDir, moduleDir);
4276
4576
  return;
@@ -4300,26 +4600,26 @@ async function generateModuleFiles(moduleName, moduleDir) {
4300
4600
  }
4301
4601
  }
4302
4602
  async function copyModuleFromSource(sourceDir, targetDir) {
4303
- const entries = await fs5.readdir(sourceDir, { withFileTypes: true });
4603
+ const entries = await fs6.readdir(sourceDir, { withFileTypes: true });
4304
4604
  for (const entry of entries) {
4305
- const sourcePath = import_path5.default.join(sourceDir, entry.name);
4306
- const targetPath = import_path5.default.join(targetDir, entry.name);
4605
+ const sourcePath = import_path6.default.join(sourceDir, entry.name);
4606
+ const targetPath = import_path6.default.join(targetDir, entry.name);
4307
4607
  if (entry.isDirectory()) {
4308
- await fs5.mkdir(targetPath, { recursive: true });
4608
+ await fs6.mkdir(targetPath, { recursive: true });
4309
4609
  await copyModuleFromSource(sourcePath, targetPath);
4310
4610
  } else {
4311
- await fs5.copyFile(sourcePath, targetPath);
4611
+ await fs6.copyFile(sourcePath, targetPath);
4312
4612
  }
4313
4613
  }
4314
4614
  }
4315
4615
  async function getModuleFiles(moduleName, moduleDir) {
4316
4616
  const files = {};
4317
- const entries = await fs5.readdir(moduleDir);
4617
+ const entries = await fs6.readdir(moduleDir);
4318
4618
  for (const entry of entries) {
4319
- const filePath = import_path5.default.join(moduleDir, entry);
4320
- const stat2 = await fs5.stat(filePath);
4619
+ const filePath = import_path6.default.join(moduleDir, entry);
4620
+ const stat2 = await fs6.stat(filePath);
4321
4621
  if (stat2.isFile() && entry.endsWith(".ts")) {
4322
- const content = await fs5.readFile(filePath, "utf-8");
4622
+ const content = await fs6.readFile(filePath, "utf-8");
4323
4623
  files[entry] = content;
4324
4624
  }
4325
4625
  }
@@ -4334,8 +4634,8 @@ async function showDiffForModule(templateManager, moduleName, moduleDir) {
4334
4634
  if (file.isModified) {
4335
4635
  console.log(import_chalk7.default.yellow(`
4336
4636
  \u{1F4C4} ${file.fileName}:`));
4337
- const currentPath = import_path5.default.join(moduleDir, file.fileName);
4338
- const currentContent = await fs5.readFile(currentPath, "utf-8");
4637
+ const currentPath = import_path6.default.join(moduleDir, file.fileName);
4638
+ const currentContent = await fs6.readFile(currentPath, "utf-8");
4339
4639
  const originalContent = await templateManager.getTemplate(moduleName, file.fileName);
4340
4640
  if (originalContent) {
4341
4641
  const diff = templateManager.generateDiff(originalContent, currentContent);
@@ -4347,11 +4647,11 @@ async function showDiffForModule(templateManager, moduleName, moduleDir) {
4347
4647
  async function performSmartMerge(templateManager, moduleName, moduleDir, _displayName) {
4348
4648
  const spinner = (0, import_ora3.default)("Analyzing files for merge...").start();
4349
4649
  const newFiles = {};
4350
- const templateDir = import_path5.default.join(templateManager["templatesDir"], moduleName);
4650
+ const templateDir = import_path6.default.join(templateManager["templatesDir"], moduleName);
4351
4651
  try {
4352
- const entries = await fs5.readdir(templateDir);
4652
+ const entries = await fs6.readdir(templateDir);
4353
4653
  for (const entry of entries) {
4354
- const content = await fs5.readFile(import_path5.default.join(templateDir, entry), "utf-8");
4654
+ const content = await fs6.readFile(import_path6.default.join(templateDir, entry), "utf-8");
4355
4655
  newFiles[entry] = content;
4356
4656
  }
4357
4657
  } catch {
@@ -4369,7 +4669,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
4369
4669
  };
4370
4670
  for (const fileInfo of modifiedFiles) {
4371
4671
  const fileName = fileInfo.fileName;
4372
- const filePath = import_path5.default.join(moduleDir, fileName);
4672
+ const filePath = import_path6.default.join(moduleDir, fileName);
4373
4673
  const newContent = newFiles[fileName];
4374
4674
  if (!newContent) {
4375
4675
  continue;
@@ -4382,7 +4682,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
4382
4682
  } else if (batchAction === "overwrite-all") {
4383
4683
  fileAction = "overwrite";
4384
4684
  } else {
4385
- const currentContent = await fs5.readFile(filePath, "utf-8");
4685
+ const currentContent = await fs6.readFile(filePath, "utf-8");
4386
4686
  const yourLines = currentContent.split("\n").length;
4387
4687
  const newLines = newContent.split("\n").length;
4388
4688
  const choice = await InteractivePrompt.askFileAction(
@@ -4406,20 +4706,20 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
4406
4706
  continue;
4407
4707
  }
4408
4708
  if (fileAction === "overwrite") {
4409
- await fs5.writeFile(filePath, newContent, "utf-8");
4709
+ await fs6.writeFile(filePath, newContent, "utf-8");
4410
4710
  stats.overwritten++;
4411
4711
  continue;
4412
4712
  }
4413
4713
  if (fileAction === "merge") {
4414
4714
  const originalContent = await templateManager.getTemplate(moduleName, fileName);
4415
- const currentContent = await fs5.readFile(filePath, "utf-8");
4715
+ const currentContent = await fs6.readFile(filePath, "utf-8");
4416
4716
  if (originalContent) {
4417
4717
  const mergeResult = await templateManager.mergeFiles(
4418
4718
  originalContent,
4419
4719
  currentContent,
4420
4720
  newContent
4421
4721
  );
4422
- await fs5.writeFile(filePath, mergeResult.merged, "utf-8");
4722
+ await fs6.writeFile(filePath, mergeResult.merged, "utf-8");
4423
4723
  if (mergeResult.hasConflicts) {
4424
4724
  stats.conflicts++;
4425
4725
  InteractivePrompt.displayConflicts(mergeResult.conflicts);
@@ -4427,7 +4727,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
4427
4727
  stats.merged++;
4428
4728
  }
4429
4729
  } else {
4430
- await fs5.writeFile(filePath, newContent, "utf-8");
4730
+ await fs6.writeFile(filePath, newContent, "utf-8");
4431
4731
  stats.overwritten++;
4432
4732
  }
4433
4733
  }
@@ -4438,7 +4738,6 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
4438
4738
  }
4439
4739
 
4440
4740
  // src/cli/commands/db.ts
4441
- init_cjs_shims();
4442
4741
  var import_commander4 = require("commander");
4443
4742
  var import_child_process2 = require("child_process");
4444
4743
  var import_ora4 = __toESM(require("ora"), 1);
@@ -4532,25 +4831,21 @@ dbCommand.command("status").description("Show migration status").action(async ()
4532
4831
  });
4533
4832
 
4534
4833
  // src/cli/commands/docs.ts
4535
- init_cjs_shims();
4536
4834
  var import_commander5 = require("commander");
4537
- var import_path7 = __toESM(require("path"), 1);
4538
- var import_promises4 = __toESM(require("fs/promises"), 1);
4835
+ var import_path8 = __toESM(require("path"), 1);
4836
+ var import_promises5 = __toESM(require("fs/promises"), 1);
4539
4837
  var import_ora6 = __toESM(require("ora"), 1);
4540
4838
  var import_chalk9 = __toESM(require("chalk"), 1);
4541
4839
 
4542
4840
  // src/cli/utils/docs-generator.ts
4543
- init_cjs_shims();
4544
- var import_promises3 = __toESM(require("fs/promises"), 1);
4545
- var import_path6 = __toESM(require("path"), 1);
4841
+ var import_promises4 = __toESM(require("fs/promises"), 1);
4842
+ var import_path7 = __toESM(require("path"), 1);
4546
4843
  var import_ora5 = __toESM(require("ora"), 1);
4547
4844
 
4548
4845
  // src/core/server.ts
4549
- init_cjs_shims();
4550
4846
  var import_fastify = __toESM(require("fastify"), 1);
4551
4847
 
4552
4848
  // src/core/logger.ts
4553
- init_cjs_shims();
4554
4849
  var import_pino = __toESM(require("pino"), 1);
4555
4850
  var defaultConfig = {
4556
4851
  level: process.env.LOG_LEVEL || "info",
@@ -4683,14 +4978,7 @@ function createServer(config2 = {}) {
4683
4978
  return new Server(config2);
4684
4979
  }
4685
4980
 
4686
- // src/middleware/index.ts
4687
- init_cjs_shims();
4688
-
4689
- // src/middleware/error-handler.ts
4690
- init_cjs_shims();
4691
-
4692
4981
  // src/utils/errors.ts
4693
- init_cjs_shims();
4694
4982
  var AppError = class _AppError extends Error {
4695
4983
  statusCode;
4696
4984
  isOperational;
@@ -4744,11 +5032,7 @@ function isAppError(error2) {
4744
5032
  return error2 instanceof AppError;
4745
5033
  }
4746
5034
 
4747
- // src/config/index.ts
4748
- init_cjs_shims();
4749
-
4750
5035
  // src/config/env.ts
4751
- init_cjs_shims();
4752
5036
  var import_zod = require("zod");
4753
5037
  var import_dotenv = __toESM(require("dotenv"), 1);
4754
5038
  import_dotenv.default.config();
@@ -4894,7 +5178,6 @@ function registerErrorHandler(app) {
4894
5178
  }
4895
5179
 
4896
5180
  // src/middleware/security.ts
4897
- init_cjs_shims();
4898
5181
  var import_helmet = __toESM(require("@fastify/helmet"), 1);
4899
5182
  var import_cors = __toESM(require("@fastify/cors"), 1);
4900
5183
  var import_rate_limit = __toESM(require("@fastify/rate-limit"), 1);
@@ -4954,11 +5237,7 @@ async function registerSecurity(app, options = {}) {
4954
5237
  }
4955
5238
  }
4956
5239
 
4957
- // src/modules/swagger/index.ts
4958
- init_cjs_shims();
4959
-
4960
5240
  // src/modules/swagger/swagger.service.ts
4961
- init_cjs_shims();
4962
5241
  var import_swagger = __toESM(require("@fastify/swagger"), 1);
4963
5242
  var import_swagger_ui = __toESM(require("@fastify/swagger-ui"), 1);
4964
5243
  var defaultConfig3 = {
@@ -5018,16 +5297,11 @@ async function registerSwagger(app, customConfig) {
5018
5297
  logger.info("Swagger documentation registered at /docs");
5019
5298
  }
5020
5299
 
5021
- // src/modules/swagger/schema-builder.ts
5022
- init_cjs_shims();
5023
-
5024
5300
  // src/modules/auth/index.ts
5025
- init_cjs_shims();
5026
5301
  var import_jwt = __toESM(require("@fastify/jwt"), 1);
5027
5302
  var import_cookie = __toESM(require("@fastify/cookie"), 1);
5028
5303
 
5029
5304
  // src/modules/auth/auth.service.ts
5030
- init_cjs_shims();
5031
5305
  var import_bcryptjs = __toESM(require("bcryptjs"), 1);
5032
5306
  var import_ioredis = require("ioredis");
5033
5307
  var AuthService = class {
@@ -5226,11 +5500,7 @@ function createAuthService(app) {
5226
5500
  return new AuthService(app);
5227
5501
  }
5228
5502
 
5229
- // src/modules/auth/auth.controller.ts
5230
- init_cjs_shims();
5231
-
5232
5503
  // src/modules/auth/schemas.ts
5233
- init_cjs_shims();
5234
5504
  var import_zod2 = require("zod");
5235
5505
  var loginSchema = import_zod2.z.object({
5236
5506
  email: import_zod2.z.string().email("Invalid email address"),
@@ -5257,7 +5527,6 @@ var changePasswordSchema = import_zod2.z.object({
5257
5527
  });
5258
5528
 
5259
5529
  // src/utils/response.ts
5260
- init_cjs_shims();
5261
5530
  function success2(reply, data, statusCode = 200) {
5262
5531
  const response = {
5263
5532
  success: true,
@@ -5273,7 +5542,6 @@ function noContent(reply) {
5273
5542
  }
5274
5543
 
5275
5544
  // src/modules/validation/validator.ts
5276
- init_cjs_shims();
5277
5545
  var import_zod3 = require("zod");
5278
5546
  function validateBody(schema, data) {
5279
5547
  const result = schema.safeParse(data);
@@ -5292,11 +5560,11 @@ function validateQuery(schema, data) {
5292
5560
  function formatZodErrors(error2) {
5293
5561
  const errors = {};
5294
5562
  for (const issue of error2.issues) {
5295
- const path11 = issue.path.join(".") || "root";
5296
- if (!errors[path11]) {
5297
- errors[path11] = [];
5563
+ const path15 = issue.path.join(".") || "root";
5564
+ if (!errors[path15]) {
5565
+ errors[path15] = [];
5298
5566
  }
5299
- errors[path11].push(issue.message);
5567
+ errors[path15].push(issue.message);
5300
5568
  }
5301
5569
  return errors;
5302
5570
  }
@@ -5444,11 +5712,7 @@ function createAuthController(authService, userService) {
5444
5712
  return new AuthController(authService, userService);
5445
5713
  }
5446
5714
 
5447
- // src/modules/auth/auth.routes.ts
5448
- init_cjs_shims();
5449
-
5450
5715
  // src/modules/auth/auth.middleware.ts
5451
- init_cjs_shims();
5452
5716
  function createAuthMiddleware(authService) {
5453
5717
  return async function authenticate(request, _reply) {
5454
5718
  const authHeader = request.headers.authorization;
@@ -5491,14 +5755,7 @@ function registerAuthRoutes(app, controller, authService) {
5491
5755
  );
5492
5756
  }
5493
5757
 
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
5758
  // src/database/prisma.ts
5501
- init_cjs_shims();
5502
5759
  var import_client = require("@prisma/client");
5503
5760
  var prismaClientSingleton = () => {
5504
5761
  return new import_client.PrismaClient({
@@ -5512,7 +5769,6 @@ if (!isProduction()) {
5512
5769
  }
5513
5770
 
5514
5771
  // src/utils/pagination.ts
5515
- init_cjs_shims();
5516
5772
  var DEFAULT_PAGE = 1;
5517
5773
  var DEFAULT_LIMIT = 20;
5518
5774
  var MAX_LIMIT = 100;
@@ -5777,7 +6033,6 @@ function createUserRepository() {
5777
6033
  }
5778
6034
 
5779
6035
  // src/modules/user/types.ts
5780
- init_cjs_shims();
5781
6036
  var DEFAULT_ROLE_PERMISSIONS = {
5782
6037
  user: ["profile:read", "profile:update"],
5783
6038
  moderator: [
@@ -5908,9 +6163,6 @@ function createUserService(repository) {
5908
6163
  return new UserService(repository || createUserRepository());
5909
6164
  }
5910
6165
 
5911
- // src/modules/auth/types.ts
5912
- init_cjs_shims();
5913
-
5914
6166
  // src/modules/auth/index.ts
5915
6167
  async function registerAuthModule(app) {
5916
6168
  await app.register(import_jwt.default, {
@@ -5930,14 +6182,7 @@ async function registerAuthModule(app) {
5930
6182
  logger.info("Auth module registered");
5931
6183
  }
5932
6184
 
5933
- // src/modules/user/index.ts
5934
- init_cjs_shims();
5935
-
5936
- // src/modules/user/user.controller.ts
5937
- init_cjs_shims();
5938
-
5939
6185
  // src/modules/user/schemas.ts
5940
- init_cjs_shims();
5941
6186
  var import_zod4 = require("zod");
5942
6187
  var userStatusEnum = import_zod4.z.enum(["active", "inactive", "suspended", "banned"]);
5943
6188
  var userRoleEnum = import_zod4.z.enum(["user", "admin", "moderator", "super_admin"]);
@@ -6064,7 +6309,6 @@ function createUserController(userService) {
6064
6309
  }
6065
6310
 
6066
6311
  // src/modules/user/user.routes.ts
6067
- init_cjs_shims();
6068
6312
  var idParamsSchema = {
6069
6313
  type: "object",
6070
6314
  properties: {
@@ -6153,9 +6397,9 @@ async function generateDocs(outputPath = "openapi.json", silent = false) {
6153
6397
  await registerUserModule(app, authService);
6154
6398
  await app.ready();
6155
6399
  const spec = app.swagger();
6156
- const absoluteOutput = import_path6.default.resolve(outputPath);
6157
- await import_promises3.default.mkdir(import_path6.default.dirname(absoluteOutput), { recursive: true });
6158
- await import_promises3.default.writeFile(absoluteOutput, JSON.stringify(spec, null, 2), "utf8");
6400
+ const absoluteOutput = import_path7.default.resolve(outputPath);
6401
+ await import_promises4.default.mkdir(import_path7.default.dirname(absoluteOutput), { recursive: true });
6402
+ await import_promises4.default.writeFile(absoluteOutput, JSON.stringify(spec, null, 2), "utf8");
6159
6403
  spinner?.succeed(`OpenAPI spec generated at ${absoluteOutput}`);
6160
6404
  await app.close();
6161
6405
  return absoluteOutput;
@@ -6171,10 +6415,10 @@ docsCommand.command("generate").alias("gen").description("Generate OpenAPI/Swagg
6171
6415
  try {
6172
6416
  const outputPath = await generateDocs(options.output, false);
6173
6417
  if (options.format === "yaml") {
6174
- const jsonContent = await import_promises4.default.readFile(outputPath, "utf-8");
6418
+ const jsonContent = await import_promises5.default.readFile(outputPath, "utf-8");
6175
6419
  const spec = JSON.parse(jsonContent);
6176
6420
  const yamlPath = outputPath.replace(".json", ".yaml");
6177
- await import_promises4.default.writeFile(yamlPath, jsonToYaml(spec));
6421
+ await import_promises5.default.writeFile(yamlPath, jsonToYaml(spec));
6178
6422
  success(`YAML documentation generated: ${yamlPath}`);
6179
6423
  }
6180
6424
  console.log("\n\u{1F4DA} Documentation URLs:");
@@ -6199,14 +6443,14 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
6199
6443
  const spinner = (0, import_ora6.default)("Exporting documentation...").start();
6200
6444
  try {
6201
6445
  const projectRoot = getProjectRoot();
6202
- const specPath = import_path7.default.join(projectRoot, "openapi.json");
6446
+ const specPath = import_path8.default.join(projectRoot, "openapi.json");
6203
6447
  try {
6204
- await import_promises4.default.access(specPath);
6448
+ await import_promises5.default.access(specPath);
6205
6449
  } catch {
6206
6450
  spinner.text = "Generating OpenAPI spec first...";
6207
6451
  await generateDocs("openapi.json", true);
6208
6452
  }
6209
- const specContent = await import_promises4.default.readFile(specPath, "utf-8");
6453
+ const specContent = await import_promises5.default.readFile(specPath, "utf-8");
6210
6454
  const spec = JSON.parse(specContent);
6211
6455
  let output;
6212
6456
  let defaultName;
@@ -6226,8 +6470,8 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
6226
6470
  default:
6227
6471
  throw new Error(`Unknown format: ${options.format}`);
6228
6472
  }
6229
- const outPath = import_path7.default.join(projectRoot, options.output || defaultName);
6230
- await import_promises4.default.writeFile(outPath, output);
6473
+ const outPath = import_path8.default.join(projectRoot, options.output || defaultName);
6474
+ await import_promises5.default.writeFile(outPath, output);
6231
6475
  spinner.succeed(`Exported to: ${options.output || defaultName}`);
6232
6476
  if (options.format === "postman") {
6233
6477
  info("\n Import in Postman: File > Import > Select file");
@@ -6240,13 +6484,13 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
6240
6484
  docsCommand.command("status").description("Show documentation status").action(async () => {
6241
6485
  const projectRoot = getProjectRoot();
6242
6486
  console.log(import_chalk9.default.bold("\n\u{1F4CA} Documentation Status\n"));
6243
- const specPath = import_path7.default.join(projectRoot, "openapi.json");
6487
+ const specPath = import_path8.default.join(projectRoot, "openapi.json");
6244
6488
  try {
6245
- const stat2 = await import_promises4.default.stat(specPath);
6489
+ const stat2 = await import_promises5.default.stat(specPath);
6246
6490
  success(
6247
6491
  `openapi.json exists (${formatBytes(stat2.size)}, modified ${formatDate(stat2.mtime)})`
6248
6492
  );
6249
- const content = await import_promises4.default.readFile(specPath, "utf-8");
6493
+ const content = await import_promises5.default.readFile(specPath, "utf-8");
6250
6494
  const spec = JSON.parse(content);
6251
6495
  const pathCount = Object.keys(spec.paths || {}).length;
6252
6496
  info(` ${pathCount} endpoints documented`);
@@ -6356,10 +6600,9 @@ function formatDate(date) {
6356
6600
  }
6357
6601
 
6358
6602
  // src/cli/commands/list.ts
6359
- init_cjs_shims();
6360
6603
  var import_commander6 = require("commander");
6361
6604
  var import_chalk10 = __toESM(require("chalk"), 1);
6362
- var import_promises5 = __toESM(require("fs/promises"), 1);
6605
+ var import_promises6 = __toESM(require("fs/promises"), 1);
6363
6606
  var AVAILABLE_MODULES2 = {
6364
6607
  // Core
6365
6608
  auth: {
@@ -6474,7 +6717,7 @@ var AVAILABLE_MODULES2 = {
6474
6717
  async function getInstalledModules() {
6475
6718
  try {
6476
6719
  const modulesDir = getModulesDir();
6477
- const entries = await import_promises5.default.readdir(modulesDir, { withFileTypes: true });
6720
+ const entries = await import_promises6.default.readdir(modulesDir, { withFileTypes: true });
6478
6721
  return entries.filter((e) => e.isDirectory()).map((e) => e.name);
6479
6722
  } catch {
6480
6723
  return [];
@@ -6514,7 +6757,7 @@ var listCommand = new import_commander6.Command("list").alias("ls").description(
6514
6757
  if (!byCategory[mod.category]) {
6515
6758
  byCategory[mod.category] = [];
6516
6759
  }
6517
- byCategory[mod.category].push({
6760
+ byCategory[mod.category]?.push({
6518
6761
  id: key,
6519
6762
  name: mod.name,
6520
6763
  description: mod.description,
@@ -6582,14 +6825,12 @@ var listCommand = new import_commander6.Command("list").alias("ls").description(
6582
6825
  );
6583
6826
 
6584
6827
  // src/cli/commands/remove.ts
6585
- init_cjs_shims();
6586
6828
  var import_commander7 = require("commander");
6587
- var import_path8 = __toESM(require("path"), 1);
6829
+ var import_path9 = __toESM(require("path"), 1);
6588
6830
  var import_ora7 = __toESM(require("ora"), 1);
6589
6831
  var import_chalk11 = __toESM(require("chalk"), 1);
6590
- var import_promises6 = __toESM(require("fs/promises"), 1);
6832
+ var import_promises7 = __toESM(require("fs/promises"), 1);
6591
6833
  var import_inquirer4 = __toESM(require("inquirer"), 1);
6592
- init_error_handler();
6593
6834
  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
6835
  const projectError = validateProject();
6595
6836
  if (projectError) {
@@ -6597,22 +6838,19 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
6597
6838
  return;
6598
6839
  }
6599
6840
  console.log(import_chalk11.default.bold.cyan("\n\u{1F5D1}\uFE0F ServCraft Module Removal\n"));
6600
- const moduleDir = import_path8.default.join(getModulesDir(), moduleName);
6841
+ const moduleDir = import_path9.default.join(getModulesDir(), moduleName);
6601
6842
  try {
6602
- const exists = await import_promises6.default.access(moduleDir).then(() => true).catch(() => false);
6843
+ const exists = await import_promises7.default.access(moduleDir).then(() => true).catch(() => false);
6603
6844
  if (!exists) {
6604
6845
  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
- )
6846
+ new ServCraftError(`Module "${moduleName}" is not installed`, [
6847
+ `Run ${import_chalk11.default.cyan("servcraft list --installed")} to see installed modules`,
6848
+ `Check the spelling of the module name`
6849
+ ])
6612
6850
  );
6613
6851
  return;
6614
6852
  }
6615
- const files = await import_promises6.default.readdir(moduleDir);
6853
+ const files = await import_promises7.default.readdir(moduleDir);
6616
6854
  const fileCount = files.length;
6617
6855
  if (!options?.yes) {
6618
6856
  console.log(import_chalk11.default.yellow(`\u26A0 This will remove the "${moduleName}" module:`));
@@ -6633,7 +6871,7 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
6633
6871
  }
6634
6872
  }
6635
6873
  const spinner = (0, import_ora7.default)("Removing module...").start();
6636
- await import_promises6.default.rm(moduleDir, { recursive: true, force: true });
6874
+ await import_promises7.default.rm(moduleDir, { recursive: true, force: true });
6637
6875
  spinner.succeed(`Module "${moduleName}" removed successfully!`);
6638
6876
  console.log("\n" + import_chalk11.default.bold("\u2713 Removed:"));
6639
6877
  success(` src/modules/${moduleName}/ (${fileCount} files)`);
@@ -6657,15 +6895,697 @@ var removeCommand = new import_commander7.Command("remove").alias("rm").descript
6657
6895
  });
6658
6896
 
6659
6897
  // src/cli/commands/doctor.ts
6660
- init_cjs_shims();
6661
6898
  var import_commander8 = require("commander");
6662
6899
  var import_chalk12 = __toESM(require("chalk"), 1);
6900
+ var import_promises8 = __toESM(require("fs/promises"), 1);
6901
+ async function checkNodeVersion() {
6902
+ const version = process.version;
6903
+ const major = parseInt(version.slice(1).split(".")[0] || "0", 10);
6904
+ if (major >= 18) {
6905
+ return { name: "Node.js", status: "pass", message: `${version} \u2713` };
6906
+ }
6907
+ return {
6908
+ name: "Node.js",
6909
+ status: "fail",
6910
+ message: `${version} (< 18)`,
6911
+ suggestion: "Upgrade to Node.js 18+"
6912
+ };
6913
+ }
6914
+ async function checkPackageJson() {
6915
+ const checks = [];
6916
+ try {
6917
+ const content = await import_promises8.default.readFile("package.json", "utf-8");
6918
+ const pkg = JSON.parse(content);
6919
+ checks.push({ name: "package.json", status: "pass", message: "Found" });
6920
+ if (pkg.dependencies?.fastify) {
6921
+ checks.push({ name: "Fastify", status: "pass", message: "Installed" });
6922
+ } else {
6923
+ checks.push({
6924
+ name: "Fastify",
6925
+ status: "fail",
6926
+ message: "Missing",
6927
+ suggestion: "npm install fastify"
6928
+ });
6929
+ }
6930
+ } catch {
6931
+ checks.push({
6932
+ name: "package.json",
6933
+ status: "fail",
6934
+ message: "Not found",
6935
+ suggestion: "Run servcraft init"
6936
+ });
6937
+ }
6938
+ return checks;
6939
+ }
6940
+ async function checkDirectories() {
6941
+ const checks = [];
6942
+ const dirs = ["src", "node_modules", ".git", ".env"];
6943
+ for (const dir of dirs) {
6944
+ try {
6945
+ await import_promises8.default.access(dir);
6946
+ checks.push({ name: dir, status: "pass", message: "Exists" });
6947
+ } catch {
6948
+ const isCritical = dir === "src" || dir === "node_modules";
6949
+ checks.push({
6950
+ name: dir,
6951
+ status: isCritical ? "fail" : "warn",
6952
+ message: "Not found",
6953
+ suggestion: dir === "node_modules" ? "npm install" : dir === ".env" ? "Create .env file" : void 0
6954
+ });
6955
+ }
6956
+ }
6957
+ return checks;
6958
+ }
6663
6959
  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"));
6960
+ console.log(import_chalk12.default.bold.cyan("\n\u{1F50D} ServCraft Doctor\n"));
6961
+ const allChecks = [];
6962
+ allChecks.push(await checkNodeVersion());
6963
+ allChecks.push(...await checkPackageJson());
6964
+ allChecks.push(...await checkDirectories());
6965
+ allChecks.forEach((check) => {
6966
+ const icon = check.status === "pass" ? import_chalk12.default.green("\u2713") : check.status === "warn" ? import_chalk12.default.yellow("\u26A0") : import_chalk12.default.red("\u2717");
6967
+ const color = check.status === "pass" ? import_chalk12.default.green : check.status === "warn" ? import_chalk12.default.yellow : import_chalk12.default.red;
6968
+ console.log(`${icon} ${check.name.padEnd(20)} ${color(check.message)}`);
6969
+ if (check.suggestion) {
6970
+ console.log(import_chalk12.default.gray(` \u2192 ${check.suggestion}`));
6971
+ }
6972
+ });
6973
+ const pass = allChecks.filter((c) => c.status === "pass").length;
6974
+ const warn2 = allChecks.filter((c) => c.status === "warn").length;
6975
+ const fail = allChecks.filter((c) => c.status === "fail").length;
6976
+ console.log(import_chalk12.default.gray("\n" + "\u2500".repeat(60)));
6977
+ console.log(
6978
+ `
6979
+ ${import_chalk12.default.green(pass + " passed")} | ${import_chalk12.default.yellow(warn2 + " warnings")} | ${import_chalk12.default.red(fail + " failed")}
6980
+ `
6981
+ );
6982
+ if (fail === 0 && warn2 === 0) {
6983
+ console.log(import_chalk12.default.green.bold("\u2728 Everything looks good!\n"));
6984
+ } else if (fail > 0) {
6985
+ console.log(import_chalk12.default.red.bold("\u2717 Fix critical issues before using ServCraft.\n"));
6986
+ } else {
6987
+ console.log(import_chalk12.default.yellow.bold("\u26A0 Some warnings, but should work.\n"));
6988
+ }
6989
+ });
6990
+
6991
+ // src/cli/commands/update.ts
6992
+ var import_commander9 = require("commander");
6993
+ var import_chalk13 = __toESM(require("chalk"), 1);
6994
+ var import_promises9 = __toESM(require("fs/promises"), 1);
6995
+ var import_path10 = __toESM(require("path"), 1);
6996
+ var import_url = require("url");
6997
+ var import_inquirer5 = __toESM(require("inquirer"), 1);
6998
+ var __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
6999
+ var __dirname = import_path10.default.dirname(__filename2);
7000
+ var AVAILABLE_MODULES3 = [
7001
+ "auth",
7002
+ "users",
7003
+ "email",
7004
+ "mfa",
7005
+ "oauth",
7006
+ "rate-limit",
7007
+ "cache",
7008
+ "upload",
7009
+ "search",
7010
+ "notification",
7011
+ "webhook",
7012
+ "websocket",
7013
+ "queue",
7014
+ "payment",
7015
+ "i18n",
7016
+ "feature-flag",
7017
+ "analytics",
7018
+ "media-processing",
7019
+ "api-versioning",
7020
+ "audit",
7021
+ "swagger",
7022
+ "validation"
7023
+ ];
7024
+ async function getInstalledModules2() {
7025
+ try {
7026
+ const modulesDir = getModulesDir();
7027
+ const entries = await import_promises9.default.readdir(modulesDir, { withFileTypes: true });
7028
+ const installedModules = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => AVAILABLE_MODULES3.includes(name));
7029
+ return installedModules;
7030
+ } catch {
7031
+ return [];
7032
+ }
7033
+ }
7034
+ async function copyModuleFiles(moduleName, _projectRoot) {
7035
+ const cliRoot = import_path10.default.resolve(__dirname, "../../../");
7036
+ const sourceModulePath = import_path10.default.join(cliRoot, "src", "modules", moduleName);
7037
+ const targetModulesDir = getModulesDir();
7038
+ const targetModulePath = import_path10.default.join(targetModulesDir, moduleName);
7039
+ try {
7040
+ await import_promises9.default.access(sourceModulePath);
7041
+ } catch {
7042
+ throw new Error(`Module source not found: ${moduleName}`);
7043
+ }
7044
+ await import_promises9.default.cp(sourceModulePath, targetModulePath, { recursive: true });
7045
+ }
7046
+ async function updateModule(moduleName, options) {
7047
+ const projectError = validateProject();
7048
+ if (projectError) {
7049
+ displayError(projectError);
7050
+ return;
7051
+ }
7052
+ const projectRoot = getProjectRoot();
7053
+ const installedModules = await getInstalledModules2();
7054
+ if (!installedModules.includes(moduleName)) {
7055
+ console.log(import_chalk13.default.yellow(`
7056
+ \u26A0 Module "${moduleName}" is not installed
7057
+ `));
7058
+ console.log(
7059
+ import_chalk13.default.gray(`Run ${import_chalk13.default.cyan(`servcraft add ${moduleName}`)} to install it first.
7060
+ `)
7061
+ );
7062
+ return;
7063
+ }
7064
+ if (options.check) {
7065
+ console.log(import_chalk13.default.cyan(`
7066
+ \u{1F4E6} Checking updates for "${moduleName}"...
7067
+ `));
7068
+ console.log(import_chalk13.default.gray("Note: Version tracking will be implemented in a future release."));
7069
+ console.log(import_chalk13.default.gray("Currently, update will always reinstall the latest version.\n"));
7070
+ return;
7071
+ }
7072
+ const { confirmed } = await import_inquirer5.default.prompt([
7073
+ {
7074
+ type: "confirm",
7075
+ name: "confirmed",
7076
+ message: `Update "${moduleName}" module? This will overwrite existing files.`,
7077
+ default: false
7078
+ }
7079
+ ]);
7080
+ if (!confirmed) {
7081
+ console.log(import_chalk13.default.yellow("\n\u26A0 Update cancelled\n"));
7082
+ return;
7083
+ }
7084
+ console.log(import_chalk13.default.cyan(`
7085
+ \u{1F504} Updating "${moduleName}" module...
7086
+ `));
7087
+ try {
7088
+ await copyModuleFiles(moduleName, projectRoot);
7089
+ console.log(import_chalk13.default.green(`\u2714 Module "${moduleName}" updated successfully!
7090
+ `));
7091
+ console.log(
7092
+ import_chalk13.default.gray("Note: Remember to review any breaking changes in the documentation.\n")
7093
+ );
7094
+ } catch (error2) {
7095
+ if (error2 instanceof Error) {
7096
+ console.error(import_chalk13.default.red(`
7097
+ \u2717 Failed to update module: ${error2.message}
7098
+ `));
7099
+ }
7100
+ }
7101
+ }
7102
+ async function updateAllModules(options) {
7103
+ const projectError = validateProject();
7104
+ if (projectError) {
7105
+ displayError(projectError);
7106
+ return;
7107
+ }
7108
+ const installedModules = await getInstalledModules2();
7109
+ if (installedModules.length === 0) {
7110
+ console.log(import_chalk13.default.yellow("\n\u26A0 No modules installed\n"));
7111
+ console.log(import_chalk13.default.gray(`Run ${import_chalk13.default.cyan("servcraft list")} to see available modules.
7112
+ `));
7113
+ return;
7114
+ }
7115
+ if (options.check) {
7116
+ console.log(import_chalk13.default.cyan("\n\u{1F4E6} Checking updates for all modules...\n"));
7117
+ console.log(import_chalk13.default.bold("Installed modules:"));
7118
+ installedModules.forEach((mod) => {
7119
+ console.log(` \u2022 ${import_chalk13.default.cyan(mod)}`);
7120
+ });
7121
+ console.log();
7122
+ console.log(import_chalk13.default.gray("Note: Version tracking will be implemented in a future release."));
7123
+ console.log(import_chalk13.default.gray("Currently, update will always reinstall the latest version.\n"));
7124
+ return;
7125
+ }
7126
+ console.log(import_chalk13.default.cyan(`
7127
+ \u{1F4E6} Found ${installedModules.length} installed module(s):
7128
+ `));
7129
+ installedModules.forEach((mod) => {
7130
+ console.log(` \u2022 ${import_chalk13.default.cyan(mod)}`);
7131
+ });
7132
+ console.log();
7133
+ const { confirmed } = await import_inquirer5.default.prompt([
7134
+ {
7135
+ type: "confirm",
7136
+ name: "confirmed",
7137
+ message: "Update all modules? This will overwrite existing files.",
7138
+ default: false
7139
+ }
7140
+ ]);
7141
+ if (!confirmed) {
7142
+ console.log(import_chalk13.default.yellow("\n\u26A0 Update cancelled\n"));
7143
+ return;
7144
+ }
7145
+ console.log(import_chalk13.default.cyan("\n\u{1F504} Updating all modules...\n"));
7146
+ const projectRoot = getProjectRoot();
7147
+ let successCount = 0;
7148
+ let failCount = 0;
7149
+ for (const moduleName of installedModules) {
7150
+ try {
7151
+ await copyModuleFiles(moduleName, projectRoot);
7152
+ console.log(import_chalk13.default.green(`\u2714 Updated: ${moduleName}`));
7153
+ successCount++;
7154
+ } catch {
7155
+ console.error(import_chalk13.default.red(`\u2717 Failed: ${moduleName}`));
7156
+ failCount++;
7157
+ }
7158
+ }
7159
+ console.log();
7160
+ console.log(
7161
+ import_chalk13.default.bold(
7162
+ `
7163
+ \u2714 Update complete: ${import_chalk13.default.green(successCount)} succeeded, ${import_chalk13.default.red(failCount)} failed
7164
+ `
7165
+ )
7166
+ );
7167
+ if (successCount > 0) {
7168
+ console.log(
7169
+ import_chalk13.default.gray("Note: Remember to review any breaking changes in the documentation.\n")
7170
+ );
7171
+ }
7172
+ }
7173
+ 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) => {
7174
+ if (moduleName) {
7175
+ await updateModule(moduleName, { check: options?.check });
7176
+ } else {
7177
+ await updateAllModules({ check: options?.check });
7178
+ }
7179
+ });
7180
+
7181
+ // src/cli/commands/completion.ts
7182
+ var import_commander10 = require("commander");
7183
+ var bashScript = `
7184
+ # servcraft bash completion script
7185
+ _servcraft_completions() {
7186
+ local cur prev words cword
7187
+ _init_completion || return
7188
+
7189
+ # Main commands
7190
+ local commands="init add generate list remove doctor update completion docs --version --help"
7191
+
7192
+ # Generate subcommands
7193
+ local generate_subcommands="module controller service repository types schema routes m c s r t"
7194
+
7195
+ case "\${words[1]}" in
7196
+ generate|g)
7197
+ if [[ \${cword} -eq 2 ]]; then
7198
+ COMPREPLY=( $(compgen -W "\${generate_subcommands}" -- "\${cur}") )
7199
+ fi
7200
+ ;;
7201
+ add|remove|rm|update)
7202
+ if [[ \${cword} -eq 2 ]]; then
7203
+ # Get available modules
7204
+ local modules="auth cache rate-limit notification payment oauth mfa queue websocket upload"
7205
+ COMPREPLY=( $(compgen -W "\${modules}" -- "\${cur}") )
7206
+ fi
7207
+ ;;
7208
+ completion)
7209
+ if [[ \${cword} -eq 2 ]]; then
7210
+ COMPREPLY=( $(compgen -W "bash zsh" -- "\${cur}") )
7211
+ fi
7212
+ ;;
7213
+ *)
7214
+ if [[ \${cword} -eq 1 ]]; then
7215
+ COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
7216
+ fi
7217
+ ;;
7218
+ esac
7219
+ }
7220
+
7221
+ complete -F _servcraft_completions servcraft
7222
+ `;
7223
+ var zshScript = `
7224
+ #compdef servcraft
7225
+
7226
+ _servcraft() {
7227
+ local context state state_descr line
7228
+ typeset -A opt_args
7229
+
7230
+ _arguments -C \\
7231
+ '1: :_servcraft_commands' \\
7232
+ '*::arg:->args'
7233
+
7234
+ case $state in
7235
+ args)
7236
+ case $line[1] in
7237
+ generate|g)
7238
+ _servcraft_generate
7239
+ ;;
7240
+ add|remove|rm|update)
7241
+ _servcraft_modules
7242
+ ;;
7243
+ completion)
7244
+ _arguments '1: :(bash zsh)'
7245
+ ;;
7246
+ esac
7247
+ ;;
7248
+ esac
7249
+ }
7250
+
7251
+ _servcraft_commands() {
7252
+ local commands
7253
+ commands=(
7254
+ 'init:Initialize a new ServCraft project'
7255
+ 'add:Add a pre-built module to your project'
7256
+ 'generate:Generate code files (controller, service, etc.)'
7257
+ 'list:List available and installed modules'
7258
+ 'remove:Remove an installed module'
7259
+ 'doctor:Diagnose project configuration'
7260
+ 'update:Update installed modules'
7261
+ 'completion:Generate shell completion scripts'
7262
+ 'docs:Open documentation'
7263
+ '--version:Show version'
7264
+ '--help:Show help'
7265
+ )
7266
+ _describe 'command' commands
7267
+ }
7268
+
7269
+ _servcraft_generate() {
7270
+ local subcommands
7271
+ subcommands=(
7272
+ 'module:Generate a complete module (controller + service + routes)'
7273
+ 'controller:Generate a controller'
7274
+ 'service:Generate a service'
7275
+ 'repository:Generate a repository'
7276
+ 'types:Generate TypeScript types'
7277
+ 'schema:Generate validation schema'
7278
+ 'routes:Generate routes file'
7279
+ 'm:Alias for module'
7280
+ 'c:Alias for controller'
7281
+ 's:Alias for service'
7282
+ 'r:Alias for repository'
7283
+ 't:Alias for types'
7284
+ )
7285
+ _describe 'subcommand' subcommands
7286
+ }
7287
+
7288
+ _servcraft_modules() {
7289
+ local modules
7290
+ modules=(
7291
+ 'auth:Authentication & Authorization'
7292
+ 'cache:Redis caching'
7293
+ 'rate-limit:Rate limiting'
7294
+ 'notification:Email/SMS notifications'
7295
+ 'payment:Payment integration'
7296
+ 'oauth:OAuth providers'
7297
+ 'mfa:Multi-factor authentication'
7298
+ 'queue:Background jobs'
7299
+ 'websocket:WebSocket support'
7300
+ 'upload:File upload handling'
7301
+ )
7302
+ _describe 'module' modules
7303
+ }
7304
+
7305
+ _servcraft "$@"
7306
+ `;
7307
+ var completionCommand = new import_commander10.Command("completion").description("Generate shell completion scripts").argument("<shell>", "Shell type (bash or zsh)").action((shell) => {
7308
+ const shellLower = shell.toLowerCase();
7309
+ if (shellLower === "bash") {
7310
+ console.log(bashScript);
7311
+ } else if (shellLower === "zsh") {
7312
+ console.log(zshScript);
7313
+ } else {
7314
+ console.error(`Unsupported shell: ${shell}`);
7315
+ console.error("Supported shells: bash, zsh");
7316
+ process.exit(1);
7317
+ }
6665
7318
  });
6666
7319
 
7320
+ // src/cli/commands/scaffold.ts
7321
+ var import_commander11 = require("commander");
7322
+ var import_path11 = __toESM(require("path"), 1);
7323
+ var import_ora8 = __toESM(require("ora"), 1);
7324
+ var import_chalk14 = __toESM(require("chalk"), 1);
7325
+ var scaffoldCommand = new import_commander11.Command("scaffold").description("Generate complete CRUD with Prisma model").argument("<name>", "Resource name (e.g., product, user)").option(
7326
+ "--fields <fields>",
7327
+ 'Field definitions: "name:string email:string? age:number category:relation"'
7328
+ ).option("--validator <type>", "Validator type: zod, joi, yup", "zod").option("--dry-run", "Preview changes without writing files").action(
7329
+ async (name, options) => {
7330
+ const dryRun = DryRunManager.getInstance();
7331
+ if (options.dryRun) {
7332
+ dryRun.enable();
7333
+ console.log(import_chalk14.default.yellow("\n\u26A0 DRY RUN MODE - No files will be written\n"));
7334
+ }
7335
+ if (!options.fields) {
7336
+ console.log(import_chalk14.default.red("\n\u2717 Error: --fields option is required\n"));
7337
+ console.log(import_chalk14.default.gray("Example:"));
7338
+ console.log(
7339
+ import_chalk14.default.cyan(
7340
+ ' servcraft scaffold product --fields "name:string price:number category:relation"'
7341
+ )
7342
+ );
7343
+ process.exit(1);
7344
+ }
7345
+ const spinner = (0, import_ora8.default)("Scaffolding resource...").start();
7346
+ try {
7347
+ const fields = parseFields(options.fields || "");
7348
+ if (!fields || fields.length === 0) {
7349
+ spinner.fail("No valid fields provided");
7350
+ console.log(import_chalk14.default.gray(`
7351
+ Received: ${options.fields}`));
7352
+ console.log(import_chalk14.default.gray(`Parsed: ${JSON.stringify(fields)}`));
7353
+ process.exit(1);
7354
+ }
7355
+ const kebabName = toKebabCase(name);
7356
+ const pascalName = toPascalCase(name);
7357
+ const camelName = toCamelCase(name);
7358
+ const pluralName = pluralize(camelName);
7359
+ const tableName = pluralize(kebabName);
7360
+ const modulesDir = getModulesDir();
7361
+ const moduleDir = import_path11.default.join(modulesDir, kebabName);
7362
+ const controllerTpl = await getTemplate("controller", controllerTemplate);
7363
+ const serviceTpl = await getTemplate("service", serviceTemplate);
7364
+ const repositoryTpl = await getTemplate("repository", repositoryTemplate);
7365
+ const routesTpl = await getTemplate("routes", routesTemplate);
7366
+ const moduleIndexTpl = await getTemplate("module-index", moduleIndexTemplate);
7367
+ const files = [
7368
+ {
7369
+ name: `${kebabName}.types.ts`,
7370
+ content: dynamicTypesTemplate(kebabName, pascalName, fields)
7371
+ },
7372
+ {
7373
+ name: `${kebabName}.schemas.ts`,
7374
+ content: dynamicSchemasTemplate(
7375
+ kebabName,
7376
+ pascalName,
7377
+ camelName,
7378
+ fields,
7379
+ options.validator || "zod"
7380
+ )
7381
+ },
7382
+ {
7383
+ name: `${kebabName}.service.ts`,
7384
+ content: serviceTpl(kebabName, pascalName, camelName)
7385
+ },
7386
+ {
7387
+ name: `${kebabName}.controller.ts`,
7388
+ content: controllerTpl(kebabName, pascalName, camelName)
7389
+ },
7390
+ {
7391
+ name: "index.ts",
7392
+ content: moduleIndexTpl(kebabName, pascalName, camelName)
7393
+ },
7394
+ {
7395
+ name: `${kebabName}.repository.ts`,
7396
+ content: repositoryTpl(kebabName, pascalName, camelName, pluralName)
7397
+ },
7398
+ {
7399
+ name: `${kebabName}.routes.ts`,
7400
+ content: routesTpl(kebabName, pascalName, camelName, pluralName)
7401
+ }
7402
+ ];
7403
+ for (const file of files) {
7404
+ await writeFile(import_path11.default.join(moduleDir, file.name), file.content);
7405
+ }
7406
+ const testDir = import_path11.default.join(moduleDir, "__tests__");
7407
+ const controllerTestTpl = await getTemplate("controller-test", controllerTestTemplate);
7408
+ const serviceTestTpl = await getTemplate("service-test", serviceTestTemplate);
7409
+ const integrationTestTpl = await getTemplate("integration-test", integrationTestTemplate);
7410
+ await writeFile(
7411
+ import_path11.default.join(testDir, `${kebabName}.controller.test.ts`),
7412
+ controllerTestTpl(kebabName, pascalName, camelName)
7413
+ );
7414
+ await writeFile(
7415
+ import_path11.default.join(testDir, `${kebabName}.service.test.ts`),
7416
+ serviceTestTpl(kebabName, pascalName, camelName)
7417
+ );
7418
+ await writeFile(
7419
+ import_path11.default.join(testDir, `${kebabName}.integration.test.ts`),
7420
+ integrationTestTpl(kebabName, pascalName, camelName)
7421
+ );
7422
+ spinner.succeed(`Resource "${pascalName}" scaffolded successfully!`);
7423
+ console.log("\n" + "\u2500".repeat(70));
7424
+ info("\u{1F4CB} Prisma model to add to schema.prisma:");
7425
+ console.log(import_chalk14.default.gray("\n// Copy this to your schema.prisma file:\n"));
7426
+ console.log(dynamicPrismaTemplate(pascalName, tableName, fields));
7427
+ console.log("\u2500".repeat(70));
7428
+ console.log("\n\u{1F4CB} Fields scaffolded:");
7429
+ fields.forEach((f) => {
7430
+ const opts = [];
7431
+ if (f.isOptional) opts.push("optional");
7432
+ if (f.isArray) opts.push("array");
7433
+ if (f.isUnique) opts.push("unique");
7434
+ if (f.relation) opts.push(`relation: ${f.relation.model}`);
7435
+ const optsStr = opts.length > 0 ? ` (${opts.join(", ")})` : "";
7436
+ success(` ${f.name}: ${f.type}${optsStr}`);
7437
+ });
7438
+ console.log("\n\u{1F4C1} Files created:");
7439
+ files.forEach((f) => success(` src/modules/${kebabName}/${f.name}`));
7440
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.controller.test.ts`);
7441
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.service.test.ts`);
7442
+ success(` src/modules/${kebabName}/__tests__/${kebabName}.integration.test.ts`);
7443
+ console.log("\n\u{1F4CC} Next steps:");
7444
+ info(" 1. Add the Prisma model to your schema.prisma file");
7445
+ info(" 2. Run: npx prisma db push (or prisma migrate dev)");
7446
+ info(" 3. Run: npx prisma generate");
7447
+ info(" 4. Register the module routes in your app");
7448
+ info(" 5. Update the test files with actual test data");
7449
+ console.log(
7450
+ import_chalk14.default.gray("\n\u{1F4A1} Tip: Use --dry-run to preview changes before applying them\n")
7451
+ );
7452
+ if (options.dryRun) {
7453
+ dryRun.printSummary();
7454
+ }
7455
+ } catch (error2) {
7456
+ spinner.fail("Failed to scaffold resource");
7457
+ if (error2 instanceof Error) {
7458
+ console.error(import_chalk14.default.red(`
7459
+ \u2717 ${error2.message}
7460
+ `));
7461
+ console.error(import_chalk14.default.gray(error2.stack));
7462
+ }
7463
+ process.exit(1);
7464
+ }
7465
+ }
7466
+ );
7467
+
7468
+ // src/cli/commands/templates.ts
7469
+ var import_commander12 = require("commander");
7470
+ var import_chalk15 = __toESM(require("chalk"), 1);
7471
+ var import_promises10 = __toESM(require("fs/promises"), 1);
7472
+ var import_path12 = __toESM(require("path"), 1);
7473
+ var TEMPLATE_TYPES = [
7474
+ "controller",
7475
+ "service",
7476
+ "repository",
7477
+ "types",
7478
+ "schemas",
7479
+ "routes",
7480
+ "module-index",
7481
+ "controller-test",
7482
+ "service-test",
7483
+ "integration-test"
7484
+ ];
7485
+ async function initTemplates() {
7486
+ const projectError = validateProject();
7487
+ if (projectError) {
7488
+ displayError(projectError);
7489
+ return;
7490
+ }
7491
+ const projectRoot = getProjectRoot();
7492
+ const templatesDir = import_path12.default.join(projectRoot, ".servcraft", "templates");
7493
+ try {
7494
+ await import_promises10.default.mkdir(templatesDir, { recursive: true });
7495
+ console.log(import_chalk15.default.cyan("\n\u{1F4C1} Creating custom template directory...\n"));
7496
+ const exampleController = `// Custom controller template
7497
+ // Available variables: name, pascalName, camelName, pluralName
7498
+ export function controllerTemplate(name: string, pascalName: string, camelName: string): string {
7499
+ return \`import type { FastifyRequest, FastifyReply } from 'fastify';
7500
+ import type { \${pascalName}Service } from './\${name}.service.js';
7501
+
7502
+ export class \${pascalName}Controller {
7503
+ constructor(private \${camelName}Service: \${pascalName}Service) {}
7504
+
7505
+ // Add your custom controller methods here
7506
+ async getAll(request: FastifyRequest, reply: FastifyReply) {
7507
+ const data = await this.\${camelName}Service.getAll();
7508
+ return reply.send({ data });
7509
+ }
7510
+ }
7511
+ \`;
7512
+ }
7513
+ `;
7514
+ await import_promises10.default.writeFile(
7515
+ import_path12.default.join(templatesDir, "controller.example.ts"),
7516
+ exampleController,
7517
+ "utf-8"
7518
+ );
7519
+ console.log(import_chalk15.default.green("\u2714 Created template directory: .servcraft/templates/"));
7520
+ console.log(import_chalk15.default.green("\u2714 Created example template: controller.example.ts\n"));
7521
+ console.log(import_chalk15.default.bold("\u{1F4CB} Available template types:\n"));
7522
+ TEMPLATE_TYPES.forEach((type) => {
7523
+ console.log(import_chalk15.default.gray(` \u2022 ${type}.ts`));
7524
+ });
7525
+ console.log(import_chalk15.default.yellow("\n\u{1F4A1} To customize a template:"));
7526
+ console.log(import_chalk15.default.gray(" 1. Copy the example template"));
7527
+ console.log(import_chalk15.default.gray(" 2. Rename it (remove .example)"));
7528
+ console.log(import_chalk15.default.gray(" 3. Modify the template code"));
7529
+ console.log(import_chalk15.default.gray(" 4. Use --template flag when generating\n"));
7530
+ } catch (error2) {
7531
+ if (error2 instanceof Error) {
7532
+ console.error(import_chalk15.default.red(`
7533
+ \u2717 Failed to initialize templates: ${error2.message}
7534
+ `));
7535
+ }
7536
+ }
7537
+ }
7538
+ async function listTemplates() {
7539
+ const projectError = validateProject();
7540
+ if (projectError) {
7541
+ displayError(projectError);
7542
+ return;
7543
+ }
7544
+ const projectRoot = getProjectRoot();
7545
+ const projectTemplatesDir = import_path12.default.join(projectRoot, ".servcraft", "templates");
7546
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
7547
+ const userTemplatesDir = import_path12.default.join(homeDir, ".servcraft", "templates");
7548
+ console.log(import_chalk15.default.bold.cyan("\n\u{1F4CB} Available Templates\n"));
7549
+ console.log(import_chalk15.default.bold("Project templates (.servcraft/templates/):"));
7550
+ try {
7551
+ const files = await import_promises10.default.readdir(projectTemplatesDir);
7552
+ const templates = files.filter((f) => f.endsWith(".ts") && !f.endsWith(".example.ts"));
7553
+ if (templates.length > 0) {
7554
+ templates.forEach((t) => {
7555
+ console.log(import_chalk15.default.green(` \u2713 ${t}`));
7556
+ });
7557
+ } else {
7558
+ console.log(import_chalk15.default.gray(" (none)"));
7559
+ }
7560
+ } catch {
7561
+ console.log(import_chalk15.default.gray(" (directory not found)"));
7562
+ }
7563
+ console.log(import_chalk15.default.bold("\nUser templates (~/.servcraft/templates/):"));
7564
+ try {
7565
+ const files = await import_promises10.default.readdir(userTemplatesDir);
7566
+ const templates = files.filter((f) => f.endsWith(".ts") && !f.endsWith(".example.ts"));
7567
+ if (templates.length > 0) {
7568
+ templates.forEach((t) => {
7569
+ console.log(import_chalk15.default.green(` \u2713 ${t}`));
7570
+ });
7571
+ } else {
7572
+ console.log(import_chalk15.default.gray(" (none)"));
7573
+ }
7574
+ } catch {
7575
+ console.log(import_chalk15.default.gray(" (directory not found)"));
7576
+ }
7577
+ console.log(import_chalk15.default.bold("\nBuilt-in templates:"));
7578
+ TEMPLATE_TYPES.forEach((t) => {
7579
+ console.log(import_chalk15.default.cyan(` \u2022 ${t}.ts`));
7580
+ });
7581
+ console.log(import_chalk15.default.gray('\n\u{1F4A1} Run "servcraft templates init" to create custom templates\n'));
7582
+ }
7583
+ var templatesCommand = new import_commander12.Command("templates").description("Manage code generation templates").addCommand(
7584
+ new import_commander12.Command("init").description("Initialize custom templates directory").action(initTemplates)
7585
+ ).addCommand(new import_commander12.Command("list").description("List available templates").action(listTemplates));
7586
+
6667
7587
  // src/cli/index.ts
6668
- var program = new import_commander9.Command();
7588
+ var program = new import_commander13.Command();
6669
7589
  program.name("servcraft").description("Servcraft - A modular Node.js backend framework CLI").version("0.1.0");
6670
7590
  program.addCommand(initCommand);
6671
7591
  program.addCommand(generateCommand);
@@ -6675,5 +7595,9 @@ program.addCommand(docsCommand);
6675
7595
  program.addCommand(listCommand);
6676
7596
  program.addCommand(removeCommand);
6677
7597
  program.addCommand(doctorCommand);
7598
+ program.addCommand(updateCommand);
7599
+ program.addCommand(completionCommand);
7600
+ program.addCommand(scaffoldCommand);
7601
+ program.addCommand(templatesCommand);
6678
7602
  program.parse();
6679
7603
  //# sourceMappingURL=index.cjs.map