i18ntk 2.2.0 → 2.3.1

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 (34) hide show
  1. package/README.md +83 -50
  2. package/main/i18ntk-backup-class.js +37 -41
  3. package/main/i18ntk-backup.js +28 -30
  4. package/main/i18ntk-doctor.js +7 -6
  5. package/main/i18ntk-init.js +44 -8
  6. package/main/i18ntk-sizing.js +7 -8
  7. package/main/i18ntk-usage.js +17 -5
  8. package/main/i18ntk-validate.js +72 -22
  9. package/main/manage/commands/AnalyzeCommand.js +12 -14
  10. package/main/manage/commands/CommandRouter.js +15 -12
  11. package/main/manage/commands/FixerCommand.js +92 -36
  12. package/main/manage/commands/ValidateCommand.js +78 -27
  13. package/main/manage/index.js +158 -148
  14. package/main/manage/managers/DebugMenu.js +6 -6
  15. package/main/manage/managers/InteractiveMenu.js +6 -6
  16. package/main/manage/managers/LanguageMenu.js +5 -4
  17. package/main/manage/managers/SettingsMenu.js +6 -6
  18. package/main/manage/services/AuthenticationService.js +5 -6
  19. package/main/manage/services/ConfigurationService.js +22 -34
  20. package/main/manage/services/FileManagementService.js +6 -6
  21. package/main/manage/services/InitService.js +44 -8
  22. package/main/manage/services/UsageService.js +17 -5
  23. package/package.json +8 -7
  24. package/settings/settings-cli.js +2 -2
  25. package/settings/settings-manager.js +984 -968
  26. package/utils/config-helper.js +27 -16
  27. package/utils/config-manager.js +8 -7
  28. package/utils/i18n-helper.js +9 -13
  29. package/utils/init-helper.js +3 -2
  30. package/utils/json-output.js +11 -10
  31. package/utils/logger.js +4 -4
  32. package/utils/safe-json.js +3 -3
  33. package/utils/secure-backup.js +8 -7
  34. package/utils/setup-enforcer.js +63 -98
@@ -8,12 +8,14 @@ const path = require('path');
8
8
  const configManager = require('../../../settings/settings-manager');
9
9
  const { loadTranslations, t, refreshLanguageFromSettings } = require('../../../utils/i18n-helper');
10
10
 
11
- const { createPrompt, isInteractive } = require('../../../utils/prompt-helper');
12
- const { loadConfig, saveConfig, ensureConfigDefaults } = require('../../../utils/config');
13
- const { getUnifiedConfig, ensureInitialized, validateSourceDir } = require('../../../utils/config-helper');
14
- const cliHelper = require('../../../utils/cli-helper');
15
- const pkg = require('../../../package.json');
16
- const SetupEnforcer = require('../../../utils/setup-enforcer');
11
+ const { createPrompt, isInteractive } = require('../../../utils/prompt-helper');
12
+ const { loadConfig, saveConfig, ensureConfigDefaults } = require('../../../utils/config');
13
+ const { getUnifiedConfig, ensureInitialized, validateSourceDir } = require('../../../utils/config-helper');
14
+ const { checkInitialized } = require('../../../utils/init-helper');
15
+ const cliHelper = require('../../../utils/cli-helper');
16
+ const pkg = require('../../../package.json');
17
+ const SetupEnforcer = require('../../../utils/setup-enforcer');
18
+ const FrameworkDetectionService = require('./FrameworkDetectionService');
17
19
 
18
20
  module.exports = class ConfigurationService {
19
21
  constructor(config = {}) {
@@ -54,9 +56,8 @@ module.exports = class ConfigurationService {
54
56
  loadTranslations(uiLanguage);
55
57
 
56
58
  // Validate source directory exists
57
- const { validateSourceDir, displayPaths } = require('../../../utils/config-helper');
58
- try {
59
- validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
59
+ try {
60
+ validateSourceDir(this.config.sourceDir, 'i18ntk-manage');
60
61
  } catch (err) {
61
62
  console.log(t('init.requiredTitle'));
62
63
  console.log(t('init.requiredBody'));
@@ -102,23 +103,18 @@ module.exports = class ConfigurationService {
102
103
  // Ensure setup is complete before running any operations
103
104
  await SetupEnforcer.checkSetupCompleteAsync();
104
105
 
105
- prompt = createPrompt({ noPrompt: args.noPrompt || Boolean(args.adminPin) });
106
- const interactive = isInteractive({ noPrompt: args.noPrompt || Boolean(args.adminPin) });
106
+ prompt = createPrompt({ noPrompt: args.noPrompt });
107
+ const interactive = isInteractive({ noPrompt: args.noPrompt });
107
108
 
108
109
  // Load settings and UI language
109
110
  const settings = this.settings || (this.configManager.loadSettings ? this.configManager.loadSettings() : (this.configManager.getConfig ? this.configManager.getConfig() : {}));
110
111
  const uiLanguage = args.uiLanguage || settings.uiLanguage || settings.language || this.config.uiLanguage || 'en';
111
112
  loadTranslations(uiLanguage);
112
113
 
113
- if (args.adminPin) {
114
- // Handle admin PIN mode
115
- this.prompt = async () => '';
116
- }
117
-
118
- if (args.help) {
119
- this.showHelp();
120
- return;
121
- }
114
+ if (args.help) {
115
+ this.showHelp();
116
+ return;
117
+ }
122
118
 
123
119
  let cfgAfterInitCheck = {};
124
120
  if (interactive) {
@@ -155,11 +151,7 @@ module.exports = class ConfigurationService {
155
151
  * @returns {Promise<Object>} Configuration object if initialized
156
152
  */
157
153
  async ensureInitializedOrExit(prompt) {
158
- const { checkInitialized } = require('../../../utils/init-helper');
159
- const cliHelper = require('../../../utils/cli-helper');
160
- const pkg = require('../../../package.json');
161
-
162
- const { initialized, config } = await checkInitialized();
154
+ const { initialized, config } = await checkInitialized();
163
155
 
164
156
  if (!initialized) {
165
157
  console.log('\nThis project is not yet initialized with i18ntk.');
@@ -224,8 +216,7 @@ module.exports = class ConfigurationService {
224
216
  console.log('Framework detection prompt will be suppressed for this version.');
225
217
  } else if (action === 'detect') {
226
218
  // Run framework detection
227
- const FrameworkDetectionService = require('./FrameworkDetectionService');
228
- const frameworkService = new FrameworkDetectionService();
219
+ const frameworkService = new FrameworkDetectionService();
229
220
  frameworkService.initialize(this.configManager);
230
221
 
231
222
  const { detectedLanguage, detectedFramework } = await frameworkService.detectEnvironmentAndFramework();
@@ -260,8 +251,7 @@ module.exports = class ConfigurationService {
260
251
  * @returns {Promise<boolean>} True if frameworks detected, false otherwise
261
252
  */
262
253
  async checkI18nDependencies() {
263
- const FrameworkDetectionService = require('./FrameworkDetectionService');
264
- const frameworkService = new FrameworkDetectionService();
254
+ const frameworkService = new FrameworkDetectionService();
265
255
  frameworkService.initialize(this.configManager);
266
256
  return await frameworkService.checkI18nDependencies(null);
267
257
  }
@@ -274,8 +264,7 @@ module.exports = class ConfigurationService {
274
264
  * @returns {Promise<Object>} Updated configuration
275
265
  */
276
266
  async maybePromptFramework(prompt, cfg, currentVersion) {
277
- const FrameworkDetectionService = require('./FrameworkDetectionService');
278
- const frameworkService = new FrameworkDetectionService();
267
+ const frameworkService = new FrameworkDetectionService();
279
268
  frameworkService.initialize(this.configManager);
280
269
  return await frameworkService.maybePromptFramework(prompt, cfg, currentVersion);
281
270
  }
@@ -392,8 +381,7 @@ module.exports = class ConfigurationService {
392
381
  * @returns {Promise<string>} User input or empty string
393
382
  */
394
383
  prompt(question) {
395
- const cliHelper = require('../../../utils/cli-helper');
396
- // If interactive not available, return empty string to avoid hangs
384
+ // If interactive not available, return empty string to avoid hangs
397
385
  if (!process.stdin.isTTY || process.stdin.destroyed) {
398
386
  console.log('\n⚠️ Interactive input not available, using default response.');
399
387
  return Promise.resolve('');
@@ -446,4 +434,4 @@ module.exports = class ConfigurationService {
446
434
  getUI() {
447
435
  return this.ui;
448
436
  }
449
- };
437
+ };
@@ -4,9 +4,10 @@
4
4
  * @module services/FileManagementService
5
5
  */
6
6
 
7
- const path = require('path');
8
- const fs = require('fs');
9
- const SecurityUtils = require('../../../utils/security');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+ const SecurityUtils = require('../../../utils/security');
10
+ const cliHelper = require('../../../utils/cli-helper');
10
11
 
11
12
  module.exports = class FileManagementService {
12
13
  constructor(config = {}) {
@@ -108,8 +109,7 @@ module.exports = class FileManagementService {
108
109
  const authRequired = await this.isAuthRequiredForScript('deleteReports');
109
110
  if (authRequired) {
110
111
  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: ') + ': ');
112
+ const pin = await cliHelper.promptPin((ui ? ui.t('adminPin.enterPin') : 'Enter PIN: ') + ': ');
113
113
  const isValid = await this.verifyPin(pin);
114
114
  if (!isValid) {
115
115
  console.log(ui ? ui.t('adminPin.invalidPin') : 'Invalid PIN');
@@ -365,4 +365,4 @@ module.exports = class FileManagementService {
365
365
  // For now, return true as a placeholder
366
366
  return true;
367
367
  }
368
- };
368
+ };
@@ -681,7 +681,7 @@ class InitService {
681
681
  }
682
682
 
683
683
  // Interactive admin PIN setup
684
- async promptAdminPinSetup() {
684
+ async promptAdminPinSetup() {
685
685
  const { ask, askHidden, flushStdout } = require('../../../utils/cli');
686
686
 
687
687
  console.log('\n' + t('init.adminPinSetupOptional'));
@@ -733,8 +733,31 @@ class InitService {
733
733
  }
734
734
  } else {
735
735
  console.log(t('init.skippingAdminPinSetup'));
736
- }
737
- }
736
+ }
737
+ }
738
+
739
+ async promptBackupConfiguration(skipPrompt = false) {
740
+ const defaultBackupConfig = { enabled: false, maxBackups: 1, location: './i18ntk-backups' };
741
+ if (skipPrompt || !process.stdin.isTTY) {
742
+ return null;
743
+ }
744
+
745
+ const { ask } = require('../../../utils/cli');
746
+ console.log('\nBackup Settings');
747
+ console.log('Backups are disabled by default to avoid backup recursion and repo pollution.');
748
+ const enableAnswer = await ask('Enable automatic backups? (y/N): ');
749
+ const enabled = ['y', 'yes'].includes(String(enableAnswer || '').trim().toLowerCase());
750
+
751
+ if (!enabled) {
752
+ return defaultBackupConfig;
753
+ }
754
+
755
+ const keepAnswer = await ask('How many backups should be kept automatically (1-3, default 1): ');
756
+ const parsedKeep = parseInt(String(keepAnswer || '').trim(), 10);
757
+ const maxBackups = Number.isInteger(parsedKeep) ? Math.min(Math.max(parsedKeep, 1), 3) : 1;
758
+
759
+ return { enabled: true, maxBackups, location: './i18ntk-backups' };
760
+ }
738
761
 
739
762
  // Interactive language selection
740
763
  async selectLanguages(skipPrompt = false) {
@@ -845,11 +868,24 @@ class InitService {
845
868
  // Prompt for admin PIN setup if not already configured
846
869
  const securitySettings = configManager.getConfig().security || {};
847
870
 
848
- if (!securitySettings.adminPinEnabled && securitySettings.adminPinPromptOnInit !== false && !args.noPrompt) {
849
- const { flushStdout } = require('../../../utils/cli');
850
- await flushStdout();
851
- await this.promptAdminPinSetup();
852
- }
871
+ if (!securitySettings.adminPinEnabled && securitySettings.adminPinPromptOnInit !== false && !args.noPrompt) {
872
+ const { flushStdout } = require('../../../utils/cli');
873
+ await flushStdout();
874
+ await this.promptAdminPinSetup();
875
+ }
876
+
877
+ const backupSettings = await this.promptBackupConfiguration(args.noPrompt);
878
+ if (backupSettings) {
879
+ await configManager.updateConfig({
880
+ backup: {
881
+ ...(this.config.backup || {}),
882
+ ...backupSettings
883
+ }
884
+ });
885
+ this.config.backup = { ...(this.config.backup || {}), ...backupSettings };
886
+ } else if (!this.config.backup) {
887
+ this.config.backup = { enabled: false, maxBackups: 1, location: './i18ntk-backups' };
888
+ }
853
889
 
854
890
  // Get target languages - use args.languages if provided
855
891
  let targetLanguages = args.languages || await this.selectLanguages(args.noPrompt);
@@ -512,10 +512,22 @@ class UsageService {
512
512
  return keys;
513
513
  }
514
514
 
515
- collectPlaceholderKeys(obj, prefix = '', language) {
516
- const patterns = this.placeholderStyles[language] || [];
517
- const regexes = patterns.map(p => new RegExp(p));
518
- if (typeof obj !== 'object' || obj === null) return;
515
+ collectPlaceholderKeys(obj, prefix = '', language) {
516
+ const patterns = this.placeholderStyles[language] || [];
517
+ const regexes = patterns.reduce((compiled, pattern) => {
518
+ try {
519
+ compiled.push(new RegExp(pattern));
520
+ } catch (error) {
521
+ SecurityUtils.logSecurityEvent('Invalid placeholder regex pattern skipped', 'warn', {
522
+ component: 'i18ntk-usage',
523
+ language,
524
+ pattern,
525
+ error: error.message
526
+ });
527
+ }
528
+ return compiled;
529
+ }, []);
530
+ if (typeof obj !== 'object' || obj === null) return;
519
531
 
520
532
  for (const [key, value] of Object.entries(obj)) {
521
533
  const fullKey = prefix ? `${prefix}.${key}` : key;
@@ -1499,4 +1511,4 @@ Analysis Features (v1.8.3):
1499
1511
  }
1500
1512
  }
1501
1513
 
1502
- module.exports = UsageService;
1514
+ module.exports = UsageService;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "i18ntk",
3
- "version": "2.2.0",
4
- "description": "🚀 The fastest i18n toolkit with 97% performance boost! Zero-dependency, enterprise-grade internationalization for React, Vue, Angular, Python, Java, PHP & more. Features PIN protection, auto framework detection, 7+ UI languages, and comprehensive translation management. Perfect for startups to enterprises.",
3
+ "version": "2.3.1",
4
+ "description": "🚀 The fastest internationalization toolkit with 97% performance boost! Zero-dependency, enterprise-grade internationalization for React, Vue, Angular, Python, Java, PHP & more. Features PIN protection, auto framework detection, 7+ UI languages, and comprehensive translation management. Perfect for startups to enterprises.",
5
5
  "keywords": [
6
6
  "i18n",
7
7
  "internationalization",
@@ -227,21 +227,22 @@
227
227
  },
228
228
  "preferGlobal": true,
229
229
  "versionInfo": {
230
- "version": "2.2.0",
230
+ "version": "2.3.1",
231
231
  "releaseDate": "12/04/2026",
232
232
  "lastUpdated": "12/04/2026",
233
- "maintainer": "Vladimir Noskov",
233
+ "maintainer": "Vlad Noskov",
234
234
  "changelog": "./CHANGELOG.md",
235
235
  "documentation": "./README.md",
236
236
  "apiReference": "./docs/api/API_REFERENCE.md",
237
237
  "majorChanges": [
238
+ "HOTFIX: Removed deprecated package-path fallback that caused production build warnings for non-exported subpaths.",
238
239
  "CRITICAL FIX: Resolved sizing and usage-analysis regressions in v2 command flow.",
239
240
  "PACKAGING: Reduced publish footprint by removing internal development scripts and legacy fixed-file artifacts.",
240
- "SECURITY: Hardened release checks and added explicit support guidance to update from pre-2.2.0 versions.",
241
+ "SECURITY: Hardened release checks and added explicit support guidance to update from pre-2.3.1 versions.",
241
242
  "I18N: Completed internal UI locale parity and actionable untranslated-key cleanup across supported languages."
242
243
  ],
243
244
  "breakingChanges": [],
244
- "nextVersion": "2.3.0",
245
+ "nextVersion": "2.4.0",
245
246
  "supportedNodeVersions": ">=16.0.0",
246
247
  "supportedFrameworks": {
247
248
  "react-i18next": ">=11.0.0",
@@ -263,7 +264,7 @@
263
264
  "spring-boot": ">=2.5.0",
264
265
  "laravel": ">=8.0.0"
265
266
  },
266
- "supportPolicy": "Versions earlier than 2.2.0 may be unstable or insecure. Upgrade to 2.2.0 or newer."
267
+ "supportPolicy": "Versions earlier than 2.3.1 may be unstable or insecure. Upgrade to 2.3.1 or newer."
267
268
  },
268
269
  "_comment": "This package is zero-dependency and uses only native Node.js modules"
269
270
  }
@@ -567,8 +567,8 @@ class SettingsCLI {
567
567
  'backupRetention': { min: 1, max: 30, type: 'int', unit: 'days' },
568
568
  'logRetention': { min: 1, max: 90, type: 'int', unit: 'days' },
569
569
  'retentionDays': { min: 1, max: 365, type: 'int', unit: 'days' },
570
- 'maxBackups': { min: 1, max: 1000, type: 'int' }
571
- };
570
+ 'maxBackups': { min: 1, max: 3, type: 'int' }
571
+ };
572
572
 
573
573
  for (const [field, rules] of Object.entries(validations)) {
574
574
  if (key.includes(field)) {