@tng-sh/js 0.1.3 → 0.1.5
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/.tmp/tng-clone-data-1770062573900-923.json +1 -0
- package/.tmp/tng-progress-control-1770062573779-154.json +1 -0
- package/.tmp/tng_sh_js-49dc3b9a.napi_type_def.tmp +13 -0
- package/.tmp/tng_sh_js-49dc3b9a.napi_wasi_register.tmp +13 -0
- package/README.md +46 -36
- package/bin/tng.js +65 -3
- package/binaries/go-ui-darwin-amd64 +0 -0
- package/binaries/go-ui-darwin-arm64 +0 -0
- package/binaries/go-ui-linux-amd64 +0 -0
- package/binaries/go-ui-linux-arm64 +0 -0
- package/index.d.ts +15 -0
- package/index.darwin-arm64.node +0 -0
- package/{tng_sh_js.darwin-x64.node → index.darwin-x64.node} +0 -0
- package/index.js +2 -1
- package/index.linux-arm64-gnu.node +0 -0
- package/{tng_sh_js.linux-x64-gnu.node → index.linux-x64-gnu.node} +0 -0
- package/lib/generateTestsUi.js +59 -0
- package/lib/goUiSession.js +93 -2
- package/lib/jsonSession.js +5 -1
- package/out.log +0 -0
- package/package.json +1 -1
- package/tng_sh_js.darwin-arm64.node +0 -0
- package/tng_sh_js.linux-arm64-gnu.node +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"file_path":"/Users/claudiu/work/tng-inc/js-pie/test_duplicates.js","matches":[{"start1":1,"end1":13,"start2":16,"end2":28,"line_count":13,"node_count":12,"complexity_score":245,"algo":"structural"}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"step","step":1,"message":"Done!","percent":100}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
tng_sh_js:{"kind": "interface", "name": "CloneMatch", "js_doc": "", "def": "start1: number\nend1: number\nstart2: number\nend2: number\nlineCount: number\nnodeCount: number\ncomplexityScore: number\nalgo: string", "original_name": "CloneMatch"}
|
|
2
|
+
tng_sh_js:{"kind": "fn", "name": "getFileOutline", "js_doc": "/**\n * Analyzes a JavaScript/TypeScript file and returns its structural outline as a JSON string.\n * Contains information about classes and methods found in the file.\n */\n", "def": "export declare function getFileOutline(filePath: string): string"}
|
|
3
|
+
tng_sh_js:{"kind": "fn", "name": "getProjectMetadata", "js_doc": "/**\n * Gathers project-wide metadata from package.json and related files.\n * Returns a JSON string containing dependencies, frameworks, and project type.\n */\n", "def": "export declare function getProjectMetadata(projectRoot: string): string"}
|
|
4
|
+
tng_sh_js:{"kind": "fn", "name": "findCallSites", "js_doc": "/**\n * Searches the project for all locations where a specific method is called.\n * Uses ripgrep under the hood for high performance.\n */\n", "def": "export declare function findCallSites(projectRoot: string, methodName: string): string"}
|
|
5
|
+
tng_sh_js:{"kind": "fn", "name": "ping", "js_doc": "/** Pings the TNG API to verify connectivity and API key validity. */\n", "def": "export declare function ping(baseUrl: string, apiKey?: string | undefined | null): string"}
|
|
6
|
+
tng_sh_js:{"kind": "fn", "name": "submitJob", "js_doc": "/** Submits a test generation job to the API and returns the numeric job ID. */\n", "def": "export declare function submitJob(baseUrl: string, apiKey: string, payloadJson: string): number"}
|
|
7
|
+
tng_sh_js:{"kind": "fn", "name": "getUserStats", "js_doc": "/** Fetches usage statistics for the authenticated user from the API. */\n", "def": "export declare function getUserStats(baseUrl: string, apiKey: string): string"}
|
|
8
|
+
tng_sh_js:{"kind": "fn", "name": "runAudit", "js_doc": "/**\n * Orchestrates the code audit process.\n * Analyzes the source, builds context, and streams results via the provided callback.\n */\n", "def": "export declare function runAudit(filePath: string, methodName: string, className: string | undefined | null, testType: string | undefined | null, configJson: string, callback: (...args: any[]) => any): string"}
|
|
9
|
+
tng_sh_js:{"kind": "fn", "name": "applyEdit", "js_doc": "/**\n * Applies a single edit operation to a file.\n * Uses backup and atomic write for safety.\n */\n", "def": "export declare function applyEdit(filePath: string, search: string, replace: string, lineHint?: number | undefined | null): string"}
|
|
10
|
+
tng_sh_js:{"kind": "fn", "name": "applyEditsAtomic", "js_doc": "/**\n * Applies multiple edit operations atomically.\n * All succeed or all fail (with rollback).\n */\n", "def": "export declare function applyEditsAtomic(operationsJson: string): string"}
|
|
11
|
+
tng_sh_js:{"kind": "fn", "name": "generateTest", "js_doc": "/**\n * Orchestrates the test generation process.\n * Analyzes code, submits a job, and polls for the final generated test.\n */\n", "def": "export declare function generateTest(filePath: string, methodName: string, className: string | undefined | null, testType: string | undefined | null, configJson: string, callback: (...args: any[]) => any): string"}
|
|
12
|
+
tng_sh_js:{"kind": "fn", "name": "getSymbolicTrace", "js_doc": "/** Analyzes a method and generates a symbolic execution trace. */\n", "def": "export declare function getSymbolicTrace(filePath: string, methodName: string, className?: string | undefined | null): string"}
|
|
13
|
+
tng_sh_js:{"kind": "fn", "name": "analyzeClones", "js_doc": "/**\n * Analyzes a file for code clones (duplicates).\n * Returns a JSON string containing the matches found.\n */\n", "def": "export declare function analyzeClones(projectRoot: string, filePath: string, level: string): string"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
tng_sh_js: __napi_register__CloneMatch_struct_0
|
|
2
|
+
tng_sh_js: __napi_register__get_file_outline_1
|
|
3
|
+
tng_sh_js: __napi_register__get_project_metadata_2
|
|
4
|
+
tng_sh_js: __napi_register__find_call_sites_3
|
|
5
|
+
tng_sh_js: __napi_register__ping_4
|
|
6
|
+
tng_sh_js: __napi_register__submit_job_5
|
|
7
|
+
tng_sh_js: __napi_register__get_user_stats_6
|
|
8
|
+
tng_sh_js: __napi_register__run_audit_7
|
|
9
|
+
tng_sh_js: __napi_register__apply_edit_8
|
|
10
|
+
tng_sh_js: __napi_register__apply_edits_atomic_9
|
|
11
|
+
tng_sh_js: __napi_register__generate_test_10
|
|
12
|
+
tng_sh_js: __napi_register__get_symbolic_trace_11
|
|
13
|
+
tng_sh_js: __napi_register__analyze_clones_12
|
package/README.md
CHANGED
|
@@ -4,69 +4,79 @@ High-performance test generation and context analysis for JavaScript/TypeScript,
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
Install the package as a development dependency in your project:
|
|
8
|
+
|
|
7
9
|
```bash
|
|
8
10
|
npm install @tng-sh/js --save-dev
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Usage
|
|
12
14
|
|
|
13
15
|
### 1. Initialize
|
|
14
|
-
|
|
16
|
+
First, run the init command to create a `tng.config.js` configuration file. This file stores your API keys and project settings.
|
|
15
17
|
|
|
16
18
|
```bash
|
|
17
|
-
npx
|
|
19
|
+
npx tng init
|
|
18
20
|
```
|
|
19
21
|
|
|
20
|
-
### 2.
|
|
21
|
-
|
|
22
|
+
### 2. Interactive Mode (TUI)
|
|
23
|
+
The most powerful way to use TNG is via the high-performance terminal interface. It provides a guided experience for all analysis tasks.
|
|
22
24
|
|
|
23
25
|
```bash
|
|
24
|
-
npx
|
|
26
|
+
npx tng i
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Capabilities in TUI:**
|
|
30
|
+
* **Generate Tests**: Guided test creation for methods, components, and utilities.
|
|
31
|
+
* **Audit Method**: Deep security, performance, and best-practice auditing.
|
|
32
|
+
* **Trace Method**: Symbolic execution and logic flow visualization.
|
|
33
|
+
* **X-Ray Method**: Deep dependency and side-effect analysis.
|
|
34
|
+
* **Check Duplicates**: High-performance clone detection across the codebase.
|
|
35
|
+
* **Dead Code**: Identify unreachable logic and unused variables/parameters.
|
|
36
|
+
* **User Stats**: Review your usage metrics and generation history.
|
|
37
|
+
|
|
38
|
+
### 3. Command Line Interface (CLI) ⚙️
|
|
39
|
+
For direct execution or integration into scripts.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# See file outline and method list
|
|
43
|
+
npx tng -f path/to/file.js --outline
|
|
44
|
+
|
|
45
|
+
# Generate tests for a specific method
|
|
46
|
+
npx tng -f path/to/file.js -m functionName -t react_component
|
|
47
|
+
|
|
48
|
+
# Check for code clones
|
|
49
|
+
npx tng -f path/to/file.js --clones
|
|
25
50
|
```
|
|
26
51
|
|
|
27
52
|
**Supported Types (`-t`):**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- `
|
|
31
|
-
|
|
32
|
-
- `orm_model`
|
|
33
|
-
- `background_job`
|
|
34
|
-
- `mailer`
|
|
35
|
-
- `utility`
|
|
36
|
-
|
|
37
|
-
### 3. Interactive Mode
|
|
38
|
-
Launch the interactive UI to explore and generate tests visually:
|
|
53
|
+
`react_component`, `express_handler`, `graphql_resolver`, `nest_js`, `orm_model`, `background_job`, `mailer`, `utility`.
|
|
54
|
+
|
|
55
|
+
### 4. Machine-Readable Output (`--json`) 🤖
|
|
56
|
+
Add the `--json` flag to any command to receive output as structured JSON events. This is ideal for CI/CD pipelines or custom integrations.
|
|
39
57
|
|
|
40
58
|
```bash
|
|
41
|
-
npx -
|
|
59
|
+
npx tng -f path/to/file.js --clones --json
|
|
42
60
|
```
|
|
43
61
|
|
|
44
62
|
## Features
|
|
45
63
|
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
64
|
+
- **Oxc-Powered**: Blazing fast AST analysis using the industry-leading Rust parser.
|
|
65
|
+
- **Multi-Platform**: Native support for macOS (Intel/ARM) and Linux (x64/ARM64).
|
|
66
|
+
- **Prettier Integration**: Automatically formats generated code to match your existing style.
|
|
67
|
+
- **TypeScript Native**: First-class support for `.ts` and `.tsx` analysis and generation.
|
|
50
68
|
|
|
51
|
-
## Configuration
|
|
69
|
+
## ⚙️ Configuration
|
|
52
70
|
|
|
53
|
-
|
|
71
|
+
Your `tng.config.js` allows for full customization:
|
|
54
72
|
|
|
55
73
|
```javascript
|
|
56
74
|
module.exports = {
|
|
57
|
-
// API Configuration
|
|
58
75
|
API_KEY: "your-api-key-here",
|
|
59
76
|
API_URL: "https://app.tng.sh/",
|
|
60
|
-
|
|
61
|
-
//
|
|
62
|
-
// Options:
|
|
63
|
-
FRAMEWORK: "express",
|
|
64
|
-
|
|
65
|
-
// Testing Configuration
|
|
66
|
-
// Options: jest, mocha, vitest
|
|
67
|
-
TEST_FRAMEWORK: "jest",
|
|
68
|
-
|
|
69
|
-
// Test Directory
|
|
70
|
-
TEST_DIRECTORY: "tests"
|
|
77
|
+
FRAMEWORK: "express", // Options: express, nextjs, nestjs, generic
|
|
78
|
+
TESTS_PATH: "tests", // Where to save generated tests
|
|
79
|
+
TEST_FRAMEWORK: "jest" // Options: jest, mocha, vitest
|
|
71
80
|
};
|
|
72
81
|
```
|
|
82
|
+
|
package/bin/tng.js
CHANGED
|
@@ -27,8 +27,8 @@ process.on('uncaughtException', (err) => {
|
|
|
27
27
|
|
|
28
28
|
program
|
|
29
29
|
.name('tng')
|
|
30
|
-
.description('TNG - Automated Test Generation, and
|
|
31
|
-
.version('0.1.
|
|
30
|
+
.description('TNG - Automated Test Generation, and Audit generation for JavaScript')
|
|
31
|
+
.version('0.1.5');
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* @command init
|
|
@@ -211,6 +211,8 @@ program
|
|
|
211
211
|
.option('-t, --type <type>', 'Component type (react_component, express_handler, etc) [required]')
|
|
212
212
|
.option('-a, --audit', 'Run audit mode instead of test generation')
|
|
213
213
|
.option('--trace', 'Run symbolic trace visualization')
|
|
214
|
+
.option('-c, --clones', 'Run duplicate code detection')
|
|
215
|
+
.option('-l, --level <level>', 'Set clone detection level (1, 2, or all)', 'all')
|
|
214
216
|
.option('--json', 'Output results as JSON events (machine-readable)')
|
|
215
217
|
|
|
216
218
|
.action(async (options) => {
|
|
@@ -261,12 +263,72 @@ program
|
|
|
261
263
|
}
|
|
262
264
|
generateTest(options.file, options.method, options.type, options.audit, options.json);
|
|
263
265
|
} else if (options.file && !options.method) {
|
|
264
|
-
|
|
266
|
+
if (options.clones) {
|
|
267
|
+
runClones(options.file, options.level || 'all', options.json);
|
|
268
|
+
} else {
|
|
269
|
+
console.log(chalk.yellow('Specify a method with -m, use --outline to see methods, or run "tng i" for full selection.'));
|
|
270
|
+
}
|
|
265
271
|
} else if (!options.file && !options.method && process.argv.length <= 2) {
|
|
266
272
|
launchInteractive();
|
|
267
273
|
}
|
|
268
274
|
});
|
|
269
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Logic to run clone detection
|
|
278
|
+
*/
|
|
279
|
+
async function runClones(filePath, level, jsonMode = false) {
|
|
280
|
+
const { analyzeClones } = require('../index');
|
|
281
|
+
const absolutePath = path.resolve(filePath);
|
|
282
|
+
const projectRoot = process.cwd();
|
|
283
|
+
|
|
284
|
+
if (!fs.existsSync(absolutePath)) {
|
|
285
|
+
console.log(chalk.red(`File not found: ${filePath}`));
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const GoUISession = require('../lib/goUiSession');
|
|
290
|
+
const session = new GoUISession();
|
|
291
|
+
|
|
292
|
+
if (jsonMode) {
|
|
293
|
+
const { JsonSession } = require('../lib/jsonSession');
|
|
294
|
+
const jsonSession = new JsonSession();
|
|
295
|
+
jsonSession.start();
|
|
296
|
+
try {
|
|
297
|
+
const matchesJson = analyzeClones(projectRoot, absolutePath, level);
|
|
298
|
+
const matches = JSON.parse(matchesJson);
|
|
299
|
+
jsonSession.showClones(absolutePath, matches);
|
|
300
|
+
jsonSession.stop();
|
|
301
|
+
} catch (e) {
|
|
302
|
+
jsonSession.displayError(e.message);
|
|
303
|
+
jsonSession.stop();
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
console.log(chalk.blue(`🔍 Analyzing clones in ${filePath}...`));
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const matches = await session.showProgress('Analyzing clones...', async (progress) => {
|
|
313
|
+
progress.update('Scanning structures...', { percent: 50 });
|
|
314
|
+
const matchesJson = analyzeClones(projectRoot, absolutePath, level);
|
|
315
|
+
const matches = JSON.parse(matchesJson);
|
|
316
|
+
progress.update('Done!', { percent: 100 });
|
|
317
|
+
|
|
318
|
+
// Short delay to let the user see 100%
|
|
319
|
+
await new Promise(r => setTimeout(r, 100));
|
|
320
|
+
return matches;
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
if (matches) {
|
|
324
|
+
session.showClones(absolutePath, matches);
|
|
325
|
+
}
|
|
326
|
+
} catch (e) {
|
|
327
|
+
console.log(chalk.red(`Error: ${e.message}`));
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
270
332
|
/**
|
|
271
333
|
* @command fix
|
|
272
334
|
* Apply a specific fix to a file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
|
+
export interface CloneMatch {
|
|
7
|
+
start1: number
|
|
8
|
+
end1: number
|
|
9
|
+
start2: number
|
|
10
|
+
end2: number
|
|
11
|
+
lineCount: number
|
|
12
|
+
nodeCount: number
|
|
13
|
+
complexityScore: number
|
|
14
|
+
algo: string
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Analyzes a JavaScript/TypeScript file and returns its structural outline as a JSON string.
|
|
8
18
|
* Contains information about classes and methods found in the file.
|
|
@@ -46,3 +56,8 @@ export declare function applyEditsAtomic(operationsJson: string): string
|
|
|
46
56
|
export declare function generateTest(filePath: string, methodName: string, className: string | undefined | null, testType: string | undefined | null, configJson: string, callback: (...args: any[]) => any): string
|
|
47
57
|
/** Analyzes a method and generates a symbolic execution trace. */
|
|
48
58
|
export declare function getSymbolicTrace(filePath: string, methodName: string, className?: string | undefined | null): string
|
|
59
|
+
/**
|
|
60
|
+
* Analyzes a file for code clones (duplicates).
|
|
61
|
+
* Returns a JSON string containing the matches found.
|
|
62
|
+
*/
|
|
63
|
+
export declare function analyzeClones(projectRoot: string, filePath: string, level: string): string
|
|
Binary file
|
|
Binary file
|
package/index.js
CHANGED
|
@@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|
|
310
310
|
throw new Error(`Failed to load native binding`)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const { getFileOutline, getProjectMetadata, findCallSites, ping, submitJob, getUserStats, runAudit, applyEdit, applyEditsAtomic, generateTest, getSymbolicTrace } = nativeBinding
|
|
313
|
+
const { getFileOutline, getProjectMetadata, findCallSites, ping, submitJob, getUserStats, runAudit, applyEdit, applyEditsAtomic, generateTest, getSymbolicTrace, analyzeClones } = nativeBinding
|
|
314
314
|
|
|
315
315
|
module.exports.getFileOutline = getFileOutline
|
|
316
316
|
module.exports.getProjectMetadata = getProjectMetadata
|
|
@@ -323,3 +323,4 @@ module.exports.applyEdit = applyEdit
|
|
|
323
323
|
module.exports.applyEditsAtomic = applyEditsAtomic
|
|
324
324
|
module.exports.generateTest = generateTest
|
|
325
325
|
module.exports.getSymbolicTrace = getSymbolicTrace
|
|
326
|
+
module.exports.analyzeClones = analyzeClones
|
|
Binary file
|
|
Binary file
|
package/lib/generateTestsUi.js
CHANGED
|
@@ -40,6 +40,9 @@ class GenerateTestsUI {
|
|
|
40
40
|
} else if (choice === 'trace') {
|
|
41
41
|
const result = await this._showFileSelection(false, false, true);
|
|
42
42
|
if (result === 'exit') return 'exit';
|
|
43
|
+
} else if (choice === 'clones') {
|
|
44
|
+
const result = await this._showClonesSelection();
|
|
45
|
+
if (result === 'exit') return 'exit';
|
|
43
46
|
} else if (choice === 'stats') {
|
|
44
47
|
await this._showStats();
|
|
45
48
|
} else if (choice === 'about') {
|
|
@@ -708,6 +711,62 @@ class GenerateTestsUI {
|
|
|
708
711
|
console.error(chalk.red(`Trace error: ${e.message}`));
|
|
709
712
|
}
|
|
710
713
|
}
|
|
714
|
+
|
|
715
|
+
async _showClonesSelection() {
|
|
716
|
+
const files = await this._getUserFiles();
|
|
717
|
+
|
|
718
|
+
if (files.length === 0) {
|
|
719
|
+
console.log(chalk.yellow('\nNo JavaScript or TypeScript files found in your project.\n'));
|
|
720
|
+
return 'back';
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const cwd = process.cwd();
|
|
724
|
+
const items = files.map(file => ({
|
|
725
|
+
name: path.relative(cwd, file),
|
|
726
|
+
path: path.dirname(file)
|
|
727
|
+
}));
|
|
728
|
+
|
|
729
|
+
const selectedName = this.goUiSession.showListView('Select File for Duplicate Detection', items);
|
|
730
|
+
|
|
731
|
+
if (selectedName === 'back') return 'back';
|
|
732
|
+
if (!selectedName || selectedName === 'exit') return 'exit';
|
|
733
|
+
|
|
734
|
+
const selectedFile = path.resolve(cwd, selectedName);
|
|
735
|
+
|
|
736
|
+
// Show level selection
|
|
737
|
+
const level = this.goUiSession.showCloneMenu();
|
|
738
|
+
if (level === 'back') return this._showClonesSelection();
|
|
739
|
+
|
|
740
|
+
await this._runClones(selectedFile, level);
|
|
741
|
+
return 'main_menu';
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
async _runClones(filePath, level) {
|
|
745
|
+
const { analyzeClones } = require('../index');
|
|
746
|
+
const absolutePath = path.resolve(filePath);
|
|
747
|
+
const projectRoot = process.cwd();
|
|
748
|
+
|
|
749
|
+
console.log(chalk.blue(`🔍 Analyzing clones in ${path.relative(projectRoot, filePath)}...`));
|
|
750
|
+
|
|
751
|
+
try {
|
|
752
|
+
const matches = await this.goUiSession.showProgress('Analyzing clones...', async (progress) => {
|
|
753
|
+
progress.update('Scanning structures...', { percent: 50 });
|
|
754
|
+
const matchesJson = analyzeClones(projectRoot, absolutePath, level);
|
|
755
|
+
const matches = JSON.parse(matchesJson);
|
|
756
|
+
progress.update('Done!', { percent: 100 });
|
|
757
|
+
|
|
758
|
+
// Short delay to let the user see 100%
|
|
759
|
+
await new Promise(r => setTimeout(r, 100));
|
|
760
|
+
return matches;
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
if (matches) {
|
|
764
|
+
this.goUiSession.showClones(absolutePath, matches);
|
|
765
|
+
}
|
|
766
|
+
} catch (e) {
|
|
767
|
+
console.log(chalk.red(`Error: ${e.message}`));
|
|
768
|
+
}
|
|
769
|
+
}
|
|
711
770
|
}
|
|
712
771
|
|
|
713
772
|
module.exports = GenerateTestsUI;
|
package/lib/goUiSession.js
CHANGED
|
@@ -78,6 +78,30 @@ class GoUISession {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
showCloneMenu() {
|
|
82
|
+
const outputFile = this._trackTempFile(this._createTempFile('clone-menu-output', '.txt'));
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const result = spawnSync(this._binaryPath, ['clone-menu', '--output', outputFile], {
|
|
86
|
+
stdio: 'inherit'
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (result.error) throw result.error;
|
|
90
|
+
|
|
91
|
+
if (!fs.existsSync(outputFile) || fs.statSync(outputFile).size === 0) {
|
|
92
|
+
return 'back';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const choice = fs.readFileSync(outputFile, 'utf8').trim();
|
|
96
|
+
return choice || 'back';
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error('Clone Menu error:', error.message);
|
|
99
|
+
return 'back';
|
|
100
|
+
} finally {
|
|
101
|
+
this._cleanupTempFile(outputFile);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
81
105
|
showListView(title, items) {
|
|
82
106
|
const dataJson = JSON.stringify({ title, items });
|
|
83
107
|
const outputFile = this._trackTempFile(this._createTempFile('list-view-output', '.txt'));
|
|
@@ -148,6 +172,22 @@ class GoUISession {
|
|
|
148
172
|
}
|
|
149
173
|
}
|
|
150
174
|
|
|
175
|
+
showSystemStatus(statusData) {
|
|
176
|
+
const dataJson = JSON.stringify(statusData);
|
|
177
|
+
const inputFile = this._trackTempFile(this._createTempFile('system-status', '.json'));
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
fs.writeFileSync(inputFile, dataJson);
|
|
181
|
+
spawnSync(this._binaryPath, ['system-status', '--file', inputFile], {
|
|
182
|
+
stdio: 'inherit'
|
|
183
|
+
});
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error('System status error:', error.message);
|
|
186
|
+
} finally {
|
|
187
|
+
this._cleanupTempFile(inputFile);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
151
191
|
showAbout() {
|
|
152
192
|
try {
|
|
153
193
|
spawnSync(this._binaryPath, ['about'], {
|
|
@@ -311,6 +351,24 @@ class GoUISession {
|
|
|
311
351
|
}
|
|
312
352
|
}
|
|
313
353
|
|
|
354
|
+
async showClones(filePath, results) {
|
|
355
|
+
const dataJson = JSON.stringify({ file_path: filePath, matches: results });
|
|
356
|
+
const inputFile = this._trackTempFile(this._createTempFile('clone-data', '.json'));
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
fs.writeFileSync(inputFile, dataJson);
|
|
360
|
+
|
|
361
|
+
spawnSync(this._binaryPath, ['clones', '--file', inputFile], {
|
|
362
|
+
stdio: 'inherit',
|
|
363
|
+
env: process.env
|
|
364
|
+
});
|
|
365
|
+
} catch (error) {
|
|
366
|
+
console.error('Clone results display error:', error.message);
|
|
367
|
+
} finally {
|
|
368
|
+
this._cleanupTempFile(inputFile);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
314
372
|
async showStreamingAuditResults(methodName, className, sourceCode) {
|
|
315
373
|
const outputFile = this._trackTempFile(this._createTempFile('audit-choice', '.txt'));
|
|
316
374
|
|
|
@@ -374,6 +432,27 @@ class GoUISession {
|
|
|
374
432
|
}
|
|
375
433
|
}
|
|
376
434
|
|
|
435
|
+
showAuthError(message = 'Authentication failed') {
|
|
436
|
+
try {
|
|
437
|
+
spawnSync(this._binaryPath, ['auth-error', '--message', message], {
|
|
438
|
+
stdio: 'inherit'
|
|
439
|
+
});
|
|
440
|
+
} catch (error) {
|
|
441
|
+
console.error('Auth error:', error.message);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
showConfigError(missing = []) {
|
|
446
|
+
try {
|
|
447
|
+
const dataJson = JSON.stringify({ missing });
|
|
448
|
+
spawnSync(this._binaryPath, ['config-error', '--data', dataJson], {
|
|
449
|
+
stdio: 'inherit'
|
|
450
|
+
});
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error('Config error:', error.message);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
377
456
|
_findGoUiBinary() {
|
|
378
457
|
const platform = process.platform;
|
|
379
458
|
const arch = process.arch;
|
|
@@ -397,8 +476,20 @@ class GoUISession {
|
|
|
397
476
|
|
|
398
477
|
_createTempFile(prefix, suffix) {
|
|
399
478
|
const tmpDir = os.tmpdir();
|
|
400
|
-
const
|
|
401
|
-
|
|
479
|
+
const fileName = `tng-${prefix}-${Date.now()}-${Math.floor(Math.random() * 1000)}${suffix}`;
|
|
480
|
+
let filePath = path.join(tmpDir, fileName);
|
|
481
|
+
|
|
482
|
+
try {
|
|
483
|
+
fs.writeFileSync(filePath, '');
|
|
484
|
+
} catch (e) {
|
|
485
|
+
// Fallback to local directory if system tmp is not writable
|
|
486
|
+
const localTmp = path.join(process.cwd(), '.tng-tmp');
|
|
487
|
+
if (!fs.existsSync(localTmp)) {
|
|
488
|
+
fs.mkdirSync(localTmp, { recursive: true });
|
|
489
|
+
}
|
|
490
|
+
filePath = path.join(localTmp, fileName);
|
|
491
|
+
fs.writeFileSync(filePath, '');
|
|
492
|
+
}
|
|
402
493
|
return filePath;
|
|
403
494
|
}
|
|
404
495
|
|
package/lib/jsonSession.js
CHANGED
|
@@ -66,6 +66,10 @@ class JsonSession {
|
|
|
66
66
|
this.emitEvent('result', auditResult || {});
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
showClones(filePath, results) {
|
|
70
|
+
this.emitEvent('clones', { file_path: filePath, matches: results });
|
|
71
|
+
}
|
|
72
|
+
|
|
69
73
|
showTestResults(title, passed, failed, errors, total, results = []) {
|
|
70
74
|
this.emitEvent('test_results', {
|
|
71
75
|
title: title || 'Test Results',
|
|
@@ -105,7 +109,7 @@ class JsonSession {
|
|
|
105
109
|
|
|
106
110
|
emitEvent(type, data = {}) {
|
|
107
111
|
try {
|
|
108
|
-
const event = { type, ...data
|
|
112
|
+
const event = { type, ...data };
|
|
109
113
|
console.log(JSON.stringify(event));
|
|
110
114
|
} catch (e) {
|
|
111
115
|
// If serialization fails (e.g. circular ref), emit minimal error
|
package/out.log
ADDED
|
File without changes
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|