roxify 1.12.7 → 1.12.8

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/Cargo.toml ADDED
@@ -0,0 +1,98 @@
1
+ [package]
2
+ name = "roxify_native"
3
+ version = "1.12.8"
4
+ edition = "2021"
5
+ publish = false
6
+
7
+ [lib]
8
+ name = "roxify_native"
9
+ crate-type = ["cdylib"]
10
+ path = "native/lib.rs"
11
+
12
+ [[bin]]
13
+ name = "roxify_native"
14
+ path = "native/main.rs"
15
+
16
+ [[bin]]
17
+ name = "bench_hybrid"
18
+ path = "native/bench_hybrid.rs"
19
+
20
+ [[bin]]
21
+ name = "test_stages"
22
+ path = "native/test_stages.rs"
23
+
24
+ [[bin]]
25
+ name = "test_small_bwt"
26
+ path = "native/test_small_bwt.rs"
27
+
28
+ [dev-dependencies]
29
+
30
+ [dependencies]
31
+ napi = "2"
32
+ napi-derive = "2"
33
+ rayon = "1.7"
34
+ zstd = { version = "0.11", features = ["zstdmt"] }
35
+ crc32fast = "1.3"
36
+ num_cpus = "1.16"
37
+ clap = { version = "4", features = ["derive"] }
38
+ serde_json = "1.0"
39
+ anyhow = "1.0"
40
+ png = "0.18.0"
41
+ image = { version = "0.25", default-features = false, features = ["png"] }
42
+ # indicatif removed from default deps to reduce heavy build graph
43
+ walkdir = "2.5.0"
44
+ tar = "0.4"
45
+ aes-gcm = "0.10"
46
+ aes = "0.8"
47
+ ctr = "0.9"
48
+ cipher = { version = "0.4", features = ["std"] }
49
+ hmac = "0.12"
50
+ pbkdf2 = "0.12"
51
+ rand = "0.8"
52
+ sha2 = "0.10"
53
+ mimalloc = "0.1"
54
+
55
+ wgpu = { version = "0.19", optional = true }
56
+ memmap2 = "0.9"
57
+ bytemuck = { version = "1.14", features = ["derive"] }
58
+ # tokio is optional now; enable via the 'async' feature
59
+ tokio = { version = "1", features = ["sync", "rt"], optional = true }
60
+ parking_lot = "0.12"
61
+ pollster = { version = "0.3", optional = true }
62
+ libsais = { version = "0.2.0", default-features = false }
63
+
64
+ [features]
65
+ # default is intentionally empty so the crate compiles fast for local checks.
66
+ # Enable 'gpu' to pull in the WGPU and pollster dependencies (heavy).
67
+ # Enable 'async' to include tokio runtime (optional).
68
+ # Example: `cargo build -p roxify_native --features gpu`
69
+ default = []
70
+
71
+ gpu = ["wgpu", "pollster"]
72
+ async = ["tokio"]
73
+ full = ["gpu", "async"]
74
+
75
+ [profile.release]
76
+ opt-level = 3
77
+ lto = "fat"
78
+ codegen-units = 1
79
+ strip = true
80
+ panic = "abort"
81
+
82
+ [profile.release-size]
83
+ inherits = "release"
84
+ opt-level = "z"
85
+ lto = true
86
+ strip = true
87
+
88
+ [profile.fastdev]
89
+ # Fast development profile for minimal user CPU and fast compilation.
90
+ # Lower optimization and high codegen units to parallelize compilation work,
91
+ # enable incremental to speed up subsequent incremental builds.
92
+ inherits = "release"
93
+ opt-level = 1
94
+ lto = false
95
+ codegen-units = 16
96
+ debug = false
97
+ incremental = true
98
+ panic = "abort"
package/dist/cli.js CHANGED
@@ -5,8 +5,8 @@ import { basename, dirname, join, resolve } from 'path';
5
5
  import { DataFormatError, decodePngToBinary, encodeBinaryToPng, hasPassphraseInPng, IncorrectPassphraseError, listFilesInPng, PassphraseRequiredError, } from './index.js';
6
6
  import { packPathsGenerator, unpackBuffer } from './pack.js';
7
7
  import * as cliProgress from './stub-progress.js';
8
- import { decodeWithRustCLI, encodeWithRustCLI, isRustBinaryAvailable, } from './utils/rust-cli-wrapper.js';
9
- const VERSION = '1.12.3';
8
+ import { encodeWithRustCLI, isRustBinaryAvailable, } from './utils/rust-cli-wrapper.js';
9
+ const VERSION = '1.12.0';
10
10
  function getDirectorySize(dirPath) {
11
11
  let totalSize = 0;
12
12
  try {
@@ -51,54 +51,54 @@ async function readLargeFile(filePath) {
51
51
  return Buffer.concat(chunks);
52
52
  }
53
53
  function showHelp() {
54
- console.log(`
55
- ROX CLI — Encode/decode binary in PNG or WAV
56
-
57
- Usage:
58
- npx rox <command> [options]
59
-
60
- Commands:
61
- encode <input>... [output] Encode one or more files/directories
62
- decode <input> [output] Decode PNG/WAV to original file
63
- list <input> List files in a Rox archive
64
- havepassphrase <input> Check whether the archive requires a passphrase
65
-
66
- Options:
67
- --image Use PNG container (default)
68
- --sound Use WAV audio container (smaller overhead, faster)
69
- --bwt-ans Use BWT-ANS compression instead of Zstd
70
- -p, --passphrase <pass> Use passphrase (AES-256-GCM)
71
- -m, --mode <mode> Mode: screenshot (default)
72
- -e, --encrypt <type> auto|aes|xor|none
73
- --no-compress Disable compression
74
- --dict <file> Use zstd dictionary when compressing
75
- --force-ts Force TypeScript encoder (slower but supports encryption)
76
- -o, --output <path> Output file path
77
- -s, --sizes Show file sizes in 'list' output (default)
78
- --no-sizes Disable file size reporting in 'list'
79
- --files <list> Extract only specified files (comma-separated)
80
- --view-reconst Export the reconstituted PNG for debugging
81
- --debug Export debug images (doubled.png, reconstructed.png)
82
- -v, --verbose Show detailed errors
83
-
84
- Lossy-Resilient Encoding:
85
- --lossy-resilient Enable lossy-resilient mode (survives JPEG/MP3)
86
- --ecc-level <level> ECC redundancy: low|medium|quartile|high (default: medium)
87
- --block-size <n> Robust image block size: 2-8 pixels (default: 4)
88
-
89
- When --lossy-resilient is active, data is encoded with Reed-Solomon ECC
90
- and rendered as a QR-code-style grid (image) or MFSK tones (audio).
91
- Use --sound or --image to choose the container format.
92
-
93
- Examples:
94
- npx rox encode secret.pdf Encode to PNG
95
- npx rox encode secret.pdf --sound Encode to WAV
96
- npx rox encode secret.pdf --lossy-resilient Lossy-resilient PNG
97
- npx rox encode secret.pdf --lossy-resilient --sound --ecc-level high
98
- npx rox decode secret.pdf.png Decode back
99
- npx rox decode secret.pdf.wav Decode WAV back
100
-
101
- Run "npx rox help" for this message.
54
+ console.log(`
55
+ ROX CLI — Encode/decode binary in PNG or WAV
56
+
57
+ Usage:
58
+ npx rox <command> [options]
59
+
60
+ Commands:
61
+ encode <input>... [output] Encode one or more files/directories
62
+ decode <input> [output] Decode PNG/WAV to original file
63
+ list <input> List files in a Rox archive
64
+ havepassphrase <input> Check whether the archive requires a passphrase
65
+
66
+ Options:
67
+ --image Use PNG container (default)
68
+ --sound Use WAV audio container (smaller overhead, faster)
69
+ --bwt-ans Use BWT-ANS compression instead of Zstd
70
+ -p, --passphrase <pass> Use passphrase (AES-256-GCM)
71
+ -m, --mode <mode> Mode: screenshot (default)
72
+ -e, --encrypt <type> auto|aes|xor|none
73
+ --no-compress Disable compression
74
+ --dict <file> Use zstd dictionary when compressing
75
+ --force-ts Force TypeScript encoder (slower but supports encryption)
76
+ -o, --output <path> Output file path
77
+ -s, --sizes Show file sizes in 'list' output (default)
78
+ --no-sizes Disable file size reporting in 'list'
79
+ --files <list> Extract only specified files (comma-separated)
80
+ --view-reconst Export the reconstituted PNG for debugging
81
+ --debug Export debug images (doubled.png, reconstructed.png)
82
+ -v, --verbose Show detailed errors
83
+
84
+ Lossy-Resilient Encoding:
85
+ --lossy-resilient Enable lossy-resilient mode (survives JPEG/MP3)
86
+ --ecc-level <level> ECC redundancy: low|medium|quartile|high (default: medium)
87
+ --block-size <n> Robust image block size: 2-8 pixels (default: 4)
88
+
89
+ When --lossy-resilient is active, data is encoded with Reed-Solomon ECC
90
+ and rendered as a QR-code-style grid (image) or MFSK tones (audio).
91
+ Use --sound or --image to choose the container format.
92
+
93
+ Examples:
94
+ npx rox encode secret.pdf Encode to PNG
95
+ npx rox encode secret.pdf --sound Encode to WAV
96
+ npx rox encode secret.pdf --lossy-resilient Lossy-resilient PNG
97
+ npx rox encode secret.pdf --lossy-resilient --sound --ecc-level high
98
+ npx rox decode secret.pdf.png Decode back
99
+ npx rox decode secret.pdf.wav Decode WAV back
100
+
101
+ Run "npx rox help" for this message.
102
102
  `);
103
103
  }
104
104
  function parseArgs(args) {
@@ -303,35 +303,22 @@ async function encodeCommand(args) {
303
303
  const startTime = Date.now();
304
304
  const encodeBar = new cliProgress.SingleBar({ format: ' {bar} {percentage}% | {step} | {elapsed}s' }, cliProgress.Presets.shades_classic);
305
305
  let barValue = 0;
306
- encodeBar.start(100, 0, { step: 'Starting', elapsed: '0' });
307
- const onProgress = (pct) => {
308
- barValue = Math.max(barValue, pct);
306
+ encodeBar.start(100, 0, { step: 'Encoding', elapsed: '0' });
307
+ const progressInterval = setInterval(() => {
308
+ barValue = Math.min(barValue + 1, 99);
309
309
  const elapsed = Math.floor((Date.now() - startTime) / 1000);
310
- let step = 'Encoding';
311
- if (pct < 30)
312
- step = 'Packing files';
313
- else if (pct < 80)
314
- step = 'Compressing';
315
- else if (pct < 100)
316
- step = 'Writing PNG';
317
- else
318
- step = 'Done';
319
- encodeBar.update(Math.min(barValue, 99), { step, elapsed: String(elapsed) });
320
- };
321
- const smoothInterval = setInterval(() => {
322
- if (barValue < 99) {
323
- barValue = Math.min(barValue + 1, 99);
324
- const elapsed = Math.floor((Date.now() - startTime) / 1000);
325
- encodeBar.update(barValue, { step: barValue < 30 ? 'Packing files' : barValue < 80 ? 'Compressing' : 'Writing PNG', elapsed: String(elapsed) });
326
- }
327
- }, 2000);
310
+ encodeBar.update(barValue, {
311
+ step: 'Encoding',
312
+ elapsed: String(elapsed),
313
+ });
314
+ }, 500);
328
315
  const encryptType = parsed.encrypt === 'xor' ? 'xor' : 'aes';
329
316
  const fileName = basename(inputPaths[0]);
330
- await encodeWithRustCLI(inputPaths.length === 1 ? resolvedInputs[0] : resolvedInputs[0], resolvedOutput, 0, parsed.passphrase, encryptType, fileName, onProgress);
331
- clearInterval(smoothInterval);
317
+ await encodeWithRustCLI(inputPaths.length === 1 ? resolvedInputs[0] : resolvedInputs[0], resolvedOutput, 19, parsed.passphrase, encryptType, fileName);
318
+ clearInterval(progressInterval);
332
319
  const encodeTime = Date.now() - startTime;
333
320
  encodeBar.update(100, {
334
- step: 'Done',
321
+ step: 'done',
335
322
  elapsed: String(Math.floor(encodeTime / 1000)),
336
323
  });
337
324
  encodeBar.stop();
@@ -373,18 +360,28 @@ async function encodeCommand(args) {
373
360
  const encodeBar = new cliProgress.SingleBar({
374
361
  format: ' {bar} {percentage}% | {step} | {elapsed}s',
375
362
  }, cliProgress.Presets.shades_classic);
363
+ let barStarted = false;
376
364
  const startEncode = Date.now();
377
365
  let currentEncodeStep = 'Starting';
378
366
  let displayedPct = 0;
379
367
  let targetPct = 0;
380
368
  const TICK_MS = 100;
381
369
  const PCT_STEP = 1;
382
- encodeBar.start(100, 0, { step: currentEncodeStep, elapsed: '0' });
383
370
  const encodeHeartbeat = setInterval(() => {
384
371
  const elapsed = Date.now() - startEncode;
372
+ if (!barStarted) {
373
+ encodeBar.start(100, Math.floor(displayedPct), {
374
+ step: currentEncodeStep,
375
+ elapsed: '0',
376
+ });
377
+ barStarted = true;
378
+ }
385
379
  if (displayedPct < targetPct) {
386
380
  displayedPct = Math.min(displayedPct + PCT_STEP, targetPct);
387
381
  }
382
+ else if (displayedPct < 99) {
383
+ displayedPct = Math.min(displayedPct + PCT_STEP, 99);
384
+ }
388
385
  encodeBar.update(Math.floor(displayedPct), {
389
386
  step: currentEncodeStep,
390
387
  elapsed: String(Math.floor(elapsed / 1000)),
@@ -395,7 +392,7 @@ async function encodeCommand(args) {
395
392
  mode,
396
393
  name: parsed.outputName || 'archive',
397
394
  skipOptimization: false,
398
- compressionLevel: 0,
395
+ compressionLevel: 6,
399
396
  outputFormat: 'auto',
400
397
  container: containerMode,
401
398
  });
@@ -541,11 +538,13 @@ async function encodeCommand(args) {
541
538
  const output = await encodeBinaryToPng(inputBuffer, options);
542
539
  const encodeTime = Date.now() - startEncode;
543
540
  clearInterval(encodeHeartbeat);
544
- encodeBar.update(100, {
545
- step: 'Done',
546
- elapsed: String(Math.floor(encodeTime / 1000)),
547
- });
548
- encodeBar.stop();
541
+ if (barStarted) {
542
+ encodeBar.update(100, {
543
+ step: 'done',
544
+ elapsed: String(Math.floor(encodeTime / 1000)),
545
+ });
546
+ encodeBar.stop();
547
+ }
549
548
  writeFileSync(resolvedOutput, output);
550
549
  const outputSize = (output.length / 1024 / 1024).toFixed(2);
551
550
  const inputSize = (inputSizeVal / 1024 / 1024).toFixed(2);
@@ -576,42 +575,6 @@ async function decodeCommand(args) {
576
575
  }
577
576
  const resolvedInput = resolve(inputPath);
578
577
  const resolvedOutput = parsed.output || outputPath || 'decoded.bin';
579
- const inputFileSize = statSync(resolvedInput).size;
580
- if (isRustBinaryAvailable() && !parsed.dict && inputFileSize > 10 * 1024 * 1024) {
581
- try {
582
- console.log(' ');
583
- console.log(`Decoding... (Using native Rust decoder)\n`);
584
- const decodeBar = new cliProgress.SingleBar({ format: ' {bar} {percentage}% | {step} | {elapsed}s' }, cliProgress.Presets.shades_classic);
585
- const startDecode = Date.now();
586
- let barValue = 0;
587
- decodeBar.start(100, 0, { step: 'Reading PNG', elapsed: '0' });
588
- const onProgress = (pct) => {
589
- barValue = Math.max(barValue, pct);
590
- const elapsed = Math.floor((Date.now() - startDecode) / 1000);
591
- let step = 'Reading PNG';
592
- if (pct >= 10 && pct < 60)
593
- step = 'Decompressing';
594
- else if (pct >= 60 && pct < 100)
595
- step = 'Extracting files';
596
- else if (pct >= 100)
597
- step = 'Done';
598
- decodeBar.update(Math.min(barValue, 99), { step, elapsed: String(elapsed) });
599
- };
600
- await decodeWithRustCLI(resolvedInput, resolvedOutput, parsed.passphrase, parsed.files, onProgress);
601
- const decodeTime = Date.now() - startDecode;
602
- decodeBar.update(100, { step: 'Done', elapsed: String(Math.floor(decodeTime / 1000)) });
603
- decodeBar.stop();
604
- console.log(`\nSuccess!`);
605
- console.log(` Time: ${decodeTime}ms`);
606
- console.log(` Output: ${resolve(resolvedOutput)}`);
607
- console.log(' ');
608
- return;
609
- }
610
- catch (err) {
611
- console.warn('\nRust decoder failed, falling back to TypeScript decoder...');
612
- console.warn(`Reason: ${err.message}\n`);
613
- }
614
- }
615
578
  try {
616
579
  const options = {};
617
580
  if (parsed.passphrase) {
@@ -638,19 +601,28 @@ async function decodeCommand(args) {
638
601
  const decodeBar = new cliProgress.SingleBar({
639
602
  format: ' {bar} {percentage}% | {step} | {elapsed}s',
640
603
  }, cliProgress.Presets.shades_classic);
604
+ let barStarted = false;
641
605
  const startDecode = Date.now();
642
606
  let currentPct = 0;
643
607
  let targetPct = 0;
644
- let currentStep = 'Reading PNG';
645
- decodeBar.start(100, 0, { step: currentStep, elapsed: '0' });
608
+ let currentStep = 'Decoding';
646
609
  const heartbeat = setInterval(() => {
647
610
  if (currentPct < targetPct) {
648
611
  currentPct = Math.min(currentPct + 2, targetPct);
649
612
  }
650
- decodeBar.update(Math.floor(currentPct), {
651
- step: currentStep,
652
- elapsed: String(Math.floor((Date.now() - startDecode) / 1000)),
653
- });
613
+ if (!barStarted && targetPct > 0) {
614
+ decodeBar.start(100, Math.floor(currentPct), {
615
+ step: currentStep,
616
+ elapsed: String(Math.floor((Date.now() - startDecode) / 1000)),
617
+ });
618
+ barStarted = true;
619
+ }
620
+ else if (barStarted) {
621
+ decodeBar.update(Math.floor(currentPct), {
622
+ step: currentStep,
623
+ elapsed: String(Math.floor((Date.now() - startDecode) / 1000)),
624
+ });
625
+ }
654
626
  }, 100);
655
627
  options.onProgress = (info) => {
656
628
  if (info.phase === 'decompress_start') {
@@ -661,7 +633,7 @@ async function decodeCommand(args) {
661
633
  info.loaded &&
662
634
  info.total) {
663
635
  targetPct = 50 + Math.floor((info.loaded / info.total) * 40);
664
- currentStep = 'Decompressing';
636
+ currentStep = `Decompressing (${info.loaded}/${info.total})`;
665
637
  }
666
638
  else if (info.phase === 'decompress_done') {
667
639
  targetPct = 90;
@@ -676,12 +648,14 @@ async function decodeCommand(args) {
676
648
  const result = await decodePngToBinary(inputBuffer, options);
677
649
  const decodeTime = Date.now() - startDecode;
678
650
  clearInterval(heartbeat);
679
- currentPct = 100;
680
- decodeBar.update(100, {
681
- step: 'Done',
682
- elapsed: String(Math.floor(decodeTime / 1000)),
683
- });
684
- decodeBar.stop();
651
+ if (barStarted) {
652
+ currentPct = 100;
653
+ decodeBar.update(100, {
654
+ step: 'done',
655
+ elapsed: String(Math.floor(decodeTime / 1000)),
656
+ });
657
+ decodeBar.stop();
658
+ }
685
659
  if (result.files) {
686
660
  const baseDir = parsed.output || outputPath || '.';
687
661
  const totalBytes = result.files.reduce((s, f) => s + f.buf.length, 0);
@@ -1,14 +1,9 @@
1
- import cliProgress from 'cli-progress';
2
1
  export declare class SingleBar {
3
- private bar;
4
- constructor(opts?: any, preset?: any);
5
- start(total: number, startValue: number, payload?: any): void;
6
- update(value: number, payload?: any): void;
7
- stop(): void;
2
+ constructor(...args: any[]);
3
+ start(...args: any[]): void;
4
+ update(...args: any[]): void;
5
+ stop(...args: any[]): void;
8
6
  }
9
7
  export declare const Presets: {
10
- legacy: cliProgress.Preset;
11
- rect: cliProgress.Preset;
12
- shades_classic: cliProgress.Preset;
13
- shades_grey: cliProgress.Preset;
8
+ shades_classic: {};
14
9
  };
@@ -1,22 +1,9 @@
1
- import cliProgress from 'cli-progress';
2
1
  export class SingleBar {
3
- constructor(opts, preset) {
4
- this.bar = new cliProgress.SingleBar({
5
- ...opts,
6
- hideCursor: true,
7
- forceRedraw: true,
8
- barCompleteChar: '\u2588',
9
- barIncompleteChar: '\u2591',
10
- }, preset || cliProgress.Presets.shades_classic);
11
- }
12
- start(total, startValue, payload) {
13
- this.bar.start(total, startValue, payload);
14
- }
15
- update(value, payload) {
16
- this.bar.update(value, payload);
17
- }
18
- stop() {
19
- this.bar.stop();
20
- }
2
+ constructor(...args) { }
3
+ start(...args) { }
4
+ update(...args) { }
5
+ stop(...args) { }
21
6
  }
22
- export const Presets = cliProgress.Presets;
7
+ export const Presets = {
8
+ shades_classic: {},
9
+ };
@@ -59,7 +59,7 @@ export async function encodeBinaryToPng(input, opts = {}) {
59
59
  };
60
60
  }
61
61
  }
62
- const compressionLevel = opts.compressionLevel ?? 3;
62
+ const compressionLevel = opts.compressionLevel ?? 19;
63
63
  // ─── Lossy-resilient encoding fast path ────────────────────────────────────
64
64
  // When lossyResilient is true, use QR-code-style block encoding with
65
65
  // Reed-Solomon FEC. This produces output that survives lossy compression.
@@ -1,7 +1,4 @@
1
1
  declare function findRustBinary(): string | null;
2
2
  export { findRustBinary };
3
3
  export declare function isRustBinaryAvailable(): boolean;
4
- export declare function encodeWithRustCLI(inputPath: string, outputPath: string, compressionLevel?: number, passphrase?: string, encryptType?: 'aes' | 'xor', name?: string, onProgress?: (pct: number) => void): Promise<void>;
5
- export declare function decodeWithRustCLI(inputPath: string, outputPath: string, passphrase?: string, files?: string[], onProgress?: (pct: number) => void): Promise<{
6
- usedRust: boolean;
7
- }>;
4
+ export declare function encodeWithRustCLI(inputPath: string, outputPath: string, compressionLevel?: number, passphrase?: string, encryptType?: 'aes' | 'xor', name?: string): Promise<void>;
@@ -128,7 +128,7 @@ export function isRustBinaryAvailable() {
128
128
  }
129
129
  import { chmodSync, mkdtempSync, readFileSync, unlinkSync, writeFileSync, } from 'fs';
130
130
  import { tmpdir } from 'os';
131
- export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel = 3, passphrase, encryptType = 'aes', name, onProgress) {
131
+ export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel = 3, passphrase, encryptType = 'aes', name) {
132
132
  const cliPath = findRustBinary();
133
133
  if (!cliPath) {
134
134
  throw new Error('Rust CLI binary not found');
@@ -145,7 +145,7 @@ export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel
145
145
  return dest;
146
146
  }
147
147
  return new Promise((resolve, reject) => {
148
- const args = ['encode', '--level', String(compressionLevel), '--progress'];
148
+ const args = ['encode', '--level', String(compressionLevel)];
149
149
  let supportsName = false;
150
150
  if (name) {
151
151
  try {
@@ -173,9 +173,7 @@ export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel
173
173
  const runSpawn = (exePath) => {
174
174
  let proc;
175
175
  try {
176
- proc = spawn(exePath, args, {
177
- stdio: ['inherit', 'inherit', 'pipe'],
178
- });
176
+ proc = spawn(exePath, args, { stdio: 'inherit' });
179
177
  }
180
178
  catch (err) {
181
179
  if (!triedExtract) {
@@ -190,20 +188,6 @@ export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel
190
188
  }
191
189
  return reject(err);
192
190
  }
193
- if (proc.stderr && onProgress) {
194
- let stderrBuf = '';
195
- proc.stderr.on('data', (chunk) => {
196
- stderrBuf += chunk.toString();
197
- const lines = stderrBuf.split('\n');
198
- stderrBuf = lines.pop() || '';
199
- for (const line of lines) {
200
- const match = line.match(/PROGRESS:(\d+)/);
201
- if (match) {
202
- onProgress(parseInt(match[1], 10));
203
- }
204
- }
205
- });
206
- }
207
191
  proc.on('error', (err) => {
208
192
  if (!triedExtract) {
209
193
  triedExtract = true;
@@ -235,53 +219,3 @@ export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel
235
219
  runSpawn(cliPath);
236
220
  });
237
221
  }
238
- export async function decodeWithRustCLI(inputPath, outputPath, passphrase, files, onProgress) {
239
- const cliPath = findRustBinary();
240
- if (!cliPath) {
241
- throw new Error('Rust CLI binary not found');
242
- }
243
- return new Promise((resolve, reject) => {
244
- const args = ['decompress', '--progress'];
245
- if (passphrase) {
246
- args.push('--passphrase', passphrase);
247
- }
248
- if (files && files.length > 0) {
249
- args.push('--files', JSON.stringify(files));
250
- }
251
- args.push(inputPath, outputPath);
252
- const proc = spawn(cliPath, args, {
253
- stdio: ['inherit', 'pipe', 'pipe'],
254
- });
255
- let stdout = '';
256
- if (proc.stdout) {
257
- proc.stdout.on('data', (chunk) => {
258
- const text = chunk.toString();
259
- stdout += text;
260
- process.stdout.write(text);
261
- });
262
- }
263
- if (proc.stderr && onProgress) {
264
- let stderrBuf = '';
265
- proc.stderr.on('data', (chunk) => {
266
- stderrBuf += chunk.toString();
267
- const lines = stderrBuf.split('\n');
268
- stderrBuf = lines.pop() || '';
269
- for (const line of lines) {
270
- const match = line.match(/PROGRESS:(\d+)/);
271
- if (match) {
272
- onProgress(parseInt(match[1], 10));
273
- }
274
- }
275
- });
276
- }
277
- proc.on('error', (err) => reject(err));
278
- proc.on('close', (code) => {
279
- if (code === 0) {
280
- resolve({ usedRust: true });
281
- }
282
- else {
283
- reject(new Error(`Rust decoder exited with status ${code}`));
284
- }
285
- });
286
- });
287
- }