@blazediff/matcher 1.3.0 → 1.3.1
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 +6 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/dist/worker.js +1 -1
- package/dist/worker.mjs +1 -1
- package/package.json +5 -5
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`, `
|
|
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/
|
|
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 (
|
|
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
|
-
### `
|
|
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: '
|
|
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: '
|
|
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 = "
|
|
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/
|
|
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 (
|
|
65
|
+
* Enable anti-aliasing detection (core-native method)
|
|
66
66
|
* @default false
|
|
67
67
|
*/
|
|
68
68
|
antialiasing?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';var fs=require('fs'),path=require('path'),
|
|
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:
|
|
3
|
-
`)}u();L();_();k();u();var y=null,Te=0,ce=false,
|
|
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:()=>G,isFilePath:()=>I,isImageBuffer:()=>fe,isImageData:()=>C,isRawPngBuffer:()=>E,loadPNG:()=>B,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 B(e){if(!fs.existsSync(e))throw new Error(`Image file not found: ${e}`);let t=await codecPngjs.pngjsTransformer.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.pngjsTransformer.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 B(e);if(E(e)){let t=Buffer.isBuffer(e)?e:Buffer.from(e),r=await codecPngjs.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 G(e){return fs.existsSync(e)}function ee(e){fs.existsSync(e)||fs.mkdirSync(e,{recursive:true});}var k=S(()=>{u();});function N(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 z(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});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 F(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,{compareBin:()=>z,compareCore:()=>N,compareGmsd:()=>F,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 z(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=F(a,i,f,o);return {score:m.score,diffOutput:m.diffOutput}}let c=N(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 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":"'-"},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(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:o,diffPath:n,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(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(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,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 ve(e){return e.testName.replace(/[^a-zA-Z0-9-_\s]/g,"").replace(/\s+/g,"-").toLowerCase()||"snapshot"}function Ge(e,t){let r=path.dirname(e.testPath),o=t.snapshotsDir??"__snapshots__",n=path.isAbsolute(o)?o:path.join(r,o),a=t.snapshotIdentifier??ve(e);return {snapshotDir:n,baselinePath:path.join(n,`${a}.png`),receivedPath:path.join(n,`${a}.received.png`),diffPath:path.join(n,`${a}.diff.png`)}}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 Ne(e,t,r){let o=Ge(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:B,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=G(a),v=t.updateSnapshots===true?"all":t.updateSnapshots===false||t.updateSnapshots===void 0?"new":t.updateSnapshots;if(!U&&v!=="none"&&(v==="new"||v==="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(v==="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 ze(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=ze;exports.fileExists=G;exports.formatReport=T;exports.getOrCreateSnapshot=Ne;exports.isFilePath=I;exports.isImageBuffer=fe;exports.isImageData=C;exports.isRawPngBuffer=E;exports.loadPNG=B;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
|
|
2
|
-
`)}function Ue(){return `${s.green(
|
|
3
|
-
`)}u();H();q();$();u();var x=null,We=0,
|
|
1
|
+
import Pe,{dirname,isAbsolute,join}from'path';import {fileURLToPath}from'url';import {existsSync,mkdirSync,writeFileSync,unlinkSync}from'fs';import {pngjsTransformer}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:()=>N,isFilePath:()=>C,isImageBuffer:()=>me,isImageData:()=>P,isRawPngBuffer:()=>B,loadPNG:()=>W,normalizeImageInput:()=>b,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 b(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 N(e){return existsSync(e)}function te(e){existsSync(e)||mkdirSync(e,{recursive:true});}var $=D(()=>{u();});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 {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 F(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});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,{compareBin:()=>F,compareCore:()=>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 F(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=z(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 v={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(v.success)} ${s.green("New snapshot created")}`,` ${s.dim(v.arrow)} ${s.dim(e)}`].join(`
|
|
2
|
+
`)}function Ue(){return `${s.green(v.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(v.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(v.info)} ${s.cyan(`Run with ${M??"--update"} to update the snapshot`)}`),p.join(`
|
|
3
|
+
`)}u();H();q();$();u();var x=null,We=0,he=false,k=new Map;function ve(){let e=join(I,"worker.js");if(existsSync(e))return e;let t=join(I,"../dist/worker.js");return existsSync(t)?t:null}function Ge(){if(he)return null;if(!x){let e=ve();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=Ge();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 Ne(){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,c=B(e),m=c?e:null,M=t.runInWorker!==false,p=M?le:b,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=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(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(G==="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,N as fileExists,A as formatReport,qe as getOrCreateSnapshot,C as isFilePath,me as isImageBuffer,P as isImageData,B as isRawPngBuffer,W as loadPNG,b as normalizeImageInput,T as runComparison,y as savePNG,R as saveRawPNGBuffer,Ne 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
|
|
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.pngjsTransformer.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.pngjsTransformer.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});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 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},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 C(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(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=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 N(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 N(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
|
|
1
|
+
import {parentPort}from'worker_threads';import {existsSync}from'fs';import'path';import {pngjsTransformer}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 pngjsTransformer.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 pngjsTransformer.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});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 P(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 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},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=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=I(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: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(N(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.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Core matcher logic for visual regression testing with blazediff",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"picocolors": "^1.1.1",
|
|
36
|
-
"@blazediff/
|
|
37
|
-
"@blazediff/core": "1.9.1",
|
|
36
|
+
"@blazediff/core-native": "4.0.0",
|
|
38
37
|
"@blazediff/gmsd": "1.7.1",
|
|
39
|
-
"@blazediff/pngjs
|
|
40
|
-
"@blazediff/ssim": "1.7.1"
|
|
38
|
+
"@blazediff/codec-pngjs": "3.0.0",
|
|
39
|
+
"@blazediff/ssim": "1.7.1",
|
|
40
|
+
"@blazediff/core": "1.9.1"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^24.3.0",
|