roxify 1.5.8 → 1.5.10

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
@@ -195,6 +195,28 @@ async function encodeCommand(args) {
195
195
  catch (e) {
196
196
  resolvedOutput = join('/', parsed.output || outputPath || outputName);
197
197
  }
198
+ // Check for empty directories *before* attempting native Rust encoder.
199
+ try {
200
+ const anyDir = inputPaths.some((p) => {
201
+ try {
202
+ return statSync(resolve(safeCwd, p)).isDirectory();
203
+ }
204
+ catch (e) {
205
+ return false;
206
+ }
207
+ });
208
+ if (anyDir) {
209
+ const { index } = await packPathsGenerator(inputPaths, undefined, () => { });
210
+ if (!index || index.length === 0) {
211
+ console.log(' ');
212
+ console.error('Error: No files found in specified input paths.');
213
+ process.exit(1);
214
+ }
215
+ }
216
+ }
217
+ catch (e) {
218
+ // ignore errors from the quick pre-check and proceed to try Rust encoding
219
+ }
198
220
  if (isRustBinaryAvailable() && !parsed.forceTs) {
199
221
  try {
200
222
  console.log(`Encoding to ${resolvedOutput} (Using native Rust encoder)\n`);
@@ -313,6 +335,11 @@ async function encodeCommand(args) {
313
335
  if (inputPaths.length > 1) {
314
336
  currentEncodeStep = 'Reading files';
315
337
  const { index, stream, totalSize } = await packPathsGenerator(inputPaths, undefined, onProgress);
338
+ if (!index || index.length === 0) {
339
+ console.log(' ');
340
+ console.error('Error: No files found in specified input paths.');
341
+ process.exit(1);
342
+ }
316
343
  inputData = stream;
317
344
  inputSizeVal = totalSize;
318
345
  displayName = parsed.outputName || 'archive';
@@ -328,6 +355,11 @@ async function encodeCommand(args) {
328
355
  if (st.isDirectory()) {
329
356
  currentEncodeStep = 'Reading files';
330
357
  const { index, stream, totalSize } = await packPathsGenerator([resolvedInput], dirname(resolvedInput), onProgress);
358
+ if (!index || index.length === 0) {
359
+ console.log(' ');
360
+ console.error(`Error: No files found in ${resolvedInput}`);
361
+ process.exit(1);
362
+ }
331
363
  inputData = stream;
332
364
  inputSizeVal = totalSize;
333
365
  displayName = parsed.outputName || basename(resolvedInput);
@@ -1,8 +1,8 @@
1
- import { arch, platform } from 'os';
2
- import { join, dirname, resolve } from 'path';
1
+ import { existsSync } from 'fs';
3
2
  import { createRequire } from 'module';
3
+ import { arch, platform } from 'os';
4
+ import { dirname, join, resolve } from 'path';
4
5
  import { fileURLToPath } from 'url';
5
- import { existsSync } from 'fs';
6
6
  function getNativeModule() {
7
7
  let moduleDir;
8
8
  let nativeRequire;
@@ -13,11 +13,24 @@ function getNativeModule() {
13
13
  nativeRequire = require;
14
14
  }
15
15
  else {
16
- // Mode ESM - utiliser import.meta.url
17
- // @ts-ignore - import.meta.url existe en ESM
18
- const __filename = fileURLToPath(import.meta.url);
19
- moduleDir = dirname(__filename);
20
- nativeRequire = createRequire(import.meta.url);
16
+ // Try ESM import.meta.url first (may throw in CJS/bundled contexts), otherwise fallback to CWD
17
+ try {
18
+ // @ts-ignore - import.meta.url exists in proper ESM contexts
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ moduleDir = dirname(__filename);
21
+ nativeRequire = createRequire(import.meta.url);
22
+ }
23
+ catch {
24
+ // Fallback (bundled CJS without __dirname): use current working directory
25
+ moduleDir = process.cwd();
26
+ try {
27
+ // @ts-ignore
28
+ nativeRequire = require;
29
+ }
30
+ catch {
31
+ nativeRequire = createRequire(process.cwd());
32
+ }
33
+ }
21
34
  }
22
35
  function getNativePath() {
23
36
  const platformMap = {
@@ -46,7 +59,9 @@ function getNativeModule() {
46
59
  // @ts-ignore
47
60
  console.debug('[native] moduleDir', moduleDir);
48
61
  let root = moduleDir && moduleDir !== '.' ? moduleDir : process.cwd();
49
- while (root.length > 1 && !existsSync(resolve(root, 'package.json')) && !existsSync(resolve(root, 'Cargo.toml'))) {
62
+ while (root.length > 1 &&
63
+ !existsSync(resolve(root, 'package.json')) &&
64
+ !existsSync(resolve(root, 'Cargo.toml'))) {
50
65
  const parent = resolve(root, '..');
51
66
  if (parent === root)
52
67
  break;
@@ -2,34 +2,72 @@ import { spawn } from 'child_process';
2
2
  import { existsSync } from 'fs';
3
3
  import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = dirname(__filename);
5
+ let moduleDir;
6
+ try {
7
+ // CJS bundlers may provide __dirname; prefer it when available
8
+ if (typeof __dirname !== 'undefined') {
9
+ // @ts-ignore
10
+ moduleDir = __dirname;
11
+ }
12
+ else {
13
+ // @ts-ignore - import.meta.url exists in ESM
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ moduleDir = dirname(__filename);
16
+ }
17
+ }
18
+ catch {
19
+ moduleDir = process.cwd();
20
+ }
7
21
  function findRustBinary() {
8
22
  const candidates = [];
9
23
  const binNames = process.platform === 'win32'
10
24
  ? ['roxify-cli.exe', 'roxify_cli.exe', 'roxify_native.exe']
11
25
  : ['roxify-cli', 'roxify_cli', 'roxify_native'];
26
+ const baseDir = typeof moduleDir !== 'undefined' ? moduleDir : process.cwd();
12
27
  // Possible locations relative to this file (works in repo and in packaged dist)
13
28
  const relativeDirs = [
14
- join(__dirname, '..', '..', 'target', 'release'),
15
- join(__dirname, '..', '..', 'dist'),
16
- join(__dirname, '..'),
17
- join(__dirname, '..', '..'),
29
+ join(baseDir, '..', '..', 'target', 'release'),
30
+ join(baseDir, '..', '..', 'dist'),
31
+ join(baseDir, '..'),
32
+ join(baseDir, '..', '..'),
33
+ join(baseDir, '..', 'target', 'release'),
18
34
  ];
19
35
  for (const dir of relativeDirs) {
20
36
  for (const name of binNames) {
21
37
  candidates.push(join(dir, name));
22
38
  }
23
39
  }
24
- // Common global paths
40
+ // Walk up parents to find a workspace-level target/release (repo root may contain target)
41
+ try {
42
+ let cur = baseDir;
43
+ for (let i = 0; i < 8; i++) {
44
+ for (const name of binNames) {
45
+ candidates.push(join(cur, '..', '..', '..', '..', '..', '..', '..', 'target', 'release', name));
46
+ candidates.push(join(cur, '..', '..', '..', '..', '..', 'target', 'release', name));
47
+ candidates.push(join(cur, '..', '..', '..', 'target', 'release', name));
48
+ candidates.push(join(cur, '..', '..', 'target', 'release', name));
49
+ candidates.push(join(cur, '..', 'target', 'release', name));
50
+ candidates.push(join(cur, 'target', 'release', name));
51
+ }
52
+ const parent = join(cur, '..');
53
+ if (parent === cur)
54
+ break;
55
+ cur = parent;
56
+ }
57
+ }
58
+ catch (e) { }
59
+ // Common global paths (last resort)
25
60
  if (process.platform !== 'win32') {
26
61
  candidates.push('/usr/local/bin/roxify_native');
27
62
  candidates.push('/usr/bin/roxify_native');
28
63
  }
29
64
  for (const p of candidates) {
30
65
  try {
31
- if (existsSync(p))
66
+ if (existsSync(p)) {
67
+ // eslint-disable-next-line no-console
68
+ console.log(`Found Rust binary candidate: ${p}`);
32
69
  return p;
70
+ }
33
71
  }
34
72
  catch (e) { }
35
73
  }
@@ -42,8 +80,11 @@ function findRustBinary() {
42
80
  const out = execSync(`${which} ${name}`, { encoding: 'utf-8' })
43
81
  .split('\n')[0]
44
82
  .trim();
45
- if (out && existsSync(out))
83
+ if (out && existsSync(out)) {
84
+ // eslint-disable-next-line no-console
85
+ console.debug(`Found Rust binary in PATH: ${out}`);
46
86
  return out;
87
+ }
47
88
  }
48
89
  catch (e) {
49
90
  // ignore
@@ -62,30 +103,66 @@ export async function encodeWithRustCLI(inputPath, outputPath, compressionLevel
62
103
  throw new Error('Rust CLI binary not found. Run: cargo build --release');
63
104
  }
64
105
  return new Promise((resolve, reject) => {
65
- const args = ['encode', '--level', String(compressionLevel)];
66
- if (name) {
67
- args.push('--name', name);
68
- }
69
- if (passphrase) {
70
- args.push('--passphrase', passphrase);
71
- args.push('--encrypt', encryptType);
72
- }
106
+ const baseArgs = ['encode', '--level', String(compressionLevel)];
107
+ const addNameArgs = (arr) => {
108
+ if (name) {
109
+ arr.push('--name', name);
110
+ }
111
+ };
112
+ const addPassArgs = (arr) => {
113
+ if (passphrase) {
114
+ arr.push('--passphrase', passphrase);
115
+ arr.push('--encrypt', encryptType);
116
+ }
117
+ };
118
+ const args = [...baseArgs];
119
+ addNameArgs(args);
120
+ addPassArgs(args);
73
121
  args.push(inputPath, outputPath);
74
- const proc = spawn(cliPath, args);
75
- let stderr = '';
76
- proc.stderr.on('data', (data) => {
77
- stderr += data.toString();
78
- });
79
- proc.on('error', (err) => {
80
- reject(new Error(`Failed to spawn Rust CLI: ${err.message}`));
81
- });
82
- proc.on('close', (code) => {
83
- if (code === 0) {
84
- resolve();
122
+ const spawnAndWait = (argsToUse) => {
123
+ return new Promise((res, rej) => {
124
+ const proc = spawn(cliPath, argsToUse);
125
+ let stderr = '';
126
+ proc.stderr.on('data', (data) => {
127
+ stderr += data.toString();
128
+ });
129
+ proc.on('error', (err) => rej(err));
130
+ proc.on('close', (code) => res({ code, stderr }));
131
+ });
132
+ };
133
+ (async () => {
134
+ try {
135
+ const debugMsg = `Rust CLI: ${cliPath} ${args.join(' ')}`;
136
+ // eslint-disable-next-line no-console
137
+ console.log(debugMsg);
138
+ let result = await spawnAndWait(args);
139
+ if (result.code === 0)
140
+ return resolve();
141
+ // If the error mentions an unexpected '--name' arg (older binary), retry without name
142
+ if (name &&
143
+ result.stderr &&
144
+ (/unexpected argument.*--name/.test(result.stderr) ||
145
+ /unexpected argument .*'--name'/.test(result.stderr) ||
146
+ result.stderr.includes("'--name'"))) {
147
+ const argsNoName = [...baseArgs];
148
+ addPassArgs(argsNoName);
149
+ argsNoName.push(inputPath, outputPath);
150
+ // eslint-disable-next-line no-console
151
+ console.log('Rust CLI rejected --name; retrying without --name');
152
+ const retryDebug = `Retrying Rust CLI: ${cliPath} ${argsNoName.join(' ')}`;
153
+ // eslint-disable-next-line no-console
154
+ console.log(retryDebug);
155
+ result = await spawnAndWait(argsNoName);
156
+ // eslint-disable-next-line no-console
157
+ console.log(`Rust retry exited with code ${result.code}`);
158
+ if (result.code === 0)
159
+ return resolve();
160
+ }
161
+ reject(new Error(`Rust CLI exited with code ${result.code}: ${result.stderr}`));
85
162
  }
86
- else {
87
- reject(new Error(`Rust CLI exited with code ${code}: ${stderr}`));
163
+ catch (err) {
164
+ reject(new Error(`Failed to spawn Rust CLI: ${err.message || err}`));
88
165
  }
89
- });
166
+ })();
90
167
  });
91
168
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roxify",
3
- "version": "1.5.8",
3
+ "version": "1.5.10",
4
4
  "description": "Ultra-lightweight PNG steganography with native Rust acceleration. Encode binary data into PNG images with zstd compression.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",