servcraft 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/ROADMAP.md +15 -8
- package/dist/cli/index.cjs +900 -174
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +919 -172
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/add-module.ts +36 -7
- package/src/cli/commands/doctor.ts +8 -0
- package/src/cli/commands/generate.ts +43 -1
- package/src/cli/commands/init.ts +29 -10
- package/src/cli/commands/list.ts +274 -0
- package/src/cli/commands/remove.ts +102 -0
- package/src/cli/index.ts +12 -0
- package/src/cli/utils/dry-run.ts +155 -0
- package/src/cli/utils/error-handler.ts +184 -0
- package/src/cli/utils/helpers.ts +13 -0
package/dist/cli/index.cjs
CHANGED
|
@@ -6,6 +6,13 @@ 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
|
+
};
|
|
9
16
|
var __copyProps = (to, from, except, desc) => {
|
|
10
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -22,27 +29,294 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
29
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
30
|
mod
|
|
24
31
|
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
33
|
|
|
26
34
|
// node_modules/tsup/assets/cjs_shims.js
|
|
27
|
-
var getImportMetaUrl
|
|
28
|
-
var
|
|
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
|
+
});
|
|
29
216
|
|
|
30
217
|
// src/cli/index.ts
|
|
31
|
-
|
|
218
|
+
init_cjs_shims();
|
|
219
|
+
var import_commander9 = require("commander");
|
|
32
220
|
|
|
33
221
|
// src/cli/commands/init.ts
|
|
222
|
+
init_cjs_shims();
|
|
34
223
|
var import_commander = require("commander");
|
|
35
|
-
var
|
|
224
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
36
225
|
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
37
226
|
var import_ora = __toESM(require("ora"), 1);
|
|
38
227
|
var import_inquirer = __toESM(require("inquirer"), 1);
|
|
39
|
-
var
|
|
228
|
+
var import_chalk3 = __toESM(require("chalk"), 1);
|
|
40
229
|
var import_child_process = require("child_process");
|
|
41
230
|
|
|
42
231
|
// src/cli/utils/helpers.ts
|
|
232
|
+
init_cjs_shims();
|
|
43
233
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
44
|
-
var
|
|
234
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
235
|
+
var import_chalk2 = __toESM(require("chalk"), 1);
|
|
236
|
+
|
|
237
|
+
// src/cli/utils/dry-run.ts
|
|
238
|
+
init_cjs_shims();
|
|
45
239
|
var import_chalk = __toESM(require("chalk"), 1);
|
|
240
|
+
var import_path = __toESM(require("path"), 1);
|
|
241
|
+
var DryRunManager = class _DryRunManager {
|
|
242
|
+
static instance;
|
|
243
|
+
enabled = false;
|
|
244
|
+
operations = [];
|
|
245
|
+
constructor() {
|
|
246
|
+
}
|
|
247
|
+
static getInstance() {
|
|
248
|
+
if (!_DryRunManager.instance) {
|
|
249
|
+
_DryRunManager.instance = new _DryRunManager();
|
|
250
|
+
}
|
|
251
|
+
return _DryRunManager.instance;
|
|
252
|
+
}
|
|
253
|
+
enable() {
|
|
254
|
+
this.enabled = true;
|
|
255
|
+
this.operations = [];
|
|
256
|
+
}
|
|
257
|
+
disable() {
|
|
258
|
+
this.enabled = false;
|
|
259
|
+
this.operations = [];
|
|
260
|
+
}
|
|
261
|
+
isEnabled() {
|
|
262
|
+
return this.enabled;
|
|
263
|
+
}
|
|
264
|
+
addOperation(operation) {
|
|
265
|
+
if (this.enabled) {
|
|
266
|
+
this.operations.push(operation);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
getOperations() {
|
|
270
|
+
return [...this.operations];
|
|
271
|
+
}
|
|
272
|
+
printSummary() {
|
|
273
|
+
if (!this.enabled || this.operations.length === 0) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
console.log(import_chalk.default.bold.yellow("\n\u{1F4CB} Dry Run - Preview of changes:\n"));
|
|
277
|
+
console.log(import_chalk.default.gray("No files will be written. Remove --dry-run to apply changes.\n"));
|
|
278
|
+
const createOps = this.operations.filter((op) => op.type === "create");
|
|
279
|
+
const modifyOps = this.operations.filter((op) => op.type === "modify");
|
|
280
|
+
const deleteOps = this.operations.filter((op) => op.type === "delete");
|
|
281
|
+
if (createOps.length > 0) {
|
|
282
|
+
console.log(import_chalk.default.green.bold(`
|
|
283
|
+
\u2713 Files to be created (${createOps.length}):`));
|
|
284
|
+
createOps.forEach((op) => {
|
|
285
|
+
const size = op.content ? `${op.content.length} bytes` : "unknown size";
|
|
286
|
+
console.log(` ${import_chalk.default.green("+")} ${import_chalk.default.cyan(op.path)} ${import_chalk.default.gray(`(${size})`)}`);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (modifyOps.length > 0) {
|
|
290
|
+
console.log(import_chalk.default.yellow.bold(`
|
|
291
|
+
~ Files to be modified (${modifyOps.length}):`));
|
|
292
|
+
modifyOps.forEach((op) => {
|
|
293
|
+
console.log(` ${import_chalk.default.yellow("~")} ${import_chalk.default.cyan(op.path)}`);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (deleteOps.length > 0) {
|
|
297
|
+
console.log(import_chalk.default.red.bold(`
|
|
298
|
+
- Files to be deleted (${deleteOps.length}):`));
|
|
299
|
+
deleteOps.forEach((op) => {
|
|
300
|
+
console.log(` ${import_chalk.default.red("-")} ${import_chalk.default.cyan(op.path)}`);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
console.log(import_chalk.default.gray("\n" + "\u2500".repeat(60)));
|
|
304
|
+
console.log(
|
|
305
|
+
import_chalk.default.bold(` Total operations: ${this.operations.length}`) + import_chalk.default.gray(
|
|
306
|
+
` (${createOps.length} create, ${modifyOps.length} modify, ${deleteOps.length} delete)`
|
|
307
|
+
)
|
|
308
|
+
);
|
|
309
|
+
console.log(import_chalk.default.gray("\u2500".repeat(60)));
|
|
310
|
+
console.log(import_chalk.default.yellow("\n\u26A0 This was a dry run. No files were created or modified."));
|
|
311
|
+
console.log(import_chalk.default.gray(" Remove --dry-run to apply these changes.\n"));
|
|
312
|
+
}
|
|
313
|
+
// Helper to format file path relative to cwd
|
|
314
|
+
relativePath(filePath) {
|
|
315
|
+
return import_path.default.relative(process.cwd(), filePath);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// src/cli/utils/helpers.ts
|
|
46
320
|
function toPascalCase(str) {
|
|
47
321
|
return str.split(/[-_\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
48
322
|
}
|
|
@@ -74,39 +348,54 @@ async function ensureDir(dirPath) {
|
|
|
74
348
|
await import_promises.default.mkdir(dirPath, { recursive: true });
|
|
75
349
|
}
|
|
76
350
|
async function writeFile(filePath, content) {
|
|
77
|
-
|
|
351
|
+
const dryRun = DryRunManager.getInstance();
|
|
352
|
+
if (dryRun.isEnabled()) {
|
|
353
|
+
dryRun.addOperation({
|
|
354
|
+
type: "create",
|
|
355
|
+
path: dryRun.relativePath(filePath),
|
|
356
|
+
content,
|
|
357
|
+
size: content.length
|
|
358
|
+
});
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
await ensureDir(import_path2.default.dirname(filePath));
|
|
78
362
|
await import_promises.default.writeFile(filePath, content, "utf-8");
|
|
79
363
|
}
|
|
80
364
|
function success(message) {
|
|
81
|
-
console.log(
|
|
365
|
+
console.log(import_chalk2.default.green("\u2713"), message);
|
|
82
366
|
}
|
|
83
367
|
function error(message) {
|
|
84
|
-
console.error(
|
|
368
|
+
console.error(import_chalk2.default.red("\u2717"), message);
|
|
85
369
|
}
|
|
86
370
|
function warn(message) {
|
|
87
|
-
console.log(
|
|
371
|
+
console.log(import_chalk2.default.yellow("\u26A0"), message);
|
|
88
372
|
}
|
|
89
373
|
function info(message) {
|
|
90
|
-
console.log(
|
|
374
|
+
console.log(import_chalk2.default.blue("\u2139"), message);
|
|
91
375
|
}
|
|
92
376
|
function getProjectRoot() {
|
|
93
377
|
return process.cwd();
|
|
94
378
|
}
|
|
95
379
|
function getSourceDir() {
|
|
96
|
-
return
|
|
380
|
+
return import_path2.default.join(getProjectRoot(), "src");
|
|
97
381
|
}
|
|
98
382
|
function getModulesDir() {
|
|
99
|
-
return
|
|
383
|
+
return import_path2.default.join(getSourceDir(), "modules");
|
|
100
384
|
}
|
|
101
385
|
|
|
102
386
|
// src/cli/commands/init.ts
|
|
103
|
-
var initCommand = new import_commander.Command("init").alias("new").description("Initialize a new Servcraft project").argument("[name]", "Project name").option("-y, --yes", "Skip prompts and use defaults").option("--ts, --typescript", "Use TypeScript (default)").option("--js, --javascript", "Use JavaScript").option("--esm", "Use ES Modules (import/export) - default").option("--cjs, --commonjs", "Use CommonJS (require/module.exports)").option("--db <database>", "Database type (postgresql, mysql, sqlite, mongodb, none)").action(
|
|
387
|
+
var initCommand = new import_commander.Command("init").alias("new").description("Initialize a new Servcraft project").argument("[name]", "Project name").option("-y, --yes", "Skip prompts and use defaults").option("--ts, --typescript", "Use TypeScript (default)").option("--js, --javascript", "Use JavaScript").option("--esm", "Use ES Modules (import/export) - default").option("--cjs, --commonjs", "Use CommonJS (require/module.exports)").option("--db <database>", "Database type (postgresql, mysql, sqlite, mongodb, none)").option("--dry-run", "Preview changes without writing files").action(
|
|
104
388
|
async (name, cmdOptions) => {
|
|
389
|
+
const dryRun = DryRunManager.getInstance();
|
|
390
|
+
if (cmdOptions?.dryRun) {
|
|
391
|
+
dryRun.enable();
|
|
392
|
+
console.log(import_chalk3.default.yellow("\n\u26A0 DRY RUN MODE - No files will be written\n"));
|
|
393
|
+
}
|
|
105
394
|
console.log(
|
|
106
|
-
|
|
395
|
+
import_chalk3.default.blue(`
|
|
107
396
|
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
108
397
|
\u2551 \u2551
|
|
109
|
-
\u2551 ${
|
|
398
|
+
\u2551 ${import_chalk3.default.bold("\u{1F680} Servcraft Project Generator")} \u2551
|
|
110
399
|
\u2551 \u2551
|
|
111
400
|
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
112
401
|
`)
|
|
@@ -201,7 +490,7 @@ var initCommand = new import_commander.Command("init").alias("new").description(
|
|
|
201
490
|
orm: db === "mongodb" ? "mongoose" : db === "none" ? "none" : "prisma"
|
|
202
491
|
};
|
|
203
492
|
}
|
|
204
|
-
const projectDir =
|
|
493
|
+
const projectDir = import_path3.default.resolve(process.cwd(), options.name);
|
|
205
494
|
const spinner = (0, import_ora.default)("Creating project...").start();
|
|
206
495
|
try {
|
|
207
496
|
try {
|
|
@@ -215,21 +504,21 @@ var initCommand = new import_commander.Command("init").alias("new").description(
|
|
|
215
504
|
spinner.text = "Generating project files...";
|
|
216
505
|
const packageJson = generatePackageJson(options);
|
|
217
506
|
await writeFile(
|
|
218
|
-
|
|
507
|
+
import_path3.default.join(projectDir, "package.json"),
|
|
219
508
|
JSON.stringify(packageJson, null, 2)
|
|
220
509
|
);
|
|
221
510
|
if (options.language === "typescript") {
|
|
222
|
-
await writeFile(
|
|
223
|
-
await writeFile(
|
|
511
|
+
await writeFile(import_path3.default.join(projectDir, "tsconfig.json"), generateTsConfig(options));
|
|
512
|
+
await writeFile(import_path3.default.join(projectDir, "tsup.config.ts"), generateTsupConfig(options));
|
|
224
513
|
} else {
|
|
225
|
-
await writeFile(
|
|
514
|
+
await writeFile(import_path3.default.join(projectDir, "jsconfig.json"), generateJsConfig(options));
|
|
226
515
|
}
|
|
227
|
-
await writeFile(
|
|
228
|
-
await writeFile(
|
|
229
|
-
await writeFile(
|
|
230
|
-
await writeFile(
|
|
516
|
+
await writeFile(import_path3.default.join(projectDir, ".env.example"), generateEnvExample(options));
|
|
517
|
+
await writeFile(import_path3.default.join(projectDir, ".env"), generateEnvExample(options));
|
|
518
|
+
await writeFile(import_path3.default.join(projectDir, ".gitignore"), generateGitignore());
|
|
519
|
+
await writeFile(import_path3.default.join(projectDir, "Dockerfile"), generateDockerfile(options));
|
|
231
520
|
await writeFile(
|
|
232
|
-
|
|
521
|
+
import_path3.default.join(projectDir, "docker-compose.yml"),
|
|
233
522
|
generateDockerCompose(options)
|
|
234
523
|
);
|
|
235
524
|
const ext = options.language === "typescript" ? "ts" : options.moduleSystem === "esm" ? "js" : "cjs";
|
|
@@ -250,59 +539,63 @@ var initCommand = new import_commander.Command("init").alias("new").description(
|
|
|
250
539
|
dirs.push("src/database/models");
|
|
251
540
|
}
|
|
252
541
|
for (const dir of dirs) {
|
|
253
|
-
await ensureDir(
|
|
542
|
+
await ensureDir(import_path3.default.join(projectDir, dir));
|
|
254
543
|
}
|
|
255
|
-
await writeFile(
|
|
544
|
+
await writeFile(import_path3.default.join(projectDir, `src/index.${ext}`), generateEntryFile(options));
|
|
256
545
|
await writeFile(
|
|
257
|
-
|
|
546
|
+
import_path3.default.join(projectDir, `src/core/server.${ext}`),
|
|
258
547
|
generateServerFile(options)
|
|
259
548
|
);
|
|
260
549
|
await writeFile(
|
|
261
|
-
|
|
550
|
+
import_path3.default.join(projectDir, `src/core/logger.${ext}`),
|
|
262
551
|
generateLoggerFile(options)
|
|
263
552
|
);
|
|
264
553
|
await writeFile(
|
|
265
|
-
|
|
554
|
+
import_path3.default.join(projectDir, `src/config/index.${ext}`),
|
|
266
555
|
generateConfigFile(options)
|
|
267
556
|
);
|
|
268
557
|
await writeFile(
|
|
269
|
-
|
|
558
|
+
import_path3.default.join(projectDir, `src/middleware/index.${ext}`),
|
|
270
559
|
generateMiddlewareFile(options)
|
|
271
560
|
);
|
|
272
561
|
await writeFile(
|
|
273
|
-
|
|
562
|
+
import_path3.default.join(projectDir, `src/utils/index.${ext}`),
|
|
274
563
|
generateUtilsFile(options)
|
|
275
564
|
);
|
|
276
565
|
await writeFile(
|
|
277
|
-
|
|
566
|
+
import_path3.default.join(projectDir, `src/types/index.${ext}`),
|
|
278
567
|
generateTypesFile(options)
|
|
279
568
|
);
|
|
280
569
|
if (options.orm === "prisma") {
|
|
281
570
|
await writeFile(
|
|
282
|
-
|
|
571
|
+
import_path3.default.join(projectDir, "prisma/schema.prisma"),
|
|
283
572
|
generatePrismaSchema(options)
|
|
284
573
|
);
|
|
285
574
|
} else if (options.orm === "mongoose") {
|
|
286
575
|
await writeFile(
|
|
287
|
-
|
|
576
|
+
import_path3.default.join(projectDir, `src/database/connection.${ext}`),
|
|
288
577
|
generateMongooseConnection(options)
|
|
289
578
|
);
|
|
290
579
|
await writeFile(
|
|
291
|
-
|
|
580
|
+
import_path3.default.join(projectDir, `src/database/models/user.model.${ext}`),
|
|
292
581
|
generateMongooseUserModel(options)
|
|
293
582
|
);
|
|
294
583
|
}
|
|
295
584
|
spinner.succeed("Project files generated!");
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
585
|
+
if (!cmdOptions?.dryRun) {
|
|
586
|
+
const installSpinner = (0, import_ora.default)("Installing dependencies...").start();
|
|
587
|
+
try {
|
|
588
|
+
(0, import_child_process.execSync)("npm install", { cwd: projectDir, stdio: "pipe" });
|
|
589
|
+
installSpinner.succeed("Dependencies installed!");
|
|
590
|
+
} catch {
|
|
591
|
+
installSpinner.warn("Failed to install dependencies automatically");
|
|
592
|
+
warn(' Run "npm install" manually in the project directory');
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (!cmdOptions?.dryRun) {
|
|
596
|
+
console.log("\n" + import_chalk3.default.green("\u2728 Project created successfully!"));
|
|
303
597
|
}
|
|
304
|
-
console.log("\n" +
|
|
305
|
-
console.log("\n" + import_chalk2.default.bold("\u{1F4C1} Project structure:"));
|
|
598
|
+
console.log("\n" + import_chalk3.default.bold("\u{1F4C1} Project structure:"));
|
|
306
599
|
console.log(`
|
|
307
600
|
${options.name}/
|
|
308
601
|
\u251C\u2500\u2500 src/
|
|
@@ -317,19 +610,22 @@ var initCommand = new import_commander.Command("init").alias("new").description(
|
|
|
317
610
|
\u251C\u2500\u2500 docker-compose.yml
|
|
318
611
|
\u2514\u2500\u2500 package.json
|
|
319
612
|
`);
|
|
320
|
-
console.log(
|
|
613
|
+
console.log(import_chalk3.default.bold("\u{1F680} Get started:"));
|
|
321
614
|
console.log(`
|
|
322
|
-
${
|
|
323
|
-
${options.database !== "none" ?
|
|
324
|
-
${
|
|
615
|
+
${import_chalk3.default.cyan(`cd ${options.name}`)}
|
|
616
|
+
${options.database !== "none" ? import_chalk3.default.cyan("npm run db:push # Setup database") : ""}
|
|
617
|
+
${import_chalk3.default.cyan("npm run dev # Start development server")}
|
|
325
618
|
`);
|
|
326
|
-
console.log(
|
|
619
|
+
console.log(import_chalk3.default.bold("\u{1F4DA} Available commands:"));
|
|
327
620
|
console.log(`
|
|
328
|
-
${
|
|
329
|
-
${
|
|
330
|
-
${
|
|
331
|
-
${
|
|
621
|
+
${import_chalk3.default.yellow("servcraft generate module <name>")} Generate a new module
|
|
622
|
+
${import_chalk3.default.yellow("servcraft generate controller <name>")} Generate a controller
|
|
623
|
+
${import_chalk3.default.yellow("servcraft generate service <name>")} Generate a service
|
|
624
|
+
${import_chalk3.default.yellow("servcraft add auth")} Add authentication module
|
|
332
625
|
`);
|
|
626
|
+
if (cmdOptions?.dryRun) {
|
|
627
|
+
dryRun.printSummary();
|
|
628
|
+
}
|
|
333
629
|
} catch (err) {
|
|
334
630
|
spinner.fail("Failed to create project");
|
|
335
631
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -1120,12 +1416,15 @@ declare module 'fastify' {
|
|
|
1120
1416
|
}
|
|
1121
1417
|
|
|
1122
1418
|
// src/cli/commands/generate.ts
|
|
1419
|
+
init_cjs_shims();
|
|
1123
1420
|
var import_commander2 = require("commander");
|
|
1124
|
-
var
|
|
1421
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
1125
1422
|
var import_ora2 = __toESM(require("ora"), 1);
|
|
1126
1423
|
var import_inquirer2 = __toESM(require("inquirer"), 1);
|
|
1424
|
+
var import_chalk5 = __toESM(require("chalk"), 1);
|
|
1127
1425
|
|
|
1128
1426
|
// src/cli/utils/field-parser.ts
|
|
1427
|
+
init_cjs_shims();
|
|
1129
1428
|
var tsTypeMap = {
|
|
1130
1429
|
string: "string",
|
|
1131
1430
|
number: "number",
|
|
@@ -1267,7 +1566,11 @@ function parseFields(fieldsStr) {
|
|
|
1267
1566
|
return fieldsStr.split(/\s+/).filter(Boolean).map(parseField);
|
|
1268
1567
|
}
|
|
1269
1568
|
|
|
1569
|
+
// src/cli/commands/generate.ts
|
|
1570
|
+
init_error_handler();
|
|
1571
|
+
|
|
1270
1572
|
// src/cli/templates/controller.ts
|
|
1573
|
+
init_cjs_shims();
|
|
1271
1574
|
function controllerTemplate(name, pascalName, camelName) {
|
|
1272
1575
|
return `import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
1273
1576
|
import type { ${pascalName}Service } from './${name}.service.js';
|
|
@@ -1337,6 +1640,7 @@ export function create${pascalName}Controller(${camelName}Service: ${pascalName}
|
|
|
1337
1640
|
}
|
|
1338
1641
|
|
|
1339
1642
|
// src/cli/templates/service.ts
|
|
1643
|
+
init_cjs_shims();
|
|
1340
1644
|
function serviceTemplate(name, pascalName, camelName) {
|
|
1341
1645
|
return `import type { PaginatedResult, PaginationParams } from '../../types/index.js';
|
|
1342
1646
|
import { NotFoundError, ConflictError } from '../../utils/errors.js';
|
|
@@ -1397,6 +1701,7 @@ export function create${pascalName}Service(repository?: ${pascalName}Repository)
|
|
|
1397
1701
|
}
|
|
1398
1702
|
|
|
1399
1703
|
// src/cli/templates/repository.ts
|
|
1704
|
+
init_cjs_shims();
|
|
1400
1705
|
function repositoryTemplate(name, pascalName, camelName, pluralName) {
|
|
1401
1706
|
return `import { randomUUID } from 'crypto';
|
|
1402
1707
|
import type { PaginatedResult, PaginationParams } from '../../types/index.js';
|
|
@@ -1503,6 +1808,7 @@ export function create${pascalName}Repository(): ${pascalName}Repository {
|
|
|
1503
1808
|
}
|
|
1504
1809
|
|
|
1505
1810
|
// src/cli/templates/types.ts
|
|
1811
|
+
init_cjs_shims();
|
|
1506
1812
|
function typesTemplate(name, pascalName) {
|
|
1507
1813
|
return `import type { BaseEntity } from '../../types/index.js';
|
|
1508
1814
|
|
|
@@ -1532,6 +1838,7 @@ export interface ${pascalName}Filters {
|
|
|
1532
1838
|
}
|
|
1533
1839
|
|
|
1534
1840
|
// src/cli/templates/schemas.ts
|
|
1841
|
+
init_cjs_shims();
|
|
1535
1842
|
function schemasTemplate(name, pascalName, camelName) {
|
|
1536
1843
|
return `import { z } from 'zod';
|
|
1537
1844
|
|
|
@@ -1560,6 +1867,7 @@ export type ${pascalName}QueryInput = z.infer<typeof ${camelName}QuerySchema>;
|
|
|
1560
1867
|
}
|
|
1561
1868
|
|
|
1562
1869
|
// src/cli/templates/routes.ts
|
|
1870
|
+
init_cjs_shims();
|
|
1563
1871
|
function routesTemplate(name, pascalName, camelName, pluralName) {
|
|
1564
1872
|
return `import type { FastifyInstance } from 'fastify';
|
|
1565
1873
|
import type { ${pascalName}Controller } from './${name}.controller.js';
|
|
@@ -1612,6 +1920,7 @@ export function register${pascalName}Routes(
|
|
|
1612
1920
|
}
|
|
1613
1921
|
|
|
1614
1922
|
// src/cli/templates/module-index.ts
|
|
1923
|
+
init_cjs_shims();
|
|
1615
1924
|
function moduleIndexTemplate(name, pascalName, camelName) {
|
|
1616
1925
|
return `import type { FastifyInstance } from 'fastify';
|
|
1617
1926
|
import { logger } from '../../core/logger.js';
|
|
@@ -1647,6 +1956,7 @@ export * from './${name}.schemas.js';
|
|
|
1647
1956
|
}
|
|
1648
1957
|
|
|
1649
1958
|
// src/cli/templates/prisma-model.ts
|
|
1959
|
+
init_cjs_shims();
|
|
1650
1960
|
function prismaModelTemplate(name, pascalName, tableName) {
|
|
1651
1961
|
return `
|
|
1652
1962
|
// Add this model to your prisma/schema.prisma file
|
|
@@ -1666,6 +1976,7 @@ model ${pascalName} {
|
|
|
1666
1976
|
}
|
|
1667
1977
|
|
|
1668
1978
|
// src/cli/templates/dynamic-types.ts
|
|
1979
|
+
init_cjs_shims();
|
|
1669
1980
|
function dynamicTypesTemplate(name, pascalName, fields) {
|
|
1670
1981
|
const fieldLines = fields.map((field) => {
|
|
1671
1982
|
const tsType = tsTypeMap[field.type];
|
|
@@ -1710,6 +2021,7 @@ ${fields.filter((f) => ["string", "enum", "boolean"].includes(f.type)).map((f) =
|
|
|
1710
2021
|
}
|
|
1711
2022
|
|
|
1712
2023
|
// src/cli/templates/dynamic-schemas.ts
|
|
2024
|
+
init_cjs_shims();
|
|
1713
2025
|
function dynamicSchemasTemplate(name, pascalName, camelName, fields, validator = "zod") {
|
|
1714
2026
|
switch (validator) {
|
|
1715
2027
|
case "joi":
|
|
@@ -1888,6 +2200,7 @@ function getJsType(field) {
|
|
|
1888
2200
|
}
|
|
1889
2201
|
|
|
1890
2202
|
// src/cli/templates/dynamic-prisma.ts
|
|
2203
|
+
init_cjs_shims();
|
|
1891
2204
|
function dynamicPrismaTemplate(modelName, tableName, fields) {
|
|
1892
2205
|
const fieldLines = [];
|
|
1893
2206
|
for (const field of fields) {
|
|
@@ -1956,10 +2269,23 @@ ${indexLines.join("\n")}
|
|
|
1956
2269
|
}
|
|
1957
2270
|
|
|
1958
2271
|
// src/cli/commands/generate.ts
|
|
2272
|
+
function enableDryRunIfNeeded(options) {
|
|
2273
|
+
const dryRun = DryRunManager.getInstance();
|
|
2274
|
+
if (options.dryRun) {
|
|
2275
|
+
dryRun.enable();
|
|
2276
|
+
console.log(import_chalk5.default.yellow("\n\u26A0 DRY RUN MODE - No files will be written\n"));
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
function showDryRunSummary(options) {
|
|
2280
|
+
if (options.dryRun) {
|
|
2281
|
+
DryRunManager.getInstance().printSummary();
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
1959
2284
|
var generateCommand = new import_commander2.Command("generate").alias("g").description("Generate resources (module, controller, service, etc.)");
|
|
1960
2285
|
generateCommand.command("module <name> [fields...]").alias("m").description(
|
|
1961
2286
|
"Generate a complete module with controller, service, repository, types, schemas, and routes"
|
|
1962
|
-
).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").action(async (name, fieldsArgs, options) => {
|
|
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) => {
|
|
2288
|
+
enableDryRunIfNeeded(options);
|
|
1963
2289
|
let fields = [];
|
|
1964
2290
|
if (options.interactive) {
|
|
1965
2291
|
fields = await promptForFields();
|
|
@@ -1974,7 +2300,7 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
|
|
|
1974
2300
|
const pluralName = pluralize(kebabName);
|
|
1975
2301
|
const tableName = pluralize(kebabName.replace(/-/g, "_"));
|
|
1976
2302
|
const validatorType = options.validator || "zod";
|
|
1977
|
-
const moduleDir =
|
|
2303
|
+
const moduleDir = import_path4.default.join(getModulesDir(), kebabName);
|
|
1978
2304
|
if (await fileExists(moduleDir)) {
|
|
1979
2305
|
spinner.stop();
|
|
1980
2306
|
error(`Module "${kebabName}" already exists`);
|
|
@@ -2013,7 +2339,7 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
|
|
|
2013
2339
|
});
|
|
2014
2340
|
}
|
|
2015
2341
|
for (const file of files) {
|
|
2016
|
-
await writeFile(
|
|
2342
|
+
await writeFile(import_path4.default.join(moduleDir, file.name), file.content);
|
|
2017
2343
|
}
|
|
2018
2344
|
spinner.succeed(`Module "${pascalName}" generated successfully!`);
|
|
2019
2345
|
if (options.prisma || hasFields) {
|
|
@@ -2051,42 +2377,46 @@ generateCommand.command("module <name> [fields...]").alias("m").description(
|
|
|
2051
2377
|
info(` ${hasFields ? "3" : "4"}. Add the Prisma model to schema.prisma`);
|
|
2052
2378
|
info(` ${hasFields ? "4" : "5"}. Run: npm run db:migrate`);
|
|
2053
2379
|
}
|
|
2380
|
+
showDryRunSummary(options);
|
|
2054
2381
|
} catch (err) {
|
|
2055
2382
|
spinner.fail("Failed to generate module");
|
|
2056
2383
|
error(err instanceof Error ? err.message : String(err));
|
|
2057
2384
|
}
|
|
2058
2385
|
});
|
|
2059
|
-
generateCommand.command("controller <name>").alias("c").description("Generate a controller").option("-m, --module <module>", "Target module name").action(async (name, options) => {
|
|
2386
|
+
generateCommand.command("controller <name>").alias("c").description("Generate a controller").option("-m, --module <module>", "Target module name").option("--dry-run", "Preview changes without writing files").action(async (name, options) => {
|
|
2387
|
+
enableDryRunIfNeeded(options);
|
|
2060
2388
|
const spinner = (0, import_ora2.default)("Generating controller...").start();
|
|
2061
2389
|
try {
|
|
2062
2390
|
const kebabName = toKebabCase(name);
|
|
2063
2391
|
const pascalName = toPascalCase(name);
|
|
2064
2392
|
const camelName = toCamelCase(name);
|
|
2065
2393
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2066
|
-
const moduleDir =
|
|
2067
|
-
const filePath =
|
|
2394
|
+
const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
|
|
2395
|
+
const filePath = import_path4.default.join(moduleDir, `${kebabName}.controller.ts`);
|
|
2068
2396
|
if (await fileExists(filePath)) {
|
|
2069
2397
|
spinner.stop();
|
|
2070
|
-
|
|
2398
|
+
displayError(ErrorTypes.FILE_ALREADY_EXISTS(`${kebabName}.controller.ts`));
|
|
2071
2399
|
return;
|
|
2072
2400
|
}
|
|
2073
2401
|
await writeFile(filePath, controllerTemplate(kebabName, pascalName, camelName));
|
|
2074
2402
|
spinner.succeed(`Controller "${pascalName}Controller" generated!`);
|
|
2075
2403
|
success(` src/modules/${moduleName}/${kebabName}.controller.ts`);
|
|
2404
|
+
showDryRunSummary(options);
|
|
2076
2405
|
} catch (err) {
|
|
2077
2406
|
spinner.fail("Failed to generate controller");
|
|
2078
2407
|
error(err instanceof Error ? err.message : String(err));
|
|
2079
2408
|
}
|
|
2080
2409
|
});
|
|
2081
|
-
generateCommand.command("service <name>").alias("s").description("Generate a service").option("-m, --module <module>", "Target module name").action(async (name, options) => {
|
|
2410
|
+
generateCommand.command("service <name>").alias("s").description("Generate a service").option("-m, --module <module>", "Target module name").option("--dry-run", "Preview changes without writing files").action(async (name, options) => {
|
|
2411
|
+
enableDryRunIfNeeded(options);
|
|
2082
2412
|
const spinner = (0, import_ora2.default)("Generating service...").start();
|
|
2083
2413
|
try {
|
|
2084
2414
|
const kebabName = toKebabCase(name);
|
|
2085
2415
|
const pascalName = toPascalCase(name);
|
|
2086
2416
|
const camelName = toCamelCase(name);
|
|
2087
2417
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2088
|
-
const moduleDir =
|
|
2089
|
-
const filePath =
|
|
2418
|
+
const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
|
|
2419
|
+
const filePath = import_path4.default.join(moduleDir, `${kebabName}.service.ts`);
|
|
2090
2420
|
if (await fileExists(filePath)) {
|
|
2091
2421
|
spinner.stop();
|
|
2092
2422
|
error(`Service "${kebabName}" already exists`);
|
|
@@ -2095,12 +2425,14 @@ generateCommand.command("service <name>").alias("s").description("Generate a ser
|
|
|
2095
2425
|
await writeFile(filePath, serviceTemplate(kebabName, pascalName, camelName));
|
|
2096
2426
|
spinner.succeed(`Service "${pascalName}Service" generated!`);
|
|
2097
2427
|
success(` src/modules/${moduleName}/${kebabName}.service.ts`);
|
|
2428
|
+
showDryRunSummary(options);
|
|
2098
2429
|
} catch (err) {
|
|
2099
2430
|
spinner.fail("Failed to generate service");
|
|
2100
2431
|
error(err instanceof Error ? err.message : String(err));
|
|
2101
2432
|
}
|
|
2102
2433
|
});
|
|
2103
|
-
generateCommand.command("repository <name>").alias("r").description("Generate a repository").option("-m, --module <module>", "Target module name").action(async (name, options) => {
|
|
2434
|
+
generateCommand.command("repository <name>").alias("r").description("Generate a repository").option("-m, --module <module>", "Target module name").option("--dry-run", "Preview changes without writing files").action(async (name, options) => {
|
|
2435
|
+
enableDryRunIfNeeded(options);
|
|
2104
2436
|
const spinner = (0, import_ora2.default)("Generating repository...").start();
|
|
2105
2437
|
try {
|
|
2106
2438
|
const kebabName = toKebabCase(name);
|
|
@@ -2108,8 +2440,8 @@ generateCommand.command("repository <name>").alias("r").description("Generate a
|
|
|
2108
2440
|
const camelName = toCamelCase(name);
|
|
2109
2441
|
const pluralName = pluralize(kebabName);
|
|
2110
2442
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2111
|
-
const moduleDir =
|
|
2112
|
-
const filePath =
|
|
2443
|
+
const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
|
|
2444
|
+
const filePath = import_path4.default.join(moduleDir, `${kebabName}.repository.ts`);
|
|
2113
2445
|
if (await fileExists(filePath)) {
|
|
2114
2446
|
spinner.stop();
|
|
2115
2447
|
error(`Repository "${kebabName}" already exists`);
|
|
@@ -2118,19 +2450,21 @@ generateCommand.command("repository <name>").alias("r").description("Generate a
|
|
|
2118
2450
|
await writeFile(filePath, repositoryTemplate(kebabName, pascalName, camelName, pluralName));
|
|
2119
2451
|
spinner.succeed(`Repository "${pascalName}Repository" generated!`);
|
|
2120
2452
|
success(` src/modules/${moduleName}/${kebabName}.repository.ts`);
|
|
2453
|
+
showDryRunSummary(options);
|
|
2121
2454
|
} catch (err) {
|
|
2122
2455
|
spinner.fail("Failed to generate repository");
|
|
2123
2456
|
error(err instanceof Error ? err.message : String(err));
|
|
2124
2457
|
}
|
|
2125
2458
|
});
|
|
2126
|
-
generateCommand.command("types <name>").alias("t").description("Generate types/interfaces").option("-m, --module <module>", "Target module name").action(async (name, options) => {
|
|
2459
|
+
generateCommand.command("types <name>").alias("t").description("Generate types/interfaces").option("-m, --module <module>", "Target module name").option("--dry-run", "Preview changes without writing files").action(async (name, options) => {
|
|
2460
|
+
enableDryRunIfNeeded(options);
|
|
2127
2461
|
const spinner = (0, import_ora2.default)("Generating types...").start();
|
|
2128
2462
|
try {
|
|
2129
2463
|
const kebabName = toKebabCase(name);
|
|
2130
2464
|
const pascalName = toPascalCase(name);
|
|
2131
2465
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2132
|
-
const moduleDir =
|
|
2133
|
-
const filePath =
|
|
2466
|
+
const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
|
|
2467
|
+
const filePath = import_path4.default.join(moduleDir, `${kebabName}.types.ts`);
|
|
2134
2468
|
if (await fileExists(filePath)) {
|
|
2135
2469
|
spinner.stop();
|
|
2136
2470
|
error(`Types file "${kebabName}.types.ts" already exists`);
|
|
@@ -2139,20 +2473,22 @@ generateCommand.command("types <name>").alias("t").description("Generate types/i
|
|
|
2139
2473
|
await writeFile(filePath, typesTemplate(kebabName, pascalName));
|
|
2140
2474
|
spinner.succeed(`Types for "${pascalName}" generated!`);
|
|
2141
2475
|
success(` src/modules/${moduleName}/${kebabName}.types.ts`);
|
|
2476
|
+
showDryRunSummary(options);
|
|
2142
2477
|
} catch (err) {
|
|
2143
2478
|
spinner.fail("Failed to generate types");
|
|
2144
2479
|
error(err instanceof Error ? err.message : String(err));
|
|
2145
2480
|
}
|
|
2146
2481
|
});
|
|
2147
|
-
generateCommand.command("schema <name>").alias("v").description("Generate validation schemas").option("-m, --module <module>", "Target module name").action(async (name, options) => {
|
|
2482
|
+
generateCommand.command("schema <name>").alias("v").description("Generate validation schemas").option("-m, --module <module>", "Target module name").option("--dry-run", "Preview changes without writing files").action(async (name, options) => {
|
|
2483
|
+
enableDryRunIfNeeded(options);
|
|
2148
2484
|
const spinner = (0, import_ora2.default)("Generating schemas...").start();
|
|
2149
2485
|
try {
|
|
2150
2486
|
const kebabName = toKebabCase(name);
|
|
2151
2487
|
const pascalName = toPascalCase(name);
|
|
2152
2488
|
const camelName = toCamelCase(name);
|
|
2153
2489
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2154
|
-
const moduleDir =
|
|
2155
|
-
const filePath =
|
|
2490
|
+
const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
|
|
2491
|
+
const filePath = import_path4.default.join(moduleDir, `${kebabName}.schemas.ts`);
|
|
2156
2492
|
if (await fileExists(filePath)) {
|
|
2157
2493
|
spinner.stop();
|
|
2158
2494
|
error(`Schemas file "${kebabName}.schemas.ts" already exists`);
|
|
@@ -2161,12 +2497,14 @@ generateCommand.command("schema <name>").alias("v").description("Generate valida
|
|
|
2161
2497
|
await writeFile(filePath, schemasTemplate(kebabName, pascalName, camelName));
|
|
2162
2498
|
spinner.succeed(`Schemas for "${pascalName}" generated!`);
|
|
2163
2499
|
success(` src/modules/${moduleName}/${kebabName}.schemas.ts`);
|
|
2500
|
+
showDryRunSummary(options);
|
|
2164
2501
|
} catch (err) {
|
|
2165
2502
|
spinner.fail("Failed to generate schemas");
|
|
2166
2503
|
error(err instanceof Error ? err.message : String(err));
|
|
2167
2504
|
}
|
|
2168
2505
|
});
|
|
2169
|
-
generateCommand.command("routes <name>").description("Generate routes").option("-m, --module <module>", "Target module name").action(async (name, options) => {
|
|
2506
|
+
generateCommand.command("routes <name>").description("Generate routes").option("-m, --module <module>", "Target module name").option("--dry-run", "Preview changes without writing files").action(async (name, options) => {
|
|
2507
|
+
enableDryRunIfNeeded(options);
|
|
2170
2508
|
const spinner = (0, import_ora2.default)("Generating routes...").start();
|
|
2171
2509
|
try {
|
|
2172
2510
|
const kebabName = toKebabCase(name);
|
|
@@ -2174,8 +2512,8 @@ generateCommand.command("routes <name>").description("Generate routes").option("
|
|
|
2174
2512
|
const camelName = toCamelCase(name);
|
|
2175
2513
|
const pluralName = pluralize(kebabName);
|
|
2176
2514
|
const moduleName = options.module ? toKebabCase(options.module) : kebabName;
|
|
2177
|
-
const moduleDir =
|
|
2178
|
-
const filePath =
|
|
2515
|
+
const moduleDir = import_path4.default.join(getModulesDir(), moduleName);
|
|
2516
|
+
const filePath = import_path4.default.join(moduleDir, `${kebabName}.routes.ts`);
|
|
2179
2517
|
if (await fileExists(filePath)) {
|
|
2180
2518
|
spinner.stop();
|
|
2181
2519
|
error(`Routes file "${kebabName}.routes.ts" already exists`);
|
|
@@ -2184,6 +2522,7 @@ generateCommand.command("routes <name>").description("Generate routes").option("
|
|
|
2184
2522
|
await writeFile(filePath, routesTemplate(kebabName, pascalName, camelName, pluralName));
|
|
2185
2523
|
spinner.succeed(`Routes for "${pascalName}" generated!`);
|
|
2186
2524
|
success(` src/modules/${moduleName}/${kebabName}.routes.ts`);
|
|
2525
|
+
showDryRunSummary(options);
|
|
2187
2526
|
} catch (err) {
|
|
2188
2527
|
spinner.fail("Failed to generate routes");
|
|
2189
2528
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -2261,22 +2600,24 @@ async function promptForFields() {
|
|
|
2261
2600
|
}
|
|
2262
2601
|
|
|
2263
2602
|
// src/cli/commands/add-module.ts
|
|
2603
|
+
init_cjs_shims();
|
|
2264
2604
|
var import_commander3 = require("commander");
|
|
2265
|
-
var
|
|
2605
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
2266
2606
|
var import_ora3 = __toESM(require("ora"), 1);
|
|
2267
|
-
var
|
|
2607
|
+
var import_chalk7 = __toESM(require("chalk"), 1);
|
|
2268
2608
|
var fs5 = __toESM(require("fs/promises"), 1);
|
|
2269
2609
|
|
|
2270
2610
|
// src/cli/utils/env-manager.ts
|
|
2611
|
+
init_cjs_shims();
|
|
2271
2612
|
var fs3 = __toESM(require("fs/promises"), 1);
|
|
2272
|
-
var
|
|
2613
|
+
var path5 = __toESM(require("path"), 1);
|
|
2273
2614
|
var import_fs = require("fs");
|
|
2274
2615
|
var EnvManager = class {
|
|
2275
2616
|
envPath;
|
|
2276
2617
|
envExamplePath;
|
|
2277
2618
|
constructor(projectRoot) {
|
|
2278
|
-
this.envPath =
|
|
2279
|
-
this.envExamplePath =
|
|
2619
|
+
this.envPath = path5.join(projectRoot, ".env");
|
|
2620
|
+
this.envExamplePath = path5.join(projectRoot, ".env.example");
|
|
2280
2621
|
}
|
|
2281
2622
|
/**
|
|
2282
2623
|
* Add environment variables to .env file
|
|
@@ -2926,16 +3267,17 @@ var EnvManager = class {
|
|
|
2926
3267
|
};
|
|
2927
3268
|
|
|
2928
3269
|
// src/cli/utils/template-manager.ts
|
|
3270
|
+
init_cjs_shims();
|
|
2929
3271
|
var fs4 = __toESM(require("fs/promises"), 1);
|
|
2930
|
-
var
|
|
3272
|
+
var path6 = __toESM(require("path"), 1);
|
|
2931
3273
|
var import_crypto = require("crypto");
|
|
2932
3274
|
var import_fs2 = require("fs");
|
|
2933
3275
|
var TemplateManager = class {
|
|
2934
3276
|
templatesDir;
|
|
2935
3277
|
manifestsDir;
|
|
2936
3278
|
constructor(projectRoot) {
|
|
2937
|
-
this.templatesDir =
|
|
2938
|
-
this.manifestsDir =
|
|
3279
|
+
this.templatesDir = path6.join(projectRoot, ".servcraft", "templates");
|
|
3280
|
+
this.manifestsDir = path6.join(projectRoot, ".servcraft", "manifests");
|
|
2939
3281
|
}
|
|
2940
3282
|
/**
|
|
2941
3283
|
* Initialize template system
|
|
@@ -2949,10 +3291,10 @@ var TemplateManager = class {
|
|
|
2949
3291
|
*/
|
|
2950
3292
|
async saveTemplate(moduleName, files) {
|
|
2951
3293
|
await this.initialize();
|
|
2952
|
-
const moduleTemplateDir =
|
|
3294
|
+
const moduleTemplateDir = path6.join(this.templatesDir, moduleName);
|
|
2953
3295
|
await fs4.mkdir(moduleTemplateDir, { recursive: true });
|
|
2954
3296
|
for (const [fileName, content] of Object.entries(files)) {
|
|
2955
|
-
const filePath =
|
|
3297
|
+
const filePath = path6.join(moduleTemplateDir, fileName);
|
|
2956
3298
|
await fs4.writeFile(filePath, content, "utf-8");
|
|
2957
3299
|
}
|
|
2958
3300
|
}
|
|
@@ -2961,7 +3303,7 @@ var TemplateManager = class {
|
|
|
2961
3303
|
*/
|
|
2962
3304
|
async getTemplate(moduleName, fileName) {
|
|
2963
3305
|
try {
|
|
2964
|
-
const filePath =
|
|
3306
|
+
const filePath = path6.join(this.templatesDir, moduleName, fileName);
|
|
2965
3307
|
return await fs4.readFile(filePath, "utf-8");
|
|
2966
3308
|
} catch {
|
|
2967
3309
|
return null;
|
|
@@ -2986,7 +3328,7 @@ var TemplateManager = class {
|
|
|
2986
3328
|
installedAt: /* @__PURE__ */ new Date(),
|
|
2987
3329
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2988
3330
|
};
|
|
2989
|
-
const manifestPath =
|
|
3331
|
+
const manifestPath = path6.join(this.manifestsDir, `${moduleName}.json`);
|
|
2990
3332
|
await fs4.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
2991
3333
|
}
|
|
2992
3334
|
/**
|
|
@@ -2994,7 +3336,7 @@ var TemplateManager = class {
|
|
|
2994
3336
|
*/
|
|
2995
3337
|
async getManifest(moduleName) {
|
|
2996
3338
|
try {
|
|
2997
|
-
const manifestPath =
|
|
3339
|
+
const manifestPath = path6.join(this.manifestsDir, `${moduleName}.json`);
|
|
2998
3340
|
const content = await fs4.readFile(manifestPath, "utf-8");
|
|
2999
3341
|
return JSON.parse(content);
|
|
3000
3342
|
} catch {
|
|
@@ -3023,7 +3365,7 @@ var TemplateManager = class {
|
|
|
3023
3365
|
}
|
|
3024
3366
|
const results = [];
|
|
3025
3367
|
for (const [fileName, fileInfo] of Object.entries(manifest.files)) {
|
|
3026
|
-
const filePath =
|
|
3368
|
+
const filePath = path6.join(moduleDir, fileName);
|
|
3027
3369
|
if (!(0, import_fs2.existsSync)(filePath)) {
|
|
3028
3370
|
results.push({
|
|
3029
3371
|
fileName,
|
|
@@ -3049,7 +3391,7 @@ var TemplateManager = class {
|
|
|
3049
3391
|
*/
|
|
3050
3392
|
async createBackup(moduleName, moduleDir) {
|
|
3051
3393
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
|
|
3052
|
-
const backupDir =
|
|
3394
|
+
const backupDir = path6.join(path6.dirname(moduleDir), `${moduleName}.backup-${timestamp}`);
|
|
3053
3395
|
await this.copyDirectory(moduleDir, backupDir);
|
|
3054
3396
|
return backupDir;
|
|
3055
3397
|
}
|
|
@@ -3060,8 +3402,8 @@ var TemplateManager = class {
|
|
|
3060
3402
|
await fs4.mkdir(dest, { recursive: true });
|
|
3061
3403
|
const entries = await fs4.readdir(src, { withFileTypes: true });
|
|
3062
3404
|
for (const entry of entries) {
|
|
3063
|
-
const srcPath =
|
|
3064
|
-
const destPath =
|
|
3405
|
+
const srcPath = path6.join(src, entry.name);
|
|
3406
|
+
const destPath = path6.join(dest, entry.name);
|
|
3065
3407
|
if (entry.isDirectory()) {
|
|
3066
3408
|
await this.copyDirectory(srcPath, destPath);
|
|
3067
3409
|
} else {
|
|
@@ -3162,23 +3504,24 @@ var TemplateManager = class {
|
|
|
3162
3504
|
}
|
|
3163
3505
|
manifest.files = fileHashes;
|
|
3164
3506
|
manifest.updatedAt = /* @__PURE__ */ new Date();
|
|
3165
|
-
const manifestPath =
|
|
3507
|
+
const manifestPath = path6.join(this.manifestsDir, `${moduleName}.json`);
|
|
3166
3508
|
await fs4.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
3167
3509
|
}
|
|
3168
3510
|
};
|
|
3169
3511
|
|
|
3170
3512
|
// src/cli/utils/interactive-prompt.ts
|
|
3513
|
+
init_cjs_shims();
|
|
3171
3514
|
var import_inquirer3 = __toESM(require("inquirer"), 1);
|
|
3172
|
-
var
|
|
3515
|
+
var import_chalk6 = __toESM(require("chalk"), 1);
|
|
3173
3516
|
var InteractivePrompt = class {
|
|
3174
3517
|
/**
|
|
3175
3518
|
* Ask what to do when module already exists
|
|
3176
3519
|
*/
|
|
3177
3520
|
static async askModuleExists(moduleName, hasModifications) {
|
|
3178
|
-
console.log(
|
|
3521
|
+
console.log(import_chalk6.default.yellow(`
|
|
3179
3522
|
\u26A0\uFE0F Module "${moduleName}" already exists`));
|
|
3180
3523
|
if (hasModifications) {
|
|
3181
|
-
console.log(
|
|
3524
|
+
console.log(import_chalk6.default.yellow(" Some files have been modified by you.\n"));
|
|
3182
3525
|
}
|
|
3183
3526
|
const { action } = await import_inquirer3.default.prompt([
|
|
3184
3527
|
{
|
|
@@ -3216,12 +3559,12 @@ var InteractivePrompt = class {
|
|
|
3216
3559
|
* Ask what to do with a specific file
|
|
3217
3560
|
*/
|
|
3218
3561
|
static async askFileAction(fileName, isModified, yourLines, newLines) {
|
|
3219
|
-
console.log(
|
|
3562
|
+
console.log(import_chalk6.default.cyan(`
|
|
3220
3563
|
\u{1F4C1} ${fileName}`));
|
|
3221
3564
|
console.log(
|
|
3222
|
-
|
|
3565
|
+
import_chalk6.default.gray(` Your version: ${yourLines} lines${isModified ? " (modified)" : ""}`)
|
|
3223
3566
|
);
|
|
3224
|
-
console.log(
|
|
3567
|
+
console.log(import_chalk6.default.gray(` New version: ${newLines} lines
|
|
3225
3568
|
`));
|
|
3226
3569
|
const { action } = await import_inquirer3.default.prompt([
|
|
3227
3570
|
{
|
|
@@ -3273,7 +3616,7 @@ var InteractivePrompt = class {
|
|
|
3273
3616
|
* Display diff and ask to continue
|
|
3274
3617
|
*/
|
|
3275
3618
|
static async showDiffAndAsk(diff) {
|
|
3276
|
-
console.log(
|
|
3619
|
+
console.log(import_chalk6.default.cyan("\n\u{1F4CA} Differences:\n"));
|
|
3277
3620
|
console.log(diff);
|
|
3278
3621
|
return await this.confirm("\nDo you want to proceed with this change?", true);
|
|
3279
3622
|
}
|
|
@@ -3281,41 +3624,41 @@ var InteractivePrompt = class {
|
|
|
3281
3624
|
* Display merge conflicts
|
|
3282
3625
|
*/
|
|
3283
3626
|
static displayConflicts(conflicts) {
|
|
3284
|
-
console.log(
|
|
3627
|
+
console.log(import_chalk6.default.red("\n\u26A0\uFE0F Merge Conflicts Detected:\n"));
|
|
3285
3628
|
conflicts.forEach((conflict, i) => {
|
|
3286
|
-
console.log(
|
|
3629
|
+
console.log(import_chalk6.default.yellow(` ${i + 1}. ${conflict}`));
|
|
3287
3630
|
});
|
|
3288
|
-
console.log(
|
|
3289
|
-
console.log(
|
|
3290
|
-
console.log(
|
|
3291
|
-
console.log(
|
|
3292
|
-
console.log(
|
|
3293
|
-
console.log(
|
|
3631
|
+
console.log(import_chalk6.default.gray("\n Conflict markers have been added to the file:"));
|
|
3632
|
+
console.log(import_chalk6.default.gray(" <<<<<<< YOUR VERSION"));
|
|
3633
|
+
console.log(import_chalk6.default.gray(" ... your code ..."));
|
|
3634
|
+
console.log(import_chalk6.default.gray(" ======="));
|
|
3635
|
+
console.log(import_chalk6.default.gray(" ... new code ..."));
|
|
3636
|
+
console.log(import_chalk6.default.gray(" >>>>>>> NEW VERSION\n"));
|
|
3294
3637
|
}
|
|
3295
3638
|
/**
|
|
3296
3639
|
* Show backup location
|
|
3297
3640
|
*/
|
|
3298
3641
|
static showBackupCreated(backupPath) {
|
|
3299
|
-
console.log(
|
|
3300
|
-
\u2713 Backup created: ${
|
|
3642
|
+
console.log(import_chalk6.default.green(`
|
|
3643
|
+
\u2713 Backup created: ${import_chalk6.default.cyan(backupPath)}`));
|
|
3301
3644
|
}
|
|
3302
3645
|
/**
|
|
3303
3646
|
* Show merge summary
|
|
3304
3647
|
*/
|
|
3305
3648
|
static showMergeSummary(stats) {
|
|
3306
|
-
console.log(
|
|
3649
|
+
console.log(import_chalk6.default.bold("\n\u{1F4CA} Merge Summary:\n"));
|
|
3307
3650
|
if (stats.merged > 0) {
|
|
3308
|
-
console.log(
|
|
3651
|
+
console.log(import_chalk6.default.green(` \u2713 Merged: ${stats.merged} file(s)`));
|
|
3309
3652
|
}
|
|
3310
3653
|
if (stats.kept > 0) {
|
|
3311
|
-
console.log(
|
|
3654
|
+
console.log(import_chalk6.default.blue(` \u2192 Kept: ${stats.kept} file(s)`));
|
|
3312
3655
|
}
|
|
3313
3656
|
if (stats.overwritten > 0) {
|
|
3314
|
-
console.log(
|
|
3657
|
+
console.log(import_chalk6.default.yellow(` \u26A0 Overwritten: ${stats.overwritten} file(s)`));
|
|
3315
3658
|
}
|
|
3316
3659
|
if (stats.conflicts > 0) {
|
|
3317
|
-
console.log(
|
|
3318
|
-
console.log(
|
|
3660
|
+
console.log(import_chalk6.default.red(` \u26A0 Conflicts: ${stats.conflicts} file(s)`));
|
|
3661
|
+
console.log(import_chalk6.default.gray("\n Please resolve conflicts manually before committing.\n"));
|
|
3319
3662
|
}
|
|
3320
3663
|
}
|
|
3321
3664
|
/**
|
|
@@ -3353,6 +3696,7 @@ var InteractivePrompt = class {
|
|
|
3353
3696
|
};
|
|
3354
3697
|
|
|
3355
3698
|
// src/cli/commands/add-module.ts
|
|
3699
|
+
init_error_handler();
|
|
3356
3700
|
var AVAILABLE_MODULES = {
|
|
3357
3701
|
auth: {
|
|
3358
3702
|
name: "Authentication",
|
|
@@ -3487,31 +3831,40 @@ var AVAILABLE_MODULES = {
|
|
|
3487
3831
|
var addModuleCommand = new import_commander3.Command("add").description("Add a pre-built module to your project").argument(
|
|
3488
3832
|
"[module]",
|
|
3489
3833
|
"Module to add (auth, users, email, audit, upload, cache, notifications, settings)"
|
|
3490
|
-
).option("-l, --list", "List available modules").option("-f, --force", "Force overwrite existing module").option("-u, --update", "Update existing module (smart merge)").option("--skip-existing", "Skip if module already exists").action(
|
|
3834
|
+
).option("-l, --list", "List available modules").option("-f, --force", "Force overwrite existing module").option("-u, --update", "Update existing module (smart merge)").option("--skip-existing", "Skip if module already exists").option("--dry-run", "Preview changes without writing files").action(
|
|
3491
3835
|
async (moduleName, options) => {
|
|
3492
3836
|
if (options?.list || !moduleName) {
|
|
3493
|
-
console.log(
|
|
3837
|
+
console.log(import_chalk7.default.bold("\n\u{1F4E6} Available Modules:\n"));
|
|
3494
3838
|
for (const [key, mod] of Object.entries(AVAILABLE_MODULES)) {
|
|
3495
|
-
console.log(` ${
|
|
3496
|
-
console.log(` ${" ".repeat(15)} ${
|
|
3839
|
+
console.log(` ${import_chalk7.default.cyan(key.padEnd(15))} ${mod.name}`);
|
|
3840
|
+
console.log(` ${" ".repeat(15)} ${import_chalk7.default.gray(mod.description)}
|
|
3497
3841
|
`);
|
|
3498
3842
|
}
|
|
3499
|
-
console.log(
|
|
3500
|
-
console.log(` ${
|
|
3501
|
-
console.log(` ${
|
|
3502
|
-
console.log(` ${
|
|
3843
|
+
console.log(import_chalk7.default.bold("Usage:"));
|
|
3844
|
+
console.log(` ${import_chalk7.default.yellow("servcraft add auth")} Add authentication module`);
|
|
3845
|
+
console.log(` ${import_chalk7.default.yellow("servcraft add users")} Add user management module`);
|
|
3846
|
+
console.log(` ${import_chalk7.default.yellow("servcraft add email")} Add email service module
|
|
3503
3847
|
`);
|
|
3504
3848
|
return;
|
|
3505
3849
|
}
|
|
3850
|
+
const dryRun = DryRunManager.getInstance();
|
|
3851
|
+
if (options?.dryRun) {
|
|
3852
|
+
dryRun.enable();
|
|
3853
|
+
console.log(import_chalk7.default.yellow("\n\u26A0 DRY RUN MODE - No files will be written\n"));
|
|
3854
|
+
}
|
|
3506
3855
|
const module2 = AVAILABLE_MODULES[moduleName];
|
|
3507
3856
|
if (!module2) {
|
|
3508
|
-
|
|
3509
|
-
|
|
3857
|
+
displayError(ErrorTypes.MODULE_NOT_FOUND(moduleName));
|
|
3858
|
+
return;
|
|
3859
|
+
}
|
|
3860
|
+
const projectError = validateProject();
|
|
3861
|
+
if (projectError) {
|
|
3862
|
+
displayError(projectError);
|
|
3510
3863
|
return;
|
|
3511
3864
|
}
|
|
3512
3865
|
const spinner = (0, import_ora3.default)(`Adding ${module2.name} module...`).start();
|
|
3513
3866
|
try {
|
|
3514
|
-
const moduleDir =
|
|
3867
|
+
const moduleDir = import_path5.default.join(getModulesDir(), moduleName);
|
|
3515
3868
|
const templateManager = new TemplateManager(process.cwd());
|
|
3516
3869
|
const moduleExists = await fileExists(moduleDir);
|
|
3517
3870
|
if (moduleExists) {
|
|
@@ -3579,16 +3932,16 @@ var addModuleCommand = new import_commander3.Command("add").description("Add a p
|
|
|
3579
3932
|
info("\n\u{1F4DD} Created new .env file");
|
|
3580
3933
|
}
|
|
3581
3934
|
if (result.added.length > 0) {
|
|
3582
|
-
console.log(
|
|
3935
|
+
console.log(import_chalk7.default.bold("\n\u2705 Added to .env:"));
|
|
3583
3936
|
result.added.forEach((key) => success(` ${key}`));
|
|
3584
3937
|
}
|
|
3585
3938
|
if (result.skipped.length > 0) {
|
|
3586
|
-
console.log(
|
|
3939
|
+
console.log(import_chalk7.default.bold("\n\u23ED\uFE0F Already in .env (skipped):"));
|
|
3587
3940
|
result.skipped.forEach((key) => info(` ${key}`));
|
|
3588
3941
|
}
|
|
3589
3942
|
const requiredVars = envSections.flatMap((section) => section.variables).filter((v) => v.required && !v.value).map((v) => v.key);
|
|
3590
3943
|
if (requiredVars.length > 0) {
|
|
3591
|
-
console.log(
|
|
3944
|
+
console.log(import_chalk7.default.bold("\n\u26A0\uFE0F Required configuration:"));
|
|
3592
3945
|
requiredVars.forEach((key) => warn(` ${key} - Please configure this variable`));
|
|
3593
3946
|
}
|
|
3594
3947
|
} catch (err) {
|
|
@@ -3596,10 +3949,15 @@ var addModuleCommand = new import_commander3.Command("add").description("Add a p
|
|
|
3596
3949
|
error(err instanceof Error ? err.message : String(err));
|
|
3597
3950
|
}
|
|
3598
3951
|
}
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3952
|
+
if (!options?.dryRun) {
|
|
3953
|
+
console.log("\n\u{1F4CC} Next steps:");
|
|
3954
|
+
info(" 1. Configure environment variables in .env (if needed)");
|
|
3955
|
+
info(" 2. Register the module in your main app file");
|
|
3956
|
+
info(" 3. Run database migrations if needed");
|
|
3957
|
+
}
|
|
3958
|
+
if (options?.dryRun) {
|
|
3959
|
+
dryRun.printSummary();
|
|
3960
|
+
}
|
|
3603
3961
|
} catch (err) {
|
|
3604
3962
|
spinner.fail("Failed to add module");
|
|
3605
3963
|
error(err instanceof Error ? err.message : String(err));
|
|
@@ -3650,7 +4008,7 @@ export * from './auth.schemas.js';
|
|
|
3650
4008
|
`
|
|
3651
4009
|
};
|
|
3652
4010
|
for (const [name, content] of Object.entries(files)) {
|
|
3653
|
-
await writeFile(
|
|
4011
|
+
await writeFile(import_path5.default.join(dir, name), content);
|
|
3654
4012
|
}
|
|
3655
4013
|
}
|
|
3656
4014
|
async function generateUsersModule(dir) {
|
|
@@ -3690,7 +4048,7 @@ export * from './user.schemas.js';
|
|
|
3690
4048
|
`
|
|
3691
4049
|
};
|
|
3692
4050
|
for (const [name, content] of Object.entries(files)) {
|
|
3693
|
-
await writeFile(
|
|
4051
|
+
await writeFile(import_path5.default.join(dir, name), content);
|
|
3694
4052
|
}
|
|
3695
4053
|
}
|
|
3696
4054
|
async function generateEmailModule(dir) {
|
|
@@ -3747,7 +4105,7 @@ export { EmailService, emailService } from './email.service.js';
|
|
|
3747
4105
|
`
|
|
3748
4106
|
};
|
|
3749
4107
|
for (const [name, content] of Object.entries(files)) {
|
|
3750
|
-
await writeFile(
|
|
4108
|
+
await writeFile(import_path5.default.join(dir, name), content);
|
|
3751
4109
|
}
|
|
3752
4110
|
}
|
|
3753
4111
|
async function generateAuditModule(dir) {
|
|
@@ -3791,7 +4149,7 @@ export { AuditService, auditService } from './audit.service.js';
|
|
|
3791
4149
|
`
|
|
3792
4150
|
};
|
|
3793
4151
|
for (const [name, content] of Object.entries(files)) {
|
|
3794
|
-
await writeFile(
|
|
4152
|
+
await writeFile(import_path5.default.join(dir, name), content);
|
|
3795
4153
|
}
|
|
3796
4154
|
}
|
|
3797
4155
|
async function generateUploadModule(dir) {
|
|
@@ -3817,7 +4175,7 @@ export interface UploadOptions {
|
|
|
3817
4175
|
`
|
|
3818
4176
|
};
|
|
3819
4177
|
for (const [name, content] of Object.entries(files)) {
|
|
3820
|
-
await writeFile(
|
|
4178
|
+
await writeFile(import_path5.default.join(dir, name), content);
|
|
3821
4179
|
}
|
|
3822
4180
|
}
|
|
3823
4181
|
async function generateCacheModule(dir) {
|
|
@@ -3863,7 +4221,7 @@ export { CacheService, cacheService } from './cache.service.js';
|
|
|
3863
4221
|
`
|
|
3864
4222
|
};
|
|
3865
4223
|
for (const [name, content] of Object.entries(files)) {
|
|
3866
|
-
await writeFile(
|
|
4224
|
+
await writeFile(import_path5.default.join(dir, name), content);
|
|
3867
4225
|
}
|
|
3868
4226
|
}
|
|
3869
4227
|
async function generateGenericModule(dir, name) {
|
|
@@ -3877,18 +4235,18 @@ export interface ${name.charAt(0).toUpperCase() + name.slice(1)}Data {
|
|
|
3877
4235
|
`
|
|
3878
4236
|
};
|
|
3879
4237
|
for (const [fileName, content] of Object.entries(files)) {
|
|
3880
|
-
await writeFile(
|
|
4238
|
+
await writeFile(import_path5.default.join(dir, fileName), content);
|
|
3881
4239
|
}
|
|
3882
4240
|
}
|
|
3883
4241
|
async function findServercraftModules() {
|
|
3884
|
-
const scriptDir =
|
|
4242
|
+
const scriptDir = import_path5.default.dirname(new URL(importMetaUrl).pathname);
|
|
3885
4243
|
const possiblePaths = [
|
|
3886
4244
|
// Local node_modules (when servcraft is a dependency)
|
|
3887
|
-
|
|
4245
|
+
import_path5.default.join(process.cwd(), "node_modules", "servcraft", "src", "modules"),
|
|
3888
4246
|
// From dist/cli/index.js -> src/modules (npx or global install)
|
|
3889
|
-
|
|
4247
|
+
import_path5.default.resolve(scriptDir, "..", "..", "src", "modules"),
|
|
3890
4248
|
// From src/cli/commands/add-module.ts -> src/modules (development)
|
|
3891
|
-
|
|
4249
|
+
import_path5.default.resolve(scriptDir, "..", "..", "modules")
|
|
3892
4250
|
];
|
|
3893
4251
|
for (const p of possiblePaths) {
|
|
3894
4252
|
try {
|
|
@@ -3912,7 +4270,7 @@ async function generateModuleFiles(moduleName, moduleDir) {
|
|
|
3912
4270
|
const sourceDirName = moduleNameMap[moduleName] || moduleName;
|
|
3913
4271
|
const servercraftModulesDir = await findServercraftModules();
|
|
3914
4272
|
if (servercraftModulesDir) {
|
|
3915
|
-
const sourceModuleDir =
|
|
4273
|
+
const sourceModuleDir = import_path5.default.join(servercraftModulesDir, sourceDirName);
|
|
3916
4274
|
if (await fileExists(sourceModuleDir)) {
|
|
3917
4275
|
await copyModuleFromSource(sourceModuleDir, moduleDir);
|
|
3918
4276
|
return;
|
|
@@ -3944,8 +4302,8 @@ async function generateModuleFiles(moduleName, moduleDir) {
|
|
|
3944
4302
|
async function copyModuleFromSource(sourceDir, targetDir) {
|
|
3945
4303
|
const entries = await fs5.readdir(sourceDir, { withFileTypes: true });
|
|
3946
4304
|
for (const entry of entries) {
|
|
3947
|
-
const sourcePath =
|
|
3948
|
-
const targetPath =
|
|
4305
|
+
const sourcePath = import_path5.default.join(sourceDir, entry.name);
|
|
4306
|
+
const targetPath = import_path5.default.join(targetDir, entry.name);
|
|
3949
4307
|
if (entry.isDirectory()) {
|
|
3950
4308
|
await fs5.mkdir(targetPath, { recursive: true });
|
|
3951
4309
|
await copyModuleFromSource(sourcePath, targetPath);
|
|
@@ -3958,7 +4316,7 @@ async function getModuleFiles(moduleName, moduleDir) {
|
|
|
3958
4316
|
const files = {};
|
|
3959
4317
|
const entries = await fs5.readdir(moduleDir);
|
|
3960
4318
|
for (const entry of entries) {
|
|
3961
|
-
const filePath =
|
|
4319
|
+
const filePath = import_path5.default.join(moduleDir, entry);
|
|
3962
4320
|
const stat2 = await fs5.stat(filePath);
|
|
3963
4321
|
if (stat2.isFile() && entry.endsWith(".ts")) {
|
|
3964
4322
|
const content = await fs5.readFile(filePath, "utf-8");
|
|
@@ -3969,14 +4327,14 @@ async function getModuleFiles(moduleName, moduleDir) {
|
|
|
3969
4327
|
}
|
|
3970
4328
|
async function showDiffForModule(templateManager, moduleName, moduleDir) {
|
|
3971
4329
|
const modifiedFiles = await templateManager.getModifiedFiles(moduleName, moduleDir);
|
|
3972
|
-
console.log(
|
|
4330
|
+
console.log(import_chalk7.default.cyan(`
|
|
3973
4331
|
\u{1F4CA} Changes in module "${moduleName}":
|
|
3974
4332
|
`));
|
|
3975
4333
|
for (const file of modifiedFiles) {
|
|
3976
4334
|
if (file.isModified) {
|
|
3977
|
-
console.log(
|
|
4335
|
+
console.log(import_chalk7.default.yellow(`
|
|
3978
4336
|
\u{1F4C4} ${file.fileName}:`));
|
|
3979
|
-
const currentPath =
|
|
4337
|
+
const currentPath = import_path5.default.join(moduleDir, file.fileName);
|
|
3980
4338
|
const currentContent = await fs5.readFile(currentPath, "utf-8");
|
|
3981
4339
|
const originalContent = await templateManager.getTemplate(moduleName, file.fileName);
|
|
3982
4340
|
if (originalContent) {
|
|
@@ -3989,11 +4347,11 @@ async function showDiffForModule(templateManager, moduleName, moduleDir) {
|
|
|
3989
4347
|
async function performSmartMerge(templateManager, moduleName, moduleDir, _displayName) {
|
|
3990
4348
|
const spinner = (0, import_ora3.default)("Analyzing files for merge...").start();
|
|
3991
4349
|
const newFiles = {};
|
|
3992
|
-
const templateDir =
|
|
4350
|
+
const templateDir = import_path5.default.join(templateManager["templatesDir"], moduleName);
|
|
3993
4351
|
try {
|
|
3994
4352
|
const entries = await fs5.readdir(templateDir);
|
|
3995
4353
|
for (const entry of entries) {
|
|
3996
|
-
const content = await fs5.readFile(
|
|
4354
|
+
const content = await fs5.readFile(import_path5.default.join(templateDir, entry), "utf-8");
|
|
3997
4355
|
newFiles[entry] = content;
|
|
3998
4356
|
}
|
|
3999
4357
|
} catch {
|
|
@@ -4011,7 +4369,7 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4011
4369
|
};
|
|
4012
4370
|
for (const fileInfo of modifiedFiles) {
|
|
4013
4371
|
const fileName = fileInfo.fileName;
|
|
4014
|
-
const filePath =
|
|
4372
|
+
const filePath = import_path5.default.join(moduleDir, fileName);
|
|
4015
4373
|
const newContent = newFiles[fileName];
|
|
4016
4374
|
if (!newContent) {
|
|
4017
4375
|
continue;
|
|
@@ -4080,10 +4438,11 @@ async function performSmartMerge(templateManager, moduleName, moduleDir, _displa
|
|
|
4080
4438
|
}
|
|
4081
4439
|
|
|
4082
4440
|
// src/cli/commands/db.ts
|
|
4441
|
+
init_cjs_shims();
|
|
4083
4442
|
var import_commander4 = require("commander");
|
|
4084
4443
|
var import_child_process2 = require("child_process");
|
|
4085
4444
|
var import_ora4 = __toESM(require("ora"), 1);
|
|
4086
|
-
var
|
|
4445
|
+
var import_chalk8 = __toESM(require("chalk"), 1);
|
|
4087
4446
|
var dbCommand = new import_commander4.Command("db").description("Database management commands");
|
|
4088
4447
|
dbCommand.command("migrate").description("Run database migrations").option("-n, --name <name>", "Migration name").action(async (options) => {
|
|
4089
4448
|
const spinner = (0, import_ora4.default)("Running migrations...").start();
|
|
@@ -4140,7 +4499,7 @@ dbCommand.command("seed").description("Run database seed").action(async () => {
|
|
|
4140
4499
|
});
|
|
4141
4500
|
dbCommand.command("reset").description("Reset database (drop all data and re-run migrations)").option("-f, --force", "Skip confirmation").action(async (options) => {
|
|
4142
4501
|
if (!options.force) {
|
|
4143
|
-
console.log(
|
|
4502
|
+
console.log(import_chalk8.default.yellow("\n\u26A0\uFE0F WARNING: This will delete all data in your database!\n"));
|
|
4144
4503
|
const readline = await import("readline");
|
|
4145
4504
|
const rl = readline.createInterface({
|
|
4146
4505
|
input: process.stdin,
|
|
@@ -4173,21 +4532,25 @@ dbCommand.command("status").description("Show migration status").action(async ()
|
|
|
4173
4532
|
});
|
|
4174
4533
|
|
|
4175
4534
|
// src/cli/commands/docs.ts
|
|
4535
|
+
init_cjs_shims();
|
|
4176
4536
|
var import_commander5 = require("commander");
|
|
4177
|
-
var
|
|
4537
|
+
var import_path7 = __toESM(require("path"), 1);
|
|
4178
4538
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
4179
4539
|
var import_ora6 = __toESM(require("ora"), 1);
|
|
4180
|
-
var
|
|
4540
|
+
var import_chalk9 = __toESM(require("chalk"), 1);
|
|
4181
4541
|
|
|
4182
4542
|
// src/cli/utils/docs-generator.ts
|
|
4543
|
+
init_cjs_shims();
|
|
4183
4544
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
4184
|
-
var
|
|
4545
|
+
var import_path6 = __toESM(require("path"), 1);
|
|
4185
4546
|
var import_ora5 = __toESM(require("ora"), 1);
|
|
4186
4547
|
|
|
4187
4548
|
// src/core/server.ts
|
|
4549
|
+
init_cjs_shims();
|
|
4188
4550
|
var import_fastify = __toESM(require("fastify"), 1);
|
|
4189
4551
|
|
|
4190
4552
|
// src/core/logger.ts
|
|
4553
|
+
init_cjs_shims();
|
|
4191
4554
|
var import_pino = __toESM(require("pino"), 1);
|
|
4192
4555
|
var defaultConfig = {
|
|
4193
4556
|
level: process.env.LOG_LEVEL || "info",
|
|
@@ -4320,7 +4683,14 @@ function createServer(config2 = {}) {
|
|
|
4320
4683
|
return new Server(config2);
|
|
4321
4684
|
}
|
|
4322
4685
|
|
|
4686
|
+
// src/middleware/index.ts
|
|
4687
|
+
init_cjs_shims();
|
|
4688
|
+
|
|
4689
|
+
// src/middleware/error-handler.ts
|
|
4690
|
+
init_cjs_shims();
|
|
4691
|
+
|
|
4323
4692
|
// src/utils/errors.ts
|
|
4693
|
+
init_cjs_shims();
|
|
4324
4694
|
var AppError = class _AppError extends Error {
|
|
4325
4695
|
statusCode;
|
|
4326
4696
|
isOperational;
|
|
@@ -4374,7 +4744,11 @@ function isAppError(error2) {
|
|
|
4374
4744
|
return error2 instanceof AppError;
|
|
4375
4745
|
}
|
|
4376
4746
|
|
|
4747
|
+
// src/config/index.ts
|
|
4748
|
+
init_cjs_shims();
|
|
4749
|
+
|
|
4377
4750
|
// src/config/env.ts
|
|
4751
|
+
init_cjs_shims();
|
|
4378
4752
|
var import_zod = require("zod");
|
|
4379
4753
|
var import_dotenv = __toESM(require("dotenv"), 1);
|
|
4380
4754
|
import_dotenv.default.config();
|
|
@@ -4520,6 +4894,7 @@ function registerErrorHandler(app) {
|
|
|
4520
4894
|
}
|
|
4521
4895
|
|
|
4522
4896
|
// src/middleware/security.ts
|
|
4897
|
+
init_cjs_shims();
|
|
4523
4898
|
var import_helmet = __toESM(require("@fastify/helmet"), 1);
|
|
4524
4899
|
var import_cors = __toESM(require("@fastify/cors"), 1);
|
|
4525
4900
|
var import_rate_limit = __toESM(require("@fastify/rate-limit"), 1);
|
|
@@ -4579,7 +4954,11 @@ async function registerSecurity(app, options = {}) {
|
|
|
4579
4954
|
}
|
|
4580
4955
|
}
|
|
4581
4956
|
|
|
4957
|
+
// src/modules/swagger/index.ts
|
|
4958
|
+
init_cjs_shims();
|
|
4959
|
+
|
|
4582
4960
|
// src/modules/swagger/swagger.service.ts
|
|
4961
|
+
init_cjs_shims();
|
|
4583
4962
|
var import_swagger = __toESM(require("@fastify/swagger"), 1);
|
|
4584
4963
|
var import_swagger_ui = __toESM(require("@fastify/swagger-ui"), 1);
|
|
4585
4964
|
var defaultConfig3 = {
|
|
@@ -4639,11 +5018,16 @@ async function registerSwagger(app, customConfig) {
|
|
|
4639
5018
|
logger.info("Swagger documentation registered at /docs");
|
|
4640
5019
|
}
|
|
4641
5020
|
|
|
5021
|
+
// src/modules/swagger/schema-builder.ts
|
|
5022
|
+
init_cjs_shims();
|
|
5023
|
+
|
|
4642
5024
|
// src/modules/auth/index.ts
|
|
5025
|
+
init_cjs_shims();
|
|
4643
5026
|
var import_jwt = __toESM(require("@fastify/jwt"), 1);
|
|
4644
5027
|
var import_cookie = __toESM(require("@fastify/cookie"), 1);
|
|
4645
5028
|
|
|
4646
5029
|
// src/modules/auth/auth.service.ts
|
|
5030
|
+
init_cjs_shims();
|
|
4647
5031
|
var import_bcryptjs = __toESM(require("bcryptjs"), 1);
|
|
4648
5032
|
var import_ioredis = require("ioredis");
|
|
4649
5033
|
var AuthService = class {
|
|
@@ -4842,7 +5226,11 @@ function createAuthService(app) {
|
|
|
4842
5226
|
return new AuthService(app);
|
|
4843
5227
|
}
|
|
4844
5228
|
|
|
5229
|
+
// src/modules/auth/auth.controller.ts
|
|
5230
|
+
init_cjs_shims();
|
|
5231
|
+
|
|
4845
5232
|
// src/modules/auth/schemas.ts
|
|
5233
|
+
init_cjs_shims();
|
|
4846
5234
|
var import_zod2 = require("zod");
|
|
4847
5235
|
var loginSchema = import_zod2.z.object({
|
|
4848
5236
|
email: import_zod2.z.string().email("Invalid email address"),
|
|
@@ -4869,6 +5257,7 @@ var changePasswordSchema = import_zod2.z.object({
|
|
|
4869
5257
|
});
|
|
4870
5258
|
|
|
4871
5259
|
// src/utils/response.ts
|
|
5260
|
+
init_cjs_shims();
|
|
4872
5261
|
function success2(reply, data, statusCode = 200) {
|
|
4873
5262
|
const response = {
|
|
4874
5263
|
success: true,
|
|
@@ -4884,6 +5273,7 @@ function noContent(reply) {
|
|
|
4884
5273
|
}
|
|
4885
5274
|
|
|
4886
5275
|
// src/modules/validation/validator.ts
|
|
5276
|
+
init_cjs_shims();
|
|
4887
5277
|
var import_zod3 = require("zod");
|
|
4888
5278
|
function validateBody(schema, data) {
|
|
4889
5279
|
const result = schema.safeParse(data);
|
|
@@ -4902,11 +5292,11 @@ function validateQuery(schema, data) {
|
|
|
4902
5292
|
function formatZodErrors(error2) {
|
|
4903
5293
|
const errors = {};
|
|
4904
5294
|
for (const issue of error2.issues) {
|
|
4905
|
-
const
|
|
4906
|
-
if (!errors[
|
|
4907
|
-
errors[
|
|
5295
|
+
const path11 = issue.path.join(".") || "root";
|
|
5296
|
+
if (!errors[path11]) {
|
|
5297
|
+
errors[path11] = [];
|
|
4908
5298
|
}
|
|
4909
|
-
errors[
|
|
5299
|
+
errors[path11].push(issue.message);
|
|
4910
5300
|
}
|
|
4911
5301
|
return errors;
|
|
4912
5302
|
}
|
|
@@ -5054,7 +5444,11 @@ function createAuthController(authService, userService) {
|
|
|
5054
5444
|
return new AuthController(authService, userService);
|
|
5055
5445
|
}
|
|
5056
5446
|
|
|
5447
|
+
// src/modules/auth/auth.routes.ts
|
|
5448
|
+
init_cjs_shims();
|
|
5449
|
+
|
|
5057
5450
|
// src/modules/auth/auth.middleware.ts
|
|
5451
|
+
init_cjs_shims();
|
|
5058
5452
|
function createAuthMiddleware(authService) {
|
|
5059
5453
|
return async function authenticate(request, _reply) {
|
|
5060
5454
|
const authHeader = request.headers.authorization;
|
|
@@ -5097,7 +5491,14 @@ function registerAuthRoutes(app, controller, authService) {
|
|
|
5097
5491
|
);
|
|
5098
5492
|
}
|
|
5099
5493
|
|
|
5494
|
+
// src/modules/user/user.service.ts
|
|
5495
|
+
init_cjs_shims();
|
|
5496
|
+
|
|
5497
|
+
// src/modules/user/user.repository.ts
|
|
5498
|
+
init_cjs_shims();
|
|
5499
|
+
|
|
5100
5500
|
// src/database/prisma.ts
|
|
5501
|
+
init_cjs_shims();
|
|
5101
5502
|
var import_client = require("@prisma/client");
|
|
5102
5503
|
var prismaClientSingleton = () => {
|
|
5103
5504
|
return new import_client.PrismaClient({
|
|
@@ -5111,6 +5512,7 @@ if (!isProduction()) {
|
|
|
5111
5512
|
}
|
|
5112
5513
|
|
|
5113
5514
|
// src/utils/pagination.ts
|
|
5515
|
+
init_cjs_shims();
|
|
5114
5516
|
var DEFAULT_PAGE = 1;
|
|
5115
5517
|
var DEFAULT_LIMIT = 20;
|
|
5116
5518
|
var MAX_LIMIT = 100;
|
|
@@ -5375,6 +5777,7 @@ function createUserRepository() {
|
|
|
5375
5777
|
}
|
|
5376
5778
|
|
|
5377
5779
|
// src/modules/user/types.ts
|
|
5780
|
+
init_cjs_shims();
|
|
5378
5781
|
var DEFAULT_ROLE_PERMISSIONS = {
|
|
5379
5782
|
user: ["profile:read", "profile:update"],
|
|
5380
5783
|
moderator: [
|
|
@@ -5505,6 +5908,9 @@ function createUserService(repository) {
|
|
|
5505
5908
|
return new UserService(repository || createUserRepository());
|
|
5506
5909
|
}
|
|
5507
5910
|
|
|
5911
|
+
// src/modules/auth/types.ts
|
|
5912
|
+
init_cjs_shims();
|
|
5913
|
+
|
|
5508
5914
|
// src/modules/auth/index.ts
|
|
5509
5915
|
async function registerAuthModule(app) {
|
|
5510
5916
|
await app.register(import_jwt.default, {
|
|
@@ -5524,7 +5930,14 @@ async function registerAuthModule(app) {
|
|
|
5524
5930
|
logger.info("Auth module registered");
|
|
5525
5931
|
}
|
|
5526
5932
|
|
|
5933
|
+
// src/modules/user/index.ts
|
|
5934
|
+
init_cjs_shims();
|
|
5935
|
+
|
|
5936
|
+
// src/modules/user/user.controller.ts
|
|
5937
|
+
init_cjs_shims();
|
|
5938
|
+
|
|
5527
5939
|
// src/modules/user/schemas.ts
|
|
5940
|
+
init_cjs_shims();
|
|
5528
5941
|
var import_zod4 = require("zod");
|
|
5529
5942
|
var userStatusEnum = import_zod4.z.enum(["active", "inactive", "suspended", "banned"]);
|
|
5530
5943
|
var userRoleEnum = import_zod4.z.enum(["user", "admin", "moderator", "super_admin"]);
|
|
@@ -5651,6 +6064,7 @@ function createUserController(userService) {
|
|
|
5651
6064
|
}
|
|
5652
6065
|
|
|
5653
6066
|
// src/modules/user/user.routes.ts
|
|
6067
|
+
init_cjs_shims();
|
|
5654
6068
|
var idParamsSchema = {
|
|
5655
6069
|
type: "object",
|
|
5656
6070
|
properties: {
|
|
@@ -5739,8 +6153,8 @@ async function generateDocs(outputPath = "openapi.json", silent = false) {
|
|
|
5739
6153
|
await registerUserModule(app, authService);
|
|
5740
6154
|
await app.ready();
|
|
5741
6155
|
const spec = app.swagger();
|
|
5742
|
-
const absoluteOutput =
|
|
5743
|
-
await import_promises3.default.mkdir(
|
|
6156
|
+
const absoluteOutput = import_path6.default.resolve(outputPath);
|
|
6157
|
+
await import_promises3.default.mkdir(import_path6.default.dirname(absoluteOutput), { recursive: true });
|
|
5744
6158
|
await import_promises3.default.writeFile(absoluteOutput, JSON.stringify(spec, null, 2), "utf8");
|
|
5745
6159
|
spinner?.succeed(`OpenAPI spec generated at ${absoluteOutput}`);
|
|
5746
6160
|
await app.close();
|
|
@@ -5785,7 +6199,7 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
5785
6199
|
const spinner = (0, import_ora6.default)("Exporting documentation...").start();
|
|
5786
6200
|
try {
|
|
5787
6201
|
const projectRoot = getProjectRoot();
|
|
5788
|
-
const specPath =
|
|
6202
|
+
const specPath = import_path7.default.join(projectRoot, "openapi.json");
|
|
5789
6203
|
try {
|
|
5790
6204
|
await import_promises4.default.access(specPath);
|
|
5791
6205
|
} catch {
|
|
@@ -5812,7 +6226,7 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
5812
6226
|
default:
|
|
5813
6227
|
throw new Error(`Unknown format: ${options.format}`);
|
|
5814
6228
|
}
|
|
5815
|
-
const outPath =
|
|
6229
|
+
const outPath = import_path7.default.join(projectRoot, options.output || defaultName);
|
|
5816
6230
|
await import_promises4.default.writeFile(outPath, output);
|
|
5817
6231
|
spinner.succeed(`Exported to: ${options.output || defaultName}`);
|
|
5818
6232
|
if (options.format === "postman") {
|
|
@@ -5825,8 +6239,8 @@ docsCommand.command("export").description("Export documentation to Postman, Inso
|
|
|
5825
6239
|
});
|
|
5826
6240
|
docsCommand.command("status").description("Show documentation status").action(async () => {
|
|
5827
6241
|
const projectRoot = getProjectRoot();
|
|
5828
|
-
console.log(
|
|
5829
|
-
const specPath =
|
|
6242
|
+
console.log(import_chalk9.default.bold("\n\u{1F4CA} Documentation Status\n"));
|
|
6243
|
+
const specPath = import_path7.default.join(projectRoot, "openapi.json");
|
|
5830
6244
|
try {
|
|
5831
6245
|
const stat2 = await import_promises4.default.stat(specPath);
|
|
5832
6246
|
success(
|
|
@@ -5941,13 +6355,325 @@ function formatDate(date) {
|
|
|
5941
6355
|
});
|
|
5942
6356
|
}
|
|
5943
6357
|
|
|
6358
|
+
// src/cli/commands/list.ts
|
|
6359
|
+
init_cjs_shims();
|
|
6360
|
+
var import_commander6 = require("commander");
|
|
6361
|
+
var import_chalk10 = __toESM(require("chalk"), 1);
|
|
6362
|
+
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
6363
|
+
var AVAILABLE_MODULES2 = {
|
|
6364
|
+
// Core
|
|
6365
|
+
auth: {
|
|
6366
|
+
name: "Authentication",
|
|
6367
|
+
description: "JWT authentication with access/refresh tokens",
|
|
6368
|
+
category: "Core"
|
|
6369
|
+
},
|
|
6370
|
+
users: {
|
|
6371
|
+
name: "User Management",
|
|
6372
|
+
description: "User CRUD with RBAC (roles & permissions)",
|
|
6373
|
+
category: "Core"
|
|
6374
|
+
},
|
|
6375
|
+
email: {
|
|
6376
|
+
name: "Email Service",
|
|
6377
|
+
description: "SMTP email with templates (Handlebars)",
|
|
6378
|
+
category: "Core"
|
|
6379
|
+
},
|
|
6380
|
+
// Security
|
|
6381
|
+
mfa: {
|
|
6382
|
+
name: "MFA/TOTP",
|
|
6383
|
+
description: "Two-factor authentication with QR codes",
|
|
6384
|
+
category: "Security"
|
|
6385
|
+
},
|
|
6386
|
+
oauth: {
|
|
6387
|
+
name: "OAuth",
|
|
6388
|
+
description: "Social login (Google, GitHub, Facebook, Twitter, Apple)",
|
|
6389
|
+
category: "Security"
|
|
6390
|
+
},
|
|
6391
|
+
"rate-limit": {
|
|
6392
|
+
name: "Rate Limiting",
|
|
6393
|
+
description: "Advanced rate limiting with multiple algorithms",
|
|
6394
|
+
category: "Security"
|
|
6395
|
+
},
|
|
6396
|
+
// Data & Storage
|
|
6397
|
+
cache: {
|
|
6398
|
+
name: "Redis Cache",
|
|
6399
|
+
description: "Redis caching with TTL & invalidation",
|
|
6400
|
+
category: "Data & Storage"
|
|
6401
|
+
},
|
|
6402
|
+
upload: {
|
|
6403
|
+
name: "File Upload",
|
|
6404
|
+
description: "File upload with local/S3/Cloudinary storage",
|
|
6405
|
+
category: "Data & Storage"
|
|
6406
|
+
},
|
|
6407
|
+
search: {
|
|
6408
|
+
name: "Search",
|
|
6409
|
+
description: "Full-text search with Elasticsearch/Meilisearch",
|
|
6410
|
+
category: "Data & Storage"
|
|
6411
|
+
},
|
|
6412
|
+
// Communication
|
|
6413
|
+
notification: {
|
|
6414
|
+
name: "Notifications",
|
|
6415
|
+
description: "Email, SMS, Push notifications",
|
|
6416
|
+
category: "Communication"
|
|
6417
|
+
},
|
|
6418
|
+
webhook: {
|
|
6419
|
+
name: "Webhooks",
|
|
6420
|
+
description: "Outgoing webhooks with HMAC signatures & retry",
|
|
6421
|
+
category: "Communication"
|
|
6422
|
+
},
|
|
6423
|
+
websocket: {
|
|
6424
|
+
name: "WebSockets",
|
|
6425
|
+
description: "Real-time communication with Socket.io",
|
|
6426
|
+
category: "Communication"
|
|
6427
|
+
},
|
|
6428
|
+
// Background Processing
|
|
6429
|
+
queue: {
|
|
6430
|
+
name: "Queue/Jobs",
|
|
6431
|
+
description: "Background jobs with Bull/BullMQ & cron scheduling",
|
|
6432
|
+
category: "Background Processing"
|
|
6433
|
+
},
|
|
6434
|
+
"media-processing": {
|
|
6435
|
+
name: "Media Processing",
|
|
6436
|
+
description: "Image/video processing with FFmpeg",
|
|
6437
|
+
category: "Background Processing"
|
|
6438
|
+
},
|
|
6439
|
+
// Monitoring & Analytics
|
|
6440
|
+
audit: {
|
|
6441
|
+
name: "Audit Logs",
|
|
6442
|
+
description: "Activity logging and audit trail",
|
|
6443
|
+
category: "Monitoring & Analytics"
|
|
6444
|
+
},
|
|
6445
|
+
analytics: {
|
|
6446
|
+
name: "Analytics/Metrics",
|
|
6447
|
+
description: "Prometheus metrics & event tracking",
|
|
6448
|
+
category: "Monitoring & Analytics"
|
|
6449
|
+
},
|
|
6450
|
+
// Internationalization
|
|
6451
|
+
i18n: {
|
|
6452
|
+
name: "i18n/Localization",
|
|
6453
|
+
description: "Multi-language support with 7+ locales",
|
|
6454
|
+
category: "Internationalization"
|
|
6455
|
+
},
|
|
6456
|
+
// API Management
|
|
6457
|
+
"feature-flag": {
|
|
6458
|
+
name: "Feature Flags",
|
|
6459
|
+
description: "A/B testing & progressive rollout",
|
|
6460
|
+
category: "API Management"
|
|
6461
|
+
},
|
|
6462
|
+
"api-versioning": {
|
|
6463
|
+
name: "API Versioning",
|
|
6464
|
+
description: "Multiple API versions support",
|
|
6465
|
+
category: "API Management"
|
|
6466
|
+
},
|
|
6467
|
+
// Payments
|
|
6468
|
+
payment: {
|
|
6469
|
+
name: "Payments",
|
|
6470
|
+
description: "Payment processing (Stripe, PayPal, Mobile Money)",
|
|
6471
|
+
category: "Payments"
|
|
6472
|
+
}
|
|
6473
|
+
};
|
|
6474
|
+
async function getInstalledModules() {
|
|
6475
|
+
try {
|
|
6476
|
+
const modulesDir = getModulesDir();
|
|
6477
|
+
const entries = await import_promises5.default.readdir(modulesDir, { withFileTypes: true });
|
|
6478
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6479
|
+
} catch {
|
|
6480
|
+
return [];
|
|
6481
|
+
}
|
|
6482
|
+
}
|
|
6483
|
+
function isServercraftProject() {
|
|
6484
|
+
try {
|
|
6485
|
+
getProjectRoot();
|
|
6486
|
+
return true;
|
|
6487
|
+
} catch {
|
|
6488
|
+
return false;
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6491
|
+
var listCommand = new import_commander6.Command("list").alias("ls").description("List available and installed modules").option("-a, --available", "Show only available modules").option("-i, --installed", "Show only installed modules").option("-c, --category <category>", "Filter by category").option("--json", "Output as JSON").action(
|
|
6492
|
+
async (options) => {
|
|
6493
|
+
const installedModules = await getInstalledModules();
|
|
6494
|
+
const isProject = isServercraftProject();
|
|
6495
|
+
if (options.json) {
|
|
6496
|
+
const output = {
|
|
6497
|
+
available: Object.entries(AVAILABLE_MODULES2).map(([key, mod]) => ({
|
|
6498
|
+
id: key,
|
|
6499
|
+
...mod,
|
|
6500
|
+
installed: installedModules.includes(key)
|
|
6501
|
+
}))
|
|
6502
|
+
};
|
|
6503
|
+
if (isProject) {
|
|
6504
|
+
output.installed = installedModules;
|
|
6505
|
+
}
|
|
6506
|
+
console.log(JSON.stringify(output, null, 2));
|
|
6507
|
+
return;
|
|
6508
|
+
}
|
|
6509
|
+
const byCategory = {};
|
|
6510
|
+
for (const [key, mod] of Object.entries(AVAILABLE_MODULES2)) {
|
|
6511
|
+
if (options.category && mod.category.toLowerCase() !== options.category.toLowerCase()) {
|
|
6512
|
+
continue;
|
|
6513
|
+
}
|
|
6514
|
+
if (!byCategory[mod.category]) {
|
|
6515
|
+
byCategory[mod.category] = [];
|
|
6516
|
+
}
|
|
6517
|
+
byCategory[mod.category].push({
|
|
6518
|
+
id: key,
|
|
6519
|
+
name: mod.name,
|
|
6520
|
+
description: mod.description,
|
|
6521
|
+
installed: installedModules.includes(key)
|
|
6522
|
+
});
|
|
6523
|
+
}
|
|
6524
|
+
if (options.installed) {
|
|
6525
|
+
if (!isProject) {
|
|
6526
|
+
console.log(import_chalk10.default.yellow("\n\u26A0 Not in a Servcraft project directory\n"));
|
|
6527
|
+
return;
|
|
6528
|
+
}
|
|
6529
|
+
console.log(import_chalk10.default.bold("\n\u{1F4E6} Installed Modules:\n"));
|
|
6530
|
+
if (installedModules.length === 0) {
|
|
6531
|
+
console.log(import_chalk10.default.gray(" No modules installed yet.\n"));
|
|
6532
|
+
console.log(` Run ${import_chalk10.default.cyan("servcraft add <module>")} to add a module.
|
|
6533
|
+
`);
|
|
6534
|
+
return;
|
|
6535
|
+
}
|
|
6536
|
+
for (const modId of installedModules) {
|
|
6537
|
+
const mod = AVAILABLE_MODULES2[modId];
|
|
6538
|
+
if (mod) {
|
|
6539
|
+
console.log(` ${import_chalk10.default.green("\u2713")} ${import_chalk10.default.cyan(modId.padEnd(18))} ${mod.name}`);
|
|
6540
|
+
} else {
|
|
6541
|
+
console.log(
|
|
6542
|
+
` ${import_chalk10.default.green("\u2713")} ${import_chalk10.default.cyan(modId.padEnd(18))} ${import_chalk10.default.gray("(custom module)")}`
|
|
6543
|
+
);
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
console.log(`
|
|
6547
|
+
Total: ${import_chalk10.default.bold(installedModules.length)} module(s) installed
|
|
6548
|
+
`);
|
|
6549
|
+
return;
|
|
6550
|
+
}
|
|
6551
|
+
console.log(import_chalk10.default.bold("\n\u{1F4E6} Available Modules\n"));
|
|
6552
|
+
if (isProject) {
|
|
6553
|
+
console.log(
|
|
6554
|
+
import_chalk10.default.gray(` ${import_chalk10.default.green("\u2713")} = installed ${import_chalk10.default.dim("\u25CB")} = not installed
|
|
6555
|
+
`)
|
|
6556
|
+
);
|
|
6557
|
+
}
|
|
6558
|
+
for (const [category, modules] of Object.entries(byCategory)) {
|
|
6559
|
+
console.log(import_chalk10.default.bold.blue(` ${category}`));
|
|
6560
|
+
console.log(import_chalk10.default.gray(" " + "\u2500".repeat(40)));
|
|
6561
|
+
for (const mod of modules) {
|
|
6562
|
+
const status = isProject ? mod.installed ? import_chalk10.default.green("\u2713") : import_chalk10.default.dim("\u25CB") : " ";
|
|
6563
|
+
const nameColor = mod.installed ? import_chalk10.default.green : import_chalk10.default.cyan;
|
|
6564
|
+
console.log(` ${status} ${nameColor(mod.id.padEnd(18))} ${mod.name}`);
|
|
6565
|
+
console.log(` ${import_chalk10.default.gray(mod.description)}`);
|
|
6566
|
+
}
|
|
6567
|
+
console.log();
|
|
6568
|
+
}
|
|
6569
|
+
const totalAvailable = Object.keys(AVAILABLE_MODULES2).length;
|
|
6570
|
+
const totalInstalled = installedModules.filter((m) => AVAILABLE_MODULES2[m]).length;
|
|
6571
|
+
console.log(import_chalk10.default.gray("\u2500".repeat(50)));
|
|
6572
|
+
console.log(
|
|
6573
|
+
` ${import_chalk10.default.bold(totalAvailable)} modules available` + (isProject ? ` | ${import_chalk10.default.green.bold(totalInstalled)} installed` : "")
|
|
6574
|
+
);
|
|
6575
|
+
console.log();
|
|
6576
|
+
console.log(import_chalk10.default.bold(" Usage:"));
|
|
6577
|
+
console.log(` ${import_chalk10.default.yellow("servcraft add <module>")} Add a module`);
|
|
6578
|
+
console.log(` ${import_chalk10.default.yellow("servcraft list --installed")} Show installed only`);
|
|
6579
|
+
console.log(` ${import_chalk10.default.yellow("servcraft list --category Security")} Filter by category`);
|
|
6580
|
+
console.log();
|
|
6581
|
+
}
|
|
6582
|
+
);
|
|
6583
|
+
|
|
6584
|
+
// src/cli/commands/remove.ts
|
|
6585
|
+
init_cjs_shims();
|
|
6586
|
+
var import_commander7 = require("commander");
|
|
6587
|
+
var import_path8 = __toESM(require("path"), 1);
|
|
6588
|
+
var import_ora7 = __toESM(require("ora"), 1);
|
|
6589
|
+
var import_chalk11 = __toESM(require("chalk"), 1);
|
|
6590
|
+
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
6591
|
+
var import_inquirer4 = __toESM(require("inquirer"), 1);
|
|
6592
|
+
init_error_handler();
|
|
6593
|
+
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
|
+
const projectError = validateProject();
|
|
6595
|
+
if (projectError) {
|
|
6596
|
+
displayError(projectError);
|
|
6597
|
+
return;
|
|
6598
|
+
}
|
|
6599
|
+
console.log(import_chalk11.default.bold.cyan("\n\u{1F5D1}\uFE0F ServCraft Module Removal\n"));
|
|
6600
|
+
const moduleDir = import_path8.default.join(getModulesDir(), moduleName);
|
|
6601
|
+
try {
|
|
6602
|
+
const exists = await import_promises6.default.access(moduleDir).then(() => true).catch(() => false);
|
|
6603
|
+
if (!exists) {
|
|
6604
|
+
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
|
+
)
|
|
6612
|
+
);
|
|
6613
|
+
return;
|
|
6614
|
+
}
|
|
6615
|
+
const files = await import_promises6.default.readdir(moduleDir);
|
|
6616
|
+
const fileCount = files.length;
|
|
6617
|
+
if (!options?.yes) {
|
|
6618
|
+
console.log(import_chalk11.default.yellow(`\u26A0 This will remove the "${moduleName}" module:`));
|
|
6619
|
+
console.log(import_chalk11.default.gray(` Directory: ${moduleDir}`));
|
|
6620
|
+
console.log(import_chalk11.default.gray(` Files: ${fileCount} file(s)`));
|
|
6621
|
+
console.log();
|
|
6622
|
+
const { confirm } = await import_inquirer4.default.prompt([
|
|
6623
|
+
{
|
|
6624
|
+
type: "confirm",
|
|
6625
|
+
name: "confirm",
|
|
6626
|
+
message: "Are you sure you want to remove this module?",
|
|
6627
|
+
default: false
|
|
6628
|
+
}
|
|
6629
|
+
]);
|
|
6630
|
+
if (!confirm) {
|
|
6631
|
+
console.log(import_chalk11.default.yellow("\n\u2716 Removal cancelled\n"));
|
|
6632
|
+
return;
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
const spinner = (0, import_ora7.default)("Removing module...").start();
|
|
6636
|
+
await import_promises6.default.rm(moduleDir, { recursive: true, force: true });
|
|
6637
|
+
spinner.succeed(`Module "${moduleName}" removed successfully!`);
|
|
6638
|
+
console.log("\n" + import_chalk11.default.bold("\u2713 Removed:"));
|
|
6639
|
+
success(` src/modules/${moduleName}/ (${fileCount} files)`);
|
|
6640
|
+
if (!options?.keepEnv) {
|
|
6641
|
+
console.log("\n" + import_chalk11.default.bold("\u{1F4CC} Manual cleanup needed:"));
|
|
6642
|
+
info(" 1. Remove environment variables related to this module from .env");
|
|
6643
|
+
info(" 2. Remove module imports from your main app file");
|
|
6644
|
+
info(" 3. Remove related database migrations if any");
|
|
6645
|
+
info(" 4. Update your routes if they reference this module");
|
|
6646
|
+
} else {
|
|
6647
|
+
console.log("\n" + import_chalk11.default.bold("\u{1F4CC} Manual cleanup needed:"));
|
|
6648
|
+
info(" 1. Environment variables were kept (--keep-env flag)");
|
|
6649
|
+
info(" 2. Remove module imports from your main app file");
|
|
6650
|
+
info(" 3. Update your routes if they reference this module");
|
|
6651
|
+
}
|
|
6652
|
+
console.log();
|
|
6653
|
+
} catch (err) {
|
|
6654
|
+
error(err instanceof Error ? err.message : String(err));
|
|
6655
|
+
console.log();
|
|
6656
|
+
}
|
|
6657
|
+
});
|
|
6658
|
+
|
|
6659
|
+
// src/cli/commands/doctor.ts
|
|
6660
|
+
init_cjs_shims();
|
|
6661
|
+
var import_commander8 = require("commander");
|
|
6662
|
+
var import_chalk12 = __toESM(require("chalk"), 1);
|
|
6663
|
+
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"));
|
|
6665
|
+
});
|
|
6666
|
+
|
|
5944
6667
|
// src/cli/index.ts
|
|
5945
|
-
var program = new
|
|
6668
|
+
var program = new import_commander9.Command();
|
|
5946
6669
|
program.name("servcraft").description("Servcraft - A modular Node.js backend framework CLI").version("0.1.0");
|
|
5947
6670
|
program.addCommand(initCommand);
|
|
5948
6671
|
program.addCommand(generateCommand);
|
|
5949
6672
|
program.addCommand(addModuleCommand);
|
|
5950
6673
|
program.addCommand(dbCommand);
|
|
5951
6674
|
program.addCommand(docsCommand);
|
|
6675
|
+
program.addCommand(listCommand);
|
|
6676
|
+
program.addCommand(removeCommand);
|
|
6677
|
+
program.addCommand(doctorCommand);
|
|
5952
6678
|
program.parse();
|
|
5953
6679
|
//# sourceMappingURL=index.cjs.map
|