projax 0.1.29
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/LINKING.md +86 -0
- package/README.md +141 -0
- package/dist/core/database.d.ts +66 -0
- package/dist/core/database.js +312 -0
- package/dist/core/detector.d.ts +9 -0
- package/dist/core/detector.js +149 -0
- package/dist/core/index.d.ts +11 -0
- package/dist/core/index.js +43 -0
- package/dist/core/scanner.d.ts +8 -0
- package/dist/core/scanner.js +114 -0
- package/dist/electron/main.d.ts +1 -0
- package/dist/electron/main.js +310 -0
- package/dist/electron/port-extractor.d.ts +9 -0
- package/dist/electron/port-extractor.js +351 -0
- package/dist/electron/port-scanner.d.ts +13 -0
- package/dist/electron/port-scanner.js +93 -0
- package/dist/electron/port-utils.d.ts +21 -0
- package/dist/electron/port-utils.js +200 -0
- package/dist/electron/preload.d.ts +49 -0
- package/dist/electron/preload.js +21 -0
- package/dist/electron/renderer/assets/index-BZ6USRnW.js +42 -0
- package/dist/electron/renderer/assets/index-DNtxfrZe.js +42 -0
- package/dist/electron/renderer/assets/index-khk3K-qG.css +1 -0
- package/dist/electron/renderer/index.html +15 -0
- package/dist/electron/script-runner.d.ts +40 -0
- package/dist/electron/script-runner.js +651 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +971 -0
- package/dist/port-extractor.d.ts +9 -0
- package/dist/port-extractor.js +351 -0
- package/dist/port-scanner.d.ts +13 -0
- package/dist/port-scanner.js +93 -0
- package/dist/port-utils.d.ts +21 -0
- package/dist/port-utils.js +200 -0
- package/dist/script-runner.d.ts +40 -0
- package/dist/script-runner.js +651 -0
- package/package.json +40 -0
- package/rebuild-sqlite.js +82 -0
package/LINKING.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Linking the CLI Locally
|
|
2
|
+
|
|
3
|
+
The CLI can be linked locally using `npm link`. This allows you to use the `prx` command globally while developing.
|
|
4
|
+
|
|
5
|
+
## Option 1: Standard npm link (if you have permissions)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd packages/cli
|
|
9
|
+
npm link
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
This will create a global symlink. You can then use `prx` from anywhere:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
prx --help
|
|
16
|
+
prx add /path/to/project
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Option 2: If you get permission errors
|
|
20
|
+
|
|
21
|
+
If you get `EACCES` permission errors, you have a few options:
|
|
22
|
+
|
|
23
|
+
### A. Use a user-writable npm prefix (Recommended)
|
|
24
|
+
|
|
25
|
+
Configure npm to use a directory in your home folder:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
mkdir -p ~/.npm-global
|
|
29
|
+
npm config set prefix '~/.npm-global'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Add to your `~/.zshrc` or `~/.bashrc`:
|
|
33
|
+
```bash
|
|
34
|
+
export PATH=~/.npm-global/bin:$PATH
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then reload your shell and link:
|
|
38
|
+
```bash
|
|
39
|
+
source ~/.zshrc # or source ~/.bashrc
|
|
40
|
+
cd packages/cli
|
|
41
|
+
npm link
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### B. Use sudo (Not recommended, but works)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd packages/cli
|
|
48
|
+
sudo npm link
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### C. Use npx to run directly
|
|
52
|
+
|
|
53
|
+
You can also run the CLI directly without linking:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# From the project root
|
|
57
|
+
npx --package=./packages/cli prx --help
|
|
58
|
+
|
|
59
|
+
# Or use node directly
|
|
60
|
+
node packages/cli/dist/index.js --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Unlinking
|
|
64
|
+
|
|
65
|
+
To remove the global link:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm unlink -g prx-dashboard
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or from the CLI package directory:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd packages/cli
|
|
75
|
+
npm unlink
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Testing the Link
|
|
79
|
+
|
|
80
|
+
After linking, test it:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
prx --help
|
|
84
|
+
prx list
|
|
85
|
+
```
|
|
86
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# projax CLI
|
|
2
|
+
|
|
3
|
+
Command-line interface for projax - a project management dashboard for tracking local development projects.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g projax
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
### `prx add [path]`
|
|
14
|
+
|
|
15
|
+
Add a project to the dashboard. If no path is provided, you'll be prompted to enter one.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
prx add /path/to/project
|
|
19
|
+
prx add # Interactive mode
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### `prx list`
|
|
23
|
+
|
|
24
|
+
List all tracked projects.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
prx list
|
|
28
|
+
prx list --verbose # Show detailed information
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### `prx scan [project]`
|
|
32
|
+
|
|
33
|
+
Scan projects for test files. If no project is specified, all projects are scanned.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
prx scan # Scan all projects
|
|
37
|
+
prx scan 1 # Scan project with ID 1
|
|
38
|
+
prx scan my-project # Scan project named "my-project"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `prx remove <project>`
|
|
42
|
+
|
|
43
|
+
Remove a project from the dashboard.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
prx remove 1
|
|
47
|
+
prx remove my-project
|
|
48
|
+
prx remove my-project --force # Skip confirmation
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### `prx cd [project]`
|
|
52
|
+
|
|
53
|
+
Get the path to a project directory for quick navigation. Use with command substitution to change directories.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
cd $(prx cd 1) # Change to project with ID 1
|
|
57
|
+
cd $(prx cd my-project) # Change to project named "my-project"
|
|
58
|
+
prx cd # Interactive selection
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Tip:** For even easier navigation, add this to your shell config (`~/.zshrc` or `~/.bashrc`):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
prxcd() {
|
|
65
|
+
cd $(prx cd "$@")
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Then you can simply use: `prxcd 1` or `prxcd my-project`
|
|
70
|
+
|
|
71
|
+
### `prx <project> <script> [args...]`
|
|
72
|
+
|
|
73
|
+
Run a script from a project's configuration file. Supports multiple project types:
|
|
74
|
+
|
|
75
|
+
- **Node.js**: Runs scripts from `package.json` using `npm run`
|
|
76
|
+
- **Python**: Runs scripts from `pyproject.toml` (supports Poetry)
|
|
77
|
+
- **Rust**: Runs common `cargo` commands (build, run, test, etc.)
|
|
78
|
+
- **Go**: Runs common `go` commands or Makefile targets
|
|
79
|
+
- **Makefile**: Runs Makefile targets
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
prx 1 dev # Run "dev" script from project ID 1
|
|
83
|
+
prx my-project build # Run "build" script from "my-project"
|
|
84
|
+
prx 2 test --watch # Run "test" script with --watch flag
|
|
85
|
+
prx api-server start --port 3000 # Run "start" script with arguments
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `prx scripts [project]`
|
|
89
|
+
|
|
90
|
+
List all available scripts for a project.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
prx scripts # Interactive project selection
|
|
94
|
+
prx scripts 1 # List scripts for project ID 1
|
|
95
|
+
prx scripts my-project # List scripts for "my-project"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `prx web`
|
|
99
|
+
|
|
100
|
+
Start the Electron web interface.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
prx web
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# Add multiple projects
|
|
110
|
+
prx add ~/projects/api-server
|
|
111
|
+
prx add ~/projects/frontend-app
|
|
112
|
+
prx add ~/projects/mobile-app
|
|
113
|
+
|
|
114
|
+
# List all projects
|
|
115
|
+
prx list
|
|
116
|
+
|
|
117
|
+
# Scan all projects for tests
|
|
118
|
+
prx scan
|
|
119
|
+
|
|
120
|
+
# View detailed project information
|
|
121
|
+
prx list --verbose
|
|
122
|
+
|
|
123
|
+
# Remove a project
|
|
124
|
+
prx remove api-server
|
|
125
|
+
|
|
126
|
+
# Quickly navigate to a project
|
|
127
|
+
cd $(prx cd api-server)
|
|
128
|
+
|
|
129
|
+
# Run scripts from projects
|
|
130
|
+
prx api-server dev
|
|
131
|
+
prx 1 build
|
|
132
|
+
prx frontend-app test --watch
|
|
133
|
+
|
|
134
|
+
# List available scripts
|
|
135
|
+
prx scripts api-server
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Database
|
|
139
|
+
|
|
140
|
+
The CLI uses a shared SQLite database located at `~/.projax/dashboard.db`. This database is shared with the Electron web interface.
|
|
141
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
export interface Project {
|
|
3
|
+
id: number;
|
|
4
|
+
name: string;
|
|
5
|
+
path: string;
|
|
6
|
+
last_scanned: number | null;
|
|
7
|
+
created_at: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Test {
|
|
10
|
+
id: number;
|
|
11
|
+
project_id: number;
|
|
12
|
+
file_path: string;
|
|
13
|
+
framework: string | null;
|
|
14
|
+
status: string | null;
|
|
15
|
+
last_run: number | null;
|
|
16
|
+
created_at: number;
|
|
17
|
+
}
|
|
18
|
+
export interface JenkinsJob {
|
|
19
|
+
id: number;
|
|
20
|
+
project_id: number;
|
|
21
|
+
job_name: string;
|
|
22
|
+
job_url: string;
|
|
23
|
+
last_build_status: string | null;
|
|
24
|
+
last_build_number: number | null;
|
|
25
|
+
last_updated: number | null;
|
|
26
|
+
created_at: number;
|
|
27
|
+
}
|
|
28
|
+
export interface ProjectPort {
|
|
29
|
+
id: number;
|
|
30
|
+
project_id: number;
|
|
31
|
+
port: number;
|
|
32
|
+
script_name: string | null;
|
|
33
|
+
config_source: string;
|
|
34
|
+
last_detected: number;
|
|
35
|
+
created_at: number;
|
|
36
|
+
}
|
|
37
|
+
declare class DatabaseManager {
|
|
38
|
+
private db;
|
|
39
|
+
private dbPath;
|
|
40
|
+
constructor();
|
|
41
|
+
getDatabase(): Database.Database;
|
|
42
|
+
private initializeSchema;
|
|
43
|
+
addProject(name: string, projectPath: string): Project;
|
|
44
|
+
getProject(id: number): Project | null;
|
|
45
|
+
getProjectByPath(projectPath: string): Project | null;
|
|
46
|
+
getAllProjects(): Project[];
|
|
47
|
+
updateProjectLastScanned(id: number): void;
|
|
48
|
+
updateProjectName(id: number, newName: string): Project;
|
|
49
|
+
removeProject(id: number): void;
|
|
50
|
+
addTest(projectId: number, filePath: string, framework?: string | null): Test;
|
|
51
|
+
getTest(id: number): Test | null;
|
|
52
|
+
getTestsByProject(projectId: number): Test[];
|
|
53
|
+
removeTestsByProject(projectId: number): void;
|
|
54
|
+
addJenkinsJob(projectId: number, jobName: string, jobUrl: string): JenkinsJob;
|
|
55
|
+
getJenkinsJob(id: number): JenkinsJob | null;
|
|
56
|
+
getJenkinsJobsByProject(projectId: number): JenkinsJob[];
|
|
57
|
+
addProjectPort(projectId: number, port: number, configSource: string, scriptName?: string | null): ProjectPort;
|
|
58
|
+
getProjectPort(id: number): ProjectPort | null;
|
|
59
|
+
getProjectPorts(projectId: number): ProjectPort[];
|
|
60
|
+
getProjectPortsByScript(projectId: number, scriptName: string): ProjectPort[];
|
|
61
|
+
removeProjectPorts(projectId: number): void;
|
|
62
|
+
updateProjectPortLastDetected(projectId: number, port: number, scriptName: string | null): void;
|
|
63
|
+
close(): void;
|
|
64
|
+
}
|
|
65
|
+
export declare function getDatabaseManager(): DatabaseManager;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.getDatabaseManager = getDatabaseManager;
|
|
40
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const os = __importStar(require("os"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
class DatabaseManager {
|
|
45
|
+
db = null;
|
|
46
|
+
dbPath;
|
|
47
|
+
constructor() {
|
|
48
|
+
const dataDir = path.join(os.homedir(), '.projax');
|
|
49
|
+
const oldDataDir = path.join(os.homedir(), '.vids-dashboard');
|
|
50
|
+
const oldDbPath = path.join(oldDataDir, 'dashboard.db');
|
|
51
|
+
// Migrate data from old directory if it exists
|
|
52
|
+
const newDbPath = path.join(dataDir, 'dashboard.db');
|
|
53
|
+
if (fs.existsSync(oldDataDir) && fs.existsSync(oldDbPath) && !fs.existsSync(newDbPath)) {
|
|
54
|
+
console.log('Migrating data from ~/.vids-dashboard to ~/.projax...');
|
|
55
|
+
try {
|
|
56
|
+
// Create new directory
|
|
57
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
58
|
+
// Copy database file
|
|
59
|
+
fs.copyFileSync(oldDbPath, newDbPath);
|
|
60
|
+
console.log('✓ Database migrated successfully');
|
|
61
|
+
// Copy other files (processes.json, logs directory)
|
|
62
|
+
const oldProcessesFile = path.join(oldDataDir, 'processes.json');
|
|
63
|
+
if (fs.existsSync(oldProcessesFile)) {
|
|
64
|
+
const newProcessesFile = path.join(dataDir, 'processes.json');
|
|
65
|
+
fs.copyFileSync(oldProcessesFile, newProcessesFile);
|
|
66
|
+
}
|
|
67
|
+
const oldLogsDir = path.join(oldDataDir, 'logs');
|
|
68
|
+
if (fs.existsSync(oldLogsDir)) {
|
|
69
|
+
const newLogsDir = path.join(dataDir, 'logs');
|
|
70
|
+
fs.mkdirSync(newLogsDir, { recursive: true });
|
|
71
|
+
// Copy all log files
|
|
72
|
+
const logFiles = fs.readdirSync(oldLogsDir);
|
|
73
|
+
for (const file of logFiles) {
|
|
74
|
+
const oldLogPath = path.join(oldLogsDir, file);
|
|
75
|
+
const newLogPath = path.join(newLogsDir, file);
|
|
76
|
+
if (fs.statSync(oldLogPath).isFile()) {
|
|
77
|
+
fs.copyFileSync(oldLogPath, newLogPath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
console.log('✓ Migration complete. You can safely remove ~/.vids-dashboard if desired.');
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error('⚠️ Migration failed:', error);
|
|
85
|
+
console.error(' Continuing with new directory. Old data remains in ~/.vids-dashboard');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (!fs.existsSync(dataDir)) {
|
|
89
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
this.dbPath = path.join(dataDir, 'dashboard.db');
|
|
92
|
+
}
|
|
93
|
+
getDatabase() {
|
|
94
|
+
if (!this.db) {
|
|
95
|
+
this.db = new better_sqlite3_1.default(this.dbPath);
|
|
96
|
+
this.db.pragma('journal_mode = WAL');
|
|
97
|
+
this.initializeSchema();
|
|
98
|
+
}
|
|
99
|
+
return this.db;
|
|
100
|
+
}
|
|
101
|
+
initializeSchema() {
|
|
102
|
+
if (!this.db)
|
|
103
|
+
return;
|
|
104
|
+
// Projects table
|
|
105
|
+
this.db.exec(`
|
|
106
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
107
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
108
|
+
name TEXT NOT NULL,
|
|
109
|
+
path TEXT NOT NULL UNIQUE,
|
|
110
|
+
last_scanned INTEGER,
|
|
111
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
|
112
|
+
)
|
|
113
|
+
`);
|
|
114
|
+
// Tests table
|
|
115
|
+
this.db.exec(`
|
|
116
|
+
CREATE TABLE IF NOT EXISTS tests (
|
|
117
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
118
|
+
project_id INTEGER NOT NULL,
|
|
119
|
+
file_path TEXT NOT NULL,
|
|
120
|
+
framework TEXT,
|
|
121
|
+
status TEXT,
|
|
122
|
+
last_run INTEGER,
|
|
123
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
124
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
125
|
+
UNIQUE(project_id, file_path)
|
|
126
|
+
)
|
|
127
|
+
`);
|
|
128
|
+
// Jenkins jobs table (for future integration)
|
|
129
|
+
this.db.exec(`
|
|
130
|
+
CREATE TABLE IF NOT EXISTS jenkins_jobs (
|
|
131
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
132
|
+
project_id INTEGER NOT NULL,
|
|
133
|
+
job_name TEXT NOT NULL,
|
|
134
|
+
job_url TEXT NOT NULL,
|
|
135
|
+
last_build_status TEXT,
|
|
136
|
+
last_build_number INTEGER,
|
|
137
|
+
last_updated INTEGER,
|
|
138
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
139
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
140
|
+
UNIQUE(project_id, job_name)
|
|
141
|
+
)
|
|
142
|
+
`);
|
|
143
|
+
// Project ports table
|
|
144
|
+
this.db.exec(`
|
|
145
|
+
CREATE TABLE IF NOT EXISTS project_ports (
|
|
146
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
147
|
+
project_id INTEGER NOT NULL,
|
|
148
|
+
port INTEGER NOT NULL,
|
|
149
|
+
script_name TEXT,
|
|
150
|
+
config_source TEXT NOT NULL,
|
|
151
|
+
last_detected INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
152
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
153
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
154
|
+
UNIQUE(project_id, port, script_name)
|
|
155
|
+
)
|
|
156
|
+
`);
|
|
157
|
+
// Create indexes
|
|
158
|
+
this.db.exec(`
|
|
159
|
+
CREATE INDEX IF NOT EXISTS idx_tests_project_id ON tests(project_id);
|
|
160
|
+
CREATE INDEX IF NOT EXISTS idx_jenkins_jobs_project_id ON jenkins_jobs(project_id);
|
|
161
|
+
CREATE INDEX IF NOT EXISTS idx_project_ports_project_id ON project_ports(project_id);
|
|
162
|
+
CREATE INDEX IF NOT EXISTS idx_project_ports_port ON project_ports(port);
|
|
163
|
+
`);
|
|
164
|
+
}
|
|
165
|
+
// Project operations
|
|
166
|
+
addProject(name, projectPath) {
|
|
167
|
+
const db = this.getDatabase();
|
|
168
|
+
const stmt = db.prepare(`
|
|
169
|
+
INSERT INTO projects (name, path)
|
|
170
|
+
VALUES (?, ?)
|
|
171
|
+
`);
|
|
172
|
+
const result = stmt.run(name, projectPath);
|
|
173
|
+
return this.getProject(result.lastInsertRowid);
|
|
174
|
+
}
|
|
175
|
+
getProject(id) {
|
|
176
|
+
const db = this.getDatabase();
|
|
177
|
+
const stmt = db.prepare('SELECT * FROM projects WHERE id = ?');
|
|
178
|
+
return stmt.get(id);
|
|
179
|
+
}
|
|
180
|
+
getProjectByPath(projectPath) {
|
|
181
|
+
const db = this.getDatabase();
|
|
182
|
+
const stmt = db.prepare('SELECT * FROM projects WHERE path = ?');
|
|
183
|
+
return stmt.get(projectPath);
|
|
184
|
+
}
|
|
185
|
+
getAllProjects() {
|
|
186
|
+
const db = this.getDatabase();
|
|
187
|
+
const stmt = db.prepare('SELECT * FROM projects ORDER BY id');
|
|
188
|
+
return stmt.all();
|
|
189
|
+
}
|
|
190
|
+
updateProjectLastScanned(id) {
|
|
191
|
+
const db = this.getDatabase();
|
|
192
|
+
const stmt = db.prepare('UPDATE projects SET last_scanned = strftime("%s", "now") WHERE id = ?');
|
|
193
|
+
stmt.run(id);
|
|
194
|
+
}
|
|
195
|
+
updateProjectName(id, newName) {
|
|
196
|
+
const db = this.getDatabase();
|
|
197
|
+
const stmt = db.prepare('UPDATE projects SET name = ? WHERE id = ?');
|
|
198
|
+
stmt.run(newName, id);
|
|
199
|
+
const updated = this.getProject(id);
|
|
200
|
+
if (!updated) {
|
|
201
|
+
throw new Error('Failed to update project name');
|
|
202
|
+
}
|
|
203
|
+
return updated;
|
|
204
|
+
}
|
|
205
|
+
removeProject(id) {
|
|
206
|
+
const db = this.getDatabase();
|
|
207
|
+
const stmt = db.prepare('DELETE FROM projects WHERE id = ?');
|
|
208
|
+
stmt.run(id);
|
|
209
|
+
}
|
|
210
|
+
// Test operations
|
|
211
|
+
addTest(projectId, filePath, framework = null) {
|
|
212
|
+
const db = this.getDatabase();
|
|
213
|
+
const stmt = db.prepare(`
|
|
214
|
+
INSERT OR REPLACE INTO tests (project_id, file_path, framework)
|
|
215
|
+
VALUES (?, ?, ?)
|
|
216
|
+
`);
|
|
217
|
+
const result = stmt.run(projectId, filePath, framework);
|
|
218
|
+
return this.getTest(result.lastInsertRowid);
|
|
219
|
+
}
|
|
220
|
+
getTest(id) {
|
|
221
|
+
const db = this.getDatabase();
|
|
222
|
+
const stmt = db.prepare('SELECT * FROM tests WHERE id = ?');
|
|
223
|
+
return stmt.get(id);
|
|
224
|
+
}
|
|
225
|
+
getTestsByProject(projectId) {
|
|
226
|
+
const db = this.getDatabase();
|
|
227
|
+
const stmt = db.prepare('SELECT * FROM tests WHERE project_id = ? ORDER BY file_path');
|
|
228
|
+
return stmt.all(projectId);
|
|
229
|
+
}
|
|
230
|
+
removeTestsByProject(projectId) {
|
|
231
|
+
const db = this.getDatabase();
|
|
232
|
+
const stmt = db.prepare('DELETE FROM tests WHERE project_id = ?');
|
|
233
|
+
stmt.run(projectId);
|
|
234
|
+
}
|
|
235
|
+
// Jenkins operations (for future use)
|
|
236
|
+
addJenkinsJob(projectId, jobName, jobUrl) {
|
|
237
|
+
const db = this.getDatabase();
|
|
238
|
+
const stmt = db.prepare(`
|
|
239
|
+
INSERT OR REPLACE INTO jenkins_jobs (project_id, job_name, job_url)
|
|
240
|
+
VALUES (?, ?, ?)
|
|
241
|
+
`);
|
|
242
|
+
const result = stmt.run(projectId, jobName, jobUrl);
|
|
243
|
+
return this.getJenkinsJob(result.lastInsertRowid);
|
|
244
|
+
}
|
|
245
|
+
getJenkinsJob(id) {
|
|
246
|
+
const db = this.getDatabase();
|
|
247
|
+
const stmt = db.prepare('SELECT * FROM jenkins_jobs WHERE id = ?');
|
|
248
|
+
return stmt.get(id);
|
|
249
|
+
}
|
|
250
|
+
getJenkinsJobsByProject(projectId) {
|
|
251
|
+
const db = this.getDatabase();
|
|
252
|
+
const stmt = db.prepare('SELECT * FROM jenkins_jobs WHERE project_id = ? ORDER BY job_name');
|
|
253
|
+
return stmt.all(projectId);
|
|
254
|
+
}
|
|
255
|
+
// Project port operations
|
|
256
|
+
addProjectPort(projectId, port, configSource, scriptName = null) {
|
|
257
|
+
const db = this.getDatabase();
|
|
258
|
+
const stmt = db.prepare(`
|
|
259
|
+
INSERT OR REPLACE INTO project_ports (project_id, port, script_name, config_source, last_detected)
|
|
260
|
+
VALUES (?, ?, ?, ?, strftime('%s', 'now'))
|
|
261
|
+
`);
|
|
262
|
+
const result = stmt.run(projectId, port, scriptName, configSource);
|
|
263
|
+
const portRecord = this.getProjectPort(result.lastInsertRowid);
|
|
264
|
+
if (!portRecord) {
|
|
265
|
+
throw new Error('Failed to create project port record');
|
|
266
|
+
}
|
|
267
|
+
return portRecord;
|
|
268
|
+
}
|
|
269
|
+
getProjectPort(id) {
|
|
270
|
+
const db = this.getDatabase();
|
|
271
|
+
const stmt = db.prepare('SELECT * FROM project_ports WHERE id = ?');
|
|
272
|
+
return stmt.get(id);
|
|
273
|
+
}
|
|
274
|
+
getProjectPorts(projectId) {
|
|
275
|
+
const db = this.getDatabase();
|
|
276
|
+
const stmt = db.prepare('SELECT * FROM project_ports WHERE project_id = ? ORDER BY port');
|
|
277
|
+
return stmt.all(projectId);
|
|
278
|
+
}
|
|
279
|
+
getProjectPortsByScript(projectId, scriptName) {
|
|
280
|
+
const db = this.getDatabase();
|
|
281
|
+
const stmt = db.prepare('SELECT * FROM project_ports WHERE project_id = ? AND script_name = ? ORDER BY port');
|
|
282
|
+
return stmt.all(projectId, scriptName);
|
|
283
|
+
}
|
|
284
|
+
removeProjectPorts(projectId) {
|
|
285
|
+
const db = this.getDatabase();
|
|
286
|
+
const stmt = db.prepare('DELETE FROM project_ports WHERE project_id = ?');
|
|
287
|
+
stmt.run(projectId);
|
|
288
|
+
}
|
|
289
|
+
updateProjectPortLastDetected(projectId, port, scriptName) {
|
|
290
|
+
const db = this.getDatabase();
|
|
291
|
+
const stmt = db.prepare(`
|
|
292
|
+
UPDATE project_ports
|
|
293
|
+
SET last_detected = strftime('%s', 'now')
|
|
294
|
+
WHERE project_id = ? AND port = ? AND (script_name = ? OR (script_name IS NULL AND ? IS NULL))
|
|
295
|
+
`);
|
|
296
|
+
stmt.run(projectId, port, scriptName, scriptName);
|
|
297
|
+
}
|
|
298
|
+
close() {
|
|
299
|
+
if (this.db) {
|
|
300
|
+
this.db.close();
|
|
301
|
+
this.db = null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// Singleton instance
|
|
306
|
+
let dbManager = null;
|
|
307
|
+
function getDatabaseManager() {
|
|
308
|
+
if (!dbManager) {
|
|
309
|
+
dbManager = new DatabaseManager();
|
|
310
|
+
}
|
|
311
|
+
return dbManager;
|
|
312
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface TestFramework {
|
|
2
|
+
name: string;
|
|
3
|
+
configFiles: string[];
|
|
4
|
+
testPatterns: RegExp[];
|
|
5
|
+
testDirs: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare const FRAMEWORKS: TestFramework[];
|
|
8
|
+
export declare function detectTestFramework(projectPath: string): string | null;
|
|
9
|
+
export declare function isTestFile(filePath: string, detectedFramework?: string | null): boolean;
|