Sign In

@applitools/image

Package Overview
Dependencies
Maintainers
49
Versions
75
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@applitools/image - npm Package Compare versions

Comparing version
1.2.6
to
1.2.7
+44
dist/formats/png-sharp.js
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.toPngSharp = exports.fromPngBufferSharp = void 0;
const sharp_1 = __importDefault(require("sharp"));
const buffer_1 = require("buffer");
async function fromPngBufferSharp(buffer) {
// Ensure we always get RGBA (4 channels) output for consistency
const { data, info } = await (0, sharp_1.default)(buffer_1.Buffer.from(buffer))
.ensureAlpha() // Force RGBA output
.raw()
.toBuffer({ resolveWithObject: true });
return {
width: info.width,
height: info.height,
data: data,
};
}
exports.fromPngBufferSharp = fromPngBufferSharp;
async function toPngSharp(image) {
// Calculate channels from data size (only support RGB=3 and RGBA=4)
const calculatedChannels = image.data.length / (image.width * image.height);
const channels = Math.round(calculatedChannels);
// Validate channels and data size
if (![3, 4].includes(channels) || Math.abs(channels - calculatedChannels) > 0.01) {
throw new Error(`Invalid image data: expected ${image.width}x${image.height} with RGB (3) or RGBA (4) channels, ` +
`got ${image.data.length} bytes (${calculatedChannels.toFixed(2)} channels)`);
}
return await (0, sharp_1.default)(buffer_1.Buffer.from(image.data), {
raw: {
width: image.width,
height: image.height,
channels,
},
})
.png({
compressionLevel: 9,
adaptiveFiltering: false,
})
.toBuffer();
}
exports.toPngSharp = toPngSharp;
import type { ImageBuffer, ImageRaw } from '../types';
export declare function fromPngBufferSharp(buffer: ImageBuffer): Promise<ImageRaw>;
export declare function toPngSharp(image: ImageRaw): Promise<ImageBuffer>;
+7
-0
# Changelog
## [1.2.7](https://github.com/Applitools-Dev/sdk/compare/js/image@1.2.6...js/image@1.2.7) (2026-03-01)
### Performance Improvements
* use sharp to accelerate png performance in all js packages ([#3547](https://github.com/Applitools-Dev/sdk/issues/3547)) ([6e3c7b9](https://github.com/Applitools-Dev/sdk/commit/6e3c7b9e34815f470032ece52a161001592ad1b1))
## [1.2.6](https://github.com/Applitools-Dev/sdk/compare/js/image@1.2.5...js/image@1.2.6) (2025-12-01)

@@ -4,0 +11,0 @@

+63
-12

@@ -39,18 +39,69 @@ "use strict";

exports.extractPngSize = extractPngSize;
async function fromPngBuffer(buffer) {
return new Promise((resolve, reject) => {
const image = new png.Image();
image.parse(buffer_1.Buffer.from(buffer), (err, image) => {
if (err)
return reject(err);
resolve(image);
/**
* Dynamically import png-sharp module
* Tries .js first (production), then .ts (ts-node/dev)
*/
async function importPngSharp() {
try {
return await import('./png-sharp.js');
}
catch (error) {
// In ts-node, .js imports fail, try .ts instead
// Use dynamic string to bypass TypeScript's static import checking
const tsPath = './png-sharp' + '.ts';
return await import(tsPath);
}
}
/**
* Decode PNG buffer to ImageRaw
* Tries sharp first (faster), falls back to png-async if sharp fails
*/
async function fromPngBuffer(buffer, defaultLogger) {
const logger = defaultLogger === null || defaultLogger === void 0 ? void 0 : defaultLogger.extend({ label: 'image/png' });
try {
const pngSharp = await importPngSharp();
const result = await pngSharp.fromPngBufferSharp(buffer);
logger === null || logger === void 0 ? void 0 : logger.log('Used Sharp for PNG decoding');
return result;
}
catch (error) {
// Fallback to png-async if sharp fails (SEA, not installed, or runtime error)
logger === null || logger === void 0 ? void 0 : logger.log('Falling back to png-async for PNG decoding:', error instanceof Error ? error.message : String(error));
return new Promise((resolve, reject) => {
const image = new png.Image();
image.parse(buffer_1.Buffer.from(buffer), (err, image) => {
if (err)
return reject(err);
resolve(image);
});
});
});
}
}
exports.fromPngBuffer = fromPngBuffer;
async function toPng(image) {
const wrapper = new png.Image({ width: image.width, height: image.height });
wrapper.data = buffer_1.Buffer.from(image.data);
return utils.streams.toBuffer(wrapper.pack());
/**
* Encode ImageRaw to PNG buffer
* Tries sharp first (22× faster), falls back to png-async if sharp fails
*/
async function toPng(image, defaultLogger) {
const logger = defaultLogger === null || defaultLogger === void 0 ? void 0 : defaultLogger.extend({ label: 'image/png' });
try {
const pngSharp = await importPngSharp();
const result = await pngSharp.toPngSharp(image);
logger === null || logger === void 0 ? void 0 : logger.log('Used Sharp for PNG encoding');
return result;
}
catch (error) {
// Fallback to png-async if sharp fails (SEA, not installed, or runtime error)
// Uses RLE strategy (default) for optimal performance (~35ms)
logger === null || logger === void 0 ? void 0 : logger.log('Falling back to png-async for PNG encoding:', error instanceof Error ? error.message : String(error));
const expectedSize = image.width * image.height * 4;
const actualSize = image.data.length;
if (actualSize !== expectedSize) {
throw new Error(`Image data size mismatch: expected ${expectedSize} bytes for ${image.width}x${image.height}, got ${actualSize} bytes`);
}
const wrapper = new png.Image({ width: image.width, height: image.height });
wrapper.data = buffer_1.Buffer.from(image.data);
return utils.streams.toBuffer(wrapper.pack());
}
}
exports.toPng = toPng;
+8
-7

@@ -38,9 +38,10 @@ "use strict";

const utils = __importStar(require("@applitools/utils"));
function makeImage(data) {
function makeImage(data, defaultLogger) {
let image, size;
let transforms = { rotate: 0, scale: 1, crop: undefined, modifiers: [] };
const logger = defaultLogger === null || defaultLogger === void 0 ? void 0 : defaultLogger.extend({ label: 'image' });
if (utils.types.isUint8Array(data) || utils.types.isAnyArrayBuffer(data)) {
const buffer = utils.types.isAnyArrayBuffer(data) ? new Uint8Array(data) : data;
if ((0, png_1.isPngBuffer)(buffer)) {
image = (0, png_1.fromPngBuffer)(buffer);
image = (0, png_1.fromPngBuffer)(buffer, logger);
size = (0, png_1.extractPngSize)(buffer);

@@ -188,6 +189,6 @@ }

async toPng() {
return (0, png_1.toPng)(await this.toObject());
return (0, png_1.toPng)(await this.toObject(), logger);
},
async toFile(path) {
return toFile(await image, path);
return toFile(await image, path, logger);
},

@@ -206,3 +207,3 @@ async toObject() {

const transformedImage = await transform(image ? await image : size, transforms);
return toFile(transformedImage, path_1.default.join(debug.path, filename)).catch(() => undefined);
return toFile(transformedImage, path_1.default.join(debug.path, filename), logger).catch(() => undefined);
},

@@ -215,4 +216,4 @@ };

}
async function toFile(image, filepath) {
const buffer = await (0, png_1.toPng)(image);
async function toFile(image, filepath, logger) {
const buffer = await (0, png_1.toPng)(image, logger);
return new Promise((resolve, reject) => {

@@ -219,0 +220,0 @@ fs_1.default.mkdirSync(path_1.default.dirname(filepath), { recursive: true });

{
"name": "@applitools/image",
"version": "1.2.6",
"version": "1.2.7",
"description": "Image primitive used for capturing and manipulating screenshots",

@@ -39,2 +39,3 @@ "homepage": "https://applitools.com",

"dependencies": {
"@applitools/logger": "2.2.8",
"@applitools/utils": "1.14.1",

@@ -44,3 +45,4 @@ "bmpimagejs": "1.0.4",

"omggif": "1.0.10",
"png-async": "0.9.4"
"png-async": "0.9.4",
"sharp": "^0.34.5"
},

@@ -59,3 +61,8 @@ "devDependencies": {

"access": "public"
},
"lavamoat": {
"allowScripts": {
"sharp": true
}
}
}
import type { ImageBuffer, ImageRaw } from '../types';
import type { Size } from '@applitools/utils';
import type { Logger } from '@applitools/logger';
export declare function isPngBuffer(buffer: ImageBuffer): boolean;
export declare function extractPngSize(buffer: ImageBuffer): Size;
export declare function fromPngBuffer(buffer: ImageBuffer): Promise<ImageRaw>;
export declare function toPng(image: ImageRaw): Promise<ImageBuffer>;
/**
* Decode PNG buffer to ImageRaw
* Tries sharp first (faster), falls back to png-async if sharp fails
*/
export declare function fromPngBuffer(buffer: ImageBuffer, defaultLogger?: Logger): Promise<ImageRaw>;
/**
* Encode ImageRaw to PNG buffer
* Tries sharp first (22× faster), falls back to png-async if sharp fails
*/
export declare function toPng(image: ImageRaw, defaultLogger?: Logger): Promise<ImageBuffer>;
import type { ImageBuffer, ImageRaw } from './types';
import type { Location, Size, Region } from '@applitools/utils';
import type { Logger } from '@applitools/logger';
export interface Image {

@@ -44,3 +45,3 @@ isImage: true;

};
export declare function makeImage(data: ImageSource): Image;
export declare function makeImage(data: ImageSource, defaultLogger?: Logger): Image;
export {};