@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.
@@ -0,0 +1,259 @@
1
+ import { analyzeProject } from './analysis/analyze-project';
2
+ import { readAnalysisData, writeAnalysisData } from './registry';
3
+ import chalk from 'chalk';
4
+ /**
5
+ * Incremental Analyzer
6
+ *
7
+ * Performs incremental analysis by comparing new analysis results with
8
+ * previously stored results and generating a diff of changes.
9
+ */
10
+ /**
11
+ * Perform incremental analysis on a project
12
+ *
13
+ * @param {string} projectId - Project identifier
14
+ * @param {string} projectPath - Absolute path to project
15
+ * @param {string[]} changedFiles - Optional array of changed file paths
16
+ * @returns {Object} - Incremental update with added, removed, and modified nodes/edges
17
+ */
18
+ export function performIncrementalAnalysis(projectId, projectPath, changedFiles = []) {
19
+ console.log(chalk.blue(`🔄 Running incremental analysis for ${projectId}...`));
20
+ // Load previous analysis
21
+ const previousAnalysis = readAnalysisData(projectId);
22
+ if (!previousAnalysis) {
23
+ console.log(chalk.yellow(' No previous analysis found, running full analysis...'));
24
+ // Run full analysis if no previous data exists
25
+ const newAnalysis = analyzeProject(projectPath);
26
+ writeAnalysisData(projectId, newAnalysis);
27
+ return {
28
+ type: 'full',
29
+ analysis: newAnalysis,
30
+ update: null,
31
+ };
32
+ }
33
+ // Run full analysis (in the future, this could be optimized to analyze only changed files)
34
+ const newAnalysis = analyzeProject(projectPath);
35
+ // Generate diff
36
+ const update = generateDiff(previousAnalysis, newAnalysis);
37
+ // Save new analysis
38
+ writeAnalysisData(projectId, newAnalysis);
39
+ // Log statistics
40
+ const stats = getUpdateStats(update);
41
+ console.log(chalk.green(`✓ Incremental analysis complete:`));
42
+ console.log(chalk.gray(` Added: ${stats.addedNodes} nodes, ${stats.addedEdges} edges`));
43
+ console.log(chalk.gray(` Removed: ${stats.removedNodes} nodes, ${stats.removedEdges} edges`));
44
+ console.log(chalk.gray(` Modified: ${stats.modifiedNodes} nodes, ${stats.modifiedEdges} edges`));
45
+ return {
46
+ type: 'incremental',
47
+ analysis: newAnalysis,
48
+ update,
49
+ };
50
+ }
51
+ /**
52
+ * Generate diff between old and new analysis
53
+ *
54
+ * @param {Object} oldAnalysis - Previous analysis data
55
+ * @param {Object} newAnalysis - New analysis data
56
+ * @returns {Object} - Incremental update object
57
+ */
58
+ export function generateDiff(oldAnalysis, newAnalysis) {
59
+ const update = {
60
+ added: {
61
+ nodes: [],
62
+ edges: [],
63
+ },
64
+ removed: {
65
+ nodeIds: [],
66
+ edgeIds: [],
67
+ },
68
+ modified: {
69
+ nodes: [],
70
+ edges: [],
71
+ },
72
+ };
73
+ // Process each branch
74
+ const branches = new Set([
75
+ ...Object.keys(oldAnalysis.planData || {}),
76
+ ...Object.keys(newAnalysis.planData || {}),
77
+ ]);
78
+ for (const branchId of branches) {
79
+ const oldPlanData = oldAnalysis.planData?.[branchId];
80
+ const newPlanData = newAnalysis.planData?.[branchId];
81
+ if (!oldPlanData && newPlanData) {
82
+ // New branch - all nodes/edges are added
83
+ update.added.nodes.push(...(newPlanData.nodes || []));
84
+ update.added.edges.push(...(newPlanData.edges || []));
85
+ continue;
86
+ }
87
+ if (oldPlanData && !newPlanData) {
88
+ // Branch removed - all nodes/edges are removed
89
+ update.removed.nodeIds.push(...(oldPlanData.nodes || []).map(n => n.id));
90
+ update.removed.edgeIds.push(...(oldPlanData.edges || []).map(e => e.id));
91
+ continue;
92
+ }
93
+ // Branch exists in both - compare nodes and edges
94
+ const branchDiff = comparePlanData(oldPlanData, newPlanData);
95
+ update.added.nodes.push(...branchDiff.added.nodes);
96
+ update.added.edges.push(...branchDiff.added.edges);
97
+ update.removed.nodeIds.push(...branchDiff.removed.nodeIds);
98
+ update.removed.edgeIds.push(...branchDiff.removed.edgeIds);
99
+ update.modified.nodes.push(...branchDiff.modified.nodes);
100
+ update.modified.edges.push(...branchDiff.modified.edges);
101
+ }
102
+ return update;
103
+ }
104
+ /**
105
+ * Compare plan data for a single branch
106
+ *
107
+ * @param {Object} oldPlanData - Previous plan data
108
+ * @param {Object} newPlanData - New plan data
109
+ * @returns {Object} - Diff object
110
+ */
111
+ function comparePlanData(oldPlanData, newPlanData) {
112
+ const oldNodes = oldPlanData?.nodes || [];
113
+ const newNodes = newPlanData?.nodes || [];
114
+ const oldEdges = oldPlanData?.edges || [];
115
+ const newEdges = newPlanData?.edges || [];
116
+ // Create maps for quick lookup
117
+ const oldNodeMap = new Map(oldNodes.map(n => [n.id, n]));
118
+ const newNodeMap = new Map(newNodes.map(n => [n.id, n]));
119
+ const oldEdgeMap = new Map(oldEdges.map(e => [e.id, e]));
120
+ const newEdgeMap = new Map(newEdges.map(e => [e.id, e]));
121
+ const diff = {
122
+ added: { nodes: [], edges: [] },
123
+ removed: { nodeIds: [], edgeIds: [] },
124
+ modified: { nodes: [], edges: [] },
125
+ };
126
+ // Find added and modified nodes
127
+ for (const [nodeId, newNode] of newNodeMap) {
128
+ const oldNode = oldNodeMap.get(nodeId);
129
+ if (!oldNode) {
130
+ // Node was added
131
+ diff.added.nodes.push(newNode);
132
+ }
133
+ else if (hasNodeChanged(oldNode, newNode)) {
134
+ // Node was modified
135
+ diff.modified.nodes.push({
136
+ id: nodeId,
137
+ ...getNodeChanges(oldNode, newNode),
138
+ });
139
+ }
140
+ }
141
+ // Find removed nodes
142
+ for (const nodeId of oldNodeMap.keys()) {
143
+ if (!newNodeMap.has(nodeId)) {
144
+ diff.removed.nodeIds.push(nodeId);
145
+ }
146
+ }
147
+ // Find added and modified edges
148
+ for (const [edgeId, newEdge] of newEdgeMap) {
149
+ const oldEdge = oldEdgeMap.get(edgeId);
150
+ if (!oldEdge) {
151
+ // Edge was added
152
+ diff.added.edges.push(newEdge);
153
+ }
154
+ else if (hasEdgeChanged(oldEdge, newEdge)) {
155
+ // Edge was modified
156
+ diff.modified.edges.push({
157
+ id: edgeId,
158
+ ...getEdgeChanges(oldEdge, newEdge),
159
+ });
160
+ }
161
+ }
162
+ // Find removed edges
163
+ for (const edgeId of oldEdgeMap.keys()) {
164
+ if (!newEdgeMap.has(edgeId)) {
165
+ diff.removed.edgeIds.push(edgeId);
166
+ }
167
+ }
168
+ return diff;
169
+ }
170
+ /**
171
+ * Check if a node has changed
172
+ *
173
+ * @param {Object} oldNode - Previous node
174
+ * @param {Object} newNode - New node
175
+ * @returns {boolean}
176
+ */
177
+ function hasNodeChanged(oldNode, newNode) {
178
+ // Compare relevant properties (exclude position)
179
+ const oldData = JSON.stringify({ ...oldNode.data, type: oldNode.type });
180
+ const newData = JSON.stringify({ ...newNode.data, type: newNode.type });
181
+ return oldData !== newData;
182
+ }
183
+ /**
184
+ * Get node changes
185
+ *
186
+ * @param {Object} oldNode - Previous node
187
+ * @param {Object} newNode - New node
188
+ * @returns {Object} - Changed properties
189
+ */
190
+ function getNodeChanges(oldNode, newNode) {
191
+ const changes = {};
192
+ if (oldNode.type !== newNode.type) {
193
+ changes.type = newNode.type;
194
+ }
195
+ if (JSON.stringify(oldNode.data) !== JSON.stringify(newNode.data)) {
196
+ changes.data = newNode.data;
197
+ }
198
+ return changes;
199
+ }
200
+ /**
201
+ * Check if an edge has changed
202
+ *
203
+ * @param {Object} oldEdge - Previous edge
204
+ * @param {Object} newEdge - New edge
205
+ * @returns {boolean}
206
+ */
207
+ function hasEdgeChanged(oldEdge, newEdge) {
208
+ // Compare relevant properties
209
+ return (oldEdge.source !== newEdge.source ||
210
+ oldEdge.target !== newEdge.target ||
211
+ oldEdge.type !== newEdge.type ||
212
+ JSON.stringify(oldEdge.data) !== JSON.stringify(newEdge.data));
213
+ }
214
+ /**
215
+ * Get edge changes
216
+ *
217
+ * @param {Object} oldEdge - Previous edge
218
+ * @param {Object} newEdge - New edge
219
+ * @returns {Object} - Changed properties
220
+ */
221
+ function getEdgeChanges(oldEdge, newEdge) {
222
+ const changes = {};
223
+ if (oldEdge.source !== newEdge.source)
224
+ changes.source = newEdge.source;
225
+ if (oldEdge.target !== newEdge.target)
226
+ changes.target = newEdge.target;
227
+ if (oldEdge.type !== newEdge.type)
228
+ changes.type = newEdge.type;
229
+ if (JSON.stringify(oldEdge.data) !== JSON.stringify(newEdge.data)) {
230
+ changes.data = newEdge.data;
231
+ }
232
+ return changes;
233
+ }
234
+ /**
235
+ * Get statistics from an update
236
+ *
237
+ * @param {Object} update - Incremental update object
238
+ * @returns {Object} - Statistics
239
+ */
240
+ function getUpdateStats(update) {
241
+ return {
242
+ addedNodes: update.added.nodes.length,
243
+ addedEdges: update.added.edges.length,
244
+ removedNodes: update.removed.nodeIds.length,
245
+ removedEdges: update.removed.edgeIds.length,
246
+ modifiedNodes: update.modified.nodes.length,
247
+ modifiedEdges: update.modified.edges.length,
248
+ };
249
+ }
250
+ /**
251
+ * Check if an update has any changes
252
+ *
253
+ * @param {Object} update - Incremental update object
254
+ * @returns {boolean}
255
+ */
256
+ export function hasChanges(update) {
257
+ const stats = getUpdateStats(update);
258
+ return Object.values(stats).some(count => count > 0);
259
+ }
@@ -0,0 +1,6 @@
1
+ export { rayburstPlugin } from './vite-plugin';
2
+ export type { RayburstPluginOptions } from './vite-plugin';
3
+ export { analyzeProject } from './analysis/analyze-project';
4
+ export { ensureRayburstDir, readLocalAnalysis, writeLocalAnalysis, readLocalMeta, writeLocalMeta, addGitignoreEntry, isProjectInitialized, initializeProject, } from './local-storage';
5
+ export type { ProjectMeta } from './local-storage';
6
+ export type { AnalysisResult } from '@rayburst/types';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // Main Vite plugin export
2
+ export { rayburstPlugin } from './vite-plugin';
3
+ // Re-export analysis functionality for advanced usage
4
+ export { analyzeProject } from './analysis/analyze-project';
5
+ // Re-export storage utilities
6
+ export { ensureRayburstDir, readLocalAnalysis, writeLocalAnalysis, readLocalMeta, writeLocalMeta, addGitignoreEntry, isProjectInitialized, initializeProject, } from './local-storage';
@@ -0,0 +1,39 @@
1
+ import type { AnalysisResult } from '@rayburst/types';
2
+ export interface ProjectMeta {
3
+ projectId: string;
4
+ projectName: string;
5
+ createdAt: string;
6
+ cliVersion: string;
7
+ }
8
+ /**
9
+ * Ensures the .rayburst directory exists in the project
10
+ */
11
+ export declare function ensureRayburstDir(projectPath: string): Promise<string>;
12
+ /**
13
+ * Reads analysis data from .rayburst/analysis.json
14
+ */
15
+ export declare function readLocalAnalysis(projectPath: string): Promise<AnalysisResult | null>;
16
+ /**
17
+ * Writes analysis data to .rayburst/analysis.json
18
+ */
19
+ export declare function writeLocalAnalysis(projectPath: string, analysis: AnalysisResult): Promise<void>;
20
+ /**
21
+ * Reads project metadata from .rayburst/meta.json
22
+ */
23
+ export declare function readLocalMeta(projectPath: string): Promise<ProjectMeta | null>;
24
+ /**
25
+ * Writes project metadata to .rayburst/meta.json
26
+ */
27
+ export declare function writeLocalMeta(projectPath: string, meta: ProjectMeta): Promise<void>;
28
+ /**
29
+ * Adds .rayburst/ entry to .gitignore if not already present
30
+ */
31
+ export declare function addGitignoreEntry(projectPath: string): Promise<void>;
32
+ /**
33
+ * Checks if a project has been initialized (has .rayburst directory)
34
+ */
35
+ export declare function isProjectInitialized(projectPath: string): Promise<boolean>;
36
+ /**
37
+ * Initializes a project with .rayburst directory and metadata
38
+ */
39
+ export declare function initializeProject(projectPath: string, projectName: string, cliVersion: string): Promise<ProjectMeta>;
@@ -0,0 +1,117 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ /**
4
+ * Ensures the .rayburst directory exists in the project
5
+ */
6
+ export async function ensureRayburstDir(projectPath) {
7
+ const rayburstDir = path.join(projectPath, '.rayburst');
8
+ await fs.mkdir(rayburstDir, { recursive: true });
9
+ return rayburstDir;
10
+ }
11
+ /**
12
+ * Reads analysis data from .rayburst/analysis.json
13
+ */
14
+ export async function readLocalAnalysis(projectPath) {
15
+ const analysisPath = path.join(projectPath, '.rayburst', 'analysis.json');
16
+ try {
17
+ const data = await fs.readFile(analysisPath, 'utf-8');
18
+ return JSON.parse(data);
19
+ }
20
+ catch (error) {
21
+ // File doesn't exist or is invalid
22
+ return null;
23
+ }
24
+ }
25
+ /**
26
+ * Writes analysis data to .rayburst/analysis.json
27
+ */
28
+ export async function writeLocalAnalysis(projectPath, analysis) {
29
+ await ensureRayburstDir(projectPath);
30
+ const analysisPath = path.join(projectPath, '.rayburst', 'analysis.json');
31
+ await fs.writeFile(analysisPath, JSON.stringify(analysis, null, 2), 'utf-8');
32
+ }
33
+ /**
34
+ * Reads project metadata from .rayburst/meta.json
35
+ */
36
+ export async function readLocalMeta(projectPath) {
37
+ const metaPath = path.join(projectPath, '.rayburst', 'meta.json');
38
+ try {
39
+ const data = await fs.readFile(metaPath, 'utf-8');
40
+ return JSON.parse(data);
41
+ }
42
+ catch (error) {
43
+ return null;
44
+ }
45
+ }
46
+ /**
47
+ * Writes project metadata to .rayburst/meta.json
48
+ */
49
+ export async function writeLocalMeta(projectPath, meta) {
50
+ await ensureRayburstDir(projectPath);
51
+ const metaPath = path.join(projectPath, '.rayburst', 'meta.json');
52
+ await fs.writeFile(metaPath, JSON.stringify(meta, null, 2), 'utf-8');
53
+ }
54
+ /**
55
+ * Adds .rayburst/ entry to .gitignore if not already present
56
+ */
57
+ export async function addGitignoreEntry(projectPath) {
58
+ const gitignorePath = path.join(projectPath, '.gitignore');
59
+ let gitignoreContent = '';
60
+ try {
61
+ gitignoreContent = await fs.readFile(gitignorePath, 'utf-8');
62
+ }
63
+ catch (error) {
64
+ // .gitignore doesn't exist, will create it
65
+ }
66
+ // Check if .rayburst is already in .gitignore
67
+ if (gitignoreContent.includes('.rayburst')) {
68
+ return; // Already exists
69
+ }
70
+ // Add .rayburst entry
71
+ const entry = '\n# Rayburst analysis data\n.rayburst/\n';
72
+ gitignoreContent += entry;
73
+ await fs.writeFile(gitignorePath, gitignoreContent, 'utf-8');
74
+ }
75
+ /**
76
+ * Checks if a project has been initialized (has .rayburst directory)
77
+ */
78
+ export async function isProjectInitialized(projectPath) {
79
+ const rayburstDir = path.join(projectPath, '.rayburst');
80
+ try {
81
+ const stats = await fs.stat(rayburstDir);
82
+ return stats.isDirectory();
83
+ }
84
+ catch (error) {
85
+ return false;
86
+ }
87
+ }
88
+ /**
89
+ * Initializes a project with .rayburst directory and metadata
90
+ */
91
+ export async function initializeProject(projectPath, projectName, cliVersion) {
92
+ // Ensure directory exists
93
+ await ensureRayburstDir(projectPath);
94
+ // Generate project ID (simple hash based on path and timestamp)
95
+ const projectId = generateProjectId(projectPath);
96
+ // Create metadata
97
+ const meta = {
98
+ projectId,
99
+ projectName,
100
+ createdAt: new Date().toISOString(),
101
+ cliVersion,
102
+ };
103
+ // Write metadata
104
+ await writeLocalMeta(projectPath, meta);
105
+ // Add to .gitignore
106
+ await addGitignoreEntry(projectPath);
107
+ return meta;
108
+ }
109
+ /**
110
+ * Generates a simple project ID based on path
111
+ */
112
+ function generateProjectId(projectPath) {
113
+ // Simple hash: take last part of path + timestamp
114
+ const baseName = path.basename(projectPath);
115
+ const timestamp = Date.now().toString(36);
116
+ return `${baseName}-${timestamp}`;
117
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Ensure the .rayburst directory and subdirectories exist
3
+ */
4
+ export declare function ensureRayburstDir(): void;
5
+ /**
6
+ * Read the projects registry
7
+ */
8
+ export declare function readRegistry(): any;
9
+ /**
10
+ * Write to the projects registry
11
+ */
12
+ export declare function writeRegistry(registry: any): boolean;
13
+ /**
14
+ * Generate a unique project ID from the project path
15
+ */
16
+ export declare function generateProjectId(projectPath: any): string;
17
+ /**
18
+ * Check if a directory is a valid project (has package.json)
19
+ */
20
+ export declare function isValidProject(projectPath: any): boolean;
21
+ /**
22
+ * Read package.json from a project
23
+ */
24
+ export declare function readPackageJson(projectPath: any): any;
25
+ /**
26
+ * Validate if a project's path still exists and is valid
27
+ */
28
+ export declare function validateProjectPath(project: any): boolean;
29
+ /**
30
+ * Validate all registered projects
31
+ * Returns array of projects with validation status
32
+ */
33
+ export declare function validateAllProjects(): any;
34
+ /**
35
+ * Update a project's path
36
+ * Validates the new path and ensures it's the same project
37
+ */
38
+ export declare function updateProjectPath(projectId: any, newPath: any): any;
39
+ /**
40
+ * Register a new project
41
+ */
42
+ export declare function registerProject(projectPath: any): {
43
+ id: string;
44
+ name: any;
45
+ path: string;
46
+ registeredAt: string;
47
+ lastAnalyzed: any;
48
+ packageJson: {
49
+ name: any;
50
+ version: any;
51
+ description: any;
52
+ };
53
+ };
54
+ /**
55
+ * Unregister a project
56
+ */
57
+ export declare function unregisterProject(projectPathOrId: any): any;
58
+ /**
59
+ * List all registered projects
60
+ */
61
+ export declare function listProjects(): any;
62
+ /**
63
+ * Get a specific project by ID or path
64
+ */
65
+ export declare function getProject(projectIdOrPath: any): any;
66
+ /**
67
+ * Update project's lastAnalyzed timestamp
68
+ */
69
+ export declare function updateProjectAnalysis(projectId: any): any;
70
+ /**
71
+ * Get path to analysis file for a project
72
+ */
73
+ export declare function getAnalysisFilePath(projectId: any): string;
74
+ /**
75
+ * Read analysis data for a project
76
+ */
77
+ export declare function readAnalysisData(projectId: any): any;
78
+ /**
79
+ * Write analysis data for a project
80
+ */
81
+ export declare function writeAnalysisData(projectId: any, analysisData: any): boolean;
82
+ /**
83
+ * Get registry paths for external access
84
+ */
85
+ export declare function getRegistryPaths(): {
86
+ rayburstDir: string;
87
+ projectsFile: string;
88
+ analyzedDir: string;
89
+ };