add-nest-auth 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -36,6 +36,457 @@ var init_cjs_shims = __esm({
36
36
  }
37
37
  });
38
38
 
39
+ // src/analyzer/orm-detector.ts
40
+ async function detectORM(packageJson) {
41
+ const dependencies = {
42
+ ...packageJson.dependencies,
43
+ ...packageJson.devDependencies
44
+ };
45
+ if (dependencies["@nestjs/typeorm"] || dependencies["typeorm"]) {
46
+ return "typeorm";
47
+ }
48
+ if (dependencies["@prisma/client"] || dependencies["prisma"]) {
49
+ return "prisma";
50
+ }
51
+ if (dependencies["@nestjs/mongoose"] || dependencies["mongoose"]) {
52
+ return "mongoose";
53
+ }
54
+ return "none";
55
+ }
56
+ function detectDatabase(packageJson, orm) {
57
+ const dependencies = {
58
+ ...packageJson.dependencies,
59
+ ...packageJson.devDependencies
60
+ };
61
+ if (orm === "typeorm") {
62
+ if (dependencies["pg"]) return "postgres";
63
+ if (dependencies["mysql2"] || dependencies["mysql"]) return "mysql";
64
+ if (dependencies["sqlite3"]) return "sqlite";
65
+ if (dependencies["mongodb"]) return "mongodb";
66
+ }
67
+ if (orm === "prisma") {
68
+ return void 0;
69
+ }
70
+ if (orm === "mongoose") {
71
+ return "mongodb";
72
+ }
73
+ return void 0;
74
+ }
75
+ var init_orm_detector = __esm({
76
+ "src/analyzer/orm-detector.ts"() {
77
+ "use strict";
78
+ init_cjs_shims();
79
+ }
80
+ });
81
+
82
+ // src/analyzer/project-detector.ts
83
+ async function detectProject(cwd = process.cwd()) {
84
+ const detector = new ProjectDetector(cwd);
85
+ return detector.detectProject();
86
+ }
87
+ var path, fs, ProjectDetector;
88
+ var init_project_detector = __esm({
89
+ "src/analyzer/project-detector.ts"() {
90
+ "use strict";
91
+ init_cjs_shims();
92
+ path = __toESM(require("path"));
93
+ fs = __toESM(require("fs-extra"));
94
+ init_orm_detector();
95
+ ProjectDetector = class {
96
+ constructor(cwd) {
97
+ this.cwd = cwd;
98
+ }
99
+ /**
100
+ * Detect and validate a NestJS project
101
+ */
102
+ async detectProject() {
103
+ const errors = [];
104
+ const root = this.cwd;
105
+ const packageJsonPath = path.join(root, "package.json");
106
+ if (!await fs.pathExists(packageJsonPath)) {
107
+ errors.push("package.json not found");
108
+ return this.createInvalidProject(root, errors);
109
+ }
110
+ const packageJson = await this.readPackageJson(packageJsonPath);
111
+ if (!packageJson) {
112
+ errors.push("Failed to read package.json");
113
+ return this.createInvalidProject(root, errors);
114
+ }
115
+ const hasNestCore = packageJson.dependencies?.["@nestjs/core"];
116
+ const hasNestCommon = packageJson.dependencies?.["@nestjs/common"];
117
+ if (!hasNestCore || !hasNestCommon) {
118
+ errors.push("Not a NestJS project (missing @nestjs/core or @nestjs/common)");
119
+ return this.createInvalidProject(root, errors);
120
+ }
121
+ const nestCliConfigPath = path.join(root, "nest-cli.json");
122
+ const nestCliConfig = await this.readNestCliConfig(nestCliConfigPath);
123
+ const sourceRoot = nestCliConfig?.sourceRoot || "src";
124
+ const appModulePath = path.join(root, sourceRoot, "app.module.ts");
125
+ if (!await fs.pathExists(appModulePath)) {
126
+ errors.push(`app.module.ts not found at ${sourceRoot}/app.module.ts`);
127
+ return this.createInvalidProject(root, errors);
128
+ }
129
+ const mainTsPath = path.join(root, sourceRoot, "main.ts");
130
+ const orm = await detectORM(packageJson);
131
+ const database = detectDatabase(packageJson, orm);
132
+ const authModulePath = path.join(root, sourceRoot, "auth");
133
+ const authExists = await fs.pathExists(authModulePath);
134
+ return {
135
+ authExists,
136
+ root,
137
+ sourceRoot,
138
+ appModulePath,
139
+ mainTsPath,
140
+ packageJsonPath,
141
+ nestCliConfigPath,
142
+ orm,
143
+ database,
144
+ nestVersion: packageJson.dependencies?.["@nestjs/core"],
145
+ typescriptVersion: packageJson.devDependencies?.["typescript"],
146
+ isValid: errors.length === 0,
147
+ errors
148
+ };
149
+ }
150
+ /**
151
+ * Read and parse package.json
152
+ */
153
+ async readPackageJson(packageJsonPath) {
154
+ try {
155
+ const content = await fs.readFile(packageJsonPath, "utf-8");
156
+ return JSON.parse(content);
157
+ } catch (error) {
158
+ return null;
159
+ }
160
+ }
161
+ /**
162
+ * Read and parse nest-cli.json
163
+ */
164
+ async readNestCliConfig(nestCliConfigPath) {
165
+ try {
166
+ if (!await fs.pathExists(nestCliConfigPath)) {
167
+ return null;
168
+ }
169
+ const content = await fs.readFile(nestCliConfigPath, "utf-8");
170
+ return JSON.parse(content);
171
+ } catch (error) {
172
+ return null;
173
+ }
174
+ }
175
+ /**
176
+ * Create invalid project info object
177
+ */
178
+ createInvalidProject(root, errors) {
179
+ return {
180
+ root,
181
+ sourceRoot: "src",
182
+ appModulePath: path.join(root, "src", "app.module.ts"),
183
+ mainTsPath: path.join(root, "src", "main.ts"),
184
+ packageJsonPath: path.join(root, "package.json"),
185
+ nestCliConfigPath: path.join(root, "nest-cli.json"),
186
+ orm: "none",
187
+ isValid: false,
188
+ errors
189
+ };
190
+ }
191
+ };
192
+ }
193
+ });
194
+
195
+ // src/analyzer/index.ts
196
+ var init_analyzer = __esm({
197
+ "src/analyzer/index.ts"() {
198
+ "use strict";
199
+ init_cjs_shims();
200
+ init_project_detector();
201
+ init_orm_detector();
202
+ }
203
+ });
204
+
205
+ // src/config/utils.ts
206
+ function generateSecret(length = 32) {
207
+ return (0, import_crypto.randomBytes)(length).toString("base64");
208
+ }
209
+ var import_crypto;
210
+ var init_utils = __esm({
211
+ "src/config/utils.ts"() {
212
+ "use strict";
213
+ init_cjs_shims();
214
+ import_crypto = require("crypto");
215
+ }
216
+ });
217
+
218
+ // src/cli/prompts.ts
219
+ async function promptConfig(detectedORM, detectedDB) {
220
+ const dbLabel = detectedDB ? ` with ${detectedDB.charAt(0).toUpperCase() + detectedDB.slice(1)}` : "";
221
+ const answers = await import_inquirer.default.prompt([
222
+ {
223
+ type: "list",
224
+ name: "strategy",
225
+ message: "Choose authentication strategy:",
226
+ choices: [
227
+ { name: "JWT Authentication (Recommended)", value: "jwt" },
228
+ { name: "OAuth 2.0 (Google, GitHub) [Coming soon]", value: "oauth", disabled: true },
229
+ { name: "Session-based (Traditional) [Coming soon]", value: "session", disabled: true }
230
+ ],
231
+ default: "jwt"
232
+ },
233
+ {
234
+ type: "confirm",
235
+ name: "enableRBAC",
236
+ message: "Enable Role-Based Access Control (RBAC)?",
237
+ default: true
238
+ },
239
+ {
240
+ type: "checkbox",
241
+ name: "roles",
242
+ message: "Select default roles:",
243
+ choices: [
244
+ { name: "Admin", value: "Admin", checked: true },
245
+ { name: "User", value: "User", checked: true },
246
+ { name: "Moderator", value: "Moderator", checked: false },
247
+ { name: "Guest", value: "Guest", checked: false }
248
+ ],
249
+ when: (answers2) => answers2.enableRBAC,
250
+ validate: (input) => {
251
+ if (input.length === 0) {
252
+ return "Please select at least one role";
253
+ }
254
+ return true;
255
+ }
256
+ },
257
+ {
258
+ type: "confirm",
259
+ name: "refreshTokens",
260
+ message: "Enable Refresh Token rotation?",
261
+ default: true
262
+ },
263
+ {
264
+ type: "list",
265
+ name: "accessExpiration",
266
+ message: "JWT Access Token expiration:",
267
+ choices: [
268
+ { name: "15 minutes", value: "15m" },
269
+ { name: "30 minutes", value: "30m" },
270
+ { name: "1 hour (Recommended)", value: "1h" },
271
+ { name: "4 hours", value: "4h" },
272
+ { name: "1 day", value: "1d" }
273
+ ],
274
+ default: "1h"
275
+ },
276
+ {
277
+ type: "list",
278
+ name: "refreshExpiration",
279
+ message: "JWT Refresh Token expiration:",
280
+ choices: [
281
+ { name: "7 days (Recommended)", value: "7d" },
282
+ { name: "30 days", value: "30d" },
283
+ { name: "90 days", value: "90d" },
284
+ { name: "1 year", value: "1y" }
285
+ ],
286
+ default: "7d",
287
+ when: (answers2) => answers2.refreshTokens
288
+ },
289
+ {
290
+ type: "confirm",
291
+ name: "enableRateLimiting",
292
+ message: "Enable rate limiting on auth endpoints? (recommended)",
293
+ default: true
294
+ },
295
+ {
296
+ type: "confirm",
297
+ name: "useDetectedORM",
298
+ message: `Detected ${detectedORM.toUpperCase()}${dbLabel}. Use it?`,
299
+ default: true,
300
+ when: () => detectedORM !== "none"
301
+ },
302
+ {
303
+ type: "list",
304
+ name: "database",
305
+ message: "Select database:",
306
+ choices: [
307
+ { name: "PostgreSQL (Recommended)", value: "postgres" },
308
+ { name: "MySQL", value: "mysql" },
309
+ { name: "SQLite (for testing)", value: "sqlite" },
310
+ { name: "MongoDB", value: "mongodb" }
311
+ ],
312
+ default: "postgres",
313
+ when: (answers2) => detectedORM === "none" || !answers2.useDetectedORM
314
+ },
315
+ {
316
+ type: "confirm",
317
+ name: "autoInstall",
318
+ message: "Auto-install dependencies after generation?",
319
+ default: true
320
+ }
321
+ ]);
322
+ return answers;
323
+ }
324
+ function getDefaultAnswers(detectedORM, detectedDB) {
325
+ return {
326
+ strategy: "jwt",
327
+ enableRBAC: true,
328
+ roles: ["Admin", "User"],
329
+ refreshTokens: true,
330
+ accessExpiration: "1h",
331
+ refreshExpiration: "7d",
332
+ enableRateLimiting: true,
333
+ useDetectedORM: true,
334
+ database: detectedDB || "postgres",
335
+ autoInstall: true
336
+ };
337
+ }
338
+ function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB) {
339
+ const config = {
340
+ projectName,
341
+ sourceRoot,
342
+ strategy: answers.strategy,
343
+ rbac: {
344
+ enabled: answers.enableRBAC,
345
+ roles: answers.roles || []
346
+ },
347
+ orm: answers.useDetectedORM !== false ? detectedORM : "typeorm",
348
+ database: answers.database || detectedDB || "postgres",
349
+ features: {
350
+ refreshTokens: answers.refreshTokens,
351
+ rateLimiting: answers.enableRateLimiting
352
+ },
353
+ jwt: {
354
+ secret: generateSecret(),
355
+ accessExpiration: answers.accessExpiration,
356
+ refreshExpiration: answers.refreshExpiration || "7d"
357
+ },
358
+ autoInstall: answers.autoInstall,
359
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
360
+ generatorVersion: "1.1.0"
361
+ };
362
+ return config;
363
+ }
364
+ var import_inquirer;
365
+ var init_prompts = __esm({
366
+ "src/cli/prompts.ts"() {
367
+ "use strict";
368
+ init_cjs_shims();
369
+ import_inquirer = __toESM(require("inquirer"));
370
+ init_utils();
371
+ }
372
+ });
373
+
374
+ // src/cli/ui.ts
375
+ function getVersion() {
376
+ try {
377
+ const packageJsonPath = (0, import_path.join)(__dirname, "../package.json");
378
+ const packageJson = JSON.parse((0, import_fs.readFileSync)(packageJsonPath, "utf-8"));
379
+ return packageJson.version;
380
+ } catch (error) {
381
+ return "1.0.0";
382
+ }
383
+ }
384
+ function showBanner() {
385
+ console.log(import_chalk.default.cyan(`
386
+ ___ _ _ __ __
387
+ / _ \\ | | | | | \\/ |
388
+ / /_\\ \\_ _ | |_| |__ | \\ / | ___
389
+ | _ | | | || __| '_ \\ | |\\/| |/ _ \\
390
+ | | | | |_| || |_| | | | | | | | __/
391
+ \\_| |_/\\__,_| \\__|_| |_| \\_| |_/\\___|
392
+ `));
393
+ console.log(import_chalk.default.bold(`\u{1F510} NestJS Authentication Module Generator v${getVersion()}`));
394
+ console.log();
395
+ }
396
+ function showProjectInfo(info) {
397
+ console.log(import_chalk.default.green("\u2713"), `Detected NestJS ${info.nestVersion || "project"}`);
398
+ if (info.orm !== "none") {
399
+ console.log(import_chalk.default.green("\u2713"), `Found ${info.orm.toUpperCase()}`);
400
+ }
401
+ console.log(import_chalk.default.green("\u2713"), `Source directory: ${info.sourceRoot}/`);
402
+ console.log(import_chalk.default.green("\u2713"), "No existing auth module found");
403
+ console.log();
404
+ }
405
+ function showError(message, errors) {
406
+ console.log();
407
+ console.log(import_chalk.default.red("\u274C Error:"), import_chalk.default.bold(message));
408
+ if (errors && errors.length > 0) {
409
+ console.log();
410
+ errors.forEach((error) => {
411
+ console.log(import_chalk.default.red(" \u2022"), error);
412
+ });
413
+ }
414
+ console.log();
415
+ }
416
+ function showNestJSHelp() {
417
+ console.log(import_chalk.default.yellow("To create a new NestJS project:"));
418
+ console.log();
419
+ console.log(import_chalk.default.cyan(" npm i -g @nestjs/cli"));
420
+ console.log(import_chalk.default.cyan(" nest new my-project"));
421
+ console.log();
422
+ }
423
+ function showSuccess(stats) {
424
+ console.log();
425
+ console.log(import_chalk.default.green.bold("\u{1F389} Success!"), "Authentication module generated.");
426
+ console.log();
427
+ console.log(import_chalk.default.bold("\u{1F4C1} Files created:"));
428
+ console.log(` \u2022 ${stats.filesCreated} new files in src/auth/ and src/users/`);
429
+ console.log(` \u2022 Updated src/app.module.ts`);
430
+ console.log(` \u2022 Updated package.json`);
431
+ console.log();
432
+ console.log(import_chalk.default.bold("\u{1F4E6} Dependencies added:"));
433
+ console.log(` \u2022 @nestjs/jwt, @nestjs/passport, @nestjs/config`);
434
+ console.log(` \u2022 passport, passport-jwt, passport-local`);
435
+ console.log(` \u2022 bcrypt, class-validator, class-transformer`);
436
+ console.log(` \u2022 ${stats.dependenciesAdded} packages total`);
437
+ console.log();
438
+ console.log(import_chalk.default.bold("\u{1F510} JWT Configuration:"));
439
+ console.log(` \u2022 Access token: ${stats.jwt.accessExpiration}`);
440
+ if (stats.jwt.refreshExpiration) {
441
+ console.log(` \u2022 Refresh token: ${stats.jwt.refreshExpiration}`);
442
+ }
443
+ console.log(` \u2022 Secret: Auto-generated (see .env)`);
444
+ console.log();
445
+ console.log(import_chalk.default.bold("\u{1F4CB} Next steps:"));
446
+ console.log(import_chalk.default.cyan(" 1. Review .env file (auto-generated with secure secret)"));
447
+ console.log(import_chalk.default.gray(" # .env.example is also provided as a git-safe reference"));
448
+ console.log();
449
+ console.log(import_chalk.default.cyan(" 2. Create database migration (if using TypeORM)"));
450
+ console.log(import_chalk.default.gray(" npm run migration:generate -- src/migrations/CreateUserTable"));
451
+ console.log(import_chalk.default.gray(" npm run migration:run"));
452
+ console.log();
453
+ console.log(import_chalk.default.cyan(" 3. Start your NestJS app"));
454
+ console.log(import_chalk.default.gray(" npm run start:dev"));
455
+ console.log();
456
+ console.log(import_chalk.default.cyan(" 4. Test authentication endpoints"));
457
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/register"));
458
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/login"));
459
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/refresh"));
460
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/logout (requires JWT)"));
461
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/logout-all (requires JWT)"));
462
+ console.log(import_chalk.default.gray(" GET http://localhost:3000/users/profile (requires JWT)"));
463
+ console.log();
464
+ console.log(import_chalk.default.bold("\u{1F4D6} Full documentation:"), "src/auth/README.md");
465
+ console.log();
466
+ console.log(import_chalk.default.bold("\u{1F4A1} Tips:"));
467
+ console.log(" \u2022 Use @Public() decorator for routes that don't require auth");
468
+ console.log(" \u2022 Use @Roles('Admin') to restrict routes by role");
469
+ console.log(" \u2022 Access current user with @CurrentUser() decorator");
470
+ console.log();
471
+ }
472
+ function createSpinner(text) {
473
+ return (0, import_ora.default)({
474
+ text,
475
+ color: "cyan"
476
+ });
477
+ }
478
+ var import_chalk, import_ora, import_fs, import_path;
479
+ var init_ui = __esm({
480
+ "src/cli/ui.ts"() {
481
+ "use strict";
482
+ init_cjs_shims();
483
+ import_chalk = __toESM(require("chalk"));
484
+ import_ora = __toESM(require("ora"));
485
+ import_fs = require("fs");
486
+ import_path = require("path");
487
+ }
488
+ });
489
+
39
490
  // src/generator/template-engine.ts
40
491
  var import_handlebars, path2, fs2, TemplateEngine;
41
492
  var init_template_engine = __esm({
@@ -321,6 +772,7 @@ var init_generator = __esm({
321
772
  }
322
773
  plan.push(
323
774
  { template: "shared/env.template.hbs", output: ".env.example" },
775
+ { template: "shared/env.hbs", output: ".env" },
324
776
  { template: "shared/README.auth.md.hbs", output: `${config.sourceRoot}/auth/README.md` },
325
777
  { template: "shared/main.ts.snippet.hbs", output: "main.ts.example" }
326
778
  );
@@ -456,20 +908,27 @@ var init_ast_updater = __esm({
456
908
  throw new Error("imports is not an array");
457
909
  }
458
910
  const existingModules = this.getExistingModuleNames(importsArray);
911
+ const existingElements = importsArray.getElements().map((e) => e.getText());
912
+ const allElements = [...existingElements];
459
913
  if (!existingModules.has("ConfigModule")) {
460
- importsArray.addElement("ConfigModule.forRoot({ isGlobal: true })");
914
+ allElements.push("ConfigModule.forRoot({ isGlobal: true })");
461
915
  }
462
916
  if (config && config.orm === "typeorm" && !existingModules.has("TypeOrmModule")) {
463
917
  const entities = config.features.refreshTokens ? "[User, RefreshToken]" : "[User]";
464
- const typeOrmConfig = this.buildTypeOrmConfig(config.database, entities);
465
- importsArray.addElement(typeOrmConfig);
918
+ allElements.push(this.buildTypeOrmConfig(config.database, entities));
466
919
  }
467
920
  if (!existingModules.has("AuthModule")) {
468
- importsArray.addElement("AuthModule");
921
+ allElements.push("AuthModule");
469
922
  }
470
923
  if (!existingModules.has("UsersModule")) {
471
- importsArray.addElement("UsersModule");
924
+ allElements.push("UsersModule");
472
925
  }
926
+ const indent = " ";
927
+ const formattedElements = allElements.map((el) => `${indent}${el}`).join(",\n");
928
+ const multiLineArray = `[
929
+ ${formattedElements},
930
+ ]`;
931
+ importsProperty.setInitializer(multiLineArray);
473
932
  }
474
933
  /**
475
934
  * Build TypeORM.forRoot() configuration string based on database type
@@ -777,6 +1236,9 @@ var init_package_updater = __esm({
777
1236
  break;
778
1237
  }
779
1238
  }
1239
+ if (config.features.rateLimiting) {
1240
+ dependencies["@nestjs/throttler"] = "^6.0.0";
1241
+ }
780
1242
  return { dependencies, devDependencies };
781
1243
  }
782
1244
  /**
@@ -808,477 +1270,80 @@ var init_package_updater = __esm({
808
1270
  * Clean up backup
809
1271
  */
810
1272
  async cleanupBackup() {
811
- if (this.backupPath && await fs6.pathExists(this.backupPath)) {
812
- await fs6.remove(this.backupPath);
813
- }
814
- }
815
- };
816
- }
817
- });
818
-
819
- // src/installer/dependency-installer.ts
820
- var import_execa, import_detect_package_manager, DependencyInstaller;
821
- var init_dependency_installer = __esm({
822
- "src/installer/dependency-installer.ts"() {
823
- "use strict";
824
- init_cjs_shims();
825
- import_execa = require("execa");
826
- import_detect_package_manager = require("detect-package-manager");
827
- DependencyInstaller = class {
828
- /**
829
- * Install dependencies using the detected package manager
830
- */
831
- async install(cwd) {
832
- const packageManager = await this.detectPackageManager(cwd);
833
- console.log(`\u{1F4E6} Installing dependencies with ${packageManager}...`);
834
- try {
835
- await (0, import_execa.execa)(packageManager, ["install"], {
836
- cwd,
837
- stdio: "inherit"
838
- });
839
- console.log("\u2705 Dependencies installed successfully");
840
- } catch (error) {
841
- throw new Error(
842
- `Failed to install dependencies with ${packageManager}: ${error instanceof Error ? error.message : "Unknown error"}`
843
- );
844
- }
845
- }
846
- /**
847
- * Detect which package manager is being used
848
- */
849
- async detectPackageManager(cwd) {
850
- try {
851
- return await (0, import_detect_package_manager.detect)({ cwd });
852
- } catch (error) {
853
- return "npm";
854
- }
855
- }
856
- };
857
- }
858
- });
859
-
860
- // src/installer/index.ts
861
- var installer_exports = {};
862
- __export(installer_exports, {
863
- AppModuleUpdater: () => AppModuleUpdater,
864
- DependencyInstaller: () => DependencyInstaller,
865
- MainTsUpdater: () => MainTsUpdater,
866
- PackageUpdater: () => PackageUpdater
867
- });
868
- var init_installer = __esm({
869
- "src/installer/index.ts"() {
870
- "use strict";
871
- init_cjs_shims();
872
- init_ast_updater();
873
- init_main_ts_updater();
874
- init_package_updater();
875
- init_dependency_installer();
876
- }
877
- });
878
-
879
- // src/cli.ts
880
- init_cjs_shims();
881
-
882
- // src/index.ts
883
- init_cjs_shims();
884
-
885
- // src/analyzer/index.ts
886
- init_cjs_shims();
887
-
888
- // src/analyzer/project-detector.ts
889
- init_cjs_shims();
890
- var path = __toESM(require("path"));
891
- var fs = __toESM(require("fs-extra"));
892
-
893
- // src/analyzer/orm-detector.ts
894
- init_cjs_shims();
895
- async function detectORM(packageJson) {
896
- const dependencies = {
897
- ...packageJson.dependencies,
898
- ...packageJson.devDependencies
899
- };
900
- if (dependencies["@nestjs/typeorm"] || dependencies["typeorm"]) {
901
- return "typeorm";
902
- }
903
- if (dependencies["@prisma/client"] || dependencies["prisma"]) {
904
- return "prisma";
905
- }
906
- if (dependencies["@nestjs/mongoose"] || dependencies["mongoose"]) {
907
- return "mongoose";
908
- }
909
- return "none";
910
- }
911
- function detectDatabase(packageJson, orm) {
912
- const dependencies = {
913
- ...packageJson.dependencies,
914
- ...packageJson.devDependencies
915
- };
916
- if (orm === "typeorm") {
917
- if (dependencies["pg"]) return "postgres";
918
- if (dependencies["mysql2"] || dependencies["mysql"]) return "mysql";
919
- if (dependencies["sqlite3"]) return "sqlite";
920
- if (dependencies["mongodb"]) return "mongodb";
921
- }
922
- if (orm === "prisma") {
923
- return void 0;
924
- }
925
- if (orm === "mongoose") {
926
- return "mongodb";
927
- }
928
- return void 0;
929
- }
930
-
931
- // src/analyzer/project-detector.ts
932
- var ProjectDetector = class {
933
- constructor(cwd) {
934
- this.cwd = cwd;
935
- }
936
- /**
937
- * Detect and validate a NestJS project
938
- */
939
- async detectProject() {
940
- const errors = [];
941
- const root = this.cwd;
942
- const packageJsonPath = path.join(root, "package.json");
943
- if (!await fs.pathExists(packageJsonPath)) {
944
- errors.push("package.json not found");
945
- return this.createInvalidProject(root, errors);
946
- }
947
- const packageJson = await this.readPackageJson(packageJsonPath);
948
- if (!packageJson) {
949
- errors.push("Failed to read package.json");
950
- return this.createInvalidProject(root, errors);
951
- }
952
- const hasNestCore = packageJson.dependencies?.["@nestjs/core"];
953
- const hasNestCommon = packageJson.dependencies?.["@nestjs/common"];
954
- if (!hasNestCore || !hasNestCommon) {
955
- errors.push("Not a NestJS project (missing @nestjs/core or @nestjs/common)");
956
- return this.createInvalidProject(root, errors);
957
- }
958
- const nestCliConfigPath = path.join(root, "nest-cli.json");
959
- const nestCliConfig = await this.readNestCliConfig(nestCliConfigPath);
960
- const sourceRoot = nestCliConfig?.sourceRoot || "src";
961
- const appModulePath = path.join(root, sourceRoot, "app.module.ts");
962
- if (!await fs.pathExists(appModulePath)) {
963
- errors.push(`app.module.ts not found at ${sourceRoot}/app.module.ts`);
964
- return this.createInvalidProject(root, errors);
965
- }
966
- const mainTsPath = path.join(root, sourceRoot, "main.ts");
967
- const orm = await detectORM(packageJson);
968
- const database = detectDatabase(packageJson, orm);
969
- const authModulePath = path.join(root, sourceRoot, "auth");
970
- const authExists = await fs.pathExists(authModulePath);
971
- return {
972
- authExists,
973
- root,
974
- sourceRoot,
975
- appModulePath,
976
- mainTsPath,
977
- packageJsonPath,
978
- nestCliConfigPath,
979
- orm,
980
- database,
981
- nestVersion: packageJson.dependencies?.["@nestjs/core"],
982
- typescriptVersion: packageJson.devDependencies?.["typescript"],
983
- isValid: errors.length === 0,
984
- errors
985
- };
986
- }
987
- /**
988
- * Read and parse package.json
989
- */
990
- async readPackageJson(packageJsonPath) {
991
- try {
992
- const content = await fs.readFile(packageJsonPath, "utf-8");
993
- return JSON.parse(content);
994
- } catch (error) {
995
- return null;
996
- }
997
- }
998
- /**
999
- * Read and parse nest-cli.json
1000
- */
1001
- async readNestCliConfig(nestCliConfigPath) {
1002
- try {
1003
- if (!await fs.pathExists(nestCliConfigPath)) {
1004
- return null;
1005
- }
1006
- const content = await fs.readFile(nestCliConfigPath, "utf-8");
1007
- return JSON.parse(content);
1008
- } catch (error) {
1009
- return null;
1010
- }
1011
- }
1012
- /**
1013
- * Create invalid project info object
1014
- */
1015
- createInvalidProject(root, errors) {
1016
- return {
1017
- root,
1018
- sourceRoot: "src",
1019
- appModulePath: path.join(root, "src", "app.module.ts"),
1020
- mainTsPath: path.join(root, "src", "main.ts"),
1021
- packageJsonPath: path.join(root, "package.json"),
1022
- nestCliConfigPath: path.join(root, "nest-cli.json"),
1023
- orm: "none",
1024
- isValid: false,
1025
- errors
1026
- };
1027
- }
1028
- };
1029
- async function detectProject(cwd = process.cwd()) {
1030
- const detector = new ProjectDetector(cwd);
1031
- return detector.detectProject();
1032
- }
1033
-
1034
- // src/cli/prompts.ts
1035
- init_cjs_shims();
1036
- var import_inquirer = __toESM(require("inquirer"));
1037
-
1038
- // src/config/utils.ts
1039
- init_cjs_shims();
1040
- var import_crypto = require("crypto");
1041
- function generateSecret(length = 32) {
1042
- return (0, import_crypto.randomBytes)(length).toString("base64");
1043
- }
1044
-
1045
- // src/cli/prompts.ts
1046
- async function promptConfig(detectedORM, detectedDB) {
1047
- const dbLabel = detectedDB ? ` with ${detectedDB.charAt(0).toUpperCase() + detectedDB.slice(1)}` : "";
1048
- const answers = await import_inquirer.default.prompt([
1049
- {
1050
- type: "list",
1051
- name: "strategy",
1052
- message: "Choose authentication strategy:",
1053
- choices: [
1054
- { name: "JWT Authentication (Recommended)", value: "jwt" },
1055
- { name: "OAuth 2.0 (Google, GitHub) [v1.1]", value: "oauth", disabled: true },
1056
- { name: "Session-based (Traditional) [v1.2]", value: "session", disabled: true }
1057
- ],
1058
- default: "jwt"
1059
- },
1060
- {
1061
- type: "confirm",
1062
- name: "enableRBAC",
1063
- message: "Enable Role-Based Access Control (RBAC)?",
1064
- default: true
1065
- },
1066
- {
1067
- type: "checkbox",
1068
- name: "roles",
1069
- message: "Select default roles:",
1070
- choices: [
1071
- { name: "Admin", value: "Admin", checked: true },
1072
- { name: "User", value: "User", checked: true },
1073
- { name: "Moderator", value: "Moderator", checked: false },
1074
- { name: "Guest", value: "Guest", checked: false }
1075
- ],
1076
- when: (answers2) => answers2.enableRBAC,
1077
- validate: (input) => {
1078
- if (input.length === 0) {
1079
- return "Please select at least one role";
1273
+ if (this.backupPath && await fs6.pathExists(this.backupPath)) {
1274
+ await fs6.remove(this.backupPath);
1080
1275
  }
1081
- return true;
1082
1276
  }
1083
- },
1084
- {
1085
- type: "confirm",
1086
- name: "refreshTokens",
1087
- message: "Enable Refresh Token rotation?",
1088
- default: true
1089
- },
1090
- {
1091
- type: "list",
1092
- name: "accessExpiration",
1093
- message: "JWT Access Token expiration:",
1094
- choices: [
1095
- { name: "15 minutes", value: "15m" },
1096
- { name: "30 minutes", value: "30m" },
1097
- { name: "1 hour (Recommended)", value: "1h" },
1098
- { name: "4 hours", value: "4h" },
1099
- { name: "1 day", value: "1d" }
1100
- ],
1101
- default: "1h"
1102
- },
1103
- {
1104
- type: "list",
1105
- name: "refreshExpiration",
1106
- message: "JWT Refresh Token expiration:",
1107
- choices: [
1108
- { name: "7 days (Recommended)", value: "7d" },
1109
- { name: "30 days", value: "30d" },
1110
- { name: "90 days", value: "90d" },
1111
- { name: "1 year", value: "1y" }
1112
- ],
1113
- default: "7d",
1114
- when: (answers2) => answers2.refreshTokens
1115
- },
1116
- {
1117
- type: "confirm",
1118
- name: "useDetectedORM",
1119
- message: `Detected ${detectedORM.toUpperCase()}${dbLabel}. Use it?`,
1120
- default: true,
1121
- when: () => detectedORM !== "none"
1122
- },
1123
- {
1124
- type: "list",
1125
- name: "database",
1126
- message: "Select database:",
1127
- choices: [
1128
- { name: "PostgreSQL (Recommended)", value: "postgres" },
1129
- { name: "MySQL", value: "mysql" },
1130
- { name: "SQLite (for testing)", value: "sqlite" },
1131
- { name: "MongoDB", value: "mongodb" }
1132
- ],
1133
- default: "postgres",
1134
- when: (answers2) => detectedORM === "none" || !answers2.useDetectedORM
1135
- },
1136
- {
1137
- type: "confirm",
1138
- name: "autoInstall",
1139
- message: "Auto-install dependencies after generation?",
1140
- default: true
1141
- }
1142
- ]);
1143
- return answers;
1144
- }
1145
- function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB) {
1146
- const config = {
1147
- projectName,
1148
- sourceRoot,
1149
- strategy: answers.strategy,
1150
- rbac: {
1151
- enabled: answers.enableRBAC,
1152
- roles: answers.roles || []
1153
- },
1154
- orm: answers.useDetectedORM !== false ? detectedORM : "typeorm",
1155
- database: answers.database || detectedDB || "postgres",
1156
- features: {
1157
- refreshTokens: answers.refreshTokens
1158
- },
1159
- jwt: {
1160
- secret: generateSecret(),
1161
- accessExpiration: answers.accessExpiration,
1162
- refreshExpiration: answers.refreshExpiration || "7d"
1163
- },
1164
- autoInstall: answers.autoInstall,
1165
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1166
- generatorVersion: "1.0.0"
1167
- };
1168
- return config;
1169
- }
1170
-
1171
- // src/cli/ui.ts
1172
- init_cjs_shims();
1173
- var import_chalk = __toESM(require("chalk"));
1174
- var import_ora = __toESM(require("ora"));
1175
- var import_fs = require("fs");
1176
- var import_path = require("path");
1177
- function getVersion() {
1178
- try {
1179
- const packageJsonPath = (0, import_path.join)(__dirname, "../package.json");
1180
- const packageJson = JSON.parse((0, import_fs.readFileSync)(packageJsonPath, "utf-8"));
1181
- return packageJson.version;
1182
- } catch (error) {
1183
- return "1.0.0";
1184
- }
1185
- }
1186
- function showBanner() {
1187
- console.log(import_chalk.default.cyan(`
1188
- ___ _ _ __ __
1189
- / _ \\ | | | | | \\/ |
1190
- / /_\\ \\_ _ | |_| |__ | \\ / | ___
1191
- | _ | | | || __| '_ \\ | |\\/| |/ _ \\
1192
- | | | | |_| || |_| | | | | | | | __/
1193
- \\_| |_/\\__,_| \\__|_| |_| \\_| |_/\\___|
1194
- `));
1195
- console.log(import_chalk.default.bold(`\u{1F510} NestJS Authentication Module Generator v${getVersion()}`));
1196
- console.log();
1197
- }
1198
- function showProjectInfo(info) {
1199
- console.log(import_chalk.default.green("\u2713"), `Detected NestJS ${info.nestVersion || "project"}`);
1200
- if (info.orm !== "none") {
1201
- console.log(import_chalk.default.green("\u2713"), `Found ${info.orm.toUpperCase()}`);
1277
+ };
1202
1278
  }
1203
- console.log(import_chalk.default.green("\u2713"), `Source directory: ${info.sourceRoot}/`);
1204
- console.log(import_chalk.default.green("\u2713"), "No existing auth module found");
1205
- console.log();
1206
- }
1207
- function showError(message, errors) {
1208
- console.log();
1209
- console.log(import_chalk.default.red("\u274C Error:"), import_chalk.default.bold(message));
1210
- if (errors && errors.length > 0) {
1211
- console.log();
1212
- errors.forEach((error) => {
1213
- console.log(import_chalk.default.red(" \u2022"), error);
1214
- });
1279
+ });
1280
+
1281
+ // src/installer/dependency-installer.ts
1282
+ var import_execa, import_detect_package_manager, DependencyInstaller;
1283
+ var init_dependency_installer = __esm({
1284
+ "src/installer/dependency-installer.ts"() {
1285
+ "use strict";
1286
+ init_cjs_shims();
1287
+ import_execa = require("execa");
1288
+ import_detect_package_manager = require("detect-package-manager");
1289
+ DependencyInstaller = class {
1290
+ /**
1291
+ * Install dependencies using the detected package manager
1292
+ */
1293
+ async install(cwd) {
1294
+ const packageManager = await this.detectPackageManager(cwd);
1295
+ console.log(`\u{1F4E6} Installing dependencies with ${packageManager}...`);
1296
+ try {
1297
+ await (0, import_execa.execa)(packageManager, ["install"], {
1298
+ cwd,
1299
+ stdio: "inherit"
1300
+ });
1301
+ console.log("\u2705 Dependencies installed successfully");
1302
+ } catch (error) {
1303
+ throw new Error(
1304
+ `Failed to install dependencies with ${packageManager}: ${error instanceof Error ? error.message : "Unknown error"}`
1305
+ );
1306
+ }
1307
+ }
1308
+ /**
1309
+ * Detect which package manager is being used
1310
+ */
1311
+ async detectPackageManager(cwd) {
1312
+ try {
1313
+ return await (0, import_detect_package_manager.detect)({ cwd });
1314
+ } catch (error) {
1315
+ return "npm";
1316
+ }
1317
+ }
1318
+ };
1215
1319
  }
1216
- console.log();
1217
- }
1218
- function showNestJSHelp() {
1219
- console.log(import_chalk.default.yellow("To create a new NestJS project:"));
1220
- console.log();
1221
- console.log(import_chalk.default.cyan(" npm i -g @nestjs/cli"));
1222
- console.log(import_chalk.default.cyan(" nest new my-project"));
1223
- console.log();
1224
- }
1225
- function showSuccess(stats) {
1226
- console.log();
1227
- console.log(import_chalk.default.green.bold("\u{1F389} Success!"), "Authentication module generated.");
1228
- console.log();
1229
- console.log(import_chalk.default.bold("\u{1F4C1} Files created:"));
1230
- console.log(` \u2022 ${stats.filesCreated} new files in src/auth/ and src/users/`);
1231
- console.log(` \u2022 Updated src/app.module.ts`);
1232
- console.log(` \u2022 Updated package.json`);
1233
- console.log();
1234
- console.log(import_chalk.default.bold("\u{1F4E6} Dependencies added:"));
1235
- console.log(` \u2022 @nestjs/jwt, @nestjs/passport, @nestjs/config`);
1236
- console.log(` \u2022 passport, passport-jwt, passport-local`);
1237
- console.log(` \u2022 bcrypt, class-validator, class-transformer`);
1238
- console.log(` \u2022 ${stats.dependenciesAdded} packages total`);
1239
- console.log();
1240
- console.log(import_chalk.default.bold("\u{1F510} JWT Configuration:"));
1241
- console.log(` \u2022 Access token: ${stats.jwt.accessExpiration}`);
1242
- if (stats.jwt.refreshExpiration) {
1243
- console.log(` \u2022 Refresh token: ${stats.jwt.refreshExpiration}`);
1320
+ });
1321
+
1322
+ // src/installer/index.ts
1323
+ var installer_exports = {};
1324
+ __export(installer_exports, {
1325
+ AppModuleUpdater: () => AppModuleUpdater,
1326
+ DependencyInstaller: () => DependencyInstaller,
1327
+ MainTsUpdater: () => MainTsUpdater,
1328
+ PackageUpdater: () => PackageUpdater
1329
+ });
1330
+ var init_installer = __esm({
1331
+ "src/installer/index.ts"() {
1332
+ "use strict";
1333
+ init_cjs_shims();
1334
+ init_ast_updater();
1335
+ init_main_ts_updater();
1336
+ init_package_updater();
1337
+ init_dependency_installer();
1244
1338
  }
1245
- console.log(` \u2022 Secret: Auto-generated (see .env.example)`);
1246
- console.log();
1247
- console.log(import_chalk.default.bold("\u{1F4CB} Next steps:"));
1248
- console.log(import_chalk.default.cyan(" 1. Copy .env.example to .env"));
1249
- console.log(import_chalk.default.gray(" cp .env.example .env"));
1250
- console.log();
1251
- console.log(import_chalk.default.cyan(" 2. Update JWT_SECRET in .env (or keep auto-generated)"));
1252
- console.log();
1253
- console.log(import_chalk.default.cyan(" 3. Create database migration (if using TypeORM)"));
1254
- console.log(import_chalk.default.gray(" npm run migration:generate -- src/migrations/CreateUserTable"));
1255
- console.log(import_chalk.default.gray(" npm run migration:run"));
1256
- console.log();
1257
- console.log(import_chalk.default.cyan(" 4. Start your NestJS app"));
1258
- console.log(import_chalk.default.gray(" npm run start:dev"));
1259
- console.log();
1260
- console.log(import_chalk.default.cyan(" 5. Test authentication endpoints"));
1261
- console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/register"));
1262
- console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/login"));
1263
- console.log(import_chalk.default.gray(" GET http://localhost:3000/users/profile (requires JWT)"));
1264
- console.log();
1265
- console.log(import_chalk.default.bold("\u{1F4D6} Full documentation:"), "src/auth/README.md");
1266
- console.log();
1267
- console.log(import_chalk.default.bold("\u{1F4A1} Tips:"));
1268
- console.log(" \u2022 Use @Public() decorator for routes that don't require auth");
1269
- console.log(" \u2022 Use @Roles('Admin') to restrict routes by role");
1270
- console.log(" \u2022 Access current user with @CurrentUser() decorator");
1271
- console.log();
1272
- }
1273
- function createSpinner(text) {
1274
- return (0, import_ora.default)({
1275
- text,
1276
- color: "cyan"
1277
- });
1278
- }
1339
+ });
1279
1340
 
1280
1341
  // src/index.ts
1281
- async function run(cwd = process.cwd()) {
1342
+ var index_exports = {};
1343
+ __export(index_exports, {
1344
+ run: () => run
1345
+ });
1346
+ async function run(cwd = process.cwd(), options = {}) {
1282
1347
  showBanner();
1283
1348
  const spinner = createSpinner("Analyzing project...").start();
1284
1349
  const projectInfo = await detectProject(cwd);
@@ -1295,6 +1360,10 @@ async function run(cwd = process.cwd()) {
1295
1360
  sourceRoot: projectInfo.sourceRoot
1296
1361
  });
1297
1362
  if (projectInfo.authExists) {
1363
+ if (options.yes) {
1364
+ console.log("\n auth/ directory already exists. Use interactive mode to overwrite.\n");
1365
+ process.exit(0);
1366
+ }
1298
1367
  const inquirer2 = (await import("inquirer")).default;
1299
1368
  const { overwrite } = await inquirer2.prompt([{
1300
1369
  type: "confirm",
@@ -1307,7 +1376,7 @@ async function run(cwd = process.cwd()) {
1307
1376
  process.exit(0);
1308
1377
  }
1309
1378
  }
1310
- const answers = await promptConfig(projectInfo.orm, projectInfo.database);
1379
+ const answers = options.yes ? getDefaultAnswers(projectInfo.orm, projectInfo.database) : await promptConfig(projectInfo.orm, projectInfo.database);
1311
1380
  const config = buildConfig(
1312
1381
  answers,
1313
1382
  projectInfo.root.split(/[/\\]/).pop() || "project",
@@ -1397,14 +1466,32 @@ async function run(cwd = process.cwd()) {
1397
1466
  console.log("\u2B50 Like it? https://github.com/Islamawad132/add-nest-auth");
1398
1467
  console.log();
1399
1468
  }
1469
+ var init_index = __esm({
1470
+ "src/index.ts"() {
1471
+ "use strict";
1472
+ init_cjs_shims();
1473
+ init_analyzer();
1474
+ init_prompts();
1475
+ init_ui();
1476
+ }
1477
+ });
1400
1478
 
1401
1479
  // src/cli.ts
1480
+ init_cjs_shims();
1481
+ var import_commander = require("commander");
1402
1482
  process.on("unhandledRejection", (error) => {
1403
1483
  console.error("Unhandled rejection:", error);
1404
1484
  process.exit(1);
1405
1485
  });
1406
- run().catch((error) => {
1407
- console.error("Fatal error:", error);
1408
- process.exit(1);
1486
+ var program = new import_commander.Command();
1487
+ program.name("add-nest-auth").description("Add production-ready authentication to any NestJS project").version("1.1.0").option("-y, --yes", "Skip all prompts and use sensible defaults").action(async (options) => {
1488
+ try {
1489
+ const { run: run2 } = await Promise.resolve().then(() => (init_index(), index_exports));
1490
+ await run2(process.cwd(), { yes: options.yes || false });
1491
+ } catch (error) {
1492
+ console.error("Fatal error:", error);
1493
+ process.exit(1);
1494
+ }
1409
1495
  });
1496
+ program.parse();
1410
1497
  //# sourceMappingURL=cli.js.map