@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.
@@ -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
- ## Quick Start
13
+ ## Usage
12
14
 
13
15
  ### 1. Initialize
14
- Run the init command to create a `tng.config.js` configuration file in your project:
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 -p @tng-sh/js tng init
19
+ npx tng init
18
20
  ```
19
21
 
20
- ### 2. Generate Tests
21
- Generate a test for a specific function in a file:
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 -p @tng-sh/js tng -f path/to/file.js -m functionName -t react_component
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
- - `react_component`
29
- - `express_handler`
30
- - `graphql_resolver`
31
- - `nest_js` (Service/Controller)
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 -p @tng-sh/js tng i
59
+ npx tng -f path/to/file.js --clones --json
42
60
  ```
43
61
 
44
62
  ## Features
45
63
 
46
- - ⚡️ **Fast AST Analysis**: Powered by Rust and the `oxc` parser for blazing fast static analysis.
47
- - 🖥️ **Interactive UI**: Rich Go-based terminal UI for managing test generation.
48
- - 🎨 **Prettier Integration**: Automatically formats generated tests to match your project style.
49
- - 📘 **TypeScript Support**: First-class support for TypeScript analysis.
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
- The `tng.config.js` file allows you to customize the behavior:
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
- // Framework Detection
62
- // Options: express, nextjs, nestjs, generic
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 audit generation for JavaScript')
31
- .version('0.1.3');
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
- console.log(chalk.yellow('Specify a method with -m, use --outline to see methods, or run "tng i" for full selection.'));
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
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
@@ -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;
@@ -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 filePath = path.join(tmpDir, `tng-${prefix}-${Date.now()}-${Math.floor(Math.random() * 1000)}${suffix}`);
401
- fs.writeFileSync(filePath, '');
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
 
@@ -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, timestamp: Date.now() };
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tng-sh/js",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "TNG JavaScript CLI",
5
5
  "repository": {
6
6
  "type": "git",
Binary file
Binary file