i18ntk 2.1.0 → 2.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.
- package/README.md +87 -50
- package/main/i18ntk-analyze.js +63 -63
- package/main/i18ntk-backup-class.js +37 -41
- package/main/i18ntk-backup.js +28 -30
- package/main/i18ntk-complete.js +75 -74
- package/main/i18ntk-doctor.js +7 -6
- package/main/i18ntk-fixer.js +3 -3
- package/main/i18ntk-init.js +49 -13
- package/main/i18ntk-scanner.js +2 -2
- package/main/i18ntk-sizing.js +36 -37
- package/main/i18ntk-summary.js +4 -4
- package/main/i18ntk-ui.js +95 -96
- package/main/i18ntk-usage.js +31 -19
- package/main/i18ntk-validate.js +78 -27
- package/main/manage/commands/AnalyzeCommand.js +71 -73
- package/main/manage/commands/CommandRouter.js +15 -12
- package/main/manage/commands/FixerCommand.js +94 -38
- package/main/manage/commands/ScannerCommand.js +2 -2
- package/main/manage/commands/ValidateCommand.js +87 -36
- package/main/manage/index.js +165 -152
- package/main/manage/managers/DebugMenu.js +6 -6
- package/main/manage/managers/InteractiveMenu.js +6 -6
- package/main/manage/managers/LanguageMenu.js +12 -6
- package/main/manage/managers/SettingsMenu.js +6 -6
- package/main/manage/services/AuthenticationService.js +5 -6
- package/main/manage/services/ConfigurationService.js +22 -34
- package/main/manage/services/FileManagementService.js +6 -6
- package/main/manage/services/InitService.js +44 -8
- package/main/manage/services/UsageService.js +24 -12
- package/package.json +21 -42
- package/settings/settings-cli.js +5 -5
- package/settings/settings-manager.js +984 -968
- package/ui-locales/de.json +12 -11
- package/ui-locales/en.json +12 -11
- package/ui-locales/es.json +12 -11
- package/ui-locales/fr.json +12 -11
- package/ui-locales/ja.json +12 -11
- package/ui-locales/ru.json +12 -11
- package/ui-locales/zh.json +12 -11
- package/utils/config-helper.js +27 -16
- package/utils/config-manager.js +8 -7
- package/utils/i18n-helper.js +161 -166
- package/utils/init-helper.js +3 -2
- package/utils/json-output.js +11 -10
- package/{scripts → utils}/locale-optimizer.js +61 -60
- package/utils/logger.js +4 -4
- package/utils/safe-json.js +3 -3
- package/utils/secure-backup.js +8 -7
- package/utils/setup-enforcer.js +63 -98
- package/main/i18ntk-go.js +0 -283
- package/main/i18ntk-java.js +0 -380
- package/main/i18ntk-js.js +0 -512
- package/main/i18ntk-manage.js +0 -1694
- package/main/i18ntk-php.js +0 -462
- package/main/i18ntk-py.js +0 -379
- package/main/i18ntk-settings.js +0 -23
- package/main/manage/index-fixed.js +0 -1447
- package/scripts/build-lite.js +0 -279
- package/scripts/deprecate-versions.js +0 -317
- package/scripts/export-translations.js +0 -84
- package/scripts/fix-all-i18n.js +0 -236
- package/scripts/fix-and-purify-i18n.js +0 -233
- package/scripts/fix-locale-control-chars.js +0 -110
- package/scripts/lint-locales.js +0 -80
- package/scripts/prepublish-dev.js +0 -221
- package/scripts/prepublish.js +0 -362
- package/scripts/security-check.js +0 -117
- package/scripts/sync-translations.js +0 -151
- package/scripts/sync-ui-locales.js +0 -20
- package/scripts/validate-all-translations.js +0 -195
- package/scripts/verify-deprecations.js +0 -157
- package/scripts/verify-translations.js +0 -63
- package/utils/security-fixed.js +0 -609
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
* during package initialization. Integrates with i18ntk init process.
|
|
8
8
|
*
|
|
9
9
|
* Usage:
|
|
10
|
-
* node
|
|
11
|
-
* node
|
|
12
|
-
* node
|
|
13
|
-
* node
|
|
10
|
+
* node utils/locale-optimizer.js --interactive
|
|
11
|
+
* node utils/locale-optimizer.js --list
|
|
12
|
+
* node utils/locale-optimizer.js --keep en,es,de
|
|
13
|
+
* node utils/locale-optimizer.js --restore
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const SecurityUtils = require('./security');
|
|
19
|
+
const cliHelper = require('./cli-helper');
|
|
20
|
+
const JsonOutput = require('./json-output');
|
|
21
|
+
const { getGlobalReadline } = require('./cli');
|
|
21
22
|
|
|
22
23
|
class LocaleOptimizer {
|
|
23
24
|
constructor() {
|
|
@@ -77,19 +78,19 @@ class LocaleOptimizer {
|
|
|
77
78
|
return;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
console.log('\n
|
|
81
|
-
console.log('
|
|
81
|
+
console.log('\n📊 UI Locale Package Analysis');
|
|
82
|
+
console.log('â•'.repeat(40));
|
|
82
83
|
|
|
83
84
|
const available = this.getAvailableLocales();
|
|
84
85
|
let totalSize = 0;
|
|
85
86
|
|
|
86
87
|
if (available.length === 0) {
|
|
87
|
-
console.log('
|
|
88
|
+
console.log(' ⌠No locale files found');
|
|
88
89
|
return;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
console.log(' Language Size Status');
|
|
92
|
-
console.log('
|
|
93
|
+
console.log(' ─────────────────────────');
|
|
93
94
|
|
|
94
95
|
available.forEach(locale => {
|
|
95
96
|
const filePath = path.join(this.uiLocalesDir, `${locale}.json`);
|
|
@@ -105,11 +106,11 @@ class LocaleOptimizer {
|
|
|
105
106
|
|
|
106
107
|
const missing = this.allLocales.filter(l => !available.includes(l));
|
|
107
108
|
if (missing.length > 0) {
|
|
108
|
-
console.log(`\n
|
|
109
|
+
console.log(`\n âš ï¸ Missing: ${missing.join(', ').toUpperCase()}`);
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
console.log(`\n
|
|
112
|
-
console.log(
|
|
112
|
+
console.log(`\n📦 Total package size: ${(totalSize / 1024).toFixed(1)}KB`);
|
|
113
|
+
console.log(`💡 Potential savings: ${(totalSize/1024 - this.getLocaleSize('en')).toFixed(1)}KB (English only)`);
|
|
113
114
|
console.log(` Run --interactive to optimize now`);
|
|
114
115
|
}
|
|
115
116
|
|
|
@@ -135,7 +136,7 @@ class LocaleOptimizer {
|
|
|
135
136
|
keepLocales(localesToKeep) {
|
|
136
137
|
const keepList = Array.isArray(localesToKeep) ? localesToKeep : localesToKeep.split(',').map(l => l.trim().toLowerCase());
|
|
137
138
|
|
|
138
|
-
console.log('
|
|
139
|
+
console.log('🎯 Optimizing package size...');
|
|
139
140
|
console.log(` Keeping: ${keepList.join(', ').toUpperCase()}`);
|
|
140
141
|
|
|
141
142
|
// Create backup directory
|
|
@@ -162,22 +163,22 @@ class LocaleOptimizer {
|
|
|
162
163
|
removedCount++;
|
|
163
164
|
removedLocales.push(locale);
|
|
164
165
|
|
|
165
|
-
console.log(`
|
|
166
|
+
console.log(` ✅ Removed ${locale.toUpperCase()} (backed up)`);
|
|
166
167
|
}
|
|
167
168
|
});
|
|
168
169
|
|
|
169
170
|
if (removedCount > 0) {
|
|
170
|
-
console.log(`\
|
|
171
|
-
console.log(` If issues occur, restore with: node
|
|
171
|
+
console.log(`\nâš ï¸ WARNING: Removing locales may break UI functionality`);
|
|
172
|
+
console.log(` If issues occur, restore with: node utils/locale-optimizer.js --restore`);
|
|
172
173
|
console.log(` Or reinstall the package: npm install -g i18ntk`);
|
|
173
174
|
|
|
174
175
|
// Create warning file
|
|
175
176
|
const warningPath = path.join(this.backupDir, 'REMOVED_LOCALES.txt');
|
|
176
|
-
SecurityUtils.safeWriteFileSync(warningPath, `Removed locales: ${removedLocales.join(',')}\nRestore with: node
|
|
177
|
+
SecurityUtils.safeWriteFileSync(warningPath, `Removed locales: ${removedLocales.join(',')}\nRestore with: node utils/locale-optimizer.js --restore`);
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
const savedKB = (savedSpace / 1024).toFixed(1);
|
|
180
|
-
console.log(`\n
|
|
181
|
+
console.log(`\n🎉 Optimization complete!`);
|
|
181
182
|
console.log(` Removed: ${removedCount} locales`);
|
|
182
183
|
console.log(` Saved: ${savedKB}KB`);
|
|
183
184
|
console.log(` Backup location: ${this.backupDir}`);
|
|
@@ -187,17 +188,17 @@ class LocaleOptimizer {
|
|
|
187
188
|
* Restore locales from backup
|
|
188
189
|
*/
|
|
189
190
|
restoreLocales() {
|
|
190
|
-
console.log('
|
|
191
|
+
console.log('🔄 Restoring locales from backup...');
|
|
191
192
|
|
|
192
193
|
if (!SecurityUtils.safeExistsSync(this.backupDir)) {
|
|
193
|
-
console.log('
|
|
194
|
+
console.log(' ⌠No backup directory found');
|
|
194
195
|
return;
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
const backupFiles = fs.readdirSync(this.backupDir).filter(f => f.endsWith('.json'));
|
|
198
199
|
|
|
199
200
|
if (backupFiles.length === 0) {
|
|
200
|
-
console.log('
|
|
201
|
+
console.log(' ⌠No backup files found');
|
|
201
202
|
return;
|
|
202
203
|
}
|
|
203
204
|
|
|
@@ -209,10 +210,10 @@ class LocaleOptimizer {
|
|
|
209
210
|
|
|
210
211
|
fs.copyFileSync(backupPath, restorePath);
|
|
211
212
|
restoredCount++;
|
|
212
|
-
console.log(`
|
|
213
|
+
console.log(` ✅ Restored ${file.replace('.json', '').toUpperCase()}`);
|
|
213
214
|
});
|
|
214
215
|
|
|
215
|
-
console.log(`\n
|
|
216
|
+
console.log(`\n🎉 Restoration complete!`);
|
|
216
217
|
console.log(` Restored: ${restoredCount} locales`);
|
|
217
218
|
}
|
|
218
219
|
|
|
@@ -220,13 +221,13 @@ class LocaleOptimizer {
|
|
|
220
221
|
* Dry run mode - show optimization scenarios without making changes
|
|
221
222
|
*/
|
|
222
223
|
dryRun() {
|
|
223
|
-
console.log('
|
|
224
|
-
console.log('
|
|
224
|
+
console.log('🔠LOCALE OPTIMIZER - DRY RUN MODE');
|
|
225
|
+
console.log('â•'.repeat(50));
|
|
225
226
|
|
|
226
227
|
const available = this.getAvailableLocales();
|
|
227
228
|
const totalSize = this.getTotalSize();
|
|
228
229
|
|
|
229
|
-
console.log(
|
|
230
|
+
console.log(`📊 Current state:`);
|
|
230
231
|
console.log(` Total locales: ${available.length}`);
|
|
231
232
|
console.log(` Total package size: ${totalSize.toFixed(1)}KB`);
|
|
232
233
|
console.log(` Individual locale sizes:`);
|
|
@@ -237,7 +238,7 @@ class LocaleOptimizer {
|
|
|
237
238
|
console.log(` - ${locale.toUpperCase()}: ${size.toFixed(1)}KB (${status})`);
|
|
238
239
|
});
|
|
239
240
|
|
|
240
|
-
console.log('\n
|
|
241
|
+
console.log('\n🎯 Optimization scenarios:');
|
|
241
242
|
|
|
242
243
|
// Scenario 1: English only
|
|
243
244
|
const enSize = this.getLocaleSize('en');
|
|
@@ -259,42 +260,42 @@ class LocaleOptimizer {
|
|
|
259
260
|
console.log(` - Size: ${enEsFrSize.toFixed(1)}KB`);
|
|
260
261
|
console.log(` - Savings: ${(totalSize - enEsFrSize).toFixed(1)}KB (${(((totalSize - enEsFrSize) / totalSize) * 100).toFixed(1)}%)`);
|
|
261
262
|
|
|
262
|
-
console.log('\n
|
|
263
|
-
console.log(' node
|
|
264
|
-
console.log('\n
|
|
263
|
+
console.log('\n💡 To run actual optimization:');
|
|
264
|
+
console.log(' node utils/locale-optimizer.js --interactive');
|
|
265
|
+
console.log('\n✅ Dry run complete - no files were modified');
|
|
265
266
|
}
|
|
266
267
|
|
|
267
268
|
/**
|
|
268
269
|
* Interactive locale selection with enhanced UI
|
|
269
270
|
*/
|
|
270
271
|
async interactiveSelect() {
|
|
271
|
-
console.log('\n
|
|
272
|
-
console.log('
|
|
272
|
+
console.log('\n🌠i18ntk Locale Optimizer');
|
|
273
|
+
console.log('â•'.repeat(50));
|
|
273
274
|
console.log('Select which UI locales to keep (English is always kept)');
|
|
274
275
|
console.log('This will reduce package size by removing unused language files\n');
|
|
275
276
|
|
|
276
277
|
const available = this.getAvailableLocales();
|
|
277
278
|
const selections = new Set(['en']); // Always keep English
|
|
278
279
|
|
|
279
|
-
console.log('
|
|
280
|
+
console.log('📊 Available locales and sizes:');
|
|
280
281
|
available.forEach(locale => {
|
|
281
282
|
const size = this.getLocaleSize(locale);
|
|
282
|
-
const status = locale === 'en' ? '
|
|
283
|
+
const status = locale === 'en' ? '✅ (Required)' : '○';
|
|
283
284
|
console.log(` ${status} ${locale.toUpperCase()}: ${size}KB`);
|
|
284
285
|
});
|
|
285
286
|
|
|
286
|
-
console.log('\n
|
|
287
|
+
console.log('\n💡 Type locale codes separated by commas (e.g., en,es,de)');
|
|
287
288
|
console.log(' Press Enter to keep all, or type "cancel" to abort\n');
|
|
288
289
|
|
|
289
290
|
const answer = await cliHelper.prompt('Select locales to keep: ');
|
|
290
291
|
|
|
291
292
|
if (answer.toLowerCase() === 'cancel') {
|
|
292
|
-
console.log('
|
|
293
|
+
console.log('⌠Operation cancelled');
|
|
293
294
|
return false;
|
|
294
295
|
}
|
|
295
296
|
|
|
296
297
|
if (answer.trim() === '') {
|
|
297
|
-
console.log('
|
|
298
|
+
console.log('✅ Keeping all locales');
|
|
298
299
|
return true;
|
|
299
300
|
}
|
|
300
301
|
|
|
@@ -302,7 +303,7 @@ class LocaleOptimizer {
|
|
|
302
303
|
const valid = selected.filter(l => available.includes(l));
|
|
303
304
|
|
|
304
305
|
if (valid.length === 0) {
|
|
305
|
-
console.log('
|
|
306
|
+
console.log('âš ï¸ No valid locales selected, keeping all');
|
|
306
307
|
return true;
|
|
307
308
|
}
|
|
308
309
|
|
|
@@ -310,16 +311,16 @@ class LocaleOptimizer {
|
|
|
310
311
|
valid.push('en');
|
|
311
312
|
const unique = [...new Set(valid)];
|
|
312
313
|
|
|
313
|
-
console.log(`\n
|
|
314
|
+
console.log(`\n🎯 Selected: ${unique.join(', ').toUpperCase()}`);
|
|
314
315
|
this.showImpact(unique);
|
|
315
316
|
|
|
316
317
|
const confirm = await cliHelper.prompt('\nProceed? (y/N): ');
|
|
317
318
|
if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 'yes') {
|
|
318
319
|
this.keepLocales(unique.join(','));
|
|
319
|
-
console.log('\n
|
|
320
|
-
console.log('
|
|
320
|
+
console.log('\n🎉 Package optimized successfully!');
|
|
321
|
+
console.log('💡 Use --restore to bring back removed locales');
|
|
321
322
|
} else {
|
|
322
|
-
console.log('
|
|
323
|
+
console.log('⌠Operation cancelled');
|
|
323
324
|
}
|
|
324
325
|
return true;
|
|
325
326
|
}
|
|
@@ -336,7 +337,7 @@ class LocaleOptimizer {
|
|
|
336
337
|
const saved = allSize - selectedSize;
|
|
337
338
|
const percentage = ((saved / allSize) * 100).toFixed(1);
|
|
338
339
|
|
|
339
|
-
console.log(`\n
|
|
340
|
+
console.log(`\n📈 Impact Analysis:`);
|
|
340
341
|
console.log(` Current: ${allSize.toFixed(1)}KB`);
|
|
341
342
|
console.log(` New: ${selectedSize.toFixed(1)}KB`);
|
|
342
343
|
console.log(` Saved: ${saved.toFixed(1)}KB (${percentage}%)`);
|
|
@@ -536,8 +537,8 @@ async function main() {
|
|
|
536
537
|
|
|
537
538
|
// Handle --init
|
|
538
539
|
if (args.includes('--init')) {
|
|
539
|
-
console.log('\n
|
|
540
|
-
console.log('
|
|
540
|
+
console.log('\n📦 Package Size Optimization');
|
|
541
|
+
console.log('â•'.repeat(30));
|
|
541
542
|
optimizer.listLocales();
|
|
542
543
|
|
|
543
544
|
const answer = await cliHelper.prompt('\nOptimize package size now? (Y/n): ');
|
|
@@ -552,20 +553,20 @@ async function main() {
|
|
|
552
553
|
}
|
|
553
554
|
|
|
554
555
|
if (!options.interactive && !options.list && !options.keep && !options.restore && !options.dryRun && !args.includes('--init')) {
|
|
555
|
-
console.log('
|
|
556
|
-
console.log('
|
|
556
|
+
console.log('🌠i18ntk Locale Optimizer v2.0');
|
|
557
|
+
console.log('â•'.repeat(35));
|
|
557
558
|
console.log('Interactive package size optimization for UI locales');
|
|
558
559
|
console.log('');
|
|
559
560
|
console.log('Usage:');
|
|
560
|
-
console.log(' node
|
|
561
|
-
console.log(' node
|
|
562
|
-
console.log(' node
|
|
563
|
-
console.log(' node
|
|
564
|
-
console.log(' node
|
|
565
|
-
console.log(' node
|
|
561
|
+
console.log(' node utils/locale-optimizer.js --interactive 🎯 Interactive selection');
|
|
562
|
+
console.log(' node utils/locale-optimizer.js --list 📊 List all locales');
|
|
563
|
+
console.log(' node utils/locale-optimizer.js --keep en,es,de âš¡ Quick keep');
|
|
564
|
+
console.log(' node utils/locale-optimizer.js --restore 🔄 Restore all locales');
|
|
565
|
+
console.log(' node utils/locale-optimizer.js --init 🚀 Called during init');
|
|
566
|
+
console.log(' node utils/locale-optimizer.js --dry-run 🔠Simulation mode');
|
|
566
567
|
console.log('');
|
|
567
|
-
console.log('
|
|
568
|
-
console.log(' node
|
|
568
|
+
console.log('💡 Example: Keep only English and Spanish');
|
|
569
|
+
console.log(' node utils/locale-optimizer.js --keep en,es');
|
|
569
570
|
return;
|
|
570
571
|
}
|
|
571
572
|
|
|
@@ -581,4 +582,4 @@ if (require.main === module) {
|
|
|
581
582
|
main();
|
|
582
583
|
}
|
|
583
584
|
|
|
584
|
-
module.exports = LocaleOptimizer;
|
|
585
|
+
module.exports = LocaleOptimizer;
|
package/utils/logger.js
CHANGED
|
@@ -45,10 +45,10 @@ const logger = {
|
|
|
45
45
|
logger.log(output, colors.blue);
|
|
46
46
|
},
|
|
47
47
|
|
|
48
|
-
// Debug logging (only when DEBUG env var is set or log level is debug)
|
|
49
|
-
debug: (message) => {
|
|
50
|
-
const logLevel = envManager.get('I18NTK_LOG_LEVEL');
|
|
51
|
-
const debugEnabled =
|
|
48
|
+
// Debug logging (only when DEBUG env var is set or log level is debug)
|
|
49
|
+
debug: (message) => {
|
|
50
|
+
const logLevel = envManager.get('I18NTK_LOG_LEVEL');
|
|
51
|
+
const debugEnabled = logLevel === 'debug';
|
|
52
52
|
|
|
53
53
|
if (debugEnabled) {
|
|
54
54
|
const output = `[DEBUG] ${message}`;
|
package/utils/safe-json.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// utils/safe-json.js
|
|
2
|
-
const
|
|
2
|
+
const fs = require('fs');
|
|
3
3
|
|
|
4
4
|
function stripBOM(s) {
|
|
5
5
|
if (typeof s === 'string' && s.charCodeAt(0) === 0xFEFF) return s.slice(1);
|
|
@@ -13,7 +13,7 @@ function stripBOM(s) {
|
|
|
13
13
|
* - Single, typed error (no loops)
|
|
14
14
|
*/
|
|
15
15
|
async function readJsonSafe(filePath, { maxBytes = 1_000_000 } = {}) {
|
|
16
|
-
const buf = await readFile(filePath);
|
|
16
|
+
const buf = await fs.promises.readFile(filePath);
|
|
17
17
|
if (buf.length === 0) {
|
|
18
18
|
const err = new Error('Empty JSON file');
|
|
19
19
|
err.code = 'EJSONEMPTY';
|
|
@@ -37,4 +37,4 @@ async function readJsonSafe(filePath, { maxBytes = 1_000_000 } = {}) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
module.exports = { readJsonSafe };
|
|
40
|
+
module.exports = { readJsonSafe };
|
package/utils/secure-backup.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const crypto = require('crypto');
|
|
2
|
-
const fs = require('fs
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const fsp = fs.promises;
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const zlib = require('zlib');
|
|
5
6
|
const { promisify } = require('util');
|
|
@@ -180,7 +181,7 @@ class SecureBackupManager {
|
|
|
180
181
|
const backupPath = path.join(this.config.backupDir, backupName);
|
|
181
182
|
|
|
182
183
|
// Write the backup file
|
|
183
|
-
await
|
|
184
|
+
await fsp.writeFile(backupPath, JSON.stringify(backupData, null, 2), 'utf8');
|
|
184
185
|
|
|
185
186
|
// Clean up old backups if we've exceeded the limit
|
|
186
187
|
await this.cleanupOldBackups();
|
|
@@ -203,7 +204,7 @@ class SecureBackupManager {
|
|
|
203
204
|
async restoreBackup(backupPath, password) {
|
|
204
205
|
try {
|
|
205
206
|
// Read the backup file
|
|
206
|
-
const backupData = JSON.parse(await
|
|
207
|
+
const backupData = JSON.parse(await fsp.readFile(backupPath, 'utf8'));
|
|
207
208
|
|
|
208
209
|
// Validate the backup
|
|
209
210
|
if (backupData.header !== BACKUP_HEADER) {
|
|
@@ -236,7 +237,7 @@ class SecureBackupManager {
|
|
|
236
237
|
async listBackups() {
|
|
237
238
|
try {
|
|
238
239
|
// Read the backup directory
|
|
239
|
-
const files = (await
|
|
240
|
+
const files = (await fsp.readdir(this.config.backupDir, { withFileTypes: true }))
|
|
240
241
|
.filter(dirent => dirent.isFile())
|
|
241
242
|
.map(dirent => dirent.name);
|
|
242
243
|
|
|
@@ -247,7 +248,7 @@ class SecureBackupManager {
|
|
|
247
248
|
const backups = await Promise.all(
|
|
248
249
|
backupFiles.map(async (file) => {
|
|
249
250
|
const filePath = path.join(this.config.backupDir, file);
|
|
250
|
-
const stat = await
|
|
251
|
+
const stat = await fsp.stat(filePath);
|
|
251
252
|
if (stat.isDirectory()) {
|
|
252
253
|
throw new Error('Backup path is a directory');
|
|
253
254
|
}
|
|
@@ -290,7 +291,7 @@ class SecureBackupManager {
|
|
|
290
291
|
const deleted = [];
|
|
291
292
|
for (const backup of toDelete) {
|
|
292
293
|
try {
|
|
293
|
-
await
|
|
294
|
+
await fsp.unlink(backup.path);
|
|
294
295
|
deleted.push(backup.name);
|
|
295
296
|
} catch (error) {
|
|
296
297
|
console.error(`Failed to delete backup ${backup.name}:`, error);
|
|
@@ -310,7 +311,7 @@ class SecureBackupManager {
|
|
|
310
311
|
async verifyBackup(backupPath, password) {
|
|
311
312
|
try {
|
|
312
313
|
// Read the backup file
|
|
313
|
-
const backupData = JSON.parse(await
|
|
314
|
+
const backupData = JSON.parse(await fsp.readFile(backupPath, 'utf8'));
|
|
314
315
|
|
|
315
316
|
// Try to decrypt a small part to verify the password
|
|
316
317
|
const testData = await this.decryptData(backupData, password);
|
package/utils/setup-enforcer.js
CHANGED
|
@@ -9,9 +9,25 @@
|
|
|
9
9
|
|
|
10
10
|
const { getIcon } = require('./terminal-icons');
|
|
11
11
|
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
const { blue, yellow, gray, cyan, green, red } = require('./colors-new');
|
|
14
|
-
const SecurityUtils = require('./security');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { blue, yellow, gray, cyan, green, red } = require('./colors-new');
|
|
14
|
+
const SecurityUtils = require('./security');
|
|
15
|
+
const I18nSetupModule = require('../main/i18ntk-setup');
|
|
16
|
+
|
|
17
|
+
async function runSetupModule() {
|
|
18
|
+
if (I18nSetupModule && typeof I18nSetupModule.run === 'function') {
|
|
19
|
+
return await I18nSetupModule.run();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof I18nSetupModule === 'function') {
|
|
23
|
+
const setupManager = new I18nSetupModule();
|
|
24
|
+
if (typeof setupManager.setup === 'function') {
|
|
25
|
+
return await setupManager.setup();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
throw new Error('Setup module does not expose a runnable entrypoint');
|
|
30
|
+
}
|
|
15
31
|
|
|
16
32
|
class SetupEnforcer {
|
|
17
33
|
static _setupCheckInProgress = false;
|
|
@@ -107,39 +123,22 @@ class SetupEnforcer {
|
|
|
107
123
|
process.exit(0);
|
|
108
124
|
}
|
|
109
125
|
|
|
110
|
-
console.log(green(`${getIcon('rocket')} Running setup...`));
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
// Import and
|
|
114
|
-
const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
|
|
115
|
-
if (SecurityUtils.safeExistsSync(setupPath)) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
console.log(gray('You can now run your original command.'));
|
|
128
|
-
resolve(true);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error(red(`${getIcon('cross')} Setup failed:`), error.message);
|
|
131
|
-
console.error(cyan(' Please try running setup manually:'));
|
|
132
|
-
console.error(cyan(' npm run i18ntk-setup'));
|
|
133
|
-
process.exit(1);
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
|
|
137
|
-
console.error(cyan(' npm run i18ntk-setup'));
|
|
138
|
-
process.exit(1);
|
|
139
|
-
}
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
|
|
142
|
-
console.error(cyan(' npm run i18ntk-setup'));
|
|
126
|
+
console.log(green(`${getIcon('rocket')} Running setup...`));
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
// Import once at module scope and execute the stable entrypoint.
|
|
130
|
+
const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
|
|
131
|
+
if (!SecurityUtils.safeExistsSync(setupPath)) {
|
|
132
|
+
console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
|
|
133
|
+
console.error(cyan(' npm run i18ntk-setup'));
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
await runSetupModule();
|
|
137
|
+
console.log(gray('You can now run your original command.'));
|
|
138
|
+
resolve(true);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
|
|
141
|
+
console.error(cyan(' npm run i18ntk-setup'));
|
|
143
142
|
process.exit(1);
|
|
144
143
|
}
|
|
145
144
|
});
|
|
@@ -185,37 +184,20 @@ static async handleIncompleteSetup() {
|
|
|
185
184
|
process.exit(0);
|
|
186
185
|
}
|
|
187
186
|
|
|
188
|
-
console.log(green(`${getIcon('rocket')} Running setup...`));
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
|
|
192
|
-
if (SecurityUtils.safeExistsSync(setupPath)) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
await setupManager.setup();
|
|
203
|
-
}
|
|
204
|
-
resolve(true);
|
|
205
|
-
} catch (error) {
|
|
206
|
-
console.error(red(`${getIcon('cross')} Setup failed:`), error.message);
|
|
207
|
-
console.error(cyan(' Please try running setup manually:'));
|
|
208
|
-
console.error(cyan(' npm run i18ntk-setup'));
|
|
209
|
-
process.exit(1);
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
212
|
-
console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
|
|
213
|
-
console.error(cyan(' npm run i18ntk-setup'));
|
|
214
|
-
process.exit(1);
|
|
215
|
-
}
|
|
216
|
-
} catch (error) {
|
|
217
|
-
console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
|
|
218
|
-
process.exit(1);
|
|
187
|
+
console.log(green(`${getIcon('rocket')} Running setup...`));
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
|
|
191
|
+
if (!SecurityUtils.safeExistsSync(setupPath)) {
|
|
192
|
+
console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
|
|
193
|
+
console.error(cyan(' npm run i18ntk-setup'));
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
await runSetupModule();
|
|
197
|
+
resolve(true);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
|
|
200
|
+
process.exit(1);
|
|
219
201
|
}
|
|
220
202
|
});
|
|
221
203
|
});
|
|
@@ -260,37 +242,20 @@ static async handleInvalidConfig() {
|
|
|
260
242
|
process.exit(0);
|
|
261
243
|
}
|
|
262
244
|
|
|
263
|
-
console.log(green(`${getIcon('rocket')} Running setup...`));
|
|
264
|
-
|
|
265
|
-
try {
|
|
266
|
-
const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
|
|
267
|
-
if (SecurityUtils.safeExistsSync(setupPath)) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
await setupManager.setup();
|
|
278
|
-
}
|
|
279
|
-
resolve(true);
|
|
280
|
-
} catch (error) {
|
|
281
|
-
console.error(red(`${getIcon('cross')} Setup failed:`), error.message);
|
|
282
|
-
console.error(cyan(' Please try running setup manually:'));
|
|
283
|
-
console.error(cyan(' npm run i18ntk-setup'));
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
} else {
|
|
287
|
-
console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
|
|
288
|
-
console.error(cyan(' npm run i18ntk-setup'));
|
|
289
|
-
process.exit(1);
|
|
290
|
-
}
|
|
291
|
-
} catch (error) {
|
|
292
|
-
console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
|
|
293
|
-
process.exit(1);
|
|
245
|
+
console.log(green(`${getIcon('rocket')} Running setup...`));
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const setupPath = path.join(__dirname, '..', 'main', 'i18ntk-setup.js');
|
|
249
|
+
if (!SecurityUtils.safeExistsSync(setupPath)) {
|
|
250
|
+
console.error(red(`${getIcon('cross')} Setup script not found. Please run:`));
|
|
251
|
+
console.error(cyan(' npm run i18ntk-setup'));
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
await runSetupModule();
|
|
255
|
+
resolve(true);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(red(`${getIcon('cross')} Error running setup:`), error.message);
|
|
258
|
+
process.exit(1);
|
|
294
259
|
}
|
|
295
260
|
});
|
|
296
261
|
});
|