cobolx 1.0.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/.devcontainer/devcontainer.json +26 -0
- package/.dockerignore +4 -0
- package/.github/workflows/ci.yml +157 -0
- package/Cargo.lock +2245 -0
- package/Cargo.toml +39 -0
- package/bin/check-update.js +44 -0
- package/bin/cobolx.js +81 -0
- package/docker-compose.yml +33 -0
- package/dockerfile +18 -0
- package/dockerfile.test +39 -0
- package/package.json +27 -0
- package/scripts/install.js +145 -0
- package/src/agent/client.rs +1345 -0
- package/src/agent.rs +1 -0
- package/src/cobol/copybook.rs +71 -0
- package/src/cobol/data_parser.rs +290 -0
- package/src/cobol/indexer.rs +256 -0
- package/src/cobol/layout.rs +278 -0
- package/src/cobol/lexer.rs +135 -0
- package/src/cobol/model.rs +196 -0
- package/src/cobol/scanner.rs +72 -0
- package/src/cobol/source_parser.rs +91 -0
- package/src/cobol.rs +8 -0
- package/src/config/config.rs +64 -0
- package/src/config.rs +3 -0
- package/src/lib.rs +6 -0
- package/src/main.rs +20 -0
- package/src/memory/files.rs +155 -0
- package/src/memory/store.rs +406 -0
- package/src/memory.rs +5 -0
- package/src/ui/draw.rs +519 -0
- package/src/ui/tui.rs +812 -0
- package/src/ui.rs +2 -0
- package/tests/indexer_tests.rs +192 -0
- package/tests/memory_store_tests.rs +21 -0
- package/tests/project_files_tests.rs +72 -0
- package/tests/sandbox_tests.rs +178 -0
package/Cargo.toml
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "rdo"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
|
|
6
|
+
[dependencies]
|
|
7
|
+
clap = { version = "4.5", features = ["derive"] }
|
|
8
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
9
|
+
serde_json = "1.0"
|
|
10
|
+
colored = "2.1"
|
|
11
|
+
chrono = { version = "0.4", features = ["serde"] }
|
|
12
|
+
ratatui = "0.26"
|
|
13
|
+
crossterm = "0.27"
|
|
14
|
+
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] }
|
|
15
|
+
tokio = { version = "1", features = ["full"] }
|
|
16
|
+
directories = "5.0"
|
|
17
|
+
futures-util = "0.3"
|
|
18
|
+
rusqlite = { version = "0.40.1", features = ["bundled"] }
|
|
19
|
+
|
|
20
|
+
[dev-dependencies]
|
|
21
|
+
tempfile = "3.8"
|
|
22
|
+
|
|
23
|
+
[lints.rust]
|
|
24
|
+
unused_variables = "warn"
|
|
25
|
+
dead_code = "allow"
|
|
26
|
+
|
|
27
|
+
[lints.clippy]
|
|
28
|
+
collapsible_if = "allow"
|
|
29
|
+
new_without_default = "allow"
|
|
30
|
+
implicit_saturating_sub = "allow"
|
|
31
|
+
manual_div_ceil = "allow"
|
|
32
|
+
if_same_then_else = "allow"
|
|
33
|
+
module_inception = "allow"
|
|
34
|
+
unnecessary_map_or = "allow"
|
|
35
|
+
needless_range_loop = "allow"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const https = require('https');
|
|
4
|
+
|
|
5
|
+
const pkg = require('../package.json');
|
|
6
|
+
const noticeFile = path.join(__dirname, '.update-notice');
|
|
7
|
+
|
|
8
|
+
const options = {
|
|
9
|
+
hostname: 'registry.npmjs.org',
|
|
10
|
+
path: `/${pkg.name}/latest`,
|
|
11
|
+
timeout: 3000,
|
|
12
|
+
headers: {
|
|
13
|
+
'User-Agent': 'cobolx-cli-update-checker'
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const req = https.get(options, (res) => {
|
|
18
|
+
if (res.statusCode !== 200) return;
|
|
19
|
+
let data = '';
|
|
20
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
21
|
+
res.on('end', () => {
|
|
22
|
+
try {
|
|
23
|
+
const info = JSON.parse(data);
|
|
24
|
+
const latest = info.version;
|
|
25
|
+
if (latest && latest !== pkg.version) {
|
|
26
|
+
const msg = [
|
|
27
|
+
'\x1b[33m---------------------------------------------------------\x1b[0m',
|
|
28
|
+
` 💡 \x1b[36mUpdate available:\x1b[0m \x1b[32m${latest}\x1b[0m (current: ${pkg.version})`,
|
|
29
|
+
` Run \x1b[33mnpm update -g ${pkg.name}\x1b[0m to update!`,
|
|
30
|
+
'\x1b[33m---------------------------------------------------------\x1b[0m'
|
|
31
|
+
].join('\n');
|
|
32
|
+
fs.writeFileSync(noticeFile, msg, 'utf8');
|
|
33
|
+
}
|
|
34
|
+
} catch (e) {
|
|
35
|
+
// ignore
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
req.on('error', () => {
|
|
41
|
+
// ignore
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
req.end();
|
package/bin/cobolx.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
const isWin = process.platform === 'win32';
|
|
8
|
+
const binName = isWin ? 'cobolx.exe' : 'cobolx';
|
|
9
|
+
const binaryPath = path.join(__dirname, binName);
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(binaryPath)) {
|
|
12
|
+
console.error(`Error: Native binary not found at ${binaryPath}`);
|
|
13
|
+
console.error('Please try reinstalling the package: npm install -g cobolx');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 1. Check for pending update notification from previous background checks
|
|
18
|
+
const noticeFile = path.join(__dirname, '.update-notice');
|
|
19
|
+
if (fs.existsSync(noticeFile)) {
|
|
20
|
+
try {
|
|
21
|
+
const notice = fs.readFileSync(noticeFile, 'utf8').trim();
|
|
22
|
+
if (notice) {
|
|
23
|
+
console.log('\n' + notice);
|
|
24
|
+
}
|
|
25
|
+
fs.unlinkSync(noticeFile);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
// ignore
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 2. Start the native binary child process
|
|
32
|
+
const args = process.argv.slice(2);
|
|
33
|
+
const child = spawn(binaryPath, args, { stdio: 'inherit' });
|
|
34
|
+
|
|
35
|
+
child.on('error', (err) => {
|
|
36
|
+
console.error('Failed to start the native binary:', err);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
child.on('close', (code) => {
|
|
41
|
+
// 3. Trigger background update check
|
|
42
|
+
triggerBackgroundUpdateCheck();
|
|
43
|
+
process.exit(code ?? 0);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
function triggerBackgroundUpdateCheck() {
|
|
47
|
+
try {
|
|
48
|
+
const pkg = require('../package.json');
|
|
49
|
+
const cacheFile = path.join(__dirname, '.last-update-check');
|
|
50
|
+
const CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
|
|
53
|
+
let lastCheck = 0;
|
|
54
|
+
if (fs.existsSync(cacheFile)) {
|
|
55
|
+
lastCheck = parseInt(fs.readFileSync(cacheFile, 'utf8'), 10) || 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (now - lastCheck < CHECK_INTERVAL) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Write new timestamp immediately to throttle requests
|
|
63
|
+
fs.writeFileSync(cacheFile, now.toString(), 'utf8');
|
|
64
|
+
|
|
65
|
+
// Spawn a detached background process to check for updates
|
|
66
|
+
const checkerScript = path.join(__dirname, 'check-update.js');
|
|
67
|
+
|
|
68
|
+
// Ignore outputs or pipe to a log file
|
|
69
|
+
const logFile = path.join(__dirname, '.checker-log');
|
|
70
|
+
const out = fs.openSync(logFile, 'a');
|
|
71
|
+
|
|
72
|
+
const child = spawn(process.execPath, [checkerScript], {
|
|
73
|
+
detached: true,
|
|
74
|
+
stdio: ['ignore', out, out]
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
child.unref();
|
|
78
|
+
} catch (err) {
|
|
79
|
+
// ignore
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
services:
|
|
2
|
+
dev:
|
|
3
|
+
build:
|
|
4
|
+
context: .
|
|
5
|
+
dockerfile: dockerfile
|
|
6
|
+
command: sleep infinity
|
|
7
|
+
stdin_open: true
|
|
8
|
+
tty: true
|
|
9
|
+
environment:
|
|
10
|
+
CARGO_TARGET_DIR: /workspaces/cobolX/target
|
|
11
|
+
RUST_BACKTRACE: "1"
|
|
12
|
+
volumes:
|
|
13
|
+
- .:/workspaces/cobolX:cached
|
|
14
|
+
- cargo-registry:/usr/local/cargo/registry
|
|
15
|
+
- cargo-git:/usr/local/cargo/git
|
|
16
|
+
- target-cache:/workspaces/cobolX/target
|
|
17
|
+
|
|
18
|
+
test:
|
|
19
|
+
profiles:
|
|
20
|
+
- test
|
|
21
|
+
build:
|
|
22
|
+
context: .
|
|
23
|
+
dockerfile: dockerfile.test
|
|
24
|
+
stdin_open: true
|
|
25
|
+
tty: true
|
|
26
|
+
environment:
|
|
27
|
+
RUST_BACKTRACE: "1"
|
|
28
|
+
TERM: xterm-256color
|
|
29
|
+
|
|
30
|
+
volumes:
|
|
31
|
+
cargo-registry:
|
|
32
|
+
cargo-git:
|
|
33
|
+
target-cache:
|
package/dockerfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
|
|
3
|
+
FROM mcr.microsoft.com/devcontainers/rust:1-bookworm
|
|
4
|
+
|
|
5
|
+
RUN apt-get update \
|
|
6
|
+
&& apt-get install -y --no-install-recommends \
|
|
7
|
+
ca-certificates \
|
|
8
|
+
git \
|
|
9
|
+
pkg-config \
|
|
10
|
+
libssl-dev \
|
|
11
|
+
gdb \
|
|
12
|
+
lldb \
|
|
13
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
14
|
+
|
|
15
|
+
RUN mkdir -p /workspaces/cobolX/target \
|
|
16
|
+
&& chown -R vscode:vscode /workspaces
|
|
17
|
+
|
|
18
|
+
WORKDIR /workspaces/cobolX
|
package/dockerfile.test
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
|
|
3
|
+
FROM rust:1-bookworm AS build
|
|
4
|
+
|
|
5
|
+
RUN apt-get update \
|
|
6
|
+
&& apt-get install -y --no-install-recommends \
|
|
7
|
+
ca-certificates \
|
|
8
|
+
pkg-config \
|
|
9
|
+
libssl-dev \
|
|
10
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
11
|
+
|
|
12
|
+
WORKDIR /app
|
|
13
|
+
|
|
14
|
+
COPY Cargo.toml Cargo.lock ./
|
|
15
|
+
COPY src ./src
|
|
16
|
+
COPY tests ./tests
|
|
17
|
+
|
|
18
|
+
ENV CARGO_TARGET_DIR=/app/target \
|
|
19
|
+
RUST_BACKTRACE=1
|
|
20
|
+
|
|
21
|
+
RUN cargo test --locked --release
|
|
22
|
+
RUN cargo build --locked --release
|
|
23
|
+
|
|
24
|
+
FROM debian:bookworm-slim AS runtime
|
|
25
|
+
|
|
26
|
+
RUN apt-get update \
|
|
27
|
+
&& apt-get install -y --no-install-recommends \
|
|
28
|
+
ca-certificates \
|
|
29
|
+
libssl3 \
|
|
30
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
31
|
+
|
|
32
|
+
WORKDIR /app
|
|
33
|
+
|
|
34
|
+
COPY --from=build /app/target/release/rdo /usr/local/bin/rdo
|
|
35
|
+
COPY example /app/example
|
|
36
|
+
ENV RUST_BACKTRACE=1 \
|
|
37
|
+
TERM=xterm-256color
|
|
38
|
+
|
|
39
|
+
CMD ["rdo"]
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cobolx",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CobolX CLI",
|
|
5
|
+
"main": "bin/cobolx.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cobolx": "./bin/cobolx.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"postinstall": "node scripts/install.js"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/WSPlXA/cobolX.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"cobol",
|
|
18
|
+
"rust",
|
|
19
|
+
"cli"
|
|
20
|
+
],
|
|
21
|
+
"author": "WSPlXA",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/WSPlXA/cobolX/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/WSPlXA/cobolX#readme"
|
|
27
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const https = require('https');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
const pkg = require('../package.json');
|
|
7
|
+
const version = pkg.version;
|
|
8
|
+
const repo = 'WSPlXA/cobolX';
|
|
9
|
+
const binDir = path.join(__dirname, '../bin');
|
|
10
|
+
|
|
11
|
+
// Map process.platform and process.arch to Rust target triples and archives
|
|
12
|
+
function getTarget() {
|
|
13
|
+
const platform = process.platform;
|
|
14
|
+
const arch = process.arch;
|
|
15
|
+
|
|
16
|
+
if (platform === 'win32' && arch === 'x64') {
|
|
17
|
+
return {
|
|
18
|
+
triple: 'x86_64-pc-windows-msvc',
|
|
19
|
+
ext: 'zip'
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (platform === 'darwin') {
|
|
23
|
+
if (arch === 'x64') {
|
|
24
|
+
return {
|
|
25
|
+
triple: 'x86_64-apple-darwin',
|
|
26
|
+
ext: 'tar.gz'
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (arch === 'arm64') {
|
|
30
|
+
return {
|
|
31
|
+
triple: 'aarch64-apple-darwin',
|
|
32
|
+
ext: 'tar.gz'
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (platform === 'linux') {
|
|
37
|
+
if (arch === 'x64') {
|
|
38
|
+
return {
|
|
39
|
+
triple: 'x86_64-unknown-linux-gnu',
|
|
40
|
+
ext: 'tar.gz'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (arch === 'arm64') {
|
|
44
|
+
return {
|
|
45
|
+
triple: 'aarch64-unknown-linux-gnu',
|
|
46
|
+
ext: 'tar.gz'
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw new Error(`Unsupported platform/architecture: ${platform}/${arch}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function downloadFile(url, dest, callback) {
|
|
55
|
+
https.get(url, (res) => {
|
|
56
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
57
|
+
// Follow redirect
|
|
58
|
+
downloadFile(res.headers.location, dest, callback);
|
|
59
|
+
} else if (res.statusCode === 200) {
|
|
60
|
+
const file = fs.createWriteStream(dest);
|
|
61
|
+
res.pipe(file);
|
|
62
|
+
file.on('finish', () => {
|
|
63
|
+
file.close(callback);
|
|
64
|
+
});
|
|
65
|
+
} else {
|
|
66
|
+
callback(new Error(`Failed to download: HTTP ${res.statusCode} ${res.statusMessage}`));
|
|
67
|
+
}
|
|
68
|
+
}).on('error', (err) => {
|
|
69
|
+
callback(err);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function install() {
|
|
74
|
+
try {
|
|
75
|
+
const target = getTarget();
|
|
76
|
+
const archiveName = `rdo-${target.triple}.${target.ext}`;
|
|
77
|
+
const downloadUrl = `https://github.com/${repo}/releases/download/v${version}/${archiveName}`;
|
|
78
|
+
const tempFile = path.join(binDir, `temp-${archiveName}`);
|
|
79
|
+
|
|
80
|
+
console.log(`Downloading CobolX binary for ${target.triple}...`);
|
|
81
|
+
|
|
82
|
+
// Ensure bin directory exists
|
|
83
|
+
if (!fs.existsSync(binDir)) {
|
|
84
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
downloadFile(downloadUrl, tempFile, (err) => {
|
|
88
|
+
if (err) {
|
|
89
|
+
console.error('Error downloading binary:', err.message);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log('Extracting binary...');
|
|
94
|
+
try {
|
|
95
|
+
if (target.ext === 'zip') {
|
|
96
|
+
// Decompress zip on Windows using PowerShell
|
|
97
|
+
const cmd = `powershell -Command "Expand-Archive -Path '${tempFile}' -DestinationPath '${binDir}' -Force"`;
|
|
98
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
99
|
+
} else {
|
|
100
|
+
// Decompress tar.gz on Unix
|
|
101
|
+
const cmd = `tar -xzf "${tempFile}" -C "${binDir}"`;
|
|
102
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Clean up temp archive
|
|
106
|
+
fs.unlinkSync(tempFile);
|
|
107
|
+
|
|
108
|
+
// Rename the binary from 'rdo' to 'cobolx'
|
|
109
|
+
const rawBinName = target.ext === 'zip' ? 'rdo.exe' : 'rdo';
|
|
110
|
+
const finalBinName = target.ext === 'zip' ? 'cobolx.exe' : 'cobolx';
|
|
111
|
+
|
|
112
|
+
const rawBinPath = path.join(binDir, rawBinName);
|
|
113
|
+
const finalBinPath = path.join(binDir, finalBinName);
|
|
114
|
+
|
|
115
|
+
if (fs.existsSync(rawBinPath)) {
|
|
116
|
+
fs.renameSync(rawBinPath, finalBinPath);
|
|
117
|
+
} else {
|
|
118
|
+
// Check if it was already named correctly or packaged differently
|
|
119
|
+
if (!fs.existsSync(finalBinPath)) {
|
|
120
|
+
throw new Error(`Could not find extracted binary in ${binDir}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Set execution permissions on Unix
|
|
125
|
+
if (target.ext !== 'zip') {
|
|
126
|
+
fs.chmodSync(finalBinPath, 0o755);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('CobolX binary installed successfully.');
|
|
130
|
+
} catch (extractErr) {
|
|
131
|
+
console.error('Error extracting binary:', extractErr.message);
|
|
132
|
+
// Clean up temp file in case of failure
|
|
133
|
+
if (fs.existsSync(tempFile)) {
|
|
134
|
+
fs.unlinkSync(tempFile);
|
|
135
|
+
}
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.error('Installation failed:', err.message);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
install();
|