roxify 1.14.1 → 1.14.2

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.
Files changed (47) hide show
  1. package/Cargo.toml +76 -0
  2. package/dist/cli.js +45 -22
  3. package/native/archive.rs +220 -0
  4. package/native/audio.rs +151 -0
  5. package/native/bench_hybrid.rs +145 -0
  6. package/native/bwt.rs +56 -0
  7. package/native/context_mixing.rs +117 -0
  8. package/native/core.rs +378 -0
  9. package/native/crypto.rs +209 -0
  10. package/native/encoder.rs +405 -0
  11. package/native/hybrid.rs +297 -0
  12. package/native/image_utils.rs +82 -0
  13. package/native/io_advice.rs +43 -0
  14. package/native/io_ntfs_optimized.rs +99 -0
  15. package/native/lib.rs +480 -0
  16. package/native/main.rs +939 -0
  17. package/native/mtf.rs +106 -0
  18. package/native/packer.rs +863 -0
  19. package/native/png_chunk_writer.rs +146 -0
  20. package/native/png_utils.rs +554 -0
  21. package/native/pool.rs +101 -0
  22. package/native/progress.rs +142 -0
  23. package/native/rans.rs +149 -0
  24. package/native/rans_byte.rs +286 -0
  25. package/native/reconstitution.rs +623 -0
  26. package/native/streaming.rs +189 -0
  27. package/native/streaming_decode.rs +720 -0
  28. package/native/streaming_encode.rs +684 -0
  29. package/native/test_small_bwt.rs +31 -0
  30. package/native/test_stages.rs +70 -0
  31. package/package.json +6 -6
  32. package/scripts/download-binary.cjs +259 -0
  33. package/scripts/postinstall.cjs +136 -110
  34. package/dist/rox-macos-universal +0 -0
  35. package/dist/roxify_native +0 -0
  36. package/dist/roxify_native-macos-arm64 +0 -0
  37. package/dist/roxify_native-macos-x64 +0 -0
  38. package/dist/roxify_native.exe +0 -0
  39. package/roxify_native-aarch64-apple-darwin.node +0 -0
  40. package/roxify_native-aarch64-pc-windows-msvc.node +0 -0
  41. package/roxify_native-aarch64-unknown-linux-gnu.node +0 -0
  42. package/roxify_native-i686-pc-windows-msvc.node +0 -0
  43. package/roxify_native-i686-unknown-linux-gnu.node +0 -0
  44. package/roxify_native-universal-apple-darwin.node +0 -0
  45. package/roxify_native-x86_64-apple-darwin.node +0 -0
  46. package/roxify_native-x86_64-pc-windows-msvc.node +0 -0
  47. package/roxify_native-x86_64-unknown-linux-gnu.node +0 -0
@@ -0,0 +1,70 @@
1
+ mod bwt;
2
+ mod mtf;
3
+ mod rans_byte;
4
+ mod context_mixing;
5
+ mod pool;
6
+
7
+ fn main() {
8
+ let data = b"banana";
9
+
10
+ // Test BWT
11
+ let bwt_result = bwt::bwt_encode(data).unwrap();
12
+ println!("BWT of 'banana': {:?}", String::from_utf8_lossy(&bwt_result.transformed));
13
+ println!("Primary index: {}", bwt_result.primary_index);
14
+
15
+ let decoded = bwt::bwt_decode(&bwt_result.transformed, bwt_result.primary_index).unwrap();
16
+ println!("BWT decode: {:?}", String::from_utf8_lossy(&decoded));
17
+ assert_eq!(&decoded, data, "BWT round-trip failed");
18
+ println!("BWT round-trip OK!\n");
19
+
20
+ // Test MTF
21
+ let mtf_enc = mtf::mtf_encode(data);
22
+ println!("MTF of 'banana': {:?}", mtf_enc);
23
+ let mtf_dec = mtf::mtf_decode(&mtf_enc);
24
+ assert_eq!(mtf_dec, data, "MTF round-trip failed");
25
+ println!("MTF round-trip OK!\n");
26
+
27
+ // Test RLE0
28
+ let test_rle = vec![0, 0, 0, 5, 0, 3, 0, 0, 0, 0, 0, 7];
29
+ let rle_enc = mtf::rle0_encode(&test_rle);
30
+ println!("RLE0 of {:?}: {:?}", test_rle, rle_enc);
31
+ let rle_dec = mtf::rle0_decode(&rle_enc);
32
+ println!("RLE0 decode: {:?}", rle_dec);
33
+ assert_eq!(rle_dec, test_rle, "RLE0 round-trip failed");
34
+ println!("RLE0 round-trip OK!\n");
35
+
36
+ // Test rANS byte
37
+ let test_data = b"abracadabra";
38
+ let stats = rans_byte::SymbolStats::from_data(test_data);
39
+ let encoded = rans_byte::rans_encode_block(test_data, &stats);
40
+ println!("rANS encoded 'abracadabra': {} bytes -> {} bytes", test_data.len(), encoded.len());
41
+ let decoded = rans_byte::rans_decode_block(&encoded, &stats, test_data.len()).unwrap();
42
+ assert_eq!(&decoded, test_data, "rANS round-trip failed");
43
+ println!("rANS round-trip OK!\n");
44
+
45
+ // Test full pipeline on larger data
46
+ let big_data: Vec<u8> = "Hello World! Testing the full BWT+MTF+RLE+rANS pipeline. ".repeat(20).into_bytes();
47
+ let bwt_r = bwt::bwt_encode(&big_data).unwrap();
48
+ let mtf_r = mtf::mtf_encode(&bwt_r.transformed);
49
+ let rle_r = mtf::rle0_encode(&mtf_r);
50
+ let stats = rans_byte::SymbolStats::from_data(&rle_r);
51
+ let enc = rans_byte::rans_encode_block(&rle_r, &stats);
52
+
53
+ println!("Full pipeline: {} -> BWT {} -> MTF {} -> RLE {} -> rANS {}",
54
+ big_data.len(), bwt_r.transformed.len(), mtf_r.len(), rle_r.len(), enc.len());
55
+
56
+ let dec_rle = rans_byte::rans_decode_block(&enc, &stats, rle_r.len()).unwrap();
57
+ assert_eq!(dec_rle, rle_r, "rANS stage failed");
58
+
59
+ let dec_mtf = mtf::rle0_decode(&dec_rle);
60
+ assert_eq!(dec_mtf, mtf_r, "RLE0 stage failed");
61
+
62
+ let dec_bwt = mtf::mtf_decode(&dec_mtf);
63
+ assert_eq!(dec_bwt, bwt_r.transformed, "MTF stage failed");
64
+
65
+ let dec_orig = bwt::bwt_decode(&dec_bwt, bwt_r.primary_index).unwrap();
66
+ assert_eq!(dec_orig, big_data, "BWT stage failed");
67
+
68
+ println!("Full pipeline round-trip OK!");
69
+ println!("Ratio: {:.1}%", (enc.len() as f64 / big_data.len() as f64) * 100.0);
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roxify",
3
- "version": "1.14.1",
3
+ "version": "1.14.2",
4
4
  "type": "module",
5
5
  "description": "Ultra-lightweight PNG steganography with native Rust acceleration. Encode binary data into PNG images with zstd compression.",
6
6
  "main": "dist/index.js",
@@ -12,11 +12,11 @@
12
12
  "files": [
13
13
  "dist/**/*.js",
14
14
  "dist/**/*.d.ts",
15
- "dist/roxify_native*",
16
- "dist/rox-macos-universal",
17
15
  "scripts/postinstall.cjs",
18
- "roxify_native-*.node",
19
- "libroxify_native-*.node",
16
+ "scripts/download-binary.cjs",
17
+ "native/Cargo.toml",
18
+ "native/**/*.rs",
19
+ "Cargo.toml",
20
20
  "README.md",
21
21
  "LICENSE"
22
22
  ],
@@ -96,4 +96,4 @@
96
96
  "node": ">=18.0.0"
97
97
  },
98
98
  "dependencies": {}
99
- }
99
+ }
@@ -0,0 +1,259 @@
1
+ const https = require('https');
2
+ const { createWriteStream, existsSync, mkdirSync, chmodSync, unlinkSync } = require('fs');
3
+ const { join } = require('path');
4
+ const { platform, arch } = require('os');
5
+
6
+ const GITHUB_REPO = 'RoxasYTB/roxify';
7
+ const DIST_DIR = join(__dirname, '..', 'dist');
8
+ const root = join(__dirname, '..');
9
+
10
+ function getPlatformBinary() {
11
+ const os = platform();
12
+ const cpu = arch();
13
+
14
+ const map = {
15
+ linux: { x64: 'roxify_native', arm64: 'roxify_native' },
16
+ win32: { x64: 'roxify_native.exe', arm64: 'roxify_native.exe' },
17
+ darwin: { x64: 'rox-macos-universal', arm64: 'rox-macos-universal' },
18
+ };
19
+
20
+ return (map[os] && map[os][cpu]) || null;
21
+ }
22
+
23
+ function getReleaseAssetName(version) {
24
+ const os = platform();
25
+ const cpu = arch();
26
+
27
+ // Map platform/arch to release asset names as uploaded by GitHub Actions
28
+ const map = {
29
+ linux: {
30
+ x64: `roxify_native-x86_64-unknown-linux-gnu`,
31
+ arm64: `roxify_native-aarch64-unknown-linux-gnu`
32
+ },
33
+ win32: {
34
+ x64: `roxify_native-x86_64-pc-windows-msvc.exe`,
35
+ arm64: `roxify_native-aarch64-pc-windows-msvc.exe`
36
+ },
37
+ darwin: {
38
+ x64: `rox-macos-universal`,
39
+ arm64: `rox-macos-universal`
40
+ },
41
+ };
42
+
43
+ return (map[os] && map[os][cpu]) || null;
44
+ }
45
+
46
+ function getNativeLibAssetName(version) {
47
+ const os = platform();
48
+ const cpu = arch();
49
+
50
+ const triples = {
51
+ linux: { x64: 'x86_64-unknown-linux-gnu', arm64: 'aarch64-unknown-linux-gnu' },
52
+ win32: { x64: 'x86_64-pc-windows-msvc', arm64: 'aarch64-pc-windows-msvc' },
53
+ darwin: { x64: 'x86_64-apple-darwin', arm64: 'aarch64-apple-darwin' },
54
+ };
55
+
56
+ const triple = triples[os] && triples[os][cpu];
57
+ if (!triple) return null;
58
+
59
+ return `roxify_native-${triple}.node`;
60
+ }
61
+
62
+ function downloadFile(url, dest, maxRedirects = 5) {
63
+ return new Promise((resolve, reject) => {
64
+ if (maxRedirects <= 0) {
65
+ reject(new Error('Too many redirects'));
66
+ return;
67
+ }
68
+
69
+ const file = createWriteStream(dest);
70
+ const request = https.get(url, { timeout: 60000 }, (response) => {
71
+ // Handle redirects
72
+ if (response.statusCode === 302 || response.statusCode === 301) {
73
+ const location = response.headers.location;
74
+ if (location) {
75
+ file.close();
76
+ // Clean up partial file
77
+ try { unlinkSync(dest); } catch { }
78
+ downloadFile(location, dest, maxRedirects - 1).then(resolve).catch(reject);
79
+ return;
80
+ }
81
+ }
82
+
83
+ if (response.statusCode !== 200) {
84
+ file.close();
85
+ try { unlinkSync(dest); } catch { }
86
+ reject(new Error(`HTTP ${response.statusCode} for ${url}`));
87
+ return;
88
+ }
89
+
90
+ let downloaded = 0;
91
+ response.on('data', (chunk) => {
92
+ downloaded += chunk.length;
93
+ });
94
+
95
+ response.pipe(file);
96
+
97
+ file.on('finish', () => {
98
+ file.close();
99
+ console.log(`roxify: Downloaded ${(downloaded / 1024 / 1024).toFixed(2)} MB`);
100
+ resolve();
101
+ });
102
+ });
103
+
104
+ request.on('error', (err) => {
105
+ file.close();
106
+ try { unlinkSync(dest); } catch { }
107
+ reject(err);
108
+ });
109
+
110
+ request.on('timeout', () => {
111
+ request.destroy();
112
+ file.close();
113
+ try { unlinkSync(dest); } catch { }
114
+ reject(new Error('Request timeout'));
115
+ });
116
+ });
117
+ }
118
+
119
+ async function getLatestReleaseVersion() {
120
+ const url = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
121
+
122
+ return new Promise((resolve, reject) => {
123
+ https.get(url, {
124
+ headers: {
125
+ 'User-Agent': 'roxify-install',
126
+ 'Accept': 'application/vnd.github.v3+json'
127
+ },
128
+ timeout: 10000
129
+ }, (response) => {
130
+ if (response.statusCode !== 200) {
131
+ reject(new Error(`GitHub API HTTP ${response.statusCode}`));
132
+ return;
133
+ }
134
+
135
+ let data = '';
136
+ response.on('data', chunk => data += chunk);
137
+ response.on('end', () => {
138
+ try {
139
+ const release = JSON.parse(data);
140
+ const version = release.tag_name.replace(/^v/, '');
141
+ resolve(version);
142
+ } catch (e) {
143
+ reject(new Error('Failed to parse GitHub API response'));
144
+ }
145
+ });
146
+ }).on('error', reject).on('timeout', function () { this.destroy(); reject(new Error('API timeout')); });
147
+ });
148
+ }
149
+
150
+ async function downloadBinary() {
151
+ let version;
152
+ try {
153
+ version = require('../package.json').version;
154
+ } catch (e) {
155
+ version = '1.14.2';
156
+ }
157
+
158
+ // Try to get latest release version from GitHub
159
+ try {
160
+ const latestVersion = await getLatestReleaseVersion();
161
+ if (latestVersion && latestVersion !== version) {
162
+ console.log(`roxify: Latest release is v${latestVersion} (package is v${version})`);
163
+ version = latestVersion;
164
+ }
165
+ } catch (e) {
166
+ console.log(`roxify: Using package version v${version} (could not check latest: ${e.message})`);
167
+ }
168
+
169
+ const binaryName = getPlatformBinary();
170
+ const assetName = getReleaseAssetName(version);
171
+
172
+ if (!binaryName || !assetName) {
173
+ console.log(`roxify: Unsupported platform ${platform()}/${arch()}`);
174
+ return false;
175
+ }
176
+
177
+ if (!existsSync(DIST_DIR)) {
178
+ mkdirSync(DIST_DIR, { recursive: true });
179
+ }
180
+
181
+ const destPath = join(DIST_DIR, binaryName);
182
+
183
+ // Check if already exists
184
+ if (existsSync(destPath)) {
185
+ console.log(`roxify: CLI binary already exists at ${destPath}`);
186
+ return true;
187
+ }
188
+
189
+ const url = `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${assetName}`;
190
+
191
+ console.log(`roxify: Downloading ${assetName} from GitHub...`);
192
+ console.log(`roxify: URL: ${url}`);
193
+
194
+ try {
195
+ await downloadFile(url, destPath);
196
+
197
+ if (platform() !== 'win32') {
198
+ try { chmodSync(destPath, 0o755); } catch { }
199
+ }
200
+
201
+ console.log(`roxify: CLI binary ready at ${destPath}`);
202
+ return true;
203
+ } catch (e) {
204
+ console.log(`roxify: Download failed: ${e.message}`);
205
+ // Clean up partial download
206
+ try { unlinkSync(destPath); } catch { }
207
+ return false;
208
+ }
209
+ }
210
+
211
+ async function downloadNativeLib() {
212
+ let version;
213
+ try {
214
+ version = require('../package.json').version;
215
+ } catch (e) {
216
+ version = '1.14.2';
217
+ }
218
+
219
+ // Try to get latest release version from GitHub
220
+ try {
221
+ const latestVersion = await getLatestReleaseVersion();
222
+ if (latestVersion && latestVersion !== version) {
223
+ version = latestVersion;
224
+ }
225
+ } catch (e) {
226
+ // Use package version
227
+ }
228
+
229
+ const assetName = getNativeLibAssetName(version);
230
+
231
+ if (!assetName) {
232
+ console.log(`roxify: Unsupported platform for native lib ${platform()}/${arch()}`);
233
+ return false;
234
+ }
235
+
236
+ const destPath = join(root, assetName);
237
+
238
+ // Check if already exists
239
+ if (existsSync(destPath)) {
240
+ console.log(`roxify: Native lib already exists at ${destPath}`);
241
+ return true;
242
+ }
243
+
244
+ const url = `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${assetName}`;
245
+
246
+ console.log(`roxify: Downloading native lib ${assetName}...`);
247
+
248
+ try {
249
+ await downloadFile(url, destPath);
250
+ console.log(`roxify: Native lib ready at ${destPath}`);
251
+ return true;
252
+ } catch (e) {
253
+ console.log(`roxify: Native lib download failed: ${e.message}`);
254
+ try { unlinkSync(destPath); } catch { }
255
+ return false;
256
+ }
257
+ }
258
+
259
+ module.exports = { downloadBinary, downloadNativeLib, getPlatformBinary, getLatestReleaseVersion };
@@ -1,11 +1,20 @@
1
1
  const { execSync } = require('child_process');
2
- const { existsSync, copyFileSync, mkdirSync, readdirSync, unlinkSync } = require('fs');
2
+ const { existsSync, copyFileSync, mkdirSync, readdirSync, unlinkSync, chmodSync } = require('fs');
3
3
  const { join } = require('path');
4
4
  const { platform, arch } = require('os');
5
5
 
6
6
  const root = join(__dirname, '..');
7
7
  const distDir = join(root, 'dist');
8
8
 
9
+ // Try to import download helper
10
+ try {
11
+ var { downloadBinary, downloadNativeLib } = require('./download-binary.cjs');
12
+ } catch (e) {
13
+ console.log('roxify: Download helper not available, will use local build only');
14
+ var downloadBinary = null;
15
+ var downloadNativeLib = null;
16
+ }
17
+
9
18
  function hasCargo() {
10
19
  try {
11
20
  execSync('cargo --version', { stdio: 'ignore', timeout: 5000 });
@@ -18,165 +27,182 @@ function getTriples() {
18
27
  const cpu = arch();
19
28
  const map = {
20
29
  linux: { x64: ['x86_64-unknown-linux-gnu'], arm64: ['aarch64-unknown-linux-gnu'] },
21
- win32: { x64: ['x86_64-pc-windows-msvc', 'x86_64-pc-windows-gnu'], arm64: ['aarch64-pc-windows-msvc'] },
30
+ win32: { x64: ['x86_64-pc-windows-msvc'], arm64: ['aarch64-pc-windows-msvc'] },
22
31
  darwin: { x64: ['x86_64-apple-darwin'], arm64: ['aarch64-apple-darwin'] },
23
32
  };
24
33
  return (map[os] && map[os][cpu]) || [];
25
34
  }
26
35
 
27
- function getLibExt() {
28
- if (platform() === 'win32') return 'dll';
29
- if (platform() === 'darwin') return 'dylib';
30
- return 'so';
31
- }
32
-
33
36
  function getBinaryName() {
34
37
  return platform() === 'win32' ? 'roxify_native.exe' : 'roxify_native';
35
38
  }
36
39
 
37
- function findExistingBinary() {
38
- const name = getBinaryName();
39
- const triples = getTriples();
40
- const candidates = [join(distDir, name), join(root, 'target', 'release', name)];
41
- for (const t of triples) {
42
- candidates.push(join(root, 'target', t, 'release', name));
43
- }
44
- for (const c of candidates) {
45
- if (existsSync(c)) return c;
46
- }
47
- return null;
40
+ function getMacosBinaryName() {
41
+ return 'rox-macos-universal';
48
42
  }
49
43
 
50
- function findExistingNativeLib() {
51
- const triples = getTriples();
52
- const ext = getLibExt();
53
- const prefix = platform() === 'win32' ? '' : 'lib';
54
- for (const t of triples) {
55
- const specific = join(root, `roxify_native-${t}.node`);
56
- if (existsSync(specific)) return { path: specific, triple: t };
57
- }
58
- for (const t of triples) {
59
- for (const profile of ['release', 'fastdev']) {
60
- const paths = [
61
- join(root, 'target', t, profile, `${prefix}roxify_native.${ext}`),
62
- join(root, 'target', profile, `${prefix}roxify_native.${ext}`),
63
- ];
64
- for (const p of paths) {
65
- if (existsSync(p)) return { path: p, triple: t };
66
- }
67
- }
68
- }
69
- return null;
70
- }
71
-
72
- function buildNative(target) {
73
- if (!hasCargo()) return false;
74
- const args = target ? ` --target ${target}` : '';
75
- console.log(`roxify: Building native lib${args}...`);
76
- try {
77
- execSync(`cargo build --release --lib${args}`, { cwd: root, stdio: 'inherit', timeout: 600000 });
78
- return true;
79
- } catch {
80
- console.log('roxify: Native lib build failed');
81
- return false;
82
- }
83
- }
84
-
85
- function buildBinary() {
44
+ function buildBinaryLocally() {
86
45
  if (!hasCargo()) {
87
- console.log('roxify: Cargo not found, skipping native build (TypeScript fallback will be used)');
46
+ console.log('roxify: Cargo not found, will try to download prebuilt binary');
88
47
  return false;
89
48
  }
90
- console.log('roxify: Building native CLI binary...');
49
+ console.log('roxify: Building native CLI binary locally...');
91
50
  try {
92
51
  execSync('cargo build --release --bin roxify_native', { cwd: root, stdio: 'inherit', timeout: 600000 });
93
52
  return true;
94
- } catch {
95
- console.log('roxify: Native build failed, TypeScript fallback will be used');
53
+ } catch (e) {
54
+ console.log('roxify: Local build failed, will try to download prebuilt binary');
96
55
  return false;
97
56
  }
98
57
  }
99
58
 
100
- function ensureCliBinary() {
59
+ async function ensureCliBinary() {
101
60
  if (!existsSync(distDir)) mkdirSync(distDir, { recursive: true });
102
- const dest = join(distDir, getBinaryName());
103
- if (existsSync(dest)) return;
104
61
 
105
- const existing = findExistingBinary();
106
- if (existing && existing !== dest) {
107
- copyFileSync(existing, dest);
62
+ const binaryName = platform() === 'darwin' ? getMacosBinaryName() : getBinaryName();
63
+ const dest = join(distDir, binaryName);
64
+
65
+ if (existsSync(dest)) {
66
+ console.log(`roxify: CLI binary already exists at ${dest}`);
67
+ return true;
68
+ }
69
+
70
+ // Check if built locally exists
71
+ const localBuilt = join(root, 'target', 'release', getBinaryName());
72
+ if (existsSync(localBuilt)) {
73
+ copyFileSync(localBuilt, dest);
108
74
  if (platform() !== 'win32') {
109
- try { require('fs').chmodSync(dest, 0o755); } catch { }
75
+ try { chmodSync(dest, 0o755); } catch { }
110
76
  }
111
- console.log(`roxify: Copied CLI binary from ${existing}`);
112
- return;
77
+ console.log(`roxify: CLI binary copied from local build`);
78
+ return true;
79
+ }
80
+
81
+ if (process.env.ROXIFY_SKIP_BUILD === '1') return false;
82
+
83
+ // PRIMARY: Try download from GitHub releases (fast, no compilation)
84
+ if (downloadBinary && !process.env.ROXIFY_FORCE_LOCAL_BUILD) {
85
+ console.log(`roxify: Attempting to download prebuilt binary from GitHub...`);
86
+ const downloaded = await downloadBinary();
87
+ if (downloaded) {
88
+ console.log(`roxify: Successfully downloaded prebuilt binary`);
89
+ return true;
90
+ }
91
+ console.log(`roxify: Download failed, will try local build...`);
113
92
  }
114
- if (existing) return;
115
- if (process.env.ROXIFY_SKIP_BUILD === '1') return;
116
93
 
117
- if (buildBinary()) {
118
- const built = join(root, 'target', 'release', getBinaryName());
119
- if (existsSync(built)) {
120
- copyFileSync(built, dest);
94
+ // FALLBACK: Try local build (requires Cargo)
95
+ if (buildBinaryLocally()) {
96
+ if (existsSync(localBuilt)) {
97
+ copyFileSync(localBuilt, dest);
121
98
  if (platform() !== 'win32') {
122
- try { require('fs').chmodSync(dest, 0o755); } catch { }
99
+ try { chmodSync(dest, 0o755); } catch { }
123
100
  }
124
- console.log('roxify: CLI binary built and copied to dist/');
101
+ console.log(`roxify: CLI binary built locally`);
102
+ return true;
125
103
  }
126
104
  }
105
+
106
+ console.log('roxify: No native binary available, TypeScript fallback will be used');
107
+ return false;
127
108
  }
128
109
 
129
- function ensureNativeLib() {
110
+ async function ensureNativeLib() {
130
111
  const triples = getTriples();
131
112
  if (!triples.length) return;
132
113
 
133
- for (const t of triples) {
134
- if (existsSync(join(root, `roxify_native-${t}.node`))) return;
135
- }
114
+ const triple = triples[0];
115
+ const dest = join(root, `roxify_native-${triple}.node`);
116
+
117
+ if (existsSync(dest)) return;
118
+
119
+ // Check if built locally
120
+ const ext = platform() === 'win32' ? 'dll' : (platform() === 'darwin' ? 'dylib' : 'so');
121
+ const prefix = platform() === 'win32' ? '' : 'lib';
122
+ const localBuilt = join(root, 'target', 'release', `${prefix}roxify_native.${ext}`);
136
123
 
137
- const found = findExistingNativeLib();
138
- if (found) {
139
- const dest = join(root, `roxify_native-${found.triple}.node`);
140
- copyFileSync(found.path, dest);
141
- console.log(`roxify: Copied native lib → ${dest}`);
124
+ if (existsSync(localBuilt)) {
125
+ copyFileSync(localBuilt, dest);
126
+ console.log(`roxify: Native lib copied from local build → ${dest}`);
142
127
  return;
143
128
  }
144
129
 
145
130
  if (process.env.ROXIFY_SKIP_BUILD === '1') return;
146
131
 
147
- const triple = triples[0];
148
- if (buildNative(null)) {
149
- const ext = getLibExt();
150
- const prefix = platform() === 'win32' ? '' : 'lib';
151
- const built = join(root, 'target', 'release', `${prefix}roxify_native.${ext}`);
152
- if (existsSync(built)) {
153
- const dest = join(root, `roxify_native-${triple}.node`);
154
- copyFileSync(built, dest);
155
- console.log(`roxify: Native lib built → ${dest}`);
132
+ // PRIMARY: Try download from GitHub releases
133
+ if (downloadNativeLib && !process.env.ROXIFY_FORCE_LOCAL_BUILD) {
134
+ console.log(`roxify: Attempting to download native lib from GitHub...`);
135
+ const downloaded = await downloadNativeLib();
136
+ if (downloaded) {
137
+ console.log(`roxify: Successfully downloaded native lib`);
138
+ return;
156
139
  }
140
+ console.log(`roxify: Native lib download failed, will try local build...`);
157
141
  }
158
- }
159
142
 
160
- if (process.env.ROXIFY_ENABLE_RUST_CLI === '1') {
161
- ensureCliBinary();
143
+ // FALLBACK: Try build locally
144
+ if (hasCargo()) {
145
+ console.log(`roxify: Building native lib locally...`);
146
+ try {
147
+ execSync('cargo build --release --lib', { cwd: root, stdio: 'inherit', timeout: 600000 });
148
+ if (existsSync(localBuilt)) {
149
+ copyFileSync(localBuilt, dest);
150
+ console.log(`roxify: Native lib built locally → ${dest}`);
151
+ }
152
+ } catch {
153
+ console.log('roxify: Native lib build failed, TypeScript fallback will be used');
154
+ }
155
+ }
162
156
  }
163
- ensureNativeLib();
164
157
 
165
- // Cleanup: remove other platform .node artifacts to reduce disk usage after install
158
+ // Always ensure CLI binary is available (download first, build as fallback)
159
+ (async () => {
160
+ await ensureCliBinary();
161
+ await ensureNativeLib();
162
+
163
+ // Summary
164
+ console.log('');
165
+ console.log('roxify: Post-install complete');
166
+ console.log(`roxify: Platform: ${platform()}/${arch()}`);
167
+ if (!existsSync(join(distDir, platform() === 'darwin' ? getMacosBinaryName() : getBinaryName()))) {
168
+ console.log('roxify: WARNING: No native CLI binary available - will use TypeScript fallback');
169
+ }
170
+ })();
171
+
172
+ // Cleanup: remove ALL other platform .node artifacts
166
173
  try {
167
174
  const triples = getTriples();
168
- if (triples && triples.length) {
169
- const files = readdirSync(root);
170
- for (const f of files) {
171
- // match roxify_native-<triple>.node or libroxify_native-<triple>.node
172
- const m = f.match(/^(lib)?roxify_native-(.+)\.node$/);
173
- if (m) {
174
- const fileTriple = m[2];
175
- // keep if fileTriple is one of the allowed triples
176
- if (!triples.includes(fileTriple)) {
177
- try { unlinkSync(join(root, f)); console.log(`roxify: removed ${f}`); } catch (e) { }
178
- }
175
+ const files = readdirSync(root);
176
+ for (const f of files) {
177
+ const m = f.match(/^roxify_native-(.+)\.node$/);
178
+ if (m) {
179
+ const fileTriple = m[1];
180
+ if (!triples.includes(fileTriple)) {
181
+ try {
182
+ unlinkSync(join(root, f));
183
+ console.log(`roxify: removed unused native lib ${f}`);
184
+ } catch (e) { }
179
185
  }
180
186
  }
181
187
  }
182
188
  } catch (e) { }
189
+
190
+ // Cleanup: remove ALL other platform CLI binaries from dist/
191
+ try {
192
+ const os = platform();
193
+ const distFiles = existsSync(distDir) ? readdirSync(distDir) : [];
194
+ const keepName = os === 'darwin' ? getMacosBinaryName() : getBinaryName();
195
+
196
+ for (const f of distFiles) {
197
+ // Skip if it's the correct binary for this platform
198
+ if (f === keepName) continue;
199
+
200
+ // Remove any roxify_native binary
201
+ if (f.match(/^roxify_native/) || f === 'rox-macos-universal') {
202
+ try {
203
+ unlinkSync(join(distDir, f));
204
+ console.log(`roxify: removed unused binary dist/${f}`);
205
+ } catch (e) { }
206
+ }
207
+ }
208
+ } catch (e) { }
Binary file
Binary file
Binary file
Binary file
Binary file