i18ntk 1.10.2 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +141 -1191
  3. package/main/i18ntk-analyze.js +65 -84
  4. package/main/i18ntk-backup-class.js +420 -0
  5. package/main/i18ntk-backup.js +3 -3
  6. package/main/i18ntk-complete.js +90 -65
  7. package/main/i18ntk-doctor.js +123 -103
  8. package/main/i18ntk-fixer.js +61 -725
  9. package/main/i18ntk-go.js +14 -15
  10. package/main/i18ntk-init.js +77 -26
  11. package/main/i18ntk-java.js +27 -32
  12. package/main/i18ntk-js.js +70 -68
  13. package/main/i18ntk-manage.js +129 -30
  14. package/main/i18ntk-php.js +75 -75
  15. package/main/i18ntk-py.js +55 -56
  16. package/main/i18ntk-scanner.js +59 -57
  17. package/main/i18ntk-setup.js +9 -404
  18. package/main/i18ntk-sizing.js +6 -6
  19. package/main/i18ntk-summary.js +21 -18
  20. package/main/i18ntk-ui.js +11 -10
  21. package/main/i18ntk-usage.js +54 -18
  22. package/main/i18ntk-validate.js +13 -13
  23. package/main/manage/commands/AnalyzeCommand.js +1124 -0
  24. package/main/manage/commands/BackupCommand.js +62 -0
  25. package/main/manage/commands/CommandRouter.js +295 -0
  26. package/main/manage/commands/CompleteCommand.js +61 -0
  27. package/main/manage/commands/DoctorCommand.js +60 -0
  28. package/main/manage/commands/FixerCommand.js +624 -0
  29. package/main/manage/commands/InitCommand.js +62 -0
  30. package/main/manage/commands/ScannerCommand.js +654 -0
  31. package/main/manage/commands/SizingCommand.js +60 -0
  32. package/main/manage/commands/SummaryCommand.js +61 -0
  33. package/main/manage/commands/UsageCommand.js +60 -0
  34. package/main/manage/commands/ValidateCommand.js +978 -0
  35. package/main/manage/index-fixed.js +1447 -0
  36. package/main/manage/index.js +1462 -0
  37. package/main/manage/managers/DebugMenu.js +140 -0
  38. package/main/manage/managers/InteractiveMenu.js +177 -0
  39. package/main/manage/managers/LanguageMenu.js +62 -0
  40. package/main/manage/managers/SettingsMenu.js +53 -0
  41. package/main/manage/services/AuthenticationService.js +263 -0
  42. package/main/manage/services/ConfigurationService-fixed.js +449 -0
  43. package/main/manage/services/ConfigurationService.js +449 -0
  44. package/main/manage/services/FileManagementService.js +368 -0
  45. package/main/manage/services/FrameworkDetectionService.js +458 -0
  46. package/main/manage/services/InitService.js +1051 -0
  47. package/main/manage/services/SetupService.js +462 -0
  48. package/main/manage/services/SummaryService.js +450 -0
  49. package/main/manage/services/UsageService.js +1502 -0
  50. package/package.json +32 -29
  51. package/runtime/enhanced.d.ts +221 -221
  52. package/runtime/index.d.ts +29 -29
  53. package/runtime/index.full.d.ts +331 -331
  54. package/runtime/index.js +7 -6
  55. package/scripts/build-lite.js +17 -17
  56. package/scripts/deprecate-versions.js +23 -6
  57. package/scripts/export-translations.js +5 -5
  58. package/scripts/fix-all-i18n.js +3 -3
  59. package/scripts/fix-and-purify-i18n.js +3 -2
  60. package/scripts/fix-locale-control-chars.js +110 -0
  61. package/scripts/lint-locales.js +80 -0
  62. package/scripts/locale-optimizer.js +8 -8
  63. package/scripts/prepublish.js +21 -21
  64. package/scripts/security-check.js +117 -117
  65. package/scripts/sync-translations.js +4 -4
  66. package/scripts/sync-ui-locales.js +9 -8
  67. package/scripts/validate-all-translations.js +8 -7
  68. package/scripts/verify-deprecations.js +157 -161
  69. package/scripts/verify-translations.js +6 -5
  70. package/settings/i18ntk-config.json +282 -282
  71. package/settings/language-config.json +5 -5
  72. package/settings/settings-cli.js +9 -9
  73. package/settings/settings-manager.js +18 -18
  74. package/ui-locales/de.json +2417 -2348
  75. package/ui-locales/en.json +2415 -2352
  76. package/ui-locales/es.json +2425 -2353
  77. package/ui-locales/fr.json +2418 -2348
  78. package/ui-locales/ja.json +2463 -2361
  79. package/ui-locales/ru.json +2463 -2359
  80. package/ui-locales/zh.json +2418 -2351
  81. package/utils/admin-auth.js +2 -2
  82. package/utils/admin-cli.js +297 -297
  83. package/utils/admin-pin.js +9 -9
  84. package/utils/cli-helper.js +9 -9
  85. package/utils/config-helper.js +73 -104
  86. package/utils/config-manager.js +204 -171
  87. package/utils/config.js +5 -4
  88. package/utils/env-manager.js +249 -263
  89. package/utils/framework-detector.js +27 -24
  90. package/utils/i18n-helper.js +85 -41
  91. package/utils/init-helper.js +152 -94
  92. package/utils/json-output.js +98 -98
  93. package/utils/mini-commander.js +179 -0
  94. package/utils/missing-key-validator.js +5 -5
  95. package/utils/plugin-loader.js +40 -29
  96. package/utils/prompt.js +14 -44
  97. package/utils/safe-json.js +40 -0
  98. package/utils/secure-errors.js +3 -3
  99. package/utils/security-check-improved.js +390 -0
  100. package/utils/security-config.js +5 -5
  101. package/utils/security-fixed.js +607 -0
  102. package/utils/security.js +652 -602
  103. package/utils/setup-enforcer.js +136 -44
  104. package/utils/setup-validator.js +33 -32
  105. package/utils/ultra-performance-optimizer.js +11 -9
  106. package/utils/watch-locales.js +2 -1
  107. package/utils/prompt-fixed.js +0 -55
  108. package/utils/security-check.js +0 -454
@@ -0,0 +1,368 @@
1
+ /**
2
+ * File Management Service
3
+ * Handles file operations, globbing, and cleanup operations
4
+ * @module services/FileManagementService
5
+ */
6
+
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+ const SecurityUtils = require('../../../utils/security');
10
+
11
+ module.exports = class FileManagementService {
12
+ constructor(config = {}) {
13
+ this.config = config;
14
+ this.settings = null;
15
+ this.configManager = null;
16
+ }
17
+
18
+ /**
19
+ * Initialize the service with required dependencies
20
+ * @param {Object} configManager - Configuration manager instance
21
+ */
22
+ initialize(configManager) {
23
+ this.configManager = configManager;
24
+ this.settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
25
+ }
26
+
27
+ /**
28
+ * Custom glob implementation using Node.js built-in modules (zero dependencies)
29
+ * @param {string[]} patterns - Array of glob patterns
30
+ * @param {Object} options - Options object with cwd and ignore properties
31
+ * @returns {Promise<string[]>} Array of matching file paths
32
+ */
33
+ async customGlob(patterns, options = {}) {
34
+ const cwd = options.cwd || process.cwd();
35
+ const ignorePatterns = options.ignore || [];
36
+
37
+ function matchesPattern(filename, pattern) {
38
+ // Simple pattern matching for **/*.{js,jsx,ts,tsx} style patterns
39
+ if (pattern.includes('**/*')) {
40
+ const extensionPart = pattern.split('*.')[1];
41
+ if (extensionPart) {
42
+ const extensions = extensionPart.replace('{', '').replace('}', '').split(',');
43
+ return extensions.some(ext => filename.endsWith('.' + ext.trim()));
44
+ }
45
+ }
46
+ return filename.includes(pattern.replace('**/', ''));
47
+ }
48
+
49
+ function shouldIgnore(filePath) {
50
+ return ignorePatterns.some(pattern => {
51
+ if (pattern.includes('**/')) {
52
+ const patternEnd = pattern.replace('**/', '');
53
+ return filePath.includes('/' + patternEnd) || filePath.includes('\\' + patternEnd);
54
+ }
55
+ return filePath.includes(pattern);
56
+ });
57
+ }
58
+
59
+ function findFiles(dir, results = []) {
60
+ try {
61
+ const items = fs.readdirSync(dir);
62
+
63
+ for (const item of items) {
64
+ const fullPath = path.join(dir, item);
65
+ const relativePath = path.relative(cwd, fullPath);
66
+
67
+ if (shouldIgnore(relativePath)) {
68
+ continue;
69
+ }
70
+
71
+ try {
72
+ const stat = fs.statSync(fullPath);
73
+
74
+ if (stat.isDirectory()) {
75
+ findFiles(fullPath, results);
76
+ } else if (stat.isFile()) {
77
+ // Check if file matches any of our patterns
78
+ for (const pattern of patterns) {
79
+ if (matchesPattern(item, pattern)) {
80
+ results.push(relativePath);
81
+ break;
82
+ }
83
+ }
84
+ }
85
+ } catch (error) {
86
+ // Skip files we can't access
87
+ continue;
88
+ }
89
+ }
90
+ } catch (error) {
91
+ // Skip directories we can't access
92
+ }
93
+
94
+ return results;
95
+ }
96
+
97
+ return findFiles(cwd);
98
+ }
99
+
100
+ /**
101
+ * Enhanced delete reports and logs functionality
102
+ * @param {Object} prompt - Prompt interface for user interaction
103
+ * @param {Object} ui - UI instance for translations (optional)
104
+ * @returns {Promise<void>}
105
+ */
106
+ async deleteReports(prompt, ui = null) {
107
+ // Check for PIN protection
108
+ const authRequired = await this.isAuthRequiredForScript('deleteReports');
109
+ if (authRequired) {
110
+ console.log(`\n${ui ? ui.t('adminPin.protectedAccess') : 'Protected Access'}`);
111
+ const cliHelper = require('../../../utils/cli-helper');
112
+ const pin = await cliHelper.promptPin((ui ? ui.t('adminPin.enterPin') : 'Enter PIN: ') + ': ');
113
+ const isValid = await this.verifyPin(pin);
114
+ if (!isValid) {
115
+ console.log(ui ? ui.t('adminPin.invalidPin') : 'Invalid PIN');
116
+ await prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
117
+ return;
118
+ }
119
+ console.log(ui ? ui.t('adminPin.accessGranted') : 'Access granted');
120
+ }
121
+
122
+ console.log(`\n${ui ? ui.t('operations.deleteReportsTitle') : 'Delete Reports and Logs'}`);
123
+ console.log('============================================================');
124
+
125
+ const targetDirs = [
126
+ { path: path.join(process.cwd(), 'i18ntk-reports'), name: 'Reports', type: 'reports' },
127
+ { path: path.join(process.cwd(), 'reports'), name: 'Legacy Reports', type: 'reports' },
128
+ { path: path.join(process.cwd(), 'reports', 'backups'), name: 'Reports Backups', type: 'backups' },
129
+ { path: path.join(process.cwd(), 'scripts', 'debug', 'logs'), name: 'Debug Logs', type: 'logs' },
130
+ { path: path.join(process.cwd(), 'scripts', 'debug', 'reports'), name: 'Debug Reports', type: 'reports' },
131
+ { path: path.join(process.cwd(), 'settings', 'backups'), name: 'Settings Backups', type: 'backups' },
132
+ { path: path.join(process.cwd(), 'utils', 'i18ntk-reports'), name: 'Utils Reports', type: 'reports' }
133
+ ].filter(dir => dir.path && typeof dir.path === 'string');
134
+
135
+ try {
136
+ console.log(ui ? ui.t('operations.scanningForFiles') : 'Scanning for files...');
137
+
138
+ let availableDirs = [];
139
+
140
+ // Check which directories exist and have files
141
+ for (const dir of targetDirs) {
142
+ if (SecurityUtils.safeExistsSync(dir.path)) {
143
+ const files = this.getAllReportFiles(dir.path);
144
+ if (files.length > 0) {
145
+ availableDirs.push({
146
+ ...dir,
147
+ files: files.map(file => ({ path: file, dir: dir.path })),
148
+ count: files.length
149
+ });
150
+ }
151
+ }
152
+ }
153
+
154
+ if (availableDirs.length === 0) {
155
+ console.log(ui ? ui.t('operations.noFilesFoundToDelete') : 'No files found to delete');
156
+ await prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
157
+ return;
158
+ }
159
+
160
+ // Show available directories
161
+ console.log(ui ? ui.t('operations.availableDirectories') : 'Available directories:');
162
+ availableDirs.forEach((dir, index) => {
163
+ console.log(` ${index + 1}. ${dir.name} (${dir.count} files)`);
164
+ });
165
+ console.log(` ${availableDirs.length + 1}. ${ui ? ui.t('operations.allDirectories') : 'All directories'}`);
166
+ console.log(` 0. ${ui ? ui.t('operations.cancelOption') : 'Cancel'}`);
167
+
168
+ const dirChoice = await prompt(`\nSelect directory to clean (0-${availableDirs.length + 1}): `);
169
+ const dirIndex = parseInt(dirChoice) - 1;
170
+
171
+ let selectedDirs = [];
172
+
173
+ if (dirChoice.trim() === '0') {
174
+ console.log(ui ? ui.t('operations.cancelled') : 'Cancelled');
175
+ await prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
176
+ return;
177
+ } else if (dirIndex === availableDirs.length) {
178
+ selectedDirs = availableDirs;
179
+ } else if (dirIndex >= 0 && dirIndex < availableDirs.length) {
180
+ selectedDirs = [availableDirs[dirIndex]];
181
+ } else {
182
+ console.log(ui ? ui.t('operations.invalidSelection') : 'Invalid selection');
183
+ await prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
184
+ return;
185
+ }
186
+
187
+ // Collect all files from selected directories
188
+ let allFiles = [];
189
+ selectedDirs.forEach(dir => {
190
+ allFiles.push(...dir.files);
191
+ });
192
+
193
+ console.log(ui ? ui.t('operations.foundFilesInSelectedDirectories', { count: allFiles.length }) : `Found ${allFiles.length} files in selected directories`);
194
+ selectedDirs.forEach(dir => {
195
+ console.log(` šŸ“ ${dir.name}: ${dir.count} files`);
196
+ });
197
+
198
+ // Show deletion options
199
+ console.log(ui ? ui.t('operations.deletionOptions') : 'Deletion options:');
200
+ console.log(` 1. ${ui ? ui.t('operations.deleteAllFiles') : 'Delete all files'}`);
201
+ console.log(` 2. ${ui ? ui.t('operations.keepLast3Files') : 'Keep last 3 files'}`);
202
+ console.log(` 3. ${ui ? ui.t('operations.keepLast5Files') : 'Keep last 5 files'}`);
203
+ console.log(` 0. ${ui ? ui.t('operations.cancelReportOption') : 'Cancel'}`);
204
+
205
+ const option = await prompt('\nSelect option (0-3): ');
206
+
207
+ let filesToDelete = [];
208
+
209
+ switch (option.trim()) {
210
+ case '1':
211
+ filesToDelete = allFiles;
212
+ break;
213
+ case '2':
214
+ filesToDelete = this.getFilesToDeleteKeepLast(allFiles, 3);
215
+ break;
216
+ case '3':
217
+ filesToDelete = this.getFilesToDeleteKeepLast(allFiles, 5);
218
+ break;
219
+ case '0':
220
+ console.log(ui ? ui.t('operations.cancelled') : 'Cancelled');
221
+ await prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
222
+ return;
223
+ default:
224
+ console.log(ui ? ui.t('menu.invalidOption') : 'Invalid option');
225
+ await prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
226
+ return;
227
+ }
228
+
229
+ if (filesToDelete.length === 0) {
230
+ console.log(ui ? ui.t('operations.noFilesToDelete') : 'No files to delete');
231
+ await prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
232
+ return;
233
+ }
234
+
235
+ console.log(ui ? ui.t('operations.filesToDeleteCount', { count: filesToDelete.length }) : `Files to delete: ${filesToDelete.length}`);
236
+ console.log(ui ? ui.t('operations.filesToKeepCount', { count: allFiles.length - filesToDelete.length }) : `Files to keep: ${allFiles.length - filesToDelete.length}`);
237
+
238
+ const confirm = await prompt(ui ? ui.t('operations.confirmDeletion') : 'Are you sure you want to delete these files? (y/N): ');
239
+
240
+ if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
241
+ let deletedCount = 0;
242
+
243
+ for (const fileInfo of filesToDelete) {
244
+ try {
245
+ fs.unlinkSync(fileInfo.path);
246
+ console.log(ui ? ui.t('operations.deletedFile', { filename: path.basename(fileInfo.path) }) : `Deleted: ${path.basename(fileInfo.path)}`);
247
+ deletedCount++;
248
+ } catch (error) {
249
+ console.log(ui ? ui.t('operations.failedToDeleteFile', { filename: path.basename(fileInfo.path), error: error.message }) : `Failed to delete ${path.basename(fileInfo.path)}: ${error.message}`);
250
+ }
251
+ }
252
+
253
+ console.log(`\nšŸŽ‰ Successfully deleted ${deletedCount} files!`);
254
+ } else {
255
+ console.log(ui ? ui.t('operations.cancelled') : 'Cancelled');
256
+ }
257
+
258
+ } catch (error) {
259
+ console.error(`āŒ Error during deletion process: ${error.message}`);
260
+ }
261
+
262
+ await prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
263
+ }
264
+
265
+ /**
266
+ * Helper method to get all report and log files recursively
267
+ * @param {string} dir - Directory to scan
268
+ * @returns {string[]} Array of file paths
269
+ */
270
+ getAllReportFiles(dir) {
271
+ if (!dir || typeof dir !== 'string') {
272
+ return [];
273
+ }
274
+
275
+ let files = [];
276
+
277
+ try {
278
+ if (!SecurityUtils.safeExistsSync(dir)) {
279
+ return [];
280
+ }
281
+
282
+ const items = fs.readdirSync(dir);
283
+ for (const item of items) {
284
+ const fullPath = path.join(dir, item);
285
+
286
+ try {
287
+ const stat = fs.statSync(fullPath);
288
+
289
+ if (stat.isDirectory()) {
290
+ files.push(...this.getAllReportFiles(fullPath));
291
+ } else if (
292
+ // Common report file extensions
293
+ item.endsWith('.json') ||
294
+ item.endsWith('.html') ||
295
+ item.endsWith('.txt') ||
296
+ item.endsWith('.log') ||
297
+ item.endsWith('.csv') ||
298
+ item.endsWith('.md') ||
299
+ // Specific report filename patterns
300
+ item.includes('-report.') ||
301
+ item.includes('_report.') ||
302
+ item.includes('report-') ||
303
+ item.includes('report_') ||
304
+ item.includes('analysis-') ||
305
+ item.includes('validation-')
306
+ ) {
307
+ files.push(fullPath);
308
+ }
309
+ } catch (error) {
310
+ // Skip individual files that can't be accessed
311
+ continue;
312
+ }
313
+ }
314
+ } catch (error) {
315
+ // Silent fail for inaccessible directories
316
+ console.log(`āš ļø Could not access directory: ${dir}`);
317
+ }
318
+
319
+ return files;
320
+ }
321
+
322
+ /**
323
+ * Helper method to determine which files to delete when keeping last N files
324
+ * @param {Array} allFiles - Array of file objects with path property
325
+ * @param {number} keepCount - Number of files to keep
326
+ * @returns {Array} Array of files to delete
327
+ */
328
+ getFilesToDeleteKeepLast(allFiles, keepCount = 3) {
329
+ // Sort files by modification time (newest first)
330
+ const sortedFiles = allFiles.sort((a, b) => {
331
+ try {
332
+ const statA = fs.statSync(a.path || a);
333
+ const statB = fs.statSync(b.path || b);
334
+ return statB.mtime.getTime() - statA.mtime.getTime();
335
+ } catch (error) {
336
+ // If stat fails, sort by filename as fallback
337
+ const pathA = a.path || a;
338
+ const pathB = b.path || b;
339
+ return pathB.localeCompare(pathA);
340
+ }
341
+ });
342
+
343
+ // Keep the N newest files, delete the rest
344
+ return sortedFiles.slice(keepCount);
345
+ }
346
+
347
+ /**
348
+ * Check if authentication is required for a script
349
+ * @param {string} scriptName - Name of the script
350
+ * @returns {Promise<boolean>} True if authentication is required
351
+ */
352
+ async isAuthRequiredForScript(scriptName) {
353
+ // This would need to be implemented based on the authentication service
354
+ // For now, return false as a placeholder
355
+ return false;
356
+ }
357
+
358
+ /**
359
+ * Verify PIN for authentication
360
+ * @param {string} pin - PIN to verify
361
+ * @returns {Promise<boolean>} True if PIN is valid
362
+ */
363
+ async verifyPin(pin) {
364
+ // This would need to be implemented based on the authentication service
365
+ // For now, return true as a placeholder
366
+ return true;
367
+ }
368
+ };