i18ntk 1.0.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.
Files changed (86) hide show
  1. package/CHANGELOG.md +401 -0
  2. package/LICENSE +21 -0
  3. package/README.md +507 -0
  4. package/dev/README.md +37 -0
  5. package/dev/debug/README.md +30 -0
  6. package/dev/debug/complete-console-translations.js +295 -0
  7. package/dev/debug/console-key-checker.js +408 -0
  8. package/dev/debug/console-translations.js +335 -0
  9. package/dev/debug/debugger.js +408 -0
  10. package/dev/debug/export-missing-keys.js +432 -0
  11. package/dev/debug/final-normalize.js +236 -0
  12. package/dev/debug/find-extra-keys.js +68 -0
  13. package/dev/debug/normalize-locales.js +153 -0
  14. package/dev/debug/refactor-locales.js +240 -0
  15. package/dev/debug/reorder-locales.js +85 -0
  16. package/dev/debug/replace-hardcoded-console.js +378 -0
  17. package/docs/INSTALLATION.md +449 -0
  18. package/docs/README.md +222 -0
  19. package/docs/TODO_ROADMAP.md +279 -0
  20. package/docs/api/API_REFERENCE.md +377 -0
  21. package/docs/api/COMPONENTS.md +492 -0
  22. package/docs/api/CONFIGURATION.md +651 -0
  23. package/docs/api/NPM_PUBLISHING_GUIDE.md +434 -0
  24. package/docs/debug/DEBUG_README.md +30 -0
  25. package/docs/debug/DEBUG_TOOLS.md +494 -0
  26. package/docs/development/AGENTS.md +351 -0
  27. package/docs/development/DEVELOPMENT_RULES.md +165 -0
  28. package/docs/development/DEV_README.md +37 -0
  29. package/docs/release-notes/RELEASE_NOTES_v1.0.0.md +173 -0
  30. package/docs/release-notes/RELEASE_NOTES_v1.6.0.md +141 -0
  31. package/docs/release-notes/RELEASE_NOTES_v1.6.1.md +185 -0
  32. package/docs/release-notes/RELEASE_NOTES_v1.6.3.md +199 -0
  33. package/docs/reports/ANALYSIS_README.md +17 -0
  34. package/docs/reports/CONSOLE_MISMATCH_BUG_REPORT_v1.5.0.md +181 -0
  35. package/docs/reports/SIZING_README.md +18 -0
  36. package/docs/reports/SUMMARY_README.md +18 -0
  37. package/docs/reports/TRANSLATION_BUG_REPORT_v1.5.0.md +129 -0
  38. package/docs/reports/USAGE_README.md +18 -0
  39. package/docs/reports/VALIDATION_README.md +18 -0
  40. package/locales/de/auth.json +3 -0
  41. package/locales/de/common.json +16 -0
  42. package/locales/de/pagination.json +6 -0
  43. package/locales/en/auth.json +3 -0
  44. package/locales/en/common.json +16 -0
  45. package/locales/en/pagination.json +6 -0
  46. package/locales/es/auth.json +3 -0
  47. package/locales/es/common.json +16 -0
  48. package/locales/es/pagination.json +6 -0
  49. package/locales/fr/auth.json +3 -0
  50. package/locales/fr/common.json +16 -0
  51. package/locales/fr/pagination.json +6 -0
  52. package/locales/ru/auth.json +3 -0
  53. package/locales/ru/common.json +16 -0
  54. package/locales/ru/pagination.json +6 -0
  55. package/main/i18ntk-analyze.js +625 -0
  56. package/main/i18ntk-autorun.js +461 -0
  57. package/main/i18ntk-complete.js +494 -0
  58. package/main/i18ntk-init.js +686 -0
  59. package/main/i18ntk-manage.js +848 -0
  60. package/main/i18ntk-sizing.js +557 -0
  61. package/main/i18ntk-summary.js +671 -0
  62. package/main/i18ntk-usage.js +1282 -0
  63. package/main/i18ntk-validate.js +762 -0
  64. package/main/ui-i18n.js +332 -0
  65. package/package.json +152 -0
  66. package/scripts/fix-missing-translation-keys.js +214 -0
  67. package/scripts/verify-package.js +168 -0
  68. package/ui-locales/de.json +637 -0
  69. package/ui-locales/en.json +688 -0
  70. package/ui-locales/es.json +637 -0
  71. package/ui-locales/fr.json +637 -0
  72. package/ui-locales/ja.json +637 -0
  73. package/ui-locales/ru.json +637 -0
  74. package/ui-locales/zh.json +637 -0
  75. package/utils/admin-auth.js +317 -0
  76. package/utils/admin-cli.js +353 -0
  77. package/utils/admin-pin.js +409 -0
  78. package/utils/detect-language-mismatches.js +454 -0
  79. package/utils/i18n-helper.js +128 -0
  80. package/utils/maintain-language-purity.js +433 -0
  81. package/utils/native-translations.js +478 -0
  82. package/utils/security.js +384 -0
  83. package/utils/test-complete-system.js +356 -0
  84. package/utils/test-console-i18n.js +402 -0
  85. package/utils/translate-mismatches.js +571 -0
  86. package/utils/validate-language-purity.js +531 -0
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Admin PIN Management System
3
+ * Handles secure PIN creation, validation, and storage
4
+ */
5
+
6
+ const crypto = require('crypto');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const readline = require('readline');
10
+
11
+ class AdminPinManager {
12
+ constructor() {
13
+ this.pinFile = path.join(__dirname, '..', 'settings', 'admin-pin.json');
14
+ this.algorithm = 'aes-256-gcm';
15
+ this.keyLength = 32;
16
+ this.ivLength = 16;
17
+ this.tagLength = 16;
18
+
19
+ // Session management
20
+ this.isAuthenticated = false;
21
+ this.sessionTimeout = 30 * 60 * 1000; // 30 minutes in milliseconds
22
+ this.sessionTimer = null;
23
+ this.lastActivity = null;
24
+ }
25
+
26
+ /**
27
+ * Generate a random key for encryption
28
+ */
29
+ generateKey() {
30
+ return crypto.randomBytes(this.keyLength);
31
+ }
32
+
33
+ /**
34
+ * Encrypt the PIN
35
+ */
36
+ encryptPin(pin, key) {
37
+ const iv = crypto.randomBytes(this.ivLength);
38
+ const cipher = crypto.createCipheriv(this.algorithm, key, iv);
39
+
40
+ let encrypted = cipher.update(pin, 'utf8', 'hex');
41
+ encrypted += cipher.final('hex');
42
+
43
+ const tag = cipher.getAuthTag();
44
+
45
+ return {
46
+ encrypted,
47
+ iv: iv.toString('hex'),
48
+ tag: tag.toString('hex')
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Decrypt the PIN
54
+ */
55
+ decryptPin(encryptedData, key) {
56
+ try {
57
+ const decipher = crypto.createDecipheriv(this.algorithm, key, Buffer.from(encryptedData.iv, 'hex'));
58
+ decipher.setAuthTag(Buffer.from(encryptedData.tag, 'hex'));
59
+
60
+ let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
61
+ decrypted += decipher.final('utf8');
62
+
63
+ return decrypted;
64
+ } catch (error) {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Hash PIN for verification
71
+ */
72
+ hashPin(pin) {
73
+ return crypto.createHash('sha256').update(pin).digest('hex');
74
+ }
75
+
76
+ /**
77
+ * Set up a new admin PIN
78
+ */
79
+ async setupPin() {
80
+ const rl = readline.createInterface({
81
+ input: process.stdin,
82
+ output: process.stdout
83
+ });
84
+
85
+ try {
86
+ console.log('\nšŸ” Admin PIN Setup');
87
+ console.log('============================================================');
88
+ console.log('Create a 4-6 digit PIN for admin access to sensitive settings.');
89
+ console.log('This PIN will be required for:');
90
+ console.log(' • Changing security settings');
91
+ console.log(' • Modifying advanced configurations');
92
+ console.log(' • Accessing debug tools');
93
+ console.log(' • Resetting settings');
94
+
95
+ const pin = await this.promptPin(rl, 'Enter new admin PIN (4-6 digits): ');
96
+
97
+ if (!this.validatePin(pin)) {
98
+ console.log('āŒ Invalid PIN. Must be 4-6 digits.');
99
+ rl.close();
100
+ return false;
101
+ }
102
+
103
+ const confirmPin = await this.promptPin(rl, 'Confirm admin PIN: ');
104
+
105
+ if (pin !== confirmPin) {
106
+ console.log('āŒ PINs do not match.');
107
+ rl.close();
108
+ return false;
109
+ }
110
+
111
+ // Generate encryption key and encrypt PIN
112
+ const key = this.generateKey();
113
+ const encryptedPin = this.encryptPin(pin, key);
114
+ const hashedPin = this.hashPin(pin);
115
+
116
+ // Store encrypted data
117
+ const pinData = {
118
+ hash: hashedPin,
119
+ encrypted: encryptedPin,
120
+ key: key.toString('hex'),
121
+ created: new Date().toISOString(),
122
+ attempts: 0,
123
+ locked: false
124
+ };
125
+
126
+ // Ensure settings directory exists
127
+ const settingsDir = path.dirname(this.pinFile);
128
+ if (!fs.existsSync(settingsDir)) {
129
+ fs.mkdirSync(settingsDir, { recursive: true });
130
+ }
131
+
132
+ fs.writeFileSync(this.pinFile, JSON.stringify(pinData, null, 2));
133
+
134
+ console.log('āœ… Admin PIN has been set successfully!');
135
+ console.log('āš ļø Keep this PIN secure. It cannot be recovered if lost.');
136
+
137
+ rl.close();
138
+ return true;
139
+
140
+ } catch (error) {
141
+ console.error('āŒ Error setting up PIN:', error.message);
142
+ rl.close();
143
+ return false;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Prompt for PIN with hidden input
149
+ */
150
+ promptPin(rl, message) {
151
+ return new Promise((resolve) => {
152
+ process.stdout.write(message);
153
+
154
+ // Hide input
155
+ process.stdin.setRawMode(true);
156
+ let pin = '';
157
+
158
+ const onData = (char) => {
159
+ const charStr = char.toString();
160
+
161
+ if (charStr === '\r' || charStr === '\n') {
162
+ process.stdin.setRawMode(false);
163
+ process.stdin.removeListener('data', onData);
164
+ process.stdout.write('\n');
165
+ resolve(pin);
166
+ } else if (charStr === '\u0008' || charStr === '\u007f') {
167
+ // Backspace
168
+ if (pin.length > 0) {
169
+ pin = pin.slice(0, -1);
170
+ process.stdout.write('\b \b');
171
+ }
172
+ } else if (charStr >= '0' && charStr <= '9') {
173
+ pin += charStr;
174
+ process.stdout.write('*');
175
+ }
176
+ };
177
+
178
+ process.stdin.on('data', onData);
179
+ });
180
+ }
181
+
182
+ /**
183
+ * Validate PIN format
184
+ */
185
+ validatePin(pin) {
186
+ return /^\d{4,6}$/.test(pin);
187
+ }
188
+
189
+ /**
190
+ * Check if PIN is set
191
+ */
192
+ isPinSet() {
193
+ return fs.existsSync(this.pinFile);
194
+ }
195
+
196
+ /**
197
+ * Start authentication session
198
+ */
199
+ startSession() {
200
+ this.isAuthenticated = true;
201
+ this.lastActivity = Date.now();
202
+
203
+ // Clear existing timer
204
+ if (this.sessionTimer) {
205
+ clearTimeout(this.sessionTimer);
206
+ }
207
+
208
+ // Set new timeout
209
+ this.sessionTimer = setTimeout(() => {
210
+ this.endSession();
211
+ console.log('\nā° Admin session expired due to inactivity.');
212
+ }, this.sessionTimeout);
213
+ }
214
+
215
+ /**
216
+ * End authentication session
217
+ */
218
+ endSession() {
219
+ this.isAuthenticated = false;
220
+ this.lastActivity = null;
221
+
222
+ if (this.sessionTimer) {
223
+ clearTimeout(this.sessionTimer);
224
+ this.sessionTimer = null;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Check if currently authenticated
230
+ */
231
+ isCurrentlyAuthenticated() {
232
+ if (!this.isAuthenticated) {
233
+ return false;
234
+ }
235
+
236
+ // Check if session has expired
237
+ if (this.lastActivity && (Date.now() - this.lastActivity) > this.sessionTimeout) {
238
+ this.endSession();
239
+ return false;
240
+ }
241
+
242
+ // Update last activity
243
+ this.lastActivity = Date.now();
244
+ return true;
245
+ }
246
+
247
+ /**
248
+ * Require authentication (with session support)
249
+ */
250
+ async requireAuth(forceSetup = false) {
251
+ // Check if already authenticated in current session
252
+ if (this.isCurrentlyAuthenticated()) {
253
+ return true;
254
+ }
255
+
256
+ // Need to authenticate
257
+ const authenticated = await this.verifyPin(forceSetup);
258
+ if (authenticated) {
259
+ this.startSession();
260
+ }
261
+
262
+ return authenticated;
263
+ }
264
+
265
+ /**
266
+ * Verify admin PIN
267
+ */
268
+ async verifyPin(forceSetup = false) {
269
+ if (!this.isPinSet()) {
270
+ if (forceSetup) {
271
+ console.log('āš ļø No admin PIN set. Setting up PIN...');
272
+ return await this.setupPin();
273
+ } else {
274
+ console.log('āš ļø No admin PIN configured. Access denied.');
275
+ console.log('šŸ’” Use the admin settings to set up a PIN first.');
276
+ return false;
277
+ }
278
+ }
279
+
280
+ const rl = readline.createInterface({
281
+ input: process.stdin,
282
+ output: process.stdout
283
+ });
284
+
285
+ try {
286
+ const pinData = JSON.parse(fs.readFileSync(this.pinFile, 'utf8'));
287
+
288
+ if (pinData.locked) {
289
+ console.log('šŸ”’ Admin access is locked due to too many failed attempts.');
290
+ console.log('Please wait 5 minutes before trying again.');
291
+ rl.close();
292
+ return false;
293
+ }
294
+
295
+ const enteredPin = await this.promptPin(rl, 'šŸ” Enter admin PIN: ');
296
+ const hashedEnteredPin = this.hashPin(enteredPin);
297
+
298
+ if (hashedEnteredPin === pinData.hash) {
299
+ // Reset attempts on successful login
300
+ pinData.attempts = 0;
301
+ fs.writeFileSync(this.pinFile, JSON.stringify(pinData, null, 2));
302
+
303
+ console.log('āœ… Admin access granted.');
304
+ rl.close();
305
+ return true;
306
+ } else {
307
+ pinData.attempts = (pinData.attempts || 0) + 1;
308
+
309
+ if (pinData.attempts >= 3) {
310
+ pinData.locked = true;
311
+ setTimeout(() => {
312
+ pinData.locked = false;
313
+ pinData.attempts = 0;
314
+ fs.writeFileSync(this.pinFile, JSON.stringify(pinData, null, 2));
315
+ }, 5 * 60 * 1000); // 5 minutes
316
+ }
317
+
318
+ fs.writeFileSync(this.pinFile, JSON.stringify(pinData, null, 2));
319
+
320
+ console.log(`āŒ Incorrect PIN. ${3 - pinData.attempts} attempts remaining.`);
321
+ rl.close();
322
+ return false;
323
+ }
324
+
325
+ } catch (error) {
326
+ console.error('āŒ Error verifying PIN:', error.message);
327
+ rl.close();
328
+ return false;
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Prompt user for optional PIN setup
334
+ */
335
+ async promptOptionalSetup() {
336
+ if (this.isPinSet()) {
337
+ console.log('āœ… Admin PIN is already configured.');
338
+ return true;
339
+ }
340
+
341
+ const rl = readline.createInterface({
342
+ input: process.stdin,
343
+ output: process.stdout
344
+ });
345
+
346
+ try {
347
+ console.log('\nšŸ” Admin PIN Setup (Optional)');
348
+ console.log('============================================================');
349
+ console.log('Admin PIN protection adds security for sensitive operations like:');
350
+ console.log(' • Changing security settings');
351
+ console.log(' • Modifying advanced configurations');
352
+ console.log(' • Accessing debug tools');
353
+ console.log(' • Resetting settings');
354
+ console.log('');
355
+
356
+ const response = await new Promise(resolve => {
357
+ rl.question('Would you like to set up an admin PIN? (y/N): ', resolve);
358
+ });
359
+
360
+ rl.close();
361
+
362
+ if (response.toLowerCase() === 'y' || response.toLowerCase() === 'yes') {
363
+ return await this.setupPin();
364
+ } else {
365
+ console.log('ā­ļø Skipping admin PIN setup. You can set it up later in settings.');
366
+ return false;
367
+ }
368
+ } catch (error) {
369
+ rl.close();
370
+ console.error('āŒ Error during PIN setup prompt:', error.message);
371
+ return false;
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Get PIN display (masked)
377
+ */
378
+ getPinDisplay() {
379
+ if (!this.isPinSet()) {
380
+ return 'Not Set';
381
+ }
382
+
383
+ try {
384
+ const pinData = JSON.parse(fs.readFileSync(this.pinFile, 'utf8'));
385
+ const key = Buffer.from(pinData.key, 'hex');
386
+ const decryptedPin = this.decryptPin(pinData.encrypted, key);
387
+
388
+ if (decryptedPin) {
389
+ return '*'.repeat(decryptedPin.length);
390
+ }
391
+ } catch (error) {
392
+ // Ignore errors, return default
393
+ }
394
+
395
+ return '####';
396
+ }
397
+
398
+ /**
399
+ * Reset PIN
400
+ */
401
+ async resetPin() {
402
+ if (fs.existsSync(this.pinFile)) {
403
+ fs.unlinkSync(this.pinFile);
404
+ }
405
+ return await this.setupPin();
406
+ }
407
+ }
408
+
409
+ module.exports = AdminPinManager;