@rayburst/cli 0.1.18 → 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.
- package/README.md +165 -257
- package/dist/analysis/analyze-project.d.ts +9 -0
- package/dist/analysis/analyze-project.js +440 -0
- package/dist/git-utils.d.ts +33 -0
- package/dist/git-utils.js +96 -0
- package/dist/incremental-analyzer.d.ts +128 -0
- package/dist/incremental-analyzer.js +259 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/local-storage.d.ts +39 -0
- package/dist/local-storage.js +117 -0
- package/dist/registry.d.ts +89 -0
- package/dist/registry.js +287 -0
- package/dist/vite-plugin.d.ts +7 -0
- package/dist/vite-plugin.js +109 -0
- package/package.json +36 -25
- package/bin/rayburst.js +0 -216
- package/index.html +0 -54
- package/scripts/analyze-project.js +0 -475
- package/server.js +0 -147
- package/src/file-watcher.js +0 -174
- package/src/git-utils.js +0 -105
- package/src/incremental-analyzer.js +0 -295
- package/src/main.tsx +0 -225
- package/src/registry.js +0 -262
- package/vite-plugin-api.js +0 -123
- package/vite.config.ts +0 -72
package/src/file-watcher.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import chokidar from 'chokidar';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* File Watcher for Rayburst Projects
|
|
7
|
-
*
|
|
8
|
-
* Watches registered project directories for file changes and triggers
|
|
9
|
-
* incremental analysis on relevant file modifications.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const watchers = new Map(); // Map<projectId, { watcher, callback }>
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Start watching a project directory for file changes
|
|
16
|
-
*
|
|
17
|
-
* @param {string} projectId - Unique project identifier
|
|
18
|
-
* @param {string} projectPath - Absolute path to project directory
|
|
19
|
-
* @param {Function} onChange - Callback function (changedFiles: string[]) => void
|
|
20
|
-
* @param {Object} options - Watch options
|
|
21
|
-
* @returns {Object} - Watcher instance
|
|
22
|
-
*/
|
|
23
|
-
export function watchProject(projectId, projectPath, onChange, options = {}) {
|
|
24
|
-
// Stop existing watcher if any
|
|
25
|
-
unwatchProject(projectId);
|
|
26
|
-
|
|
27
|
-
const {
|
|
28
|
-
debounceMs = 1500,
|
|
29
|
-
ignorePatterns = [
|
|
30
|
-
'**/node_modules/**',
|
|
31
|
-
'**/.git/**',
|
|
32
|
-
'**/dist/**',
|
|
33
|
-
'**/build/**',
|
|
34
|
-
'**/*.test.ts',
|
|
35
|
-
'**/*.test.tsx',
|
|
36
|
-
'**/*.spec.ts',
|
|
37
|
-
'**/*.spec.tsx',
|
|
38
|
-
],
|
|
39
|
-
} = options;
|
|
40
|
-
|
|
41
|
-
console.log(chalk.blue(`👁️ Watching project: ${projectId}`));
|
|
42
|
-
console.log(chalk.gray(` Path: ${projectPath}`));
|
|
43
|
-
|
|
44
|
-
// Track pending changes for debouncing
|
|
45
|
-
let pendingChanges = new Set();
|
|
46
|
-
let debounceTimer = null;
|
|
47
|
-
|
|
48
|
-
const handleChange = (filePath) => {
|
|
49
|
-
const relativePath = path.relative(projectPath, filePath);
|
|
50
|
-
|
|
51
|
-
// Only track relevant files
|
|
52
|
-
if (!isRelevantFile(filePath)) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
pendingChanges.add(filePath);
|
|
57
|
-
|
|
58
|
-
// Debounce: wait for a pause in file changes
|
|
59
|
-
if (debounceTimer) {
|
|
60
|
-
clearTimeout(debounceTimer);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
debounceTimer = setTimeout(() => {
|
|
64
|
-
const changedFiles = Array.from(pendingChanges);
|
|
65
|
-
pendingChanges.clear();
|
|
66
|
-
|
|
67
|
-
console.log(chalk.yellow(`📝 File changes detected in ${projectId}:`));
|
|
68
|
-
changedFiles.forEach(file => {
|
|
69
|
-
const rel = path.relative(projectPath, file);
|
|
70
|
-
console.log(chalk.gray(` - ${rel}`));
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Trigger onChange callback
|
|
74
|
-
onChange(changedFiles);
|
|
75
|
-
}, debounceMs);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Create watcher
|
|
79
|
-
const watcher = chokidar.watch(projectPath, {
|
|
80
|
-
ignored: ignorePatterns,
|
|
81
|
-
persistent: true,
|
|
82
|
-
ignoreInitial: true,
|
|
83
|
-
awaitWriteFinish: {
|
|
84
|
-
stabilityThreshold: 300,
|
|
85
|
-
pollInterval: 100,
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Listen for file change events
|
|
90
|
-
watcher.on('change', handleChange);
|
|
91
|
-
watcher.on('add', handleChange);
|
|
92
|
-
watcher.on('unlink', handleChange);
|
|
93
|
-
|
|
94
|
-
watcher.on('error', (error) => {
|
|
95
|
-
console.error(chalk.red(`❌ Watcher error for ${projectId}:`), error);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Store watcher reference
|
|
99
|
-
watchers.set(projectId, {
|
|
100
|
-
watcher,
|
|
101
|
-
callback: onChange,
|
|
102
|
-
projectPath,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
return watcher;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Stop watching a project
|
|
110
|
-
*
|
|
111
|
-
* @param {string} projectId - Project identifier
|
|
112
|
-
*/
|
|
113
|
-
export function unwatchProject(projectId) {
|
|
114
|
-
const watcherData = watchers.get(projectId);
|
|
115
|
-
|
|
116
|
-
if (watcherData) {
|
|
117
|
-
console.log(chalk.gray(`⏹️ Stopped watching project: ${projectId}`));
|
|
118
|
-
watcherData.watcher.close();
|
|
119
|
-
watchers.delete(projectId);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Get all currently watched projects
|
|
125
|
-
*
|
|
126
|
-
* @returns {Array<string>} - Array of project IDs
|
|
127
|
-
*/
|
|
128
|
-
export function getWatchedProjects() {
|
|
129
|
-
return Array.from(watchers.keys());
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Check if a project is being watched
|
|
134
|
-
*
|
|
135
|
-
* @param {string} projectId - Project identifier
|
|
136
|
-
* @returns {boolean}
|
|
137
|
-
*/
|
|
138
|
-
export function isWatching(projectId) {
|
|
139
|
-
return watchers.has(projectId);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Stop all watchers
|
|
144
|
-
*/
|
|
145
|
-
export function unwatchAll() {
|
|
146
|
-
console.log(chalk.gray('⏹️ Stopping all file watchers...'));
|
|
147
|
-
for (const projectId of watchers.keys()) {
|
|
148
|
-
unwatchProject(projectId);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Check if a file is relevant for analysis
|
|
154
|
-
*
|
|
155
|
-
* @param {string} filePath - Absolute file path
|
|
156
|
-
* @returns {boolean}
|
|
157
|
-
*/
|
|
158
|
-
function isRelevantFile(filePath) {
|
|
159
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
160
|
-
const relevantExtensions = ['.ts', '.tsx', '.js', '.jsx'];
|
|
161
|
-
|
|
162
|
-
// Check extension
|
|
163
|
-
if (!relevantExtensions.includes(ext)) {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Skip test files
|
|
168
|
-
const basename = path.basename(filePath);
|
|
169
|
-
if (basename.includes('.test.') || basename.includes('.spec.')) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return true;
|
|
174
|
-
}
|
package/src/git-utils.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Git Utilities
|
|
6
|
-
*
|
|
7
|
-
* Helper functions to interact with git repositories
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Get list of uncommitted changed files in a git repository
|
|
12
|
-
*
|
|
13
|
-
* @param {string} projectPath - Absolute path to project directory
|
|
14
|
-
* @returns {string[]} - Array of absolute file paths that have uncommitted changes
|
|
15
|
-
*/
|
|
16
|
-
export function getUncommittedChanges(projectPath) {
|
|
17
|
-
try {
|
|
18
|
-
// Check if directory is a git repository
|
|
19
|
-
if (!isGitRepository(projectPath)) {
|
|
20
|
-
return [];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Get modified, added, and deleted files (staged and unstaged)
|
|
24
|
-
const command = 'git diff --name-only HEAD && git diff --name-only --cached';
|
|
25
|
-
|
|
26
|
-
const output = execSync(command, {
|
|
27
|
-
cwd: projectPath,
|
|
28
|
-
encoding: 'utf8',
|
|
29
|
-
stdio: ['pipe', 'pipe', 'ignore'], // Ignore stderr
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Parse output and convert to absolute paths
|
|
33
|
-
const files = output
|
|
34
|
-
.split('\n')
|
|
35
|
-
.filter(Boolean)
|
|
36
|
-
.map(file => path.resolve(projectPath, file.trim()));
|
|
37
|
-
|
|
38
|
-
// Remove duplicates
|
|
39
|
-
return [...new Set(files)];
|
|
40
|
-
} catch (error) {
|
|
41
|
-
// If git command fails, return empty array
|
|
42
|
-
console.error('Failed to get git diff:', error.message);
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Check if a directory is a git repository
|
|
49
|
-
*
|
|
50
|
-
* @param {string} projectPath - Absolute path to project directory
|
|
51
|
-
* @returns {boolean}
|
|
52
|
-
*/
|
|
53
|
-
export function isGitRepository(projectPath) {
|
|
54
|
-
try {
|
|
55
|
-
execSync('git rev-parse --is-inside-work-tree', {
|
|
56
|
-
cwd: projectPath,
|
|
57
|
-
encoding: 'utf8',
|
|
58
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
59
|
-
});
|
|
60
|
-
return true;
|
|
61
|
-
} catch {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Get list of untracked files in a git repository
|
|
68
|
-
*
|
|
69
|
-
* @param {string} projectPath - Absolute path to project directory
|
|
70
|
-
* @returns {string[]} - Array of absolute file paths that are untracked
|
|
71
|
-
*/
|
|
72
|
-
export function getUntrackedFiles(projectPath) {
|
|
73
|
-
try {
|
|
74
|
-
if (!isGitRepository(projectPath)) {
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const output = execSync('git ls-files --others --exclude-standard', {
|
|
79
|
-
cwd: projectPath,
|
|
80
|
-
encoding: 'utf8',
|
|
81
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return output
|
|
85
|
-
.split('\n')
|
|
86
|
-
.filter(Boolean)
|
|
87
|
-
.map(file => path.resolve(projectPath, file.trim()));
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.error('Failed to get untracked files:', error.message);
|
|
90
|
-
return [];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Get all files with changes (committed or not)
|
|
96
|
-
*
|
|
97
|
-
* @param {string} projectPath - Absolute path to project directory
|
|
98
|
-
* @returns {string[]} - Array of absolute file paths
|
|
99
|
-
*/
|
|
100
|
-
export function getAllChangedFiles(projectPath) {
|
|
101
|
-
const uncommitted = getUncommittedChanges(projectPath);
|
|
102
|
-
const untracked = getUntrackedFiles(projectPath);
|
|
103
|
-
|
|
104
|
-
return [...new Set([...uncommitted, ...untracked])];
|
|
105
|
-
}
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import { analyzeProject } from '../scripts/analyze-project.js';
|
|
2
|
-
import { readAnalysisData, writeAnalysisData } from './registry.js';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Incremental Analyzer
|
|
7
|
-
*
|
|
8
|
-
* Performs incremental analysis by comparing new analysis results with
|
|
9
|
-
* previously stored results and generating a diff of changes.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Perform incremental analysis on a project
|
|
14
|
-
*
|
|
15
|
-
* @param {string} projectId - Project identifier
|
|
16
|
-
* @param {string} projectPath - Absolute path to project
|
|
17
|
-
* @param {string[]} changedFiles - Optional array of changed file paths
|
|
18
|
-
* @returns {Object} - Incremental update with added, removed, and modified nodes/edges
|
|
19
|
-
*/
|
|
20
|
-
export function performIncrementalAnalysis(projectId, projectPath, changedFiles = []) {
|
|
21
|
-
console.log(chalk.blue(`🔄 Running incremental analysis for ${projectId}...`));
|
|
22
|
-
|
|
23
|
-
// Load previous analysis
|
|
24
|
-
const previousAnalysis = readAnalysisData(projectId);
|
|
25
|
-
|
|
26
|
-
if (!previousAnalysis) {
|
|
27
|
-
console.log(chalk.yellow(' No previous analysis found, running full analysis...'));
|
|
28
|
-
// Run full analysis if no previous data exists
|
|
29
|
-
const newAnalysis = analyzeProject(projectPath);
|
|
30
|
-
writeAnalysisData(projectId, newAnalysis);
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
type: 'full',
|
|
34
|
-
analysis: newAnalysis,
|
|
35
|
-
update: null,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Run full analysis (in the future, this could be optimized to analyze only changed files)
|
|
40
|
-
const newAnalysis = analyzeProject(projectPath);
|
|
41
|
-
|
|
42
|
-
// Generate diff
|
|
43
|
-
const update = generateDiff(previousAnalysis, newAnalysis);
|
|
44
|
-
|
|
45
|
-
// Save new analysis
|
|
46
|
-
writeAnalysisData(projectId, newAnalysis);
|
|
47
|
-
|
|
48
|
-
// Log statistics
|
|
49
|
-
const stats = getUpdateStats(update);
|
|
50
|
-
console.log(chalk.green(`✓ Incremental analysis complete:`));
|
|
51
|
-
console.log(chalk.gray(` Added: ${stats.addedNodes} nodes, ${stats.addedEdges} edges`));
|
|
52
|
-
console.log(chalk.gray(` Removed: ${stats.removedNodes} nodes, ${stats.removedEdges} edges`));
|
|
53
|
-
console.log(chalk.gray(` Modified: ${stats.modifiedNodes} nodes, ${stats.modifiedEdges} edges`));
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
type: 'incremental',
|
|
57
|
-
analysis: newAnalysis,
|
|
58
|
-
update,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Generate diff between old and new analysis
|
|
64
|
-
*
|
|
65
|
-
* @param {Object} oldAnalysis - Previous analysis data
|
|
66
|
-
* @param {Object} newAnalysis - New analysis data
|
|
67
|
-
* @returns {Object} - Incremental update object
|
|
68
|
-
*/
|
|
69
|
-
export function generateDiff(oldAnalysis, newAnalysis) {
|
|
70
|
-
const update = {
|
|
71
|
-
added: {
|
|
72
|
-
nodes: [],
|
|
73
|
-
edges: [],
|
|
74
|
-
},
|
|
75
|
-
removed: {
|
|
76
|
-
nodeIds: [],
|
|
77
|
-
edgeIds: [],
|
|
78
|
-
},
|
|
79
|
-
modified: {
|
|
80
|
-
nodes: [],
|
|
81
|
-
edges: [],
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Process each branch
|
|
86
|
-
const branches = new Set([
|
|
87
|
-
...Object.keys(oldAnalysis.planData || {}),
|
|
88
|
-
...Object.keys(newAnalysis.planData || {}),
|
|
89
|
-
]);
|
|
90
|
-
|
|
91
|
-
for (const branchId of branches) {
|
|
92
|
-
const oldPlanData = oldAnalysis.planData?.[branchId];
|
|
93
|
-
const newPlanData = newAnalysis.planData?.[branchId];
|
|
94
|
-
|
|
95
|
-
if (!oldPlanData && newPlanData) {
|
|
96
|
-
// New branch - all nodes/edges are added
|
|
97
|
-
update.added.nodes.push(...(newPlanData.nodes || []));
|
|
98
|
-
update.added.edges.push(...(newPlanData.edges || []));
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (oldPlanData && !newPlanData) {
|
|
103
|
-
// Branch removed - all nodes/edges are removed
|
|
104
|
-
update.removed.nodeIds.push(...(oldPlanData.nodes || []).map(n => n.id));
|
|
105
|
-
update.removed.edgeIds.push(...(oldPlanData.edges || []).map(e => e.id));
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Branch exists in both - compare nodes and edges
|
|
110
|
-
const branchDiff = comparePlanData(oldPlanData, newPlanData);
|
|
111
|
-
update.added.nodes.push(...branchDiff.added.nodes);
|
|
112
|
-
update.added.edges.push(...branchDiff.added.edges);
|
|
113
|
-
update.removed.nodeIds.push(...branchDiff.removed.nodeIds);
|
|
114
|
-
update.removed.edgeIds.push(...branchDiff.removed.edgeIds);
|
|
115
|
-
update.modified.nodes.push(...branchDiff.modified.nodes);
|
|
116
|
-
update.modified.edges.push(...branchDiff.modified.edges);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return update;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Compare plan data for a single branch
|
|
124
|
-
*
|
|
125
|
-
* @param {Object} oldPlanData - Previous plan data
|
|
126
|
-
* @param {Object} newPlanData - New plan data
|
|
127
|
-
* @returns {Object} - Diff object
|
|
128
|
-
*/
|
|
129
|
-
function comparePlanData(oldPlanData, newPlanData) {
|
|
130
|
-
const oldNodes = oldPlanData?.nodes || [];
|
|
131
|
-
const newNodes = newPlanData?.nodes || [];
|
|
132
|
-
const oldEdges = oldPlanData?.edges || [];
|
|
133
|
-
const newEdges = newPlanData?.edges || [];
|
|
134
|
-
|
|
135
|
-
// Create maps for quick lookup
|
|
136
|
-
const oldNodeMap = new Map(oldNodes.map(n => [n.id, n]));
|
|
137
|
-
const newNodeMap = new Map(newNodes.map(n => [n.id, n]));
|
|
138
|
-
const oldEdgeMap = new Map(oldEdges.map(e => [e.id, e]));
|
|
139
|
-
const newEdgeMap = new Map(newEdges.map(e => [e.id, e]));
|
|
140
|
-
|
|
141
|
-
const diff = {
|
|
142
|
-
added: { nodes: [], edges: [] },
|
|
143
|
-
removed: { nodeIds: [], edgeIds: [] },
|
|
144
|
-
modified: { nodes: [], edges: [] },
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// Find added and modified nodes
|
|
148
|
-
for (const [nodeId, newNode] of newNodeMap) {
|
|
149
|
-
const oldNode = oldNodeMap.get(nodeId);
|
|
150
|
-
|
|
151
|
-
if (!oldNode) {
|
|
152
|
-
// Node was added
|
|
153
|
-
diff.added.nodes.push(newNode);
|
|
154
|
-
} else if (hasNodeChanged(oldNode, newNode)) {
|
|
155
|
-
// Node was modified
|
|
156
|
-
diff.modified.nodes.push({
|
|
157
|
-
id: nodeId,
|
|
158
|
-
...getNodeChanges(oldNode, newNode),
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Find removed nodes
|
|
164
|
-
for (const nodeId of oldNodeMap.keys()) {
|
|
165
|
-
if (!newNodeMap.has(nodeId)) {
|
|
166
|
-
diff.removed.nodeIds.push(nodeId);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Find added and modified edges
|
|
171
|
-
for (const [edgeId, newEdge] of newEdgeMap) {
|
|
172
|
-
const oldEdge = oldEdgeMap.get(edgeId);
|
|
173
|
-
|
|
174
|
-
if (!oldEdge) {
|
|
175
|
-
// Edge was added
|
|
176
|
-
diff.added.edges.push(newEdge);
|
|
177
|
-
} else if (hasEdgeChanged(oldEdge, newEdge)) {
|
|
178
|
-
// Edge was modified
|
|
179
|
-
diff.modified.edges.push({
|
|
180
|
-
id: edgeId,
|
|
181
|
-
...getEdgeChanges(oldEdge, newEdge),
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Find removed edges
|
|
187
|
-
for (const edgeId of oldEdgeMap.keys()) {
|
|
188
|
-
if (!newEdgeMap.has(edgeId)) {
|
|
189
|
-
diff.removed.edgeIds.push(edgeId);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return diff;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Check if a node has changed
|
|
198
|
-
*
|
|
199
|
-
* @param {Object} oldNode - Previous node
|
|
200
|
-
* @param {Object} newNode - New node
|
|
201
|
-
* @returns {boolean}
|
|
202
|
-
*/
|
|
203
|
-
function hasNodeChanged(oldNode, newNode) {
|
|
204
|
-
// Compare relevant properties (exclude position)
|
|
205
|
-
const oldData = JSON.stringify({ ...oldNode.data, type: oldNode.type });
|
|
206
|
-
const newData = JSON.stringify({ ...newNode.data, type: newNode.type });
|
|
207
|
-
|
|
208
|
-
return oldData !== newData;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Get node changes
|
|
213
|
-
*
|
|
214
|
-
* @param {Object} oldNode - Previous node
|
|
215
|
-
* @param {Object} newNode - New node
|
|
216
|
-
* @returns {Object} - Changed properties
|
|
217
|
-
*/
|
|
218
|
-
function getNodeChanges(oldNode, newNode) {
|
|
219
|
-
const changes = {};
|
|
220
|
-
|
|
221
|
-
if (oldNode.type !== newNode.type) {
|
|
222
|
-
changes.type = newNode.type;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (JSON.stringify(oldNode.data) !== JSON.stringify(newNode.data)) {
|
|
226
|
-
changes.data = newNode.data;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return changes;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Check if an edge has changed
|
|
234
|
-
*
|
|
235
|
-
* @param {Object} oldEdge - Previous edge
|
|
236
|
-
* @param {Object} newEdge - New edge
|
|
237
|
-
* @returns {boolean}
|
|
238
|
-
*/
|
|
239
|
-
function hasEdgeChanged(oldEdge, newEdge) {
|
|
240
|
-
// Compare relevant properties
|
|
241
|
-
return (
|
|
242
|
-
oldEdge.source !== newEdge.source ||
|
|
243
|
-
oldEdge.target !== newEdge.target ||
|
|
244
|
-
oldEdge.type !== newEdge.type ||
|
|
245
|
-
JSON.stringify(oldEdge.data) !== JSON.stringify(newEdge.data)
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Get edge changes
|
|
251
|
-
*
|
|
252
|
-
* @param {Object} oldEdge - Previous edge
|
|
253
|
-
* @param {Object} newEdge - New edge
|
|
254
|
-
* @returns {Object} - Changed properties
|
|
255
|
-
*/
|
|
256
|
-
function getEdgeChanges(oldEdge, newEdge) {
|
|
257
|
-
const changes = {};
|
|
258
|
-
|
|
259
|
-
if (oldEdge.source !== newEdge.source) changes.source = newEdge.source;
|
|
260
|
-
if (oldEdge.target !== newEdge.target) changes.target = newEdge.target;
|
|
261
|
-
if (oldEdge.type !== newEdge.type) changes.type = newEdge.type;
|
|
262
|
-
if (JSON.stringify(oldEdge.data) !== JSON.stringify(newEdge.data)) {
|
|
263
|
-
changes.data = newEdge.data;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return changes;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Get statistics from an update
|
|
271
|
-
*
|
|
272
|
-
* @param {Object} update - Incremental update object
|
|
273
|
-
* @returns {Object} - Statistics
|
|
274
|
-
*/
|
|
275
|
-
function getUpdateStats(update) {
|
|
276
|
-
return {
|
|
277
|
-
addedNodes: update.added.nodes.length,
|
|
278
|
-
addedEdges: update.added.edges.length,
|
|
279
|
-
removedNodes: update.removed.nodeIds.length,
|
|
280
|
-
removedEdges: update.removed.edgeIds.length,
|
|
281
|
-
modifiedNodes: update.modified.nodes.length,
|
|
282
|
-
modifiedEdges: update.modified.edges.length,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Check if an update has any changes
|
|
288
|
-
*
|
|
289
|
-
* @param {Object} update - Incremental update object
|
|
290
|
-
* @returns {boolean}
|
|
291
|
-
*/
|
|
292
|
-
export function hasChanges(update) {
|
|
293
|
-
const stats = getUpdateStats(update);
|
|
294
|
-
return Object.values(stats).some(count => count > 0);
|
|
295
|
-
}
|