vako 1.3.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.
@@ -0,0 +1,737 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
3
+ const ora = require('ora');
4
+ const boxen = require('boxen');
5
+ const figlet = require('figlet');
6
+ const gradient = require('gradient-string');
7
+ const { createSpinner } = require('nanospinner');
8
+ const path = require('path');
9
+ const fs = require('fs').promises;
10
+
11
+ /**
12
+ * Assistant de configuration interactif pour les projets Veko.js
13
+ * Fournit une interface utilisateur complète pour la création de projets
14
+ */
15
+ class SetupWizard {
16
+ constructor() {
17
+ this.config = {
18
+ projectName: '',
19
+ template: 'default',
20
+ features: [],
21
+ database: 'sqlite',
22
+ auth: { enabled: false },
23
+ plugins: [],
24
+ styling: 'bootstrap',
25
+ theme: 'light',
26
+ git: true,
27
+ install: true,
28
+ description: '',
29
+ author: '',
30
+ license: 'MIT',
31
+ scripts: true,
32
+ docker: false,
33
+ env: true
34
+ };
35
+
36
+ // Configuration de sécurité
37
+ this.securityConfig = {
38
+ maxProjectNameLength: 50,
39
+ maxDescriptionLength: 200,
40
+ maxAuthorLength: 100,
41
+ allowedFileNameChars: /^[a-zA-Z0-9\-_]+$/,
42
+ maxFeatures: 20,
43
+ maxPlugins: 15
44
+ };
45
+
46
+ // Templates prédéfinis
47
+ this.templates = new Map([
48
+ ['default', {
49
+ name: '🌟 Default - Full-featured web application',
50
+ description: 'Complete web application with all features',
51
+ files: ['views/', 'routes/', 'public/', 'plugins/', 'middleware/', 'app.js']
52
+ }],
53
+ ['api', {
54
+ name: '🔌 API Only - REST API server',
55
+ description: 'RESTful API server with authentication',
56
+ files: ['routes/api/', 'middleware/', 'models/', 'tests/', 'server.js']
57
+ }],
58
+ ['blog', {
59
+ name: '📝 Blog - Content management system',
60
+ description: 'Blog engine with admin interface',
61
+ files: ['views/blog/', 'content/posts/', 'admin/', 'uploads/', 'blog.js']
62
+ }],
63
+ ['admin', {
64
+ name: '👑 Admin Dashboard - Management interface',
65
+ description: 'Administrative dashboard and management tools',
66
+ files: ['admin/views/', 'dashboard/', 'auth/', 'api/admin/', 'admin.js']
67
+ }],
68
+ ['ecommerce', {
69
+ name: '🛍️ E-commerce - Online store',
70
+ description: 'Complete e-commerce solution',
71
+ files: ['shop/views/', 'products/', 'orders/', 'payments/', 'store.js']
72
+ }],
73
+ ['portfolio', {
74
+ name: '🎭 Portfolio - Personal showcase',
75
+ description: 'Personal portfolio and project showcase',
76
+ files: ['portfolio/views/', 'projects/', 'gallery/', 'blog/', 'portfolio.js']
77
+ }],
78
+ ['pwa', {
79
+ name: '📱 PWA - Progressive Web App',
80
+ description: 'Progressive Web Application with offline support',
81
+ files: ['pwa/', 'sw/', 'manifest/', 'offline/', 'pwa.js']
82
+ }]
83
+ ]);
84
+ }
85
+
86
+ /**
87
+ * Point d'entrée principal du wizard
88
+ */
89
+ async start() {
90
+ try {
91
+ console.clear();
92
+ await this.showWelcome();
93
+ await this.gatherProjectInfo();
94
+ await this.selectTemplate();
95
+ await this.selectFeatures();
96
+ await this.configureDatabase();
97
+ await this.configureAuth();
98
+ await this.selectPlugins();
99
+ await this.selectStyling();
100
+ await this.finalOptions();
101
+ await this.confirmOptions();
102
+ await this.executeSetup();
103
+ await this.showCompletion();
104
+ } catch (error) {
105
+ await this.handleError(error);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Affichage de l'écran d'accueil avec titre stylisé
111
+ */
112
+ async showWelcome() {
113
+ try {
114
+ const title = figlet.textSync('VEKO.JS', {
115
+ font: 'ANSI Shadow',
116
+ horizontalLayout: 'fitted'
117
+ });
118
+
119
+ console.log(gradient.rainbow(title));
120
+ } catch (error) {
121
+ // Fallback si figlet échoue
122
+ console.log(chalk.cyan.bold('╔══════════════════════════════════╗'));
123
+ console.log(chalk.cyan.bold('║ VEKO.JS ║'));
124
+ console.log(chalk.cyan.bold('╚══════════════════════════════════╝'));
125
+ }
126
+
127
+ console.log(chalk.cyan.bold('\n✨ Interactive Project Setup Wizard ✨\n'));
128
+
129
+ const welcomeBox = boxen(
130
+ chalk.white('🎉 Welcome to Veko.js Setup Wizard!\n\n') +
131
+ chalk.gray('This wizard will guide you through creating a new\n') +
132
+ chalk.gray('Veko.js project with all the features you need.\n\n') +
133
+ chalk.blue('✓ Templates & Examples\n') +
134
+ chalk.blue('✓ Authentication System\n') +
135
+ chalk.blue('✓ Database Integration\n') +
136
+ chalk.blue('✓ Plugin Ecosystem\n') +
137
+ chalk.blue('✓ Beautiful UI Frameworks'),
138
+ {
139
+ padding: 1,
140
+ margin: 1,
141
+ borderStyle: 'double',
142
+ borderColor: 'cyan',
143
+ textAlignment: 'center'
144
+ }
145
+ );
146
+
147
+ console.log(welcomeBox);
148
+
149
+ const { ready } = await inquirer.prompt([{
150
+ type: 'confirm',
151
+ name: 'ready',
152
+ message: '🚀 Ready to create something amazing?',
153
+ default: true
154
+ }]);
155
+
156
+ if (!ready) {
157
+ console.log(chalk.yellow('\n👋 See you later! Happy coding!'));
158
+ process.exit(0);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Collecte des informations de base du projet
164
+ */
165
+ async gatherProjectInfo() {
166
+ console.log(chalk.blue.bold('\n📝 Project Information\n'));
167
+
168
+ const questions = [
169
+ {
170
+ type: 'input',
171
+ name: 'projectName',
172
+ message: '📁 What\'s your project name?',
173
+ default: 'my-veko-app',
174
+ validate: (input) => this.validateProjectName(input)
175
+ },
176
+ {
177
+ type: 'input',
178
+ name: 'description',
179
+ message: '📄 Project description:',
180
+ default: 'A modern web application built with Veko.js',
181
+ validate: (input) => this.validateDescription(input)
182
+ },
183
+ {
184
+ type: 'input',
185
+ name: 'author',
186
+ message: '👤 Author name:',
187
+ default: process.env.USER || process.env.USERNAME || '',
188
+ validate: (input) => this.validateAuthor(input)
189
+ },
190
+ {
191
+ type: 'list',
192
+ name: 'license',
193
+ message: '📜 Choose a license:',
194
+ choices: [
195
+ { name: '📋 MIT - Most permissive', value: 'MIT' },
196
+ { name: '🔒 ISC - Simple and permissive', value: 'ISC' },
197
+ { name: '⚖️ Apache-2.0 - Patent protection', value: 'Apache-2.0' },
198
+ { name: '🆓 GPL-3.0 - Copyleft', value: 'GPL-3.0' },
199
+ { name: '🚫 Unlicense - Public domain', value: 'Unlicense' }
200
+ ],
201
+ default: 'MIT'
202
+ }
203
+ ];
204
+
205
+ const answers = await inquirer.prompt(questions);
206
+ Object.assign(this.config, this.sanitizeProjectInfo(answers));
207
+ }
208
+
209
+ /**
210
+ * Sélection du template de projet
211
+ */
212
+ async selectTemplate() {
213
+ console.log(chalk.blue.bold('\n🎨 Choose Your Template\n'));
214
+
215
+ const templateChoices = Array.from(this.templates.entries()).map(([value, template]) => ({
216
+ name: template.name,
217
+ value
218
+ }));
219
+
220
+ const { template } = await inquirer.prompt([{
221
+ type: 'list',
222
+ name: 'template',
223
+ message: '🎯 Select a template:',
224
+ choices: templateChoices,
225
+ pageSize: 10
226
+ }]);
227
+
228
+ this.config.template = template;
229
+ this.showTemplatePreview(template);
230
+ }
231
+
232
+ /**
233
+ * Affichage de l'aperçu du template sélectionné
234
+ */
235
+ showTemplatePreview(templateName) {
236
+ const template = this.templates.get(templateName);
237
+ if (!template) return;
238
+
239
+ const preview = template.files.map(file => `📁 ${file}`).join('\n');
240
+
241
+ const previewBox = boxen(
242
+ chalk.cyan('📋 Template Structure:\n\n') +
243
+ chalk.gray(preview) + '\n\n' +
244
+ chalk.blue('Description: ') + chalk.white(template.description),
245
+ {
246
+ padding: 1,
247
+ margin: { top: 1, bottom: 1, left: 2, right: 2 },
248
+ borderStyle: 'round',
249
+ borderColor: 'blue',
250
+ title: '📦 Project Structure',
251
+ titleAlignment: 'center'
252
+ }
253
+ );
254
+
255
+ console.log(previewBox);
256
+ }
257
+
258
+ /**
259
+ * Sélection des fonctionnalités
260
+ */
261
+ async selectFeatures() {
262
+ console.log(chalk.blue.bold('\n⚡ Select Features & Add-ons\n'));
263
+
264
+ const featureChoices = [
265
+ { name: '🔥 Hot Reload Development Server', value: 'hotreload', checked: true },
266
+ { name: '📱 Progressive Web App (PWA)', value: 'pwa' },
267
+ { name: '🎨 Advanced Layout System', value: 'layouts', checked: true },
268
+ { name: '🔍 SEO Optimization', value: 'seo' },
269
+ { name: '📊 Analytics Integration', value: 'analytics' },
270
+ { name: '💬 Real-time WebSocket Support', value: 'websocket' },
271
+ { name: '📧 Email System (Nodemailer)', value: 'email' },
272
+ { name: '🔒 Rate Limiting & Security', value: 'security' },
273
+ { name: '📁 File Upload System', value: 'upload' },
274
+ { name: '🌐 Multi-language (i18n)', value: 'i18n' },
275
+ { name: '📋 Form Validation', value: 'validation' },
276
+ { name: '🎭 Component System', value: 'components' },
277
+ { name: '🗜️ Image Processing', value: 'imageprocessing' },
278
+ { name: '🔄 Backup System', value: 'backup' }
279
+ ];
280
+
281
+ const { features } = await inquirer.prompt([{
282
+ type: 'checkbox',
283
+ name: 'features',
284
+ message: '🎁 Which features would you like to include?',
285
+ choices: featureChoices,
286
+ pageSize: 15,
287
+ validate: (input) => this.validateFeatures(input)
288
+ }]);
289
+
290
+ this.config.features = features;
291
+ }
292
+
293
+ /**
294
+ * Configuration de la base de données
295
+ */
296
+ async configureDatabase() {
297
+ const dbRequiredTemplates = ['api', 'blog', 'admin', 'ecommerce'];
298
+
299
+ if (dbRequiredTemplates.includes(this.config.template)) {
300
+ console.log(chalk.blue.bold('\n🗄️ Database Configuration\n'));
301
+
302
+ const databaseChoices = [
303
+ { name: '📄 SQLite - File-based (recommended for dev)', value: 'sqlite' },
304
+ { name: '🐘 PostgreSQL - Advanced relational database', value: 'postgresql' },
305
+ { name: '🐬 MySQL - Popular relational database', value: 'mysql' },
306
+ { name: '🍃 MongoDB - Document database', value: 'mongodb' },
307
+ { name: '⚡ Redis - In-memory cache/database', value: 'redis' },
308
+ { name: '🚫 None - No database', value: 'none' }
309
+ ];
310
+
311
+ const { database } = await inquirer.prompt([{
312
+ type: 'list',
313
+ name: 'database',
314
+ message: '💾 Choose your database:',
315
+ choices: databaseChoices
316
+ }]);
317
+
318
+ this.config.database = database;
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Configuration du système d'authentification
324
+ */
325
+ async configureAuth() {
326
+ const authRequiredTemplates = ['default', 'blog', 'admin', 'ecommerce'];
327
+
328
+ if (authRequiredTemplates.includes(this.config.template)) {
329
+ console.log(chalk.blue.bold('\n🔐 Authentication System\n'));
330
+
331
+ const { enableAuth } = await inquirer.prompt([{
332
+ type: 'confirm',
333
+ name: 'enableAuth',
334
+ message: '🔑 Enable authentication system?',
335
+ default: ['admin', 'ecommerce'].includes(this.config.template)
336
+ }]);
337
+
338
+ if (enableAuth) {
339
+ const authConfig = await this.configureAuthDetails();
340
+ this.config.auth = { enabled: true, ...authConfig };
341
+ } else {
342
+ this.config.auth = { enabled: false };
343
+ }
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Configuration détaillée de l'authentification
349
+ */
350
+ async configureAuthDetails() {
351
+ return await inquirer.prompt([
352
+ {
353
+ type: 'checkbox',
354
+ name: 'methods',
355
+ message: '🚪 Authentication methods:',
356
+ choices: [
357
+ { name: '📧 Email/Password (Local)', value: 'local', checked: true },
358
+ { name: '🌐 Google OAuth', value: 'google' },
359
+ { name: '📘 Facebook OAuth', value: 'facebook' },
360
+ { name: '🐙 GitHub OAuth', value: 'github' },
361
+ { name: '💼 LinkedIn OAuth', value: 'linkedin' },
362
+ { name: '🔗 JWT Tokens', value: 'jwt' }
363
+ ],
364
+ validate: (input) => input.length > 0 || 'At least one method is required'
365
+ },
366
+ {
367
+ type: 'checkbox',
368
+ name: 'features',
369
+ message: '🛡️ Authentication features:',
370
+ choices: [
371
+ { name: '👤 User profiles', value: 'profiles', checked: true },
372
+ { name: '👑 Role-based access control', value: 'roles' },
373
+ { name: '📧 Email verification', value: 'emailVerification' },
374
+ { name: '🔄 Password reset', value: 'passwordReset' },
375
+ { name: '🔒 Two-factor authentication', value: '2fa' },
376
+ { name: '📊 Login analytics', value: 'analytics' },
377
+ { name: '🚫 Account lockout', value: 'lockout' }
378
+ ]
379
+ }
380
+ ]);
381
+ }
382
+
383
+ /**
384
+ * Sélection des plugins
385
+ */
386
+ async selectPlugins() {
387
+ console.log(chalk.blue.bold('\n🔌 Plugins & Extensions\n'));
388
+
389
+ const pluginChoices = [
390
+ { name: '📊 Logger - Advanced request/error logging', value: 'logger', checked: true },
391
+ { name: '🛡️ Security - Helmet, CORS, rate limiting', value: 'security', checked: true },
392
+ { name: '⚡ Cache - Redis/Memory caching system', value: 'cache' },
393
+ { name: '📈 Monitoring - Health checks & metrics', value: 'monitoring' },
394
+ { name: '📦 Compression - Gzip response compression', value: 'compression' },
395
+ { name: '🔄 Backup - Automated data backups', value: 'backup' },
396
+ { name: '🎨 Image Processing - Sharp/Jimp integration', value: 'images' },
397
+ { name: '📧 Mailer - Email templates & sending', value: 'mailer' },
398
+ { name: '📅 Scheduler - Cron jobs & tasks', value: 'scheduler' },
399
+ { name: '🔍 Search - Full-text search engine', value: 'search' },
400
+ { name: '📱 Push Notifications', value: 'notifications' },
401
+ { name: '🏪 Session Store - Persistent sessions', value: 'sessionstore' }
402
+ ];
403
+
404
+ const { plugins } = await inquirer.prompt([{
405
+ type: 'checkbox',
406
+ name: 'plugins',
407
+ message: '🎯 Select plugins to install:',
408
+ choices: pluginChoices,
409
+ pageSize: 12,
410
+ validate: (input) => this.validatePlugins(input)
411
+ }]);
412
+
413
+ this.config.plugins = plugins;
414
+ }
415
+
416
+ /**
417
+ * Sélection du framework de style
418
+ */
419
+ async selectStyling() {
420
+ console.log(chalk.blue.bold('\n🎨 Styling & UI Framework\n'));
421
+
422
+ const stylingQuestions = [
423
+ {
424
+ type: 'list',
425
+ name: 'framework',
426
+ message: '🎭 Choose a CSS framework:',
427
+ choices: [
428
+ { name: '🅱️ Bootstrap 5 - Popular component library', value: 'bootstrap' },
429
+ { name: '🎯 Tailwind CSS - Utility-first framework', value: 'tailwind' },
430
+ { name: '🎪 Bulma - Modern CSS framework', value: 'bulma' },
431
+ { name: '⚡ Material Design - Google Material UI', value: 'material' },
432
+ { name: '🎨 Foundation - Responsive front-end framework', value: 'foundation' },
433
+ { name: '🎭 Semantic UI - Human-friendly HTML', value: 'semantic' },
434
+ { name: '🖼️ Custom CSS - Write your own styles', value: 'custom' },
435
+ { name: '🚫 None - No CSS framework', value: 'none' }
436
+ ]
437
+ },
438
+ {
439
+ type: 'list',
440
+ name: 'theme',
441
+ message: '🌈 Color theme preference:',
442
+ choices: [
443
+ { name: '🌅 Light - Clean and bright', value: 'light' },
444
+ { name: '🌙 Dark - Easy on the eyes', value: 'dark' },
445
+ { name: '🎨 Auto - Follow system preference', value: 'auto' },
446
+ { name: '🌈 Custom - Define your own colors', value: 'custom' }
447
+ ],
448
+ when: (answers) => answers.framework !== 'none'
449
+ }
450
+ ];
451
+
452
+ const stylingAnswers = await inquirer.prompt(stylingQuestions);
453
+ this.config.styling = stylingAnswers.framework;
454
+ this.config.theme = stylingAnswers.theme || 'light';
455
+ }
456
+
457
+ /**
458
+ * Options finales de configuration
459
+ */
460
+ async finalOptions() {
461
+ console.log(chalk.blue.bold('\n⚙️ Final Configuration\n'));
462
+
463
+ const finalQuestions = [
464
+ {
465
+ type: 'confirm',
466
+ name: 'git',
467
+ message: '📦 Initialize Git repository?',
468
+ default: true
469
+ },
470
+ {
471
+ type: 'confirm',
472
+ name: 'install',
473
+ message: '📥 Install dependencies automatically?',
474
+ default: true
475
+ },
476
+ {
477
+ type: 'confirm',
478
+ name: 'scripts',
479
+ message: '📜 Add useful npm scripts?',
480
+ default: true
481
+ },
482
+ {
483
+ type: 'confirm',
484
+ name: 'docker',
485
+ message: '🐳 Generate Docker configuration?',
486
+ default: false
487
+ },
488
+ {
489
+ type: 'confirm',
490
+ name: 'env',
491
+ message: '🔐 Create environment configuration?',
492
+ default: true
493
+ }
494
+ ];
495
+
496
+ const finalAnswers = await inquirer.prompt(finalQuestions);
497
+ Object.assign(this.config, finalAnswers);
498
+ }
499
+
500
+ /**
501
+ * Confirmation de la configuration
502
+ */
503
+ async confirmOptions() {
504
+ console.log(chalk.blue.bold('\n📋 Configuration Summary\n'));
505
+
506
+ const summary = this.generateSummary();
507
+ const summaryBox = boxen(summary, {
508
+ padding: 1,
509
+ margin: 1,
510
+ borderStyle: 'double',
511
+ borderColor: 'green',
512
+ title: '📦 Project Configuration',
513
+ titleAlignment: 'center'
514
+ });
515
+
516
+ console.log(summaryBox);
517
+
518
+ const { confirm } = await inquirer.prompt([{
519
+ type: 'confirm',
520
+ name: 'confirm',
521
+ message: '✅ Proceed with this configuration?',
522
+ default: true
523
+ }]);
524
+
525
+ if (!confirm) {
526
+ console.log(chalk.yellow('\n👋 Setup cancelled. See you later!'));
527
+ process.exit(0);
528
+ }
529
+ }
530
+
531
+ /**
532
+ * Génération du résumé de configuration
533
+ */
534
+ generateSummary() {
535
+ const { projectName, template, features, database, auth, plugins, styling, theme } = this.config;
536
+
537
+ return chalk.white(`
538
+ 🏷️ Project: ${chalk.cyan.bold(projectName)}
539
+ 📝 Description: ${chalk.gray(this.config.description)}
540
+ 👤 Author: ${chalk.green(this.config.author)}
541
+ 🎨 Template: ${chalk.yellow(template)}
542
+ 🗄️ Database: ${chalk.blue(database)}
543
+ 🔐 Auth: ${chalk.magenta(auth.enabled ? '✅ Enabled' : '❌ Disabled')}
544
+ 🎭 Styling: ${chalk.yellow(styling)} ${theme ? `(${theme})` : ''}
545
+
546
+ 📦 Features (${features.length}):
547
+ ${features.map(f => ` ✓ ${f}`).join('\n') || ' No additional features'}
548
+
549
+ 🔌 Plugins (${plugins.length}):
550
+ ${plugins.map(p => ` ⚡ ${p}`).join('\n') || ' No plugins selected'}
551
+
552
+ ⚙️ Options:
553
+ 📦 Git: ${this.config.git ? '✅' : '❌'}
554
+ 📥 Auto-install: ${this.config.install ? '✅' : '❌'}
555
+ 🐳 Docker: ${this.config.docker ? '✅' : '❌'}
556
+ 🔐 Environment: ${this.config.env ? '✅' : '❌'}
557
+ `);
558
+ }
559
+
560
+ /**
561
+ * Exécution de la configuration
562
+ */
563
+ async executeSetup() {
564
+ try {
565
+ const SetupExecutor = require('./setup-executor');
566
+ const executor = new SetupExecutor(this.config);
567
+ await executor.execute();
568
+ } catch (error) {
569
+ throw new Error(`Setup execution failed: ${error.message}`);
570
+ }
571
+ }
572
+
573
+ /**
574
+ * Écran de finalisation
575
+ */
576
+ async showCompletion() {
577
+ console.log(chalk.green.bold('\n🎉 Setup Complete!\n'));
578
+
579
+ const completionMessage = this.generateCompletionMessage();
580
+ const completionBox = boxen(completionMessage, {
581
+ padding: 1,
582
+ margin: 1,
583
+ borderStyle: 'round',
584
+ borderColor: 'green',
585
+ title: '🎊 Success!',
586
+ titleAlignment: 'center'
587
+ });
588
+
589
+ console.log(completionBox);
590
+ console.log(chalk.rainbow('\n✨ Happy coding with Veko.js! ✨\n'));
591
+ }
592
+
593
+ /**
594
+ * Génération du message de finalisation
595
+ */
596
+ generateCompletionMessage() {
597
+ const { projectName } = this.config;
598
+
599
+ return chalk.white(`Your project "${chalk.cyan.bold(projectName)}" has been created successfully!\n\n`) +
600
+ chalk.gray('Next steps:\n') +
601
+ chalk.white(` 📁 cd ${projectName}\n`) +
602
+ chalk.white(' 🚀 npm run dev\n') +
603
+ chalk.white(' 🌐 veko dev\n\n`') +
604
+ chalk.gray('Your app will be available at: ') +
605
+ chalk.blue.underline('http://localhost:3000\n\n') +
606
+ chalk.yellow('📚 Documentation: ') + chalk.blue.underline('https://veko.js.org');
607
+ }
608
+
609
+ // === Méthodes de validation sécurisées ===
610
+
611
+ /**
612
+ * Validation du nom de projet
613
+ */
614
+ validateProjectName(input) {
615
+ if (!input || input.length < 1) {
616
+ return 'Project name is required';
617
+ }
618
+
619
+ if (input.length > this.securityConfig.maxProjectNameLength) {
620
+ return `Project name must be less than ${this.securityConfig.maxProjectNameLength} characters`;
621
+ }
622
+
623
+ if (!this.securityConfig.allowedFileNameChars.test(input)) {
624
+ return 'Use only letters, numbers, hyphens and underscores';
625
+ }
626
+
627
+ try {
628
+ // Vérification synchrone de l'existence du répertoire
629
+ const fs = require('fs');
630
+ if (fs.existsSync(input)) {
631
+ return 'Directory already exists';
632
+ }
633
+ } catch (error) {
634
+ // Ignorer les erreurs de vérification
635
+ }
636
+
637
+ return true;
638
+ }
639
+
640
+ /**
641
+ * Validation de la description
642
+ */
643
+ validateDescription(input) {
644
+ if (input && input.length > this.securityConfig.maxDescriptionLength) {
645
+ return `Description must be less than ${this.securityConfig.maxDescriptionLength} characters`;
646
+ }
647
+ return true;
648
+ }
649
+
650
+ /**
651
+ * Validation de l'auteur
652
+ */
653
+ validateAuthor(input) {
654
+ if (input && input.length > this.securityConfig.maxAuthorLength) {
655
+ return `Author name must be less than ${this.securityConfig.maxAuthorLength} characters`;
656
+ }
657
+ return true;
658
+ }
659
+
660
+ /**
661
+ * Validation des fonctionnalités
662
+ */
663
+ validateFeatures(input) {
664
+ if (input.length > this.securityConfig.maxFeatures) {
665
+ return `Maximum ${this.securityConfig.maxFeatures} features allowed`;
666
+ }
667
+ return true;
668
+ }
669
+
670
+ /**
671
+ * Validation des plugins
672
+ */
673
+ validatePlugins(input) {
674
+ if (input.length > this.securityConfig.maxPlugins) {
675
+ return `Maximum ${this.securityConfig.maxPlugins} plugins allowed`;
676
+ }
677
+ return true;
678
+ }
679
+
680
+ /**
681
+ * Nettoyage sécurisé des informations de projet
682
+ */
683
+ sanitizeProjectInfo(info) {
684
+ return {
685
+ projectName: this.sanitizeString(info.projectName, this.securityConfig.maxProjectNameLength),
686
+ description: this.sanitizeString(info.description, this.securityConfig.maxDescriptionLength),
687
+ author: this.sanitizeString(info.author, this.securityConfig.maxAuthorLength),
688
+ license: info.license || 'MIT'
689
+ };
690
+ }
691
+
692
+ /**
693
+ * Nettoyage générique de chaîne
694
+ */
695
+ sanitizeString(str, maxLength) {
696
+ if (typeof str !== 'string') return '';
697
+ return str.trim().substring(0, maxLength);
698
+ }
699
+
700
+ /**
701
+ * Gestion centralisée des erreurs
702
+ */
703
+ async handleError(error) {
704
+ console.log(chalk.red.bold('\n❌ Setup Error\n'));
705
+
706
+ const errorBox = boxen(
707
+ chalk.red('An error occurred during setup:\n\n') +
708
+ chalk.white(error.message) + '\n\n' +
709
+ chalk.gray('Please try again or report this issue if it persists.'),
710
+ {
711
+ padding: 1,
712
+ margin: 1,
713
+ borderStyle: 'round',
714
+ borderColor: 'red',
715
+ title: '🚨 Error',
716
+ titleAlignment: 'center'
717
+ }
718
+ );
719
+
720
+ console.log(errorBox);
721
+
722
+ const { retry } = await inquirer.prompt([{
723
+ type: 'confirm',
724
+ name: 'retry',
725
+ message: '🔄 Would you like to try again?',
726
+ default: true
727
+ }]);
728
+
729
+ if (retry) {
730
+ await this.start();
731
+ } else {
732
+ process.exit(1);
733
+ }
734
+ }
735
+ }
736
+
737
+ module.exports = SetupWizard;