i18ntk 1.10.1 → 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 (110) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +141 -1185
  3. package/main/i18ntk-analyze.js +149 -133
  4. package/main/i18ntk-backup-class.js +420 -0
  5. package/main/i18ntk-backup.js +4 -4
  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 +76 -25
  11. package/main/i18ntk-java.js +27 -32
  12. package/main/i18ntk-js.js +70 -68
  13. package/main/i18ntk-manage.js +128 -29
  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 +10 -396
  18. package/main/i18ntk-sizing.js +46 -40
  19. package/main/i18ntk-summary.js +21 -18
  20. package/main/i18ntk-ui.js +11 -10
  21. package/main/i18ntk-usage.js +55 -19
  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 -30
  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 +13 -5
  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 +23 -15
  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 +23 -20
  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 +152 -103
  86. package/utils/config-manager.js +204 -164
  87. package/utils/config.js +5 -4
  88. package/utils/env-manager.js +256 -0
  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/logger.js +6 -2
  94. package/utils/mini-commander.js +179 -0
  95. package/utils/missing-key-validator.js +5 -5
  96. package/utils/plugin-loader.js +29 -11
  97. package/utils/prompt.js +14 -44
  98. package/utils/safe-json.js +40 -0
  99. package/utils/secure-errors.js +3 -3
  100. package/utils/security-check-improved.js +390 -0
  101. package/utils/security-config.js +5 -5
  102. package/utils/security-fixed.js +607 -0
  103. package/utils/security.js +462 -248
  104. package/utils/setup-enforcer.js +136 -44
  105. package/utils/setup-validator.js +33 -32
  106. package/utils/terminal-icons.js +1 -1
  107. package/utils/ultra-performance-optimizer.js +11 -9
  108. package/utils/watch-locales.js +2 -1
  109. package/utils/prompt-fixed.js +0 -55
  110. package/utils/security-check.js +0 -450
@@ -1,16 +1,16 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
-
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const SecurityUtils = require('./security');
5
5
 
6
6
  // Determine package directory and user project root
7
7
  const packageDir = path.resolve(__dirname, '..');
8
8
  const userProjectRoot = process.cwd();
9
9
 
10
- // Always use package's internal settings directory to avoid polluting user projects
11
- // The settings directory should be within the package, not in user space
12
- const PROJECT_SETTINGS_DIR = path.join(packageDir, 'settings');
13
- const PROJECT_CONFIG_PATH = path.join(PROJECT_SETTINGS_DIR, 'i18ntk-config.json');
10
+ // Always use current working directory for settings to support test environments
11
+ // This ensures config works correctly when tests change the working directory
12
+ const PROJECT_CONFIG_PATH = path.join(process.cwd(), '.i18ntk-config');
13
+ const PROJECT_SETTINGS_DIR = path.dirname(PROJECT_CONFIG_PATH);
14
14
 
15
15
  // Setup tracking file
16
16
  const SETUP_COMPLETED_FILE = path.join(PROJECT_SETTINGS_DIR, 'setup.json');
@@ -241,21 +241,26 @@ const DEFAULT_CONFIG = {
241
241
  "timezone": "auto"
242
242
  };
243
243
 
244
- // Mapping of supported environment variables to config keys
245
- const ENV_VAR_MAP = {
246
- I18NTK_PROJECT_ROOT: 'projectRoot',
247
- I18NTK_SOURCE_DIR: 'sourceDir',
248
- I18NTK_I18N_DIR: 'i18nDir',
249
- I18NTK_OUTPUT_DIR: 'outputDir',
250
- I18NTK_FRAMEWORK_PREFERENCE: 'framework.preference',
251
- I18NTK_FRAMEWORK_FALLBACK: 'framework.fallback',
252
- I18NTK_FRAMEWORK_DETECT: 'framework.detect',
253
- };
244
+ // Environment variable support has been removed in favor of exclusive .i18ntk-config configuration
254
245
 
255
246
  let currentConfig = null;
247
+ let configLoadInProgress = false;
248
+ let recursionDepth = 0;
249
+ const MAX_RECURSION_DEPTH = 15; // Increased to handle legitimate sequential calls
256
250
 
257
251
  function clone(obj) {
258
- return JSON.parse(JSON.stringify(obj));
252
+ return JSON.parse(JSON.stringify(obj));
253
+ }
254
+
255
+ function checkRecursionGuard() {
256
+ // Disabled recursion detection to prevent false positives on legitimate sequential calls
257
+ // TODO: Implement more sophisticated recursion detection in future versions
258
+ return null;
259
+ }
260
+
261
+ function resetRecursionGuard() {
262
+ recursionDepth = 0;
263
+ configLoadInProgress = false;
259
264
  }
260
265
 
261
266
  function ensureProjectSettingsDir() {
@@ -294,52 +299,38 @@ function deepMerge(target, source, basePath = '') {
294
299
  }
295
300
 
296
301
  function applyEnvOverrides(cfg) {
297
- for (const [envVar, keyPath] of Object.entries(ENV_VAR_MAP)) {
298
- const value = process.env[envVar];
299
- if (value === undefined) continue;
300
- const keys = keyPath.split('.');
301
- let current = cfg;
302
- for (let i = 0; i < keys.length - 1; i++) {
303
- const k = keys[i];
304
- if (!current[k] || typeof current[k] !== 'object') current[k] = {};
305
- current = current[k];
306
- }
307
- const leaf = keys[keys.length - 1];
308
- if (keyPath === 'framework.detect') {
309
- current[leaf] = String(value).toLowerCase() !== 'false';
310
- } else {
311
- current[leaf] = normalizePathValue(keyPath, value);
312
- }
313
- }
302
+ // Environment variable support has been removed in favor of exclusive .i18ntk-config configuration
303
+ // This function is kept for backward compatibility but does nothing
314
304
  return cfg;
315
305
  }
316
306
 
317
307
  function tryReadJson(filePath) {
318
308
  try {
319
- if (!fs.existsSync(filePath)) {
309
+ if (!SecurityUtils.safeExistsSync(filePath)) {
320
310
  return null;
321
311
  }
322
-
323
- const data = fs.readFileSync(filePath, 'utf8');
312
+
313
+ const data = SecurityUtils.safeReadFileSync(filePath, path.dirname(filePath), 'utf8');
324
314
  if (!data || data.trim() === '') {
325
315
  console.warn(`[i18ntk] Warning: Empty or invalid JSON file at ${filePath}`);
326
316
  return null;
327
317
  }
328
-
329
- try {
330
- return JSON.parse(data);
331
- } catch (parseError) {
332
- console.error(`[i18ntk] Error parsing JSON from ${filePath}: ${parseError.message}`);
333
- // Create a backup of the corrupted file
334
- const backupPath = `${filePath}.corrupted-${Date.now()}.bak`;
335
- try {
336
- fs.writeFileSync(backupPath, data, 'utf8');
337
- console.warn(`[i18ntk] Created backup of corrupted config at ${backupPath}`);
338
- } catch (backupError) {
339
- console.error(`[i18ntk] Failed to create backup of corrupted config: ${backupError.message}`);
340
- }
341
- return null;
342
- }
318
+
319
+ const parsed = SecurityUtils.safeParseJSON(data);
320
+ if (parsed && typeof parsed === 'object') {
321
+ return parsed;
322
+ }
323
+
324
+ console.error(`[i18ntk] Error parsing JSON from ${filePath}: Invalid JSON content`);
325
+ // Create a backup of the corrupted file
326
+ const backupPath = `${filePath}.corrupted-${Date.now()}.bak`;
327
+ try {
328
+ SecurityUtils.safeWriteFileSync(backupPath, data, path.dirname(backupPath), 'utf8');
329
+ console.warn(`[i18ntk] Created backup of corrupted config at ${backupPath}`);
330
+ } catch (backupError) {
331
+ console.error(`[i18ntk] Failed to create backup of corrupted config: ${backupError.message}`);
332
+ }
333
+ return null;
343
334
  } catch (error) {
344
335
  console.error(`[i18ntk] Error reading config file at ${filePath}: ${error.message}`);
345
336
  return null;
@@ -348,7 +339,7 @@ function tryReadJson(filePath) {
348
339
 
349
340
  async function migrateLegacyIfNeeded(baseCfg) {
350
341
  // If project config does not exist but legacy exists, migrate once
351
- if (!fs.existsSync(PROJECT_CONFIG_PATH) && fs.existsSync(LEGACY_CONFIG_PATH)) {
342
+ if (!SecurityUtils.safeExistsSync(PROJECT_CONFIG_PATH) && SecurityUtils.safeExistsSync(LEGACY_CONFIG_PATH)) {
352
343
  const legacy = tryReadJson(LEGACY_CONFIG_PATH);
353
344
  if (legacy && typeof legacy === 'object') {
354
345
  const merged = deepMerge(clone(baseCfg), legacy);
@@ -360,61 +351,95 @@ async function migrateLegacyIfNeeded(baseCfg) {
360
351
  // Best-effort removal of legacy file to prevent future use
361
352
  try { fs.unlinkSync(LEGACY_CONFIG_PATH); } catch (_) {}
362
353
  // Deprecation notice
363
- console.warn('[i18ntk] Deprecated config location detected (~/.i18ntk). Your config has been migrated to settings/i18ntk-config.json. Please commit the settings/ directory to your project.');
364
- return merged;
365
- } catch (_) {
366
- // If write fails, fall back to in-memory config without deleting legacy
367
- console.warn('[i18ntk] Deprecated config location detected (~/.i18ntk). Using migrated settings in memory; failed to persist to settings/. Ensure the project has write permissions.');
368
- return merged;
369
- }
354
+ console.warn('[i18ntk] Deprecated config location detected (~/.i18ntk). Configuration was migrated to project .i18ntk-config.');
355
+ return merged;
356
+ } catch (_) {
357
+ // If write fails, fall back to in-memory config without deleting legacy
358
+ console.warn('[i18ntk] Deprecated config location detected (~/.i18ntk). Using migrated settings in memory; failed to persist to .i18ntk-config.');
359
+ return merged;
360
+ }
370
361
  }
371
362
  }
372
363
  return null;
373
364
  }
374
365
 
375
366
  function loadConfig() {
376
- if (currentConfig) return currentConfig;
377
- let cfg = clone(DEFAULT_CONFIG);
378
- // 1) Project config (primary)
379
- const projectCfg = tryReadJson(PROJECT_CONFIG_PATH);
380
- if (projectCfg) {
381
- cfg = deepMerge(clone(DEFAULT_CONFIG), projectCfg);
382
- } else {
383
- // 2) Package default (read-only)
384
- const pkgCfg = tryReadJson(PACKAGE_CONFIG_PATH);
385
- if (pkgCfg) {
386
- cfg = deepMerge(clone(DEFAULT_CONFIG), pkgCfg);
367
+ // Check for recursion
368
+ const recursionFallback = checkRecursionGuard();
369
+ if (recursionFallback) return recursionFallback;
370
+
371
+ // Return cached config if available
372
+ if (currentConfig) {
373
+ resetRecursionGuard();
374
+ return currentConfig;
387
375
  }
388
- // 3) Legacy migration (read-only source)
389
- if (!projectCfg) {
390
- const fromLegacy = tryReadJson(LEGACY_CONFIG_PATH);
391
- if (fromLegacy) {
392
- cfg = deepMerge(clone(DEFAULT_CONFIG), fromLegacy);
393
- // Attempt to migrate to project settings
394
- // Ignore migration errors; we still return merged cfg in memory
395
- // eslint-disable-next-line no-unused-vars
396
- console.warn('[i18ntk] Detected legacy config at ~/.i18ntk. Migrating to project settings directory...');
397
- const _ = (async () => { await migrateLegacyIfNeeded(DEFAULT_CONFIG); })();
398
- }
376
+
377
+ // Prevent concurrent loading
378
+ if (configLoadInProgress) {
379
+ console.warn('[i18ntk] Configuration loading already in progress, returning defaults');
380
+ resetRecursionGuard();
381
+ return clone(DEFAULT_CONFIG);
399
382
  }
400
- }
401
- applyEnvOverrides(cfg);
402
- currentConfig = cfg;
403
- return currentConfig;
383
+
384
+ configLoadInProgress = true;
385
+
386
+ try {
387
+ let cfg = clone(DEFAULT_CONFIG);
388
+ // 1) Project config (primary)
389
+ const projectCfg = tryReadJson(PROJECT_CONFIG_PATH);
390
+ if (projectCfg) {
391
+ cfg = deepMerge(clone(DEFAULT_CONFIG), projectCfg);
392
+ } else {
393
+ // 2) Package default (read-only)
394
+ const pkgCfg = tryReadJson(PACKAGE_CONFIG_PATH);
395
+ if (pkgCfg) {
396
+ cfg = deepMerge(clone(DEFAULT_CONFIG), pkgCfg);
397
+ }
398
+ // 3) Legacy migration (read-only source)
399
+ if (!projectCfg) {
400
+ const fromLegacy = tryReadJson(LEGACY_CONFIG_PATH);
401
+ if (fromLegacy) {
402
+ cfg = deepMerge(clone(DEFAULT_CONFIG), fromLegacy);
403
+ // Attempt to migrate to project settings
404
+ // Ignore migration errors; we still return merged cfg in memory
405
+ // eslint-disable-next-line no-unused-vars
406
+ console.warn('[i18ntk] Detected legacy config at ~/.i18ntk. Migrating to project settings directory...');
407
+ const _ = (async () => { await migrateLegacyIfNeeded(DEFAULT_CONFIG); })();
408
+ }
409
+ }
410
+ }
411
+ applyEnvOverrides(cfg);
412
+ currentConfig = cfg;
413
+ return currentConfig;
414
+ } catch (error) {
415
+ console.error('[i18ntk] Error in loadConfig:', error.message);
416
+ currentConfig = clone(DEFAULT_CONFIG);
417
+ return currentConfig;
418
+ } finally {
419
+ configLoadInProgress = false;
420
+ recursionDepth = Math.max(0, recursionDepth - 1);
421
+ }
404
422
  }
405
423
 
406
- async function saveConfig(cfg = currentConfig) {
407
- if (!cfg) return;
408
-
424
+ async function saveConfig(cfg = currentConfig) {
425
+ if (!cfg || typeof cfg !== 'object') return;
426
+
409
427
  try {
410
428
  // Ensure settings directory exists
411
- if (!fs.existsSync(PROJECT_SETTINGS_DIR)) {
429
+ if (!SecurityUtils.safeExistsSync(PROJECT_SETTINGS_DIR)) {
412
430
  fs.mkdirSync(PROJECT_SETTINGS_DIR, { recursive: true });
413
431
  }
414
-
415
- // Save configuration to the project settings directory
416
- await fs.promises.writeFile(PROJECT_CONFIG_PATH, JSON.stringify(cfg, null, 2), 'utf8');
417
- currentConfig = cfg;
432
+
433
+ const serialized = JSON.stringify(cfg, null, 2);
434
+ if (typeof serialized !== 'string' || serialized.length === 0) {
435
+ throw new Error('Cannot save empty configuration payload');
436
+ }
437
+
438
+ // Atomic write prevents partially-written/empty config files.
439
+ const tempPath = `${PROJECT_CONFIG_PATH}.tmp`;
440
+ await fs.promises.writeFile(tempPath, serialized, 'utf8');
441
+ await fs.promises.rename(tempPath, PROJECT_CONFIG_PATH);
442
+ currentConfig = cfg;
418
443
  } catch (error) {
419
444
  console.error('[i18ntk] Error saving configuration:', error.message);
420
445
  throw error;
@@ -422,58 +447,71 @@ async function saveConfig(cfg = currentConfig) {
422
447
  }
423
448
 
424
449
  function getConfig() {
425
- if (currentConfig) {
426
- return resolvePaths(currentConfig);
427
- }
428
-
429
- try {
430
- // Ensure settings directory exists
431
- if (!fs.existsSync(PROJECT_SETTINGS_DIR)) {
432
- fs.mkdirSync(PROJECT_SETTINGS_DIR, { recursive: true });
433
- }
450
+ // Check for recursion
451
+ const recursionFallback = checkRecursionGuard();
452
+ if (recursionFallback) return resolvePaths(recursionFallback);
434
453
 
435
- // Setup is now handled automatically by the unified config system
436
- // No need to check here - handled by getUnifiedConfig
437
-
438
- // Check if config file exists
439
- if (fs.existsSync(PROJECT_CONFIG_PATH)) {
440
- const config = JSON.parse(fs.readFileSync(PROJECT_CONFIG_PATH, 'utf8'));
441
- currentConfig = config;
442
- return resolvePaths(config);
443
- }
444
-
445
- // Check for legacy config for migration
446
- if (fs.existsSync(LEGACY_CONFIG_PATH)) {
447
- console.log('📦 Migrating legacy configuration...');
448
- const legacyConfig = JSON.parse(fs.readFileSync(LEGACY_CONFIG_PATH, 'utf8'));
449
- const migratedConfig = { ...DEFAULT_CONFIG, ...legacyConfig };
450
- saveConfig(migratedConfig);
451
- currentConfig = migratedConfig;
452
-
453
- // Clean up legacy config
454
- try {
455
- fs.unlinkSync(LEGACY_CONFIG_PATH);
456
- if (fs.readdirSync(LEGACY_CONFIG_DIR).length === 0) {
457
- fs.rmdirSync(LEGACY_CONFIG_DIR);
458
- }
459
- } catch (cleanupError) {
460
- // Ignore cleanup errors
461
- }
462
-
463
- return resolvePaths(migratedConfig);
454
+ if (currentConfig) {
455
+ resetRecursionGuard();
456
+ return resolvePaths(currentConfig);
464
457
  }
465
458
 
466
- // Use package defaults for new installation
467
- console.log('📦 Initializing with default configuration...');
468
- saveConfig(DEFAULT_CONFIG);
469
- currentConfig = DEFAULT_CONFIG;
470
- return resolvePaths(DEFAULT_CONFIG);
471
-
472
- } catch (error) {
473
- console.warn('⚠️ Error loading configuration, using defaults:', error.message);
474
- currentConfig = DEFAULT_CONFIG;
475
- return resolvePaths(DEFAULT_CONFIG);
476
- }
459
+ try {
460
+ // Ensure settings directory exists
461
+ if (!SecurityUtils.safeExistsSync(PROJECT_SETTINGS_DIR)) {
462
+ fs.mkdirSync(PROJECT_SETTINGS_DIR, { recursive: true });
463
+ }
464
+
465
+ // Setup is now handled automatically by the unified config system
466
+ // No need to check here - handled by getUnifiedConfig
467
+
468
+ // Check if config file exists
469
+ if (SecurityUtils.safeExistsSync(PROJECT_CONFIG_PATH)) {
470
+ const rawConfig = SecurityUtils.safeReadFileSync(PROJECT_CONFIG_PATH, path.dirname(PROJECT_CONFIG_PATH), 'utf8');
471
+ const config = SecurityUtils.safeParseJSON(rawConfig);
472
+ if (config && typeof config === 'object') {
473
+ currentConfig = config;
474
+ return resolvePaths(config);
475
+ }
476
+ throw new Error('Invalid project configuration JSON');
477
+ }
478
+
479
+ // Check for legacy config for migration
480
+ if (SecurityUtils.safeExistsSync(LEGACY_CONFIG_PATH)) {
481
+ console.log('📦 Migrating legacy configuration...');
482
+ const legacyRaw = SecurityUtils.safeReadFileSync(LEGACY_CONFIG_PATH, path.dirname(LEGACY_CONFIG_PATH), 'utf8');
483
+ const legacyConfig = SecurityUtils.safeParseJSON(legacyRaw);
484
+ if (!legacyConfig || typeof legacyConfig !== 'object') {
485
+ throw new Error('Invalid legacy configuration JSON');
486
+ }
487
+ const migratedConfig = { ...DEFAULT_CONFIG, ...legacyConfig };
488
+ saveConfig(migratedConfig);
489
+ currentConfig = migratedConfig;
490
+
491
+ // Clean up legacy config
492
+ try {
493
+ fs.unlinkSync(LEGACY_CONFIG_PATH);
494
+ if (fs.readdirSync(LEGACY_CONFIG_DIR).length === 0) {
495
+ fs.rmdirSync(LEGACY_CONFIG_DIR);
496
+ }
497
+ } catch (cleanupError) {
498
+ // Ignore cleanup errors
499
+ }
500
+
501
+ return resolvePaths(migratedConfig);
502
+ }
503
+
504
+ // Use package defaults for new installation
505
+ console.log('📦 Initializing with default configuration...');
506
+ saveConfig(DEFAULT_CONFIG);
507
+ currentConfig = DEFAULT_CONFIG;
508
+ return resolvePaths(DEFAULT_CONFIG);
509
+
510
+ } catch (error) {
511
+ console.warn('⚠️ Error loading configuration, using defaults:', error.message);
512
+ currentConfig = DEFAULT_CONFIG;
513
+ return resolvePaths(DEFAULT_CONFIG);
514
+ }
477
515
  }
478
516
 
479
517
  async function setConfig(cfg) {
@@ -495,27 +533,30 @@ async function resetToDefaults() {
495
533
  return currentConfig;
496
534
  }
497
535
 
498
- function resolvePaths(cfg = getConfig()) {
499
- const root = path.resolve(projectRoot, cfg.projectRoot || '.');
500
- const resolved = clone(cfg);
501
- resolved.projectRoot = root;
502
- ['sourceDir', 'i18nDir', 'outputDir'].forEach(key => {
503
- if (resolved[key]) resolved[key] = path.resolve(root, resolved[key]);
504
- });
505
- if (resolved.scriptDirectories) {
506
- resolved.scriptDirectories = { ...resolved.scriptDirectories };
507
- for (const [k, v] of Object.entries(resolved.scriptDirectories)) {
508
- if (v) resolved.scriptDirectories[k] = path.resolve(root, v);
536
+ function resolvePaths(cfg) {
537
+ if (!cfg) {
538
+ cfg = clone(DEFAULT_CONFIG);
509
539
  }
510
- }
511
- return resolved;
540
+ const root = path.resolve(projectRoot, cfg.projectRoot || '.');
541
+ const resolved = clone(cfg);
542
+ resolved.projectRoot = root;
543
+ ['sourceDir', 'i18nDir', 'outputDir'].forEach(key => {
544
+ if (resolved[key]) resolved[key] = path.resolve(root, resolved[key]);
545
+ });
546
+ if (resolved.scriptDirectories) {
547
+ resolved.scriptDirectories = { ...resolved.scriptDirectories };
548
+ for (const [k, v] of Object.entries(resolved.scriptDirectories)) {
549
+ if (v) resolved.scriptDirectories[k] = path.resolve(root, v);
550
+ }
551
+ }
552
+ return resolved;
512
553
  }
513
554
 
514
555
  function toRelative(absPath) {
515
- if (!absPath) return absPath;
516
- const rel = path.relative(projectRoot, absPath);
517
- const normalized = rel ? `./${rel.replace(/\\/g, '/')}` : '.';
518
- return normalized;
556
+ if (!absPath) return absPath;
557
+ const rel = path.relative(projectRoot, absPath);
558
+ const normalized = rel ? `./${rel.replace(/\\/g, '/')}` : '.';
559
+ return normalized;
519
560
  }
520
561
 
521
562
 
@@ -532,5 +573,4 @@ module.exports = {
532
573
  resolvePaths,
533
574
  toRelative,
534
575
  normalizePathValue,
535
- }
536
-
576
+ }
package/utils/config.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const SecurityUtils = require('./security');
3
4
 
4
5
  const settingsManager = require('../settings/settings-manager');
5
6
  const CONFIG_FILE = 'i18ntk-config.json';
@@ -46,12 +47,12 @@ function loadConfig(cwd = settingsManager.configDir) {
46
47
  const configPath = getConfigPath(cwd);
47
48
 
48
49
  // Check if file exists and is accessible
49
- if (!fs.existsSync(configPath)) {
50
+ if (!SecurityUtils.safeExistsSync(configPath)) {
50
51
  return null;
51
52
  }
52
53
 
53
54
  // Read file with explicit encoding
54
- const raw = fs.readFileSync(configPath, { encoding: 'utf8', flag: 'r' });
55
+ const raw = SecurityUtils.safeReadFileSync(configPath, settingsManager.configDir, 'utf8');
55
56
 
56
57
  // Basic validation of file content
57
58
  if (!raw || typeof raw !== 'string') {
@@ -83,12 +84,12 @@ function saveConfig(config, cwd = settingsManager.configDir) {
83
84
  const dir = path.dirname(configPath);
84
85
 
85
86
  // Ensure directory exists
86
- if (!fs.existsSync(dir)) {
87
+ if (!SecurityUtils.safeExistsSync(dir)) {
87
88
  fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
88
89
  }
89
90
 
90
91
  // Write file with secure permissions (read/write for owner only)
91
- fs.writeFileSync(
92
+ SecurityUtils.safeWriteFileSync(
92
93
  configPath,
93
94
  JSON.stringify(config, null, 2),
94
95
  { mode: 0o600, encoding: 'utf8' }