@blazediff/matcher 1.3.0 → 1.4.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
@@ -11,7 +11,7 @@ Core matcher logic for visual regression testing. Provides snapshot comparison w
11
11
 
12
12
  ## Features
13
13
 
14
- - **Multiple comparison methods**: `core`, `bin`, `ssim`, `msssim`, `hitchhikers-ssim`, `gmsd`
14
+ - **Multiple comparison methods**: `core`, `core-native`, `ssim`, `msssim`, `hitchhikers-ssim`, `gmsd`
15
15
  - **Flexible input types**: File paths or image buffers
16
16
  - **Snapshot state tracking**: Reports added/matched/updated/failed status
17
17
  - **Configurable thresholds**: Pixel count or percentage-based
@@ -127,13 +127,13 @@ Main function for snapshot comparison.
127
127
  <td><code>threshold</code></td>
128
128
  <td>number</td>
129
129
  <td>0.1</td>
130
- <td>Color difference threshold for core/bin methods (0-1)</td>
130
+ <td>Color difference threshold for core/core-native methods (0-1)</td>
131
131
  </tr>
132
132
  <tr>
133
133
  <td><code>antialiasing</code></td>
134
134
  <td>boolean</td>
135
135
  <td>false</td>
136
- <td>Enable anti-aliasing detection (bin method)</td>
136
+ <td>Enable anti-aliasing detection (core-native method)</td>
137
137
  </tr>
138
138
  <tr>
139
139
  <td><code>includeAA</code></td>
@@ -242,7 +242,7 @@ type ImageInput =
242
242
 
243
243
  ## Comparison Methods
244
244
 
245
- ### `bin`
245
+ ### `core-native`
246
246
  Rust-native comparison via N-API bindings. **Fastest method** with native performance.
247
247
  - **Input**: File paths only
248
248
  - **Algorithm**: YIQ color space with block-based optimization
@@ -287,7 +287,7 @@ Gradient Magnitude Similarity Deviation.
287
287
  ```typescript
288
288
  const result = await getOrCreateSnapshot(
289
289
  '/path/to/screenshot.png',
290
- { method: 'bin' },
290
+ { method: 'core-native' },
291
291
  { testPath: __filename, testName: 'test name' }
292
292
  );
293
293
  ```
@@ -326,7 +326,7 @@ const result = await getOrCreateSnapshot(
326
326
 
327
327
  ```typescript
328
328
  // Fastest - Rust native (file paths only)
329
- await getOrCreateSnapshot(imagePath, { method: 'bin' }, context);
329
+ await getOrCreateSnapshot(imagePath, { method: 'core-native' }, context);
330
330
 
331
331
  // Pure JavaScript
332
332
  await getOrCreateSnapshot(imageBuffer, { method: 'core' }, context);
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Comparison methods available in blazediff
3
3
  */
4
- type ComparisonMethod = "bin" | "core" | "ssim" | "msssim" | "hitchhikers-ssim" | "gmsd";
4
+ type ComparisonMethod = "core-native" | "core" | "ssim" | "msssim" | "hitchhikers-ssim" | "gmsd";
5
5
  /**
6
6
  * Status of a snapshot operation
7
7
  */
@@ -56,13 +56,13 @@ interface MatcherOptions {
56
56
  */
57
57
  updateCommand?: string;
58
58
  /**
59
- * Color difference threshold for core/bin methods (0-1)
59
+ * Color difference threshold for core/core-native methods (0-1)
60
60
  * Lower = more strict
61
61
  * @default 0.1
62
62
  */
63
63
  threshold?: number;
64
64
  /**
65
- * Enable anti-aliasing detection (bin method)
65
+ * Enable anti-aliasing detection (core-native method)
66
66
  * @default false
67
67
  */
68
68
  antialiasing?: boolean;
@@ -97,6 +97,13 @@ interface MatcherOptions {
97
97
  * @default true
98
98
  */
99
99
  runInWorker?: boolean;
100
+ /**
101
+ * Output format for diff visualization (core-native method only)
102
+ * - "png": standard diff image (default)
103
+ * - "html": interactive HTML interpret report
104
+ * @default 'png'
105
+ */
106
+ outputFormat?: "png" | "html";
100
107
  }
101
108
  /**
102
109
  * Result of a comparison operation
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict';var fs=require('fs'),path=require('path'),pngjsTransformer=require('@blazediff/pngjs-transformer'),bin=require('@blazediff/bin'),core=require('@blazediff/core'),gmsd=require('@blazediff/gmsd'),ssim=require('@blazediff/ssim'),hitchhikersSsim=require('@blazediff/ssim/hitchhikers-ssim'),msssim=require('@blazediff/ssim/msssim'),s=require('picocolors'),worker_threads=require('worker_threads');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var s__default=/*#__PURE__*/_interopDefault(s);var Ie=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var ie=(e,t)=>{for(var r in t)Ie(e,r,{get:t[r],enumerable:true});};var u=b(()=>{});var me={};ie(me,{ensureDir:()=>ee,fileExists:()=>N,isFilePath:()=>I,isImageBuffer:()=>fe,isImageData:()=>C,isRawPngBuffer:()=>E,loadPNG:()=>B,normalizeImageInput:()=>S,savePNG:()=>P,saveRawPNGBuffer:()=>R});function I(e){return typeof e=="string"}function E(e){return (Buffer.isBuffer(e)||e instanceof Uint8Array)&&!("width"in e)}function fe(e){return typeof e=="object"&&e!==null&&"data"in e&&"width"in e&&"height"in e}function C(e){return typeof e=="object"&&e!==null&&"data"in e&&"width"in e&&"height"in e}async function B(e){if(!fs.existsSync(e))throw new Error(`Image file not found: ${e}`);let t=await pngjsTransformer.pngjsTransformer.read(e);return {data:new Uint8Array(t.data),width:t.width,height:t.height}}async function P(e,t,r,n){let o=path.dirname(e);fs.existsSync(o)||fs.mkdirSync(o,{recursive:true}),await pngjsTransformer.pngjsTransformer.write({data:t instanceof Uint8Array?t:new Uint8Array(t),width:r,height:n},e);}function R(e,t){let r=path.dirname(e);fs.existsSync(r)||fs.mkdirSync(r,{recursive:true}),fs.writeFileSync(e,t);}async function S(e){if(I(e))return B(e);if(E(e)){let t=Buffer.isBuffer(e)?e:Buffer.from(e),r=await pngjsTransformer.pngjsTransformer.read(t);return {data:new Uint8Array(r.data),width:r.width,height:r.height}}return e.data instanceof Uint8Array?e:{data:new Uint8Array(e.data),width:e.width,height:e.height}}function N(e){return fs.existsSync(e)}function ee(e){fs.existsSync(e)||fs.mkdirSync(e,{recursive:true});}var k=b(()=>{u();});async function z(e,t,r,n){if(!I(e))throw new Error("Method 'bin' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");if(!I(t))throw new Error("Method 'bin' only supports file paths for baseline, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");let o=await bin.compare(e,t,r,{threshold:n.threshold,antialiasing:n.antialiasing});if(o.match)return {diffCount:0,diffPercentage:0};if(o.reason==="layout-diff")return {diffCount:Number.MAX_SAFE_INTEGER,diffPercentage:100};if(o.reason==="pixel-diff")return {diffCount:o.diffCount,diffPercentage:o.diffPercentage};if(o.reason==="file-not-exists")throw new Error(`Image file not found: ${o.file}`);return {diffCount:0,diffPercentage:0}}var te=b(()=>{u();k();});function F(e,t,r,n){let{width:o,height:a}=e,i=o*a;if(e.width!==t.width||e.height!==t.height)return {diffCount:i,diffPercentage:100};let f=r?new Uint8Array(i*4):void 0,c=core.diff(e.data,t.data,f,o,a,{threshold:n.threshold??.1,includeAA:n.includeAA??false});return {diffCount:c,diffPercentage:c/i*100,diffOutput:f}}var re=b(()=>{u();});function j(e,t,r,n){let{width:o,height:a}=e,i=o*a;if(e.width!==t.width||e.height!==t.height)return {score:1};let f=r?new Uint8Array(i*4):void 0;return {score:gmsd.gmsd(e.data,t.data,f,o,a,{downsample:n.downsample}),diffOutput:f}}var ae=b(()=>{u();});function v(e,t,r,n,o){let{width:a,height:i}=e,f=a*i;if(e.width!==t.width||e.height!==t.height)return {score:0};let c=n?new Uint8Array(f*4):void 0,m={windowSize:o.windowSize,k1:o.k1,k2:o.k2},x;switch(r){case "ssim":x=ssim.ssim(e.data,t.data,c,a,i,m);break;case "msssim":x=msssim.msssim(e.data,t.data,c,a,i,m);break;case "hitchhikers-ssim":x=hitchhikersSsim.hitchhikersSSIM(e.data,t.data,c,a,i,m);break;default:throw new Error(`Unknown SSIM method: ${r}`)}return {score:x,diffOutput:c}}function $(e){return e==="ssim"||e==="msssim"||e==="hitchhikers-ssim"}var _=b(()=>{u();});var ue={};ie(ue,{compareBin:()=>z,compareCore:()=>F,compareGmsd:()=>j,compareSsim:()=>v,isSsimMethod:()=>$,runComparison:()=>O,validateMethodSupportsInput:()=>q});function q(e,t){if(e==="bin"&&!I(t))throw new Error("Method 'bin' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.")}async function O(e,t,r,n,o){if(q(r,e),q(r,t),r==="bin"){let m=await z(e,t,o,n);return {diffCount:m.diffCount,diffPercentage:m.diffPercentage}}let a=C(e)?e:await S(e),i=C(t)?t:await S(t),f=o!==void 0;if($(r)){let m=v(a,i,r,f,n);return {score:m.score,diffOutput:m.diffOutput}}if(r==="gmsd"){let m=j(a,i,f,n);return {score:m.score,diffOutput:m.diffOutput}}let c=F(a,i,f,n);return {diffCount:c.diffCount,diffPercentage:c.diffPercentage,diffOutput:c.diffOutput}}var L=b(()=>{u();k();te();re();ae();_();te();re();ae();_();});u();L();k();u();var W={success:s__default.default.isColorSupported?"\u2714":"\u221A",error:s__default.default.isColorSupported?"\u2716":"\xD7",info:s__default.default.isColorSupported?"\u2139":"i",arrow:s__default.default.isColorSupported?"\u2514\u2500":"'-"},De=new Set(["ssim","msssim","hitchhikers-ssim"]);function T(e){return e.snapshotCreated?Re(e.baselinePath):e.pass?ke():$e(e)}function Re(e){return [`${s__default.default.green(W.success)} ${s__default.default.green("New snapshot created")}`,` ${s__default.default.dim(W.arrow)} ${s__default.default.dim(e)}`].join(`
2
- `)}function ke(){return `${s__default.default.green(W.success)} ${s__default.default.green("Image matches snapshot")}`}function $e(e){let{method:t,baselinePath:r,receivedPath:n,diffPath:o,diffCount:a=0,diffPercentage:i,score:f=0,threshold:c,thresholdType:m,updateCommand:x}=e,p=[`${s__default.default.red(W.error)} ${s__default.default.red(s__default.default.bold("Image snapshot mismatch"))}`,""],g=12;if(p.push(` ${s__default.default.dim("Method".padEnd(g))}${t}`),p.push(` ${s__default.default.dim("Baseline".padEnd(g))}${s__default.default.dim(r)}`),p.push(` ${s__default.default.dim("Received".padEnd(g))}${s__default.default.dim(n)}`),p.push(` ${s__default.default.dim("Diff".padEnd(g))}${s__default.default.dim(o)}`),p.push(""),De.has(t)){let U=((1-f)*100).toFixed(2);p.push(` ${s__default.default.dim("SSIM Score".padEnd(g))}${s__default.default.yellow(f.toFixed(4))} ${s__default.default.dim("(1.0 = identical)")}`),p.push(` ${s__default.default.dim("Difference".padEnd(g))}${s__default.default.yellow(`${U}%`)}`);}else if(t==="gmsd")p.push(` ${s__default.default.dim("GMSD Score".padEnd(g))}${s__default.default.yellow(f.toFixed(4))} ${s__default.default.dim("(0.0 = identical)")}`);else {let U=i?.toFixed(2)??"0.00";p.push(` ${s__default.default.dim("Difference".padEnd(g))}${s__default.default.yellow(a.toLocaleString())} pixels ${s__default.default.dim(`(${U}%)`)}`);}let J=m==="percent"?"%":"pixels";return p.push(` ${s__default.default.dim("Threshold".padEnd(g))}${c} ${J}`),p.push(""),p.push(` ${s__default.default.cyan(W.info)} ${s__default.default.cyan(`Run with ${x??"--update"} to update the snapshot`)}`),p.join(`
3
- `)}u();L();_();k();u();var y=null,Te=0,ce=false,D=new Map;function Ue(){let e=path.join(__dirname,"worker.js");if(fs.existsSync(e))return e;let t=path.join(__dirname,"../dist/worker.js");return fs.existsSync(t)?t:null}function Ae(){if(ce)return null;if(!y){let e=Ue();if(!e)return ce=true,null;y=new worker_threads.Worker(e),y.on("message",t=>{let r=D.get(t.id);r&&(D.delete(t.id),t.success?r.resolve(t.result):r.reject(new Error(t.error)));}),y.on("error",t=>{for(let r of D.values())r.reject(t);D.clear(),y=null;}),y.on("exit",t=>{if(t!==0){let r=new Error(`Worker exited with code ${t}`);for(let n of D.values())n.reject(r);D.clear();}y=null;});}return y}async function ne(e,t){let r=Ae();if(!r){let{runComparison:n}=await Promise.resolve().then(()=>(L(),ue)),{loadPNG:o,normalizeImageInput:a}=await Promise.resolve().then(()=>(k(),me));switch(e){case "normalize":return a(t.input);case "loadPNG":return o(t.filePath);case "compare":{let i=t;return n(i.received,i.baseline,i.method,i.options,i.diffOutputPath)}default:throw new Error(`Unknown request type: ${e}`)}}return new Promise((n,o)=>{let a=++Te;D.set(a,{resolve:n,reject:o});let i={id:a,type:e,payload:t};r.postMessage(i);})}function oe(e){return e.data instanceof Uint8Array?e:{data:new Uint8Array(e.data),width:e.width,height:e.height}}async function he(e){let t=await ne("normalize",{input:e});return oe(t)}async function le(e){let t=await ne("loadPNG",{filePath:e});return oe(t)}function ge(e,t,r,n,o){let a=oe(e);return ne("compare",{received:a,baseline:t,method:r,options:n,diffOutputPath:o})}function Ee(){if(y){let e=y;return y=null,e.terminate()}return Promise.resolve(0)}function Ge(e){return e.testName.replace(/[^a-zA-Z0-9-_\s]/g,"").replace(/\s+/g,"-").toLowerCase()||"snapshot"}function Ne(e,t){let r=path.dirname(e.testPath),n=t.snapshotsDir??"__snapshots__",o=path.isAbsolute(n)?n:path.join(r,n),a=t.snapshotIdentifier??Ge(e);return {snapshotDir:o,baselinePath:path.join(o,`${a}.png`),receivedPath:path.join(o,`${a}.received.png`),diffPath:path.join(o,`${a}.diff.png`)}}function we(e,t,r){let n=r.failureThreshold??0,o=r.failureThresholdType??"pixel";if($(e)){let a=t.score??0;return o==="percent"?(1-a)*100<=n:a>=1-n/100}if(e==="gmsd"){let a=t.score??0;return o==="percent"?a*100<=n:a<=n/100}return o==="percent"?(t.diffPercentage??0)<=n:(t.diffCount??0)<=n}async function ze(e,t,r){let n=Ne(r,t),{snapshotDir:o,baselinePath:a,receivedPath:i,diffPath:f}=n,c=E(e),m=c?e:null,x=t.runInWorker!==false,p=x?he:S,g=x?le:B,J=x?(w,l,Q)=>ge(w,l,t.method,t,Q):(w,l,Q)=>O(w,l,t.method,t,Q);ee(o);let U=N(a),G=t.updateSnapshots===true?"all":t.updateSnapshots===false||t.updateSnapshots===void 0?"new":t.updateSnapshots;if(!U&&G!=="none"&&(G==="new"||G==="all")){if(m)R(a,m);else if(I(e)){let l=await g(e);await P(a,l.data,l.width,l.height);}else if(C(e))await P(a,e.data,e.width,e.height);else {let l=await p(e);await P(a,l.data,l.width,l.height);}return {pass:true,message:T({pass:true,method:t.method,snapshotCreated:true,baselinePath:a,receivedPath:i,diffPath:f,diffCount:0,diffPercentage:0,score:0,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),baselinePath:a,snapshotStatus:"added"}}let h,d;t.method==="bin"?(h=await O(e,a,t.method,t,f),d=e):(d=c?await p(e):e,h=await J(C(d)?d:await p(d),a,f));let K=we(t.method,h,t);if(K)return fs.existsSync(i)&&fs.unlinkSync(i),fs.existsSync(f)&&fs.unlinkSync(f),{pass:true,message:T({pass:K,method:t.method,snapshotCreated:false,baselinePath:a,receivedPath:i,diffPath:f,diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,baselinePath:a,snapshotStatus:"matched"};if(G==="all"){if(m)R(a,m);else if(I(d)){let l=await g(d);await P(a,l.data,l.width,l.height);}else C(d)&&await P(a,d.data,d.width,d.height);return fs.existsSync(i)&&fs.unlinkSync(i),fs.existsSync(f)&&fs.unlinkSync(f),{pass:true,message:T({pass:true,method:t.method,snapshotCreated:true,baselinePath:a,receivedPath:i,diffPath:f,diffCount:0,diffPercentage:0,score:0,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),baselinePath:a,snapshotStatus:"updated"}}if(m)R(i,m);else if(I(d)){let w=await g(d);await P(i,w.data,w.width,w.height);}else C(d)&&await P(i,d.data,d.width,d.height);if(h.diffOutput&&t.method!=="bin"){let w=C(d)?d:await p(d);await P(f,h.diffOutput,w.width,w.height);}return {pass:false,message:T({pass:K,method:t.method,snapshotCreated:false,baselinePath:a,receivedPath:i,diffPath:f,diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,baselinePath:a,receivedPath:i,diffPath:f,snapshotStatus:"failed"}}async function Fe(e,t,r){let n=await O(e,t,r.method,r),o=we(r.method,n,r);return {pass:o,message:o?"Images match.":`Images differ: ${n.diffCount??n.score} ${n.diffCount!==void 0?"pixels":"score"}`,diffCount:n.diffCount,diffPercentage:n.diffPercentage,score:n.score}}exports.compareImages=Fe;exports.fileExists=N;exports.formatReport=T;exports.getOrCreateSnapshot=ze;exports.isFilePath=I;exports.isImageBuffer=fe;exports.isImageData=C;exports.isRawPngBuffer=E;exports.loadPNG=B;exports.normalizeImageInput=S;exports.runComparison=O;exports.savePNG=P;exports.saveRawPNGBuffer=R;exports.terminateWorker=Ee;exports.validateMethodSupportsInput=q;
1
+ 'use strict';var fs=require('fs'),path=require('path'),codecPngjs=require('@blazediff/codec-pngjs'),core=require('@blazediff/core'),coreNative=require('@blazediff/core-native'),gmsd=require('@blazediff/gmsd'),ssim=require('@blazediff/ssim'),hitchhikersSsim=require('@blazediff/ssim/hitchhikers-ssim'),msssim=require('@blazediff/ssim/msssim'),s=require('picocolors'),worker_threads=require('worker_threads');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var s__default=/*#__PURE__*/_interopDefault(s);var Ie=Object.defineProperty;var S=(e,t)=>()=>(e&&(t=e(e=0)),t);var ie=(e,t)=>{for(var r in t)Ie(e,r,{get:t[r],enumerable:true});};var u=S(()=>{});var me={};ie(me,{ensureDir:()=>ee,fileExists:()=>B,isFilePath:()=>I,isImageBuffer:()=>fe,isImageData:()=>C,isRawPngBuffer:()=>E,loadPNG:()=>v,normalizeImageInput:()=>D,savePNG:()=>P,saveRawPNGBuffer:()=>R});function I(e){return typeof e=="string"}function E(e){return (Buffer.isBuffer(e)||e instanceof Uint8Array)&&!("width"in e)}function fe(e){return typeof e=="object"&&e!==null&&"data"in e&&"width"in e&&"height"in e}function C(e){return typeof e=="object"&&e!==null&&"data"in e&&"width"in e&&"height"in e}async function v(e){if(!fs.existsSync(e))throw new Error(`Image file not found: ${e}`);let t=await codecPngjs.codecPngjs.read(e);return {data:new Uint8Array(t.data),width:t.width,height:t.height}}async function P(e,t,r,o){let n=path.dirname(e);fs.existsSync(n)||fs.mkdirSync(n,{recursive:true}),await codecPngjs.codecPngjs.write({data:t instanceof Uint8Array?t:new Uint8Array(t),width:r,height:o},e);}function R(e,t){let r=path.dirname(e);fs.existsSync(r)||fs.mkdirSync(r,{recursive:true}),fs.writeFileSync(e,t);}async function D(e){if(I(e))return v(e);if(E(e)){let t=Buffer.isBuffer(e)?e:Buffer.from(e),r=await codecPngjs.codecPngjs.read(t);return {data:new Uint8Array(r.data),width:r.width,height:r.height}}return e.data instanceof Uint8Array?e:{data:new Uint8Array(e.data),width:e.width,height:e.height}}function B(e){return fs.existsSync(e)}function ee(e){fs.existsSync(e)||fs.mkdirSync(e,{recursive:true});}var k=S(()=>{u();});function G(e,t,r,o){let{width:n,height:a}=e,i=n*a;if(e.width!==t.width||e.height!==t.height)return {diffCount:i,diffPercentage:100};let f=r?new Uint8Array(i*4):void 0,c=core.diff(e.data,t.data,f,n,a,{threshold:o.threshold??.1,includeAA:o.includeAA??false});return {diffCount:c,diffPercentage:c/i*100,diffOutput:f}}var te=S(()=>{u();});async function F(e,t,r,o){if(!I(e))throw new Error("Method 'core-native' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");if(!I(t))throw new Error("Method 'core-native' only supports file paths for baseline, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");let n=await coreNative.compare(e,t,r,{threshold:o.threshold,antialiasing:o.antialiasing,interpret:o.outputFormat==="html",outputFormat:o.outputFormat});if(n.match)return {diffCount:0,diffPercentage:0};if(n.reason==="layout-diff")return {diffCount:Number.MAX_SAFE_INTEGER,diffPercentage:100};if(n.reason==="pixel-diff")return {diffCount:n.diffCount,diffPercentage:n.diffPercentage};if(n.reason==="file-not-exists")throw new Error(`Image file not found: ${n.file}`);return {diffCount:0,diffPercentage:0}}var re=S(()=>{u();k();});function z(e,t,r,o){let{width:n,height:a}=e,i=n*a;if(e.width!==t.width||e.height!==t.height)return {score:1};let f=r?new Uint8Array(i*4):void 0;return {score:gmsd.gmsd(e.data,t.data,f,n,a,{downsample:o.downsample}),diffOutput:f}}var ae=S(()=>{u();});function j(e,t,r,o,n){let{width:a,height:i}=e,f=a*i;if(e.width!==t.width||e.height!==t.height)return {score:0};let c=o?new Uint8Array(f*4):void 0,m={windowSize:n.windowSize,k1:n.k1,k2:n.k2},x;switch(r){case "ssim":x=ssim.ssim(e.data,t.data,c,a,i,m);break;case "msssim":x=msssim.msssim(e.data,t.data,c,a,i,m);break;case "hitchhikers-ssim":x=hitchhikersSsim.hitchhikersSSIM(e.data,t.data,c,a,i,m);break;default:throw new Error(`Unknown SSIM method: ${r}`)}return {score:x,diffOutput:c}}function $(e){return e==="ssim"||e==="msssim"||e==="hitchhikers-ssim"}var _=S(()=>{u();});var ue={};ie(ue,{compareCore:()=>G,compareCoreNative:()=>F,compareGmsd:()=>z,compareSsim:()=>j,isSsimMethod:()=>$,runComparison:()=>O,validateMethodSupportsInput:()=>q});function q(e,t){if(e==="core-native"&&!I(t))throw new Error("Method 'core-native' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.")}async function O(e,t,r,o,n){if(q(r,e),q(r,t),r==="core-native"){let m=await F(e,t,n,o);return {diffCount:m.diffCount,diffPercentage:m.diffPercentage}}let a=C(e)?e:await D(e),i=C(t)?t:await D(t),f=n!==void 0;if($(r)){let m=j(a,i,r,f,o);return {score:m.score,diffOutput:m.diffOutput}}if(r==="gmsd"){let m=z(a,i,f,o);return {score:m.score,diffOutput:m.diffOutput}}let c=G(a,i,f,o);return {diffCount:c.diffCount,diffPercentage:c.diffPercentage,diffOutput:c.diffOutput}}var L=S(()=>{u();k();te();re();ae();_();te();re();ae();_();});u();L();k();u();var N={success:s__default.default.isColorSupported?"\u2714":"\u221A",error:s__default.default.isColorSupported?"\u2716":"\xD7",info:s__default.default.isColorSupported?"\u2139":"i",arrow:s__default.default.isColorSupported?"\u2514\u2500":"'-"},be=new Set(["ssim","msssim","hitchhikers-ssim"]);function T(e){return e.snapshotCreated?Re(e.baselinePath):e.pass?ke():$e(e)}function Re(e){return [`${s__default.default.green(N.success)} ${s__default.default.green("New snapshot created")}`,` ${s__default.default.dim(N.arrow)} ${s__default.default.dim(e)}`].join(`
2
+ `)}function ke(){return `${s__default.default.green(N.success)} ${s__default.default.green("Image matches snapshot")}`}function $e(e){let{method:t,baselinePath:r,receivedPath:o,diffPath:n,diffCount:a=0,diffPercentage:i,score:f=0,threshold:c,thresholdType:m,updateCommand:x}=e,p=[`${s__default.default.red(N.error)} ${s__default.default.red(s__default.default.bold("Image snapshot mismatch"))}`,""],g=12;if(p.push(` ${s__default.default.dim("Method".padEnd(g))}${t}`),p.push(` ${s__default.default.dim("Baseline".padEnd(g))}${s__default.default.dim(r)}`),p.push(` ${s__default.default.dim("Received".padEnd(g))}${s__default.default.dim(o)}`),p.push(` ${s__default.default.dim("Diff".padEnd(g))}${s__default.default.dim(n)}`),p.push(""),be.has(t)){let U=((1-f)*100).toFixed(2);p.push(` ${s__default.default.dim("SSIM Score".padEnd(g))}${s__default.default.yellow(f.toFixed(4))} ${s__default.default.dim("(1.0 = identical)")}`),p.push(` ${s__default.default.dim("Difference".padEnd(g))}${s__default.default.yellow(`${U}%`)}`);}else if(t==="gmsd")p.push(` ${s__default.default.dim("GMSD Score".padEnd(g))}${s__default.default.yellow(f.toFixed(4))} ${s__default.default.dim("(0.0 = identical)")}`);else {let U=i?.toFixed(2)??"0.00";p.push(` ${s__default.default.dim("Difference".padEnd(g))}${s__default.default.yellow(a.toLocaleString())} pixels ${s__default.default.dim(`(${U}%)`)}`);}let J=m==="percent"?"%":"pixels";return p.push(` ${s__default.default.dim("Threshold".padEnd(g))}${c} ${J}`),p.push(""),p.push(` ${s__default.default.cyan(N.info)} ${s__default.default.cyan(`Run with ${x??"--update"} to update the snapshot`)}`),p.join(`
3
+ `)}u();L();_();k();u();var y=null,Te=0,ce=false,b=new Map;function Ue(){let e=path.join(__dirname,"worker.js");if(fs.existsSync(e))return e;let t=path.join(__dirname,"../dist/worker.js");return fs.existsSync(t)?t:null}function Ae(){if(ce)return null;if(!y){let e=Ue();if(!e)return ce=true,null;y=new worker_threads.Worker(e),y.on("message",t=>{let r=b.get(t.id);r&&(b.delete(t.id),t.success?r.resolve(t.result):r.reject(new Error(t.error)));}),y.on("error",t=>{for(let r of b.values())r.reject(t);b.clear(),y=null;}),y.on("exit",t=>{if(t!==0){let r=new Error(`Worker exited with code ${t}`);for(let o of b.values())o.reject(r);b.clear();}y=null;});}return y}async function oe(e,t){let r=Ae();if(!r){let{runComparison:o}=await Promise.resolve().then(()=>(L(),ue)),{loadPNG:n,normalizeImageInput:a}=await Promise.resolve().then(()=>(k(),me));switch(e){case "normalize":return a(t.input);case "loadPNG":return n(t.filePath);case "compare":{let i=t;return o(i.received,i.baseline,i.method,i.options,i.diffOutputPath)}default:throw new Error(`Unknown request type: ${e}`)}}return new Promise((o,n)=>{let a=++Te;b.set(a,{resolve:o,reject:n});let i={id:a,type:e,payload:t};r.postMessage(i);})}function ne(e){return e.data instanceof Uint8Array?e:{data:new Uint8Array(e.data),width:e.width,height:e.height}}async function he(e){let t=await oe("normalize",{input:e});return ne(t)}async function le(e){let t=await oe("loadPNG",{filePath:e});return ne(t)}function ge(e,t,r,o,n){let a=ne(e);return oe("compare",{received:a,baseline:t,method:r,options:o,diffOutputPath:n})}function Ee(){if(y){let e=y;return y=null,e.terminate()}return Promise.resolve(0)}function We(e){return e.testName.replace(/[^a-zA-Z0-9-_\s]/g,"").replace(/\s+/g,"-").toLowerCase()||"snapshot"}function Be(e,t){let r=path.dirname(e.testPath),o=t.snapshotsDir??"__snapshots__",n=path.isAbsolute(o)?o:path.join(r,o),a=t.snapshotIdentifier??We(e),i=t.method==="core-native"&&t.outputFormat==="html"?"html":"png";return {snapshotDir:n,baselinePath:path.join(n,`${a}.png`),receivedPath:path.join(n,`${a}.received.png`),diffPath:path.join(n,`${a}.diff.${i}`)}}function we(e,t,r){let o=r.failureThreshold??0,n=r.failureThresholdType??"pixel";if($(e)){let a=t.score??0;return n==="percent"?(1-a)*100<=o:a>=1-o/100}if(e==="gmsd"){let a=t.score??0;return n==="percent"?a*100<=o:a<=o/100}return n==="percent"?(t.diffPercentage??0)<=o:(t.diffCount??0)<=o}async function Ge(e,t,r){let o=Be(r,t),{snapshotDir:n,baselinePath:a,receivedPath:i,diffPath:f}=o,c=E(e),m=c?e:null,x=t.runInWorker!==false,p=x?he:D,g=x?le:v,J=x?(w,l,Q)=>ge(w,l,t.method,t,Q):(w,l,Q)=>O(w,l,t.method,t,Q);ee(n);let U=B(a),W=t.updateSnapshots===true?"all":t.updateSnapshots===false||t.updateSnapshots===void 0?"new":t.updateSnapshots;if(!U&&W!=="none"&&(W==="new"||W==="all")){if(m)R(a,m);else if(I(e)){let l=await g(e);await P(a,l.data,l.width,l.height);}else if(C(e))await P(a,e.data,e.width,e.height);else {let l=await p(e);await P(a,l.data,l.width,l.height);}return {pass:true,message:T({pass:true,method:t.method,snapshotCreated:true,baselinePath:a,receivedPath:i,diffPath:f,diffCount:0,diffPercentage:0,score:0,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),baselinePath:a,snapshotStatus:"added"}}let h,d;t.method==="core-native"?(h=await O(e,a,t.method,t,f),d=e):(d=c?await p(e):e,h=await J(C(d)?d:await p(d),a,f));let K=we(t.method,h,t);if(K)return fs.existsSync(i)&&fs.unlinkSync(i),fs.existsSync(f)&&fs.unlinkSync(f),{pass:true,message:T({pass:K,method:t.method,snapshotCreated:false,baselinePath:a,receivedPath:i,diffPath:f,diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,baselinePath:a,snapshotStatus:"matched"};if(W==="all"){if(m)R(a,m);else if(I(d)){let l=await g(d);await P(a,l.data,l.width,l.height);}else C(d)&&await P(a,d.data,d.width,d.height);return fs.existsSync(i)&&fs.unlinkSync(i),fs.existsSync(f)&&fs.unlinkSync(f),{pass:true,message:T({pass:true,method:t.method,snapshotCreated:true,baselinePath:a,receivedPath:i,diffPath:f,diffCount:0,diffPercentage:0,score:0,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),baselinePath:a,snapshotStatus:"updated"}}if(m)R(i,m);else if(I(d)){let w=await g(d);await P(i,w.data,w.width,w.height);}else C(d)&&await P(i,d.data,d.width,d.height);if(h.diffOutput&&t.method!=="core-native"){let w=C(d)?d:await p(d);await P(f,h.diffOutput,w.width,w.height);}return {pass:false,message:T({pass:K,method:t.method,snapshotCreated:false,baselinePath:a,receivedPath:i,diffPath:f,diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,baselinePath:a,receivedPath:i,diffPath:f,snapshotStatus:"failed"}}async function Fe(e,t,r){let o=await O(e,t,r.method,r),n=we(r.method,o,r);return {pass:n,message:n?"Images match.":`Images differ: ${o.diffCount??o.score} ${o.diffCount!==void 0?"pixels":"score"}`,diffCount:o.diffCount,diffPercentage:o.diffPercentage,score:o.score}}exports.compareImages=Fe;exports.fileExists=B;exports.formatReport=T;exports.getOrCreateSnapshot=Ge;exports.isFilePath=I;exports.isImageBuffer=fe;exports.isImageData=C;exports.isRawPngBuffer=E;exports.loadPNG=v;exports.normalizeImageInput=D;exports.runComparison=O;exports.savePNG=P;exports.saveRawPNGBuffer=R;exports.terminateWorker=Ee;exports.validateMethodSupportsInput=q;
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import Pe,{dirname,isAbsolute,join}from'path';import {fileURLToPath}from'url';import {existsSync,mkdirSync,writeFileSync,unlinkSync}from'fs';import {pngjsTransformer}from'@blazediff/pngjs-transformer';import {compare}from'@blazediff/bin';import {diff}from'@blazediff/core';import {gmsd}from'@blazediff/gmsd';import {ssim}from'@blazediff/ssim';import {hitchhikersSSIM}from'@blazediff/ssim/hitchhikers-ssim';import {msssim}from'@blazediff/ssim/msssim';import s from'picocolors';import {Worker}from'worker_threads';var Ce=Object.defineProperty;var S=(e,t)=>()=>(e&&(t=e(e=0)),t);var se=(e,t)=>{for(var r in t)Ce(e,r,{get:t[r],enumerable:true});};var xe,Me,I,u=S(()=>{xe=()=>fileURLToPath(import.meta.url),Me=()=>Pe.dirname(xe()),I=Me();});var ue={};se(ue,{ensureDir:()=>te,fileExists:()=>z,isFilePath:()=>C,isImageBuffer:()=>me,isImageData:()=>P,isRawPngBuffer:()=>B,loadPNG:()=>W,normalizeImageInput:()=>D,savePNG:()=>y,saveRawPNGBuffer:()=>R});function C(e){return typeof e=="string"}function B(e){return (Buffer.isBuffer(e)||e instanceof Uint8Array)&&!("width"in e)}function me(e){return typeof e=="object"&&e!==null&&"data"in e&&"width"in e&&"height"in e}function P(e){return typeof e=="object"&&e!==null&&"data"in e&&"width"in e&&"height"in e}async function W(e){if(!existsSync(e))throw new Error(`Image file not found: ${e}`);let t=await pngjsTransformer.read(e);return {data:new Uint8Array(t.data),width:t.width,height:t.height}}async function y(e,t,r,o){let n=dirname(e);existsSync(n)||mkdirSync(n,{recursive:true}),await pngjsTransformer.write({data:t instanceof Uint8Array?t:new Uint8Array(t),width:r,height:o},e);}function R(e,t){let r=dirname(e);existsSync(r)||mkdirSync(r,{recursive:true}),writeFileSync(e,t);}async function D(e){if(C(e))return W(e);if(B(e)){let t=Buffer.isBuffer(e)?e:Buffer.from(e),r=await pngjsTransformer.read(t);return {data:new Uint8Array(r.data),width:r.width,height:r.height}}return e.data instanceof Uint8Array?e:{data:new Uint8Array(e.data),width:e.width,height:e.height}}function z(e){return existsSync(e)}function te(e){existsSync(e)||mkdirSync(e,{recursive:true});}var $=S(()=>{u();});async function F(e,t,r,o){if(!C(e))throw new Error("Method 'bin' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");if(!C(t))throw new Error("Method 'bin' only supports file paths for baseline, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");let n=await compare(e,t,r,{threshold:o.threshold,antialiasing:o.antialiasing});if(n.match)return {diffCount:0,diffPercentage:0};if(n.reason==="layout-diff")return {diffCount:Number.MAX_SAFE_INTEGER,diffPercentage:100};if(n.reason==="pixel-diff")return {diffCount:n.diffCount,diffPercentage:n.diffPercentage};if(n.reason==="file-not-exists")throw new Error(`Image file not found: ${n.file}`);return {diffCount:0,diffPercentage:0}}var re=S(()=>{u();$();});function v(e,t,r,o){let{width:n,height:a}=e,i=n*a;if(e.width!==t.width||e.height!==t.height)return {diffCount:i,diffPercentage:100};let f=r?new Uint8Array(i*4):void 0,h=diff(e.data,t.data,f,n,a,{threshold:o.threshold??.1,includeAA:o.includeAA??false});return {diffCount:h,diffPercentage:h/i*100,diffOutput:f}}var ae=S(()=>{u();});function j(e,t,r,o){let{width:n,height:a}=e,i=n*a;if(e.width!==t.width||e.height!==t.height)return {score:1};let f=r?new Uint8Array(i*4):void 0;return {score:gmsd(e.data,t.data,f,n,a,{downsample:o.downsample}),diffOutput:f}}var oe=S(()=>{u();});function _(e,t,r,o,n){let{width:a,height:i}=e,f=a*i;if(e.width!==t.width||e.height!==t.height)return {score:0};let h=o?new Uint8Array(f*4):void 0,m={windowSize:n.windowSize,k1:n.k1,k2:n.k2},M;switch(r){case "ssim":M=ssim(e.data,t.data,h,a,i,m);break;case "msssim":M=msssim(e.data,t.data,h,a,i,m);break;case "hitchhikers-ssim":M=hitchhikersSSIM(e.data,t.data,h,a,i,m);break;default:throw new Error(`Unknown SSIM method: ${r}`)}return {score:M,diffOutput:h}}function O(e){return e==="ssim"||e==="msssim"||e==="hitchhikers-ssim"}var q=S(()=>{u();});var de={};se(de,{compareBin:()=>F,compareCore:()=>v,compareGmsd:()=>j,compareSsim:()=>_,isSsimMethod:()=>O,runComparison:()=>T,validateMethodSupportsInput:()=>L});function L(e,t){if(e==="bin"&&!C(t))throw new Error("Method 'bin' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.")}async function T(e,t,r,o,n){if(L(r,e),L(r,t),r==="bin"){let m=await F(e,t,n,o);return {diffCount:m.diffCount,diffPercentage:m.diffPercentage}}let a=P(e)?e:await D(e),i=P(t)?t:await D(t),f=n!==void 0;if(O(r)){let m=_(a,i,r,f,o);return {score:m.score,diffOutput:m.diffOutput}}if(r==="gmsd"){let m=j(a,i,f,o);return {score:m.score,diffOutput:m.diffOutput}}let h=v(a,i,f,o);return {diffCount:h.diffCount,diffPercentage:h.diffPercentage,diffOutput:h.diffOutput}}var H=S(()=>{u();$();re();ae();oe();q();re();ae();oe();q();});u();H();$();u();var G={success:s.isColorSupported?"\u2714":"\u221A",error:s.isColorSupported?"\u2716":"\xD7",info:s.isColorSupported?"\u2139":"i",arrow:s.isColorSupported?"\u2514\u2500":"'-"},Te=new Set(["ssim","msssim","hitchhikers-ssim"]);function A(e){return e.snapshotCreated?Ae(e.baselinePath):e.pass?Ue():Ee(e)}function Ae(e){return [`${s.green(G.success)} ${s.green("New snapshot created")}`,` ${s.dim(G.arrow)} ${s.dim(e)}`].join(`
2
- `)}function Ue(){return `${s.green(G.success)} ${s.green("Image matches snapshot")}`}function Ee(e){let{method:t,baselinePath:r,receivedPath:o,diffPath:n,diffCount:a=0,diffPercentage:i,score:f=0,threshold:h,thresholdType:m,updateCommand:M}=e,p=[`${s.red(G.error)} ${s.red(s.bold("Image snapshot mismatch"))}`,""],g=12;if(p.push(` ${s.dim("Method".padEnd(g))}${t}`),p.push(` ${s.dim("Baseline".padEnd(g))}${s.dim(r)}`),p.push(` ${s.dim("Received".padEnd(g))}${s.dim(o)}`),p.push(` ${s.dim("Diff".padEnd(g))}${s.dim(n)}`),p.push(""),Te.has(t)){let U=((1-f)*100).toFixed(2);p.push(` ${s.dim("SSIM Score".padEnd(g))}${s.yellow(f.toFixed(4))} ${s.dim("(1.0 = identical)")}`),p.push(` ${s.dim("Difference".padEnd(g))}${s.yellow(`${U}%`)}`);}else if(t==="gmsd")p.push(` ${s.dim("GMSD Score".padEnd(g))}${s.yellow(f.toFixed(4))} ${s.dim("(0.0 = identical)")}`);else {let U=i?.toFixed(2)??"0.00";p.push(` ${s.dim("Difference".padEnd(g))}${s.yellow(a.toLocaleString())} pixels ${s.dim(`(${U}%)`)}`);}let K=m==="percent"?"%":"pixels";return p.push(` ${s.dim("Threshold".padEnd(g))}${h} ${K}`),p.push(""),p.push(` ${s.cyan(G.info)} ${s.cyan(`Run with ${M??"--update"} to update the snapshot`)}`),p.join(`
3
- `)}u();H();q();$();u();var x=null,We=0,ce=false,k=new Map;function Ge(){let e=join(I,"worker.js");if(existsSync(e))return e;let t=join(I,"../dist/worker.js");return existsSync(t)?t:null}function Ne(){if(ce)return null;if(!x){let e=Ge();if(!e)return ce=true,null;x=new Worker(e),x.on("message",t=>{let r=k.get(t.id);r&&(k.delete(t.id),t.success?r.resolve(t.result):r.reject(new Error(t.error)));}),x.on("error",t=>{for(let r of k.values())r.reject(t);k.clear(),x=null;}),x.on("exit",t=>{if(t!==0){let r=new Error(`Worker exited with code ${t}`);for(let o of k.values())o.reject(r);k.clear();}x=null;});}return x}async function ne(e,t){let r=Ne();if(!r){let{runComparison:o}=await Promise.resolve().then(()=>(H(),de)),{loadPNG:n,normalizeImageInput:a}=await Promise.resolve().then(()=>($(),ue));switch(e){case "normalize":return a(t.input);case "loadPNG":return n(t.filePath);case "compare":{let i=t;return o(i.received,i.baseline,i.method,i.options,i.diffOutputPath)}default:throw new Error(`Unknown request type: ${e}`)}}return new Promise((o,n)=>{let a=++We;k.set(a,{resolve:o,reject:n});let i={id:a,type:e,payload:t};r.postMessage(i);})}function ie(e){return e.data instanceof Uint8Array?e:{data:new Uint8Array(e.data),width:e.width,height:e.height}}async function le(e){let t=await ne("normalize",{input:e});return ie(t)}async function ge(e){let t=await ne("loadPNG",{filePath:e});return ie(t)}function we(e,t,r,o,n){let a=ie(e);return ne("compare",{received:a,baseline:t,method:r,options:o,diffOutputPath:n})}function ze(){if(x){let e=x;return x=null,e.terminate()}return Promise.resolve(0)}function je(e){return e.testName.replace(/[^a-zA-Z0-9-_\s]/g,"").replace(/\s+/g,"-").toLowerCase()||"snapshot"}function _e(e,t){let r=dirname(e.testPath),o=t.snapshotsDir??"__snapshots__",n=isAbsolute(o)?o:join(r,o),a=t.snapshotIdentifier??je(e);return {snapshotDir:n,baselinePath:join(n,`${a}.png`),receivedPath:join(n,`${a}.received.png`),diffPath:join(n,`${a}.diff.png`)}}function Ie(e,t,r){let o=r.failureThreshold??0,n=r.failureThresholdType??"pixel";if(O(e)){let a=t.score??0;return n==="percent"?(1-a)*100<=o:a>=1-o/100}if(e==="gmsd"){let a=t.score??0;return n==="percent"?a*100<=o:a<=o/100}return n==="percent"?(t.diffPercentage??0)<=o:(t.diffCount??0)<=o}async function qe(e,t,r){let o=_e(r,t),{snapshotDir:n,baselinePath:a,receivedPath:i,diffPath:f}=o,h=B(e),m=h?e:null,M=t.runInWorker!==false,p=M?le:D,g=M?ge:W,K=M?(w,l,V)=>we(w,l,t.method,t,V):(w,l,V)=>T(w,l,t.method,t,V);te(n);let U=z(a),N=t.updateSnapshots===true?"all":t.updateSnapshots===false||t.updateSnapshots===void 0?"new":t.updateSnapshots;if(!U&&N!=="none"&&(N==="new"||N==="all")){if(m)R(a,m);else if(C(e)){let l=await g(e);await y(a,l.data,l.width,l.height);}else if(P(e))await y(a,e.data,e.width,e.height);else {let l=await p(e);await y(a,l.data,l.width,l.height);}return {pass:true,message:A({pass:true,method:t.method,snapshotCreated:true,baselinePath:a,receivedPath:i,diffPath:f,diffCount:0,diffPercentage:0,score:0,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),baselinePath:a,snapshotStatus:"added"}}let c,d;t.method==="bin"?(c=await T(e,a,t.method,t,f),d=e):(d=h?await p(e):e,c=await K(P(d)?d:await p(d),a,f));let Q=Ie(t.method,c,t);if(Q)return existsSync(i)&&unlinkSync(i),existsSync(f)&&unlinkSync(f),{pass:true,message:A({pass:Q,method:t.method,snapshotCreated:false,baselinePath:a,receivedPath:i,diffPath:f,diffCount:c.diffCount,diffPercentage:c.diffPercentage,score:c.score,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),diffCount:c.diffCount,diffPercentage:c.diffPercentage,score:c.score,baselinePath:a,snapshotStatus:"matched"};if(N==="all"){if(m)R(a,m);else if(C(d)){let l=await g(d);await y(a,l.data,l.width,l.height);}else P(d)&&await y(a,d.data,d.width,d.height);return existsSync(i)&&unlinkSync(i),existsSync(f)&&unlinkSync(f),{pass:true,message:A({pass:true,method:t.method,snapshotCreated:true,baselinePath:a,receivedPath:i,diffPath:f,diffCount:0,diffPercentage:0,score:0,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),baselinePath:a,snapshotStatus:"updated"}}if(m)R(i,m);else if(C(d)){let w=await g(d);await y(i,w.data,w.width,w.height);}else P(d)&&await y(i,d.data,d.width,d.height);if(c.diffOutput&&t.method!=="bin"){let w=P(d)?d:await p(d);await y(f,c.diffOutput,w.width,w.height);}return {pass:false,message:A({pass:Q,method:t.method,snapshotCreated:false,baselinePath:a,receivedPath:i,diffPath:f,diffCount:c.diffCount,diffPercentage:c.diffPercentage,score:c.score,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),diffCount:c.diffCount,diffPercentage:c.diffPercentage,score:c.score,baselinePath:a,receivedPath:i,diffPath:f,snapshotStatus:"failed"}}async function Le(e,t,r){let o=await T(e,t,r.method,r),n=Ie(r.method,o,r);return {pass:n,message:n?"Images match.":`Images differ: ${o.diffCount??o.score} ${o.diffCount!==void 0?"pixels":"score"}`,diffCount:o.diffCount,diffPercentage:o.diffPercentage,score:o.score}}export{Le as compareImages,z as fileExists,A as formatReport,qe as getOrCreateSnapshot,C as isFilePath,me as isImageBuffer,P as isImageData,B as isRawPngBuffer,W as loadPNG,D as normalizeImageInput,T as runComparison,y as savePNG,R as saveRawPNGBuffer,ze as terminateWorker,L as validateMethodSupportsInput};
1
+ import Pe,{dirname,isAbsolute,join}from'path';import {fileURLToPath}from'url';import {existsSync,mkdirSync,writeFileSync,unlinkSync}from'fs';import {codecPngjs}from'@blazediff/codec-pngjs';import {diff}from'@blazediff/core';import {compare}from'@blazediff/core-native';import {gmsd}from'@blazediff/gmsd';import {ssim}from'@blazediff/ssim';import {hitchhikersSSIM}from'@blazediff/ssim/hitchhikers-ssim';import {msssim}from'@blazediff/ssim/msssim';import s from'picocolors';import {Worker}from'worker_threads';var Ce=Object.defineProperty;var D=(e,t)=>()=>(e&&(t=e(e=0)),t);var se=(e,t)=>{for(var r in t)Ce(e,r,{get:t[r],enumerable:true});};var xe,Me,I,u=D(()=>{xe=()=>fileURLToPath(import.meta.url),Me=()=>Pe.dirname(xe()),I=Me();});var ue={};se(ue,{ensureDir:()=>te,fileExists:()=>F,isFilePath:()=>C,isImageBuffer:()=>me,isImageData:()=>P,isRawPngBuffer:()=>v,loadPNG:()=>N,normalizeImageInput:()=>b,savePNG:()=>y,saveRawPNGBuffer:()=>R});function C(e){return typeof e=="string"}function v(e){return (Buffer.isBuffer(e)||e instanceof Uint8Array)&&!("width"in e)}function me(e){return typeof e=="object"&&e!==null&&"data"in e&&"width"in e&&"height"in e}function P(e){return typeof e=="object"&&e!==null&&"data"in e&&"width"in e&&"height"in e}async function N(e){if(!existsSync(e))throw new Error(`Image file not found: ${e}`);let t=await codecPngjs.read(e);return {data:new Uint8Array(t.data),width:t.width,height:t.height}}async function y(e,t,r,o){let n=dirname(e);existsSync(n)||mkdirSync(n,{recursive:true}),await codecPngjs.write({data:t instanceof Uint8Array?t:new Uint8Array(t),width:r,height:o},e);}function R(e,t){let r=dirname(e);existsSync(r)||mkdirSync(r,{recursive:true}),writeFileSync(e,t);}async function b(e){if(C(e))return N(e);if(v(e)){let t=Buffer.isBuffer(e)?e:Buffer.from(e),r=await codecPngjs.read(t);return {data:new Uint8Array(r.data),width:r.width,height:r.height}}return e.data instanceof Uint8Array?e:{data:new Uint8Array(e.data),width:e.width,height:e.height}}function F(e){return existsSync(e)}function te(e){existsSync(e)||mkdirSync(e,{recursive:true});}var $=D(()=>{u();});function G(e,t,r,o){let{width:n,height:a}=e,i=n*a;if(e.width!==t.width||e.height!==t.height)return {diffCount:i,diffPercentage:100};let f=r?new Uint8Array(i*4):void 0,c=diff(e.data,t.data,f,n,a,{threshold:o.threshold??.1,includeAA:o.includeAA??false});return {diffCount:c,diffPercentage:c/i*100,diffOutput:f}}var re=D(()=>{u();});async function z(e,t,r,o){if(!C(e))throw new Error("Method 'core-native' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");if(!C(t))throw new Error("Method 'core-native' only supports file paths for baseline, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");let n=await compare(e,t,r,{threshold:o.threshold,antialiasing:o.antialiasing,interpret:o.outputFormat==="html",outputFormat:o.outputFormat});if(n.match)return {diffCount:0,diffPercentage:0};if(n.reason==="layout-diff")return {diffCount:Number.MAX_SAFE_INTEGER,diffPercentage:100};if(n.reason==="pixel-diff")return {diffCount:n.diffCount,diffPercentage:n.diffPercentage};if(n.reason==="file-not-exists")throw new Error(`Image file not found: ${n.file}`);return {diffCount:0,diffPercentage:0}}var ae=D(()=>{u();$();});function j(e,t,r,o){let{width:n,height:a}=e,i=n*a;if(e.width!==t.width||e.height!==t.height)return {score:1};let f=r?new Uint8Array(i*4):void 0;return {score:gmsd(e.data,t.data,f,n,a,{downsample:o.downsample}),diffOutput:f}}var oe=D(()=>{u();});function _(e,t,r,o,n){let{width:a,height:i}=e,f=a*i;if(e.width!==t.width||e.height!==t.height)return {score:0};let c=o?new Uint8Array(f*4):void 0,m={windowSize:n.windowSize,k1:n.k1,k2:n.k2},M;switch(r){case "ssim":M=ssim(e.data,t.data,c,a,i,m);break;case "msssim":M=msssim(e.data,t.data,c,a,i,m);break;case "hitchhikers-ssim":M=hitchhikersSSIM(e.data,t.data,c,a,i,m);break;default:throw new Error(`Unknown SSIM method: ${r}`)}return {score:M,diffOutput:c}}function O(e){return e==="ssim"||e==="msssim"||e==="hitchhikers-ssim"}var q=D(()=>{u();});var de={};se(de,{compareCore:()=>G,compareCoreNative:()=>z,compareGmsd:()=>j,compareSsim:()=>_,isSsimMethod:()=>O,runComparison:()=>T,validateMethodSupportsInput:()=>L});function L(e,t){if(e==="core-native"&&!C(t))throw new Error("Method 'core-native' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.")}async function T(e,t,r,o,n){if(L(r,e),L(r,t),r==="core-native"){let m=await z(e,t,n,o);return {diffCount:m.diffCount,diffPercentage:m.diffPercentage}}let a=P(e)?e:await b(e),i=P(t)?t:await b(t),f=n!==void 0;if(O(r)){let m=_(a,i,r,f,o);return {score:m.score,diffOutput:m.diffOutput}}if(r==="gmsd"){let m=j(a,i,f,o);return {score:m.score,diffOutput:m.diffOutput}}let c=G(a,i,f,o);return {diffCount:c.diffCount,diffPercentage:c.diffPercentage,diffOutput:c.diffOutput}}var H=D(()=>{u();$();re();ae();oe();q();re();ae();oe();q();});u();H();$();u();var W={success:s.isColorSupported?"\u2714":"\u221A",error:s.isColorSupported?"\u2716":"\xD7",info:s.isColorSupported?"\u2139":"i",arrow:s.isColorSupported?"\u2514\u2500":"'-"},Te=new Set(["ssim","msssim","hitchhikers-ssim"]);function A(e){return e.snapshotCreated?Ae(e.baselinePath):e.pass?Ue():Ee(e)}function Ae(e){return [`${s.green(W.success)} ${s.green("New snapshot created")}`,` ${s.dim(W.arrow)} ${s.dim(e)}`].join(`
2
+ `)}function Ue(){return `${s.green(W.success)} ${s.green("Image matches snapshot")}`}function Ee(e){let{method:t,baselinePath:r,receivedPath:o,diffPath:n,diffCount:a=0,diffPercentage:i,score:f=0,threshold:c,thresholdType:m,updateCommand:M}=e,p=[`${s.red(W.error)} ${s.red(s.bold("Image snapshot mismatch"))}`,""],g=12;if(p.push(` ${s.dim("Method".padEnd(g))}${t}`),p.push(` ${s.dim("Baseline".padEnd(g))}${s.dim(r)}`),p.push(` ${s.dim("Received".padEnd(g))}${s.dim(o)}`),p.push(` ${s.dim("Diff".padEnd(g))}${s.dim(n)}`),p.push(""),Te.has(t)){let U=((1-f)*100).toFixed(2);p.push(` ${s.dim("SSIM Score".padEnd(g))}${s.yellow(f.toFixed(4))} ${s.dim("(1.0 = identical)")}`),p.push(` ${s.dim("Difference".padEnd(g))}${s.yellow(`${U}%`)}`);}else if(t==="gmsd")p.push(` ${s.dim("GMSD Score".padEnd(g))}${s.yellow(f.toFixed(4))} ${s.dim("(0.0 = identical)")}`);else {let U=i?.toFixed(2)??"0.00";p.push(` ${s.dim("Difference".padEnd(g))}${s.yellow(a.toLocaleString())} pixels ${s.dim(`(${U}%)`)}`);}let K=m==="percent"?"%":"pixels";return p.push(` ${s.dim("Threshold".padEnd(g))}${c} ${K}`),p.push(""),p.push(` ${s.cyan(W.info)} ${s.cyan(`Run with ${M??"--update"} to update the snapshot`)}`),p.join(`
3
+ `)}u();H();q();$();u();var x=null,Ne=0,he=false,k=new Map;function We(){let e=join(I,"worker.js");if(existsSync(e))return e;let t=join(I,"../dist/worker.js");return existsSync(t)?t:null}function Be(){if(he)return null;if(!x){let e=We();if(!e)return he=true,null;x=new Worker(e),x.on("message",t=>{let r=k.get(t.id);r&&(k.delete(t.id),t.success?r.resolve(t.result):r.reject(new Error(t.error)));}),x.on("error",t=>{for(let r of k.values())r.reject(t);k.clear(),x=null;}),x.on("exit",t=>{if(t!==0){let r=new Error(`Worker exited with code ${t}`);for(let o of k.values())o.reject(r);k.clear();}x=null;});}return x}async function ne(e,t){let r=Be();if(!r){let{runComparison:o}=await Promise.resolve().then(()=>(H(),de)),{loadPNG:n,normalizeImageInput:a}=await Promise.resolve().then(()=>($(),ue));switch(e){case "normalize":return a(t.input);case "loadPNG":return n(t.filePath);case "compare":{let i=t;return o(i.received,i.baseline,i.method,i.options,i.diffOutputPath)}default:throw new Error(`Unknown request type: ${e}`)}}return new Promise((o,n)=>{let a=++Ne;k.set(a,{resolve:o,reject:n});let i={id:a,type:e,payload:t};r.postMessage(i);})}function ie(e){return e.data instanceof Uint8Array?e:{data:new Uint8Array(e.data),width:e.width,height:e.height}}async function le(e){let t=await ne("normalize",{input:e});return ie(t)}async function ge(e){let t=await ne("loadPNG",{filePath:e});return ie(t)}function we(e,t,r,o,n){let a=ie(e);return ne("compare",{received:a,baseline:t,method:r,options:o,diffOutputPath:n})}function Fe(){if(x){let e=x;return x=null,e.terminate()}return Promise.resolve(0)}function je(e){return e.testName.replace(/[^a-zA-Z0-9-_\s]/g,"").replace(/\s+/g,"-").toLowerCase()||"snapshot"}function _e(e,t){let r=dirname(e.testPath),o=t.snapshotsDir??"__snapshots__",n=isAbsolute(o)?o:join(r,o),a=t.snapshotIdentifier??je(e),i=t.method==="core-native"&&t.outputFormat==="html"?"html":"png";return {snapshotDir:n,baselinePath:join(n,`${a}.png`),receivedPath:join(n,`${a}.received.png`),diffPath:join(n,`${a}.diff.${i}`)}}function Ie(e,t,r){let o=r.failureThreshold??0,n=r.failureThresholdType??"pixel";if(O(e)){let a=t.score??0;return n==="percent"?(1-a)*100<=o:a>=1-o/100}if(e==="gmsd"){let a=t.score??0;return n==="percent"?a*100<=o:a<=o/100}return n==="percent"?(t.diffPercentage??0)<=o:(t.diffCount??0)<=o}async function qe(e,t,r){let o=_e(r,t),{snapshotDir:n,baselinePath:a,receivedPath:i,diffPath:f}=o,c=v(e),m=c?e:null,M=t.runInWorker!==false,p=M?le:b,g=M?ge:N,K=M?(w,l,V)=>we(w,l,t.method,t,V):(w,l,V)=>T(w,l,t.method,t,V);te(n);let U=F(a),B=t.updateSnapshots===true?"all":t.updateSnapshots===false||t.updateSnapshots===void 0?"new":t.updateSnapshots;if(!U&&B!=="none"&&(B==="new"||B==="all")){if(m)R(a,m);else if(C(e)){let l=await g(e);await y(a,l.data,l.width,l.height);}else if(P(e))await y(a,e.data,e.width,e.height);else {let l=await p(e);await y(a,l.data,l.width,l.height);}return {pass:true,message:A({pass:true,method:t.method,snapshotCreated:true,baselinePath:a,receivedPath:i,diffPath:f,diffCount:0,diffPercentage:0,score:0,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),baselinePath:a,snapshotStatus:"added"}}let h,d;t.method==="core-native"?(h=await T(e,a,t.method,t,f),d=e):(d=c?await p(e):e,h=await K(P(d)?d:await p(d),a,f));let Q=Ie(t.method,h,t);if(Q)return existsSync(i)&&unlinkSync(i),existsSync(f)&&unlinkSync(f),{pass:true,message:A({pass:Q,method:t.method,snapshotCreated:false,baselinePath:a,receivedPath:i,diffPath:f,diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,baselinePath:a,snapshotStatus:"matched"};if(B==="all"){if(m)R(a,m);else if(C(d)){let l=await g(d);await y(a,l.data,l.width,l.height);}else P(d)&&await y(a,d.data,d.width,d.height);return existsSync(i)&&unlinkSync(i),existsSync(f)&&unlinkSync(f),{pass:true,message:A({pass:true,method:t.method,snapshotCreated:true,baselinePath:a,receivedPath:i,diffPath:f,diffCount:0,diffPercentage:0,score:0,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),baselinePath:a,snapshotStatus:"updated"}}if(m)R(i,m);else if(C(d)){let w=await g(d);await y(i,w.data,w.width,w.height);}else P(d)&&await y(i,d.data,d.width,d.height);if(h.diffOutput&&t.method!=="core-native"){let w=P(d)?d:await p(d);await y(f,h.diffOutput,w.width,w.height);}return {pass:false,message:A({pass:Q,method:t.method,snapshotCreated:false,baselinePath:a,receivedPath:i,diffPath:f,diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,threshold:t.failureThreshold??0,thresholdType:t.failureThresholdType??"pixel",updateCommand:t.updateCommand}),diffCount:h.diffCount,diffPercentage:h.diffPercentage,score:h.score,baselinePath:a,receivedPath:i,diffPath:f,snapshotStatus:"failed"}}async function Le(e,t,r){let o=await T(e,t,r.method,r),n=Ie(r.method,o,r);return {pass:n,message:n?"Images match.":`Images differ: ${o.diffCount??o.score} ${o.diffCount!==void 0?"pixels":"score"}`,diffCount:o.diffCount,diffPercentage:o.diffPercentage,score:o.score}}export{Le as compareImages,F as fileExists,A as formatReport,qe as getOrCreateSnapshot,C as isFilePath,me as isImageBuffer,P as isImageData,v as isRawPngBuffer,N as loadPNG,b as normalizeImageInput,T as runComparison,y as savePNG,R as saveRawPNGBuffer,Fe as terminateWorker,L as validateMethodSupportsInput};
package/dist/worker.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var worker_threads=require('worker_threads'),fs=require('fs');require('path');var pngjsTransformer=require('@blazediff/pngjs-transformer'),bin=require('@blazediff/bin'),core=require('@blazediff/core'),gmsd=require('@blazediff/gmsd'),ssim=require('@blazediff/ssim'),hitchhikersSsim=require('@blazediff/ssim/hitchhikers-ssim'),msssim=require('@blazediff/ssim/msssim');function p(t){return typeof t=="string"}function D(t){return (Buffer.isBuffer(t)||t instanceof Uint8Array)&&!("width"in t)}function g(t){return typeof t=="object"&&t!==null&&"data"in t&&"width"in t&&"height"in t}async function l(t){if(!fs.existsSync(t))throw new Error(`Image file not found: ${t}`);let r=await pngjsTransformer.pngjsTransformer.read(t);return {data:new Uint8Array(r.data),width:r.width,height:r.height}}async function d(t){if(p(t))return l(t);if(D(t)){let r=Buffer.isBuffer(t)?t:Buffer.from(t),o=await pngjsTransformer.pngjsTransformer.read(r);return {data:new Uint8Array(o.data),width:o.width,height:o.height}}return t.data instanceof Uint8Array?t:{data:new Uint8Array(t.data),width:t.width,height:t.height}}async function y(t,r,o,a){if(!p(t))throw new Error("Method 'bin' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");if(!p(r))throw new Error("Method 'bin' only supports file paths for baseline, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");let e=await bin.compare(t,r,o,{threshold:a.threshold,antialiasing:a.antialiasing});if(e.match)return {diffCount:0,diffPercentage:0};if(e.reason==="layout-diff")return {diffCount:Number.MAX_SAFE_INTEGER,diffPercentage:100};if(e.reason==="pixel-diff")return {diffCount:e.diffCount,diffPercentage:e.diffPercentage};if(e.reason==="file-not-exists")throw new Error(`Image file not found: ${e.file}`);return {diffCount:0,diffPercentage:0}}function I(t,r,o,a){let{width:e,height:i}=t,n=e*i;if(t.width!==r.width||t.height!==r.height)return {diffCount:n,diffPercentage:100};let m=o?new Uint8Array(n*4):void 0,s=core.diff(t.data,r.data,m,e,i,{threshold:a.threshold??.1,includeAA:a.includeAA??false});return {diffCount:s,diffPercentage:s/n*100,diffOutput:m}}function w(t,r,o,a){let{width:e,height:i}=t,n=e*i;if(t.width!==r.width||t.height!==r.height)return {score:1};let m=o?new Uint8Array(n*4):void 0;return {score:gmsd.gmsd(t.data,r.data,m,e,i,{downsample:a.downsample}),diffOutput:m}}function P(t,r,o,a,e){let{width:i,height:n}=t,m=i*n;if(t.width!==r.width||t.height!==r.height)return {score:0};let s=a?new Uint8Array(m*4):void 0,f={windowSize:e.windowSize,k1:e.k1,k2:e.k2},c;switch(o){case "ssim":c=ssim.ssim(t.data,r.data,s,i,n,f);break;case "msssim":c=msssim.msssim(t.data,r.data,s,i,n,f);break;case "hitchhikers-ssim":c=hitchhikersSsim.hitchhikersSSIM(t.data,r.data,s,i,n,f);break;default:throw new Error(`Unknown SSIM method: ${o}`)}return {score:c,diffOutput:s}}function C(t){return t==="ssim"||t==="msssim"||t==="hitchhikers-ssim"}function M(t,r){if(t==="bin"&&!p(r))throw new Error("Method 'bin' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.")}async function b(t,r,o,a,e){if(M(o,t),M(o,r),o==="bin"){let f=await y(t,r,e,a);return {diffCount:f.diffCount,diffPercentage:f.diffPercentage}}let i=g(t)?t:await d(t),n=g(r)?r:await d(r),m=e!==void 0;if(C(o)){let f=P(i,n,o,m,a);return {score:f.score,diffOutput:f.diffOutput}}if(o==="gmsd"){let f=w(i,n,m,a);return {score:f.score,diffOutput:f.diffOutput}}let s=I(i,n,m,a);return {diffCount:s.diffCount,diffPercentage:s.diffPercentage,diffOutput:s.diffOutput}}function G(t){return t.data instanceof Uint8Array?t:{data:new Uint8Array(t.data),width:t.width,height:t.height}}async function N(t){switch(t.type){case "normalize":{let{input:r}=t.payload;return d(r)}case "loadPNG":{let{filePath:r}=t.payload;return l(r)}case "compare":{let{received:r,baseline:o,method:a,options:e,diffOutputPath:i}=t.payload;return b(G(r),o,a,e,i)}default:throw new Error(`Unknown request type: ${t.type}`)}}worker_threads.parentPort&&worker_threads.parentPort.on("message",async t=>{try{let r=await N(t);worker_threads.parentPort.postMessage({id:t.id,success:!0,result:r});}catch(r){worker_threads.parentPort.postMessage({id:t.id,success:false,error:r instanceof Error?r.message:String(r)});}});
1
+ 'use strict';var worker_threads=require('worker_threads'),fs=require('fs');require('path');var codecPngjs=require('@blazediff/codec-pngjs'),core=require('@blazediff/core'),coreNative=require('@blazediff/core-native'),gmsd=require('@blazediff/gmsd'),ssim=require('@blazediff/ssim'),hitchhikersSsim=require('@blazediff/ssim/hitchhikers-ssim'),msssim=require('@blazediff/ssim/msssim');function p(t){return typeof t=="string"}function D(t){return (Buffer.isBuffer(t)||t instanceof Uint8Array)&&!("width"in t)}function g(t){return typeof t=="object"&&t!==null&&"data"in t&&"width"in t&&"height"in t}async function l(t){if(!fs.existsSync(t))throw new Error(`Image file not found: ${t}`);let e=await codecPngjs.codecPngjs.read(t);return {data:new Uint8Array(e.data),width:e.width,height:e.height}}async function d(t){if(p(t))return l(t);if(D(t)){let e=Buffer.isBuffer(t)?t:Buffer.from(t),o=await codecPngjs.codecPngjs.read(e);return {data:new Uint8Array(o.data),width:o.width,height:o.height}}return t.data instanceof Uint8Array?t:{data:new Uint8Array(t.data),width:t.width,height:t.height}}function y(t,e,o,a){let{width:r,height:i}=t,n=r*i;if(t.width!==e.width||t.height!==e.height)return {diffCount:n,diffPercentage:100};let m=o?new Uint8Array(n*4):void 0,s=core.diff(t.data,e.data,m,r,i,{threshold:a.threshold??.1,includeAA:a.includeAA??false});return {diffCount:s,diffPercentage:s/n*100,diffOutput:m}}async function I(t,e,o,a){if(!p(t))throw new Error("Method 'core-native' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");if(!p(e))throw new Error("Method 'core-native' only supports file paths for baseline, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");let r=await coreNative.compare(t,e,o,{threshold:a.threshold,antialiasing:a.antialiasing,interpret:a.outputFormat==="html",outputFormat:a.outputFormat});if(r.match)return {diffCount:0,diffPercentage:0};if(r.reason==="layout-diff")return {diffCount:Number.MAX_SAFE_INTEGER,diffPercentage:100};if(r.reason==="pixel-diff")return {diffCount:r.diffCount,diffPercentage:r.diffPercentage};if(r.reason==="file-not-exists")throw new Error(`Image file not found: ${r.file}`);return {diffCount:0,diffPercentage:0}}function w(t,e,o,a){let{width:r,height:i}=t,n=r*i;if(t.width!==e.width||t.height!==e.height)return {score:1};let m=o?new Uint8Array(n*4):void 0;return {score:gmsd.gmsd(t.data,e.data,m,r,i,{downsample:a.downsample}),diffOutput:m}}function C(t,e,o,a,r){let{width:i,height:n}=t,m=i*n;if(t.width!==e.width||t.height!==e.height)return {score:0};let s=a?new Uint8Array(m*4):void 0,f={windowSize:r.windowSize,k1:r.k1,k2:r.k2},c;switch(o){case "ssim":c=ssim.ssim(t.data,e.data,s,i,n,f);break;case "msssim":c=msssim.msssim(t.data,e.data,s,i,n,f);break;case "hitchhikers-ssim":c=hitchhikersSsim.hitchhikersSSIM(t.data,e.data,s,i,n,f);break;default:throw new Error(`Unknown SSIM method: ${o}`)}return {score:c,diffOutput:s}}function P(t){return t==="ssim"||t==="msssim"||t==="hitchhikers-ssim"}function M(t,e){if(t==="core-native"&&!p(e))throw new Error("Method 'core-native' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.")}async function b(t,e,o,a,r){if(M(o,t),M(o,e),o==="core-native"){let f=await I(t,e,r,a);return {diffCount:f.diffCount,diffPercentage:f.diffPercentage}}let i=g(t)?t:await d(t),n=g(e)?e:await d(e),m=r!==void 0;if(P(o)){let f=C(i,n,o,m,a);return {score:f.score,diffOutput:f.diffOutput}}if(o==="gmsd"){let f=w(i,n,m,a);return {score:f.score,diffOutput:f.diffOutput}}let s=y(i,n,m,a);return {diffCount:s.diffCount,diffPercentage:s.diffPercentage,diffOutput:s.diffOutput}}function G(t){return t.data instanceof Uint8Array?t:{data:new Uint8Array(t.data),width:t.width,height:t.height}}async function B(t){switch(t.type){case "normalize":{let{input:e}=t.payload;return d(e)}case "loadPNG":{let{filePath:e}=t.payload;return l(e)}case "compare":{let{received:e,baseline:o,method:a,options:r,diffOutputPath:i}=t.payload;return b(G(e),o,a,r,i)}default:throw new Error(`Unknown request type: ${t.type}`)}}worker_threads.parentPort&&worker_threads.parentPort.on("message",async t=>{try{let e=await B(t);worker_threads.parentPort?.postMessage({id:t.id,success:!0,result:e});}catch(e){worker_threads.parentPort?.postMessage({id:t.id,success:false,error:e instanceof Error?e.message:String(e)});}});
package/dist/worker.mjs CHANGED
@@ -1 +1 @@
1
- import {parentPort}from'worker_threads';import {existsSync}from'fs';import'path';import {pngjsTransformer}from'@blazediff/pngjs-transformer';import {compare}from'@blazediff/bin';import {diff}from'@blazediff/core';import {gmsd}from'@blazediff/gmsd';import {ssim}from'@blazediff/ssim';import {hitchhikersSSIM}from'@blazediff/ssim/hitchhikers-ssim';import {msssim}from'@blazediff/ssim/msssim';function d(t){return typeof t=="string"}function O(t){return (Buffer.isBuffer(t)||t instanceof Uint8Array)&&!("width"in t)}function l(t){return typeof t=="object"&&t!==null&&"data"in t&&"width"in t&&"height"in t}async function y(t){if(!existsSync(t))throw new Error(`Image file not found: ${t}`);let r=await pngjsTransformer.read(t);return {data:new Uint8Array(r.data),width:r.width,height:r.height}}async function c(t){if(d(t))return y(t);if(O(t)){let r=Buffer.isBuffer(t)?t:Buffer.from(t),o=await pngjsTransformer.read(r);return {data:new Uint8Array(o.data),width:o.width,height:o.height}}return t.data instanceof Uint8Array?t:{data:new Uint8Array(t.data),width:t.width,height:t.height}}async function I(t,r,o,a){if(!d(t))throw new Error("Method 'bin' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");if(!d(r))throw new Error("Method 'bin' only supports file paths for baseline, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");let e=await compare(t,r,o,{threshold:a.threshold,antialiasing:a.antialiasing});if(e.match)return {diffCount:0,diffPercentage:0};if(e.reason==="layout-diff")return {diffCount:Number.MAX_SAFE_INTEGER,diffPercentage:100};if(e.reason==="pixel-diff")return {diffCount:e.diffCount,diffPercentage:e.diffPercentage};if(e.reason==="file-not-exists")throw new Error(`Image file not found: ${e.file}`);return {diffCount:0,diffPercentage:0}}function w(t,r,o,a){let{width:e,height:i}=t,n=e*i;if(t.width!==r.width||t.height!==r.height)return {diffCount:n,diffPercentage:100};let m=o?new Uint8Array(n*4):void 0,s=diff(t.data,r.data,m,e,i,{threshold:a.threshold??.1,includeAA:a.includeAA??false});return {diffCount:s,diffPercentage:s/n*100,diffOutput:m}}function P(t,r,o,a){let{width:e,height:i}=t,n=e*i;if(t.width!==r.width||t.height!==r.height)return {score:1};let m=o?new Uint8Array(n*4):void 0;return {score:gmsd(t.data,r.data,m,e,i,{downsample:a.downsample}),diffOutput:m}}function C(t,r,o,a,e){let{width:i,height:n}=t,m=i*n;if(t.width!==r.width||t.height!==r.height)return {score:0};let s=a?new Uint8Array(m*4):void 0,f={windowSize:e.windowSize,k1:e.k1,k2:e.k2},h;switch(o){case "ssim":h=ssim(t.data,r.data,s,i,n,f);break;case "msssim":h=msssim(t.data,r.data,s,i,n,f);break;case "hitchhikers-ssim":h=hitchhikersSSIM(t.data,r.data,s,i,n,f);break;default:throw new Error(`Unknown SSIM method: ${o}`)}return {score:h,diffOutput:s}}function x(t){return t==="ssim"||t==="msssim"||t==="hitchhikers-ssim"}function b(t,r){if(t==="bin"&&!d(r))throw new Error("Method 'bin' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.")}async function A(t,r,o,a,e){if(b(o,t),b(o,r),o==="bin"){let f=await I(t,r,e,a);return {diffCount:f.diffCount,diffPercentage:f.diffPercentage}}let i=l(t)?t:await c(t),n=l(r)?r:await c(r),m=e!==void 0;if(x(o)){let f=C(i,n,o,m,a);return {score:f.score,diffOutput:f.diffOutput}}if(o==="gmsd"){let f=P(i,n,m,a);return {score:f.score,diffOutput:f.diffOutput}}let s=w(i,n,m,a);return {diffCount:s.diffCount,diffPercentage:s.diffPercentage,diffOutput:s.diffOutput}}function N(t){return t.data instanceof Uint8Array?t:{data:new Uint8Array(t.data),width:t.width,height:t.height}}async function E(t){switch(t.type){case "normalize":{let{input:r}=t.payload;return c(r)}case "loadPNG":{let{filePath:r}=t.payload;return y(r)}case "compare":{let{received:r,baseline:o,method:a,options:e,diffOutputPath:i}=t.payload;return A(N(r),o,a,e,i)}default:throw new Error(`Unknown request type: ${t.type}`)}}parentPort&&parentPort.on("message",async t=>{try{let r=await E(t);parentPort.postMessage({id:t.id,success:!0,result:r});}catch(r){parentPort.postMessage({id:t.id,success:false,error:r instanceof Error?r.message:String(r)});}});
1
+ import {parentPort}from'worker_threads';import {existsSync}from'fs';import'path';import {codecPngjs}from'@blazediff/codec-pngjs';import {diff}from'@blazediff/core';import {compare}from'@blazediff/core-native';import {gmsd}from'@blazediff/gmsd';import {ssim}from'@blazediff/ssim';import {hitchhikersSSIM}from'@blazediff/ssim/hitchhikers-ssim';import {msssim}from'@blazediff/ssim/msssim';function d(t){return typeof t=="string"}function O(t){return (Buffer.isBuffer(t)||t instanceof Uint8Array)&&!("width"in t)}function l(t){return typeof t=="object"&&t!==null&&"data"in t&&"width"in t&&"height"in t}async function y(t){if(!existsSync(t))throw new Error(`Image file not found: ${t}`);let e=await codecPngjs.read(t);return {data:new Uint8Array(e.data),width:e.width,height:e.height}}async function c(t){if(d(t))return y(t);if(O(t)){let e=Buffer.isBuffer(t)?t:Buffer.from(t),o=await codecPngjs.read(e);return {data:new Uint8Array(o.data),width:o.width,height:o.height}}return t.data instanceof Uint8Array?t:{data:new Uint8Array(t.data),width:t.width,height:t.height}}function I(t,e,o,a){let{width:r,height:i}=t,n=r*i;if(t.width!==e.width||t.height!==e.height)return {diffCount:n,diffPercentage:100};let m=o?new Uint8Array(n*4):void 0,s=diff(t.data,e.data,m,r,i,{threshold:a.threshold??.1,includeAA:a.includeAA??false});return {diffCount:s,diffPercentage:s/n*100,diffOutput:m}}async function w(t,e,o,a){if(!d(t))throw new Error("Method 'core-native' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");if(!d(e))throw new Error("Method 'core-native' only supports file paths for baseline, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.");let r=await compare(t,e,o,{threshold:a.threshold,antialiasing:a.antialiasing,interpret:a.outputFormat==="html",outputFormat:a.outputFormat});if(r.match)return {diffCount:0,diffPercentage:0};if(r.reason==="layout-diff")return {diffCount:Number.MAX_SAFE_INTEGER,diffPercentage:100};if(r.reason==="pixel-diff")return {diffCount:r.diffCount,diffPercentage:r.diffPercentage};if(r.reason==="file-not-exists")throw new Error(`Image file not found: ${r.file}`);return {diffCount:0,diffPercentage:0}}function C(t,e,o,a){let{width:r,height:i}=t,n=r*i;if(t.width!==e.width||t.height!==e.height)return {score:1};let m=o?new Uint8Array(n*4):void 0;return {score:gmsd(t.data,e.data,m,r,i,{downsample:a.downsample}),diffOutput:m}}function P(t,e,o,a,r){let{width:i,height:n}=t,m=i*n;if(t.width!==e.width||t.height!==e.height)return {score:0};let s=a?new Uint8Array(m*4):void 0,f={windowSize:r.windowSize,k1:r.k1,k2:r.k2},h;switch(o){case "ssim":h=ssim(t.data,e.data,s,i,n,f);break;case "msssim":h=msssim(t.data,e.data,s,i,n,f);break;case "hitchhikers-ssim":h=hitchhikersSSIM(t.data,e.data,s,i,n,f);break;default:throw new Error(`Unknown SSIM method: ${o}`)}return {score:h,diffOutput:s}}function x(t){return t==="ssim"||t==="msssim"||t==="hitchhikers-ssim"}function b(t,e){if(t==="core-native"&&!d(e))throw new Error("Method 'core-native' only supports file paths, but received a buffer. Use method 'core', 'ssim', or 'gmsd' for buffer inputs.")}async function A(t,e,o,a,r){if(b(o,t),b(o,e),o==="core-native"){let f=await w(t,e,r,a);return {diffCount:f.diffCount,diffPercentage:f.diffPercentage}}let i=l(t)?t:await c(t),n=l(e)?e:await c(e),m=r!==void 0;if(x(o)){let f=P(i,n,o,m,a);return {score:f.score,diffOutput:f.diffOutput}}if(o==="gmsd"){let f=C(i,n,m,a);return {score:f.score,diffOutput:f.diffOutput}}let s=I(i,n,m,a);return {diffCount:s.diffCount,diffPercentage:s.diffPercentage,diffOutput:s.diffOutput}}function B(t){return t.data instanceof Uint8Array?t:{data:new Uint8Array(t.data),width:t.width,height:t.height}}async function E(t){switch(t.type){case "normalize":{let{input:e}=t.payload;return c(e)}case "loadPNG":{let{filePath:e}=t.payload;return y(e)}case "compare":{let{received:e,baseline:o,method:a,options:r,diffOutputPath:i}=t.payload;return A(B(e),o,a,r,i)}default:throw new Error(`Unknown request type: ${t.type}`)}}parentPort&&parentPort.on("message",async t=>{try{let e=await E(t);parentPort?.postMessage({id:t.id,success:!0,result:e});}catch(e){parentPort?.postMessage({id:t.id,success:false,error:e instanceof Error?e.message:String(e)});}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blazediff/matcher",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Core matcher logic for visual regression testing with blazediff",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -33,10 +33,10 @@
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
35
  "picocolors": "^1.1.1",
36
- "@blazediff/bin": "3.5.0",
36
+ "@blazediff/core-native": "4.1.0",
37
37
  "@blazediff/core": "1.9.1",
38
38
  "@blazediff/gmsd": "1.7.1",
39
- "@blazediff/pngjs-transformer": "2.1.1",
39
+ "@blazediff/codec-pngjs": "3.0.0",
40
40
  "@blazediff/ssim": "1.7.1"
41
41
  },
42
42
  "devDependencies": {