i18ntk 2.3.8 → 2.5.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 +10 -6
- package/main/i18ntk-backup-class.js +35 -423
- package/main/manage/commands/BackupCommand.js +62 -62
- package/main/manage/commands/FixerCommand.js +97 -97
- package/main/manage/managers/DebugMenu.js +10 -9
- package/main/manage/services/SetupService.js +444 -462
- package/package.json +61 -32
- package/runtime/index.js +14 -8
- package/utils/admin-auth.js +594 -576
- package/utils/config-manager.js +72 -72
- package/utils/env-manager.js +117 -26
- package/utils/i18n-helper.js +50 -49
- package/utils/json-output.js +13 -12
- package/utils/logger.js +7 -6
- package/utils/npm-version-warning.js +12 -141
- package/utils/prompt-helper.js +44 -41
- package/utils/secure-errors.js +156 -154
- package/utils/security.js +235 -233
- package/utils/setup-enforcer.js +110 -109
- package/utils/terminal-icons.js +164 -163
- package/settings/i18ntk-config.json +0 -283
- package/utils/admin-pin.js +0 -520
- package/utils/arg-parser.js +0 -40
- package/utils/cli-args.js +0 -210
- package/utils/mini-commander.js +0 -179
- package/utils/missing-key-validator.js +0 -858
- package/utils/path-utils.js +0 -33
- package/utils/performance-optimizer.js +0 -246
- package/utils/prompt-new.js +0 -55
- package/utils/promptPin.js +0 -76
- package/utils/safe-json.js +0 -40
- package/utils/secure-backup.js +0 -340
- package/utils/security-check-improved.js +0 -393
- package/utils/security-config.js +0 -239
- package/utils/setup-validator.js +0 -717
- package/utils/ultra-performance-optimizer.js +0 -352
package/utils/path-utils.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
|
|
3
|
-
function isWindows(p) {
|
|
4
|
-
return /^[a-zA-Z]:\\/.test(p);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Convert an absolute path to a relative path using forward slashes.
|
|
9
|
-
* Works with both Windows and POSIX style paths.
|
|
10
|
-
* @param {string} base - Base directory
|
|
11
|
-
* @param {string} target - Target path
|
|
12
|
-
* @returns {string} Relative path
|
|
13
|
-
*/
|
|
14
|
-
function toRelative(base, target) {
|
|
15
|
-
const rel = isWindows(base) || isWindows(target)
|
|
16
|
-
? path.win32.relative(base, target)
|
|
17
|
-
: path.posix.relative(base, target);
|
|
18
|
-
return rel.split(/[\\/]/).join('/');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Resolve an array of paths against a base directory.
|
|
23
|
-
* Handles both Windows and POSIX style paths.
|
|
24
|
-
* @param {string} base - Base directory
|
|
25
|
-
* @param {string[]} paths - Paths to resolve
|
|
26
|
-
* @returns {string[]} Resolved absolute paths
|
|
27
|
-
*/
|
|
28
|
-
function resolvePaths(base, paths) {
|
|
29
|
-
const resolver = isWindows(base) ? path.win32.resolve : path.posix.resolve;
|
|
30
|
-
return paths.map(p => resolver(base, p));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
module.exports = { toRelative, resolvePaths };
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ultra-Performance Optimizer
|
|
3
|
-
* Advanced performance tuning utilities for i18n toolkit
|
|
4
|
-
* Targets sub-40ms processing for 200k keys with <10MB memory
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const os = require('os');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
|
|
11
|
-
class UltraPerformanceOptimizer {
|
|
12
|
-
constructor() {
|
|
13
|
-
this.cpuCores = os.cpus().length;
|
|
14
|
-
this.totalMemory = os.totalmem();
|
|
15
|
-
this.availableMemory = this.totalMemory * 0.8; // Use 80% of system memory
|
|
16
|
-
this.optimalSettings = this.calculateOptimalSettings();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Calculate optimal settings based on system resources
|
|
21
|
-
* @returns {Object} Optimized configuration
|
|
22
|
-
*/
|
|
23
|
-
calculateOptimalSettings() {
|
|
24
|
-
const memoryPerKey = 50; // bytes per key (very aggressive)
|
|
25
|
-
const maxKeysInMemory = Math.floor(this.availableMemory / memoryPerKey);
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
batchSize: Math.min(2000, Math.floor(maxKeysInMemory / 100)),
|
|
29
|
-
concurrency: Math.min(this.cpuCores * 2, 32),
|
|
30
|
-
memoryLimit: Math.floor(this.availableMemory / 1024 / 1024 * 0.7),
|
|
31
|
-
bufferSize: 64 * 1024,
|
|
32
|
-
cacheStrategy: 'lru',
|
|
33
|
-
compression: 'brotli',
|
|
34
|
-
streaming: true,
|
|
35
|
-
parallelProcessing: true,
|
|
36
|
-
aggressiveGC: true,
|
|
37
|
-
validateOnLoad: false,
|
|
38
|
-
validateOnSave: false,
|
|
39
|
-
skipBackup: true,
|
|
40
|
-
minimalLogging: true
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Generate ultra-performance configuration
|
|
46
|
-
* @returns {Object} Ultra-performance config
|
|
47
|
-
*/
|
|
48
|
-
generateUltraConfig() {
|
|
49
|
-
return {
|
|
50
|
-
processing: {
|
|
51
|
-
mode: 'ultra-extreme',
|
|
52
|
-
batchSize: this.optimalSettings.batchSize,
|
|
53
|
-
concurrency: this.optimalSettings.concurrency,
|
|
54
|
-
timeout: 5000,
|
|
55
|
-
retryAttempts: 0,
|
|
56
|
-
retryDelay: 0,
|
|
57
|
-
maxFileSize: 1024 * 1024, // 1MB max files
|
|
58
|
-
validateOnSave: false,
|
|
59
|
-
validateOnLoad: false,
|
|
60
|
-
cacheEnabled: true,
|
|
61
|
-
cacheTTL: 300000, // 5 minutes
|
|
62
|
-
autoBackup: false,
|
|
63
|
-
fileFilter: "**/*.json",
|
|
64
|
-
memoryLimit: `${this.optimalSettings.memoryLimit}MB`,
|
|
65
|
-
garbageCollection: true,
|
|
66
|
-
gcInterval: 500
|
|
67
|
-
},
|
|
68
|
-
optimization: {
|
|
69
|
-
memoryLimit: `${this.optimalSettings.memoryLimit}MB`,
|
|
70
|
-
localeOptimizer: true,
|
|
71
|
-
compression: this.optimalSettings.compression,
|
|
72
|
-
parallelProcessing: this.optimalSettings.parallelProcessing,
|
|
73
|
-
streaming: this.optimalSettings.streaming,
|
|
74
|
-
bufferSize: this.optimalSettings.bufferSize,
|
|
75
|
-
aggressiveGC: this.optimalSettings.aggressiveGC,
|
|
76
|
-
minimalLogging: this.optimalSettings.minimalLogging
|
|
77
|
-
},
|
|
78
|
-
filesystem: {
|
|
79
|
-
concurrentReads: this.optimalSettings.concurrency,
|
|
80
|
-
bufferSize: this.optimalSettings.bufferSize,
|
|
81
|
-
cacheDirectory: '.cache-ultra',
|
|
82
|
-
tempDirectory: '.tmp-ultra',
|
|
83
|
-
cleanupInterval: 30000,
|
|
84
|
-
compression: this.optimalSettings.compression
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Apply memory-efficient processing strategies
|
|
91
|
-
* @param {Array} keys - Translation keys to process
|
|
92
|
-
* @param {Function} processor - Processing function
|
|
93
|
-
* @returns {Promise<Array>} Processed results
|
|
94
|
-
*/
|
|
95
|
-
async processWithMemoryEfficiency(keys, processor) {
|
|
96
|
-
const batchSize = this.optimalSettings.batchSize;
|
|
97
|
-
const results = [];
|
|
98
|
-
|
|
99
|
-
for (let i = 0; i < keys.length; i += batchSize) {
|
|
100
|
-
const batch = keys.slice(i, i + batchSize);
|
|
101
|
-
const batchResults = await this.processBatch(batch, processor);
|
|
102
|
-
results.push(...batchResults);
|
|
103
|
-
|
|
104
|
-
// Aggressive memory management
|
|
105
|
-
if (global.gc && i % (batchSize * 10) === 0) {
|
|
106
|
-
global.gc();
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return results;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Process a single batch with parallel execution
|
|
115
|
-
* @param {Array} batch - Batch of keys
|
|
116
|
-
* @param {Function} processor - Processing function
|
|
117
|
-
* @returns {Promise<Array>} Batch results
|
|
118
|
-
*/
|
|
119
|
-
async processBatch(batch, processor) {
|
|
120
|
-
const chunkSize = Math.ceil(batch.length / this.optimalSettings.concurrency);
|
|
121
|
-
const chunks = [];
|
|
122
|
-
|
|
123
|
-
for (let i = 0; i < batch.length; i += chunkSize) {
|
|
124
|
-
chunks.push(batch.slice(i, i + chunkSize));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const promises = chunks.map(chunk => this.processChunk(chunk, processor));
|
|
128
|
-
const results = await Promise.all(promises);
|
|
129
|
-
return results.flat();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Process a chunk of keys
|
|
134
|
-
* @param {Array} chunk - Chunk of keys
|
|
135
|
-
* @param {Function} processor - Processing function
|
|
136
|
-
* @returns {Promise<Array>} Chunk results
|
|
137
|
-
*/
|
|
138
|
-
async processChunk(chunk, processor) {
|
|
139
|
-
return chunk.map(processor);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Generate performance report
|
|
144
|
-
* @param {Object} metrics - Performance metrics
|
|
145
|
-
* @returns {Object} Formatted report
|
|
146
|
-
*/
|
|
147
|
-
generateReport(metrics) {
|
|
148
|
-
return {
|
|
149
|
-
timestamp: new Date().toISOString(),
|
|
150
|
-
system: {
|
|
151
|
-
cpuCores: this.cpuCores,
|
|
152
|
-
totalMemory: `${Math.floor(this.totalMemory / 1024 / 1024)}MB`,
|
|
153
|
-
platform: os.platform(),
|
|
154
|
-
nodeVersion: process.version
|
|
155
|
-
},
|
|
156
|
-
optimization: {
|
|
157
|
-
batchSize: this.optimalSettings.batchSize,
|
|
158
|
-
concurrency: this.optimalSettings.concurrency,
|
|
159
|
-
memoryLimit: this.optimalSettings.memoryLimit,
|
|
160
|
-
compression: this.optimalSettings.compression
|
|
161
|
-
},
|
|
162
|
-
performance: {
|
|
163
|
-
processingTime: metrics.processingTime,
|
|
164
|
-
memoryUsage: metrics.memoryUsage,
|
|
165
|
-
throughput: metrics.throughput,
|
|
166
|
-
efficiency: metrics.efficiency
|
|
167
|
-
},
|
|
168
|
-
recommendations: this.generateRecommendations(metrics)
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Generate performance recommendations
|
|
174
|
-
* @param {Object} metrics - Performance metrics
|
|
175
|
-
* @returns {Array} Recommendations
|
|
176
|
-
*/
|
|
177
|
-
generateRecommendations(metrics) {
|
|
178
|
-
const recommendations = [];
|
|
179
|
-
|
|
180
|
-
if (metrics.processingTime > 40) {
|
|
181
|
-
recommendations.push('Consider reducing batch size for better memory locality');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (metrics.memoryUsage > 15) {
|
|
185
|
-
recommendations.push('Enable more aggressive garbage collection');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (metrics.throughput < 4000000) {
|
|
189
|
-
recommendations.push('Increase concurrency if CPU cores available');
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return recommendations;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Benchmark current system capabilities
|
|
197
|
-
* @returns {Promise<Object>} Benchmark results
|
|
198
|
-
*/
|
|
199
|
-
async benchmarkSystem() {
|
|
200
|
-
const testKeys = Array.from({ length: 10000 }, (_, i) => `key_${i}`);
|
|
201
|
-
const startTime = process.hrtime.bigint();
|
|
202
|
-
const startMemory = process.memoryUsage();
|
|
203
|
-
|
|
204
|
-
const results = await this.processWithMemoryEfficiency(testKeys, key => ({
|
|
205
|
-
key,
|
|
206
|
-
processed: true,
|
|
207
|
-
timestamp: Date.now()
|
|
208
|
-
}));
|
|
209
|
-
|
|
210
|
-
const endTime = process.hrtime.bigint();
|
|
211
|
-
const endMemory = process.memoryUsage();
|
|
212
|
-
|
|
213
|
-
const processingTime = Number(endTime - startTime) / 1000000; // Convert to ms
|
|
214
|
-
const memoryDelta = (endMemory.heapUsed - startMemory.heapUsed) / 1024 / 1024;
|
|
215
|
-
const throughput = testKeys.length / (processingTime / 1000);
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
processingTime,
|
|
219
|
-
memoryUsage: Math.max(0, memoryDelta),
|
|
220
|
-
throughput,
|
|
221
|
-
efficiency: testKeys.length / processingTime * 1000
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
module.exports = UltraPerformanceOptimizer;
|
|
227
|
-
|
|
228
|
-
// CLI usage
|
|
229
|
-
if (require.main === module) {
|
|
230
|
-
const optimizer = new UltraPerformanceOptimizer();
|
|
231
|
-
optimizer.benchmarkSystem().then(metrics => {
|
|
232
|
-
const report = optimizer.generateReport(metrics);
|
|
233
|
-
console.log('🚀 Ultra-Performance Analysis');
|
|
234
|
-
console.log('================================');
|
|
235
|
-
console.log(`Processing Time: ${metrics.processingTime.toFixed(2)}ms`);
|
|
236
|
-
console.log(`Memory Usage: ${metrics.memoryUsage.toFixed(2)}MB`);
|
|
237
|
-
console.log(`Throughput: ${(metrics.throughput / 1000000).toFixed(2)}M keys/sec`);
|
|
238
|
-
console.log(`Efficiency: ${metrics.efficiency.toFixed(0)} keys/ms`);
|
|
239
|
-
|
|
240
|
-
console.log('\n⚙️ Optimal Settings:');
|
|
241
|
-
console.log(JSON.stringify(optimizer.optimalSettings, null, 2));
|
|
242
|
-
|
|
243
|
-
console.log('\n📊 Recommendations:');
|
|
244
|
-
report.recommendations.forEach(rec => console.log(`- ${rec}`));
|
|
245
|
-
});
|
|
246
|
-
}
|
package/utils/prompt-new.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
const readline = require('readline');
|
|
2
|
-
const { logger } = require('./logger');
|
|
3
|
-
|
|
4
|
-
class Prompt {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.rl = readline.createInterface({
|
|
7
|
-
input: process.stdin,
|
|
8
|
-
output: process.stdout
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
close() {
|
|
13
|
-
this.rl.close();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async question(questionText) {
|
|
17
|
-
return new Promise((resolve) => {
|
|
18
|
-
this.rl.question(questionText, (answer) => {
|
|
19
|
-
resolve(answer.trim());
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async confirm(questionText, defaultValue = false) {
|
|
25
|
-
const answer = await this.question(`${questionText} (${defaultValue ? 'Y/n' : 'y/N'}) `);
|
|
26
|
-
if (answer === '') return defaultValue;
|
|
27
|
-
return /^y|yes$/i.test(answer);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async select(questionText, choices, defaultIndex = 0) {
|
|
31
|
-
logger.log(`\n${questionText}:`);
|
|
32
|
-
choices.forEach((choice, index) => {
|
|
33
|
-
logger.log(` ${index + 1}. ${choice}`);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
while (true) {
|
|
37
|
-
const answer = await this.question(`\nSelect an option (1-${choices.length}): `);
|
|
38
|
-
const selected = parseInt(answer, 10) - 1;
|
|
39
|
-
if (!isNaN(selected) && selected >= 0 && selected < choices.length) {
|
|
40
|
-
return selected;
|
|
41
|
-
}
|
|
42
|
-
logger.log('Invalid selection. Please try again.');
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async input(questionText, defaultValue = '') {
|
|
47
|
-
const answer = await this.question(`${questionText}${defaultValue ? ` [${defaultValue}]` : ''}: `);
|
|
48
|
-
return answer || defaultValue;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const prompt = new Prompt();
|
|
53
|
-
process.on('exit', () => prompt.close());
|
|
54
|
-
|
|
55
|
-
module.exports = prompt;
|
package/utils/promptPin.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
const readline = require('readline');
|
|
2
|
-
|
|
3
|
-
async function promptPin({ rl, label = 'Enter 4-digit Admin PIN: ', length = 4, digitsOnly = true } = {}) {
|
|
4
|
-
return rawMaskedPrompt(rl, label, { length, digitsOnly });
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
async function promptPinConfirm(rl, label1 = 'Enter new 4-digit Admin PIN: ', label2 = 'Confirm PIN: ') {
|
|
8
|
-
const pin1 = await promptPin({ rl, label: label1 });
|
|
9
|
-
const pin2 = await promptPin({ rl, label: label2 });
|
|
10
|
-
if (pin1 !== pin2) throw new Error('PINs do not match');
|
|
11
|
-
return pin1;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function rawMaskedPrompt(rl, promptText, { length = 4, digitsOnly = true } = {}) {
|
|
15
|
-
return new Promise((resolve) => {
|
|
16
|
-
const input = rl.input;
|
|
17
|
-
const output = rl.output;
|
|
18
|
-
|
|
19
|
-
const isRaw = input.isRaw;
|
|
20
|
-
if (!isRaw) input.setRawMode && input.setRawMode(true);
|
|
21
|
-
|
|
22
|
-
let buf = '';
|
|
23
|
-
|
|
24
|
-
const onData = (chunk) => {
|
|
25
|
-
const s = chunk.toString('utf8');
|
|
26
|
-
|
|
27
|
-
if (s === '\r' || s === '\n') {
|
|
28
|
-
if (buf.length === length) {
|
|
29
|
-
output.write('\n');
|
|
30
|
-
cleanup();
|
|
31
|
-
return resolve(buf);
|
|
32
|
-
}
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (s === '\u0003') {
|
|
37
|
-
output.write('\n');
|
|
38
|
-
cleanup();
|
|
39
|
-
process.exit(130);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (s === '\u0008' || s === '\u007f') {
|
|
43
|
-
if (buf.length > 0) buf = buf.slice(0, -1);
|
|
44
|
-
repaint();
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const ch = s;
|
|
49
|
-
if (digitsOnly) {
|
|
50
|
-
if (/^\d$/.test(ch) && buf.length < length) {
|
|
51
|
-
buf += ch;
|
|
52
|
-
repaint();
|
|
53
|
-
}
|
|
54
|
-
} else if (buf.length < length) {
|
|
55
|
-
buf += ch;
|
|
56
|
-
repaint();
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const repaint = () => {
|
|
61
|
-
output.cursorTo && output.cursorTo(0);
|
|
62
|
-
output.clearLine && output.clearLine(1);
|
|
63
|
-
output.write(promptText + '*'.repeat(buf.length));
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const cleanup = () => {
|
|
67
|
-
input.removeListener('data', onData);
|
|
68
|
-
if (!isRaw) input.setRawMode && input.setRawMode(false);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
output.write(promptText);
|
|
72
|
-
input.on('data', onData);
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
module.exports = { promptPin, promptPinConfirm };
|
package/utils/safe-json.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// utils/safe-json.js
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
function stripBOM(s) {
|
|
5
|
-
if (typeof s === 'string' && s.charCodeAt(0) === 0xFEFF) return s.slice(1);
|
|
6
|
-
return s;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Safe JSON load with guardrails.
|
|
11
|
-
* - Max file size (default 1MB)
|
|
12
|
-
* - BOM stripping
|
|
13
|
-
* - Single, typed error (no loops)
|
|
14
|
-
*/
|
|
15
|
-
async function readJsonSafe(filePath, { maxBytes = 1_000_000 } = {}) {
|
|
16
|
-
const buf = await fs.promises.readFile(filePath);
|
|
17
|
-
if (buf.length === 0) {
|
|
18
|
-
const err = new Error('Empty JSON file');
|
|
19
|
-
err.code = 'EJSONEMPTY';
|
|
20
|
-
err.path = filePath;
|
|
21
|
-
throw err;
|
|
22
|
-
}
|
|
23
|
-
if (buf.length > maxBytes) {
|
|
24
|
-
const err = new Error(`JSON too large (${buf.length} bytes)`);
|
|
25
|
-
err.code = 'EJSONTOOBIG';
|
|
26
|
-
err.path = filePath;
|
|
27
|
-
throw err;
|
|
28
|
-
}
|
|
29
|
-
try {
|
|
30
|
-
return JSON.parse(stripBOM(buf.toString('utf8')));
|
|
31
|
-
} catch (e) {
|
|
32
|
-
const err = new Error(`Invalid JSON`);
|
|
33
|
-
err.code = 'EJSONPARSE';
|
|
34
|
-
err.path = filePath;
|
|
35
|
-
err.cause = e;
|
|
36
|
-
throw err;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = { readJsonSafe };
|