From c95103f12e5d9aa635f39000e9b8151617e67e66 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 06:38:24 +0000 Subject: [PATCH 01/14] Initial plan From 19c151434457648308b5564831810be6cda2b7dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 06:46:14 +0000 Subject: [PATCH 02/14] Plan preprocessing layer architecture Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- package-lock.json | 648 +++++++++++++++++------------------ package.json | 1 + type-definitions/quagga.d.ts | 10 + 3 files changed, 317 insertions(+), 342 deletions(-) diff --git a/package-lock.json b/package-lock.json index 66430835..114e7150 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ "ndarray-linear-interpolate": "^1.0.0", "ndarray-pixels": "^5.0.1", "nyc": "^17.1.0", + "sharp": "^0.34.5", "sinon": "^21.0.0", "sinon-chai": "^3.7.0", "source-map-loader": "^1.1.1", @@ -2291,9 +2292,9 @@ "optional": true }, "node_modules/@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", "dev": true, "license": "MIT", "optional": true, @@ -2506,10 +2507,20 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", - "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" ], @@ -2526,13 +2537,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.0" + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", - "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" ], @@ -2549,13 +2560,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.0" + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", - "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" ], @@ -2570,9 +2581,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", - "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" ], @@ -2587,9 +2598,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", - "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ "arm" ], @@ -2604,9 +2615,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", - "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ "arm64" ], @@ -2621,9 +2632,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", - "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", "cpu": [ "ppc64" ], @@ -2637,10 +2648,27 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", - "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ "s390x" ], @@ -2655,9 +2683,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", - "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ "x64" ], @@ -2672,9 +2700,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", - "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ "arm64" ], @@ -2689,9 +2717,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", - "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ "x64" ], @@ -2706,9 +2734,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", - "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ "arm" ], @@ -2725,13 +2753,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.0" + "@img/sharp-libvips-linux-arm": "1.2.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", - "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ "arm64" ], @@ -2748,13 +2776,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.0" + "@img/sharp-libvips-linux-arm64": "1.2.4" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", - "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", "cpu": [ "ppc64" ], @@ -2771,13 +2799,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.0" + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", - "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" ], @@ -2794,13 +2845,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.0" + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", - "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], @@ -2817,13 +2868,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.0" + "@img/sharp-libvips-linux-x64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", - "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], @@ -2840,13 +2891,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", - "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], @@ -2863,13 +2914,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", - "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "cpu": [ "wasm32" ], @@ -2877,7 +2928,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.4" + "@emnapi/runtime": "^1.7.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -2887,9 +2938,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", - "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", "cpu": [ "arm64" ], @@ -2907,9 +2958,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", - "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" ], @@ -2927,9 +2978,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", - "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" ], @@ -5783,20 +5834,6 @@ "node": ">=0.10.0" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -5812,37 +5849,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/colorette": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", @@ -6635,9 +6641,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -9563,13 +9569,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true, - "license": "MIT" - }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -13287,16 +13286,16 @@ } }, "node_modules/sharp": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", - "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -13305,34 +13304,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.3", - "@img/sharp-darwin-x64": "0.34.3", - "@img/sharp-libvips-darwin-arm64": "1.2.0", - "@img/sharp-libvips-darwin-x64": "1.2.0", - "@img/sharp-libvips-linux-arm": "1.2.0", - "@img/sharp-libvips-linux-arm64": "1.2.0", - "@img/sharp-libvips-linux-ppc64": "1.2.0", - "@img/sharp-libvips-linux-s390x": "1.2.0", - "@img/sharp-libvips-linux-x64": "1.2.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", - "@img/sharp-libvips-linuxmusl-x64": "1.2.0", - "@img/sharp-linux-arm": "0.34.3", - "@img/sharp-linux-arm64": "0.34.3", - "@img/sharp-linux-ppc64": "0.34.3", - "@img/sharp-linux-s390x": "0.34.3", - "@img/sharp-linux-x64": "0.34.3", - "@img/sharp-linuxmusl-arm64": "0.34.3", - "@img/sharp-linuxmusl-x64": "0.34.3", - "@img/sharp-wasm32": "0.34.3", - "@img/sharp-win32-arm64": "0.34.3", - "@img/sharp-win32-ia32": "0.34.3", - "@img/sharp-win32-x64": "0.34.3" + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" } }, "node_modules/sharp/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -13445,16 +13446,6 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/sinon": { "version": "21.0.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", @@ -17307,9 +17298,9 @@ } }, "@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", "dev": true, "optional": true, "requires": { @@ -17463,187 +17454,210 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true + }, "@img/sharp-darwin-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", - "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-darwin-arm64": "1.2.0" + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, "@img/sharp-darwin-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", - "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-darwin-x64": "1.2.0" + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, "@img/sharp-libvips-darwin-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", - "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "dev": true, "optional": true }, "@img/sharp-libvips-darwin-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", - "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-arm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", - "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", - "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-ppc64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", - "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "dev": true, + "optional": true + }, + "@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-s390x": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", - "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", - "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "dev": true, "optional": true }, "@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", - "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "dev": true, "optional": true }, "@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", - "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "dev": true, "optional": true }, "@img/sharp-linux-arm": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", - "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-arm": "1.2.0" + "@img/sharp-libvips-linux-arm": "1.2.4" } }, "@img/sharp-linux-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", - "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-arm64": "1.2.0" + "@img/sharp-libvips-linux-arm64": "1.2.4" } }, "@img/sharp-linux-ppc64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", - "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-ppc64": "1.2.0" + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "dev": true, + "optional": true, + "requires": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, "@img/sharp-linux-s390x": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", - "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-s390x": "1.2.0" + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, "@img/sharp-linux-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", - "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-x64": "1.2.0" + "@img/sharp-libvips-linux-x64": "1.2.4" } }, "@img/sharp-linuxmusl-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", - "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, "@img/sharp-linuxmusl-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", - "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, "@img/sharp-wasm32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", - "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "dev": true, "optional": true, "requires": { - "@emnapi/runtime": "^1.4.4" + "@emnapi/runtime": "^1.7.0" } }, "@img/sharp-win32-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", - "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", "dev": true, "optional": true }, "@img/sharp-win32-ia32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", - "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "dev": true, "optional": true }, "@img/sharp-win32-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", - "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "dev": true, "optional": true }, @@ -19718,33 +19732,6 @@ "object-visit": "^1.0.0" } }, - "color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dev": true, - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "dependencies": { - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -19760,16 +19747,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "colorette": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", @@ -20384,9 +20361,9 @@ "dev": true }, "detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true }, "diff": { @@ -22582,12 +22559,6 @@ "get-intrinsic": "^1.2.6" } }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - }, "is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -25357,42 +25328,44 @@ } }, "sharp": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", - "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", - "dev": true, - "requires": { - "@img/sharp-darwin-arm64": "0.34.3", - "@img/sharp-darwin-x64": "0.34.3", - "@img/sharp-libvips-darwin-arm64": "1.2.0", - "@img/sharp-libvips-darwin-x64": "1.2.0", - "@img/sharp-libvips-linux-arm": "1.2.0", - "@img/sharp-libvips-linux-arm64": "1.2.0", - "@img/sharp-libvips-linux-ppc64": "1.2.0", - "@img/sharp-libvips-linux-s390x": "1.2.0", - "@img/sharp-libvips-linux-x64": "1.2.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", - "@img/sharp-libvips-linuxmusl-x64": "1.2.0", - "@img/sharp-linux-arm": "0.34.3", - "@img/sharp-linux-arm64": "0.34.3", - "@img/sharp-linux-ppc64": "0.34.3", - "@img/sharp-linux-s390x": "0.34.3", - "@img/sharp-linux-x64": "0.34.3", - "@img/sharp-linuxmusl-arm64": "0.34.3", - "@img/sharp-linuxmusl-x64": "0.34.3", - "@img/sharp-wasm32": "0.34.3", - "@img/sharp-win32-arm64": "0.34.3", - "@img/sharp-win32-ia32": "0.34.3", - "@img/sharp-win32-x64": "0.34.3", - "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, + "requires": { + "@img/colour": "^1.0.0", + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" }, "dependencies": { "semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true } } @@ -25466,15 +25439,6 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dev": true, - "requires": { - "is-arrayish": "^0.3.1" - } - }, "sinon": { "version": "21.0.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", diff --git a/package.json b/package.json index adbf4be1..ada21120 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "ndarray-linear-interpolate": "^1.0.0", "ndarray-pixels": "^5.0.1", "nyc": "^17.1.0", + "sharp": "^0.34.5", "sinon": "^21.0.0", "sinon-chai": "^3.7.0", "source-map-loader": "^1.1.1", diff --git a/type-definitions/quagga.d.ts b/type-definitions/quagga.d.ts index 0a0253f5..1a3cc5e2 100644 --- a/type-definitions/quagga.d.ts +++ b/type-definitions/quagga.d.ts @@ -887,6 +887,16 @@ export interface QuaggaJSConfigObject { size?: number; sequence?: boolean; + /** + * Adds a white border of the specified number of pixels around the image. + * This is useful for barcodes that lack sufficient quiet zone (whitespace) + * around them. When a barcode is generated or cropped without proper margins, + * the decoder may fail to detect it. Adding a border simulates the whitespace + * that would naturally exist when displaying the barcode on paper or screen. + * @default 0 (no border added) + */ + addBorder?: number; + debug?: { /** * @default false From 95f544cfb94429fa9fe267fd26c8ed42bd0dace2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 06:56:47 +0000 Subject: [PATCH 03/14] Add preprocessing pipeline with addBorder preprocessor Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- src/preprocessing/index.ts | 6 + src/preprocessing/preprocessor.ts | 127 ++++++++++++++++++ src/preprocessing/test/preprocessor.spec.ts | 140 ++++++++++++++++++++ src/quagga.js | 3 + src/quagga/quagga.ts | 9 ++ type-definitions/quagga.d.ts | 72 ++++++++-- 6 files changed, 346 insertions(+), 11 deletions(-) create mode 100644 src/preprocessing/index.ts create mode 100644 src/preprocessing/preprocessor.ts create mode 100644 src/preprocessing/test/preprocessor.spec.ts diff --git a/src/preprocessing/index.ts b/src/preprocessing/index.ts new file mode 100644 index 00000000..a13dc294 --- /dev/null +++ b/src/preprocessing/index.ts @@ -0,0 +1,6 @@ +export type { PreprocessorFunction } from './preprocessor'; +export { + addBorder, + applyPreprocessors, + Preprocessors, +} from './preprocessor'; diff --git a/src/preprocessing/preprocessor.ts b/src/preprocessing/preprocessor.ts new file mode 100644 index 00000000..29dca9f0 --- /dev/null +++ b/src/preprocessing/preprocessor.ts @@ -0,0 +1,127 @@ +import ImageWrapper from '../common/image_wrapper'; + +/** + * A preprocessor function that transforms image data. + * Preprocessors are applied to the image data after frame grabbing + * but before barcode localization and decoding. + * + * IMPORTANT: Preprocessors should maintain the same image dimensions. + * The returned ImageWrapper should have the same size as the input. + * + * @param imageWrapper The image wrapper to process + * @returns The processed image wrapper (same size as input) + */ +export type PreprocessorFunction = (imageWrapper: ImageWrapper) => ImageWrapper; + +/** + * Built-in preprocessor: Adds a white border around the image. + * This is useful for barcodes that lack sufficient quiet zone (whitespace) + * around them. When a barcode is generated or cropped without proper margins, + * the decoder may fail to detect it. Adding a border simulates the whitespace + * that would naturally exist when displaying the barcode on paper or screen. + * + * The image is shrunk slightly and centered, with white border pixels added + * around it. The output size remains the same as the input size. + * + * @param borderSize Number of pixels of white border to add on each side + * @returns A preprocessor function that adds the border + * + * @example + * // Add 10 pixels of white border around all images + * config.preprocessing = [Quagga.Preprocessors.addBorder(10)]; + */ +export function addBorder(borderSize: number): PreprocessorFunction { + return (imageWrapper: ImageWrapper): ImageWrapper => { + if (borderSize <= 0) { + return imageWrapper; + } + + const width = imageWrapper.size.x; + const height = imageWrapper.size.y; + + // Calculate the inner image area (shrunk to make room for border) + const innerWidth = width - (borderSize * 2); + const innerHeight = height - (borderSize * 2); + + // If border is too large for the image, just fill with white + if (innerWidth <= 0 || innerHeight <= 0) { + for (let i = 0; i < imageWrapper.data.length; i++) { + imageWrapper.data[i] = 255; + } + return imageWrapper; + } + + // Calculate scale factors for shrinking + const scaleX = innerWidth / width; + const scaleY = innerHeight / height; + + // Create a temporary copy of the original data + const originalData = new Uint8Array(imageWrapper.data.length); + for (let i = 0; i < imageWrapper.data.length; i++) { + originalData[i] = imageWrapper.data[i] as number; + } + + // Fill the entire image with white first + for (let i = 0; i < imageWrapper.data.length; i++) { + imageWrapper.data[i] = 255; + } + + // Copy the shrunk image into the center using bilinear interpolation + for (let y = 0; y < innerHeight; y++) { + for (let x = 0; x < innerWidth; x++) { + // Map destination coordinates to source coordinates + const srcX = x / scaleX; + const srcY = y / scaleY; + + // Bilinear interpolation + const x0 = Math.floor(srcX); + const y0 = Math.floor(srcY); + const x1 = Math.min(x0 + 1, width - 1); + const y1 = Math.min(y0 + 1, height - 1); + + const fx = srcX - x0; + const fy = srcY - y0; + + const v00 = originalData[y0 * width + x0]; + const v10 = originalData[y0 * width + x1]; + const v01 = originalData[y1 * width + x0]; + const v11 = originalData[y1 * width + x1]; + + const v0 = v00 * (1 - fx) + v10 * fx; + const v1 = v01 * (1 - fx) + v11 * fx; + const value = Math.round(v0 * (1 - fy) + v1 * fy); + + // Write to destination with border offset + const destIdx = (y + borderSize) * width + (x + borderSize); + imageWrapper.data[destIdx] = value; + } + } + + return imageWrapper; + }; +} + +/** + * Applies a chain of preprocessor functions to an image wrapper. + * @param imageWrapper The image wrapper to process + * @param preprocessors Array of preprocessor functions to apply in order + * @returns The processed image wrapper (same instance, potentially modified) + */ +export function applyPreprocessors( + imageWrapper: ImageWrapper, + preprocessors: PreprocessorFunction[], +): ImageWrapper { + let result = imageWrapper; + for (const preprocessor of preprocessors) { + result = preprocessor(result); + } + return result; +} + +/** + * Collection of built-in preprocessor factories. + * Users can use these or provide their own PreprocessorFunction implementations. + */ +export const Preprocessors = { + addBorder, +}; diff --git a/src/preprocessing/test/preprocessor.spec.ts b/src/preprocessing/test/preprocessor.spec.ts new file mode 100644 index 00000000..3b40b0bf --- /dev/null +++ b/src/preprocessing/test/preprocessor.spec.ts @@ -0,0 +1,140 @@ +import { expect } from 'chai'; +import ImageWrapper from '../../common/image_wrapper'; +import { addBorder, applyPreprocessors, Preprocessors } from '../preprocessor'; + +describe('Preprocessors', () => { + describe('addBorder', () => { + it('should return the same image when borderSize is 0', () => { + const size = { x: 10, y: 10, type: 'XYSize' as const }; + const data = new Uint8Array(100); + data.fill(128); + const wrapper = new ImageWrapper(size, data); + + const preprocessor = addBorder(0); + const result = preprocessor(wrapper); + + expect(result).to.equal(wrapper); + expect(result.data).to.deep.equal(data); + }); + + it('should return the same image when borderSize is negative', () => { + const size = { x: 10, y: 10, type: 'XYSize' as const }; + const data = new Uint8Array(100); + data.fill(128); + const wrapper = new ImageWrapper(size, data); + + const preprocessor = addBorder(-5); + const result = preprocessor(wrapper); + + expect(result).to.equal(wrapper); + }); + + it('should add white border and shrink image', () => { + const size = { x: 10, y: 10, type: 'XYSize' as const }; + const data = new Uint8Array(100); + data.fill(0); // Black image + const wrapper = new ImageWrapper(size, data); + + const borderSize = 2; + const preprocessor = addBorder(borderSize); + const result = preprocessor(wrapper); + + // Size should remain the same + expect(result.size.x).to.equal(10); + expect(result.size.y).to.equal(10); + + // Border pixels should be white (255) + // Top border + for (let y = 0; y < borderSize; y++) { + for (let x = 0; x < size.x; x++) { + expect(result.data[y * size.x + x]).to.equal(255, `Top border at (${x}, ${y})`); + } + } + // Bottom border + for (let y = size.y - borderSize; y < size.y; y++) { + for (let x = 0; x < size.x; x++) { + expect(result.data[y * size.x + x]).to.equal(255, `Bottom border at (${x}, ${y})`); + } + } + // Left border + for (let y = 0; y < size.y; y++) { + for (let x = 0; x < borderSize; x++) { + expect(result.data[y * size.x + x]).to.equal(255, `Left border at (${x}, ${y})`); + } + } + // Right border + for (let y = 0; y < size.y; y++) { + for (let x = size.x - borderSize; x < size.x; x++) { + expect(result.data[y * size.x + x]).to.equal(255, `Right border at (${x}, ${y})`); + } + } + + // Center should contain shrunk black image (approximately 0) + const centerX = Math.floor(size.x / 2); + const centerY = Math.floor(size.y / 2); + expect(result.data[centerY * size.x + centerX]).to.be.lessThan(50, 'Center should be dark'); + }); + + it('should fill with white when border is too large', () => { + const size = { x: 10, y: 10, type: 'XYSize' as const }; + const data = new Uint8Array(100); + data.fill(0); + const wrapper = new ImageWrapper(size, data); + + // Border of 5 on each side = 10 total, which equals the image size + const preprocessor = addBorder(5); + const result = preprocessor(wrapper); + + // All pixels should be white + for (let i = 0; i < result.data.length; i++) { + expect(result.data[i]).to.equal(255); + } + }); + }); + + describe('applyPreprocessors', () => { + it('should apply preprocessors in order', () => { + const size = { x: 10, y: 10, type: 'XYSize' as const }; + const data = new Uint8Array(100); + data.fill(100); + const wrapper = new ImageWrapper(size, data); + + const calls: string[] = []; + + const preprocessor1 = (img: ImageWrapper) => { + calls.push('first'); + return img; + }; + + const preprocessor2 = (img: ImageWrapper) => { + calls.push('second'); + return img; + }; + + applyPreprocessors(wrapper, [preprocessor1, preprocessor2]); + + expect(calls).to.deep.equal(['first', 'second']); + }); + + it('should return original wrapper when no preprocessors', () => { + const size = { x: 10, y: 10, type: 'XYSize' as const }; + const data = new Uint8Array(100); + const wrapper = new ImageWrapper(size, data); + + const result = applyPreprocessors(wrapper, []); + + expect(result).to.equal(wrapper); + }); + }); + + describe('Preprocessors object', () => { + it('should export addBorder function', () => { + expect(Preprocessors.addBorder).to.be.a('function'); + }); + + it('should create valid preprocessor from addBorder', () => { + const preprocessor = Preprocessors.addBorder(5); + expect(preprocessor).to.be.a('function'); + }); + }); +}); diff --git a/src/quagga.js b/src/quagga.js index 91702be3..f10f6107 100644 --- a/src/quagga.js +++ b/src/quagga.js @@ -8,6 +8,7 @@ import CameraAccess from './input/camera_access'; import ImageDebug from './common/image_debug'; import ResultCollector from './analytics/result_collector'; import Config from './config/config'; +import { Preprocessors } from './preprocessing'; import Quagga from './quagga/quagga'; @@ -174,6 +175,7 @@ const QuaggaJSStaticInterface = { ImageDebug, ImageWrapper, ResultCollector, + Preprocessors, }; export default QuaggaJSStaticInterface; @@ -185,4 +187,5 @@ export { ImageDebug, ImageWrapper, ResultCollector, + Preprocessors, }; diff --git a/src/quagga/quagga.ts b/src/quagga/quagga.ts index 431f7248..a145f36c 100644 --- a/src/quagga/quagga.ts +++ b/src/quagga/quagga.ts @@ -8,6 +8,7 @@ import CameraAccess from '../input/camera_access'; import FrameGrabber from '../input/frame_grabber.js'; import InputStream from '../input/input_stream/input_stream'; import BarcodeLocator from '../locator/barcode_locator'; +import { applyPreprocessors, PreprocessorFunction } from '../preprocessing'; import { QuaggaContext } from '../QuaggaContext'; import { BarcodeInfo } from '../reader/barcode_reader'; import _getViewPort from './getViewPort'; @@ -245,6 +246,10 @@ export default class Quagga { if (!workersUpdated) { this.context.framegrabber.attachData(this.context.inputImageWrapper?.data); if (this.context.framegrabber.grab()) { + // Apply preprocessing if configured + if (this.context.config?.preprocessing && this.context.config.preprocessing.length > 0 && this.context.inputImageWrapper) { + applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessing as PreprocessorFunction[]); + } if (!workersUpdated) { this.locateAndDecode(); } @@ -253,6 +258,10 @@ export default class Quagga { } else { this.context.framegrabber.attachData(this.context.inputImageWrapper?.data); this.context.framegrabber.grab(); + // Apply preprocessing if configured + if (this.context.config?.preprocessing && this.context.config.preprocessing.length > 0 && this.context.inputImageWrapper) { + applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessing as PreprocessorFunction[]); + } this.locateAndDecode(); } }; diff --git a/type-definitions/quagga.d.ts b/type-definitions/quagga.d.ts index 1a3cc5e2..ef01931a 100644 --- a/type-definitions/quagga.d.ts +++ b/type-definitions/quagga.d.ts @@ -65,7 +65,7 @@ export class ImageWrapper { initialize?: boolean ); - inImageWithBorder(imgRef: ImageRef, border: number): boolean; + inImageWithBorder(imgRef: XYSize, border?: number): boolean; subImageAsCopy(imageWrapper: ImageWrapper, from: XYSize): ImageWrapper; @@ -591,6 +591,23 @@ export interface QuaggaJSStatic { }; CameraAccess: QuaggaJSCameraAccess; + + /** + * Built-in preprocessor factories for image manipulation before barcode detection. + * Use these with the `preprocessing` config option. + * + * @example + * config.preprocessing = [Quagga.Preprocessors.addBorder(10)]; + */ + Preprocessors: { + /** + * Creates a preprocessor that adds a white border around the image. + * The image is shrunk slightly to maintain the same dimensions. + * Useful for barcodes that lack sufficient quiet zone (whitespace). + * @param borderSize Number of pixels of border to add on each side + */ + addBorder: (borderSize: number) => PreprocessorFunction; + }; } /** @@ -803,6 +820,27 @@ export interface QuaggaJSResultObject_CodeResult { export type InputStreamType = 'VideoStream' | 'ImageStream' | 'LiveStream'; +/** + * A preprocessor function that transforms image data. + * Preprocessors are applied to the image data after frame grabbing + * but before barcode localization and decoding. + * + * @param imageWrapper The image wrapper to process + * @returns The processed image wrapper (may be the same instance modified, or a new instance) + * + * @example + * // Custom preprocessor that inverts colors + * const invertColors: PreprocessorFunction = (imageWrapper) => { + * for (let i = 0; i < imageWrapper.data.length; i++) { + * imageWrapper.data[i] = 255 - imageWrapper.data[i]; + * } + * return imageWrapper; + * }; + * + * config.preprocessing = [invertColors]; + */ +export type PreprocessorFunction = (imageWrapper: ImageWrapper) => ImageWrapper; + export interface QuaggaJSConfigObject { /** * The image path to load from, or a data url @@ -887,16 +925,6 @@ export interface QuaggaJSConfigObject { size?: number; sequence?: boolean; - /** - * Adds a white border of the specified number of pixels around the image. - * This is useful for barcodes that lack sufficient quiet zone (whitespace) - * around them. When a barcode is generated or cropped without proper margins, - * the decoder may fail to detect it. Adding a border simulates the whitespace - * that would naturally exist when displaying the barcode on paper or screen. - * @default 0 (no border added) - */ - addBorder?: number; - debug?: { /** * @default false @@ -933,6 +961,28 @@ export interface QuaggaJSConfigObject { */ frequency?: number; + /** + * Array of preprocessor functions to apply to the image after frame grabbing + * but before barcode localization and decoding. Preprocessors are applied + * in the order they appear in the array. + * + * Use built-in preprocessors from Quagga.Preprocessors or provide custom functions. + * + * @example + * // Add 10 pixels of white border to help decode barcodes without quiet zones + * preprocessing: [Quagga.Preprocessors.addBorder(10)] + * + * @example + * // Chain multiple preprocessors + * preprocessing: [ + * Quagga.Preprocessors.addBorder(5), + * myCustomPreprocessor + * ] + * + * @default undefined (no preprocessing) + */ + preprocessing?: PreprocessorFunction[]; + /** * Canvas configuration options for controlling overlay and debug canvases. */ From 2a4c333802f702af78a5566e8aeb0427f584a241 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 07:00:36 +0000 Subject: [PATCH 04/14] Add integration test for addBorder preprocessor with no-whitespace barcode Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- .../whitespace/no-whitespace-barcode.gif | Bin 0 -> 7863 bytes test/integration/preprocessor.spec.ts | 64 ++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 test/fixtures/preprocessors/whitespace/no-whitespace-barcode.gif create mode 100644 test/integration/preprocessor.spec.ts diff --git a/test/fixtures/preprocessors/whitespace/no-whitespace-barcode.gif b/test/fixtures/preprocessors/whitespace/no-whitespace-barcode.gif new file mode 100644 index 0000000000000000000000000000000000000000..97b13f0a63d2b00eb93b06a20533ef38de6787a7 GIT binary patch literal 7863 zcmXY#byQW)7xs?`(xrY01r-sAE2ta;L{LEyLBQZ5Ee40~j*FM>lsa?@TvF+{AYF&% z(v5&L(tTcj@B7X_vu5^Mv-kcy�TUcQnR5&aZJ_7&I#{@(xr00ICQ01yB`0So|O zKmY>)7#P4H00srH0DuJnECgU-0E+-v6u<)j9t7|ZfQJD*0^m`A000CKAV2^C1_%g1 zKmif}kU)S00VEh8Api+IJsUtl0EGY)22cb*PZt0PfFJ;Y01N`iX$-^wAO-|65Qu?6 z3<6?M5DS1<5X3?t76!2hh($p>0OCOq4}o|X#3LXc1qlF106_u-5@3*kfCLmI0U!wk zNf1baK@tLzP!I(`6a-NSL}3s`K=ibZ)3!kffFJ;d0OYhR2m?SE5W+wZ28J*Qgh3%J z0AWE03qe>I!XgkBh427`2O&HJ;b91mKzI})01yF$2oOYoAp!ysP>2LTBoHD&5DA7z z2t-056o60=LLmr+AryhoQv|2PK^TBw0EPkNln9IgUmMq0LFtb9)j^Oj7MNR3KIaB0KxsdlOUJ`!z2VIp)d-- zCiUm+Ch+-iW3!_*B#iA%4K=B}o zhfq9>;t>>&q67dXfG7b%2{1}PPy&jQ0F(rxBnTzJC<#GH|4Vjiv{SWDk)QVcf0O@z z-=9ug`{ZW;;0U<&zpek>34jU(&QOu(nl3iIbzUN@P4j1K-wZ4SGtSpIw{qR?`XRas-gj-s>13^LypFr= z$U@;;$lsgtMqm1vADh`rP%5AqJLp?{m(3C28)yIh){6waqF$!jM^y5^wgz%i#yh}SI_=@2raQ8i zEZ_IS)`90nS+|>;J;d^i`{&p`FsZj&sFJ|MTFd9ZA}I&Ta0sr?r-@1F z#%dJn&c#0aVB4-)KHJ?|;)p_HniUfp6V|=TN9x(;{<(0iiOC~puW#^;b3t!xcQ3V zZQo$Y5#GeR<;|JpJqC}y6la9`3hE61Do?4+b5_qQ$Sl9~w?bBdZa#UF{`F7d!knQ| z)3R%3cK%yey7?Hx@kXQYjw(CNYO+XQvzh?~F-)D|UH`0(r;iYG7v`(`1zms7Urx(r zygatrBb@G;td&hm+CUT6v zXj(RF>|Ia*{eCsW`x~19R~R>aM+Z{#gDB| zIW`&C&VQblFC4;GycSu^q8)cy>MbxKZspjA?*9o+FlSUbkw2W zR<*OOupz2Bxygz<{%bY=@$o+QE?S6oZpKU&e#lv6U$>c9QrfF*{0&_1a5|fEaowU{ zf`+9DSLgQ8aYyON;w!}Bu$R>T!&97IAkUpoX^a}XhavM_#20RiS1-C($u=M^vzxJh zv@#^(Rr6mMv}w~sKIE%b4#{`>ddP7i*<8;p9puraK6N48u5f@t&g3qlMs^008l8Hh zUTK)PCOKb+#on>O^d^q%VL^U@7H2+<7zNW8sx} zU!#EgME)ORi(O-D5wol0yRl18_GH9s1%0}|b024vzT~6n`u^kWwF#qv2C0*OVPDUu zPP1e*AB!+N^+r?QuR;BF(qw}6!A=vrK5T;ACK~}r0r$KGWoz$BXrROExw~R5uY^7u zh8R6hYcEzQ8EFr7(hf^w({E{}J$d65>Hp^Wyij=#g_U*DCQFJ<=X)G!d~pvH?PdFtFB0`$ z@RZh(k-dv%OzoFwv%X^_DU`T%as1&ks>*B0??am$)rCau6^7d7;%aZrJ>U0w8*ieN z@^nQLGJ^AhVPUe$Pc*T8Q5tLO< zI2RuL+5INU^DJ5_?rn z_h`(BfA*j|x!C$w$FkmsW2!ghYW?wV1@)eT>1|aE1%>(BJRIR{ZwG{xi*oK~Szh)X z3>#nnSsY;W^tiQqX_ch{-p;XZEAA#xhXuW3HHF=HWvh*`JD&e4CgxW~$~`H5YNE1# zSiZAK_mBQ~^`&h3OxC@;JD$QKx(3)!FQRI1RsMT)n`7i>&p?x=)-CHS(UFUOQK@Gh zWcU#MKCfRpBM9v#uX+U+C&+wG8~-@-YQ1+Wn5Vd{vaq7@{Xl_K!=b$rSakUhv8nq z_9BP-f*&0edc!&s7;Jv}1Q>n!We}2G^FKR`uIX@bj&@?n(Jzgw!?eJo?Ri+KZ}nm#nz$=pZ6R5 zHs`38q%EJkpls(!ef-JuY~+wy-+TcHCy$`g2Tlr&a87%c>Aj7^nR^`s{g({Op|t9= zX^?1nxWam@<69ELK(1+-;hNGn+wqyudwwIsn{p`+Ee*)a;;RFULpL}6Vy;@(_Bt;Y z)g9Jz1v*sl9Ik#%h?)*7F71ySpU!KI2?d`%zqzBOIFR`GZ>%A{Cbm*GRDuEr0? zNRa{a9P`&pkG4LuI@HoLzD>DZwGyECXh!t)s#M*(4Q}U>EWrc=eNc7NX+m62JkPkB zOuVyJQS{%8-NB#q_-#I2iQ3DK`<_h_=v;@%R&O4IK5<54H@0MJ0>|j^*GFQ#?VdBD z<3uQNdeZNpm+sUw=j68K>~is&>Ac>C@;6o6Z`g;=#7fMC`pw~f)3F`X2g(i&wl+<> zc7IEp7fT$Lb**pn+J5K7g;N?U>f$Q6wT2?U=}@Y<^V-$S8(ZDL|9gSw&2Z({!+-L0 zQrdKAn{(}cDR0B;(&uVjs_SB?Y^e26x0D>Ou=P!q4S!w6SyIoLO2zf2t=aSDmn#FV z41+HeWMu!bcw7|mV778mwDG8E&=zCU)9`-dvh8tJ#+Aa`oifVV&D&M4*)@OHTAt5x z^ORbOp_?lDsneRBM zG+Tyy>quH*#i?AYZU)4A-*Zv%t@<0_Vdv@j!#$(fT{y~rsKmdRGC;r+w?`>DQsR=} z<#S>i(6{I2%;uiJ=jl%6Pi^OX6cu=!=ILt}=+EY|+2kM0=ase*Skz~~F^HSq!kyRz z#qJqamIh6*`3E+I&`<@=sQAm3dS;TlC`JV@dAoF|7%$8C^<=oEG`U542YmGk;cgB{ z-1AH33lwAb#oh>=l?mPd8fpUC*#WmUQk`s_iIDn zzYqF0HoN??{rZC1Lu=btZ{J^_>|0!#M_H+dId!U}su5IhQid%@`5M^;k7WiPiN?=`M_hUo;Hw{hOc}W#8on2mxS}7q z!5^opABR~?4AzC_y!}}S32Xu}0F5MvKjsJURFju8gJ>6n7=6Pd_CuI` zy?5;W_g-07+uPq8RNwB86tOeB(-KuO7_GS%eTmPE*qpct!?&VSGk3!e&PS9`M7L7K zvJ+B7e51@3ZN;L!0!5Q8sP8}Bi~VGf%I}c6wx52R#@e7f?v1bVeYL1|w)hyzp#8Zd zo7QMM8rWem*3sQgh$czE;D^1wuh~EJ!~PpSRkYK zQI6g)zlW@Cl~0C=0fCdC$^b{-)|YEF_*lwk?Ou=`NfTG=axzjzmObS~x5 zFXi@?rKXD{-K9QAC4&mbeDh_{g#@{xVls!=Yl{Tsou6eY`S8A<;nR~S)IqS=-+NG&y-nX}(g_}^0D zp{xfxrYK9ci1V@yLh~y`EUQZ`HQcWZUMz7f|5e=c>%3#09wuA3t+;!x_(4Sk=aM;v zT8N5YVrO}7SX&7Vrm$lq*KN`H6{hq;MTLCKXR~^a;R@pD2yy(7IC;5hTCD1idexj` z)!&$^#fqxsk*d|hs`bm&8)DU4>eV}r)q6432Nl&vBh@E|)d0tDvd6#4HGZFS`b`!4 zo2K$N-RN(Iqu)#%HRm7KTtqc$Se$CuVrwo{*3iDDJ;Ovpm&90`NJhqWw(U0uw?-{5 zM=d`caMQo`#&RvjpXoYB-E9tLL8m&pyL4*Tn5Yt|k(xRQx_V}=dSJbd`E8v5U7ehN z-Gj$$x;&?>YfyO!ey5n9cx{-EZo}o$8-g)_pN*fF9S6UZx{sYW$qt zX!^SGnNy>VKND7?N$GWyC0&#LttNB-Mw{%WZ~je(W=)REO*pz{d!)$)Y4-DP4xnoZ z%5L_!)$H@S*|U;pwA^^lzkw{N;nl4M!_mf9%Pj!Vdh4h@Ew(}3sUh2`ISgUCLRULn z(UNu4l9OFk0{~ZF*SVuz%;wkHI78YF*4x;a5vpgjb<1tPZy{_UZHM`7Y@f*KxY}u+ zwNrg+KWk2oI3XR`OkLSX=TZCFGws8-m_{|2ww_XOo;gDrWu{*5=)KiB@Vav(n^+3~ zsYab-F6|WN9l&H~_b4+NeHVp%*AYh-HLhzjww-FFi~hKiQLvj?yz4*XF50+mCe6;v z;!Jey-52S5SaO&a{Ob;omc#sxmC<$@7vv_fa}nuao9du=i?nm}Vgh=>xGo+}=8Dla zKBpeKq#m)js^!Us!b--M0RV3{V?|}>ld;yiM6##EKKbK56AqHnYf_;R>2R`7QoQS9 zCF!X+}gSCdI!z73)W^xE4rarGZK`_(j?H3YlA1PnaI4ZP3kH*4>=&}3@O zK8x|EyWx*O!~ubvK|>;=u_lu#=Kx-Q=+!Z);u;f~84catAy>_zgq8jv&Y{D}pKGC!28c6HI;I}4&M?sbQ4s2WBlPF2 z_^iCbY=zOR+?78c#%I;9%wjwKd<>ig5@!{K=5#w|zpT#cGt8+5&PfF_ZEF9K>1eql zG(-1n=7Gs16GLB`{QMor2HVh=ZD$pE~(^4C9* z$-%j*VYHFKdFa*SX(h9QUR|BbJ$=!ytq84=6xKFP)&>LDc5~MjBvy|Z z)(=>$?hrACFe1I*6TB#N5^SvDNjX93nHi)OUrMJ?NhRutHhAa(;E8 zPwU^L^FO5f-=C$L^xyM2RaC&!UO%A??%OK|z^1Sw#l4Az2SJ;BxtsY6s}%~1^{b1| zCmN+0mzv}2C2mhXy;>{zWb@fX<2%M}ncG`jm0QTv3`BqC;aY?Ct<{%LmVEE*yh0PU z59#PXA)8f3J34t=GU$$;;*QD3T{Fd9+iyGfG`1|Ncg;3VxA9s7drE)^3dfzZ; zJNb6kbVXZ@(HfDnE7ob9(R8#ph^e%5!jyPO(S3NQYLoZL(ff%F#fhD&t98<5hkvh3 z5UUw$6OKLyZDh)~zSJZa7H6t5IZ_Tfs;?dz&pSfi?hK2ckb+LQ8Bdz>PU1RFCe}_G z6pxo~qtlA$nkl-TcXTj;0?&A!72+s%7r1XshDo@P(_dF=HcPB=qh@`OicZ$ zd2}n|-xuj1HZg%zl{d_-VHf8!nSfuDasc4poLah}Bo}$dyr2{8DG3UCJ%%W(3=k%VOw24MF1^T-=#+VC2 zss%t7LFY1fHJnnIOZqbMGfVrs-?s0p+(4dY2s(50b= zgPX7F12DqMN0;WOyxi$|jW`d@rbqG}=TF)X!S$(H)yua28pRu9xiL$P?T^bh7rJn) z>CVS?Hq+VfdxT^+^;?M+_OA2%S8jbRlMML9$;jgTj8wg=KTCr(K;^bJUVM>Pq-eNE zJsC*YN}|A1mJ>3cQ5I@1RFJb~XVgSr@xPNo&0fe<7hu-I&+jH_Wqg&eHy^wf~$cMFqxC&T^h#zr&m`)J{G zzNDkzR!kAn(<=0Gv`3D&j(k(@a=Xzl8R1Tj3+)1JM;*z@7UbGZXWt*)Y6<&WSindA zpOH_esCBk{Yn0Zdt|k%FdophTdxOP>bL{j!G$mU}AaFmC%e{87VmVVPR8rXwNbVoq9n=$ndBtsQ!t!w_%^ZDr&*(k`RpJd!D4j zV4)uqlgs`#N#Sv`ex`olzJ9i)=cA>poO2dSNfmQyMnBs7FD=KUFsK{VxGO8puFtZX zm^Y|N?)p&@xMQZ*WjT*K7`tB(=diq=^Z*m%p4pEBf*(S!kf= zt<-Cbna$77Zv=Wf-Qeb4kP#8;3E^7uV^yzxe&N?!MfvWpsH_OJREzt0+aE{FR|_I8 zFbK_kS0GQCWlH8_B~pk}01ON^G!es<`{EG$@95lPV-S?;eY z=)>Nwp>V#JRe`9Yq3*SO_Q z;hPKOgC5rdVn?^m2HI5tKdjv2LS}l-StA@A2HH64w6xK6Bh7 z>&2MCyWU|E;YeOk*P5V8ia!4%IXu@)GfOkbcc7xfI?q{C;3GQRSJ_yU>-nFN_L;-} zhSu~dNQt9F{P;M>6?*j4IIikIgc*{ zJw6A-HS`phG#Hy$SE!Hg^p{jqn3#EpX-u;Wls3IEu?Vlwn0qi#*6Cqll_sXSs60?U z&|qRyRH3VQSYPrnM0nicza&_7VMt7GyM0L1 { + describe('addBorder preprocessor with no-whitespace barcode', () => { + const expectedCode = '439393900039990260001000000000000000000000123123220412'; + const barcodePath = 'test/fixtures/preprocessors/whitespace/no-whitespace-barcode.gif'; + + it('should fail to decode barcode without preprocessor', async function() { + this.timeout(20000); + + const result = await Quagga.decodeSingle({ + src: barcodePath, + inputStream: { + type: 'ImageStream', + size: 800, + mime: 'image/gif', + }, + decoder: { + readers: ['code_128_reader'], + }, + locate: true, + locator: { + halfSample: false, + patchSize: 'medium', + }, + }); + + // Without the preprocessor, the barcode should NOT be decoded + // because it has no quiet zone (whitespace) around it + expect(result.codeResult?.code).to.not.equal(expectedCode); + }); + + it('should successfully decode barcode with addBorder preprocessor', async function() { + this.timeout(20000); + + // Border size of 20 works well for this barcode + // (tested: 10 too small, 20-30 works, 50 too large) + const result = await Quagga.decodeSingle({ + src: barcodePath, + inputStream: { + type: 'ImageStream', + size: 800, + mime: 'image/gif', + }, + decoder: { + readers: ['code_128_reader'], + }, + locate: true, + locator: { + halfSample: false, + patchSize: 'medium', + }, + preprocessing: [Quagga.Preprocessors.addBorder(20)], + }); + + // With the addBorder preprocessor, the barcode should be decoded + expect(result).to.be.an('object'); + expect(result.codeResult).to.be.an('object'); + expect(result.codeResult.code).to.equal(expectedCode); + expect(result.codeResult.format).to.equal('code_128'); + }); + }); +}); From a547dbdeefc2da85225629924eeea0ffcabbbf43 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 07:03:12 +0000 Subject: [PATCH 05/14] Address code review feedback: improve array operations performance Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- src/preprocessing/preprocessor.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/preprocessing/preprocessor.ts b/src/preprocessing/preprocessor.ts index 29dca9f0..84f8603a 100644 --- a/src/preprocessing/preprocessor.ts +++ b/src/preprocessing/preprocessor.ts @@ -56,14 +56,18 @@ export function addBorder(borderSize: number): PreprocessorFunction { const scaleY = innerHeight / height; // Create a temporary copy of the original data - const originalData = new Uint8Array(imageWrapper.data.length); - for (let i = 0; i < imageWrapper.data.length; i++) { - originalData[i] = imageWrapper.data[i] as number; - } + // Note: We need to handle both TypedArray and Array types + const originalData = imageWrapper.data instanceof Uint8Array + ? new Uint8Array(imageWrapper.data) + : new Uint8Array(imageWrapper.data); // Fill the entire image with white first - for (let i = 0; i < imageWrapper.data.length; i++) { - imageWrapper.data[i] = 255; + if (imageWrapper.data instanceof Uint8Array) { + imageWrapper.data.fill(255); + } else { + for (let i = 0; i < imageWrapper.data.length; i++) { + imageWrapper.data[i] = 255; + } } // Copy the shrunk image into the center using bilinear interpolation From d88f28afe2fdda36e3e0dc107cbd23a8f37fcc80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 07:25:05 +0000 Subject: [PATCH 06/14] Address PR feedback: remove sharp dependency, rename type to QuaggaImagePreprocessor, clarify in-place modification Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- package-lock.json | 648 ++++++++++++++++-------------- package.json | 1 - src/preprocessing/index.ts | 8 +- src/preprocessing/preprocessor.ts | 26 +- src/quagga/quagga.ts | 6 +- type-definitions/quagga.d.ts | 22 +- 6 files changed, 383 insertions(+), 328 deletions(-) diff --git a/package-lock.json b/package-lock.json index 114e7150..66430835 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,7 +51,6 @@ "ndarray-linear-interpolate": "^1.0.0", "ndarray-pixels": "^5.0.1", "nyc": "^17.1.0", - "sharp": "^0.34.5", "sinon": "^21.0.0", "sinon-chai": "^3.7.0", "source-map-loader": "^1.1.1", @@ -2292,9 +2291,9 @@ "optional": true }, "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "dev": true, "license": "MIT", "optional": true, @@ -2507,20 +2506,10 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", "cpu": [ "arm64" ], @@ -2537,13 +2526,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", "cpu": [ "x64" ], @@ -2560,13 +2549,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", "cpu": [ "arm64" ], @@ -2581,9 +2570,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", "cpu": [ "x64" ], @@ -2598,9 +2587,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", "cpu": [ "arm" ], @@ -2615,9 +2604,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", "cpu": [ "arm64" ], @@ -2632,9 +2621,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", "cpu": [ "ppc64" ], @@ -2648,27 +2637,10 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", "cpu": [ "s390x" ], @@ -2683,9 +2655,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", "cpu": [ "x64" ], @@ -2700,9 +2672,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "cpu": [ "arm64" ], @@ -2717,9 +2689,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "cpu": [ "x64" ], @@ -2734,9 +2706,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "cpu": [ "arm" ], @@ -2753,13 +2725,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.2.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "cpu": [ "arm64" ], @@ -2776,13 +2748,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.2.0" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", "cpu": [ "ppc64" ], @@ -2799,36 +2771,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "cpu": [ "s390x" ], @@ -2845,13 +2794,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "cpu": [ "x64" ], @@ -2868,13 +2817,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "cpu": [ "arm64" ], @@ -2891,13 +2840,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "cpu": [ "x64" ], @@ -2914,13 +2863,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", "cpu": [ "wasm32" ], @@ -2928,7 +2877,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@emnapi/runtime": "^1.4.4" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -2938,9 +2887,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", "cpu": [ "arm64" ], @@ -2958,9 +2907,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", "cpu": [ "ia32" ], @@ -2978,9 +2927,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", "cpu": [ "x64" ], @@ -5834,6 +5783,20 @@ "node": ">=0.10.0" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -5849,6 +5812,37 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/colorette": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", @@ -6641,9 +6635,9 @@ } }, "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -9569,6 +9563,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -13286,16 +13287,16 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -13304,36 +13305,34 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" } }, "node_modules/sharp/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -13446,6 +13445,16 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/sinon": { "version": "21.0.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", @@ -17298,9 +17307,9 @@ } }, "@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "dev": true, "optional": true, "requires": { @@ -17454,210 +17463,187 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, - "@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "dev": true - }, "@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, "@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, "@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", "dev": true, "optional": true }, "@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "dev": true, - "optional": true - }, - "@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", "dev": true, "optional": true }, "@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", "dev": true, "optional": true }, "@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "dev": true, "optional": true }, "@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "dev": true, "optional": true }, "@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.2.0" } }, "@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.2.0" } }, "@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "dev": true, - "optional": true, - "requires": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, "@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, "@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.2.0" } }, "@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, "@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "dev": true, "optional": true, "requires": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, "@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", "dev": true, "optional": true, "requires": { - "@emnapi/runtime": "^1.7.0" + "@emnapi/runtime": "^1.4.4" } }, "@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", "dev": true, "optional": true }, "@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", "dev": true, "optional": true }, "@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", "dev": true, "optional": true }, @@ -19732,6 +19718,33 @@ "object-visit": "^1.0.0" } }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -19747,6 +19760,16 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "colorette": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", @@ -20361,9 +20384,9 @@ "dev": true }, "detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "dev": true }, "diff": { @@ -22559,6 +22582,12 @@ "get-intrinsic": "^1.2.6" } }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, "is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -25328,44 +25357,42 @@ } }, "sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "dev": true, - "requires": { - "@img/colour": "^1.0.0", - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "dev": true, + "requires": { + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3", + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" }, "dependencies": { "semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true } } @@ -25439,6 +25466,15 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + } + }, "sinon": { "version": "21.0.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", diff --git a/package.json b/package.json index ada21120..adbf4be1 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,6 @@ "ndarray-linear-interpolate": "^1.0.0", "ndarray-pixels": "^5.0.1", "nyc": "^17.1.0", - "sharp": "^0.34.5", "sinon": "^21.0.0", "sinon-chai": "^3.7.0", "source-map-loader": "^1.1.1", diff --git a/src/preprocessing/index.ts b/src/preprocessing/index.ts index a13dc294..299374e7 100644 --- a/src/preprocessing/index.ts +++ b/src/preprocessing/index.ts @@ -1,4 +1,10 @@ -export type { PreprocessorFunction } from './preprocessor'; +/** + * Preprocessing module - provides image preprocessing capabilities + * that run between frame grabbing and barcode localization/decoding. + * + * This is a barrel file that re-exports the public API of the preprocessing module. + */ +export type { QuaggaImagePreprocessor } from './preprocessor'; export { addBorder, applyPreprocessors, diff --git a/src/preprocessing/preprocessor.ts b/src/preprocessing/preprocessor.ts index 84f8603a..491456ba 100644 --- a/src/preprocessing/preprocessor.ts +++ b/src/preprocessing/preprocessor.ts @@ -1,17 +1,22 @@ import ImageWrapper from '../common/image_wrapper'; /** - * A preprocessor function that transforms image data. + * A preprocessor function that transforms image data in place. * Preprocessors are applied to the image data after frame grabbing * but before barcode localization and decoding. * - * IMPORTANT: Preprocessors should maintain the same image dimensions. - * The returned ImageWrapper should have the same size as the input. + * IMPORTANT: Preprocessors should: + * - Modify the imageWrapper.data array IN PLACE for best performance + * - Maintain the same image dimensions (do not resize) + * - Return the same imageWrapper instance that was passed in * - * @param imageWrapper The image wrapper to process - * @returns The processed image wrapper (same size as input) + * Modifying in place avoids unnecessary memory allocations and copies, + * which is critical for real-time video processing performance. + * + * @param imageWrapper The image wrapper to process (modify in place) + * @returns The same imageWrapper instance (for chaining) */ -export type PreprocessorFunction = (imageWrapper: ImageWrapper) => ImageWrapper; +export type QuaggaImagePreprocessor = (imageWrapper: ImageWrapper) => ImageWrapper; /** * Built-in preprocessor: Adds a white border around the image. @@ -30,7 +35,7 @@ export type PreprocessorFunction = (imageWrapper: ImageWrapper) => ImageWrapper; * // Add 10 pixels of white border around all images * config.preprocessing = [Quagga.Preprocessors.addBorder(10)]; */ -export function addBorder(borderSize: number): PreprocessorFunction { +export function addBorder(borderSize: number): QuaggaImagePreprocessor { return (imageWrapper: ImageWrapper): ImageWrapper => { if (borderSize <= 0) { return imageWrapper; @@ -107,13 +112,14 @@ export function addBorder(borderSize: number): PreprocessorFunction { /** * Applies a chain of preprocessor functions to an image wrapper. + * Each preprocessor modifies the image data in place. * @param imageWrapper The image wrapper to process * @param preprocessors Array of preprocessor functions to apply in order - * @returns The processed image wrapper (same instance, potentially modified) + * @returns The same imageWrapper instance (modified in place) */ export function applyPreprocessors( imageWrapper: ImageWrapper, - preprocessors: PreprocessorFunction[], + preprocessors: QuaggaImagePreprocessor[], ): ImageWrapper { let result = imageWrapper; for (const preprocessor of preprocessors) { @@ -124,7 +130,7 @@ export function applyPreprocessors( /** * Collection of built-in preprocessor factories. - * Users can use these or provide their own PreprocessorFunction implementations. + * Users can use these or provide their own QuaggaImagePreprocessor implementations. */ export const Preprocessors = { addBorder, diff --git a/src/quagga/quagga.ts b/src/quagga/quagga.ts index a145f36c..28fb5a34 100644 --- a/src/quagga/quagga.ts +++ b/src/quagga/quagga.ts @@ -8,7 +8,7 @@ import CameraAccess from '../input/camera_access'; import FrameGrabber from '../input/frame_grabber.js'; import InputStream from '../input/input_stream/input_stream'; import BarcodeLocator from '../locator/barcode_locator'; -import { applyPreprocessors, PreprocessorFunction } from '../preprocessing'; +import { applyPreprocessors, QuaggaImagePreprocessor } from '../preprocessing'; import { QuaggaContext } from '../QuaggaContext'; import { BarcodeInfo } from '../reader/barcode_reader'; import _getViewPort from './getViewPort'; @@ -248,7 +248,7 @@ export default class Quagga { if (this.context.framegrabber.grab()) { // Apply preprocessing if configured if (this.context.config?.preprocessing && this.context.config.preprocessing.length > 0 && this.context.inputImageWrapper) { - applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessing as PreprocessorFunction[]); + applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessing as QuaggaImagePreprocessor[]); } if (!workersUpdated) { this.locateAndDecode(); @@ -260,7 +260,7 @@ export default class Quagga { this.context.framegrabber.grab(); // Apply preprocessing if configured if (this.context.config?.preprocessing && this.context.config.preprocessing.length > 0 && this.context.inputImageWrapper) { - applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessing as PreprocessorFunction[]); + applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessing as QuaggaImagePreprocessor[]); } this.locateAndDecode(); } diff --git a/type-definitions/quagga.d.ts b/type-definitions/quagga.d.ts index ef01931a..c6c2b77f 100644 --- a/type-definitions/quagga.d.ts +++ b/type-definitions/quagga.d.ts @@ -606,7 +606,7 @@ export interface QuaggaJSStatic { * Useful for barcodes that lack sufficient quiet zone (whitespace). * @param borderSize Number of pixels of border to add on each side */ - addBorder: (borderSize: number) => PreprocessorFunction; + addBorder: (borderSize: number) => QuaggaImagePreprocessor; }; } @@ -821,16 +821,24 @@ export interface QuaggaJSResultObject_CodeResult { export type InputStreamType = 'VideoStream' | 'ImageStream' | 'LiveStream'; /** - * A preprocessor function that transforms image data. + * A preprocessor function that transforms image data in place. * Preprocessors are applied to the image data after frame grabbing * but before barcode localization and decoding. * - * @param imageWrapper The image wrapper to process - * @returns The processed image wrapper (may be the same instance modified, or a new instance) + * IMPORTANT: Preprocessors should: + * - Modify the imageWrapper.data array IN PLACE for best performance + * - Maintain the same image dimensions (do not resize) + * - Return the same imageWrapper instance that was passed in + * + * Modifying in place avoids unnecessary memory allocations and copies, + * which is critical for real-time video processing performance. + * + * @param imageWrapper The image wrapper to process (modify in place) + * @returns The same imageWrapper instance (for chaining) * * @example * // Custom preprocessor that inverts colors - * const invertColors: PreprocessorFunction = (imageWrapper) => { + * const invertColors: QuaggaImagePreprocessor = (imageWrapper) => { * for (let i = 0; i < imageWrapper.data.length; i++) { * imageWrapper.data[i] = 255 - imageWrapper.data[i]; * } @@ -839,7 +847,7 @@ export type InputStreamType = 'VideoStream' | 'ImageStream' | 'LiveStream'; * * config.preprocessing = [invertColors]; */ -export type PreprocessorFunction = (imageWrapper: ImageWrapper) => ImageWrapper; +export type QuaggaImagePreprocessor = (imageWrapper: ImageWrapper) => ImageWrapper; export interface QuaggaJSConfigObject { /** @@ -981,7 +989,7 @@ export interface QuaggaJSConfigObject { * * @default undefined (no preprocessing) */ - preprocessing?: PreprocessorFunction[]; + preprocessing?: QuaggaImagePreprocessor[]; /** * Canvas configuration options for controlling overlay and debug canvases. From 439c2d5d8b4dab16ba9c3accccca5004f6b12f46 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 07:32:52 +0000 Subject: [PATCH 07/14] Add preprocessing tests to Cypress browser test suites Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- cypress/e2e/integration.cy.ts | 1 + cypress/e2e/universal.cy.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/cypress/e2e/integration.cy.ts b/cypress/e2e/integration.cy.ts index 475ae14a..b9a5f788 100644 --- a/cypress/e2e/integration.cy.ts +++ b/cypress/e2e/integration.cy.ts @@ -17,3 +17,4 @@ import '../../test/integration/decoders/code_93.spec.ts'; import '../../test/integration/external-reader.spec.ts'; import '../../test/integration/integration.spec.ts'; import '../../test/integration/reader-order.spec.ts'; +import '../../test/integration/preprocessor.spec.ts'; diff --git a/cypress/e2e/universal.cy.ts b/cypress/e2e/universal.cy.ts index f0cba535..f8b8c358 100644 --- a/cypress/e2e/universal.cy.ts +++ b/cypress/e2e/universal.cy.ts @@ -12,3 +12,5 @@ import '../../src/quagga/test/quagga.spec.ts'; import '../../src/common/test/image_wrapper.spec.ts'; // Tests for QuaggaJSStaticInterface (init method) import '../../src/test/quagga.spec.ts'; +// Tests for preprocessing module +import '../../src/preprocessing/test/preprocessor.spec.ts'; From d9e3e289d8cdbccfd18661f187026bf043611632 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Sun, 30 Nov 2025 02:46:48 -0500 Subject: [PATCH 08/14] Update src/preprocessing/preprocessor.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/preprocessing/preprocessor.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/preprocessing/preprocessor.ts b/src/preprocessing/preprocessor.ts index 491456ba..2d37b0b2 100644 --- a/src/preprocessing/preprocessor.ts +++ b/src/preprocessing/preprocessor.ts @@ -62,9 +62,7 @@ export function addBorder(borderSize: number): QuaggaImagePreprocessor { // Create a temporary copy of the original data // Note: We need to handle both TypedArray and Array types - const originalData = imageWrapper.data instanceof Uint8Array - ? new Uint8Array(imageWrapper.data) - : new Uint8Array(imageWrapper.data); + const originalData = new Uint8Array(imageWrapper.data); // Fill the entire image with white first if (imageWrapper.data instanceof Uint8Array) { From e5161d441c2adebcd32fd23085db35ecc3d695e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 07:50:56 +0000 Subject: [PATCH 09/14] Add comprehensive preprocessing documentation to README Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- README.md | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/README.md b/README.md index e4acb1d8..86633e97 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Quick links from this README: - [API](#api) - [CameraAccess API](#cameraaccess-api) - [Configuration](#configobject) +- [Preprocessing](#preprocessing) - [Tips & Tricks](#tipsandtricks) ## Using React / Redux? @@ -519,6 +520,7 @@ high-level properties: frequency: 10, decoder:{...}, locator: {...}, + preprocessing: [...], // optional: image preprocessing pipeline debug: false, } ``` @@ -755,6 +757,99 @@ from the camera lens (lack of auto-focus, or small barcodes) then it's advised to set the size to `small` or even `x-small`. For the latter it's also recommended to crank up the resolution in order to find a barcode. +### preprocessing + +The `preprocessing` property allows you to apply image transformations before +barcode localization and decoding. This is useful for handling edge cases like +barcodes without sufficient quiet zones (whitespace around the barcode). + +```javascript +{ + preprocessing: [ + Quagga.Preprocessors.addBorder(20) // Add 20px white border + ] +} +``` + +#### Built-in Preprocessors + +**`Quagga.Preprocessors.addBorder(size)`** + +Adds a white border around the image by shrinking the image content slightly +and filling the border with white pixels. This helps decode barcodes that lack +proper quiet zones (whitespace around the barcode edges). + +```javascript +// Add 10 pixels of white border on all sides +preprocessing: [Quagga.Preprocessors.addBorder(10)] +``` + +- `size`: Number of pixels of white border to add on each side +- The image dimensions remain unchanged; the content is shrunk to make room +- Recommended values: 10-30 pixels depending on image size + +#### Chaining Multiple Preprocessors + +Preprocessors are applied in the order they appear in the array: + +```javascript +preprocessing: [ + Quagga.Preprocessors.addBorder(15), + myCustomPreprocessor +] +``` + +#### Creating Custom Preprocessors + +You can create custom preprocessor functions that follow the `QuaggaImagePreprocessor` type signature: + +```typescript +type QuaggaImagePreprocessor = (imageWrapper: ImageWrapper) => ImageWrapper; +``` + +**Important guidelines for custom preprocessors:** + +1. **Modify in place**: For best performance, modify `imageWrapper.data` directly +2. **Maintain dimensions**: Keep the same image width and height +3. **Return the same instance**: Return the same imageWrapper that was passed in + +```javascript +// Example: Custom preprocessor that inverts colors +const invertColors = (imageWrapper) => { + for (let i = 0; i < imageWrapper.data.length; i++) { + imageWrapper.data[i] = 255 - imageWrapper.data[i]; + } + return imageWrapper; +}; + +// Example: Custom preprocessor that increases contrast +const increaseContrast = (imageWrapper) => { + const factor = 1.5; // contrast factor + const midpoint = 128; + for (let i = 0; i < imageWrapper.data.length; i++) { + const value = imageWrapper.data[i]; + imageWrapper.data[i] = Math.max(0, Math.min(255, + midpoint + (value - midpoint) * factor + )); + } + return imageWrapper; +}; + +// Use custom preprocessors +Quagga.init({ + preprocessing: [invertColors, increaseContrast], + // ... other config +}); +``` + +#### When to Use Preprocessing + +- **Barcodes without whitespace**: Use `addBorder()` when barcodes are cropped + tightly without quiet zones +- **Poor contrast**: Create a custom contrast enhancement preprocessor +- **Noise reduction**: Implement smoothing or median filters +- **Color issues**: Apply color correction or channel manipulation + ## Examples The following example takes an image `src` as input and prints the result on the From d286161323b6890a829ad82549bcbc0a36b215b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 07:58:03 +0000 Subject: [PATCH 10/14] Rename 'preprocessing' config option to 'preprocessors' (nouns not verbs) Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- README.md | 18 +++++++++--------- src/preprocessing/preprocessor.ts | 2 +- src/quagga/quagga.ts | 12 ++++++------ test/integration/preprocessor.spec.ts | 2 +- type-definitions/quagga.d.ts | 14 +++++++------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 86633e97..b0207647 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Quick links from this README: - [API](#api) - [CameraAccess API](#cameraaccess-api) - [Configuration](#configobject) -- [Preprocessing](#preprocessing) +- [Preprocessors](#preprocessors) - [Tips & Tricks](#tipsandtricks) ## Using React / Redux? @@ -520,7 +520,7 @@ high-level properties: frequency: 10, decoder:{...}, locator: {...}, - preprocessing: [...], // optional: image preprocessing pipeline + preprocessors: [...], // optional: image preprocessors pipeline debug: false, } ``` @@ -757,15 +757,15 @@ from the camera lens (lack of auto-focus, or small barcodes) then it's advised to set the size to `small` or even `x-small`. For the latter it's also recommended to crank up the resolution in order to find a barcode. -### preprocessing +### preprocessors -The `preprocessing` property allows you to apply image transformations before +The `preprocessors` property allows you to apply image transformations before barcode localization and decoding. This is useful for handling edge cases like barcodes without sufficient quiet zones (whitespace around the barcode). ```javascript { - preprocessing: [ + preprocessors: [ Quagga.Preprocessors.addBorder(20) // Add 20px white border ] } @@ -781,7 +781,7 @@ proper quiet zones (whitespace around the barcode edges). ```javascript // Add 10 pixels of white border on all sides -preprocessing: [Quagga.Preprocessors.addBorder(10)] +preprocessors: [Quagga.Preprocessors.addBorder(10)] ``` - `size`: Number of pixels of white border to add on each side @@ -793,7 +793,7 @@ preprocessing: [Quagga.Preprocessors.addBorder(10)] Preprocessors are applied in the order they appear in the array: ```javascript -preprocessing: [ +preprocessors: [ Quagga.Preprocessors.addBorder(15), myCustomPreprocessor ] @@ -837,12 +837,12 @@ const increaseContrast = (imageWrapper) => { // Use custom preprocessors Quagga.init({ - preprocessing: [invertColors, increaseContrast], + preprocessors: [invertColors, increaseContrast], // ... other config }); ``` -#### When to Use Preprocessing +#### When to Use Preprocessors - **Barcodes without whitespace**: Use `addBorder()` when barcodes are cropped tightly without quiet zones diff --git a/src/preprocessing/preprocessor.ts b/src/preprocessing/preprocessor.ts index 2d37b0b2..23a44145 100644 --- a/src/preprocessing/preprocessor.ts +++ b/src/preprocessing/preprocessor.ts @@ -33,7 +33,7 @@ export type QuaggaImagePreprocessor = (imageWrapper: ImageWrapper) => ImageWrapp * * @example * // Add 10 pixels of white border around all images - * config.preprocessing = [Quagga.Preprocessors.addBorder(10)]; + * config.preprocessors = [Quagga.Preprocessors.addBorder(10)]; */ export function addBorder(borderSize: number): QuaggaImagePreprocessor { return (imageWrapper: ImageWrapper): ImageWrapper => { diff --git a/src/quagga/quagga.ts b/src/quagga/quagga.ts index 28fb5a34..c6543590 100644 --- a/src/quagga/quagga.ts +++ b/src/quagga/quagga.ts @@ -246,9 +246,9 @@ export default class Quagga { if (!workersUpdated) { this.context.framegrabber.attachData(this.context.inputImageWrapper?.data); if (this.context.framegrabber.grab()) { - // Apply preprocessing if configured - if (this.context.config?.preprocessing && this.context.config.preprocessing.length > 0 && this.context.inputImageWrapper) { - applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessing as QuaggaImagePreprocessor[]); + // Apply preprocessors if configured + if (this.context.config?.preprocessors && this.context.config.preprocessors.length > 0 && this.context.inputImageWrapper) { + applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessors as QuaggaImagePreprocessor[]); } if (!workersUpdated) { this.locateAndDecode(); @@ -258,9 +258,9 @@ export default class Quagga { } else { this.context.framegrabber.attachData(this.context.inputImageWrapper?.data); this.context.framegrabber.grab(); - // Apply preprocessing if configured - if (this.context.config?.preprocessing && this.context.config.preprocessing.length > 0 && this.context.inputImageWrapper) { - applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessing as QuaggaImagePreprocessor[]); + // Apply preprocessors if configured + if (this.context.config?.preprocessors && this.context.config.preprocessors.length > 0 && this.context.inputImageWrapper) { + applyPreprocessors(this.context.inputImageWrapper, this.context.config.preprocessors as QuaggaImagePreprocessor[]); } this.locateAndDecode(); } diff --git a/test/integration/preprocessor.spec.ts b/test/integration/preprocessor.spec.ts index 085bf971..49dd78e4 100644 --- a/test/integration/preprocessor.spec.ts +++ b/test/integration/preprocessor.spec.ts @@ -51,7 +51,7 @@ describe('Preprocessor Integration Tests', () => { halfSample: false, patchSize: 'medium', }, - preprocessing: [Quagga.Preprocessors.addBorder(20)], + preprocessors: [Quagga.Preprocessors.addBorder(20)], }); // With the addBorder preprocessor, the barcode should be decoded diff --git a/type-definitions/quagga.d.ts b/type-definitions/quagga.d.ts index c6c2b77f..0edfd674 100644 --- a/type-definitions/quagga.d.ts +++ b/type-definitions/quagga.d.ts @@ -594,10 +594,10 @@ export interface QuaggaJSStatic { /** * Built-in preprocessor factories for image manipulation before barcode detection. - * Use these with the `preprocessing` config option. + * Use these with the `preprocessors` config option. * * @example - * config.preprocessing = [Quagga.Preprocessors.addBorder(10)]; + * config.preprocessors = [Quagga.Preprocessors.addBorder(10)]; */ Preprocessors: { /** @@ -845,7 +845,7 @@ export type InputStreamType = 'VideoStream' | 'ImageStream' | 'LiveStream'; * return imageWrapper; * }; * - * config.preprocessing = [invertColors]; + * config.preprocessors = [invertColors]; */ export type QuaggaImagePreprocessor = (imageWrapper: ImageWrapper) => ImageWrapper; @@ -978,18 +978,18 @@ export interface QuaggaJSConfigObject { * * @example * // Add 10 pixels of white border to help decode barcodes without quiet zones - * preprocessing: [Quagga.Preprocessors.addBorder(10)] + * preprocessors: [Quagga.Preprocessors.addBorder(10)] * * @example * // Chain multiple preprocessors - * preprocessing: [ + * preprocessors: [ * Quagga.Preprocessors.addBorder(5), * myCustomPreprocessor * ] * - * @default undefined (no preprocessing) + * @default undefined (no preprocessors) */ - preprocessing?: QuaggaImagePreprocessor[]; + preprocessors?: QuaggaImagePreprocessor[]; /** * Canvas configuration options for controlling overlay and debug canvases. From 87e58553ae37f9a911394f49f3abd84f5205ae58 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 08:04:10 +0000 Subject: [PATCH 11/14] Fix Cypress test path for preprocessor fixture - add leading slash for browser environment Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- test/integration/preprocessor.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/preprocessor.spec.ts b/test/integration/preprocessor.spec.ts index 49dd78e4..15b287dd 100644 --- a/test/integration/preprocessor.spec.ts +++ b/test/integration/preprocessor.spec.ts @@ -4,7 +4,8 @@ import { expect } from 'chai'; describe('Preprocessor Integration Tests', () => { describe('addBorder preprocessor with no-whitespace barcode', () => { const expectedCode = '439393900039990260001000000000000000000000123123220412'; - const barcodePath = 'test/fixtures/preprocessors/whitespace/no-whitespace-barcode.gif'; + // Use leading slash for browser (Cypress) environment + const barcodePath = `${typeof window !== 'undefined' ? '/' : ''}test/fixtures/preprocessors/whitespace/no-whitespace-barcode.gif`; it('should fail to decode barcode without preprocessor', async function() { this.timeout(20000); From c346a17c97855f349ab6a621dd01133aa2501b4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 08:14:43 +0000 Subject: [PATCH 12/14] Move comprehensive preprocessors documentation to /docs following Divio system Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- README.md | 90 +--------- docs/explanation/algorithm-overview.md | 6 + docs/how-to-guides/index.md | 4 + docs/how-to-guides/use-preprocessors.md | 226 ++++++++++++++++++++++++ docs/reference/configuration.md | 42 +++++ 5 files changed, 282 insertions(+), 86 deletions(-) create mode 100644 docs/how-to-guides/use-preprocessors.md diff --git a/README.md b/README.md index b0207647..b640e5c6 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ Quick links from this README: - [API](#api) - [CameraAccess API](#cameraaccess-api) - [Configuration](#configobject) -- [Preprocessors](#preprocessors) - [Tips & Tricks](#tipsandtricks) ## Using React / Redux? @@ -757,98 +756,17 @@ from the camera lens (lack of auto-focus, or small barcodes) then it's advised to set the size to `small` or even `x-small`. For the latter it's also recommended to crank up the resolution in order to find a barcode. -### preprocessors +### preprocessors -The `preprocessors` property allows you to apply image transformations before -barcode localization and decoding. This is useful for handling edge cases like -barcodes without sufficient quiet zones (whitespace around the barcode). +The `preprocessors` config option allows you to apply image transformations before barcode detection. Useful for handling barcodes without sufficient quiet zones (whitespace). ```javascript { - preprocessors: [ - Quagga.Preprocessors.addBorder(20) // Add 20px white border - ] + preprocessors: [Quagga.Preprocessors.addBorder(20)] // Add 20px white border } ``` -#### Built-in Preprocessors - -**`Quagga.Preprocessors.addBorder(size)`** - -Adds a white border around the image by shrinking the image content slightly -and filling the border with white pixels. This helps decode barcodes that lack -proper quiet zones (whitespace around the barcode edges). - -```javascript -// Add 10 pixels of white border on all sides -preprocessors: [Quagga.Preprocessors.addBorder(10)] -``` - -- `size`: Number of pixels of white border to add on each side -- The image dimensions remain unchanged; the content is shrunk to make room -- Recommended values: 10-30 pixels depending on image size - -#### Chaining Multiple Preprocessors - -Preprocessors are applied in the order they appear in the array: - -```javascript -preprocessors: [ - Quagga.Preprocessors.addBorder(15), - myCustomPreprocessor -] -``` - -#### Creating Custom Preprocessors - -You can create custom preprocessor functions that follow the `QuaggaImagePreprocessor` type signature: - -```typescript -type QuaggaImagePreprocessor = (imageWrapper: ImageWrapper) => ImageWrapper; -``` - -**Important guidelines for custom preprocessors:** - -1. **Modify in place**: For best performance, modify `imageWrapper.data` directly -2. **Maintain dimensions**: Keep the same image width and height -3. **Return the same instance**: Return the same imageWrapper that was passed in - -```javascript -// Example: Custom preprocessor that inverts colors -const invertColors = (imageWrapper) => { - for (let i = 0; i < imageWrapper.data.length; i++) { - imageWrapper.data[i] = 255 - imageWrapper.data[i]; - } - return imageWrapper; -}; - -// Example: Custom preprocessor that increases contrast -const increaseContrast = (imageWrapper) => { - const factor = 1.5; // contrast factor - const midpoint = 128; - for (let i = 0; i < imageWrapper.data.length; i++) { - const value = imageWrapper.data[i]; - imageWrapper.data[i] = Math.max(0, Math.min(255, - midpoint + (value - midpoint) * factor - )); - } - return imageWrapper; -}; - -// Use custom preprocessors -Quagga.init({ - preprocessors: [invertColors, increaseContrast], - // ... other config -}); -``` - -#### When to Use Preprocessors - -- **Barcodes without whitespace**: Use `addBorder()` when barcodes are cropped - tightly without quiet zones -- **Poor contrast**: Create a custom contrast enhancement preprocessor -- **Noise reduction**: Implement smoothing or median filters -- **Color issues**: Apply color correction or channel manipulation +**[📖 Full Preprocessors Documentation](https://ericblade.github.io/quagga2/how-to-guides/use-preprocessors.html)** - Built-in preprocessors, creating custom preprocessors, and usage examples. ## Examples diff --git a/docs/explanation/algorithm-overview.md b/docs/explanation/algorithm-overview.md index 8057319e..42c2b71f 100644 --- a/docs/explanation/algorithm-overview.md +++ b/docs/explanation/algorithm-overview.md @@ -12,10 +12,16 @@ Input Image → Preprocessing → Localization → Decoding → Result ### 1. Preprocessing {#preprocessing} +**Built-in preprocessing:** - **Scaling**: Image is resized based on `inputStream.size` - **Grayscale conversion**: Color image converted to grayscale - **Area cropping**: If `inputStream.area` is set, crop to that region +**Custom preprocessors** (via `preprocessors` config): +- Applied after frame grabbing but before localization +- Useful for adding borders, enhancing contrast, or other transformations +- See [Use Preprocessors Guide](../how-to-guides/use-preprocessors.md) for details + ### 2. Localization {#localization} When `locate: true` (default): diff --git a/docs/how-to-guides/index.md b/docs/how-to-guides/index.md index ec214d28..243d70d3 100644 --- a/docs/how-to-guides/index.md +++ b/docs/how-to-guides/index.md @@ -22,6 +22,10 @@ Improve scanning speed and reduce CPU usage through configuration tuning. Techniques for decoding barcodes with poor lighting, damage, rotation, or small size. +### [Use Preprocessors](use-preprocessors.md) 🆕 + +Apply image transformations before barcode detection. Useful for barcodes without quiet zones. + ## Advanced Features ### [Create External Readers](external-readers.md) diff --git a/docs/how-to-guides/use-preprocessors.md b/docs/how-to-guides/use-preprocessors.md new file mode 100644 index 00000000..19cf4c0f --- /dev/null +++ b/docs/how-to-guides/use-preprocessors.md @@ -0,0 +1,226 @@ +# Use Preprocessors {#use-preprocessors} + +Preprocessors allow you to transform images before barcode detection and decoding. This guide covers using built-in preprocessors and creating your own. + +## When to Use Preprocessors {#when-to-use} + +Use preprocessors when you need to: + +- **Handle barcodes without whitespace**: Barcodes require "quiet zones" (whitespace) around them. Use `addBorder()` when barcodes are cropped tightly. +- **Improve contrast**: Enhance images with poor lighting or low contrast +- **Reduce noise**: Apply smoothing or filtering to noisy images +- **Apply color corrections**: Adjust brightness, gamma, or channels + +## Using Built-in Preprocessors {#built-in-preprocessors} + +### addBorder {#addborder} + +Adds a white border around the image by shrinking the content slightly. This simulates the quiet zone that barcodes need for proper detection. + +```javascript +Quagga.decodeSingle({ + src: 'barcode-without-whitespace.jpg', + preprocessors: [Quagga.Preprocessors.addBorder(20)], + decoder: { + readers: ['code_128_reader'] + } +}); +``` + +**Parameters:** +- `size`: Number of pixels of white border to add on each side (recommended: 10-30) + +**How it works:** +- Image dimensions remain unchanged +- Content is shrunk using bilinear interpolation to make room for the border +- Border is filled with white (255) pixels + +**Example - Fixing a barcode with no quiet zone:** + +```javascript +// Barcode image has no whitespace around it +Quagga.decodeSingle({ + src: 'tight-barcode.gif', + inputStream: { + type: 'ImageStream', + size: 800 + }, + preprocessors: [Quagga.Preprocessors.addBorder(20)], + decoder: { + readers: ['code_128_reader'] + }, + locate: true, + locator: { + halfSample: false, + patchSize: 'medium' + } +}, function(result) { + if (result && result.codeResult) { + console.log('Decoded:', result.codeResult.code); + } +}); +``` + +## Chaining Multiple Preprocessors {#chaining-preprocessors} + +Preprocessors are applied in array order: + +```javascript +preprocessors: [ + Quagga.Preprocessors.addBorder(15), + myContrastEnhancer, + myNoiseReducer +] +``` + +Each preprocessor receives the output of the previous one. + +## Creating Custom Preprocessors {#custom-preprocessors} + +Custom preprocessors are functions that modify image data in place. + +### Type Signature {#type-signature} + +```typescript +type QuaggaImagePreprocessor = (imageWrapper: ImageWrapper) => ImageWrapper; +``` + +### Guidelines {#guidelines} + +1. **Modify in place**: For best performance, modify `imageWrapper.data` directly +2. **Maintain dimensions**: Keep the same image width and height +3. **Return the same instance**: Return the imageWrapper that was passed in + +### ImageWrapper Structure {#imagewrapper-structure} + +```typescript +interface ImageWrapper { + data: Uint8Array; // Grayscale pixel values (0-255) + size: { + x: number; // Width + y: number; // Height + }; +} +``` + +### Example: Invert Colors {#example-invert} + +```javascript +const invertColors = (imageWrapper) => { + for (let i = 0; i < imageWrapper.data.length; i++) { + imageWrapper.data[i] = 255 - imageWrapper.data[i]; + } + return imageWrapper; +}; + +// Use it +Quagga.init({ + preprocessors: [invertColors], + // ... other config +}); +``` + +### Example: Increase Contrast {#example-contrast} + +```javascript +const increaseContrast = (imageWrapper) => { + const factor = 1.5; // contrast multiplier + const midpoint = 128; + + for (let i = 0; i < imageWrapper.data.length; i++) { + const value = imageWrapper.data[i]; + const adjusted = midpoint + (value - midpoint) * factor; + imageWrapper.data[i] = Math.max(0, Math.min(255, adjusted)); + } + return imageWrapper; +}; +``` + +### Example: Simple Threshold {#example-threshold} + +```javascript +const threshold = (cutoff = 128) => (imageWrapper) => { + for (let i = 0; i < imageWrapper.data.length; i++) { + imageWrapper.data[i] = imageWrapper.data[i] >= cutoff ? 255 : 0; + } + return imageWrapper; +}; + +// Use with custom threshold value +preprocessors: [threshold(100)] +``` + +### Example: Brightness Adjustment {#example-brightness} + +```javascript +const adjustBrightness = (offset) => (imageWrapper) => { + for (let i = 0; i < imageWrapper.data.length; i++) { + const adjusted = imageWrapper.data[i] + offset; + imageWrapper.data[i] = Math.max(0, Math.min(255, adjusted)); + } + return imageWrapper; +}; + +// Brighten the image +preprocessors: [adjustBrightness(30)] +``` + +## Performance Considerations {#performance} + +Preprocessors run on every frame (for live scanning) or on each image (for batch processing). + +**Best practices:** +- Modify data in place rather than creating copies +- Use TypedArray methods when possible (`fill()`, etc.) +- Keep algorithms simple - avoid expensive operations +- Test performance impact with `console.time()` + +**Example with performance measurement:** + +```javascript +const timedPreprocessor = (imageWrapper) => { + console.time('preprocessor'); + + // Your processing here + for (let i = 0; i < imageWrapper.data.length; i++) { + imageWrapper.data[i] = 255 - imageWrapper.data[i]; + } + + console.timeEnd('preprocessor'); + return imageWrapper; +}; +``` + +## Debugging Preprocessors {#debugging} + +To visualize what your preprocessor does: + +```javascript +const debugPreprocessor = (imageWrapper) => { + // Log some statistics + let min = 255, max = 0, sum = 0; + for (let i = 0; i < imageWrapper.data.length; i++) { + min = Math.min(min, imageWrapper.data[i]); + max = Math.max(max, imageWrapper.data[i]); + sum += imageWrapper.data[i]; + } + console.log('Image stats:', { + width: imageWrapper.size.x, + height: imageWrapper.size.y, + min, max, + avg: sum / imageWrapper.data.length + }); + + return imageWrapper; +}; +``` + +## Related {#related} + +- [Configuration Reference](../reference/configuration.md#preprocessors) - Complete preprocessors config +- [Algorithm Overview](../explanation/algorithm-overview.md) - Where preprocessing fits in the pipeline +- [Handle Difficult Barcodes](handle-difficult-barcodes.md) - Other techniques for problem barcodes + +--- + +[← Back to How-To Guides](index.md) diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index fb4fcac6..ce62a02d 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -13,6 +13,7 @@ The configuration object passed to `Quagga.init()` defines all aspects of barcod frequency: 10, decoder: { ... }, locator: { ... }, + preprocessors: [ ... ], // optional: image preprocessors debug: false } ``` @@ -121,6 +122,47 @@ See [Locator Configuration](#locator-configuration) below for complete details. **Note**: More fine-grained debug control is available via `inputStream.debug`, `decoder.debug`, and `locator.debug`. See [Debug Flags Guide](../how-to-guides/use-debug-flags.md). +### `preprocessors` {#preprocessors} + +**Type**: `Array` + +**Default**: `undefined` (no preprocessing) + +**Description**: Array of preprocessor functions applied to the image after frame grabbing but before barcode localization and decoding. Preprocessors are executed in array order. + +**Use cases**: +- Add white border to barcodes lacking quiet zones +- Enhance contrast for poor lighting conditions +- Apply custom image transformations + +**Example - Add border for barcodes without whitespace**: + +```javascript +Quagga.init({ + preprocessors: [Quagga.Preprocessors.addBorder(20)], + decoder: { + readers: ['code_128_reader'] + } +}); +``` + +**Example - Chain multiple preprocessors**: + +```javascript +preprocessors: [ + Quagga.Preprocessors.addBorder(15), + myCustomContrastEnhancer +] +``` + +**Built-in preprocessors** (via `Quagga.Preprocessors`): + +| Preprocessor | Description | +|--------------|-------------| +| `addBorder(size)` | Adds white border by shrinking content | + +See [Use Preprocessors Guide](../how-to-guides/use-preprocessors.md) for complete details on built-in and custom preprocessors. + ### `canvas` {#canvas} **Type**: `object` From 6b73def1036054a87a03637dca5d91a962da56e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 08:51:10 +0000 Subject: [PATCH 13/14] Add Divio documentation system guidelines to copilot-instructions.md Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- .github/copilot-instructions.md | 93 ++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0740726b..0738944e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -314,8 +314,97 @@ When preparing a release: - **Issue Tracker**: https://github.com/ericblade/quagga2/issues - **Original Quagga**: https://github.com/serratus/quaggaJS (archived) +## Documentation (Divio System) + +This project uses the [Divio Documentation System](https://documentation.divio.com/) for user-facing documentation in `/docs`. The README.md should only contain brief summaries with links to full documentation. + +### Documentation Structure + +The `/docs` folder has four distinct sections, each serving a specific purpose: + +| Section | Purpose | User Question | Content Style | +|---------|---------|---------------|---------------| +| `tutorials/` | Learning-oriented | "I want to learn" | Step-by-step lessons, beginner-friendly | +| `how-to-guides/` | Task-oriented | "I want to do X" | Practical guides for specific goals | +| `reference/` | Information-oriented | "I need details about Y" | Technical specs, API docs, config options | +| `explanation/` | Understanding-oriented | "I want to understand why" | Background, concepts, architecture | + +### When Adding a New Feature + +When implementing a new feature, update documentation in the appropriate sections: + +1. **reference/configuration.md** - Add new config options with: + - Type information + - Default values + - Examples + - Link to how-to guide if relevant + +2. **how-to-guides/** - Create or update guides if the feature requires user action: + - Create new file: `how-to-guides/use-feature-name.md` + - Update `how-to-guides/index.md` to link to new guide + - Focus on practical usage, not theory + +3. **explanation/algorithm-overview.md** - Update if the feature affects the processing pipeline + +4. **tutorials/** - Update if the feature is essential for beginners (rare for most features) + +5. **README.md** - Add only a brief mention with link to full docs: + ```markdown + ### feature-name + + Brief one-line description. + + **[📖 Full Documentation](https://ericblade.github.io/quagga2/how-to-guides/use-feature-name.html)** + ``` + +### Documentation File Format + +Use this template for new how-to guides: + +```markdown +# Feature Name {#feature-name} + +Brief description of what this guide covers. + +## When to Use {#when-to-use} + +List use cases. + +## Basic Usage {#basic-usage} + +Code examples. + +## Advanced Usage {#advanced-usage} + +More complex examples. + +## Related {#related} + +- [Link to related docs](path/to/doc.md) + +--- + +[← Back to How-To Guides](index.md) +``` + +### Documentation Index Files + +Each section has an `index.md` that lists all pages in that section. When adding a new page: +- Add entry to the appropriate section's `index.md` +- Mark new entries with 🆕 emoji +- Keep entries organized by category + +### Don't Do This + +- ❌ Don't put comprehensive documentation in README.md +- ❌ Don't duplicate content across sections +- ❌ Don't mix different documentation types (e.g., tutorials in reference) +- ❌ Don't forget to update index files + --- -**Last Updated**: 2025-11-16 +**Last Updated**: 2025-11-30 -**Note to Copilot**: When suggesting changes, always consider the impact on `DEPENDENCIES.md` and whether it needs updates. This project has complex dependency management due to bundling, version pinning, and security overrides - treat dependency changes with extra care. +**Note to Copilot**: When suggesting changes, always consider: +1. The impact on `DEPENDENCIES.md` - This project has complex dependency management due to bundling, version pinning, and security overrides. Treat dependency changes with extra care. +2. The impact on `/docs` - User-facing features need documentation following the Divio system. Comprehensive docs go in `/docs`, not README.md. From 01fd51898621cfd9ed55211222a046d187a852ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 08:58:28 +0000 Subject: [PATCH 14/14] Address doc review feedback: clarify singleChannel config, add overlay canvas visualization section Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --- docs/explanation/algorithm-overview.md | 4 +- docs/how-to-guides/use-preprocessors.md | 69 +++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/docs/explanation/algorithm-overview.md b/docs/explanation/algorithm-overview.md index 42c2b71f..82c77039 100644 --- a/docs/explanation/algorithm-overview.md +++ b/docs/explanation/algorithm-overview.md @@ -12,9 +12,9 @@ Input Image → Preprocessing → Localization → Decoding → Result ### 1. Preprocessing {#preprocessing} -**Built-in preprocessing:** +**Built-in preprocessing** (during frame grabbing): - **Scaling**: Image is resized based on `inputStream.size` -- **Grayscale conversion**: Color image converted to grayscale +- **Grayscale conversion**: Color image converted to grayscale (uses `inputStream.singleChannel` option - if true, uses only red channel; otherwise uses luminance formula) - **Area cropping**: If `inputStream.area` is set, crop to that region **Custom preprocessors** (via `preprocessors` config): diff --git a/docs/how-to-guides/use-preprocessors.md b/docs/how-to-guides/use-preprocessors.md index 19cf4c0f..c63da832 100644 --- a/docs/how-to-guides/use-preprocessors.md +++ b/docs/how-to-guides/use-preprocessors.md @@ -215,6 +215,75 @@ const debugPreprocessor = (imageWrapper) => { }; ``` +## Visualizing with Overlay Canvas {#visualizing-overlay} + +When using LiveStream or VideoStream input sources, you can draw on the overlay canvas to visualize preprocessing effects. The overlay canvas is positioned over the video feed. + +**Setting up the canvas:** + +```javascript +Quagga.init({ + inputStream: { + type: 'LiveStream', + target: document.querySelector('#barcode-scanner') + }, + // ... other config +}, function(err) { + if (err) return console.error(err); + Quagga.start(); +}); +``` + +**Drawing on the overlay:** + +```javascript +// Access the overlay canvas +const overlayCanvas = document.querySelector('canvas.drawingBuffer'); +const ctx = overlayCanvas.getContext('2d'); + +// Draw after each processed frame +Quagga.onProcessed(function(result) { + // Clear previous drawings + ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); + + // Draw custom visualizations + if (result && result.box) { + ctx.strokeStyle = 'green'; + ctx.lineWidth = 2; + ctx.strokeRect(result.box[0][0], result.box[0][1], + result.box[2][0] - result.box[0][0], + result.box[2][1] - result.box[0][1]); + } +}); +``` + +**Visualizing preprocessing effects:** + +To visualize what your preprocessor does, you can render the processed image data to the overlay: + +```javascript +const visualizePreprocessor = (imageWrapper) => { + const overlayCanvas = document.querySelector('canvas.drawingBuffer'); + if (overlayCanvas) { + const ctx = overlayCanvas.getContext('2d'); + const imageData = ctx.createImageData(imageWrapper.size.x, imageWrapper.size.y); + + // Convert grayscale to RGBA for display + for (let i = 0; i < imageWrapper.data.length; i++) { + const val = imageWrapper.data[i]; + imageData.data[i * 4 + 0] = val; // R + imageData.data[i * 4 + 1] = val; // G + imageData.data[i * 4 + 2] = val; // B + imageData.data[i * 4 + 3] = 128; // A (semi-transparent) + } + + ctx.putImageData(imageData, 0, 0); + } + + return imageWrapper; +}; +``` + ## Related {#related} - [Configuration Reference](../reference/configuration.md#preprocessors) - Complete preprocessors config