@tng-sh/js 0.1.2 → 0.1.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/bin/tng.js CHANGED
@@ -28,7 +28,7 @@ process.on('uncaughtException', (err) => {
28
28
  program
29
29
  .name('tng')
30
30
  .description('TNG - Automated Test Generation, and audit generation for JavaScript')
31
- .version('0.1.2');
31
+ .version('0.1.3');
32
32
 
33
33
  /**
34
34
  * @command init
@@ -210,10 +210,51 @@ program
210
210
  .option('-f, --file <path>', 'JavaScript file path')
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
+ .option('--trace', 'Run symbolic trace visualization')
213
214
  .option('--json', 'Output results as JSON events (machine-readable)')
214
215
 
215
216
  .action(async (options) => {
216
217
  if (options.method && options.file) {
218
+ if (options.trace) {
219
+ const { getSymbolicTrace } = require('../index');
220
+ const GoUISession = require('../lib/goUiSession');
221
+
222
+ try {
223
+ const traceJson = getSymbolicTrace(
224
+ path.resolve(options.file),
225
+ options.method,
226
+ null
227
+ );
228
+
229
+ if (options.json) {
230
+ console.log(traceJson);
231
+ return;
232
+ }
233
+
234
+ // Create temp file for the trace
235
+ const tmpDir = require('os').tmpdir();
236
+ const tmpFile = path.join(tmpDir, `trace-${Date.now()}.json`);
237
+ fs.writeFileSync(tmpFile, traceJson);
238
+
239
+ // Launch Go UI
240
+ const session = new GoUISession();
241
+ const binaryPath = session._binaryPath;
242
+
243
+ const { spawnSync } = require('child_process');
244
+ spawnSync(binaryPath, ['trace-results', '--file', tmpFile], {
245
+ stdio: 'inherit'
246
+ });
247
+
248
+ // Cleanup
249
+ try { fs.unlinkSync(tmpFile); } catch (e) { }
250
+
251
+ } catch (e) {
252
+ console.log(chalk.red(`Trace failed: ${e.message}`));
253
+ process.exit(1);
254
+ }
255
+ return;
256
+ }
257
+
217
258
  if (!options.type && !options.audit) {
218
259
  console.log(chalk.red('Error: --type <type> is required.'));
219
260
  process.exit(1);
Binary file
Binary file
Binary file
Binary file
package/index.d.ts CHANGED
@@ -44,3 +44,5 @@ export declare function applyEditsAtomic(operationsJson: string): string
44
44
  * Analyzes code, submits a job, and polls for the final generated test.
45
45
  */
46
46
  export declare function generateTest(filePath: string, methodName: string, className: string | undefined | null, testType: string | undefined | null, configJson: string, callback: (...args: any[]) => any): string
47
+ /** Analyzes a method and generates a symbolic execution trace. */
48
+ export declare function getSymbolicTrace(filePath: string, methodName: string, className?: string | undefined | null): string
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 } = nativeBinding
313
+ const { getFileOutline, getProjectMetadata, findCallSites, ping, submitJob, getUserStats, runAudit, applyEdit, applyEditsAtomic, generateTest, getSymbolicTrace } = nativeBinding
314
314
 
315
315
  module.exports.getFileOutline = getFileOutline
316
316
  module.exports.getProjectMetadata = getProjectMetadata
@@ -322,3 +322,4 @@ module.exports.runAudit = runAudit
322
322
  module.exports.applyEdit = applyEdit
323
323
  module.exports.applyEditsAtomic = applyEditsAtomic
324
324
  module.exports.generateTest = generateTest
325
+ module.exports.getSymbolicTrace = getSymbolicTrace
@@ -37,6 +37,9 @@ class GenerateTestsUI {
37
37
  } else if (choice === 'xray') {
38
38
  const result = await this._showFileSelection(false, true);
39
39
  if (result === 'exit') return 'exit';
40
+ } else if (choice === 'trace') {
41
+ const result = await this._showFileSelection(false, false, true);
42
+ if (result === 'exit') return 'exit';
40
43
  } else if (choice === 'stats') {
41
44
  await this._showStats();
42
45
  } else if (choice === 'about') {
@@ -98,7 +101,7 @@ class GenerateTestsUI {
98
101
  });
99
102
  }
100
103
 
101
- async _showFileSelection(isAudit = false, isXray = false) {
104
+ async _showFileSelection(isAudit = false, isXray = false, isTrace = false) {
102
105
  const files = await this._getUserFiles();
103
106
 
104
107
  if (files.length === 0) {
@@ -112,19 +115,23 @@ class GenerateTestsUI {
112
115
  path: path.dirname(file)
113
116
  }));
114
117
 
115
- const title = isXray ? 'Select File for X-Ray' : (isAudit ? 'Select JavaScript File to Audit' : 'Select JavaScript File');
118
+ let title = 'Select JavaScript File';
119
+ if (isAudit) title = 'Select JavaScript File to Audit';
120
+ else if (isXray) title = 'Select File for X-Ray';
121
+ else if (isTrace) title = 'Select File for Symbolic Trace';
122
+
116
123
  const selectedName = this.goUiSession.showListView(title, items);
117
124
 
118
125
  if (selectedName === 'back') return 'back';
119
126
  if (!selectedName || selectedName === 'exit') return 'exit';
120
127
 
121
128
  const selectedFile = path.resolve(cwd, selectedName);
122
- const result = await this._showMethodsForFile(selectedFile, isAudit, isXray);
129
+ const result = await this._showMethodsForFile(selectedFile, isAudit, isXray, isTrace);
123
130
  if (result === 'main_menu') return 'main_menu';
124
131
  return result;
125
132
  }
126
133
 
127
- async _showMethodsForFile(filePath, isAudit = false, isXray = false) {
134
+ async _showMethodsForFile(filePath, isAudit = false, isXray = false, isTrace = false) {
128
135
  let outline;
129
136
  try {
130
137
  const result = getFileOutline(filePath);
@@ -147,14 +154,23 @@ class GenerateTestsUI {
147
154
  methodData: m
148
155
  }));
149
156
 
150
- const title = isXray ? `Select Method to X-Ray for ${fileName}` : (isAudit ? `Select Method to Audit for ${fileName}` : `Select Method for ${fileName}`);
157
+ let title = 'Select Method';
158
+ if (isAudit) title = `Select Method to Audit for ${fileName}`;
159
+ else if (isXray) title = `Select Method to X-Ray for ${fileName}`;
160
+ else if (isTrace) title = `Select Method to Trace for ${fileName}`;
161
+
151
162
  const selectedDisplay = this.goUiSession.showListView(title, items);
152
163
 
153
- if (selectedDisplay === 'back' || !selectedDisplay) return this._showFileSelection(isAudit, isXray);
164
+ if (selectedDisplay === 'back' || !selectedDisplay) return this._showFileSelection(isAudit, isXray, isTrace);
154
165
 
155
166
  const selectedMethod = items.find(i => i.name === selectedDisplay)?.methodData;
156
167
 
157
168
  if (selectedMethod) {
169
+ if (isTrace) {
170
+ await this._launchTrace(filePath, selectedMethod.name);
171
+ return this._showFileSelection(isAudit, isXray, isTrace);
172
+ }
173
+
158
174
  if (isXray) {
159
175
  const choice = await this._generateTestsForMethod(filePath, selectedMethod, 'visualize', false, true);
160
176
  if (choice === 'main_menu') return 'main_menu';
@@ -657,6 +673,41 @@ class GenerateTestsUI {
657
673
  return [];
658
674
  }
659
675
  }
676
+
677
+ async _launchTrace(filePath, methodName) {
678
+ const { getSymbolicTrace } = require('../index');
679
+ const fs = require('fs');
680
+ const path = require('path');
681
+ const chalk = require('chalk');
682
+
683
+ try {
684
+ // 1. Generate Trace (with Spinner)
685
+ const result = this.goUiSession.showSpinner(`Tracing ${methodName}...`, () => {
686
+ try {
687
+ const traceJson = getSymbolicTrace(filePath, methodName, null);
688
+ const tmpDir = require('os').tmpdir();
689
+ const f = path.join(tmpDir, `trace-${Date.now()}.json`);
690
+ fs.writeFileSync(f, traceJson);
691
+ return { success: true, file: f };
692
+ } catch (e) {
693
+ return { success: false, message: e.message };
694
+ }
695
+ });
696
+
697
+ if (result && result.success && result.file) {
698
+ // 2. Show Trace UI
699
+ const { spawnSync } = require('child_process');
700
+ spawnSync(this.goUiSession._binaryPath, ['trace-results', '--file', result.file], {
701
+ stdio: 'inherit'
702
+ });
703
+
704
+ try { fs.unlinkSync(result.file); } catch (e) { }
705
+ }
706
+
707
+ } catch (e) {
708
+ console.error(chalk.red(`Trace error: ${e.message}`));
709
+ }
710
+ }
660
711
  }
661
712
 
662
713
  module.exports = GenerateTestsUI;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tng-sh/js",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "TNG JavaScript CLI",
5
5
  "repository": {
6
6
  "type": "git",
Binary file
Binary file
Binary file
Binary file