add-nest-auth 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +368 -0
  3. package/bin/cli.js +11 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +1133 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/generator/templates/decorators/current-user.decorator.ts.hbs +8 -0
  8. package/dist/generator/templates/decorators/public.decorator.ts.hbs +4 -0
  9. package/dist/generator/templates/decorators/roles.decorator.ts.hbs +4 -0
  10. package/dist/generator/templates/dto/auth-response.dto.ts.hbs +13 -0
  11. package/dist/generator/templates/dto/create-user.dto.ts.hbs +17 -0
  12. package/dist/generator/templates/dto/login.dto.ts.hbs +12 -0
  13. package/dist/generator/templates/dto/register.dto.ts.hbs +13 -0
  14. package/dist/generator/templates/entities/refresh-token.entity.typeorm.hbs +24 -0
  15. package/dist/generator/templates/entities/user.entity.typeorm.hbs +30 -0
  16. package/dist/generator/templates/jwt/auth.controller.ts.hbs +34 -0
  17. package/dist/generator/templates/jwt/auth.module.ts.hbs +48 -0
  18. package/dist/generator/templates/jwt/auth.service.ts.hbs +193 -0
  19. package/dist/generator/templates/jwt/jwt-auth.guard.ts.hbs +24 -0
  20. package/dist/generator/templates/jwt/jwt.strategy.ts.hbs +52 -0
  21. package/dist/generator/templates/jwt/local-auth.guard.ts.hbs +5 -0
  22. package/dist/generator/templates/jwt/local.strategy.ts.hbs +22 -0
  23. package/dist/generator/templates/rbac/role.enum.ts.hbs +5 -0
  24. package/dist/generator/templates/rbac/roles.guard.ts.hbs +22 -0
  25. package/dist/generator/templates/shared/README.auth.md.hbs +283 -0
  26. package/dist/generator/templates/shared/env.template.hbs +29 -0
  27. package/dist/generator/templates/users/users.controller.ts.hbs +31 -0
  28. package/dist/generator/templates/users/users.module.ts.hbs +27 -0
  29. package/dist/generator/templates/users/users.service.ts.hbs +93 -0
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.js +1130 -0
  32. package/dist/index.js.map +1 -0
  33. package/package.json +62 -0
package/dist/index.js ADDED
@@ -0,0 +1,1130 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // node_modules/tsup/assets/cjs_shims.js
34
+ var init_cjs_shims = __esm({
35
+ "node_modules/tsup/assets/cjs_shims.js"() {
36
+ "use strict";
37
+ }
38
+ });
39
+
40
+ // src/generator/template-engine.ts
41
+ var import_handlebars, path2, fs2, TemplateEngine;
42
+ var init_template_engine = __esm({
43
+ "src/generator/template-engine.ts"() {
44
+ "use strict";
45
+ init_cjs_shims();
46
+ import_handlebars = __toESM(require("handlebars"));
47
+ path2 = __toESM(require("path"));
48
+ fs2 = __toESM(require("fs-extra"));
49
+ TemplateEngine = class {
50
+ handlebars;
51
+ templateCache;
52
+ templatesDir;
53
+ constructor(templatesDir) {
54
+ this.handlebars = import_handlebars.default.create();
55
+ this.templateCache = /* @__PURE__ */ new Map();
56
+ this.templatesDir = templatesDir || path2.join(__dirname, "templates");
57
+ this.registerHelpers();
58
+ }
59
+ /**
60
+ * Register Handlebars helpers
61
+ */
62
+ registerHelpers() {
63
+ this.handlebars.registerHelper("eq", (a, b) => a === b);
64
+ this.handlebars.registerHelper("ne", (a, b) => a !== b);
65
+ this.handlebars.registerHelper("includes", (arr, item) => arr?.includes(item));
66
+ this.handlebars.registerHelper("capitalize", (str) => {
67
+ if (!str) return "";
68
+ return str.charAt(0).toUpperCase() + str.slice(1);
69
+ });
70
+ this.handlebars.registerHelper("lowercase", (str) => {
71
+ if (!str) return "";
72
+ return str.toLowerCase();
73
+ });
74
+ this.handlebars.registerHelper("uppercase", (str) => {
75
+ if (!str) return "";
76
+ return str.toUpperCase();
77
+ });
78
+ this.handlebars.registerHelper("camelCase", (str) => {
79
+ if (!str) return "";
80
+ return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
81
+ });
82
+ this.handlebars.registerHelper("pascalCase", (str) => {
83
+ if (!str) return "";
84
+ const camel = str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
85
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
86
+ });
87
+ }
88
+ /**
89
+ * Render a template with context
90
+ */
91
+ async render(templatePath, context) {
92
+ const template = await this.loadTemplate(templatePath);
93
+ return template(context);
94
+ }
95
+ /**
96
+ * Load a template (with caching)
97
+ */
98
+ async loadTemplate(templatePath) {
99
+ if (this.templateCache.has(templatePath)) {
100
+ return this.templateCache.get(templatePath);
101
+ }
102
+ const fullPath = path2.join(this.templatesDir, templatePath);
103
+ if (!await fs2.pathExists(fullPath)) {
104
+ throw new Error(`Template not found: ${templatePath}`);
105
+ }
106
+ const source = await fs2.readFile(fullPath, "utf-8");
107
+ const compiled = this.handlebars.compile(source);
108
+ this.templateCache.set(templatePath, compiled);
109
+ return compiled;
110
+ }
111
+ /**
112
+ * Clear template cache
113
+ */
114
+ clearCache() {
115
+ this.templateCache.clear();
116
+ }
117
+ };
118
+ }
119
+ });
120
+
121
+ // src/generator/file-writer.ts
122
+ var path3, fs3, FileWriter;
123
+ var init_file_writer = __esm({
124
+ "src/generator/file-writer.ts"() {
125
+ "use strict";
126
+ init_cjs_shims();
127
+ path3 = __toESM(require("path"));
128
+ fs3 = __toESM(require("fs-extra"));
129
+ FileWriter = class {
130
+ writtenFiles = [];
131
+ backups = /* @__PURE__ */ new Map();
132
+ /**
133
+ * Write a file to disk
134
+ */
135
+ async writeFile(filePath, content, options = {}) {
136
+ const { overwrite = false, backup = true } = options;
137
+ const exists = await fs3.pathExists(filePath);
138
+ if (exists && !overwrite) {
139
+ throw new Error(`File already exists: ${filePath}`);
140
+ }
141
+ if (exists && backup) {
142
+ await this.createBackup(filePath);
143
+ }
144
+ await fs3.ensureDir(path3.dirname(filePath));
145
+ await fs3.writeFile(filePath, content, "utf-8");
146
+ this.writtenFiles.push(filePath);
147
+ }
148
+ /**
149
+ * Create a backup of an existing file
150
+ */
151
+ async createBackup(filePath) {
152
+ const backupPath = `${filePath}.backup`;
153
+ await fs3.copy(filePath, backupPath);
154
+ this.backups.set(filePath, backupPath);
155
+ }
156
+ /**
157
+ * Rollback all written files
158
+ */
159
+ async rollback() {
160
+ for (const [originalPath, backupPath] of this.backups) {
161
+ if (await fs3.pathExists(backupPath)) {
162
+ await fs3.copy(backupPath, originalPath, { overwrite: true });
163
+ await fs3.remove(backupPath);
164
+ }
165
+ }
166
+ for (const filePath of this.writtenFiles) {
167
+ if (!this.backups.has(filePath) && await fs3.pathExists(filePath)) {
168
+ await fs3.remove(filePath);
169
+ }
170
+ }
171
+ this.writtenFiles = [];
172
+ this.backups.clear();
173
+ }
174
+ /**
175
+ * Clean up backups
176
+ */
177
+ async cleanupBackups() {
178
+ for (const backupPath of this.backups.values()) {
179
+ if (await fs3.pathExists(backupPath)) {
180
+ await fs3.remove(backupPath);
181
+ }
182
+ }
183
+ this.backups.clear();
184
+ }
185
+ /**
186
+ * Get list of written files
187
+ */
188
+ getWrittenFiles() {
189
+ return [...this.writtenFiles];
190
+ }
191
+ };
192
+ }
193
+ });
194
+
195
+ // src/config/config-builder.ts
196
+ function buildTemplateContext(config) {
197
+ return {
198
+ ...config
199
+ // Add any additional computed properties here
200
+ };
201
+ }
202
+ var init_config_builder = __esm({
203
+ "src/config/config-builder.ts"() {
204
+ "use strict";
205
+ init_cjs_shims();
206
+ }
207
+ });
208
+
209
+ // src/generator/generator.ts
210
+ var path4, Generator;
211
+ var init_generator = __esm({
212
+ "src/generator/generator.ts"() {
213
+ "use strict";
214
+ init_cjs_shims();
215
+ path4 = __toESM(require("path"));
216
+ init_template_engine();
217
+ init_file_writer();
218
+ init_config_builder();
219
+ Generator = class {
220
+ templateEngine;
221
+ fileWriter;
222
+ constructor() {
223
+ this.templateEngine = new TemplateEngine();
224
+ this.fileWriter = new FileWriter();
225
+ }
226
+ /**
227
+ * Generate all authentication files
228
+ */
229
+ async generate(config, projectInfo) {
230
+ try {
231
+ const context = buildTemplateContext(config);
232
+ const plan = this.buildGenerationPlan(config);
233
+ for (const fileSpec of plan) {
234
+ if (fileSpec.condition && !fileSpec.condition(config)) {
235
+ continue;
236
+ }
237
+ const content = await this.templateEngine.render(
238
+ fileSpec.template,
239
+ context
240
+ );
241
+ const outputPath = path4.join(projectInfo.root, fileSpec.output);
242
+ await this.fileWriter.writeFile(outputPath, content, {
243
+ overwrite: false
244
+ });
245
+ }
246
+ const filesCreated = this.fileWriter.getWrittenFiles();
247
+ await this.fileWriter.cleanupBackups();
248
+ return {
249
+ filesCreated,
250
+ success: true
251
+ };
252
+ } catch (error) {
253
+ await this.fileWriter.rollback();
254
+ return {
255
+ filesCreated: [],
256
+ success: false,
257
+ error: error instanceof Error ? error.message : "Unknown error"
258
+ };
259
+ }
260
+ }
261
+ /**
262
+ * Build file generation plan
263
+ */
264
+ buildGenerationPlan(config) {
265
+ const plan = [];
266
+ plan.push(
267
+ { template: "jwt/auth.module.ts.hbs", output: `${config.sourceRoot}/auth/auth.module.ts` },
268
+ { template: "jwt/auth.service.ts.hbs", output: `${config.sourceRoot}/auth/auth.service.ts` },
269
+ { template: "jwt/auth.controller.ts.hbs", output: `${config.sourceRoot}/auth/auth.controller.ts` }
270
+ );
271
+ plan.push(
272
+ { template: "jwt/jwt.strategy.ts.hbs", output: `${config.sourceRoot}/auth/strategies/jwt.strategy.ts` },
273
+ { template: "jwt/local.strategy.ts.hbs", output: `${config.sourceRoot}/auth/strategies/local.strategy.ts` }
274
+ );
275
+ plan.push(
276
+ { template: "jwt/jwt-auth.guard.ts.hbs", output: `${config.sourceRoot}/auth/guards/jwt-auth.guard.ts` },
277
+ { template: "jwt/local-auth.guard.ts.hbs", output: `${config.sourceRoot}/auth/guards/local-auth.guard.ts` }
278
+ );
279
+ if (config.rbac.enabled) {
280
+ plan.push(
281
+ { template: "rbac/roles.guard.ts.hbs", output: `${config.sourceRoot}/auth/guards/roles.guard.ts` },
282
+ { template: "rbac/role.enum.ts.hbs", output: `${config.sourceRoot}/auth/enums/role.enum.ts` },
283
+ { template: "decorators/roles.decorator.ts.hbs", output: `${config.sourceRoot}/auth/decorators/roles.decorator.ts` }
284
+ );
285
+ }
286
+ plan.push(
287
+ { template: "decorators/public.decorator.ts.hbs", output: `${config.sourceRoot}/auth/decorators/public.decorator.ts` },
288
+ { template: "decorators/current-user.decorator.ts.hbs", output: `${config.sourceRoot}/auth/decorators/current-user.decorator.ts` }
289
+ );
290
+ plan.push(
291
+ { template: "dto/login.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/login.dto.ts` },
292
+ { template: "dto/register.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/register.dto.ts` },
293
+ { template: "dto/auth-response.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/auth-response.dto.ts` },
294
+ { template: "dto/create-user.dto.ts.hbs", output: `${config.sourceRoot}/auth/dto/create-user.dto.ts` }
295
+ );
296
+ plan.push(
297
+ { template: "users/users.module.ts.hbs", output: `${config.sourceRoot}/users/users.module.ts` },
298
+ { template: "users/users.service.ts.hbs", output: `${config.sourceRoot}/users/users.service.ts` },
299
+ { template: "users/users.controller.ts.hbs", output: `${config.sourceRoot}/users/users.controller.ts` }
300
+ );
301
+ if (config.orm === "typeorm") {
302
+ plan.push(
303
+ { template: "entities/user.entity.typeorm.hbs", output: `${config.sourceRoot}/users/entities/user.entity.ts` }
304
+ );
305
+ if (config.features.refreshTokens) {
306
+ plan.push({
307
+ template: "entities/refresh-token.entity.typeorm.hbs",
308
+ output: `${config.sourceRoot}/users/entities/refresh-token.entity.ts`
309
+ });
310
+ }
311
+ }
312
+ plan.push(
313
+ { template: "shared/env.template.hbs", output: ".env.example" },
314
+ { template: "shared/README.auth.md.hbs", output: `${config.sourceRoot}/auth/README.md` }
315
+ );
316
+ return plan;
317
+ }
318
+ };
319
+ }
320
+ });
321
+
322
+ // src/generator/index.ts
323
+ var generator_exports = {};
324
+ __export(generator_exports, {
325
+ FileWriter: () => FileWriter,
326
+ Generator: () => Generator,
327
+ TemplateEngine: () => TemplateEngine
328
+ });
329
+ var init_generator2 = __esm({
330
+ "src/generator/index.ts"() {
331
+ "use strict";
332
+ init_cjs_shims();
333
+ init_generator();
334
+ init_template_engine();
335
+ init_file_writer();
336
+ }
337
+ });
338
+
339
+ // src/installer/ast-updater.ts
340
+ var import_ts_morph, fs4, AppModuleUpdater;
341
+ var init_ast_updater = __esm({
342
+ "src/installer/ast-updater.ts"() {
343
+ "use strict";
344
+ init_cjs_shims();
345
+ import_ts_morph = require("ts-morph");
346
+ fs4 = __toESM(require("fs-extra"));
347
+ AppModuleUpdater = class {
348
+ constructor(appModulePath) {
349
+ this.appModulePath = appModulePath;
350
+ this.project = new import_ts_morph.Project({
351
+ skipAddingFilesFromTsConfig: true
352
+ });
353
+ }
354
+ project;
355
+ sourceFile;
356
+ backupPath = null;
357
+ /**
358
+ * Update app.module.ts with auth modules
359
+ */
360
+ async update() {
361
+ await this.createBackup();
362
+ try {
363
+ this.sourceFile = this.project.addSourceFileAtPath(this.appModulePath);
364
+ this.addImports();
365
+ this.addModulesToDecorator();
366
+ await this.sourceFile.save();
367
+ } catch (error) {
368
+ await this.restoreBackup();
369
+ throw error;
370
+ }
371
+ }
372
+ /**
373
+ * Add necessary imports
374
+ */
375
+ addImports() {
376
+ if (!this.sourceFile) {
377
+ throw new Error("Source file not loaded");
378
+ }
379
+ this.addImport("@nestjs/config", ["ConfigModule"]);
380
+ this.addImport("./auth/auth.module", ["AuthModule"]);
381
+ this.addImport("./users/users.module", ["UsersModule"]);
382
+ }
383
+ /**
384
+ * Add an import statement if it doesn't exist
385
+ */
386
+ addImport(moduleSpecifier, namedImports) {
387
+ if (!this.sourceFile) return;
388
+ const existingImport = this.sourceFile.getImportDeclarations().find((imp) => imp.getModuleSpecifierValue() === moduleSpecifier);
389
+ if (existingImport) {
390
+ const existingNames = existingImport.getNamedImports().map((ni) => ni.getName());
391
+ const missingImports = namedImports.filter(
392
+ (name) => !existingNames.includes(name)
393
+ );
394
+ if (missingImports.length > 0) {
395
+ existingImport.addNamedImports(missingImports);
396
+ }
397
+ } else {
398
+ this.sourceFile.addImportDeclaration({
399
+ moduleSpecifier,
400
+ namedImports
401
+ });
402
+ }
403
+ }
404
+ /**
405
+ * Add modules to @Module decorator imports array
406
+ */
407
+ addModulesToDecorator() {
408
+ if (!this.sourceFile) return;
409
+ const appModuleClass = this.sourceFile.getClass("AppModule");
410
+ if (!appModuleClass) {
411
+ throw new Error("AppModule class not found");
412
+ }
413
+ const moduleDecorator = appModuleClass.getDecorator("Module");
414
+ if (!moduleDecorator) {
415
+ throw new Error("@Module decorator not found");
416
+ }
417
+ const decoratorArgs = moduleDecorator.getArguments()[0];
418
+ if (!decoratorArgs || !import_ts_morph.Node.isObjectLiteralExpression(decoratorArgs)) {
419
+ throw new Error("Invalid @Module decorator structure");
420
+ }
421
+ let importsProperty = decoratorArgs.getProperty("imports");
422
+ if (!importsProperty) {
423
+ decoratorArgs.addPropertyAssignment({
424
+ name: "imports",
425
+ initializer: "[]"
426
+ });
427
+ importsProperty = decoratorArgs.getProperty("imports");
428
+ }
429
+ if (!importsProperty || !import_ts_morph.Node.isPropertyAssignment(importsProperty)) {
430
+ throw new Error("Invalid imports property");
431
+ }
432
+ const importsArray = importsProperty.getInitializer();
433
+ if (!import_ts_morph.Node.isArrayLiteralExpression(importsArray)) {
434
+ throw new Error("imports is not an array");
435
+ }
436
+ const existingModules = this.getExistingModuleNames(importsArray);
437
+ if (!existingModules.has("ConfigModule")) {
438
+ importsArray.addElement("ConfigModule.forRoot({ isGlobal: true })");
439
+ }
440
+ if (!existingModules.has("AuthModule")) {
441
+ importsArray.addElement("AuthModule");
442
+ }
443
+ if (!existingModules.has("UsersModule")) {
444
+ importsArray.addElement("UsersModule");
445
+ }
446
+ }
447
+ /**
448
+ * Get existing module names from imports array
449
+ */
450
+ getExistingModuleNames(importsArray) {
451
+ const moduleNames = /* @__PURE__ */ new Set();
452
+ if (!import_ts_morph.Node.isArrayLiteralExpression(importsArray)) {
453
+ return moduleNames;
454
+ }
455
+ for (const element of importsArray.getElements()) {
456
+ const text = element.getText();
457
+ const match = text.match(/^(\w+)/);
458
+ if (match) {
459
+ moduleNames.add(match[1]);
460
+ }
461
+ }
462
+ return moduleNames;
463
+ }
464
+ /**
465
+ * Create backup of app.module.ts
466
+ */
467
+ async createBackup() {
468
+ this.backupPath = `${this.appModulePath}.backup`;
469
+ await fs4.copy(this.appModulePath, this.backupPath);
470
+ }
471
+ /**
472
+ * Restore backup
473
+ */
474
+ async restoreBackup() {
475
+ if (this.backupPath && await fs4.pathExists(this.backupPath)) {
476
+ await fs4.copy(this.backupPath, this.appModulePath, { overwrite: true });
477
+ await fs4.remove(this.backupPath);
478
+ }
479
+ }
480
+ /**
481
+ * Clean up backup
482
+ */
483
+ async cleanupBackup() {
484
+ if (this.backupPath && await fs4.pathExists(this.backupPath)) {
485
+ await fs4.remove(this.backupPath);
486
+ }
487
+ }
488
+ };
489
+ }
490
+ });
491
+
492
+ // src/installer/package-updater.ts
493
+ var fs5, PackageUpdater;
494
+ var init_package_updater = __esm({
495
+ "src/installer/package-updater.ts"() {
496
+ "use strict";
497
+ init_cjs_shims();
498
+ fs5 = __toESM(require("fs-extra"));
499
+ PackageUpdater = class {
500
+ constructor(packageJsonPath) {
501
+ this.packageJsonPath = packageJsonPath;
502
+ }
503
+ backupPath = null;
504
+ /**
505
+ * Update package.json with auth dependencies
506
+ */
507
+ async update(config) {
508
+ await this.createBackup();
509
+ try {
510
+ const packageJson = await fs5.readJSON(this.packageJsonPath);
511
+ const deps = this.getDependencies(config);
512
+ packageJson.dependencies = {
513
+ ...packageJson.dependencies,
514
+ ...deps.dependencies
515
+ };
516
+ packageJson.devDependencies = {
517
+ ...packageJson.devDependencies,
518
+ ...deps.devDependencies
519
+ };
520
+ packageJson.dependencies = this.sortObject(packageJson.dependencies);
521
+ packageJson.devDependencies = this.sortObject(
522
+ packageJson.devDependencies
523
+ );
524
+ await fs5.writeJSON(this.packageJsonPath, packageJson, { spaces: 2 });
525
+ } catch (error) {
526
+ await this.restoreBackup();
527
+ throw error;
528
+ }
529
+ }
530
+ /**
531
+ * Get dependencies based on configuration
532
+ */
533
+ getDependencies(config) {
534
+ const dependencies = {
535
+ "@nestjs/jwt": "^11.0.0",
536
+ "@nestjs/passport": "^11.0.0",
537
+ "@nestjs/config": "^3.0.0",
538
+ passport: "^0.7.0",
539
+ "passport-jwt": "^4.0.1",
540
+ "passport-local": "^1.0.0",
541
+ bcrypt: "^5.1.1",
542
+ "class-validator": "^0.14.0",
543
+ "class-transformer": "^0.5.1"
544
+ };
545
+ const devDependencies = {
546
+ "@types/passport-jwt": "^4.0.0",
547
+ "@types/passport-local": "^1.0.36",
548
+ "@types/bcrypt": "^5.0.2"
549
+ };
550
+ if (config.orm === "typeorm") {
551
+ dependencies["@nestjs/typeorm"] = "^11.0.0";
552
+ dependencies["typeorm"] = "^0.3.20";
553
+ switch (config.database) {
554
+ case "postgres":
555
+ dependencies["pg"] = "^8.11.3";
556
+ break;
557
+ case "mysql":
558
+ dependencies["mysql2"] = "^3.9.1";
559
+ break;
560
+ case "sqlite":
561
+ dependencies["sqlite3"] = "^5.1.7";
562
+ break;
563
+ case "mongodb":
564
+ dependencies["mongodb"] = "^6.3.0";
565
+ break;
566
+ }
567
+ }
568
+ return { dependencies, devDependencies };
569
+ }
570
+ /**
571
+ * Sort object keys alphabetically
572
+ */
573
+ sortObject(obj) {
574
+ return Object.keys(obj).sort().reduce((sorted, key) => {
575
+ sorted[key] = obj[key];
576
+ return sorted;
577
+ }, {});
578
+ }
579
+ /**
580
+ * Create backup
581
+ */
582
+ async createBackup() {
583
+ this.backupPath = `${this.packageJsonPath}.backup`;
584
+ await fs5.copy(this.packageJsonPath, this.backupPath);
585
+ }
586
+ /**
587
+ * Restore backup
588
+ */
589
+ async restoreBackup() {
590
+ if (this.backupPath && await fs5.pathExists(this.backupPath)) {
591
+ await fs5.copy(this.backupPath, this.packageJsonPath, { overwrite: true });
592
+ await fs5.remove(this.backupPath);
593
+ }
594
+ }
595
+ /**
596
+ * Clean up backup
597
+ */
598
+ async cleanupBackup() {
599
+ if (this.backupPath && await fs5.pathExists(this.backupPath)) {
600
+ await fs5.remove(this.backupPath);
601
+ }
602
+ }
603
+ };
604
+ }
605
+ });
606
+
607
+ // src/installer/dependency-installer.ts
608
+ var import_execa, import_detect_package_manager, DependencyInstaller;
609
+ var init_dependency_installer = __esm({
610
+ "src/installer/dependency-installer.ts"() {
611
+ "use strict";
612
+ init_cjs_shims();
613
+ import_execa = require("execa");
614
+ import_detect_package_manager = require("detect-package-manager");
615
+ DependencyInstaller = class {
616
+ /**
617
+ * Install dependencies using the detected package manager
618
+ */
619
+ async install(cwd) {
620
+ const packageManager = await this.detectPackageManager(cwd);
621
+ console.log(`\u{1F4E6} Installing dependencies with ${packageManager}...`);
622
+ try {
623
+ await (0, import_execa.execa)(packageManager, ["install"], {
624
+ cwd,
625
+ stdio: "inherit"
626
+ });
627
+ console.log("\u2705 Dependencies installed successfully");
628
+ } catch (error) {
629
+ throw new Error(
630
+ `Failed to install dependencies with ${packageManager}: ${error instanceof Error ? error.message : "Unknown error"}`
631
+ );
632
+ }
633
+ }
634
+ /**
635
+ * Detect which package manager is being used
636
+ */
637
+ async detectPackageManager(cwd) {
638
+ try {
639
+ return await (0, import_detect_package_manager.detect)({ cwd });
640
+ } catch (error) {
641
+ return "npm";
642
+ }
643
+ }
644
+ };
645
+ }
646
+ });
647
+
648
+ // src/installer/index.ts
649
+ var installer_exports = {};
650
+ __export(installer_exports, {
651
+ AppModuleUpdater: () => AppModuleUpdater,
652
+ DependencyInstaller: () => DependencyInstaller,
653
+ PackageUpdater: () => PackageUpdater
654
+ });
655
+ var init_installer = __esm({
656
+ "src/installer/index.ts"() {
657
+ "use strict";
658
+ init_cjs_shims();
659
+ init_ast_updater();
660
+ init_package_updater();
661
+ init_dependency_installer();
662
+ }
663
+ });
664
+
665
+ // src/index.ts
666
+ var index_exports = {};
667
+ __export(index_exports, {
668
+ run: () => run
669
+ });
670
+ module.exports = __toCommonJS(index_exports);
671
+ init_cjs_shims();
672
+
673
+ // src/analyzer/index.ts
674
+ init_cjs_shims();
675
+
676
+ // src/analyzer/project-detector.ts
677
+ init_cjs_shims();
678
+ var path = __toESM(require("path"));
679
+ var fs = __toESM(require("fs-extra"));
680
+
681
+ // src/analyzer/orm-detector.ts
682
+ init_cjs_shims();
683
+ async function detectORM(packageJson) {
684
+ const dependencies = {
685
+ ...packageJson.dependencies,
686
+ ...packageJson.devDependencies
687
+ };
688
+ if (dependencies["@nestjs/typeorm"] || dependencies["typeorm"]) {
689
+ return "typeorm";
690
+ }
691
+ if (dependencies["@prisma/client"] || dependencies["prisma"]) {
692
+ return "prisma";
693
+ }
694
+ if (dependencies["@nestjs/mongoose"] || dependencies["mongoose"]) {
695
+ return "mongoose";
696
+ }
697
+ return "none";
698
+ }
699
+
700
+ // src/analyzer/project-detector.ts
701
+ var ProjectDetector = class {
702
+ constructor(cwd) {
703
+ this.cwd = cwd;
704
+ }
705
+ /**
706
+ * Detect and validate a NestJS project
707
+ */
708
+ async detectProject() {
709
+ const errors = [];
710
+ const root = this.cwd;
711
+ const packageJsonPath = path.join(root, "package.json");
712
+ if (!await fs.pathExists(packageJsonPath)) {
713
+ errors.push("package.json not found");
714
+ return this.createInvalidProject(root, errors);
715
+ }
716
+ const packageJson = await this.readPackageJson(packageJsonPath);
717
+ if (!packageJson) {
718
+ errors.push("Failed to read package.json");
719
+ return this.createInvalidProject(root, errors);
720
+ }
721
+ const hasNestCore = packageJson.dependencies?.["@nestjs/core"];
722
+ const hasNestCommon = packageJson.dependencies?.["@nestjs/common"];
723
+ if (!hasNestCore || !hasNestCommon) {
724
+ errors.push("Not a NestJS project (missing @nestjs/core or @nestjs/common)");
725
+ return this.createInvalidProject(root, errors);
726
+ }
727
+ const nestCliConfigPath = path.join(root, "nest-cli.json");
728
+ const nestCliConfig = await this.readNestCliConfig(nestCliConfigPath);
729
+ const sourceRoot = nestCliConfig?.sourceRoot || "src";
730
+ const appModulePath = path.join(root, sourceRoot, "app.module.ts");
731
+ if (!await fs.pathExists(appModulePath)) {
732
+ errors.push(`app.module.ts not found at ${sourceRoot}/app.module.ts`);
733
+ return this.createInvalidProject(root, errors);
734
+ }
735
+ const orm = await detectORM(packageJson);
736
+ const authModulePath = path.join(root, sourceRoot, "auth");
737
+ if (await fs.pathExists(authModulePath)) {
738
+ errors.push("auth/ directory already exists (use --force to overwrite)");
739
+ }
740
+ return {
741
+ root,
742
+ sourceRoot,
743
+ appModulePath,
744
+ packageJsonPath,
745
+ nestCliConfigPath,
746
+ orm,
747
+ nestVersion: packageJson.dependencies?.["@nestjs/core"],
748
+ typescriptVersion: packageJson.devDependencies?.["typescript"],
749
+ isValid: errors.length === 0,
750
+ errors
751
+ };
752
+ }
753
+ /**
754
+ * Read and parse package.json
755
+ */
756
+ async readPackageJson(packageJsonPath) {
757
+ try {
758
+ const content = await fs.readFile(packageJsonPath, "utf-8");
759
+ return JSON.parse(content);
760
+ } catch (error) {
761
+ return null;
762
+ }
763
+ }
764
+ /**
765
+ * Read and parse nest-cli.json
766
+ */
767
+ async readNestCliConfig(nestCliConfigPath) {
768
+ try {
769
+ if (!await fs.pathExists(nestCliConfigPath)) {
770
+ return null;
771
+ }
772
+ const content = await fs.readFile(nestCliConfigPath, "utf-8");
773
+ return JSON.parse(content);
774
+ } catch (error) {
775
+ return null;
776
+ }
777
+ }
778
+ /**
779
+ * Create invalid project info object
780
+ */
781
+ createInvalidProject(root, errors) {
782
+ return {
783
+ root,
784
+ sourceRoot: "src",
785
+ appModulePath: path.join(root, "src", "app.module.ts"),
786
+ packageJsonPath: path.join(root, "package.json"),
787
+ nestCliConfigPath: path.join(root, "nest-cli.json"),
788
+ orm: "none",
789
+ isValid: false,
790
+ errors
791
+ };
792
+ }
793
+ };
794
+ async function detectProject(cwd = process.cwd()) {
795
+ const detector = new ProjectDetector(cwd);
796
+ return detector.detectProject();
797
+ }
798
+
799
+ // src/cli/prompts.ts
800
+ init_cjs_shims();
801
+ var import_inquirer = __toESM(require("inquirer"));
802
+
803
+ // src/config/utils.ts
804
+ init_cjs_shims();
805
+ var import_crypto = require("crypto");
806
+ function generateSecret(length = 32) {
807
+ return (0, import_crypto.randomBytes)(length).toString("base64");
808
+ }
809
+
810
+ // src/cli/prompts.ts
811
+ async function promptConfig(detectedORM) {
812
+ const answers = await import_inquirer.default.prompt([
813
+ {
814
+ type: "list",
815
+ name: "strategy",
816
+ message: "Choose authentication strategy:",
817
+ choices: [
818
+ { name: "JWT Authentication (Recommended)", value: "jwt" },
819
+ { name: "OAuth 2.0 (Google, GitHub) [v1.1]", value: "oauth", disabled: true },
820
+ { name: "Session-based (Traditional) [v1.2]", value: "session", disabled: true }
821
+ ],
822
+ default: "jwt"
823
+ },
824
+ {
825
+ type: "confirm",
826
+ name: "enableRBAC",
827
+ message: "Enable Role-Based Access Control (RBAC)?",
828
+ default: true
829
+ },
830
+ {
831
+ type: "checkbox",
832
+ name: "roles",
833
+ message: "Select default roles:",
834
+ choices: [
835
+ { name: "Admin", value: "Admin", checked: true },
836
+ { name: "User", value: "User", checked: true },
837
+ { name: "Moderator", value: "Moderator", checked: false },
838
+ { name: "Guest", value: "Guest", checked: false }
839
+ ],
840
+ when: (answers2) => answers2.enableRBAC,
841
+ validate: (input) => {
842
+ if (input.length === 0) {
843
+ return "Please select at least one role";
844
+ }
845
+ return true;
846
+ }
847
+ },
848
+ {
849
+ type: "confirm",
850
+ name: "refreshTokens",
851
+ message: "Enable Refresh Token rotation?",
852
+ default: true
853
+ },
854
+ {
855
+ type: "list",
856
+ name: "accessExpiration",
857
+ message: "JWT Access Token expiration:",
858
+ choices: [
859
+ { name: "15 minutes", value: "15m" },
860
+ { name: "30 minutes", value: "30m" },
861
+ { name: "1 hour (Recommended)", value: "1h" },
862
+ { name: "4 hours", value: "4h" },
863
+ { name: "1 day", value: "1d" }
864
+ ],
865
+ default: "1h"
866
+ },
867
+ {
868
+ type: "list",
869
+ name: "refreshExpiration",
870
+ message: "JWT Refresh Token expiration:",
871
+ choices: [
872
+ { name: "7 days (Recommended)", value: "7d" },
873
+ { name: "30 days", value: "30d" },
874
+ { name: "90 days", value: "90d" },
875
+ { name: "1 year", value: "1y" }
876
+ ],
877
+ default: "7d",
878
+ when: (answers2) => answers2.refreshTokens
879
+ },
880
+ {
881
+ type: "confirm",
882
+ name: "useDetectedORM",
883
+ message: `Detected ${detectedORM.toUpperCase()}${detectedORM === "typeorm" ? " with PostgreSQL" : ""}. Use it?`,
884
+ default: true,
885
+ when: () => detectedORM !== "none"
886
+ },
887
+ {
888
+ type: "list",
889
+ name: "database",
890
+ message: "Select database:",
891
+ choices: [
892
+ { name: "PostgreSQL (Recommended)", value: "postgres" },
893
+ { name: "MySQL", value: "mysql" },
894
+ { name: "SQLite (for testing)", value: "sqlite" },
895
+ { name: "MongoDB", value: "mongodb" }
896
+ ],
897
+ default: "postgres",
898
+ when: (answers2) => detectedORM === "none" || !answers2.useDetectedORM
899
+ },
900
+ {
901
+ type: "confirm",
902
+ name: "autoInstall",
903
+ message: "Auto-install dependencies after generation?",
904
+ default: true
905
+ }
906
+ ]);
907
+ return answers;
908
+ }
909
+ function buildConfig(answers, projectName, sourceRoot, detectedORM) {
910
+ const config = {
911
+ projectName,
912
+ sourceRoot,
913
+ strategy: answers.strategy,
914
+ rbac: {
915
+ enabled: answers.enableRBAC,
916
+ roles: answers.roles || []
917
+ },
918
+ orm: answers.useDetectedORM !== false ? detectedORM : "typeorm",
919
+ database: answers.database || "postgres",
920
+ features: {
921
+ refreshTokens: answers.refreshTokens
922
+ },
923
+ jwt: {
924
+ secret: generateSecret(),
925
+ accessExpiration: answers.accessExpiration,
926
+ refreshExpiration: answers.refreshExpiration || "7d"
927
+ },
928
+ autoInstall: answers.autoInstall,
929
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
930
+ generatorVersion: "1.0.0"
931
+ };
932
+ return config;
933
+ }
934
+
935
+ // src/cli/ui.ts
936
+ init_cjs_shims();
937
+ var import_chalk = __toESM(require("chalk"));
938
+ var import_ora = __toESM(require("ora"));
939
+ function showBanner() {
940
+ console.log(import_chalk.default.cyan(`
941
+ ___ _ _ __ __
942
+ / _ \\ | | | | | \\/ |
943
+ / /_\\ \\_ _ | |_| |__ | \\ / | ___
944
+ | _ | | | || __| '_ \\ | |\\/| |/ _ \\
945
+ | | | | |_| || |_| | | | | | | | __/
946
+ \\_| |_/\\__,_| \\__|_| |_| \\_| |_/\\___|
947
+ `));
948
+ console.log(import_chalk.default.bold("\u{1F510} NestJS Authentication Module Generator v1.0.0"));
949
+ console.log();
950
+ }
951
+ function showProjectInfo(info) {
952
+ console.log(import_chalk.default.green("\u2713"), `Detected NestJS ${info.nestVersion || "project"}`);
953
+ if (info.orm !== "none") {
954
+ console.log(import_chalk.default.green("\u2713"), `Found ${info.orm.toUpperCase()}`);
955
+ }
956
+ console.log(import_chalk.default.green("\u2713"), `Source directory: ${info.sourceRoot}/`);
957
+ console.log(import_chalk.default.green("\u2713"), "No existing auth module found");
958
+ console.log();
959
+ }
960
+ function showError(message, errors) {
961
+ console.log();
962
+ console.log(import_chalk.default.red("\u274C Error:"), import_chalk.default.bold(message));
963
+ if (errors && errors.length > 0) {
964
+ console.log();
965
+ errors.forEach((error) => {
966
+ console.log(import_chalk.default.red(" \u2022"), error);
967
+ });
968
+ }
969
+ console.log();
970
+ }
971
+ function showNestJSHelp() {
972
+ console.log(import_chalk.default.yellow("To create a new NestJS project:"));
973
+ console.log();
974
+ console.log(import_chalk.default.cyan(" npm i -g @nestjs/cli"));
975
+ console.log(import_chalk.default.cyan(" nest new my-project"));
976
+ console.log();
977
+ }
978
+ function showSuccess(stats) {
979
+ console.log();
980
+ console.log(import_chalk.default.green.bold("\u{1F389} Success!"), "Authentication module generated.");
981
+ console.log();
982
+ console.log(import_chalk.default.bold("\u{1F4C1} Files created:"));
983
+ console.log(` \u2022 ${stats.filesCreated} new files in src/auth/ and src/users/`);
984
+ console.log(` \u2022 Updated src/app.module.ts`);
985
+ console.log(` \u2022 Updated package.json`);
986
+ console.log();
987
+ console.log(import_chalk.default.bold("\u{1F4E6} Dependencies added:"));
988
+ console.log(` \u2022 @nestjs/jwt, @nestjs/passport, @nestjs/config`);
989
+ console.log(` \u2022 passport, passport-jwt, passport-local`);
990
+ console.log(` \u2022 bcrypt, class-validator, class-transformer`);
991
+ console.log(` \u2022 ${stats.dependenciesAdded} packages total`);
992
+ console.log();
993
+ console.log(import_chalk.default.bold("\u{1F510} JWT Configuration:"));
994
+ console.log(` \u2022 Access token: ${stats.jwt.accessExpiration}`);
995
+ if (stats.jwt.refreshExpiration) {
996
+ console.log(` \u2022 Refresh token: ${stats.jwt.refreshExpiration}`);
997
+ }
998
+ console.log(` \u2022 Secret: Auto-generated (see .env.example)`);
999
+ console.log();
1000
+ console.log(import_chalk.default.bold("\u{1F4CB} Next steps:"));
1001
+ console.log(import_chalk.default.cyan(" 1. Copy .env.example to .env"));
1002
+ console.log(import_chalk.default.gray(" cp .env.example .env"));
1003
+ console.log();
1004
+ console.log(import_chalk.default.cyan(" 2. Update JWT_SECRET in .env (or keep auto-generated)"));
1005
+ console.log();
1006
+ console.log(import_chalk.default.cyan(" 3. Create database migration (if using TypeORM)"));
1007
+ console.log(import_chalk.default.gray(" npm run migration:generate -- src/migrations/CreateUserTable"));
1008
+ console.log(import_chalk.default.gray(" npm run migration:run"));
1009
+ console.log();
1010
+ console.log(import_chalk.default.cyan(" 4. Start your NestJS app"));
1011
+ console.log(import_chalk.default.gray(" npm run start:dev"));
1012
+ console.log();
1013
+ console.log(import_chalk.default.cyan(" 5. Test authentication endpoints"));
1014
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/register"));
1015
+ console.log(import_chalk.default.gray(" POST http://localhost:3000/auth/login"));
1016
+ console.log(import_chalk.default.gray(" GET http://localhost:3000/users/profile (requires JWT)"));
1017
+ console.log();
1018
+ console.log(import_chalk.default.bold("\u{1F4D6} Full documentation:"), "src/auth/README.md");
1019
+ console.log();
1020
+ console.log(import_chalk.default.bold("\u{1F4A1} Tips:"));
1021
+ console.log(" \u2022 Use @Public() decorator for routes that don't require auth");
1022
+ console.log(" \u2022 Use @Roles('Admin') to restrict routes by role");
1023
+ console.log(" \u2022 Access current user with @CurrentUser() decorator");
1024
+ console.log();
1025
+ }
1026
+ function createSpinner(text) {
1027
+ return (0, import_ora.default)({
1028
+ text,
1029
+ color: "cyan"
1030
+ });
1031
+ }
1032
+
1033
+ // src/index.ts
1034
+ async function run(cwd = process.cwd()) {
1035
+ showBanner();
1036
+ const spinner = createSpinner("Analyzing project...").start();
1037
+ const projectInfo = await detectProject(cwd);
1038
+ if (!projectInfo.isValid) {
1039
+ spinner.fail("Project validation failed");
1040
+ showError("Not a valid NestJS project", projectInfo.errors);
1041
+ showNestJSHelp();
1042
+ process.exit(1);
1043
+ }
1044
+ spinner.succeed("Project analyzed");
1045
+ showProjectInfo({
1046
+ nestVersion: projectInfo.nestVersion,
1047
+ orm: projectInfo.orm,
1048
+ sourceRoot: projectInfo.sourceRoot
1049
+ });
1050
+ const answers = await promptConfig(projectInfo.orm);
1051
+ const config = buildConfig(
1052
+ answers,
1053
+ projectInfo.root.split(/[/\\]/).pop() || "project",
1054
+ projectInfo.sourceRoot,
1055
+ projectInfo.orm
1056
+ );
1057
+ console.log();
1058
+ console.log("\u2699\uFE0F Generating authentication module...");
1059
+ console.log();
1060
+ const { Generator: Generator2 } = await Promise.resolve().then(() => (init_generator2(), generator_exports));
1061
+ const generator = new Generator2();
1062
+ const genSpinner = createSpinner("Generating files from templates...").start();
1063
+ const result = await generator.generate(config, projectInfo);
1064
+ if (!result.success) {
1065
+ genSpinner.fail("Generation failed");
1066
+ showError("Failed to generate files", [result.error || "Unknown error"]);
1067
+ process.exit(1);
1068
+ }
1069
+ genSpinner.succeed(`Generated ${result.filesCreated.length} files`);
1070
+ const astSpinner = createSpinner("Updating app.module.ts...").start();
1071
+ try {
1072
+ const { AppModuleUpdater: AppModuleUpdater2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
1073
+ const astUpdater = new AppModuleUpdater2(projectInfo.appModulePath);
1074
+ await astUpdater.update();
1075
+ await astUpdater.cleanupBackup();
1076
+ astSpinner.succeed("Updated app.module.ts");
1077
+ } catch (error) {
1078
+ astSpinner.fail("Failed to update app.module.ts");
1079
+ showError(
1080
+ "AST modification failed",
1081
+ [error instanceof Error ? error.message : "Unknown error"]
1082
+ );
1083
+ process.exit(1);
1084
+ }
1085
+ const pkgSpinner = createSpinner("Updating package.json...").start();
1086
+ try {
1087
+ const { PackageUpdater: PackageUpdater2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
1088
+ const pkgUpdater = new PackageUpdater2(projectInfo.packageJsonPath);
1089
+ await pkgUpdater.update(config);
1090
+ await pkgUpdater.cleanupBackup();
1091
+ pkgSpinner.succeed("Updated package.json");
1092
+ } catch (error) {
1093
+ pkgSpinner.fail("Failed to update package.json");
1094
+ showError(
1095
+ "Package update failed",
1096
+ [error instanceof Error ? error.message : "Unknown error"]
1097
+ );
1098
+ process.exit(1);
1099
+ }
1100
+ if (config.autoInstall) {
1101
+ const installSpinner = createSpinner("Installing dependencies...").start();
1102
+ try {
1103
+ const { DependencyInstaller: DependencyInstaller2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
1104
+ const installer = new DependencyInstaller2();
1105
+ await installer.install(projectInfo.root);
1106
+ installSpinner.succeed("Dependencies installed");
1107
+ } catch (error) {
1108
+ installSpinner.fail("Failed to install dependencies");
1109
+ console.log(
1110
+ "\n\u26A0\uFE0F Please run npm install manually to install dependencies\n"
1111
+ );
1112
+ }
1113
+ }
1114
+ showSuccess({
1115
+ filesCreated: result.filesCreated.length,
1116
+ dependenciesAdded: 8,
1117
+ jwt: {
1118
+ accessExpiration: config.jwt.accessExpiration,
1119
+ refreshExpiration: config.features.refreshTokens ? config.jwt.refreshExpiration : void 0
1120
+ }
1121
+ });
1122
+ console.log("\u{1F41B} Issues? https://github.com/yourusername/add-nest-auth/issues");
1123
+ console.log("\u2B50 Like it? https://github.com/yourusername/add-nest-auth");
1124
+ console.log();
1125
+ }
1126
+ // Annotate the CommonJS export names for ESM import in node:
1127
+ 0 && (module.exports = {
1128
+ run
1129
+ });
1130
+ //# sourceMappingURL=index.js.map