@rayburst/cli 0.1.18 → 0.2.1

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/src/main.tsx DELETED
@@ -1,225 +0,0 @@
1
- import React, { Suspense, lazy, useEffect, useState } from 'react';
2
- import { createRoot } from 'react-dom/client';
3
- import * as ReactDOM from 'react-dom/client';
4
-
5
- // Expose React and ReactDOM globally for module federation
6
- (window as any).React = React;
7
- (window as any).ReactDOM = ReactDOM;
8
-
9
- // Loading component
10
- const LoadingFallback = () => (
11
- <div style={{
12
- display: 'flex',
13
- flexDirection: 'column',
14
- alignItems: 'center',
15
- justifyContent: 'center',
16
- height: '100vh',
17
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
18
- color: 'white',
19
- }}>
20
- <div style={{
21
- width: '50px',
22
- height: '50px',
23
- border: '4px solid rgba(255, 255, 255, 0.3)',
24
- borderTopColor: 'white',
25
- borderRadius: '50%',
26
- animation: 'spin 1s linear infinite',
27
- marginBottom: '20px',
28
- }} />
29
- <div style={{ fontSize: '18px', fontWeight: 500 }}>Loading Rayburst...</div>
30
- </div>
31
- );
32
-
33
- // Error boundary component
34
- class ErrorBoundary extends React.Component<
35
- { children: React.ReactNode },
36
- { hasError: boolean; error: Error | null }
37
- > {
38
- constructor(props: { children: React.ReactNode }) {
39
- super(props);
40
- this.state = { hasError: false, error: null };
41
- }
42
-
43
- static getDerivedStateFromError(error: Error) {
44
- return { hasError: true, error };
45
- }
46
-
47
- componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
48
- console.error('Remote app loading error:', error, errorInfo);
49
- }
50
-
51
- render() {
52
- if (this.state.hasError) {
53
- return (
54
- <div style={{
55
- display: 'flex',
56
- flexDirection: 'column',
57
- alignItems: 'center',
58
- justifyContent: 'center',
59
- height: '100vh',
60
- background: '#f5f5f5',
61
- padding: '20px',
62
- }}>
63
- <h1 style={{ color: '#d32f2f', marginBottom: '10px' }}>Failed to Load Rayburst</h1>
64
- <p style={{ color: '#666', marginBottom: '20px' }}>
65
- {this.state.error?.message || 'An unexpected error occurred'}
66
- </p>
67
- <button
68
- onClick={() => window.location.reload()}
69
- style={{
70
- padding: '10px 20px',
71
- background: '#667eea',
72
- color: 'white',
73
- border: 'none',
74
- borderRadius: '5px',
75
- cursor: 'pointer',
76
- fontSize: '16px',
77
- }}
78
- >
79
- Retry
80
- </button>
81
- </div>
82
- );
83
- }
84
-
85
- return this.props.children;
86
- }
87
- }
88
-
89
- // Function to load remote CSS
90
- const loadRemoteCSS = async () => {
91
- try {
92
- // Determine the remote URL based on environment
93
- const isDevelopment = import.meta.env.DEV;
94
- const remoteUrl = isDevelopment
95
- ? 'http://localhost:3000'
96
- : import.meta.env.PROD
97
- ? 'https://www.rayburst.app'
98
- : 'https://dev.rayburst.app';
99
-
100
- console.log('[CLI] Attempting to load CSS from:', remoteUrl, '(dev mode:', isDevelopment, ')');
101
-
102
- if (isDevelopment) {
103
- // In development mode, Vite serves CSS as a JavaScript module
104
- // We need to dynamically import it to trigger Vite's CSS injection
105
- console.log('[CLI] Development mode: Importing CSS module from Vite dev server');
106
-
107
- const cssModuleUrl = `${remoteUrl}/src/styles.css`;
108
-
109
- try {
110
- // Check if we've already loaded this CSS module
111
- if (!(window as any).__rayburstCssLoaded) {
112
- console.log('[CLI] Dynamically importing CSS module:', cssModuleUrl);
113
-
114
- // Create a script tag to load the CSS module
115
- const script = document.createElement('script');
116
- script.type = 'module';
117
- script.textContent = `import '${cssModuleUrl}';`;
118
- document.head.appendChild(script);
119
-
120
- // Mark as loaded
121
- (window as any).__rayburstCssLoaded = true;
122
- console.log('[CLI] CSS module import initiated');
123
-
124
- // Give it a moment to process
125
- await new Promise(resolve => setTimeout(resolve, 100));
126
- } else {
127
- console.log('[CLI] CSS module already loaded');
128
- }
129
- } catch (cssErr) {
130
- console.warn('[CLI] Could not load CSS module, styles may be missing:', cssErr);
131
- }
132
- } else {
133
- // In production/staging, fetch the index.html to find the CSS file
134
- const response = await fetch(remoteUrl);
135
- const html = await response.text();
136
-
137
- // Extract CSS filename from the HTML
138
- const cssMatch = html.match(/href="\/assets\/(style-[^"]+\.css)"/);
139
-
140
- if (cssMatch && cssMatch[1]) {
141
- const cssFile = cssMatch[1];
142
- const cssUrl = `${remoteUrl}/assets/${cssFile}`;
143
-
144
- console.log('[CLI] Loading remote CSS:', cssUrl);
145
-
146
- // Check if CSS is already loaded
147
- const existingLink = document.querySelector(`link[href="${cssUrl}"]`);
148
- if (!existingLink) {
149
- const link = document.createElement('link');
150
- link.rel = 'stylesheet';
151
- link.href = cssUrl;
152
- document.head.appendChild(link);
153
- console.log('[CLI] Remote CSS loaded successfully');
154
-
155
- // Wait for CSS to load
156
- await new Promise((resolve) => {
157
- link.onload = resolve;
158
- link.onerror = resolve;
159
- });
160
- }
161
- } else {
162
- console.warn('[CLI] No CSS file found in remote HTML');
163
- }
164
- }
165
- } catch (err) {
166
- console.error('[CLI] Failed to load remote CSS:', err);
167
- }
168
- };
169
-
170
- // Dynamically import the remote app
171
- const RemoteApp = lazy(async () => {
172
- console.log('[CLI] Attempting to load remote app from rayburstApp/App');
173
- console.log('[CLI] Expected remote URL:', 'http://localhost:3000/remoteEntry.js');
174
-
175
- // Load the remote CSS first (in parallel with module loading)
176
- const cssPromise = loadRemoteCSS();
177
-
178
- try {
179
- // @ts-ignore - Module federation import
180
- const module = await import('rayburstApp/App');
181
- console.log('[CLI] Successfully loaded remote app module:', module);
182
-
183
- // Wait for CSS to finish loading
184
- await cssPromise;
185
-
186
- return module;
187
- } catch (err: any) {
188
- console.error('[CLI] Failed to load remote app:', err);
189
- console.error('[CLI] Error details:', {
190
- name: err.name,
191
- message: err.message,
192
- stack: err.stack
193
- });
194
- throw new Error('Could not load Rayburst application. Make sure it is running on port 3000.');
195
- }
196
- });
197
-
198
- function App() {
199
- const [isReady, setIsReady] = useState(false);
200
-
201
- useEffect(() => {
202
- // Small delay to ensure remote is ready
203
- const timer = setTimeout(() => setIsReady(true), 100);
204
- return () => clearTimeout(timer);
205
- }, []);
206
-
207
- if (!isReady) {
208
- return <LoadingFallback />;
209
- }
210
-
211
- return (
212
- <ErrorBoundary>
213
- <Suspense fallback={<LoadingFallback />}>
214
- <RemoteApp />
215
- </Suspense>
216
- </ErrorBoundary>
217
- );
218
- }
219
-
220
- // Mount the app
221
- const container = document.getElementById('app');
222
- if (container) {
223
- const root = createRoot(container);
224
- root.render(<App />);
225
- }
package/src/registry.js DELETED
@@ -1,262 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import os from 'os';
5
- import crypto from 'crypto';
6
-
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = path.dirname(__filename);
9
-
10
- // Global rayburst directory in user's home
11
- const RAYBURST_DIR = path.join(os.homedir(), '.rayburst');
12
- const PROJECTS_FILE = path.join(RAYBURST_DIR, 'projects.json');
13
- const ANALYZED_DIR = path.join(RAYBURST_DIR, 'analyzed');
14
-
15
- /**
16
- * Ensure the .rayburst directory and subdirectories exist
17
- */
18
- export function ensureRayburstDir() {
19
- if (!fs.existsSync(RAYBURST_DIR)) {
20
- fs.mkdirSync(RAYBURST_DIR, { recursive: true });
21
- }
22
- if (!fs.existsSync(ANALYZED_DIR)) {
23
- fs.mkdirSync(ANALYZED_DIR, { recursive: true });
24
- }
25
- if (!fs.existsSync(PROJECTS_FILE)) {
26
- fs.writeFileSync(PROJECTS_FILE, JSON.stringify({ projects: [] }, null, 2));
27
- }
28
- }
29
-
30
- /**
31
- * Read the projects registry
32
- */
33
- export function readRegistry() {
34
- ensureRayburstDir();
35
- try {
36
- const data = fs.readFileSync(PROJECTS_FILE, 'utf-8');
37
- return JSON.parse(data);
38
- } catch (error) {
39
- console.error('Error reading registry:', error.message);
40
- return { projects: [] };
41
- }
42
- }
43
-
44
- /**
45
- * Write to the projects registry
46
- */
47
- export function writeRegistry(registry) {
48
- ensureRayburstDir();
49
- try {
50
- fs.writeFileSync(PROJECTS_FILE, JSON.stringify(registry, null, 2));
51
- return true;
52
- } catch (error) {
53
- console.error('Error writing registry:', error.message);
54
- return false;
55
- }
56
- }
57
-
58
- /**
59
- * Generate a unique project ID from the project path
60
- */
61
- export function generateProjectId(projectPath) {
62
- const hash = crypto.createHash('md5').update(projectPath).digest('hex');
63
- return hash.substring(0, 12);
64
- }
65
-
66
- /**
67
- * Check if a directory is a valid project (has package.json)
68
- */
69
- export function isValidProject(projectPath) {
70
- const packageJsonPath = path.join(projectPath, 'package.json');
71
- return fs.existsSync(packageJsonPath);
72
- }
73
-
74
- /**
75
- * Read package.json from a project
76
- */
77
- export function readPackageJson(projectPath) {
78
- const packageJsonPath = path.join(projectPath, 'package.json');
79
- try {
80
- const data = fs.readFileSync(packageJsonPath, 'utf-8');
81
- return JSON.parse(data);
82
- } catch (error) {
83
- console.error(`Error reading package.json:`, error.message);
84
- return null;
85
- }
86
- }
87
-
88
- /**
89
- * Register a new project
90
- */
91
- export function registerProject(projectPath) {
92
- const absolutePath = path.resolve(projectPath);
93
-
94
- // Validate project
95
- if (!isValidProject(absolutePath)) {
96
- throw new Error(`Not a valid project: ${absolutePath} (missing package.json)`);
97
- }
98
-
99
- const registry = readRegistry();
100
- const projectId = generateProjectId(absolutePath);
101
-
102
- // Check if already registered
103
- const existingIndex = registry.projects.findIndex(p => p.id === projectId);
104
- if (existingIndex >= 0) {
105
- throw new Error(`Project already registered: ${absolutePath}`);
106
- }
107
-
108
- // Read package.json
109
- const packageJson = readPackageJson(absolutePath);
110
- if (!packageJson) {
111
- throw new Error(`Failed to read package.json from ${absolutePath}`);
112
- }
113
-
114
- // Create project entry
115
- const project = {
116
- id: projectId,
117
- name: packageJson.name || path.basename(absolutePath),
118
- path: absolutePath,
119
- registeredAt: new Date().toISOString(),
120
- lastAnalyzed: null,
121
- packageJson: {
122
- name: packageJson.name,
123
- version: packageJson.version,
124
- description: packageJson.description,
125
- },
126
- };
127
-
128
- registry.projects.push(project);
129
-
130
- if (writeRegistry(registry)) {
131
- return project;
132
- } else {
133
- throw new Error('Failed to write registry');
134
- }
135
- }
136
-
137
- /**
138
- * Unregister a project
139
- */
140
- export function unregisterProject(projectPathOrId) {
141
- const registry = readRegistry();
142
- const absolutePath = path.resolve(projectPathOrId);
143
- const projectId = generateProjectId(absolutePath);
144
-
145
- // Find by ID or path
146
- const index = registry.projects.findIndex(
147
- p => p.id === projectId || p.id === projectPathOrId || p.path === absolutePath
148
- );
149
-
150
- if (index === -1) {
151
- throw new Error(`Project not found: ${projectPathOrId}`);
152
- }
153
-
154
- const project = registry.projects[index];
155
- registry.projects.splice(index, 1);
156
-
157
- // Remove analysis data if it exists
158
- const analysisFile = path.join(ANALYZED_DIR, `${project.id}.json`);
159
- if (fs.existsSync(analysisFile)) {
160
- fs.unlinkSync(analysisFile);
161
- }
162
-
163
- if (writeRegistry(registry)) {
164
- return project;
165
- } else {
166
- throw new Error('Failed to write registry');
167
- }
168
- }
169
-
170
- /**
171
- * List all registered projects
172
- */
173
- export function listProjects() {
174
- const registry = readRegistry();
175
- return registry.projects;
176
- }
177
-
178
- /**
179
- * Get a specific project by ID or path
180
- */
181
- export function getProject(projectIdOrPath) {
182
- const registry = readRegistry();
183
- const absolutePath = path.resolve(projectIdOrPath);
184
- const projectId = generateProjectId(absolutePath);
185
-
186
- return registry.projects.find(
187
- p => p.id === projectId || p.id === projectIdOrPath || p.path === absolutePath
188
- );
189
- }
190
-
191
- /**
192
- * Update project's lastAnalyzed timestamp
193
- */
194
- export function updateProjectAnalysis(projectId) {
195
- const registry = readRegistry();
196
- const project = registry.projects.find(p => p.id === projectId);
197
-
198
- if (!project) {
199
- throw new Error(`Project not found: ${projectId}`);
200
- }
201
-
202
- project.lastAnalyzed = new Date().toISOString();
203
-
204
- if (writeRegistry(registry)) {
205
- return project;
206
- } else {
207
- throw new Error('Failed to update registry');
208
- }
209
- }
210
-
211
- /**
212
- * Get path to analysis file for a project
213
- */
214
- export function getAnalysisFilePath(projectId) {
215
- return path.join(ANALYZED_DIR, `${projectId}.json`);
216
- }
217
-
218
- /**
219
- * Read analysis data for a project
220
- */
221
- export function readAnalysisData(projectId) {
222
- const analysisFile = getAnalysisFilePath(projectId);
223
-
224
- if (!fs.existsSync(analysisFile)) {
225
- return null;
226
- }
227
-
228
- try {
229
- const data = fs.readFileSync(analysisFile, 'utf-8');
230
- return JSON.parse(data);
231
- } catch (error) {
232
- console.error(`Error reading analysis data:`, error.message);
233
- return null;
234
- }
235
- }
236
-
237
- /**
238
- * Write analysis data for a project
239
- */
240
- export function writeAnalysisData(projectId, analysisData) {
241
- ensureRayburstDir();
242
- const analysisFile = getAnalysisFilePath(projectId);
243
-
244
- try {
245
- fs.writeFileSync(analysisFile, JSON.stringify(analysisData, null, 2));
246
- return true;
247
- } catch (error) {
248
- console.error(`Error writing analysis data:`, error.message);
249
- return false;
250
- }
251
- }
252
-
253
- /**
254
- * Get registry paths for external access
255
- */
256
- export function getRegistryPaths() {
257
- return {
258
- rayburstDir: RAYBURST_DIR,
259
- projectsFile: PROJECTS_FILE,
260
- analyzedDir: ANALYZED_DIR,
261
- };
262
- }
@@ -1,123 +0,0 @@
1
- import {
2
- listProjects,
3
- getProject,
4
- readAnalysisData,
5
- registerProject,
6
- unregisterProject,
7
- } from './src/registry.js';
8
- import { getAllChangedFiles } from './src/git-utils.js';
9
-
10
- /**
11
- * Vite plugin to handle API routes during development
12
- */
13
- export function apiPlugin() {
14
- return {
15
- name: 'rayburst-api',
16
- configureServer(server) {
17
- server.middlewares.use('/api', (req, res, next) => {
18
- // Parse JSON body for POST requests
19
- if (req.method === 'POST' || req.method === 'PUT') {
20
- let body = '';
21
- req.on('data', chunk => {
22
- body += chunk.toString();
23
- });
24
- req.on('end', () => {
25
- try {
26
- req.body = JSON.parse(body);
27
- } catch (e) {
28
- req.body = {};
29
- }
30
- handleApiRequest(req, res, next);
31
- });
32
- } else {
33
- handleApiRequest(req, res, next);
34
- }
35
- });
36
- },
37
- };
38
- }
39
-
40
- function handleApiRequest(req, res, next) {
41
- const url = (req.url || '').replace('/api', ''); // Remove /api prefix since middleware already matched it
42
- const method = req.method;
43
-
44
- // Set JSON response headers
45
- res.setHeader('Content-Type', 'application/json');
46
-
47
- try {
48
- // GET /api/projects - List all projects
49
- if (method === 'GET' && url === '/projects') {
50
- const projects = listProjects();
51
- res.end(JSON.stringify({ projects }));
52
- return;
53
- }
54
-
55
- // GET /projects/:id/changed-files - Get uncommitted changed files (check before generic project route)
56
- if (method === 'GET' && url.includes('/changed-files')) {
57
- const id = url.split('/')[2];
58
- const project = getProject(id);
59
- if (!project) {
60
- res.statusCode = 404;
61
- res.end(JSON.stringify({ error: 'Project not found' }));
62
- return;
63
- }
64
- const changedFiles = getAllChangedFiles(project.path);
65
- res.end(JSON.stringify({ changedFiles }));
66
- return;
67
- }
68
-
69
- // GET /projects/:id/analysis - Get project analysis
70
- if (method === 'GET' && url.includes('/analysis')) {
71
- const id = url.split('/')[2];
72
- const analysisData = readAnalysisData(id);
73
- if (!analysisData) {
74
- res.statusCode = 404;
75
- res.end(JSON.stringify({ error: 'Analysis data not found' }));
76
- return;
77
- }
78
- res.end(JSON.stringify(analysisData));
79
- return;
80
- }
81
-
82
- // GET /projects/:id - Get specific project
83
- if (method === 'GET' && url.startsWith('/projects/') && !url.includes('/analysis') && !url.includes('/changed-files')) {
84
- const id = url.split('/')[2];
85
- const project = getProject(id);
86
- if (!project) {
87
- res.statusCode = 404;
88
- res.end(JSON.stringify({ error: 'Project not found' }));
89
- return;
90
- }
91
- res.end(JSON.stringify({ project }));
92
- return;
93
- }
94
-
95
- // POST /projects/register - Register project
96
- if (method === 'POST' && url === '/projects/register') {
97
- const { path } = req.body || {};
98
- if (!path) {
99
- res.statusCode = 400;
100
- res.end(JSON.stringify({ error: 'Path is required' }));
101
- return;
102
- }
103
- const project = registerProject(path);
104
- res.end(JSON.stringify({ project }));
105
- return;
106
- }
107
-
108
- // DELETE /projects/:id - Unregister project
109
- if (method === 'DELETE' && url.startsWith('/projects/')) {
110
- const id = url.split('/')[2];
111
- const project = unregisterProject(id);
112
- res.end(JSON.stringify({ project }));
113
- return;
114
- }
115
-
116
- // No matching route
117
- res.statusCode = 404;
118
- res.end(JSON.stringify({ error: 'API endpoint not found' }));
119
- } catch (error) {
120
- res.statusCode = 500;
121
- res.end(JSON.stringify({ error: error.message }));
122
- }
123
- }
package/vite.config.ts DELETED
@@ -1,72 +0,0 @@
1
- import { defineConfig } from 'vite';
2
- import react from '@vitejs/plugin-react';
3
- import { federation } from '@module-federation/vite';
4
- import { apiPlugin } from './vite-plugin-api.js';
5
-
6
- // Determine remote URL based on environment
7
- function getRemoteUrl(env: string): string {
8
- switch (env) {
9
- case 'production':
10
- return 'https://www.rayburst.app';
11
- case 'staging':
12
- return 'https://dev.rayburst.app';
13
- case 'development':
14
- default:
15
- return 'http://localhost:3000';
16
- }
17
- }
18
-
19
- export default defineConfig(() => {
20
- const environment = process.env.NODE_ENV || 'development';
21
- const remoteUrl = getRemoteUrl(environment);
22
-
23
- console.log(`🔧 Building CLI for environment: ${environment}`);
24
- console.log(`📡 Remote URL: ${remoteUrl}`);
25
-
26
- return {
27
- plugins: [
28
- apiPlugin(),
29
- react(),
30
- federation({
31
- name: 'rayburstCli',
32
- remotes: {
33
- rayburstApp: {
34
- type: 'module',
35
- name: 'rayburstApp',
36
- entry: `${remoteUrl}/remoteEntry.js?t=${Date.now()}`,
37
- },
38
- },
39
- shared: {
40
- react: {
41
- singleton: true,
42
- requiredVersion: '^19.0.0',
43
- },
44
- 'react-dom': {
45
- singleton: true,
46
- requiredVersion: '^19.0.0',
47
- },
48
- },
49
- }),
50
- ],
51
-
52
- build: {
53
- modulePreload: false,
54
- target: 'esnext',
55
- minify: false,
56
- cssCodeSplit: false,
57
- outDir: 'dist',
58
- },
59
-
60
- server: {
61
- port: 3105,
62
- strictPort: true,
63
- host: true,
64
- },
65
-
66
- preview: {
67
- port: 3105,
68
- strictPort: true,
69
- host: true,
70
- },
71
- };
72
- });