i18ntk 1.10.2 → 2.0.3
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/LICENSE +1 -1
- package/README.md +141 -1191
- package/main/i18ntk-analyze.js +65 -84
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +3 -3
- package/main/i18ntk-complete.js +90 -65
- package/main/i18ntk-doctor.js +123 -103
- package/main/i18ntk-fixer.js +61 -725
- package/main/i18ntk-go.js +14 -15
- package/main/i18ntk-init.js +77 -26
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +129 -30
- package/main/i18ntk-php.js +75 -75
- package/main/i18ntk-py.js +55 -56
- package/main/i18ntk-scanner.js +59 -57
- package/main/i18ntk-setup.js +9 -404
- package/main/i18ntk-sizing.js +6 -6
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +54 -18
- package/main/i18ntk-validate.js +13 -13
- package/main/manage/commands/AnalyzeCommand.js +1124 -0
- package/main/manage/commands/BackupCommand.js +62 -0
- package/main/manage/commands/CommandRouter.js +295 -0
- package/main/manage/commands/CompleteCommand.js +61 -0
- package/main/manage/commands/DoctorCommand.js +60 -0
- package/main/manage/commands/FixerCommand.js +624 -0
- package/main/manage/commands/InitCommand.js +62 -0
- package/main/manage/commands/ScannerCommand.js +654 -0
- package/main/manage/commands/SizingCommand.js +60 -0
- package/main/manage/commands/SummaryCommand.js +61 -0
- package/main/manage/commands/UsageCommand.js +60 -0
- package/main/manage/commands/ValidateCommand.js +978 -0
- package/main/manage/index-fixed.js +1447 -0
- package/main/manage/index.js +1462 -0
- package/main/manage/managers/DebugMenu.js +140 -0
- package/main/manage/managers/InteractiveMenu.js +177 -0
- package/main/manage/managers/LanguageMenu.js +62 -0
- package/main/manage/managers/SettingsMenu.js +53 -0
- package/main/manage/services/AuthenticationService.js +263 -0
- package/main/manage/services/ConfigurationService-fixed.js +449 -0
- package/main/manage/services/ConfigurationService.js +449 -0
- package/main/manage/services/FileManagementService.js +368 -0
- package/main/manage/services/FrameworkDetectionService.js +458 -0
- package/main/manage/services/InitService.js +1051 -0
- package/main/manage/services/SetupService.js +462 -0
- package/main/manage/services/SummaryService.js +450 -0
- package/main/manage/services/UsageService.js +1502 -0
- package/package.json +32 -29
- package/runtime/enhanced.d.ts +221 -221
- package/runtime/index.d.ts +29 -29
- package/runtime/index.full.d.ts +331 -331
- package/runtime/index.js +7 -6
- package/scripts/build-lite.js +17 -17
- package/scripts/deprecate-versions.js +23 -6
- package/scripts/export-translations.js +5 -5
- package/scripts/fix-all-i18n.js +3 -3
- package/scripts/fix-and-purify-i18n.js +3 -2
- package/scripts/fix-locale-control-chars.js +110 -0
- package/scripts/lint-locales.js +80 -0
- package/scripts/locale-optimizer.js +8 -8
- package/scripts/prepublish.js +21 -21
- package/scripts/security-check.js +117 -117
- package/scripts/sync-translations.js +4 -4
- package/scripts/sync-ui-locales.js +9 -8
- package/scripts/validate-all-translations.js +8 -7
- package/scripts/verify-deprecations.js +157 -161
- package/scripts/verify-translations.js +6 -5
- package/settings/i18ntk-config.json +282 -282
- package/settings/language-config.json +5 -5
- package/settings/settings-cli.js +9 -9
- package/settings/settings-manager.js +18 -18
- package/ui-locales/de.json +2417 -2348
- package/ui-locales/en.json +2415 -2352
- package/ui-locales/es.json +2425 -2353
- package/ui-locales/fr.json +2418 -2348
- package/ui-locales/ja.json +2463 -2361
- package/ui-locales/ru.json +2463 -2359
- package/ui-locales/zh.json +2418 -2351
- package/utils/admin-auth.js +2 -2
- package/utils/admin-cli.js +297 -297
- package/utils/admin-pin.js +9 -9
- package/utils/cli-helper.js +9 -9
- package/utils/config-helper.js +73 -104
- package/utils/config-manager.js +204 -171
- package/utils/config.js +5 -4
- package/utils/env-manager.js +249 -263
- package/utils/framework-detector.js +27 -24
- package/utils/i18n-helper.js +85 -41
- package/utils/init-helper.js +152 -94
- package/utils/json-output.js +98 -98
- package/utils/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +40 -29
- package/utils/prompt.js +14 -44
- package/utils/safe-json.js +40 -0
- package/utils/secure-errors.js +3 -3
- package/utils/security-check-improved.js +390 -0
- package/utils/security-config.js +5 -5
- package/utils/security-fixed.js +607 -0
- package/utils/security.js +652 -602
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- package/utils/ultra-performance-optimizer.js +11 -9
- package/utils/watch-locales.js +2 -1
- package/utils/prompt-fixed.js +0 -55
- package/utils/security-check.js +0 -454
package/utils/env-manager.js
CHANGED
|
@@ -1,270 +1,256 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Centralized Environment Variable Manager
|
|
3
|
-
*
|
|
4
|
-
* This module provides secure access to a fixed allowlist of environment variables.
|
|
5
|
-
* Only explicitly defined environment variables are accessible, all others are ignored.
|
|
6
|
-
* No secrets or sensitive data should ever be stored in environment variables.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const ALLOWED_ENV_VARS = {
|
|
10
|
-
// Logging and output
|
|
11
|
-
'I18NTK_LOG_LEVEL': {
|
|
12
|
-
default: 'error',
|
|
13
|
-
validate: (value) => ['error', 'warn', 'info', 'debug', 'silent'].includes(value.toLowerCase()),
|
|
14
|
-
transform: (value) => value.toLowerCase()
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
'I18NTK_OUTDIR': {
|
|
18
|
-
default: './i18ntk-reports',
|
|
19
|
-
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
20
|
-
transform: (value) => value.trim()
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
// UI and interaction
|
|
24
|
-
'
|
|
1
|
+
/**
|
|
2
|
+
* Centralized Environment Variable Manager
|
|
3
|
+
*
|
|
4
|
+
* This module provides secure access to a fixed allowlist of environment variables.
|
|
5
|
+
* Only explicitly defined environment variables are accessible, all others are ignored.
|
|
6
|
+
* No secrets or sensitive data should ever be stored in environment variables.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const ALLOWED_ENV_VARS = {
|
|
10
|
+
// Logging and output
|
|
11
|
+
'I18NTK_LOG_LEVEL': {
|
|
12
|
+
default: 'error',
|
|
13
|
+
validate: (value) => ['error', 'warn', 'info', 'debug', 'silent'].includes(value.toLowerCase()),
|
|
14
|
+
transform: (value) => value.toLowerCase()
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
'I18NTK_OUTDIR': {
|
|
18
|
+
default: './i18ntk-reports',
|
|
19
|
+
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
20
|
+
transform: (value) => value.trim()
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// UI and interaction
|
|
24
|
+
'I18NTK_UI_LANGUAGE': {
|
|
25
25
|
default: 'en',
|
|
26
26
|
validate: (value) => ['en', 'de', 'es', 'fr', 'ru', 'ja', 'zh'].includes(value.toLowerCase()),
|
|
27
27
|
transform: (value) => value.toLowerCase()
|
|
28
28
|
},
|
|
29
|
-
|
|
30
|
-
'I18NTK_SILENT': {
|
|
31
|
-
default: 'false',
|
|
32
|
-
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
33
|
-
transform: (value) => {
|
|
34
|
-
const lower = value.toLowerCase();
|
|
35
|
-
return lower === 'true' || lower === '1' || lower === 'yes' ? 'true' : 'false';
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
// Debug and development
|
|
40
|
-
'I18NTK_DEBUG_LOCALES': {
|
|
41
|
-
default: '0',
|
|
42
|
-
validate: (value) => ['0', '1', 'true', 'false'].includes(value.toLowerCase()),
|
|
43
|
-
transform: (value) => {
|
|
44
|
-
const lower = value.toLowerCase();
|
|
45
|
-
return lower === '1' || lower === 'true' ? '1' : '0';
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
// Runtime configuration
|
|
50
|
-
'I18NTK_RUNTIME_DIR': {
|
|
51
|
-
default: null,
|
|
52
|
-
validate: (value) => typeof value === 'string',
|
|
53
|
-
transform: (value) => value.trim() || null
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
'I18NTK_I18N_DIR': {
|
|
57
|
-
default: './locales',
|
|
58
|
-
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
59
|
-
transform: (value) => value.trim()
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
'I18NTK_SOURCE_DIR': {
|
|
63
|
-
default: './locales',
|
|
64
|
-
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
65
|
-
transform: (value) => value.trim()
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
'I18NTK_PROJECT_ROOT': {
|
|
69
|
-
default: '.',
|
|
70
|
-
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
71
|
-
transform: (value) => value.trim()
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
// Framework detection
|
|
75
|
-
'I18NTK_FRAMEWORK_PREFERENCE': {
|
|
76
|
-
default: 'auto',
|
|
77
|
-
validate: (value) => ['auto', 'vanilla', 'react', 'vue', 'angular', 'svelte', 'i18next', 'nuxt', 'next', 'django', 'flask', 'fastapi', 'spring-boot', 'laravel'].includes(value.toLowerCase()),
|
|
78
|
-
transform: (value) => value.toLowerCase()
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
'I18NTK_FRAMEWORK_FALLBACK': {
|
|
82
|
-
default: 'vanilla',
|
|
83
|
-
validate: (value) => ['vanilla', 'react', 'vue', 'angular', 'svelte', 'i18next', 'nuxt', 'next', 'django', 'flask', 'fastapi', 'spring-boot', 'laravel'].includes(value.toLowerCase()),
|
|
84
|
-
transform: (value) => value.toLowerCase()
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
'I18NTK_FRAMEWORK_DETECT': {
|
|
88
|
-
default: 'true',
|
|
89
|
-
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
90
|
-
transform: (value) => {
|
|
91
|
-
const lower = value.toLowerCase();
|
|
92
|
-
return lower === 'true' || lower === '1' || lower === 'yes' ? 'true' : 'false';
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Security: Block access to sensitive environment variables
|
|
98
|
-
const BLOCKED_PATTERNS = [
|
|
99
|
-
/^SECRET/i,
|
|
100
|
-
/^PASSWORD/i,
|
|
101
|
-
/^KEY/i,
|
|
102
|
-
/^TOKEN/i,
|
|
103
|
-
/^API_KEY/i,
|
|
104
|
-
/^PRIVATE/i,
|
|
105
|
-
/^AUTH/i,
|
|
106
|
-
/^CREDENTIAL/i,
|
|
107
|
-
/^AWS_/i,
|
|
108
|
-
/^GITHUB_/i,
|
|
109
|
-
/^NPM_/i,
|
|
110
|
-
/^NODE_/i,
|
|
111
|
-
/^PATH$/,
|
|
112
|
-
/^HOME$/,
|
|
113
|
-
/^USER$/,
|
|
114
|
-
/^USERNAME$/,
|
|
115
|
-
/^SHELL$/,
|
|
116
|
-
/^TERM$/,
|
|
117
|
-
/^DISPLAY$/,
|
|
118
|
-
/^LANG$/,
|
|
119
|
-
/^LC_/i
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
class EnvironmentManager {
|
|
123
|
-
constructor() {
|
|
124
|
-
this._cache = new Map();
|
|
125
|
-
this._validated = new Set();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Get a validated environment variable value
|
|
130
|
-
* @param {string} name - Environment variable name
|
|
131
|
-
* @returns {string|null} - Validated value or null if not allowed
|
|
132
|
-
*/
|
|
133
|
-
get(name) {
|
|
134
|
-
// Only allow explicitly defined variables
|
|
135
|
-
if (!ALLOWED_ENV_VARS[name]) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Check cache first
|
|
140
|
-
if (this._cache.has(name)) {
|
|
141
|
-
return this._cache.get(name);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const definition = ALLOWED_ENV_VARS[name];
|
|
145
|
-
const rawValue = process.env[name];
|
|
146
|
-
|
|
147
|
-
// Use default if not set
|
|
148
|
-
if (rawValue === undefined || rawValue === null || rawValue === '') {
|
|
149
|
-
this._cache.set(name, definition.default);
|
|
150
|
-
return definition.default;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Validate and transform
|
|
154
|
-
try {
|
|
155
|
-
const transformed = definition.transform(rawValue);
|
|
156
|
-
|
|
157
|
-
if (!definition.validate(transformed)) {
|
|
158
|
-
console.warn(`[i18ntk] Invalid value for ${name}: "${rawValue}". Using default: ${definition.default}`);
|
|
159
|
-
this._cache.set(name, definition.default);
|
|
160
|
-
return definition.default;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
this._cache.set(name, transformed);
|
|
164
|
-
this._validated.add(name);
|
|
165
|
-
return transformed;
|
|
166
|
-
} catch (error) {
|
|
167
|
-
console.warn(`[i18ntk] Error processing ${name}: ${error.message}. Using default: ${definition.default}`);
|
|
168
|
-
this._cache.set(name, definition.default);
|
|
169
|
-
return definition.default;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Check if an environment variable is allowed
|
|
175
|
-
* @param {string} name - Environment variable name
|
|
176
|
-
* @returns {boolean} - True if allowed
|
|
177
|
-
*/
|
|
178
|
-
isAllowed(name) {
|
|
179
|
-
return !!ALLOWED_ENV_VARS[name];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Get all allowed environment variables with their current values
|
|
184
|
-
* @returns {Object} - Object with variable names as keys and values
|
|
185
|
-
*/
|
|
186
|
-
getAll() {
|
|
187
|
-
const result = {};
|
|
188
|
-
for (const name of Object.keys(ALLOWED_ENV_VARS)) {
|
|
189
|
-
result[name] = this.get(name);
|
|
190
|
-
}
|
|
191
|
-
return result;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Get documentation for all allowed environment variables
|
|
196
|
-
* @returns {Array} - Array of documentation objects
|
|
197
|
-
*/
|
|
198
|
-
getDocumentation() {
|
|
199
|
-
return Object.entries(ALLOWED_ENV_VARS).map(([name, definition]) => ({
|
|
200
|
-
name,
|
|
201
|
-
default: definition.default,
|
|
202
|
-
description: this._getDescription(name)
|
|
203
|
-
}));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Clear the cache (for testing)
|
|
208
|
-
*/
|
|
209
|
-
clearCache() {
|
|
210
|
-
this._cache.clear();
|
|
211
|
-
this._validated.clear();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Check if a variable name matches blocked patterns
|
|
216
|
-
* @param {string} name - Variable name to check
|
|
217
|
-
* @returns {boolean} - True if blocked
|
|
218
|
-
*/
|
|
219
|
-
isBlocked(name) {
|
|
220
|
-
return BLOCKED_PATTERNS.some(pattern => pattern.test(name));
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Get human-readable description for an environment variable
|
|
225
|
-
* @param {string} name - Environment variable name
|
|
226
|
-
* @returns {string} - Description
|
|
227
|
-
*/
|
|
228
|
-
_getDescription(name) {
|
|
229
|
-
const descriptions = {
|
|
230
|
-
'I18NTK_LOG_LEVEL': 'Logging level (error, warn, info, debug, silent)',
|
|
231
|
-
'I18NTK_OUTDIR': 'Output directory for reports and generated files',
|
|
232
|
-
'
|
|
233
|
-
'I18NTK_SILENT': 'Run in silent mode without interactive prompts',
|
|
234
|
-
'I18NTK_DEBUG_LOCALES': 'Enable debug logging for locale loading',
|
|
235
|
-
'I18NTK_RUNTIME_DIR': 'Custom runtime directory path',
|
|
236
|
-
'I18NTK_I18N_DIR': 'Directory containing i18n/locale files',
|
|
237
|
-
'I18NTK_SOURCE_DIR': 'Source directory for scanning',
|
|
238
|
-
'I18NTK_PROJECT_ROOT': 'Project root directory',
|
|
239
|
-
'I18NTK_FRAMEWORK_PREFERENCE': 'Preferred framework (auto, react, vue, etc.)',
|
|
240
|
-
'I18NTK_FRAMEWORK_FALLBACK': 'Fallback framework when auto-detection fails',
|
|
241
|
-
'I18NTK_FRAMEWORK_DETECT': 'Enable automatic framework detection'
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
return descriptions[name] || 'Configuration option';
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Create singleton instance
|
|
249
|
-
const envManager = new EnvironmentManager();
|
|
250
|
-
|
|
251
|
-
// Security check: Log any attempts to access blocked variables
|
|
252
|
-
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
|
|
253
|
-
const originalEnv = process.env;
|
|
254
|
-
process.env = new Proxy(originalEnv, {
|
|
255
|
-
get(target, prop) {
|
|
256
|
-
if (typeof prop === 'string' && envManager.isBlocked(prop)) {
|
|
257
|
-
console.warn(`[i18ntk] Security: Blocked access to sensitive environment variable: ${prop}`);
|
|
258
|
-
return undefined;
|
|
259
|
-
}
|
|
260
|
-
return target[prop];
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
|
|
29
|
+
|
|
30
|
+
'I18NTK_SILENT': {
|
|
31
|
+
default: 'false',
|
|
32
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
33
|
+
transform: (value) => {
|
|
34
|
+
const lower = value.toLowerCase();
|
|
35
|
+
return lower === 'true' || lower === '1' || lower === 'yes' ? 'true' : 'false';
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// Debug and development
|
|
40
|
+
'I18NTK_DEBUG_LOCALES': {
|
|
41
|
+
default: '0',
|
|
42
|
+
validate: (value) => ['0', '1', 'true', 'false'].includes(value.toLowerCase()),
|
|
43
|
+
transform: (value) => {
|
|
44
|
+
const lower = value.toLowerCase();
|
|
45
|
+
return lower === '1' || lower === 'true' ? '1' : '0';
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// Runtime configuration
|
|
50
|
+
'I18NTK_RUNTIME_DIR': {
|
|
51
|
+
default: null,
|
|
52
|
+
validate: (value) => typeof value === 'string',
|
|
53
|
+
transform: (value) => value.trim() || null
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
'I18NTK_I18N_DIR': {
|
|
57
|
+
default: './locales',
|
|
58
|
+
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
59
|
+
transform: (value) => value.trim()
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
'I18NTK_SOURCE_DIR': {
|
|
63
|
+
default: './locales',
|
|
64
|
+
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
65
|
+
transform: (value) => value.trim()
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
'I18NTK_PROJECT_ROOT': {
|
|
69
|
+
default: '.',
|
|
70
|
+
validate: (value) => typeof value === 'string' && value.length > 0,
|
|
71
|
+
transform: (value) => value.trim()
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Framework detection
|
|
75
|
+
'I18NTK_FRAMEWORK_PREFERENCE': {
|
|
76
|
+
default: 'auto',
|
|
77
|
+
validate: (value) => ['auto', 'vanilla', 'react', 'vue', 'angular', 'svelte', 'i18next', 'nuxt', 'next', 'django', 'flask', 'fastapi', 'spring-boot', 'laravel'].includes(value.toLowerCase()),
|
|
78
|
+
transform: (value) => value.toLowerCase()
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
'I18NTK_FRAMEWORK_FALLBACK': {
|
|
82
|
+
default: 'vanilla',
|
|
83
|
+
validate: (value) => ['vanilla', 'react', 'vue', 'angular', 'svelte', 'i18next', 'nuxt', 'next', 'django', 'flask', 'fastapi', 'spring-boot', 'laravel'].includes(value.toLowerCase()),
|
|
84
|
+
transform: (value) => value.toLowerCase()
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
'I18NTK_FRAMEWORK_DETECT': {
|
|
88
|
+
default: 'true',
|
|
89
|
+
validate: (value) => ['true', 'false', '1', '0', 'yes', 'no'].includes(value.toLowerCase()),
|
|
90
|
+
transform: (value) => {
|
|
91
|
+
const lower = value.toLowerCase();
|
|
92
|
+
return lower === 'true' || lower === '1' || lower === 'yes' ? 'true' : 'false';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Security: Block access to sensitive environment variables
|
|
98
|
+
const BLOCKED_PATTERNS = [
|
|
99
|
+
/^SECRET/i,
|
|
100
|
+
/^PASSWORD/i,
|
|
101
|
+
/^KEY/i,
|
|
102
|
+
/^TOKEN/i,
|
|
103
|
+
/^API_KEY/i,
|
|
104
|
+
/^PRIVATE/i,
|
|
105
|
+
/^AUTH/i,
|
|
106
|
+
/^CREDENTIAL/i,
|
|
107
|
+
/^AWS_/i,
|
|
108
|
+
/^GITHUB_/i,
|
|
109
|
+
/^NPM_/i,
|
|
110
|
+
/^NODE_/i,
|
|
111
|
+
/^PATH$/,
|
|
112
|
+
/^HOME$/,
|
|
113
|
+
/^USER$/,
|
|
114
|
+
/^USERNAME$/,
|
|
115
|
+
/^SHELL$/,
|
|
116
|
+
/^TERM$/,
|
|
117
|
+
/^DISPLAY$/,
|
|
118
|
+
/^LANG$/,
|
|
119
|
+
/^LC_/i
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
class EnvironmentManager {
|
|
123
|
+
constructor() {
|
|
124
|
+
this._cache = new Map();
|
|
125
|
+
this._validated = new Set();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get a validated environment variable value
|
|
130
|
+
* @param {string} name - Environment variable name
|
|
131
|
+
* @returns {string|null} - Validated value or null if not allowed
|
|
132
|
+
*/
|
|
133
|
+
get(name) {
|
|
134
|
+
// Only allow explicitly defined variables
|
|
135
|
+
if (!ALLOWED_ENV_VARS[name]) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Check cache first
|
|
140
|
+
if (this._cache.has(name)) {
|
|
141
|
+
return this._cache.get(name);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const definition = ALLOWED_ENV_VARS[name];
|
|
145
|
+
const rawValue = process.env[name];
|
|
146
|
+
|
|
147
|
+
// Use default if not set
|
|
148
|
+
if (rawValue === undefined || rawValue === null || rawValue === '') {
|
|
149
|
+
this._cache.set(name, definition.default);
|
|
150
|
+
return definition.default;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Validate and transform
|
|
154
|
+
try {
|
|
155
|
+
const transformed = definition.transform(rawValue);
|
|
156
|
+
|
|
157
|
+
if (!definition.validate(transformed)) {
|
|
158
|
+
console.warn(`[i18ntk] Invalid value for ${name}: "${rawValue}". Using default: ${definition.default}`);
|
|
159
|
+
this._cache.set(name, definition.default);
|
|
160
|
+
return definition.default;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this._cache.set(name, transformed);
|
|
164
|
+
this._validated.add(name);
|
|
165
|
+
return transformed;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.warn(`[i18ntk] Error processing ${name}: ${error.message}. Using default: ${definition.default}`);
|
|
168
|
+
this._cache.set(name, definition.default);
|
|
169
|
+
return definition.default;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check if an environment variable is allowed
|
|
175
|
+
* @param {string} name - Environment variable name
|
|
176
|
+
* @returns {boolean} - True if allowed
|
|
177
|
+
*/
|
|
178
|
+
isAllowed(name) {
|
|
179
|
+
return !!ALLOWED_ENV_VARS[name];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get all allowed environment variables with their current values
|
|
184
|
+
* @returns {Object} - Object with variable names as keys and values
|
|
185
|
+
*/
|
|
186
|
+
getAll() {
|
|
187
|
+
const result = {};
|
|
188
|
+
for (const name of Object.keys(ALLOWED_ENV_VARS)) {
|
|
189
|
+
result[name] = this.get(name);
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get documentation for all allowed environment variables
|
|
196
|
+
* @returns {Array} - Array of documentation objects
|
|
197
|
+
*/
|
|
198
|
+
getDocumentation() {
|
|
199
|
+
return Object.entries(ALLOWED_ENV_VARS).map(([name, definition]) => ({
|
|
200
|
+
name,
|
|
201
|
+
default: definition.default,
|
|
202
|
+
description: this._getDescription(name)
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Clear the cache (for testing)
|
|
208
|
+
*/
|
|
209
|
+
clearCache() {
|
|
210
|
+
this._cache.clear();
|
|
211
|
+
this._validated.clear();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Check if a variable name matches blocked patterns
|
|
216
|
+
* @param {string} name - Variable name to check
|
|
217
|
+
* @returns {boolean} - True if blocked
|
|
218
|
+
*/
|
|
219
|
+
isBlocked(name) {
|
|
220
|
+
return BLOCKED_PATTERNS.some(pattern => pattern.test(name));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get human-readable description for an environment variable
|
|
225
|
+
* @param {string} name - Environment variable name
|
|
226
|
+
* @returns {string} - Description
|
|
227
|
+
*/
|
|
228
|
+
_getDescription(name) {
|
|
229
|
+
const descriptions = {
|
|
230
|
+
'I18NTK_LOG_LEVEL': 'Logging level (error, warn, info, debug, silent)',
|
|
231
|
+
'I18NTK_OUTDIR': 'Output directory for reports and generated files',
|
|
232
|
+
'I18NTK_UI_LANGUAGE': 'UI language (en, de, es, fr, ru, ja, zh)',
|
|
233
|
+
'I18NTK_SILENT': 'Run in silent mode without interactive prompts',
|
|
234
|
+
'I18NTK_DEBUG_LOCALES': 'Enable debug logging for locale loading',
|
|
235
|
+
'I18NTK_RUNTIME_DIR': 'Custom runtime directory path',
|
|
236
|
+
'I18NTK_I18N_DIR': 'Directory containing i18n/locale files',
|
|
237
|
+
'I18NTK_SOURCE_DIR': 'Source directory for scanning',
|
|
238
|
+
'I18NTK_PROJECT_ROOT': 'Project root directory',
|
|
239
|
+
'I18NTK_FRAMEWORK_PREFERENCE': 'Preferred framework (auto, react, vue, etc.)',
|
|
240
|
+
'I18NTK_FRAMEWORK_FALLBACK': 'Fallback framework when auto-detection fails',
|
|
241
|
+
'I18NTK_FRAMEWORK_DETECT': 'Enable automatic framework detection'
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
return descriptions[name] || 'Configuration option';
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Create singleton instance
|
|
249
|
+
const envManager = new EnvironmentManager();
|
|
250
|
+
|
|
265
251
|
module.exports = {
|
|
266
252
|
EnvironmentManager,
|
|
267
253
|
envManager,
|
|
268
|
-
ALLOWED_ENV_VARS,
|
|
269
|
-
BLOCKED_PATTERNS
|
|
270
|
-
};
|
|
254
|
+
ALLOWED_ENV_VARS,
|
|
255
|
+
BLOCKED_PATTERNS
|
|
256
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
1
|
const path = require('path');
|
|
3
2
|
const { gte } = require('./version-utils');
|
|
3
|
+
const SecurityUtils = require('./security');
|
|
4
4
|
|
|
5
5
|
// Framework compatibility information
|
|
6
6
|
const FRAMEWORK_COMPATIBILITY = {
|
|
@@ -38,9 +38,9 @@ const FRAMEWORKS = {
|
|
|
38
38
|
configFilePatterns: [
|
|
39
39
|
/i18n\.(js|ts)$/,
|
|
40
40
|
/i18ntk\.config\.(js|ts)$/,
|
|
41
|
-
/\.i18nrc(\.(js|json))
|
|
41
|
+
/\.i18nrc(\.(js|json))?$/,
|
|
42
42
|
],
|
|
43
|
-
setupGuide: '
|
|
43
|
+
setupGuide: 'Refer to the official i18ntk documentation on GitHub for setup instructions.',
|
|
44
44
|
priority: 100, // Higher priority to detect before other frameworks
|
|
45
45
|
ignore: [
|
|
46
46
|
'node_modules/**',
|
|
@@ -52,7 +52,7 @@ const FRAMEWORKS = {
|
|
|
52
52
|
'**/*.test.{js,jsx,ts,tsx}'
|
|
53
53
|
]
|
|
54
54
|
},
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
// Vue i18n has highest specificity due to its unique syntax
|
|
57
57
|
'vue-i18n': {
|
|
58
58
|
name: 'vue-i18n',
|
|
@@ -66,7 +66,7 @@ const FRAMEWORKS = {
|
|
|
66
66
|
],
|
|
67
67
|
ignore: ['node_modules/**']
|
|
68
68
|
},
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
// React i18next has medium specificity
|
|
71
71
|
'react-i18next': {
|
|
72
72
|
name: 'React i18next',
|
|
@@ -76,9 +76,9 @@ const FRAMEWORKS = {
|
|
|
76
76
|
regex: /\b(?:useTranslation|withTranslation|Trans|I18n|i18n\.t|t\(?=\s*[`'"])/,
|
|
77
77
|
configFile: 'i18n.js',
|
|
78
78
|
configFilePatterns: [/i18n\.(js|ts)$/, /i18next\.config\.(js|ts)$/],
|
|
79
|
-
setupGuide: '
|
|
79
|
+
setupGuide: 'Refer to the official react-i18next documentation for setup instructions.'
|
|
80
80
|
},
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
// Base i18next has lowest specificity
|
|
83
83
|
'i18next': {
|
|
84
84
|
name: 'i18next',
|
|
@@ -246,10 +246,10 @@ const FRAMEWORKS = {
|
|
|
246
246
|
'test/tmp/**'
|
|
247
247
|
]
|
|
248
248
|
},
|
|
249
|
-
go: {
|
|
250
|
-
name: 'go',
|
|
251
|
-
deps: ['
|
|
252
|
-
globs: ['**/*.go'],
|
|
249
|
+
go: {
|
|
250
|
+
name: 'go',
|
|
251
|
+
deps: ['go-i18n', 'x-text'],
|
|
252
|
+
globs: ['**/*.go'],
|
|
253
253
|
patterns: [
|
|
254
254
|
/i18n\.NewMessage\([^,]+,\s*["`]([^"`]+)["`]/g,
|
|
255
255
|
/i18n\.NewLocalizer\([^)]+\)\.MustLocalize\([^,]+,\s*["`]([^"`]+)["`]/g,
|
|
@@ -277,11 +277,6 @@ const FRAMEWORKS = {
|
|
|
277
277
|
}
|
|
278
278
|
};
|
|
279
279
|
|
|
280
|
-
/**
|
|
281
|
-
* Detect the i18n framework being used in the project
|
|
282
|
-
* @param {string} projectRoot - Path to the project root
|
|
283
|
-
* @returns {Promise<Object>} Object containing framework info and detection confidence
|
|
284
|
-
*/
|
|
285
280
|
/**
|
|
286
281
|
* Detects the i18n framework being used in the project
|
|
287
282
|
* @param {string} projectRoot - Path to the project root
|
|
@@ -294,23 +289,31 @@ async function detectFramework(projectRoot) {
|
|
|
294
289
|
|
|
295
290
|
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
296
291
|
const detectedFrameworks = [];
|
|
297
|
-
|
|
292
|
+
|
|
298
293
|
// Only proceed if package.json exists
|
|
299
|
-
if (!
|
|
294
|
+
if (!SecurityUtils.safeExistsSync(packageJsonPath, projectRoot)) {
|
|
300
295
|
return null;
|
|
301
296
|
}
|
|
302
297
|
|
|
303
298
|
try {
|
|
304
299
|
// Read and parse package.json
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
300
|
+
const packageJsonContent = SecurityUtils.safeReadFileSync(packageJsonPath, projectRoot, 'utf8');
|
|
301
|
+
if (!packageJsonContent) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
const packageJson = SecurityUtils.safeParseJSON(packageJsonContent);
|
|
305
|
+
if (!packageJson) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const deps = {
|
|
310
|
+
...(packageJson.dependencies || {}),
|
|
308
311
|
...(packageJson.devDependencies || {}),
|
|
309
312
|
...(packageJson.peerDependencies || {})
|
|
310
313
|
};
|
|
311
314
|
|
|
312
315
|
// Sort frameworks by priority (highest first)
|
|
313
|
-
const sortedFrameworks = Object.entries(FRAMEWORKS).sort((a, b) =>
|
|
316
|
+
const sortedFrameworks = Object.entries(FRAMEWORKS).sort((a, b) =>
|
|
314
317
|
(b[1].priority || 0) - (a[1].priority || 0)
|
|
315
318
|
);
|
|
316
319
|
|
|
@@ -350,7 +353,7 @@ async function detectFramework(projectRoot) {
|
|
|
350
353
|
// First sort by confidence
|
|
351
354
|
const confidenceDiff = b.confidence - a.confidence;
|
|
352
355
|
if (confidenceDiff !== 0) return confidenceDiff;
|
|
353
|
-
|
|
356
|
+
|
|
354
357
|
// If confidence is equal, sort by priority
|
|
355
358
|
return (b.priority || 0) - (a.priority || 0);
|
|
356
359
|
})[0];
|
|
@@ -363,4 +366,4 @@ async function detectFramework(projectRoot) {
|
|
|
363
366
|
}
|
|
364
367
|
}
|
|
365
368
|
|
|
366
|
-
module.exports = { detectFramework, FRAMEWORKS };
|
|
369
|
+
module.exports = { detectFramework, FRAMEWORKS };
|