@blazediff/bin 2.1.0 → 3.1.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/README.md CHANGED
@@ -10,12 +10,17 @@
10
10
  The fastest single-threaded image diff in the world. Native Rust implementation with SIMD optimization, **3-4x faster** and **3x smaller** than [odiff](https://github.com/dmtrKovalenko/odiff).
11
11
 
12
12
  **Features:**
13
+ - **PNG & JPEG support** - auto-detected by file extension
13
14
  - SIMD-accelerated (NEON on ARM, SSE4.1 on x86)
14
15
  - Block-based two-pass optimization
15
16
  - YIQ perceptual color difference
16
17
  - Anti-aliasing detection
17
18
  - Cross-platform pre-built binaries (~700KB-900KB, no compilation required)
18
19
 
20
+ **Vendored Libraries:**
21
+ - [libspng](https://libspng.org/) - Fast PNG decoding/encoding with SIMD
22
+ - [libjpeg-turbo](https://libjpeg-turbo.org/) - High-performance JPEG codec with SIMD
23
+
19
24
  ## Installation
20
25
 
21
26
  ```bash
@@ -34,7 +39,7 @@ Pre-built [binaries](https://github.com/teimurjan/blazediff/tree/main/packages/b
34
39
 
35
40
  ### compare(basePath, comparePath, diffOutput, options?)
36
41
 
37
- Compare two PNG images and generate a diff image.
42
+ Compare two images (PNG or JPEG) and generate a diff image. Format is auto-detected from file extension.
38
43
 
39
44
  <table>
40
45
  <tr>
@@ -133,15 +138,24 @@ if (result.match) {
133
138
  ### CLI Usage
134
139
 
135
140
  ```bash
136
- # Compare two images
141
+ # Compare two PNG images
137
142
  npx blazediff expected.png actual.png diff.png
138
143
 
144
+ # Compare two JPEG images
145
+ npx blazediff expected.jpg actual.jpg diff.jpg
146
+
147
+ # Mixed formats (PNG input, JPEG output)
148
+ npx blazediff expected.png actual.png diff.jpg
149
+
139
150
  # With options
140
151
  npx blazediff expected.png actual.png diff.png --threshold 0.05 --antialiasing
141
152
 
142
- # With higher compression (smaller output file, slower)
153
+ # With higher PNG compression (smaller output file, slower)
143
154
  npx blazediff expected.png actual.png diff.png -c 6
144
155
 
156
+ # With JPEG quality setting
157
+ npx blazediff expected.jpg actual.jpg diff.jpg -q 85
158
+
145
159
  # Output as JSON
146
160
  npx blazediff expected.png actual.png diff.png --output-format json
147
161
  ```
@@ -149,24 +163,34 @@ npx blazediff expected.png actual.png diff.png --output-format json
149
163
  ### CLI Options
150
164
 
151
165
  ```
152
- Usage: blazediff [OPTIONS] <IMAGE1> <IMAGE2> <OUTPUT>
166
+ Usage: blazediff [OPTIONS] <IMAGE1> <IMAGE2> [OUTPUT]
153
167
 
154
168
  Arguments:
155
- <IMAGE1> First image path
156
- <IMAGE2> Second image path
157
- <OUTPUT> Output diff image path
169
+ <IMAGE1> First image path (PNG or JPEG)
170
+ <IMAGE2> Second image path (PNG or JPEG)
171
+ [OUTPUT] Output diff image path (optional, format detected from extension)
158
172
 
159
173
  Options:
160
174
  -t, --threshold <THRESHOLD> Color difference threshold (0.0-1.0) [default: 0.1]
161
175
  -a, --antialiasing Enable anti-aliasing detection
162
176
  --diff-mask Output only differences (transparent background)
163
177
  --fail-on-layout Fail on layout (size) difference
164
- -c, --compression <LEVEL> PNG compression level (0-9, 0=fastest/largest, 9=slowest/smallest) [default: 0]
178
+ -c, --compression <LEVEL> PNG compression level (0-9, 0=fastest, 9=smallest) [default: 0]
179
+ -q, --quality <QUALITY> JPEG quality (1-100) [default: 90]
165
180
  --output-format <FORMAT> Output format (json or text) [default: json]
166
181
  -h, --help Print help
167
182
  -V, --version Print version
168
183
  ```
169
184
 
185
+ ### Supported Formats
186
+
187
+ | Format | Extensions | Notes |
188
+ |--------|------------|-------|
189
+ | PNG | `.png` | Lossless, supports transparency |
190
+ | JPEG | `.jpg`, `.jpeg` | Lossy, smaller file sizes |
191
+
192
+ Input images can be mixed formats (e.g., compare PNG to JPEG). Output format is determined by the output file extension.
193
+
170
194
  ### Exit Codes
171
195
 
172
196
  - `0` - Images are identical
@@ -175,7 +199,7 @@ Options:
175
199
 
176
200
  ## Performance
177
201
 
178
- Benchmarked on Apple M1 Pro with 5600x3200 4K images:
202
+ Benchmarked on Apple M1 Pro with 5600x3200 4K PNG images:
179
203
 
180
204
  | Tool | Benchmark Time | vs blazediff |
181
205
  |------|------|--------------|
package/dist/index.d.mts CHANGED
@@ -9,6 +9,8 @@ interface BlazeDiffOptions {
9
9
  failOnLayoutDiff?: boolean;
10
10
  /** PNG compression level (0-9, 0=fastest/largest, 9=slowest/smallest) */
11
11
  compression?: number;
12
+ /** JPEG quality (1-100). Default: 90 */
13
+ quality?: number;
12
14
  }
13
15
  type BlazeDiffResult = {
14
16
  match: true;
@@ -26,7 +28,11 @@ type BlazeDiffResult = {
26
28
  file: string;
27
29
  };
28
30
  /**
29
- * Compare two PNG images and optionally generate a diff image.
31
+ * Compare two images (PNG or JPEG) and optionally generate a diff image.
32
+ *
33
+ * Uses native N-API bindings when available for ~10-100x better performance
34
+ * on small images (no process spawn overhead). Falls back to execFile if
35
+ * native bindings are unavailable.
30
36
  *
31
37
  * @example
32
38
  * ```ts
@@ -46,5 +52,10 @@ type BlazeDiffResult = {
46
52
  declare function compare(basePath: string, comparePath: string, diffOutput?: string, options?: BlazeDiffOptions): Promise<BlazeDiffResult>;
47
53
  /** Get the path to the blazediff binary for direct CLI usage. */
48
54
  declare function getBinaryPath(): string;
55
+ /**
56
+ * Check if native N-API bindings are available.
57
+ * Returns true if the native module loaded successfully.
58
+ */
59
+ declare function hasNativeBinding(): boolean;
49
60
 
50
- export { type BlazeDiffOptions, type BlazeDiffResult, compare, getBinaryPath };
61
+ export { type BlazeDiffOptions, type BlazeDiffResult, compare, getBinaryPath, hasNativeBinding };
package/dist/index.d.ts CHANGED
@@ -9,6 +9,8 @@ interface BlazeDiffOptions {
9
9
  failOnLayoutDiff?: boolean;
10
10
  /** PNG compression level (0-9, 0=fastest/largest, 9=slowest/smallest) */
11
11
  compression?: number;
12
+ /** JPEG quality (1-100). Default: 90 */
13
+ quality?: number;
12
14
  }
13
15
  type BlazeDiffResult = {
14
16
  match: true;
@@ -26,7 +28,11 @@ type BlazeDiffResult = {
26
28
  file: string;
27
29
  };
28
30
  /**
29
- * Compare two PNG images and optionally generate a diff image.
31
+ * Compare two images (PNG or JPEG) and optionally generate a diff image.
32
+ *
33
+ * Uses native N-API bindings when available for ~10-100x better performance
34
+ * on small images (no process spawn overhead). Falls back to execFile if
35
+ * native bindings are unavailable.
30
36
  *
31
37
  * @example
32
38
  * ```ts
@@ -46,5 +52,10 @@ type BlazeDiffResult = {
46
52
  declare function compare(basePath: string, comparePath: string, diffOutput?: string, options?: BlazeDiffOptions): Promise<BlazeDiffResult>;
47
53
  /** Get the path to the blazediff binary for direct CLI usage. */
48
54
  declare function getBinaryPath(): string;
55
+ /**
56
+ * Check if native N-API bindings are available.
57
+ * Returns true if the native module loaded successfully.
58
+ */
59
+ declare function hasNativeBinding(): boolean;
49
60
 
50
- export { type BlazeDiffOptions, type BlazeDiffResult, compare, getBinaryPath };
61
+ export { type BlazeDiffOptions, type BlazeDiffResult, compare, getBinaryPath, hasNativeBinding };
package/dist/index.js CHANGED
@@ -31,24 +31,182 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  compare: () => compare,
34
- getBinaryPath: () => getBinaryPath
34
+ getBinaryPath: () => getBinaryPath,
35
+ hasNativeBinding: () => hasNativeBinding
35
36
  });
36
37
  module.exports = __toCommonJS(index_exports);
38
+
39
+ // ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.0_postcss@8.5.6_tsx@4.20.6_typescript@5.9.2_yaml@2.8.1/node_modules/tsup/assets/cjs_shims.js
40
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
41
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
42
+
43
+ // src/index.ts
37
44
  var import_node_child_process = require("child_process");
45
+ var import_node_fs = require("fs");
46
+ var import_node_module = require("module");
47
+ var import_node_os = __toESM(require("os"));
38
48
  var import_node_path = __toESM(require("path"));
49
+ var import_node_url = require("url");
39
50
  var import_node_util = require("util");
40
51
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
41
- var BINARY_PATH = import_node_path.default.join(__dirname, "..", "bin", "blazediff.exe");
52
+ var PLATFORM_PACKAGES = {
53
+ "darwin-arm64": {
54
+ packageName: "@blazediff/bin-darwin-arm64",
55
+ packageDir: "bin-darwin-arm64"
56
+ },
57
+ "darwin-x64": {
58
+ packageName: "@blazediff/bin-darwin-x64",
59
+ packageDir: "bin-darwin-x64"
60
+ },
61
+ "linux-arm64": {
62
+ packageName: "@blazediff/bin-linux-arm64",
63
+ packageDir: "bin-linux-arm64"
64
+ },
65
+ "linux-x64": {
66
+ packageName: "@blazediff/bin-linux-x64",
67
+ packageDir: "bin-linux-x64"
68
+ },
69
+ "win32-arm64": {
70
+ packageName: "@blazediff/bin-win32-arm64",
71
+ packageDir: "bin-win32-arm64"
72
+ },
73
+ "win32-x64": {
74
+ packageName: "@blazediff/bin-win32-x64",
75
+ packageDir: "bin-win32-x64"
76
+ }
77
+ };
78
+ var nativeBinding = null;
79
+ var nativeBindingAttempted = false;
80
+ function tryLoadNativeBinding() {
81
+ if (nativeBindingAttempted) {
82
+ return nativeBinding;
83
+ }
84
+ nativeBindingAttempted = true;
85
+ const platform = import_node_os.default.platform();
86
+ const arch = import_node_os.default.arch();
87
+ const key = `${platform}-${arch}`;
88
+ const platformInfo = PLATFORM_PACKAGES[key];
89
+ if (!platformInfo) {
90
+ return null;
91
+ }
92
+ try {
93
+ const require2 = (0, import_node_module.createRequire)(importMetaUrl);
94
+ const binding = require2(platformInfo.packageName);
95
+ if (typeof binding?.compare === "function") {
96
+ nativeBinding = binding;
97
+ return binding;
98
+ }
99
+ } catch {
100
+ }
101
+ try {
102
+ const currentDir = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
103
+ const packagesDir = import_node_path.default.resolve(currentDir, "..", "..");
104
+ const nodePath = import_node_path.default.join(
105
+ packagesDir,
106
+ platformInfo.packageDir,
107
+ "blazediff.node"
108
+ );
109
+ if ((0, import_node_fs.existsSync)(nodePath)) {
110
+ const require2 = (0, import_node_module.createRequire)(importMetaUrl);
111
+ const binding = require2(nodePath);
112
+ if (typeof binding?.compare === "function") {
113
+ nativeBinding = binding;
114
+ return binding;
115
+ }
116
+ }
117
+ } catch {
118
+ }
119
+ return null;
120
+ }
121
+ function convertNapiResult(result) {
122
+ if (result.matchResult) {
123
+ return { match: true };
124
+ }
125
+ if (result.reason === "layout-diff") {
126
+ return { match: false, reason: "layout-diff" };
127
+ }
128
+ if (result.reason === "pixel-diff") {
129
+ return {
130
+ match: false,
131
+ reason: "pixel-diff",
132
+ diffCount: result.diffCount ?? 0,
133
+ diffPercentage: result.diffPercentage ?? 0
134
+ };
135
+ }
136
+ return {
137
+ match: false,
138
+ reason: "pixel-diff",
139
+ diffCount: result.diffCount ?? 0,
140
+ diffPercentage: result.diffPercentage ?? 0
141
+ };
142
+ }
143
+ function convertToNapiOptions(options) {
144
+ return {
145
+ threshold: options?.threshold,
146
+ antialiasing: options?.antialiasing,
147
+ diffMask: options?.diffMask,
148
+ failOnLayout: options?.failOnLayoutDiff,
149
+ compression: options?.compression,
150
+ quality: options?.quality
151
+ };
152
+ }
153
+ function resolveBinaryPath() {
154
+ const platform = import_node_os.default.platform();
155
+ const arch = import_node_os.default.arch();
156
+ const key = `${platform}-${arch}`;
157
+ const platformInfo = PLATFORM_PACKAGES[key];
158
+ if (!platformInfo) {
159
+ throw new Error(
160
+ `Unsupported platform: ${key}. Supported: ${Object.keys(PLATFORM_PACKAGES).join(", ")}`
161
+ );
162
+ }
163
+ const binaryName = platform === "win32" ? "blazediff.exe" : "blazediff";
164
+ try {
165
+ const require2 = (0, import_node_module.createRequire)(importMetaUrl);
166
+ const packagePath = require2.resolve(
167
+ `${platformInfo.packageName}/package.json`
168
+ );
169
+ const packageDir = import_node_path.default.dirname(packagePath);
170
+ const binaryPath = import_node_path.default.join(packageDir, binaryName);
171
+ if ((0, import_node_fs.existsSync)(binaryPath)) {
172
+ return binaryPath;
173
+ }
174
+ } catch {
175
+ }
176
+ const currentDir = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
177
+ const packagesDir = import_node_path.default.resolve(currentDir, "..", "..");
178
+ const siblingPath = import_node_path.default.join(
179
+ packagesDir,
180
+ platformInfo.packageDir,
181
+ binaryName
182
+ );
183
+ if ((0, import_node_fs.existsSync)(siblingPath)) {
184
+ return siblingPath;
185
+ }
186
+ throw new Error(
187
+ `Platform package ${platformInfo.packageName} is not installed. This usually means the optional dependency wasn't installed for your platform. Try reinstalling with: npm install @blazediff/bin`
188
+ );
189
+ }
190
+ var cachedBinaryPath = null;
191
+ function getBinaryPathInternal() {
192
+ if (!cachedBinaryPath) {
193
+ cachedBinaryPath = resolveBinaryPath();
194
+ }
195
+ return cachedBinaryPath;
196
+ }
42
197
  function buildArgs(diffOutput, options) {
43
198
  const args = [];
44
199
  if (diffOutput) args.push(diffOutput);
45
200
  args.push("--output-format=json");
46
201
  if (!options) return args;
47
- if (options.threshold !== void 0) args.push(`--threshold=${options.threshold}`);
202
+ if (options.threshold !== void 0)
203
+ args.push(`--threshold=${options.threshold}`);
48
204
  if (options.antialiasing) args.push("--antialiasing");
49
205
  if (options.diffMask) args.push("--diff-mask");
50
206
  if (options.failOnLayoutDiff) args.push("--fail-on-layout");
51
- if (options.compression !== void 0) args.push(`--compression=${options.compression}`);
207
+ if (options.compression !== void 0)
208
+ args.push(`--compression=${options.compression}`);
209
+ if (options.quality !== void 0) args.push(`--quality=${options.quality}`);
52
210
  return args;
53
211
  }
54
212
  function parseJsonOutput(text) {
@@ -66,10 +224,11 @@ function detectMissingFile(error, basePath, comparePath) {
66
224
  if (error.includes(comparePath)) return comparePath;
67
225
  return basePath;
68
226
  }
69
- async function compare(basePath, comparePath, diffOutput, options) {
227
+ async function execFileCompare(basePath, comparePath, diffOutput, options) {
228
+ const binaryPath = getBinaryPathInternal();
70
229
  const args = [basePath, comparePath, ...buildArgs(diffOutput, options)];
71
230
  try {
72
- await execFileAsync(BINARY_PATH, args);
231
+ await execFileAsync(binaryPath, args);
73
232
  return { match: true };
74
233
  } catch (err) {
75
234
  const { code, stdout, stderr } = err;
@@ -100,11 +259,37 @@ async function compare(basePath, comparePath, diffOutput, options) {
100
259
  throw new Error(output || `blazediff exited with code ${code}`);
101
260
  }
102
261
  }
262
+ async function compare(basePath, comparePath, diffOutput, options) {
263
+ const binding = tryLoadNativeBinding();
264
+ if (binding) {
265
+ try {
266
+ const result = binding.compare(
267
+ basePath,
268
+ comparePath,
269
+ diffOutput ?? null,
270
+ convertToNapiOptions(options)
271
+ );
272
+ return convertNapiResult(result);
273
+ } catch (err) {
274
+ const message = err instanceof Error ? err.message : String(err);
275
+ const missingFile = detectMissingFile(message, basePath, comparePath);
276
+ if (missingFile) {
277
+ return { match: false, reason: "file-not-exists", file: missingFile };
278
+ }
279
+ throw err;
280
+ }
281
+ }
282
+ return execFileCompare(basePath, comparePath, diffOutput, options);
283
+ }
103
284
  function getBinaryPath() {
104
- return BINARY_PATH;
285
+ return getBinaryPathInternal();
286
+ }
287
+ function hasNativeBinding() {
288
+ return tryLoadNativeBinding() !== null;
105
289
  }
106
290
  // Annotate the CommonJS export names for ESM import in node:
107
291
  0 && (module.exports = {
108
292
  compare,
109
- getBinaryPath
293
+ getBinaryPath,
294
+ hasNativeBinding
110
295
  });
package/dist/index.mjs CHANGED
@@ -1,26 +1,170 @@
1
- // ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.0_postcss@8.5.6_tsx@4.20.6_typescript@5.9.2_yaml@2.8.1/node_modules/tsup/assets/esm_shims.js
2
- import path from "path";
3
- import { fileURLToPath } from "url";
4
- var getFilename = () => fileURLToPath(import.meta.url);
5
- var getDirname = () => path.dirname(getFilename());
6
- var __dirname = /* @__PURE__ */ getDirname();
7
-
8
1
  // src/index.ts
9
2
  import { execFile } from "child_process";
10
- import path2 from "path";
3
+ import { existsSync } from "fs";
4
+ import { createRequire } from "module";
5
+ import os from "os";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
11
8
  import { promisify } from "util";
12
9
  var execFileAsync = promisify(execFile);
13
- var BINARY_PATH = path2.join(__dirname, "..", "bin", "blazediff.exe");
10
+ var PLATFORM_PACKAGES = {
11
+ "darwin-arm64": {
12
+ packageName: "@blazediff/bin-darwin-arm64",
13
+ packageDir: "bin-darwin-arm64"
14
+ },
15
+ "darwin-x64": {
16
+ packageName: "@blazediff/bin-darwin-x64",
17
+ packageDir: "bin-darwin-x64"
18
+ },
19
+ "linux-arm64": {
20
+ packageName: "@blazediff/bin-linux-arm64",
21
+ packageDir: "bin-linux-arm64"
22
+ },
23
+ "linux-x64": {
24
+ packageName: "@blazediff/bin-linux-x64",
25
+ packageDir: "bin-linux-x64"
26
+ },
27
+ "win32-arm64": {
28
+ packageName: "@blazediff/bin-win32-arm64",
29
+ packageDir: "bin-win32-arm64"
30
+ },
31
+ "win32-x64": {
32
+ packageName: "@blazediff/bin-win32-x64",
33
+ packageDir: "bin-win32-x64"
34
+ }
35
+ };
36
+ var nativeBinding = null;
37
+ var nativeBindingAttempted = false;
38
+ function tryLoadNativeBinding() {
39
+ if (nativeBindingAttempted) {
40
+ return nativeBinding;
41
+ }
42
+ nativeBindingAttempted = true;
43
+ const platform = os.platform();
44
+ const arch = os.arch();
45
+ const key = `${platform}-${arch}`;
46
+ const platformInfo = PLATFORM_PACKAGES[key];
47
+ if (!platformInfo) {
48
+ return null;
49
+ }
50
+ try {
51
+ const require2 = createRequire(import.meta.url);
52
+ const binding = require2(platformInfo.packageName);
53
+ if (typeof binding?.compare === "function") {
54
+ nativeBinding = binding;
55
+ return binding;
56
+ }
57
+ } catch {
58
+ }
59
+ try {
60
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
61
+ const packagesDir = path.resolve(currentDir, "..", "..");
62
+ const nodePath = path.join(
63
+ packagesDir,
64
+ platformInfo.packageDir,
65
+ "blazediff.node"
66
+ );
67
+ if (existsSync(nodePath)) {
68
+ const require2 = createRequire(import.meta.url);
69
+ const binding = require2(nodePath);
70
+ if (typeof binding?.compare === "function") {
71
+ nativeBinding = binding;
72
+ return binding;
73
+ }
74
+ }
75
+ } catch {
76
+ }
77
+ return null;
78
+ }
79
+ function convertNapiResult(result) {
80
+ if (result.matchResult) {
81
+ return { match: true };
82
+ }
83
+ if (result.reason === "layout-diff") {
84
+ return { match: false, reason: "layout-diff" };
85
+ }
86
+ if (result.reason === "pixel-diff") {
87
+ return {
88
+ match: false,
89
+ reason: "pixel-diff",
90
+ diffCount: result.diffCount ?? 0,
91
+ diffPercentage: result.diffPercentage ?? 0
92
+ };
93
+ }
94
+ return {
95
+ match: false,
96
+ reason: "pixel-diff",
97
+ diffCount: result.diffCount ?? 0,
98
+ diffPercentage: result.diffPercentage ?? 0
99
+ };
100
+ }
101
+ function convertToNapiOptions(options) {
102
+ return {
103
+ threshold: options?.threshold,
104
+ antialiasing: options?.antialiasing,
105
+ diffMask: options?.diffMask,
106
+ failOnLayout: options?.failOnLayoutDiff,
107
+ compression: options?.compression,
108
+ quality: options?.quality
109
+ };
110
+ }
111
+ function resolveBinaryPath() {
112
+ const platform = os.platform();
113
+ const arch = os.arch();
114
+ const key = `${platform}-${arch}`;
115
+ const platformInfo = PLATFORM_PACKAGES[key];
116
+ if (!platformInfo) {
117
+ throw new Error(
118
+ `Unsupported platform: ${key}. Supported: ${Object.keys(PLATFORM_PACKAGES).join(", ")}`
119
+ );
120
+ }
121
+ const binaryName = platform === "win32" ? "blazediff.exe" : "blazediff";
122
+ try {
123
+ const require2 = createRequire(import.meta.url);
124
+ const packagePath = require2.resolve(
125
+ `${platformInfo.packageName}/package.json`
126
+ );
127
+ const packageDir = path.dirname(packagePath);
128
+ const binaryPath = path.join(packageDir, binaryName);
129
+ if (existsSync(binaryPath)) {
130
+ return binaryPath;
131
+ }
132
+ } catch {
133
+ }
134
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
135
+ const packagesDir = path.resolve(currentDir, "..", "..");
136
+ const siblingPath = path.join(
137
+ packagesDir,
138
+ platformInfo.packageDir,
139
+ binaryName
140
+ );
141
+ if (existsSync(siblingPath)) {
142
+ return siblingPath;
143
+ }
144
+ throw new Error(
145
+ `Platform package ${platformInfo.packageName} is not installed. This usually means the optional dependency wasn't installed for your platform. Try reinstalling with: npm install @blazediff/bin`
146
+ );
147
+ }
148
+ var cachedBinaryPath = null;
149
+ function getBinaryPathInternal() {
150
+ if (!cachedBinaryPath) {
151
+ cachedBinaryPath = resolveBinaryPath();
152
+ }
153
+ return cachedBinaryPath;
154
+ }
14
155
  function buildArgs(diffOutput, options) {
15
156
  const args = [];
16
157
  if (diffOutput) args.push(diffOutput);
17
158
  args.push("--output-format=json");
18
159
  if (!options) return args;
19
- if (options.threshold !== void 0) args.push(`--threshold=${options.threshold}`);
160
+ if (options.threshold !== void 0)
161
+ args.push(`--threshold=${options.threshold}`);
20
162
  if (options.antialiasing) args.push("--antialiasing");
21
163
  if (options.diffMask) args.push("--diff-mask");
22
164
  if (options.failOnLayoutDiff) args.push("--fail-on-layout");
23
- if (options.compression !== void 0) args.push(`--compression=${options.compression}`);
165
+ if (options.compression !== void 0)
166
+ args.push(`--compression=${options.compression}`);
167
+ if (options.quality !== void 0) args.push(`--quality=${options.quality}`);
24
168
  return args;
25
169
  }
26
170
  function parseJsonOutput(text) {
@@ -38,10 +182,11 @@ function detectMissingFile(error, basePath, comparePath) {
38
182
  if (error.includes(comparePath)) return comparePath;
39
183
  return basePath;
40
184
  }
41
- async function compare(basePath, comparePath, diffOutput, options) {
185
+ async function execFileCompare(basePath, comparePath, diffOutput, options) {
186
+ const binaryPath = getBinaryPathInternal();
42
187
  const args = [basePath, comparePath, ...buildArgs(diffOutput, options)];
43
188
  try {
44
- await execFileAsync(BINARY_PATH, args);
189
+ await execFileAsync(binaryPath, args);
45
190
  return { match: true };
46
191
  } catch (err) {
47
192
  const { code, stdout, stderr } = err;
@@ -72,10 +217,36 @@ async function compare(basePath, comparePath, diffOutput, options) {
72
217
  throw new Error(output || `blazediff exited with code ${code}`);
73
218
  }
74
219
  }
220
+ async function compare(basePath, comparePath, diffOutput, options) {
221
+ const binding = tryLoadNativeBinding();
222
+ if (binding) {
223
+ try {
224
+ const result = binding.compare(
225
+ basePath,
226
+ comparePath,
227
+ diffOutput ?? null,
228
+ convertToNapiOptions(options)
229
+ );
230
+ return convertNapiResult(result);
231
+ } catch (err) {
232
+ const message = err instanceof Error ? err.message : String(err);
233
+ const missingFile = detectMissingFile(message, basePath, comparePath);
234
+ if (missingFile) {
235
+ return { match: false, reason: "file-not-exists", file: missingFile };
236
+ }
237
+ throw err;
238
+ }
239
+ }
240
+ return execFileCompare(basePath, comparePath, diffOutput, options);
241
+ }
75
242
  function getBinaryPath() {
76
- return BINARY_PATH;
243
+ return getBinaryPathInternal();
244
+ }
245
+ function hasNativeBinding() {
246
+ return tryLoadNativeBinding() !== null;
77
247
  }
78
248
  export {
79
249
  compare,
80
- getBinaryPath
250
+ getBinaryPath,
251
+ hasNativeBinding
81
252
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blazediff/bin",
3
- "version": "2.1.0",
3
+ "version": "3.1.0",
4
4
  "description": "Native Rust binaries for blazediff - the fastest image diff in the world",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -16,15 +16,17 @@
16
16
  "require": "./dist/index.js"
17
17
  }
18
18
  },
19
- "bin": {
20
- "blazediff": "bin/blazediff.exe"
21
- },
22
19
  "files": [
23
- "dist",
24
- "bin",
25
- "binaries",
26
- "post_install.js"
20
+ "dist"
27
21
  ],
22
+ "optionalDependencies": {
23
+ "@blazediff/bin-darwin-arm64": "3.1.0",
24
+ "@blazediff/bin-darwin-x64": "3.1.0",
25
+ "@blazediff/bin-linux-arm64": "3.1.0",
26
+ "@blazediff/bin-linux-x64": "3.1.0",
27
+ "@blazediff/bin-win32-arm64": "3.1.0",
28
+ "@blazediff/bin-win32-x64": "3.1.0"
29
+ },
28
30
  "keywords": [
29
31
  "image",
30
32
  "comparison",
@@ -48,11 +50,7 @@
48
50
  "typescript": "5.9.2"
49
51
  },
50
52
  "scripts": {
51
- "postinstall": "node ./post_install.js",
52
53
  "build": "tsup",
53
- "build:rust": "cd rust && ./scripts/build-all.sh --all",
54
- "build:rust:local": "cd rust && cargo build --release && cp target/release/blazediff ../binaries/blazediff-$(node -e \"console.log(process.platform === 'darwin' ? 'macos' : process.platform)-$(process.arch === 'arm64' ? 'arm64' : 'x64')\")",
55
- "prepare-binaries": "node ./scripts/prepare-binaries.js",
56
- "clean": "rm -rf dist bin"
54
+ "clean": "rm -rf dist"
57
55
  }
58
56
  }
package/bin/blazediff.exe DELETED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/post_install.js DELETED
@@ -1,40 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const os = require("os");
4
-
5
- const binaries = {
6
- "linux-x64": "blazediff-linux-x64",
7
- "linux-arm64": "blazediff-linux-arm64",
8
- "darwin-arm64": "blazediff-macos-arm64",
9
- "darwin-x64": "blazediff-macos-x64",
10
- "win32-x64": "blazediff-windows-x64.exe",
11
- "win32-arm64": "blazediff-windows-arm64.exe",
12
- };
13
-
14
- const platform = os.platform();
15
- const arch = os.arch();
16
-
17
- const binaryKey = `${platform}-${arch}`;
18
- const binaryFile = binaries[binaryKey];
19
-
20
- if (!binaryFile) {
21
- console.error(
22
- `blazediff: Sorry your platform or architecture is not supported. Supported: ${Object.keys(binaries).join(", ")}`,
23
- );
24
- process.exit(1);
25
- }
26
-
27
- const sourcePath = path.join(__dirname, "binaries", binaryFile);
28
- const binDir = path.join(__dirname, "bin");
29
- const destPath = path.join(binDir, "blazediff.exe");
30
-
31
- try {
32
- if (!fs.existsSync(binDir)) {
33
- fs.mkdirSync(binDir, { recursive: true });
34
- }
35
- fs.copyFileSync(sourcePath, destPath);
36
- fs.chmodSync(destPath, 0o755);
37
- } catch (err) {
38
- console.error(`blazediff: failed to copy and link the binary file: ${err}`);
39
- process.exit(1);
40
- }