@rayburst/cli 0.1.17 → 0.2.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.
Files changed (39) hide show
  1. package/README.md +165 -257
  2. package/dist/analysis/analyze-project.d.ts +9 -0
  3. package/dist/analysis/analyze-project.js +440 -0
  4. package/dist/git-utils.d.ts +33 -0
  5. package/dist/git-utils.js +96 -0
  6. package/dist/incremental-analyzer.d.ts +128 -0
  7. package/dist/incremental-analyzer.js +259 -0
  8. package/dist/index.d.ts +6 -0
  9. package/dist/index.js +6 -0
  10. package/dist/local-storage.d.ts +39 -0
  11. package/dist/local-storage.js +117 -0
  12. package/dist/registry.d.ts +89 -0
  13. package/dist/registry.js +287 -0
  14. package/dist/vite-plugin.d.ts +7 -0
  15. package/dist/vite-plugin.js +109 -0
  16. package/package.json +33 -30
  17. package/bin/rayburst.js +0 -232
  18. package/dist/assets/_commonjsHelpers-B85MJLTf.js +0 -5
  19. package/dist/assets/hostInit-BWYxHpMp.js +0 -9
  20. package/dist/assets/index-9R1akZrm.js +0 -578
  21. package/dist/assets/index-BW-RulSg.js +0 -258
  22. package/dist/assets/index-VnAMn3JB.js +0 -16587
  23. package/dist/assets/preload-helper-Dea3Szod.js +0 -54
  24. package/dist/assets/rayburstCli__loadRemote__rayburstApp_mf_1_App__loadRemote__-CHUYMhiU.js +0 -35
  25. package/dist/assets/rayburstCli__loadShare__react__loadShare__-CE7VtFm0.js +0 -19
  26. package/dist/assets/rayburstCli__mf_v__runtimeInit__mf_v__-C_SVfzik.js +0 -4173
  27. package/dist/assets/remoteEntry-B8biLITo.js +0 -122
  28. package/dist/assets/virtualExposes-DwA08f_D.js +0 -5
  29. package/dist/index.html +0 -56
  30. package/index.html +0 -54
  31. package/scripts/analyze-project.js +0 -475
  32. package/server.js +0 -188
  33. package/src/file-watcher.js +0 -174
  34. package/src/git-utils.js +0 -105
  35. package/src/incremental-analyzer.js +0 -295
  36. package/src/main.tsx +0 -126
  37. package/src/registry.js +0 -262
  38. package/vite-plugin-api.js +0 -123
  39. package/vite.config.ts +0 -73
@@ -0,0 +1,440 @@
1
+ import { Project, SyntaxKind, Node } from 'ts-morph';
2
+ import { execSync } from 'child_process';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import crypto from 'crypto';
6
+ /**
7
+ * Analyze a TypeScript/React project and generate nodes/edges data
8
+ * @param {string} projectPath - Absolute path to project root
9
+ * @param {string} projectId - Project ID
10
+ * @param {function} onLog - Callback for logging
11
+ * @returns {Promise<AnalysisResult>} Analysis data with nodes and edges
12
+ */
13
+ export async function analyzeProject(projectPath, projectId, onLog) {
14
+ const logs = [];
15
+ const log = (message) => {
16
+ console.log(message);
17
+ logs.push(message);
18
+ // Call the callback if provided (for real-time streaming)
19
+ if (onLog) {
20
+ onLog(message);
21
+ }
22
+ };
23
+ log(`Analyzing project at: ${projectPath}`);
24
+ const gitHash = getGitHash(projectPath);
25
+ const gitInfo = getGitInfo(projectPath);
26
+ log(` Git hash: ${gitHash}`);
27
+ log(` Current branch: ${gitInfo.branch}`);
28
+ // Find tsconfig.json
29
+ const tsconfigPath = path.join(projectPath, 'tsconfig.json');
30
+ if (!fs.existsSync(tsconfigPath)) {
31
+ log(` Warning: No tsconfig.json found, skipping TypeScript analysis`);
32
+ const emptyAnalysis = createEmptyAnalysis(projectPath, gitInfo);
33
+ emptyAnalysis.logs = logs;
34
+ return emptyAnalysis;
35
+ }
36
+ try {
37
+ // Initialize ts-morph project
38
+ const project = new Project({
39
+ tsConfigFilePath: tsconfigPath,
40
+ });
41
+ const nodes = [];
42
+ const edges = [];
43
+ const nodeMap = new Map();
44
+ const usedIds = new Set();
45
+ const idCounters = new Map();
46
+ // Track file positions for layout
47
+ let fileIndex = 0;
48
+ const filePositions = new Map();
49
+ // Get all source files (excluding node_modules, test files)
50
+ const sourceFiles = project.getSourceFiles()
51
+ .filter(sf => {
52
+ const filePath = sf.getFilePath();
53
+ return !filePath.includes('node_modules') &&
54
+ !filePath.includes('.test.') &&
55
+ !filePath.includes('.spec.') &&
56
+ (filePath.endsWith('.ts') || filePath.endsWith('.tsx'));
57
+ });
58
+ log(` Analyzing ${sourceFiles.length} source files...`);
59
+ // Analyze each source file
60
+ for (let i = 0; i < sourceFiles.length; i++) {
61
+ const sourceFile = sourceFiles[i];
62
+ const filePath = sourceFile.getFilePath();
63
+ const relativePath = filePath.replace(projectPath + '/', '');
64
+ if (!filePositions.has(relativePath)) {
65
+ filePositions.set(relativePath, fileIndex++);
66
+ }
67
+ const baseX = (filePositions.get(relativePath) || 0) * 400;
68
+ let nodeY = 100;
69
+ // Extract React components
70
+ const components = extractComponents(sourceFile, relativePath, gitHash, baseX, nodeY, nodes, nodeMap, usedIds, idCounters);
71
+ nodeY += components * 150;
72
+ // Extract functions
73
+ const functions = extractFunctions(sourceFile, relativePath, gitHash, baseX, nodeY, nodes, nodeMap, usedIds, idCounters);
74
+ nodeY += functions * 150;
75
+ // Extract state declarations
76
+ const states = extractState(sourceFile, relativePath, gitHash, baseX, nodeY, nodes, nodeMap, usedIds, idCounters);
77
+ nodeY += states * 150;
78
+ // Yield to event loop every 10 files to allow SSE streaming
79
+ if (i % 10 === 0 && i > 0) {
80
+ await new Promise(resolve => setImmediate(resolve));
81
+ }
82
+ }
83
+ log(` Extracted ${nodes.length} nodes`);
84
+ // Generate edges by analyzing relationships
85
+ for (const sourceFile of sourceFiles) {
86
+ generateEdges(sourceFile, nodeMap, edges);
87
+ }
88
+ log(` Generated ${edges.length} edges`);
89
+ // Read package.json for project metadata
90
+ const packageJsonPath = path.join(projectPath, 'package.json');
91
+ let packageJson = { name: path.basename(projectPath) };
92
+ if (fs.existsSync(packageJsonPath)) {
93
+ packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
94
+ }
95
+ const branchId = gitInfo.branch.replace(/[^a-zA-Z0-9]/g, '-');
96
+ const projectMetadata = {
97
+ id: projectId || crypto.createHash('md5').update(projectPath).digest('hex').substring(0, 12),
98
+ title: packageJson.name || path.basename(projectPath),
99
+ subtitle: new Date().toLocaleDateString(),
100
+ value: `${nodes.length} nodes`,
101
+ trend: 0,
102
+ chartData: [30, 40, 35, 50, 45, 60, 55, 70],
103
+ chartColor: '#22c55e',
104
+ };
105
+ const branchMetadata = {
106
+ id: branchId,
107
+ name: gitInfo.branch,
108
+ lastCommit: gitInfo.lastCommit,
109
+ lastCommitDate: 'just now',
110
+ author: gitInfo.author,
111
+ status: 'active',
112
+ commitCount: gitInfo.commitCount,
113
+ pullRequests: 0,
114
+ };
115
+ const planData = {
116
+ nodes,
117
+ edges,
118
+ };
119
+ const result = {
120
+ project: projectMetadata,
121
+ branches: [branchMetadata],
122
+ planData: {
123
+ [branchId]: planData
124
+ },
125
+ analyzedAt: new Date().toISOString(),
126
+ logs,
127
+ };
128
+ return result;
129
+ }
130
+ catch (error) {
131
+ console.error(` Error analyzing project:`, error.message);
132
+ log(` Error: ${error.message}`);
133
+ const emptyAnalysis = createEmptyAnalysis(projectPath, gitInfo);
134
+ emptyAnalysis.logs = logs;
135
+ return emptyAnalysis;
136
+ }
137
+ }
138
+ function createEmptyAnalysis(projectPath, gitInfo) {
139
+ const packageJsonPath = path.join(projectPath, 'package.json');
140
+ let packageJson = { name: path.basename(projectPath) };
141
+ if (fs.existsSync(packageJsonPath)) {
142
+ packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
143
+ }
144
+ const branchId = gitInfo.branch.replace(/[^a-zA-Z0-9]/g, '-');
145
+ return {
146
+ project: {
147
+ id: crypto.createHash('md5').update(projectPath).digest('hex').substring(0, 12),
148
+ title: packageJson.name || path.basename(projectPath),
149
+ subtitle: new Date().toLocaleDateString(),
150
+ value: '0 nodes',
151
+ trend: 0,
152
+ chartData: [],
153
+ chartColor: '#6b7280',
154
+ },
155
+ branches: [{
156
+ id: branchId,
157
+ name: gitInfo.branch,
158
+ lastCommit: gitInfo.lastCommit,
159
+ lastCommitDate: 'just now',
160
+ author: gitInfo.author,
161
+ status: 'active',
162
+ commitCount: gitInfo.commitCount,
163
+ pullRequests: 0,
164
+ }],
165
+ planData: {},
166
+ analyzedAt: new Date().toISOString(),
167
+ };
168
+ }
169
+ function getGitHash(projectPath) {
170
+ try {
171
+ return execSync('git rev-parse --short HEAD', { cwd: projectPath, encoding: 'utf-8' }).trim();
172
+ }
173
+ catch {
174
+ return 'nogit';
175
+ }
176
+ }
177
+ function getGitInfo(projectPath) {
178
+ try {
179
+ const branch = execSync('git branch --show-current', { cwd: projectPath, encoding: 'utf-8' }).trim() || 'main';
180
+ const lastCommit = execSync('git log -1 --pretty=%s', { cwd: projectPath, encoding: 'utf-8' }).trim() || 'No commits';
181
+ const author = execSync('git log -1 --pretty=%an', { cwd: projectPath, encoding: 'utf-8' }).trim() || 'Unknown';
182
+ const commitCount = parseInt(execSync('git rev-list --count HEAD', { cwd: projectPath, encoding: 'utf-8' }).trim() || '0');
183
+ return { branch, lastCommit, author, commitCount };
184
+ }
185
+ catch {
186
+ return {
187
+ branch: 'main',
188
+ lastCommit: 'No commits',
189
+ author: 'Unknown',
190
+ commitCount: 0
191
+ };
192
+ }
193
+ }
194
+ function createNodeId(filePath, nodeName, gitHash, counter = 0) {
195
+ const suffix = counter > 0 ? `-${counter}` : '';
196
+ return `${filePath}::${nodeName}${suffix}::${gitHash}`;
197
+ }
198
+ function getUniqueNodeId(filePath, nodeName, gitHash, usedIds, idCounters) {
199
+ const baseKey = `${filePath}::${nodeName}`;
200
+ const counter = idCounters.get(baseKey) || 0;
201
+ const id = createNodeId(filePath, nodeName, gitHash, counter);
202
+ if (usedIds.has(id)) {
203
+ idCounters.set(baseKey, counter + 1);
204
+ return getUniqueNodeId(filePath, nodeName, gitHash, usedIds, idCounters);
205
+ }
206
+ usedIds.add(id);
207
+ idCounters.set(baseKey, counter + 1);
208
+ return id;
209
+ }
210
+ function isReactComponent(func) {
211
+ const body = func.getBody();
212
+ if (!body)
213
+ return false;
214
+ const text = body.getText();
215
+ return text.includes('return') && (text.includes('</') || text.includes('<>') || text.includes('jsx'));
216
+ }
217
+ function extractComponents(sourceFile, relativePath, gitHash, baseX, startY, nodes, nodeMap, usedIds, idCounters) {
218
+ let count = 0;
219
+ let y = startY;
220
+ // Find function components
221
+ const functions = sourceFile.getFunctions();
222
+ for (const func of functions) {
223
+ if (isReactComponent(func)) {
224
+ const name = func.getName() || 'AnonymousComponent';
225
+ const id = getUniqueNodeId(relativePath, name, gitHash, usedIds, idCounters);
226
+ const params = func.getParameters();
227
+ const propsParam = params[0];
228
+ const propsType = propsParam ? propsParam.getType().getText() : 'any';
229
+ const node = {
230
+ id,
231
+ type: 'component',
232
+ position: { x: baseX, y: y },
233
+ data: {
234
+ componentName: name,
235
+ props: propsType,
236
+ label: name,
237
+ description: `Component in ${relativePath}`,
238
+ }
239
+ };
240
+ nodes.push(node);
241
+ nodeMap.set(id, node);
242
+ nodeMap.set(name, node);
243
+ count++;
244
+ y += 150;
245
+ }
246
+ }
247
+ // Find arrow function components
248
+ const variables = sourceFile.getVariableDeclarations();
249
+ for (const variable of variables) {
250
+ const init = variable.getInitializer();
251
+ if (init && (Node.isArrowFunction(init) || Node.isFunctionExpression(init))) {
252
+ if (isReactComponent(init)) {
253
+ const name = variable.getName();
254
+ const id = getUniqueNodeId(relativePath, name, gitHash, usedIds, idCounters);
255
+ const params = init.getParameters();
256
+ const propsParam = params[0];
257
+ const propsType = propsParam ? propsParam.getType().getText() : 'any';
258
+ const node = {
259
+ id,
260
+ type: 'component',
261
+ position: { x: baseX, y: y },
262
+ data: {
263
+ componentName: name,
264
+ props: propsType,
265
+ label: name,
266
+ description: `Component in ${relativePath}`,
267
+ }
268
+ };
269
+ nodes.push(node);
270
+ nodeMap.set(id, node);
271
+ nodeMap.set(name, node);
272
+ count++;
273
+ y += 150;
274
+ }
275
+ }
276
+ }
277
+ return count;
278
+ }
279
+ function extractFunctions(sourceFile, relativePath, gitHash, baseX, startY, nodes, nodeMap, usedIds, idCounters) {
280
+ let count = 0;
281
+ let y = startY;
282
+ const functions = sourceFile.getFunctions();
283
+ for (const func of functions) {
284
+ if (!isReactComponent(func)) {
285
+ const name = func.getName() || 'anonymous';
286
+ if (name === 'anonymous')
287
+ continue;
288
+ const id = getUniqueNodeId(relativePath, name, gitHash, usedIds, idCounters);
289
+ const params = func.getParameters().map((p) => {
290
+ const paramName = p.getName();
291
+ const paramType = p.getType().getText();
292
+ return `${paramName}: ${paramType}`;
293
+ }).join(', ');
294
+ const returnType = func.getReturnType().getText();
295
+ const node = {
296
+ id,
297
+ type: 'function',
298
+ position: { x: baseX, y: y },
299
+ data: {
300
+ functionName: name,
301
+ parameters: params,
302
+ returnType: returnType,
303
+ label: name,
304
+ description: `Function in ${relativePath}`,
305
+ }
306
+ };
307
+ nodes.push(node);
308
+ nodeMap.set(id, node);
309
+ nodeMap.set(name, node);
310
+ count++;
311
+ y += 150;
312
+ }
313
+ }
314
+ return count;
315
+ }
316
+ function extractState(sourceFile, relativePath, gitHash, baseX, startY, nodes, nodeMap, usedIds, idCounters) {
317
+ let count = 0;
318
+ let y = startY;
319
+ sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((callExpr) => {
320
+ const expr = callExpr.getExpression();
321
+ if (expr.getText() === 'useState') {
322
+ const parent = callExpr.getParent();
323
+ if (Node.isVariableDeclaration(parent)) {
324
+ const nameNode = parent.getNameNode();
325
+ if (Node.isArrayBindingPattern(nameNode)) {
326
+ const elements = nameNode.getElements();
327
+ if (elements.length > 0) {
328
+ const stateName = elements[0].getText();
329
+ const id = getUniqueNodeId(relativePath, stateName, gitHash, usedIds, idCounters);
330
+ const args = callExpr.getArguments();
331
+ const initialValue = args.length > 0 ? args[0].getText() : 'undefined';
332
+ const stateType = parent.getType().getText();
333
+ const node = {
334
+ id,
335
+ type: 'state',
336
+ position: { x: baseX, y: y },
337
+ data: {
338
+ stateName: stateName,
339
+ stateType: stateType,
340
+ initialValue: initialValue,
341
+ label: stateName,
342
+ description: `State in ${relativePath}`,
343
+ }
344
+ };
345
+ nodes.push(node);
346
+ nodeMap.set(id, node);
347
+ nodeMap.set(stateName, node);
348
+ count++;
349
+ y += 150;
350
+ }
351
+ }
352
+ }
353
+ }
354
+ });
355
+ return count;
356
+ }
357
+ function generateEdges(sourceFile, nodeMap, edges) {
358
+ // Find JSX elements (component usage)
359
+ sourceFile.getDescendantsOfKind(SyntaxKind.JsxElement).forEach((jsxElement) => {
360
+ const openingElement = jsxElement.getOpeningElement();
361
+ const tagName = openingElement.getTagNameNode().getText();
362
+ const containingFunc = jsxElement.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration) ||
363
+ jsxElement.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
364
+ if (containingFunc) {
365
+ const parentName = containingFunc.getName?.() ||
366
+ containingFunc.getParent()?.asKind(SyntaxKind.VariableDeclaration)?.getName() ||
367
+ null;
368
+ if (parentName && nodeMap.has(parentName) && nodeMap.has(tagName)) {
369
+ const sourceNode = nodeMap.get(parentName);
370
+ const targetNode = nodeMap.get(tagName);
371
+ if (sourceNode && targetNode) {
372
+ const edgeId = `e-${sourceNode.id}-${targetNode.id}`;
373
+ if (!edges.find(e => e.id === edgeId)) {
374
+ edges.push({
375
+ id: edgeId,
376
+ source: sourceNode.id,
377
+ target: targetNode.id,
378
+ type: 'floating',
379
+ });
380
+ }
381
+ }
382
+ }
383
+ }
384
+ });
385
+ // Find self-closing JSX elements
386
+ sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement).forEach((jsxElement) => {
387
+ const tagName = jsxElement.getTagNameNode().getText();
388
+ const containingFunc = jsxElement.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration) ||
389
+ jsxElement.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
390
+ if (containingFunc) {
391
+ const parentName = containingFunc.getName?.() ||
392
+ containingFunc.getParent()?.asKind(SyntaxKind.VariableDeclaration)?.getName() ||
393
+ null;
394
+ if (parentName && nodeMap.has(parentName) && nodeMap.has(tagName)) {
395
+ const sourceNode = nodeMap.get(parentName);
396
+ const targetNode = nodeMap.get(tagName);
397
+ if (sourceNode && targetNode) {
398
+ const edgeId = `e-${sourceNode.id}-${targetNode.id}`;
399
+ if (!edges.find(e => e.id === edgeId)) {
400
+ edges.push({
401
+ id: edgeId,
402
+ source: sourceNode.id,
403
+ target: targetNode.id,
404
+ type: 'floating',
405
+ });
406
+ }
407
+ }
408
+ }
409
+ }
410
+ });
411
+ // Find function calls
412
+ sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((callExpr) => {
413
+ const expr = callExpr.getExpression();
414
+ const calledName = expr.getText().split('.').pop();
415
+ if (calledName && nodeMap.has(calledName)) {
416
+ const containingFunc = callExpr.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration) ||
417
+ callExpr.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
418
+ if (containingFunc) {
419
+ const parentName = containingFunc.getName?.() ||
420
+ containingFunc.getParent()?.asKind(SyntaxKind.VariableDeclaration)?.getName() ||
421
+ null;
422
+ if (parentName && nodeMap.has(parentName)) {
423
+ const sourceNode = nodeMap.get(parentName);
424
+ const targetNode = nodeMap.get(calledName);
425
+ if (sourceNode && targetNode && sourceNode.id !== targetNode.id) {
426
+ const edgeId = `e-${sourceNode.id}-${targetNode.id}`;
427
+ if (!edges.find(e => e.id === edgeId)) {
428
+ edges.push({
429
+ id: edgeId,
430
+ source: sourceNode.id,
431
+ target: targetNode.id,
432
+ type: 'floating',
433
+ });
434
+ }
435
+ }
436
+ }
437
+ }
438
+ }
439
+ });
440
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Git Utilities
3
+ *
4
+ * Helper functions to interact with git repositories
5
+ */
6
+ /**
7
+ * Get list of uncommitted changed files in a git repository
8
+ *
9
+ * @param {string} projectPath - Absolute path to project directory
10
+ * @returns {string[]} - Array of absolute file paths that have uncommitted changes
11
+ */
12
+ export declare function getUncommittedChanges(projectPath: any): string[];
13
+ /**
14
+ * Check if a directory is a git repository
15
+ *
16
+ * @param {string} projectPath - Absolute path to project directory
17
+ * @returns {boolean}
18
+ */
19
+ export declare function isGitRepository(projectPath: any): boolean;
20
+ /**
21
+ * Get list of untracked files in a git repository
22
+ *
23
+ * @param {string} projectPath - Absolute path to project directory
24
+ * @returns {string[]} - Array of absolute file paths that are untracked
25
+ */
26
+ export declare function getUntrackedFiles(projectPath: any): string[];
27
+ /**
28
+ * Get all files with changes (committed or not)
29
+ *
30
+ * @param {string} projectPath - Absolute path to project directory
31
+ * @returns {string[]} - Array of absolute file paths
32
+ */
33
+ export declare function getAllChangedFiles(projectPath: any): string[];
@@ -0,0 +1,96 @@
1
+ import { execSync } from 'child_process';
2
+ import path from 'path';
3
+ /**
4
+ * Git Utilities
5
+ *
6
+ * Helper functions to interact with git repositories
7
+ */
8
+ /**
9
+ * Get list of uncommitted changed files in a git repository
10
+ *
11
+ * @param {string} projectPath - Absolute path to project directory
12
+ * @returns {string[]} - Array of absolute file paths that have uncommitted changes
13
+ */
14
+ export function getUncommittedChanges(projectPath) {
15
+ try {
16
+ // Check if directory is a git repository
17
+ if (!isGitRepository(projectPath)) {
18
+ return [];
19
+ }
20
+ // Get modified, added, and deleted files (staged and unstaged)
21
+ const command = 'git diff --name-only HEAD && git diff --name-only --cached';
22
+ const output = execSync(command, {
23
+ cwd: projectPath,
24
+ encoding: 'utf8',
25
+ stdio: ['pipe', 'pipe', 'ignore'], // Ignore stderr
26
+ });
27
+ // Parse output and convert to absolute paths
28
+ const files = output
29
+ .split('\n')
30
+ .filter(Boolean)
31
+ .map(file => path.resolve(projectPath, file.trim()));
32
+ // Remove duplicates
33
+ return [...new Set(files)];
34
+ }
35
+ catch (error) {
36
+ // If git command fails, return empty array
37
+ console.error('Failed to get git diff:', error.message);
38
+ return [];
39
+ }
40
+ }
41
+ /**
42
+ * Check if a directory is a git repository
43
+ *
44
+ * @param {string} projectPath - Absolute path to project directory
45
+ * @returns {boolean}
46
+ */
47
+ export function isGitRepository(projectPath) {
48
+ try {
49
+ execSync('git rev-parse --is-inside-work-tree', {
50
+ cwd: projectPath,
51
+ encoding: 'utf8',
52
+ stdio: ['pipe', 'pipe', 'ignore'],
53
+ });
54
+ return true;
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
60
+ /**
61
+ * Get list of untracked files in a git repository
62
+ *
63
+ * @param {string} projectPath - Absolute path to project directory
64
+ * @returns {string[]} - Array of absolute file paths that are untracked
65
+ */
66
+ export function getUntrackedFiles(projectPath) {
67
+ try {
68
+ if (!isGitRepository(projectPath)) {
69
+ return [];
70
+ }
71
+ const output = execSync('git ls-files --others --exclude-standard', {
72
+ cwd: projectPath,
73
+ encoding: 'utf8',
74
+ stdio: ['pipe', 'pipe', 'ignore'],
75
+ });
76
+ return output
77
+ .split('\n')
78
+ .filter(Boolean)
79
+ .map(file => path.resolve(projectPath, file.trim()));
80
+ }
81
+ catch (error) {
82
+ console.error('Failed to get untracked files:', error.message);
83
+ return [];
84
+ }
85
+ }
86
+ /**
87
+ * Get all files with changes (committed or not)
88
+ *
89
+ * @param {string} projectPath - Absolute path to project directory
90
+ * @returns {string[]} - Array of absolute file paths
91
+ */
92
+ export function getAllChangedFiles(projectPath) {
93
+ const uncommitted = getUncommittedChanges(projectPath);
94
+ const untracked = getUntrackedFiles(projectPath);
95
+ return [...new Set([...uncommitted, ...untracked])];
96
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Incremental Analyzer
3
+ *
4
+ * Performs incremental analysis by comparing new analysis results with
5
+ * previously stored results and generating a diff of changes.
6
+ */
7
+ /**
8
+ * Perform incremental analysis on a project
9
+ *
10
+ * @param {string} projectId - Project identifier
11
+ * @param {string} projectPath - Absolute path to project
12
+ * @param {string[]} changedFiles - Optional array of changed file paths
13
+ * @returns {Object} - Incremental update with added, removed, and modified nodes/edges
14
+ */
15
+ export declare function performIncrementalAnalysis(projectId: any, projectPath: any, changedFiles?: any[]): {
16
+ type: string;
17
+ analysis: Promise<{
18
+ project: {
19
+ id: string;
20
+ value: string;
21
+ title: string;
22
+ subtitle: string;
23
+ trend: number;
24
+ chartData: number[];
25
+ chartColor: string;
26
+ };
27
+ branches: {
28
+ id: string;
29
+ status: string;
30
+ name: string;
31
+ lastCommit: string;
32
+ lastCommitDate: string;
33
+ author: string;
34
+ commitCount: number;
35
+ pullRequests: number;
36
+ }[];
37
+ planData: Record<string, {
38
+ nodes: {
39
+ id: string;
40
+ type: "function" | "component" | "state" | "api" | "conditional" | "ternary" | "switch" | "try-catch" | "task";
41
+ position: {
42
+ x: number;
43
+ y: number;
44
+ };
45
+ data: {
46
+ label: string;
47
+ description?: string | undefined;
48
+ componentName?: string | undefined;
49
+ props?: string | undefined;
50
+ functionName?: string | undefined;
51
+ parameters?: string | undefined;
52
+ returnType?: string | undefined;
53
+ stateName?: string | undefined;
54
+ stateType?: string | undefined;
55
+ initialValue?: string | undefined;
56
+ method?: string | undefined;
57
+ endpoint?: string | undefined;
58
+ responseType?: string | undefined;
59
+ condition?: string | undefined;
60
+ trueLabel?: string | undefined;
61
+ falseLabel?: string | undefined;
62
+ ternaryCondition?: string | undefined;
63
+ ternaryTrue?: string | undefined;
64
+ ternaryFalse?: string | undefined;
65
+ trueValue?: string | undefined;
66
+ falseValue?: string | undefined;
67
+ switchExpression?: string | undefined;
68
+ cases?: string | undefined;
69
+ tryBlock?: string | undefined;
70
+ catchBlock?: string | undefined;
71
+ };
72
+ }[];
73
+ edges: {
74
+ id: string;
75
+ type: string;
76
+ source: string;
77
+ target: string;
78
+ label?: string | undefined;
79
+ sourceHandle?: string | undefined;
80
+ targetHandle?: string | undefined;
81
+ }[];
82
+ }>;
83
+ analyzedAt: string;
84
+ logs?: string[] | undefined;
85
+ }>;
86
+ update: {
87
+ added: {
88
+ nodes: any[];
89
+ edges: any[];
90
+ };
91
+ removed: {
92
+ nodeIds: any[];
93
+ edgeIds: any[];
94
+ };
95
+ modified: {
96
+ nodes: any[];
97
+ edges: any[];
98
+ };
99
+ };
100
+ };
101
+ /**
102
+ * Generate diff between old and new analysis
103
+ *
104
+ * @param {Object} oldAnalysis - Previous analysis data
105
+ * @param {Object} newAnalysis - New analysis data
106
+ * @returns {Object} - Incremental update object
107
+ */
108
+ export declare function generateDiff(oldAnalysis: any, newAnalysis: any): {
109
+ added: {
110
+ nodes: any[];
111
+ edges: any[];
112
+ };
113
+ removed: {
114
+ nodeIds: any[];
115
+ edgeIds: any[];
116
+ };
117
+ modified: {
118
+ nodes: any[];
119
+ edges: any[];
120
+ };
121
+ };
122
+ /**
123
+ * Check if an update has any changes
124
+ *
125
+ * @param {Object} update - Incremental update object
126
+ * @returns {boolean}
127
+ */
128
+ export declare function hasChanges(update: any): boolean;