jest-fuzzy 0.1.3 → 0.1.4
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 +10 -0
- package/dist/cjs/utils/imageUtils.d.ts.map +1 -1
- package/dist/cjs/utils/imageUtils.js +96 -13
- package/dist/cjs/utils/imageUtils.js.map +1 -1
- package/dist/esm/utils/imageUtils.d.ts.map +1 -1
- package/dist/esm/utils/imageUtils.js +63 -13
- package/dist/esm/utils/imageUtils.js.map +1 -1
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -226,6 +226,16 @@ await expect("./tests/error-dialog.png").toVisuallyMatch(
|
|
|
226
226
|
await expect("./tests/dashboard.png").not.toVisuallyMatch("contains sensitive user data");
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
+
**Large Image Handling:**
|
|
230
|
+
|
|
231
|
+
Images larger than 4MB are automatically resized to fit within API limits. This requires the optional `sharp` dependency:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
npm install sharp
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
If `sharp` is not installed and an image exceeds the size limit, a helpful error message will guide you to install it.
|
|
238
|
+
|
|
229
239
|
## Configuration
|
|
230
240
|
|
|
231
241
|
### API Keys
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/imageUtils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"imageUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/imageUtils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAqE7D,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAO5C;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AASD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAK9E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAe/E;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,gBAAgB,CAAC,EAAE,cAAc,GAChC,OAAO,CAAC,SAAS,CAAC,CA4CpB"}
|
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.isUrl = isUrl;
|
|
4
37
|
exports.isFilePath = isFilePath;
|
|
@@ -8,6 +41,50 @@ exports.detectMimeTypeFromExtension = detectMimeTypeFromExtension;
|
|
|
8
41
|
exports.normalizeImageInput = normalizeImageInput;
|
|
9
42
|
const promises_1 = require("fs/promises");
|
|
10
43
|
const fs_1 = require("fs");
|
|
44
|
+
// 4MB limit (Anthropic's limit is 5MB, leaving buffer)
|
|
45
|
+
const MAX_IMAGE_SIZE_BYTES = 4 * 1024 * 1024;
|
|
46
|
+
let sharpModule = undefined;
|
|
47
|
+
async function getSharp() {
|
|
48
|
+
if (sharpModule === undefined) {
|
|
49
|
+
try {
|
|
50
|
+
const mod = await Promise.resolve().then(() => __importStar(require("sharp")));
|
|
51
|
+
sharpModule = mod.default;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
sharpModule = null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return sharpModule;
|
|
58
|
+
}
|
|
59
|
+
async function resizeImageIfNeeded(buffer, mediaType) {
|
|
60
|
+
const base64Size = Math.ceil(buffer.length * 4 / 3);
|
|
61
|
+
if (base64Size <= MAX_IMAGE_SIZE_BYTES) {
|
|
62
|
+
return buffer;
|
|
63
|
+
}
|
|
64
|
+
const sharp = await getSharp();
|
|
65
|
+
if (!sharp) {
|
|
66
|
+
const sizeMB = (base64Size / (1024 * 1024)).toFixed(1);
|
|
67
|
+
throw new Error(`Image size (${sizeMB}MB) exceeds the 4MB limit for vision APIs. ` +
|
|
68
|
+
`Install 'sharp' to enable automatic resizing: npm install sharp`);
|
|
69
|
+
}
|
|
70
|
+
let currentBuffer = buffer;
|
|
71
|
+
let quality = 85;
|
|
72
|
+
while (Math.ceil(currentBuffer.length * 4 / 3) > MAX_IMAGE_SIZE_BYTES && quality >= 20) {
|
|
73
|
+
const image = sharp(buffer);
|
|
74
|
+
const metadata = await image.metadata();
|
|
75
|
+
const currentWidth = metadata.width ?? 1920;
|
|
76
|
+
// Calculate scale factor based on how much we need to reduce
|
|
77
|
+
const currentSize = Math.ceil(currentBuffer.length * 4 / 3);
|
|
78
|
+
const scaleFactor = Math.sqrt(MAX_IMAGE_SIZE_BYTES / currentSize) * 0.9;
|
|
79
|
+
const newWidth = Math.floor(currentWidth * scaleFactor);
|
|
80
|
+
currentBuffer = await sharp(buffer)
|
|
81
|
+
.resize(newWidth, null, { withoutEnlargement: true })
|
|
82
|
+
.jpeg({ quality })
|
|
83
|
+
.toBuffer();
|
|
84
|
+
quality -= 10;
|
|
85
|
+
}
|
|
86
|
+
return currentBuffer;
|
|
87
|
+
}
|
|
11
88
|
function isUrl(input) {
|
|
12
89
|
try {
|
|
13
90
|
const url = new URL(input);
|
|
@@ -53,37 +130,43 @@ function detectMimeTypeFromExtension(path) {
|
|
|
53
130
|
}
|
|
54
131
|
}
|
|
55
132
|
async function normalizeImageInput(input, mimeTypeOverride) {
|
|
56
|
-
let
|
|
133
|
+
let buffer;
|
|
57
134
|
let detectedMimeType = null;
|
|
58
135
|
if (Buffer.isBuffer(input)) {
|
|
59
|
-
|
|
60
|
-
detectedMimeType = detectMimeTypeFromBase64(base64);
|
|
136
|
+
buffer = input;
|
|
137
|
+
detectedMimeType = detectMimeTypeFromBase64(buffer.toString("base64").slice(0, 20));
|
|
61
138
|
}
|
|
62
139
|
else if (isUrl(input)) {
|
|
63
140
|
const response = await fetch(input);
|
|
64
141
|
if (!response.ok) {
|
|
65
142
|
throw new Error(`Failed to fetch image from URL: ${response.status} ${response.statusText}`);
|
|
66
143
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
detectedMimeType = detectMimeTypeFromBase64(base64);
|
|
144
|
+
buffer = Buffer.from(await response.arrayBuffer());
|
|
145
|
+
detectedMimeType = detectMimeTypeFromBase64(buffer.toString("base64").slice(0, 20));
|
|
70
146
|
}
|
|
71
147
|
else if (isFilePath(input)) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
148
|
+
buffer = await (0, promises_1.readFile)(input);
|
|
149
|
+
detectedMimeType = detectMimeTypeFromExtension(input) ??
|
|
150
|
+
detectMimeTypeFromBase64(buffer.toString("base64").slice(0, 20));
|
|
75
151
|
}
|
|
76
152
|
else if (isBase64Image(input)) {
|
|
77
|
-
|
|
78
|
-
detectedMimeType = detectMimeTypeFromBase64(
|
|
153
|
+
buffer = Buffer.from(input, "base64");
|
|
154
|
+
detectedMimeType = detectMimeTypeFromBase64(input);
|
|
79
155
|
}
|
|
80
156
|
else {
|
|
81
157
|
throw new Error("Invalid image input. Expected a file path, URL, base64 string, or Buffer.");
|
|
82
158
|
}
|
|
83
|
-
|
|
159
|
+
let mediaType = mimeTypeOverride ?? detectedMimeType;
|
|
84
160
|
if (!mediaType) {
|
|
85
161
|
throw new Error("Could not detect image MIME type. Please provide it via the mimeType option.");
|
|
86
162
|
}
|
|
87
|
-
|
|
163
|
+
// Resize if needed (converts to JPEG if resizing occurs)
|
|
164
|
+
const originalSize = buffer.length;
|
|
165
|
+
const resizedBuffer = await resizeImageIfNeeded(buffer, mediaType);
|
|
166
|
+
if (resizedBuffer.length !== originalSize) {
|
|
167
|
+
// Image was resized, it's now JPEG
|
|
168
|
+
mediaType = "image/jpeg";
|
|
169
|
+
}
|
|
170
|
+
return { base64: resizedBuffer.toString("base64"), mediaType };
|
|
88
171
|
}
|
|
89
172
|
//# sourceMappingURL=imageUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageUtils.js","sourceRoot":"","sources":["../../../src/utils/imageUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"imageUtils.js","sourceRoot":"","sources":["../../../src/utils/imageUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/imageUtils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"imageUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/imageUtils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAqE7D,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAO5C;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AASD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAK9E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAe/E;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,gBAAgB,CAAC,EAAE,cAAc,GAChC,OAAO,CAAC,SAAS,CAAC,CA4CpB"}
|
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
import { readFile } from "fs/promises";
|
|
2
2
|
import { existsSync } from "fs";
|
|
3
|
+
// 4MB limit (Anthropic's limit is 5MB, leaving buffer)
|
|
4
|
+
const MAX_IMAGE_SIZE_BYTES = 4 * 1024 * 1024;
|
|
5
|
+
let sharpModule = undefined;
|
|
6
|
+
async function getSharp() {
|
|
7
|
+
if (sharpModule === undefined) {
|
|
8
|
+
try {
|
|
9
|
+
const mod = await import("sharp");
|
|
10
|
+
sharpModule = mod.default;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
sharpModule = null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return sharpModule;
|
|
17
|
+
}
|
|
18
|
+
async function resizeImageIfNeeded(buffer, mediaType) {
|
|
19
|
+
const base64Size = Math.ceil(buffer.length * 4 / 3);
|
|
20
|
+
if (base64Size <= MAX_IMAGE_SIZE_BYTES) {
|
|
21
|
+
return buffer;
|
|
22
|
+
}
|
|
23
|
+
const sharp = await getSharp();
|
|
24
|
+
if (!sharp) {
|
|
25
|
+
const sizeMB = (base64Size / (1024 * 1024)).toFixed(1);
|
|
26
|
+
throw new Error(`Image size (${sizeMB}MB) exceeds the 4MB limit for vision APIs. ` +
|
|
27
|
+
`Install 'sharp' to enable automatic resizing: npm install sharp`);
|
|
28
|
+
}
|
|
29
|
+
let currentBuffer = buffer;
|
|
30
|
+
let quality = 85;
|
|
31
|
+
while (Math.ceil(currentBuffer.length * 4 / 3) > MAX_IMAGE_SIZE_BYTES && quality >= 20) {
|
|
32
|
+
const image = sharp(buffer);
|
|
33
|
+
const metadata = await image.metadata();
|
|
34
|
+
const currentWidth = metadata.width ?? 1920;
|
|
35
|
+
// Calculate scale factor based on how much we need to reduce
|
|
36
|
+
const currentSize = Math.ceil(currentBuffer.length * 4 / 3);
|
|
37
|
+
const scaleFactor = Math.sqrt(MAX_IMAGE_SIZE_BYTES / currentSize) * 0.9;
|
|
38
|
+
const newWidth = Math.floor(currentWidth * scaleFactor);
|
|
39
|
+
currentBuffer = await sharp(buffer)
|
|
40
|
+
.resize(newWidth, null, { withoutEnlargement: true })
|
|
41
|
+
.jpeg({ quality })
|
|
42
|
+
.toBuffer();
|
|
43
|
+
quality -= 10;
|
|
44
|
+
}
|
|
45
|
+
return currentBuffer;
|
|
46
|
+
}
|
|
3
47
|
export function isUrl(input) {
|
|
4
48
|
try {
|
|
5
49
|
const url = new URL(input);
|
|
@@ -45,37 +89,43 @@ export function detectMimeTypeFromExtension(path) {
|
|
|
45
89
|
}
|
|
46
90
|
}
|
|
47
91
|
export async function normalizeImageInput(input, mimeTypeOverride) {
|
|
48
|
-
let
|
|
92
|
+
let buffer;
|
|
49
93
|
let detectedMimeType = null;
|
|
50
94
|
if (Buffer.isBuffer(input)) {
|
|
51
|
-
|
|
52
|
-
detectedMimeType = detectMimeTypeFromBase64(base64);
|
|
95
|
+
buffer = input;
|
|
96
|
+
detectedMimeType = detectMimeTypeFromBase64(buffer.toString("base64").slice(0, 20));
|
|
53
97
|
}
|
|
54
98
|
else if (isUrl(input)) {
|
|
55
99
|
const response = await fetch(input);
|
|
56
100
|
if (!response.ok) {
|
|
57
101
|
throw new Error(`Failed to fetch image from URL: ${response.status} ${response.statusText}`);
|
|
58
102
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
detectedMimeType = detectMimeTypeFromBase64(base64);
|
|
103
|
+
buffer = Buffer.from(await response.arrayBuffer());
|
|
104
|
+
detectedMimeType = detectMimeTypeFromBase64(buffer.toString("base64").slice(0, 20));
|
|
62
105
|
}
|
|
63
106
|
else if (isFilePath(input)) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
107
|
+
buffer = await readFile(input);
|
|
108
|
+
detectedMimeType = detectMimeTypeFromExtension(input) ??
|
|
109
|
+
detectMimeTypeFromBase64(buffer.toString("base64").slice(0, 20));
|
|
67
110
|
}
|
|
68
111
|
else if (isBase64Image(input)) {
|
|
69
|
-
|
|
70
|
-
detectedMimeType = detectMimeTypeFromBase64(
|
|
112
|
+
buffer = Buffer.from(input, "base64");
|
|
113
|
+
detectedMimeType = detectMimeTypeFromBase64(input);
|
|
71
114
|
}
|
|
72
115
|
else {
|
|
73
116
|
throw new Error("Invalid image input. Expected a file path, URL, base64 string, or Buffer.");
|
|
74
117
|
}
|
|
75
|
-
|
|
118
|
+
let mediaType = mimeTypeOverride ?? detectedMimeType;
|
|
76
119
|
if (!mediaType) {
|
|
77
120
|
throw new Error("Could not detect image MIME type. Please provide it via the mimeType option.");
|
|
78
121
|
}
|
|
79
|
-
|
|
122
|
+
// Resize if needed (converts to JPEG if resizing occurs)
|
|
123
|
+
const originalSize = buffer.length;
|
|
124
|
+
const resizedBuffer = await resizeImageIfNeeded(buffer, mediaType);
|
|
125
|
+
if (resizedBuffer.length !== originalSize) {
|
|
126
|
+
// Image was resized, it's now JPEG
|
|
127
|
+
mediaType = "image/jpeg";
|
|
128
|
+
}
|
|
129
|
+
return { base64: resizedBuffer.toString("base64"), mediaType };
|
|
80
130
|
}
|
|
81
131
|
//# sourceMappingURL=imageUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageUtils.js","sourceRoot":"","sources":["../../../src/utils/imageUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAGhC,MAAM,UAAU,KAAK,CAAC,KAAa;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,iBAAiB,GAAmC;IACxD,MAAM,EAAE,YAAY;IACpB,aAAa,EAAE,WAAW;IAC1B,QAAQ,EAAE,WAAW;IACrB,OAAO,EAAE,YAAY;CACtB,CAAC;AAEF,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC;QAClE,IAAI,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,wBAAwB,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,IAAY;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IAChD,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,KAAK,CAAC;QACX,KAAK,MAAM;YACT,OAAO,YAAY,CAAC;QACtB,KAAK,KAAK;YACR,OAAO,WAAW,CAAC;QACrB,KAAK,KAAK;YACR,OAAO,WAAW,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,YAAY,CAAC;QACtB;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAsB,EACtB,gBAAiC;IAEjC,IAAI,MAAc,CAAC;IACnB,IAAI,gBAAgB,GAA0B,IAAI,CAAC;IAEnD,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC
|
|
1
|
+
{"version":3,"file":"imageUtils.js","sourceRoot":"","sources":["../../../src/utils/imageUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jest-fuzzy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "LLM-powered fuzzy matchers for Jest",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -36,8 +36,17 @@
|
|
|
36
36
|
"ai"
|
|
37
37
|
],
|
|
38
38
|
"license": "MIT",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"sharp": ">=0.33.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"sharp": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
39
47
|
"devDependencies": {
|
|
40
48
|
"@types/jest": "^29.5.14",
|
|
49
|
+
"sharp": "^0.33.0",
|
|
41
50
|
"@types/node": "^22.10.5",
|
|
42
51
|
"dotenv": "^16.4.7",
|
|
43
52
|
"jest": "^29.7.0",
|