roxify 1.3.0 → 1.3.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.
package/dist/cli.js CHANGED
@@ -5,7 +5,8 @@ import { open } from 'fs/promises';
5
5
  import { basename, dirname, join, resolve } from 'path';
6
6
  import { DataFormatError, decodePngToBinary, encodeBinaryToPng, hasPassphraseInPng, IncorrectPassphraseError, listFilesInPng, PassphraseRequiredError, } from './index.js';
7
7
  import { packPathsGenerator, unpackBuffer } from './pack.js';
8
- const VERSION = '1.2.9';
8
+ import { encodeWithRustCLI, isRustBinaryAvailable, } from './utils/rust-cli-wrapper.js';
9
+ const VERSION = '1.3.2';
9
10
  async function readLargeFile(filePath) {
10
11
  const st = statSync(filePath);
11
12
  if (st.size <= 2 * 1024 * 1024 * 1024) {
@@ -47,6 +48,7 @@ Options:
47
48
  -m, --mode <mode> Mode: screenshot (default)
48
49
  -e, --encrypt <type> auto|aes|xor|none
49
50
  --no-compress Disable compression
51
+ --force-ts Force TypeScript encoder (slower but supports encryption)
50
52
  -o, --output <path> Output file path
51
53
  -s, --sizes Show file sizes in 'list' output (default)
52
54
  --no-sizes Disable file size reporting in 'list'
@@ -89,6 +91,10 @@ function parseArgs(args) {
89
91
  parsed.debug = true;
90
92
  i++;
91
93
  }
94
+ else if (key === 'force-ts') {
95
+ parsed.forceTs = true;
96
+ i++;
97
+ }
92
98
  else if (key === 'debug-dir') {
93
99
  parsed.debugDir = args[i + 1];
94
100
  i += 2;
@@ -166,7 +172,15 @@ async function encodeCommand(args) {
166
172
  console.log('Usage: npx rox encode <input> [output] [options]');
167
173
  process.exit(1);
168
174
  }
169
- const resolvedInputs = inputPaths.map((p) => resolve(p));
175
+ let safeCwd = '/';
176
+ try {
177
+ safeCwd = process.cwd();
178
+ }
179
+ catch (e) {
180
+ // ENOENT: fallback sur racine
181
+ safeCwd = '/';
182
+ }
183
+ const resolvedInputs = inputPaths.map((p) => resolve(safeCwd, p));
170
184
  let outputName = inputPaths.length === 1 ? basename(firstInput) : 'archive';
171
185
  if (inputPaths.length === 1 && !statSync(resolvedInputs[0]).isDirectory()) {
172
186
  outputName = outputName.replace(/(\.[^.]+)?$/, '.png');
@@ -174,7 +188,65 @@ async function encodeCommand(args) {
174
188
  else {
175
189
  outputName += '.png';
176
190
  }
177
- const resolvedOutput = parsed.output || outputPath || outputName;
191
+ let resolvedOutput;
192
+ try {
193
+ resolvedOutput = resolve(safeCwd, parsed.output || outputPath || outputName);
194
+ }
195
+ catch (e) {
196
+ resolvedOutput = join('/', parsed.output || outputPath || outputName);
197
+ }
198
+ if (isRustBinaryAvailable() && !parsed.forceTs) {
199
+ try {
200
+ console.log(`Encoding to ${resolvedOutput} (Using native Rust encoder)\n`);
201
+ const startTime = Date.now();
202
+ const encodeBar = new cliProgress.SingleBar({ format: ' {bar} {percentage}% | {step} | {elapsed}s' }, cliProgress.Presets.shades_classic);
203
+ let barValue = 0;
204
+ encodeBar.start(100, 0, { step: 'Encoding', elapsed: '0' });
205
+ const progressInterval = setInterval(() => {
206
+ barValue = Math.min(barValue + 1, 99);
207
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
208
+ encodeBar.update(barValue, {
209
+ step: 'Encoding',
210
+ elapsed: String(elapsed),
211
+ });
212
+ }, 500);
213
+ const encryptType = parsed.encrypt === 'xor' ? 'xor' : 'aes';
214
+ await encodeWithRustCLI(inputPaths.length === 1 ? resolvedInputs[0] : resolvedInputs[0], resolvedOutput, 3, parsed.passphrase, encryptType);
215
+ clearInterval(progressInterval);
216
+ const encodeTime = Date.now() - startTime;
217
+ encodeBar.update(100, {
218
+ step: 'done',
219
+ elapsed: String(Math.floor(encodeTime / 1000)),
220
+ });
221
+ encodeBar.stop();
222
+ const { statSync: fstatSync } = await import('fs');
223
+ let inputSize = 0;
224
+ if (inputPaths.length === 1 &&
225
+ fstatSync(resolvedInputs[0]).isDirectory()) {
226
+ const { execSync } = await import('child_process');
227
+ const sizeOutput = execSync(`du -sb "${resolvedInputs[0]}"`, {
228
+ encoding: 'utf-8',
229
+ });
230
+ inputSize = parseInt(sizeOutput.split(/\s+/)[0]);
231
+ }
232
+ else {
233
+ inputSize = fstatSync(resolvedInputs[0]).size;
234
+ }
235
+ const outputSize = fstatSync(resolvedOutput).size;
236
+ const ratio = ((outputSize / inputSize) * 100).toFixed(1);
237
+ console.log(`\nSuccess!`);
238
+ console.log(` Input: ${(inputSize / 1024 / 1024).toFixed(2)} MB`);
239
+ console.log(` Output: ${(outputSize / 1024 / 1024).toFixed(2)} MB (${ratio}% of original)`);
240
+ console.log(` Time: ${encodeTime}ms`);
241
+ console.log(` Saved: ${resolvedOutput}`);
242
+ console.log(' ');
243
+ return;
244
+ }
245
+ catch (err) {
246
+ console.warn('\nRust encoder failed, falling back to TypeScript encoder...');
247
+ console.warn(`Reason: ${err.message}\n`);
248
+ }
249
+ }
178
250
  let options = {};
179
251
  try {
180
252
  const encodeBar = new cliProgress.SingleBar({
@@ -212,6 +284,7 @@ async function encodeCommand(args) {
212
284
  mode,
213
285
  name: parsed.outputName || 'archive',
214
286
  skipOptimization: true,
287
+ compressionLevel: 19,
215
288
  });
216
289
  if (parsed.verbose)
217
290
  options.verbose = true;
package/dist/roxify-cli CHANGED
Binary file
@@ -53,7 +53,7 @@ export async function encodeBinaryToPng(input, opts = {}) {
53
53
  }
54
54
  if (opts.onProgress)
55
55
  opts.onProgress({ phase: 'compress_start', total: totalLen });
56
- const compressionLevel = opts.compressionLevel ?? 7;
56
+ const compressionLevel = opts.compressionLevel ?? 19;
57
57
  let payload = await parallelZstdCompress(payloadInput, compressionLevel, (loaded, total) => {
58
58
  if (opts.onProgress) {
59
59
  opts.onProgress({
@@ -1 +1,2 @@
1
- export declare function encodeWithRustCLI(inputPath: string, outputPath: string, compressionLevel?: number): Promise<void>;
1
+ export declare function isRustBinaryAvailable(): boolean;
2
+ export declare function encodeWithRustCLI(inputPath: string, outputPath: string, compressionLevel?: number, passphrase?: string, encryptType?: 'aes' | 'xor'): Promise<void>;
@@ -4,19 +4,76 @@ import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = dirname(__filename);
7
- export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel = 3) {
8
- const cliPath = join(__dirname, '..', 'dist', 'roxify-cli');
9
- if (!existsSync(cliPath)) {
10
- throw new Error('Rust CLI binary not found. Run: npm run build:native');
7
+ function findRustBinary() {
8
+ const candidates = [];
9
+ const binNames = process.platform === 'win32'
10
+ ? ['roxify-cli.exe', 'roxify_cli.exe', 'roxify_native.exe']
11
+ : ['roxify-cli', 'roxify_cli', 'roxify_native'];
12
+ // Possible locations relative to this file (works in repo and in packaged dist)
13
+ const relativeDirs = [
14
+ join(__dirname, '..', '..', 'target', 'release'),
15
+ join(__dirname, '..', '..', 'dist'),
16
+ join(__dirname, '..'),
17
+ join(__dirname, '..', '..'),
18
+ ];
19
+ for (const dir of relativeDirs) {
20
+ for (const name of binNames) {
21
+ candidates.push(join(dir, name));
22
+ }
23
+ }
24
+ // Common global paths
25
+ if (process.platform !== 'win32') {
26
+ candidates.push('/usr/local/bin/roxify_native');
27
+ candidates.push('/usr/bin/roxify_native');
28
+ }
29
+ for (const p of candidates) {
30
+ try {
31
+ if (existsSync(p))
32
+ return p;
33
+ }
34
+ catch (e) { }
35
+ }
36
+ // Search in PATH for common binary names
37
+ try {
38
+ const which = process.platform === 'win32' ? 'where' : 'which';
39
+ const { execSync } = require('child_process');
40
+ for (const name of binNames) {
41
+ try {
42
+ const out = execSync(`${which} ${name}`, { encoding: 'utf-8' })
43
+ .split('\n')[0]
44
+ .trim();
45
+ if (out && existsSync(out))
46
+ return out;
47
+ }
48
+ catch (e) {
49
+ // ignore
50
+ }
51
+ }
52
+ }
53
+ catch (e) { }
54
+ return null;
55
+ }
56
+ export function isRustBinaryAvailable() {
57
+ return findRustBinary() !== null;
58
+ }
59
+ export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel = 3, passphrase, encryptType = 'aes') {
60
+ const cliPath = findRustBinary();
61
+ if (!cliPath) {
62
+ throw new Error('Rust CLI binary not found. Run: cargo build --release');
11
63
  }
12
64
  return new Promise((resolve, reject) => {
13
- const proc = spawn(cliPath, [
65
+ const args = [
14
66
  'encode',
15
67
  inputPath,
16
68
  outputPath,
17
69
  '--level',
18
70
  String(compressionLevel),
19
- ]);
71
+ ];
72
+ if (passphrase) {
73
+ args.push('--passphrase', passphrase);
74
+ args.push('--encrypt', encryptType);
75
+ }
76
+ const proc = spawn(cliPath, args);
20
77
  let stderr = '';
21
78
  proc.stderr.on('data', (data) => {
22
79
  stderr += data.toString();
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roxify",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Encode binary data into PNG images and decode them back. CLI and programmatic API with native Rust acceleration.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",