@tng-sh/js 0.0.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.
package/LICENSE.md ADDED
@@ -0,0 +1,32 @@
1
+ # Commercial License
2
+
3
+ Copyright (c) 2026 Binary Dreams LLC. All rights reserved.
4
+
5
+ ## License Grant
6
+
7
+ This software is available under a commercial license. You may use this software if you have obtained a valid license from the copyright holder.
8
+
9
+ ## Restrictions
10
+
11
+ Without a valid commercial license, you may NOT:
12
+ - Use this software in any commercial or production environment
13
+ - Fork, copy, modify, or create derivative works
14
+ - Distribute, redistribute, sublicense, or sell copies
15
+ - Reverse engineer or decompile the software
16
+ - Remove or alter any copyright notices
17
+
18
+ ## Commercial Use
19
+
20
+ To obtain a commercial license for production use, contact: [raluca@tng.sh]
21
+
22
+ ## Evaluation Use
23
+
24
+ You may evaluate this software for a period of 30 days for non-commercial purposes only.
25
+
26
+ ## Termination
27
+
28
+ Any unauthorized use will result in immediate termination of rights and may result in legal action.
29
+
30
+ ## Disclaimer
31
+
32
+ THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.
package/bin/tng.js ADDED
@@ -0,0 +1,289 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const chalk = require('chalk');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { loadConfig } = require('../lib/config');
8
+ const { saveTestFile } = require('../lib/saveFile');
9
+ const { ping, getUserStats } = require('../index');
10
+
11
+ program
12
+ .name('tng')
13
+ .description('TNG - Automated Test Generation for JavaScript')
14
+ .version('0.0.1');
15
+
16
+ /**
17
+ * @command init
18
+ * Initialize TNG configuration - creates tng.config.js
19
+ */
20
+ program
21
+ .command('init')
22
+ .description('Initialize TNG configuration')
23
+ .action(() => {
24
+ console.log(chalk.blue.bold('\nTNG Configuration Setup'));
25
+ const configPath = path.join(process.cwd(), 'tng.config.js');
26
+
27
+ if (fs.existsSync(configPath)) {
28
+ console.log(chalk.yellow('tng.config.js already exists. Keeping existing config.'));
29
+ return;
30
+ }
31
+
32
+ const content = `/**
33
+ * TNG Configuration - Project settings for test generation
34
+ */
35
+
36
+ module.exports = {
37
+ // API Configuration
38
+ API_KEY: "your-api-key-here",
39
+ API_URL: "https://app.tng.sh/",
40
+
41
+ // Framework Detection
42
+ // Options: express, nextjs, nestjs, generic
43
+ FRAMEWORK: "express",
44
+
45
+ // Testing Configuration
46
+ // Options: jest, mocha, vitest
47
+ TEST_FRAMEWORK: "jest",
48
+
49
+ // Test Directory
50
+ TEST_DIRECTORY: "tests"
51
+ };
52
+ `;
53
+ fs.writeFileSync(configPath, content);
54
+ console.log(chalk.green(`✓ Created tng.config.js`));
55
+ });
56
+
57
+ /**
58
+ * @command ping
59
+ * Verify API connectivity
60
+ */
61
+ program
62
+ .command('ping')
63
+ .description('Verify API connectivity')
64
+ .action(() => {
65
+ const config = loadConfig();
66
+ if (!config.API_KEY) {
67
+ console.log(chalk.red('No API key configured. Run: tng init'));
68
+ return;
69
+ }
70
+
71
+ console.log(chalk.blue('Pinging TNG API...'));
72
+ try {
73
+ const result = ping(config.API_URL, config.API_KEY);
74
+ console.log(chalk.green(`✓ API Response: ${result}`));
75
+ } catch (error) {
76
+ console.log(chalk.red(`✗ Ping failed: ${error.message}`));
77
+ }
78
+ });
79
+
80
+ /**
81
+ * @command stats
82
+ * Show user statistics
83
+ */
84
+ program
85
+ .command('stats')
86
+ .description('Show user statistics')
87
+ .action(() => {
88
+ const config = loadConfig();
89
+ if (!config.API_KEY) {
90
+ console.log(chalk.red('No API key configured. Run: tng init'));
91
+ return;
92
+ }
93
+
94
+ console.log(chalk.blue('Fetching user stats...'));
95
+ try {
96
+ const statsJson = getUserStats(config.API_URL, config.API_KEY);
97
+ const stats = JSON.parse(statsJson);
98
+
99
+ console.log(chalk.bold('\n--- TNG User Statistics ---'));
100
+ console.log(`User ID: ${chalk.cyan(stats.user_id || 'N/A')}`);
101
+ console.log(`Email: ${chalk.cyan(stats.email || 'N/A')}`);
102
+ console.log(`Plan: ${chalk.cyan(stats.plan || 'Free')}`);
103
+ console.log(`Generations: ${chalk.green(stats.generations_count || 0)} / ${stats.generations_limit || '∞'}`);
104
+ console.log(`Status: ${stats.active ? chalk.green('Active') : chalk.red('Inactive')}`);
105
+ console.log('---------------------------\n');
106
+ } catch (error) {
107
+ console.log(chalk.red(`✗ Failed to fetch stats: ${error.message}`));
108
+ }
109
+ });
110
+
111
+ const GenerateTestsUI = require('../lib/generateTestsUi');
112
+
113
+ function launchInteractive() {
114
+ const ui = new GenerateTestsUI();
115
+ ui.show().catch(err => {
116
+ console.error(chalk.red('\nInteractive UI Error:'), err.message);
117
+ process.exit(1);
118
+ });
119
+ }
120
+
121
+ program
122
+ .command('i')
123
+ .alias('interactive')
124
+ .description('Launch interactive UI')
125
+ .action(() => {
126
+ launchInteractive();
127
+ });
128
+
129
+ /**
130
+ * Main command handler (handles -f and -m)
131
+ */
132
+ program
133
+ .option('-m, --method <name>', 'Method name to test')
134
+ .option('-f, --file <path>', 'JavaScript file path')
135
+ .option('-t, --type <type>', 'Component type (react_component, express_handler, etc)')
136
+ .option('-a, --audit', 'Run audit mode instead of test generation')
137
+ .option('--json', 'Output results as JSON events (machine-readable)')
138
+ .option('--outline', 'Output file outline (methods/classes) in JSON format')
139
+ .action(async (options) => {
140
+ if (options.file && options.outline) {
141
+ printOutline(options.file);
142
+ } else if (options.method && options.file) {
143
+ generateTest(options.file, options.method, options.type, options.audit, options.json);
144
+ } else if (options.file && !options.method) {
145
+ console.log(chalk.yellow('Specify a method with -m, use --outline to see methods, or run "tng i" for full selection.'));
146
+ } else if (!options.file && !options.method && process.argv.length <= 2) {
147
+ launchInteractive();
148
+ }
149
+ });
150
+
151
+ /**
152
+ * Output file outline (methods)
153
+ */
154
+ function printOutline(filePath) {
155
+ const absolutePath = path.resolve(filePath);
156
+ if (!fs.existsSync(absolutePath)) {
157
+ console.error(chalk.red(`File not found: ${filePath}`));
158
+ process.exit(1);
159
+ }
160
+
161
+ const { getFileOutline } = require('../index');
162
+ try {
163
+ const outlineJson = getFileOutline(absolutePath);
164
+ console.log(outlineJson);
165
+ } catch (error) {
166
+ console.error(chalk.red(`Error getting outline: ${error.message}`));
167
+ process.exit(1);
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Logic to generate test or run audit (delegates to native binary)
173
+ */
174
+ async function generateTest(filePath, methodName, testType, auditMode = false, jsonMode = false) {
175
+ const config = loadConfig();
176
+
177
+ // Initialize JSON session if requested
178
+ const jsonSession = jsonMode ? new (require('../lib/jsonSession').JsonSession)() : null;
179
+
180
+ if (jsonSession) {
181
+ jsonSession.start();
182
+ }
183
+
184
+ if (!config.API_KEY) {
185
+ const message = 'No API key configured. Run: tng init';
186
+ if (jsonSession) {
187
+ jsonSession.showConfigError('API_KEY');
188
+ jsonSession.stop();
189
+ } else {
190
+ console.log(chalk.red(message));
191
+ }
192
+ process.exit(1);
193
+ }
194
+
195
+ const absolutePath = path.resolve(filePath);
196
+ if (!fs.existsSync(absolutePath)) {
197
+ const message = `File not found: ${filePath}`;
198
+ if (jsonSession) {
199
+ jsonSession.displayError(message);
200
+ jsonSession.stop();
201
+ } else {
202
+ console.log(chalk.red(message));
203
+ }
204
+ process.exit(1);
205
+ }
206
+
207
+ const { submitAndPoll } = require('../index');
208
+
209
+ const action = auditMode ? 'Auditing' : 'Generating test for';
210
+ const startMessage = `🔍 ${action} ${methodName} in ${filePath}...`;
211
+
212
+ if (jsonSession) {
213
+ jsonSession.displayInfo(startMessage);
214
+ if (testType) {
215
+ jsonSession.displayInfo(`Type hint: ${testType}`);
216
+ }
217
+ } else {
218
+ console.log(chalk.blue(startMessage));
219
+ if (testType) {
220
+ console.log(chalk.cyan(`ℹ️ Type hint: ${testType}`));
221
+ }
222
+ }
223
+
224
+ try {
225
+ const resultJson = submitAndPoll(
226
+ absolutePath,
227
+ methodName,
228
+ null, // class_name
229
+ testType || null,
230
+ auditMode, // audit_mode
231
+ JSON.stringify(config),
232
+ (msg, percent) => {
233
+ if (jsonSession) {
234
+ // Emit progress events in JSON mode
235
+ const reporter = new (require('../lib/jsonSession').JsonProgressReporter)();
236
+ reporter.update(msg, percent);
237
+ } else {
238
+ // Simple console progress for CLI mode
239
+ if (percent % 10 === 0 || percent === 100) {
240
+ console.log(chalk.cyan(`[${percent}%] ${msg}`));
241
+ }
242
+ }
243
+ }
244
+ );
245
+
246
+ if (auditMode) {
247
+ // In audit mode, display the results
248
+ const result = JSON.parse(resultJson);
249
+ if (jsonSession) {
250
+ jsonSession.showAuditResults(result);
251
+ jsonSession.stop();
252
+ } else {
253
+ console.log(chalk.green('\n✓ Audit complete!\n'));
254
+ console.log(JSON.stringify(result, null, 2));
255
+ }
256
+ } else {
257
+ // In test generation mode, save the file
258
+ const saved = await saveTestFile(resultJson);
259
+
260
+ if (jsonSession) {
261
+ if (saved) {
262
+ jsonSession.emitEvent('test_saved', {
263
+ file_path: saved.file_path,
264
+ message: `Test saved to: ${saved.file_path}`
265
+ });
266
+ } else {
267
+ jsonSession.displayError('Failed to save test file');
268
+ }
269
+ jsonSession.stop();
270
+ } else {
271
+ if (saved) {
272
+ console.log(chalk.green(`✓ Test saved to: ${saved.file_path}`));
273
+ } else {
274
+ console.log(chalk.yellow('Failed to save test file'));
275
+ }
276
+ }
277
+ }
278
+ } catch (error) {
279
+ if (jsonSession) {
280
+ jsonSession.displayError(error.message);
281
+ jsonSession.stop();
282
+ } else {
283
+ console.log(chalk.red(`Error: ${error.message}`));
284
+ }
285
+ process.exit(1);
286
+ }
287
+ }
288
+
289
+ program.parse(process.argv);
Binary file
Binary file
Binary file
Binary file
package/index.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ export declare function getFileOutline(filePath: string): string
7
+ export declare function getProjectMetadata(projectRoot: string): string
8
+ export declare function findCallSites(projectRoot: string, methodName: string): string
9
+ export declare function ping(baseUrl: string, apiKey?: string | undefined | null): string
10
+ export declare function submitJob(baseUrl: string, apiKey: string, payloadJson: string): number
11
+ export declare function getUserStats(baseUrl: string, apiKey: string): string
12
+ export declare function submitAndPoll(filePath: string, methodName: string, className: string | undefined | null, testType: string | undefined | null, auditMode: boolean, configJson: string, callback: (...args: any[]) => any): string
package/index.js ADDED
@@ -0,0 +1,321 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /* prettier-ignore */
4
+
5
+ /* auto-generated by NAPI-RS */
6
+
7
+ const { existsSync, readFileSync } = require('fs')
8
+ const { join } = require('path')
9
+
10
+ const { platform, arch } = process
11
+
12
+ let nativeBinding = null
13
+ let localFileExisted = false
14
+ let loadError = null
15
+
16
+ function isMusl() {
17
+ // For Node 10
18
+ if (!process.report || typeof process.report.getReport !== 'function') {
19
+ try {
20
+ const lddPath = require('child_process').execSync('which ldd').toString().trim()
21
+ return readFileSync(lddPath, 'utf8').includes('musl')
22
+ } catch (e) {
23
+ return true
24
+ }
25
+ } else {
26
+ const { glibcVersionRuntime } = process.report.getReport().header
27
+ return !glibcVersionRuntime
28
+ }
29
+ }
30
+
31
+ switch (platform) {
32
+ case 'android':
33
+ switch (arch) {
34
+ case 'arm64':
35
+ localFileExisted = existsSync(join(__dirname, 'index.android-arm64.node'))
36
+ try {
37
+ if (localFileExisted) {
38
+ nativeBinding = require('./index.android-arm64.node')
39
+ } else {
40
+ nativeBinding = require('@tng-sh/js-android-arm64')
41
+ }
42
+ } catch (e) {
43
+ loadError = e
44
+ }
45
+ break
46
+ case 'arm':
47
+ localFileExisted = existsSync(join(__dirname, 'index.android-arm-eabi.node'))
48
+ try {
49
+ if (localFileExisted) {
50
+ nativeBinding = require('./index.android-arm-eabi.node')
51
+ } else {
52
+ nativeBinding = require('@tng-sh/js-android-arm-eabi')
53
+ }
54
+ } catch (e) {
55
+ loadError = e
56
+ }
57
+ break
58
+ default:
59
+ throw new Error(`Unsupported architecture on Android ${arch}`)
60
+ }
61
+ break
62
+ case 'win32':
63
+ switch (arch) {
64
+ case 'x64':
65
+ localFileExisted = existsSync(
66
+ join(__dirname, 'index.win32-x64-msvc.node')
67
+ )
68
+ try {
69
+ if (localFileExisted) {
70
+ nativeBinding = require('./index.win32-x64-msvc.node')
71
+ } else {
72
+ nativeBinding = require('@tng-sh/js-win32-x64-msvc')
73
+ }
74
+ } catch (e) {
75
+ loadError = e
76
+ }
77
+ break
78
+ case 'ia32':
79
+ localFileExisted = existsSync(
80
+ join(__dirname, 'index.win32-ia32-msvc.node')
81
+ )
82
+ try {
83
+ if (localFileExisted) {
84
+ nativeBinding = require('./index.win32-ia32-msvc.node')
85
+ } else {
86
+ nativeBinding = require('@tng-sh/js-win32-ia32-msvc')
87
+ }
88
+ } catch (e) {
89
+ loadError = e
90
+ }
91
+ break
92
+ case 'arm64':
93
+ localFileExisted = existsSync(
94
+ join(__dirname, 'index.win32-arm64-msvc.node')
95
+ )
96
+ try {
97
+ if (localFileExisted) {
98
+ nativeBinding = require('./index.win32-arm64-msvc.node')
99
+ } else {
100
+ nativeBinding = require('@tng-sh/js-win32-arm64-msvc')
101
+ }
102
+ } catch (e) {
103
+ loadError = e
104
+ }
105
+ break
106
+ default:
107
+ throw new Error(`Unsupported architecture on Windows: ${arch}`)
108
+ }
109
+ break
110
+ case 'darwin':
111
+ localFileExisted = existsSync(join(__dirname, 'index.darwin-universal.node'))
112
+ try {
113
+ if (localFileExisted) {
114
+ nativeBinding = require('./index.darwin-universal.node')
115
+ } else {
116
+ nativeBinding = require('@tng-sh/js-darwin-universal')
117
+ }
118
+ break
119
+ } catch {}
120
+ switch (arch) {
121
+ case 'x64':
122
+ localFileExisted = existsSync(join(__dirname, 'index.darwin-x64.node'))
123
+ try {
124
+ if (localFileExisted) {
125
+ nativeBinding = require('./index.darwin-x64.node')
126
+ } else {
127
+ nativeBinding = require('@tng-sh/js-darwin-x64')
128
+ }
129
+ } catch (e) {
130
+ loadError = e
131
+ }
132
+ break
133
+ case 'arm64':
134
+ localFileExisted = existsSync(
135
+ join(__dirname, 'index.darwin-arm64.node')
136
+ )
137
+ try {
138
+ if (localFileExisted) {
139
+ nativeBinding = require('./index.darwin-arm64.node')
140
+ } else {
141
+ nativeBinding = require('@tng-sh/js-darwin-arm64')
142
+ }
143
+ } catch (e) {
144
+ loadError = e
145
+ }
146
+ break
147
+ default:
148
+ throw new Error(`Unsupported architecture on macOS: ${arch}`)
149
+ }
150
+ break
151
+ case 'freebsd':
152
+ if (arch !== 'x64') {
153
+ throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
154
+ }
155
+ localFileExisted = existsSync(join(__dirname, 'index.freebsd-x64.node'))
156
+ try {
157
+ if (localFileExisted) {
158
+ nativeBinding = require('./index.freebsd-x64.node')
159
+ } else {
160
+ nativeBinding = require('@tng-sh/js-freebsd-x64')
161
+ }
162
+ } catch (e) {
163
+ loadError = e
164
+ }
165
+ break
166
+ case 'linux':
167
+ switch (arch) {
168
+ case 'x64':
169
+ if (isMusl()) {
170
+ localFileExisted = existsSync(
171
+ join(__dirname, 'index.linux-x64-musl.node')
172
+ )
173
+ try {
174
+ if (localFileExisted) {
175
+ nativeBinding = require('./index.linux-x64-musl.node')
176
+ } else {
177
+ nativeBinding = require('@tng-sh/js-linux-x64-musl')
178
+ }
179
+ } catch (e) {
180
+ loadError = e
181
+ }
182
+ } else {
183
+ localFileExisted = existsSync(
184
+ join(__dirname, 'index.linux-x64-gnu.node')
185
+ )
186
+ try {
187
+ if (localFileExisted) {
188
+ nativeBinding = require('./index.linux-x64-gnu.node')
189
+ } else {
190
+ nativeBinding = require('@tng-sh/js-linux-x64-gnu')
191
+ }
192
+ } catch (e) {
193
+ loadError = e
194
+ }
195
+ }
196
+ break
197
+ case 'arm64':
198
+ if (isMusl()) {
199
+ localFileExisted = existsSync(
200
+ join(__dirname, 'index.linux-arm64-musl.node')
201
+ )
202
+ try {
203
+ if (localFileExisted) {
204
+ nativeBinding = require('./index.linux-arm64-musl.node')
205
+ } else {
206
+ nativeBinding = require('@tng-sh/js-linux-arm64-musl')
207
+ }
208
+ } catch (e) {
209
+ loadError = e
210
+ }
211
+ } else {
212
+ localFileExisted = existsSync(
213
+ join(__dirname, 'index.linux-arm64-gnu.node')
214
+ )
215
+ try {
216
+ if (localFileExisted) {
217
+ nativeBinding = require('./index.linux-arm64-gnu.node')
218
+ } else {
219
+ nativeBinding = require('@tng-sh/js-linux-arm64-gnu')
220
+ }
221
+ } catch (e) {
222
+ loadError = e
223
+ }
224
+ }
225
+ break
226
+ case 'arm':
227
+ if (isMusl()) {
228
+ localFileExisted = existsSync(
229
+ join(__dirname, 'index.linux-arm-musleabihf.node')
230
+ )
231
+ try {
232
+ if (localFileExisted) {
233
+ nativeBinding = require('./index.linux-arm-musleabihf.node')
234
+ } else {
235
+ nativeBinding = require('@tng-sh/js-linux-arm-musleabihf')
236
+ }
237
+ } catch (e) {
238
+ loadError = e
239
+ }
240
+ } else {
241
+ localFileExisted = existsSync(
242
+ join(__dirname, 'index.linux-arm-gnueabihf.node')
243
+ )
244
+ try {
245
+ if (localFileExisted) {
246
+ nativeBinding = require('./index.linux-arm-gnueabihf.node')
247
+ } else {
248
+ nativeBinding = require('@tng-sh/js-linux-arm-gnueabihf')
249
+ }
250
+ } catch (e) {
251
+ loadError = e
252
+ }
253
+ }
254
+ break
255
+ case 'riscv64':
256
+ if (isMusl()) {
257
+ localFileExisted = existsSync(
258
+ join(__dirname, 'index.linux-riscv64-musl.node')
259
+ )
260
+ try {
261
+ if (localFileExisted) {
262
+ nativeBinding = require('./index.linux-riscv64-musl.node')
263
+ } else {
264
+ nativeBinding = require('@tng-sh/js-linux-riscv64-musl')
265
+ }
266
+ } catch (e) {
267
+ loadError = e
268
+ }
269
+ } else {
270
+ localFileExisted = existsSync(
271
+ join(__dirname, 'index.linux-riscv64-gnu.node')
272
+ )
273
+ try {
274
+ if (localFileExisted) {
275
+ nativeBinding = require('./index.linux-riscv64-gnu.node')
276
+ } else {
277
+ nativeBinding = require('@tng-sh/js-linux-riscv64-gnu')
278
+ }
279
+ } catch (e) {
280
+ loadError = e
281
+ }
282
+ }
283
+ break
284
+ case 's390x':
285
+ localFileExisted = existsSync(
286
+ join(__dirname, 'index.linux-s390x-gnu.node')
287
+ )
288
+ try {
289
+ if (localFileExisted) {
290
+ nativeBinding = require('./index.linux-s390x-gnu.node')
291
+ } else {
292
+ nativeBinding = require('@tng-sh/js-linux-s390x-gnu')
293
+ }
294
+ } catch (e) {
295
+ loadError = e
296
+ }
297
+ break
298
+ default:
299
+ throw new Error(`Unsupported architecture on Linux: ${arch}`)
300
+ }
301
+ break
302
+ default:
303
+ throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
304
+ }
305
+
306
+ if (!nativeBinding) {
307
+ if (loadError) {
308
+ throw loadError
309
+ }
310
+ throw new Error(`Failed to load native binding`)
311
+ }
312
+
313
+ const { getFileOutline, getProjectMetadata, findCallSites, ping, submitJob, getUserStats, submitAndPoll } = nativeBinding
314
+
315
+ module.exports.getFileOutline = getFileOutline
316
+ module.exports.getProjectMetadata = getProjectMetadata
317
+ module.exports.findCallSites = findCallSites
318
+ module.exports.ping = ping
319
+ module.exports.submitJob = submitJob
320
+ module.exports.getUserStats = getUserStats
321
+ module.exports.submitAndPoll = submitAndPoll
package/lib/config.js ADDED
@@ -0,0 +1,31 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ const loadConfig = () => {
5
+ const configFile = path.join(process.cwd(), 'tng.config.js');
6
+
7
+ if (!fs.existsSync(configFile)) {
8
+ return {};
9
+ }
10
+
11
+ try {
12
+ // Clear cache to allow reloading config in same process if needed (rare for CLI but good practice)
13
+ delete require.cache[require.resolve(configFile)];
14
+ const config = require(configFile);
15
+
16
+ // Filter for uppercase keys to match Python's convention,
17
+ // but fallback to the whole object if none are uppercase.
18
+ const filtered = {};
19
+ for (const [key, value] of Object.entries(config)) {
20
+ if (key === key.toUpperCase()) {
21
+ filtered[key] = value;
22
+ }
23
+ }
24
+
25
+ return Object.keys(filtered).length > 0 ? filtered : config;
26
+ } catch (error) {
27
+ return {};
28
+ }
29
+ };
30
+
31
+ module.exports = { loadConfig };