From 48b5d9c178fe7b138011464cd45d6a2d4f822ee9 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 12 Feb 2026 14:24:02 +0100 Subject: [PATCH 1/6] :boom: :recycle: :sparkles: move PDF-related libraries to optional dependencies --- package-lock.json | 440 ++++++++++-------- package.json | 11 +- src/image/extractedImage.ts | 9 +- src/image/imageCompressor.ts | 12 +- src/image/imageExtractor.ts | 15 +- src/pdf/pdfCompressor.ts | 34 +- src/pdf/pdfOperation.ts | 17 +- src/pdf/pdfUtils.ts | 28 +- src/utils/index.ts | 1 + src/utils/optionalLoader.ts | 18 + .../invoiceSplitterExtractor.ts | 19 +- .../multiReceiptsExtractor.ts | 19 +- src/v2/client/pollingOptions.ts | 2 +- 13 files changed, 368 insertions(+), 257 deletions(-) create mode 100644 src/utils/index.ts create mode 100644 src/utils/optionalLoader.ts diff --git a/package-lock.json b/package-lock.json index b151f8e56..067c7e538 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,9 @@ "version": "4.36.3", "license": "MIT", "dependencies": { - "@cantoo/pdf-lib": "^2.3.2", "commander": "~9.4.1", "file-type": "^19.6.0", "node-poppler": "^7.2.4", - "pdf.js-extract": "^0.2.1", - "sharp": "~0.34.5", "tmp": "^0.2.3", "tslib": "^2.8.1", "undici": "^6.23.0" @@ -23,6 +20,7 @@ "mindee": "bin/mindee.js" }, "devDependencies": { + "@cantoo/pdf-lib": "^2.3.2", "@types/chai": "^5.2.3", "@types/mocha": "^10.0.10", "@types/node": "^18.19.130", @@ -33,6 +31,8 @@ "eslint": "^9.39.2", "eslint-plugin-jsdoc": "^50.6.17", "mocha": "^11.7.5", + "pdf.js-extract": "^0.2.1", + "sharp": "~0.34.5", "tsc-alias": "^1.8.16", "tsx": "^4.21.0", "typedoc": "~0.28.15", @@ -40,6 +40,11 @@ }, "engines": { "node": ">= 18.17" + }, + "optionalDependencies": { + "@cantoo/pdf-lib": "^2.3.2", + "pdf.js-extract": "^0.2.1", + "sharp": "~0.34.5" } }, "node_modules/@borewit/text-codec": { @@ -56,6 +61,7 @@ "version": "2.5.3", "resolved": "https://registry.npmjs.org/@cantoo/pdf-lib/-/pdf-lib-2.5.3.tgz", "integrity": "sha512-SBQp8i/XdWNUhLutn5P67Pwj4X9vU046BRpfOMODJZuYVrgChtsTfgdnlW2O7x8gdXs8j7NoTaWI/b78E2oVmQ==", + "dev": true, "license": "MIT", "dependencies": { "@pdf-lib/standard-fonts": "^1.0.0", @@ -71,6 +77,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -95,9 +102,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", "cpu": [ "ppc64" ], @@ -112,9 +119,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", "cpu": [ "arm" ], @@ -129,9 +136,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", "cpu": [ "arm64" ], @@ -146,9 +153,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", "cpu": [ "x64" ], @@ -163,9 +170,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", "cpu": [ "arm64" ], @@ -180,9 +187,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", "cpu": [ "x64" ], @@ -197,9 +204,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", "cpu": [ "arm64" ], @@ -214,9 +221,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", "cpu": [ "x64" ], @@ -231,9 +238,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", "cpu": [ "arm" ], @@ -248,9 +255,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", "cpu": [ "arm64" ], @@ -265,9 +272,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", "cpu": [ "ia32" ], @@ -282,9 +289,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", "cpu": [ "loong64" ], @@ -299,9 +306,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", "cpu": [ "mips64el" ], @@ -316,9 +323,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", "cpu": [ "ppc64" ], @@ -333,9 +340,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", "cpu": [ "riscv64" ], @@ -350,9 +357,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", "cpu": [ "s390x" ], @@ -367,9 +374,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", "cpu": [ "x64" ], @@ -384,9 +391,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", "cpu": [ "arm64" ], @@ -401,9 +408,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", "cpu": [ "x64" ], @@ -418,9 +425,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", "cpu": [ "arm64" ], @@ -435,9 +442,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", "cpu": [ "x64" ], @@ -452,9 +459,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", "cpu": [ "arm64" ], @@ -469,9 +476,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", "cpu": [ "x64" ], @@ -486,9 +493,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", "cpu": [ "arm64" ], @@ -503,9 +510,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", "cpu": [ "ia32" ], @@ -520,9 +527,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", "cpu": [ "x64" ], @@ -726,16 +733,16 @@ } }, "node_modules/@gerrit0/mini-shiki": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.21.0.tgz", - "integrity": "sha512-9PrsT5DjZA+w3lur/aOIx3FlDeHdyCEFlv9U+fmsVyjPZh61G5SYURQ/1ebe2U63KbDmI2V8IhIUegWb8hjOyg==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.22.0.tgz", + "integrity": "sha512-jMpciqEVUBKE1QwU64S4saNMzpsSza6diNCk4MWAeCxO2+LFi2FIFmL2S0VDLzEJCxuvCbU783xi8Hp/gkM5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/engine-oniguruma": "^3.21.0", - "@shikijs/langs": "^3.21.0", - "@shikijs/themes": "^3.21.0", - "@shikijs/types": "^3.21.0", + "@shikijs/engine-oniguruma": "^3.22.0", + "@shikijs/langs": "^3.22.0", + "@shikijs/themes": "^3.22.0", + "@shikijs/types": "^3.22.0", "@shikijs/vscode-textmate": "^10.0.2" } }, @@ -795,6 +802,7 @@ "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" @@ -807,6 +815,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -829,6 +838,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -851,6 +861,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -867,6 +878,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -883,6 +895,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -899,6 +912,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -915,6 +929,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -931,6 +946,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -947,6 +963,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -963,6 +980,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -979,6 +997,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -995,6 +1014,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1011,6 +1031,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1033,6 +1054,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1055,6 +1077,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1077,6 +1100,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1099,6 +1123,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1121,6 +1146,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1143,6 +1169,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1165,6 +1192,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1187,6 +1215,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { @@ -1206,6 +1235,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1225,6 +1255,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1244,6 +1275,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1316,6 +1348,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "dev": true, "license": "MIT", "dependencies": { "pako": "^1.0.6" @@ -1325,6 +1358,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "dev": true, "license": "MIT", "dependencies": { "pako": "^1.0.10" @@ -1348,40 +1382,40 @@ "license": "MIT" }, "node_modules/@shikijs/engine-oniguruma": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.21.0.tgz", - "integrity": "sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.22.0.tgz", + "integrity": "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/types": "3.21.0", + "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "node_modules/@shikijs/langs": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.21.0.tgz", - "integrity": "sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.22.0.tgz", + "integrity": "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/types": "3.21.0" + "@shikijs/types": "3.22.0" } }, "node_modules/@shikijs/themes": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.21.0.tgz", - "integrity": "sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.22.0.tgz", + "integrity": "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g==", "dev": true, "license": "MIT", "dependencies": { - "@shikijs/types": "3.21.0" + "@shikijs/types": "3.22.0" } }, "node_modules/@shikijs/types": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.21.0.tgz", - "integrity": "sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.22.0.tgz", + "integrity": "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==", "dev": true, "license": "MIT", "dependencies": { @@ -1476,17 +1510,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz", - "integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", + "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/type-utils": "8.53.1", - "@typescript-eslint/utils": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/type-utils": "8.55.0", + "@typescript-eslint/utils": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -1499,22 +1533,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.53.1", + "@typescript-eslint/parser": "^8.55.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", - "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", + "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3" }, "engines": { @@ -1530,14 +1564,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz", - "integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", + "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.53.1", - "@typescript-eslint/types": "^8.53.1", + "@typescript-eslint/tsconfig-utils": "^8.55.0", + "@typescript-eslint/types": "^8.55.0", "debug": "^4.4.3" }, "engines": { @@ -1552,14 +1586,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz", - "integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", + "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1" + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1570,9 +1604,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz", - "integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", + "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", "dev": true, "license": "MIT", "engines": { @@ -1587,15 +1621,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz", - "integrity": "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", + "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1", - "@typescript-eslint/utils": "8.53.1", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/utils": "8.55.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -1612,9 +1646,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz", - "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", + "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", "dev": true, "license": "MIT", "engines": { @@ -1626,16 +1660,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz", - "integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", + "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.53.1", - "@typescript-eslint/tsconfig-utils": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", + "@typescript-eslint/project-service": "8.55.0", + "@typescript-eslint/tsconfig-utils": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", @@ -1654,16 +1688,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz", - "integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", + "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1" + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1678,13 +1712,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz", - "integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", + "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/types": "8.55.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2038,6 +2072,7 @@ "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", @@ -2051,6 +2086,7 @@ "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" @@ -2063,12 +2099,14 @@ "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/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", @@ -2120,6 +2158,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -2164,6 +2203,7 @@ "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": { "node": ">=8" @@ -2196,6 +2236,7 @@ "version": "0.0.24", "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-0.0.24.tgz", "integrity": "sha512-PatEhAW5pIHr28MvFQGV5iiHNloqvecQZlxs7/8s/eulLqZI3uVqPkrO7YDuqsebovr/9mmcWDSWzVG4amEZgQ==", + "dev": true, "license": "MIT" }, "node_modules/eastasianwidth": { @@ -2226,9 +2267,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", - "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2239,32 +2280,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.2", - "@esbuild/android-arm": "0.27.2", - "@esbuild/android-arm64": "0.27.2", - "@esbuild/android-x64": "0.27.2", - "@esbuild/darwin-arm64": "0.27.2", - "@esbuild/darwin-x64": "0.27.2", - "@esbuild/freebsd-arm64": "0.27.2", - "@esbuild/freebsd-x64": "0.27.2", - "@esbuild/linux-arm": "0.27.2", - "@esbuild/linux-arm64": "0.27.2", - "@esbuild/linux-ia32": "0.27.2", - "@esbuild/linux-loong64": "0.27.2", - "@esbuild/linux-mips64el": "0.27.2", - "@esbuild/linux-ppc64": "0.27.2", - "@esbuild/linux-riscv64": "0.27.2", - "@esbuild/linux-s390x": "0.27.2", - "@esbuild/linux-x64": "0.27.2", - "@esbuild/netbsd-arm64": "0.27.2", - "@esbuild/netbsd-x64": "0.27.2", - "@esbuild/openbsd-arm64": "0.27.2", - "@esbuild/openbsd-x64": "0.27.2", - "@esbuild/openharmony-arm64": "0.27.2", - "@esbuild/sunos-x64": "0.27.2", - "@esbuild/win32-arm64": "0.27.2", - "@esbuild/win32-ia32": "0.27.2", - "@esbuild/win32-x64": "0.27.2" + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" } }, "node_modules/escalade": { @@ -2759,9 +2800,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", "dev": true, "license": "MIT", "dependencies": { @@ -2775,6 +2816,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -2873,6 +2915,7 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, "funding": [ { "type": "github", @@ -2946,6 +2989,7 @@ "version": "0.3.4", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, "license": "MIT" }, "node_modules/is-binary-path": { @@ -3205,9 +3249,9 @@ "license": "MIT" }, "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", "dev": true, "license": "MIT", "dependencies": { @@ -3377,6 +3421,7 @@ "version": "1.5.8", "resolved": "https://registry.npmjs.org/node-html-better-parser/-/node-html-better-parser-1.5.8.tgz", "integrity": "sha512-t/wAKvaTSKco43X+yf9+76RiMt18MtMmzd4wc7rKj+fWav6DV4ajDEKdWlLzSE8USDF5zr/06uGj0Wr/dGAFtw==", + "dev": true, "license": "MIT", "dependencies": { "html-entities": "^2.3.2" @@ -3469,6 +3514,7 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { @@ -3552,6 +3598,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/pdf.js-extract/-/pdf.js-extract-0.2.1.tgz", "integrity": "sha512-oUs5KaTVCelIyiBajCx3zAZKurkN9oVwRdqbSeDqeofddxNuwJRur86fCETvKZ/tX5nZJUSZWq3ie76PsArz7A==", + "dev": true, "license": "MIT", "dependencies": { "dommatrix": "0.0.24", @@ -3779,9 +3826,9 @@ "license": "MIT" }, "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.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3804,6 +3851,7 @@ "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": { @@ -3884,6 +3932,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" @@ -4353,6 +4402,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" diff --git a/package.json b/package.json index 0aae7698b..0955ac184 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,9 @@ }, "homepage": "https://mindee.com/", "devDependencies": { + "sharp": "~0.34.5", + "@cantoo/pdf-lib": "^2.3.2", + "pdf.js-extract": "^0.2.1", "@types/chai": "^5.2.3", "@types/mocha": "^10.0.10", "@types/node": "^18.19.130", @@ -59,16 +62,18 @@ "typescript": "^5.9.3" }, "dependencies": { - "@cantoo/pdf-lib": "^2.3.2", "commander": "~9.4.1", "file-type": "^19.6.0", "node-poppler": "^7.2.4", - "pdf.js-extract": "^0.2.1", - "sharp": "~0.34.5", "tmp": "^0.2.3", "tslib": "^2.8.1", "undici": "^6.23.0" }, + "optionalDependencies": { + "sharp": "~0.34.5", + "@cantoo/pdf-lib": "^2.3.2", + "pdf.js-extract": "^0.2.1" + }, "keywords": [ "typescript", "mindee", diff --git a/src/image/extractedImage.ts b/src/image/extractedImage.ts index e568014c2..05c5c5fc5 100644 --- a/src/image/extractedImage.ts +++ b/src/image/extractedImage.ts @@ -4,8 +4,9 @@ import { writeFileSync } from "node:fs"; import path from "node:path"; import { logger } from "@/logger.js"; import { BufferInput, MIMETYPES } from "@/input/index.js"; -import { Poppler } from "node-poppler"; +import type * as popplerTypes from "node-poppler"; import { writeFile } from "fs/promises"; +import { loadOptionalDependency } from "@/utils/index.js"; /** * Generic class for image extraction @@ -33,7 +34,9 @@ export class ExtractedImage { try { let outputBuffer: Buffer = this.buffer; if (fileExt !== ".pdf") { - const poppler = new Poppler(); + const popplerImport = await loadOptionalDependency("node-poppler", "Image Processing"); + const poppler = (popplerImport as any).default || popplerImport; + const popplerInstance = new poppler.Poppler(); const options: Record = { firstPageToConvert: 1, lastPageToConvert: 1, @@ -48,7 +51,7 @@ export class ExtractedImage { options.tiffFile = true; } - const result = await poppler.pdfToCairo(this.buffer, undefined, options); + const result = await popplerInstance.pdfToCairo(this.buffer, undefined, options); outputBuffer = Buffer.from(result, "latin1"); } diff --git a/src/image/imageCompressor.ts b/src/image/imageCompressor.ts index e9f77c1a5..216e44420 100644 --- a/src/image/imageCompressor.ts +++ b/src/image/imageCompressor.ts @@ -1,7 +1,9 @@ -import sharp from "sharp"; -import { Sharp, Metadata } from "sharp"; +import { loadOptionalDependency } from "@/utils/index.js"; +import type * as SharpTypes from "sharp"; + import { MindeeImageError } from "@/errors/index.js"; + /** * Compresses an image with the given parameters. * @@ -16,9 +18,11 @@ export async function compressImage( maxWidth:number|null = null, maxHeight:number|null = null, ) { - let sharpImage: Sharp = sharp(imageBuffer); + const sharpLoaded = await loadOptionalDependency("sharp", "Image compression"); + const sharp = (sharpLoaded as any).default || sharpLoaded; + let sharpImage: SharpTypes.Sharp = sharp(imageBuffer); - const metadata: Metadata = await sharpImage.metadata(); + const metadata: SharpTypes.Metadata = await sharpImage.metadata(); if (metadata.width === undefined || metadata.height === undefined){ throw new MindeeImageError("Source image has invalid dimensions."); } diff --git a/src/image/imageExtractor.ts b/src/image/imageExtractor.ts index f9895c91f..e0909e5ea 100644 --- a/src/image/imageExtractor.ts +++ b/src/image/imageExtractor.ts @@ -1,6 +1,9 @@ -import { PDFDocument, PDFPage, degrees } from "@cantoo/pdf-lib"; +import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { getMinMaxX, getMinMaxY, Polygon } from "@/geometry/index.js"; import { adjustForRotation } from "@/geometry/polygonUtils.js"; +import { loadOptionalDependency } from "@/utils/index.js"; +const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); +const pdfLib = (pdfLibImport as any).default || pdfLibImport; /** * Extracts elements from a page based off of a list of bounding boxes. @@ -9,7 +12,7 @@ import { adjustForRotation } from "@/geometry/polygonUtils.js"; * @param polygons List of coordinates to pull the elements from. */ export async function extractFromPage( - pdfPage: PDFPage, + pdfPage: pdfLibTypes.PDFPage, polygons: Polygon[]) { const { width, height } = pdfPage.getSize(); const extractedElements :Uint8Array[] = []; @@ -21,7 +24,7 @@ export async function extractFromPage( for (const origPolygon of polygons) { const polygon = adjustForRotation(origPolygon, orientation); - const tempPdf = await PDFDocument.create(); + const tempPdf = await pdfLib.PDFDocument.create(); const newWidth = width * (getMinMaxX(polygon).max - getMinMaxX(polygon).min); const newHeight = height * (getMinMaxY(polygon).max - getMinMaxY(polygon).min); @@ -65,7 +68,7 @@ export async function extractFromPage( y: finalHeight, width: newWidth * qualityScale, height: newHeight * qualityScale, - rotate: degrees(270), + rotate: pdfLib.degrees(270), }); } else if (orientation === 180) { samplePage.drawPage(cropped, { @@ -73,7 +76,7 @@ export async function extractFromPage( y: finalHeight, width: newWidth * qualityScale, height: newHeight * qualityScale, - rotate: degrees(180), + rotate: pdfLib.degrees(180), }); } else if (orientation === 270) { samplePage.drawPage(cropped, { @@ -81,7 +84,7 @@ export async function extractFromPage( y: 0, width: newWidth * qualityScale, height: newHeight * qualityScale, - rotate: degrees(90), + rotate: pdfLib.degrees(90), }); } diff --git a/src/pdf/pdfCompressor.ts b/src/pdf/pdfCompressor.ts index c031a6798..c096469ad 100644 --- a/src/pdf/pdfCompressor.ts +++ b/src/pdf/pdfCompressor.ts @@ -2,9 +2,12 @@ import { logger } from "@/logger.js"; import tmp from "tmp"; import { ExtractedPdfInfo, extractTextFromPdf, hasSourceText } from "./pdfUtils.js"; import * as fs from "node:fs"; -import { Poppler } from "node-poppler"; -import { PDFDocument, PDFFont, PDFPage, rgb, StandardFonts } from "@cantoo/pdf-lib"; +import type * as popplerTypes from "node-poppler"; +import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { compressImage } from "@/image/index.js"; +import { loadOptionalDependency } from "@/utils/index.js"; +const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); +const pdfLib = (pdfLibImport as any).default || pdfLibImport; /** * Compresses each page of a provided PDF buffer. @@ -128,7 +131,7 @@ async function compressPagesWithQuality( disableSourceText: boolean, extractedText: ExtractedPdfInfo | null ): Promise { - const pdfDoc = await PDFDocument.load(pdfData, { + const pdfDoc = await pdfLib.PDFDocument.load(pdfData, { ignoreEncryption: true, password: "" }); @@ -180,7 +183,7 @@ function isCompressionSuccessful(totalCompressedSize: number, originalSize: numb * @returns A Promise resolving to the new PDF as a Buffer. */ async function createNewPdfFromCompressedPages(compressedPages: Buffer[]): Promise { - const newPdfDoc = await PDFDocument.create(); + const newPdfDoc = await pdfLib.PDFDocument.create(); for (const compressedPage of compressedPages) { const image = await newPdfDoc.embedJpg(compressedPage); @@ -198,7 +201,7 @@ async function createNewPdfFromCompressedPages(compressedPages: Buffer[]): Promi } async function addTextToPdfPage( - page: PDFPage, + page: pdfLibTypes.PDFPage, textInfo: ExtractedPdfInfo | null ): Promise { if (textInfo === null) { @@ -210,20 +213,22 @@ async function addTextToPdfPage( x: textPage.x, y: textPage.y, size: textPage.height, - color: rgb(0, 0, 0), + color: pdfLib.rgb(0, 0, 0), font: await getFontFromName(textPage.fontName) }); } } } -async function getFontFromName(fontName: string): Promise { - const pdfDoc = await PDFDocument.create(); - let font: PDFFont; - if (Object.values(StandardFonts).map(value => value.toString()).includes(fontName)) { +async function getFontFromName(fontName: string): Promise { + const pdfDoc = await pdfLib.PDFDocument.create(); + let font: pdfLibTypes.PDFFont; + const standardFontValues = Object.values(pdfLib.StandardFonts) as string[]; + + if (standardFontValues.includes(fontName)) { font = await pdfDoc.embedFont(fontName); } else { - font = await pdfDoc.embedFont(StandardFonts.Helvetica); + font = await pdfDoc.embedFont(pdfLib.StandardFonts.Helvetica); } return font; @@ -237,7 +242,10 @@ async function getFontFromName(fontName: string): Promise { * @param quality Quality to apply during rasterization. */ async function rasterizePage(pdfData: Buffer, index: number, quality = 85): Promise { - const poppler = new Poppler(); + + const popplerImport = await loadOptionalDependency("node-poppler", "Image Processing"); + const poppler = (popplerImport as any).default || popplerImport; + const popplerInstance = new poppler.Poppler(); const tmpPdf = tmp.fileSync(); const tempPdfPath = tmpPdf.name; const antialiasOption: "fast" | "best" | "default" | "good" | "gray" | "none" | "subpixel" = "best"; @@ -252,7 +260,7 @@ async function rasterizePage(pdfData: Buffer, index: number, quality = 85): Prom singleFile: true }; - const jpegBuffer = await poppler.pdfToCairo(tempPdfPath, undefined, options); + const jpegBuffer = await popplerInstance.pdfToCairo(tempPdfPath, undefined, options); await fs.promises.unlink(tempPdfPath); diff --git a/src/pdf/pdfOperation.ts b/src/pdf/pdfOperation.ts index 278454687..ac796bc1b 100644 --- a/src/pdf/pdfOperation.ts +++ b/src/pdf/pdfOperation.ts @@ -1,8 +1,11 @@ +import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { errorHandler } from "@/errors/handler.js"; -import { PDFDocument } from "@cantoo/pdf-lib"; import { PageOptions, PageOptionsOperation } from "@/input/pageOptions.js"; import { MindeeError } from "@/errors/index.js"; import { logger } from "@/logger.js"; +import { loadOptionalDependency } from "@/utils/index.js"; +const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); +const pdfLib = (pdfLibImport as any).default || pdfLibImport; export interface SplitPdf { file: Buffer; @@ -19,12 +22,12 @@ export async function extractPages( file: Buffer, pageOptions: PageOptions ): Promise { - const currentPdf = await PDFDocument.load(file, { + const currentPdf = await pdfLib.PDFDocument.load(file, { ignoreEncryption: true, password: "" }); - const newPdf = await PDFDocument.create(); + const newPdf = await pdfLib.PDFDocument.create(); const pageCount = currentPdf.getPageCount(); @@ -65,15 +68,15 @@ export async function extractPages( if (pageOptions.operation === PageOptionsOperation.KeepOnly) { const keptPages = await newPdf.copyPages(currentPdf, pageIndexes); - keptPages.forEach((keptPage) => { + keptPages.forEach((keptPage: pdfLibTypes.PDFPage) => { newPdf.addPage(keptPage); }); } else if (pageOptions.operation === PageOptionsOperation.Remove) { const pagesToKeep = currentPdf .getPageIndices() - .filter((v) => !pageIndexes.includes(v)); + .filter((v:number) => !pageIndexes.includes(v)); const keptPages = await newPdf.copyPages(currentPdf, pagesToKeep); - keptPages.forEach((keptPage) => { + keptPages.forEach((keptPage: pdfLibTypes.PDFPage) => { newPdf.addPage(keptPage); }); } else { @@ -90,7 +93,7 @@ export async function extractPages( * @returns the number of pages in the file. */ export async function countPages(file: Buffer): Promise { - const currentPdf = await PDFDocument.load(file, { + const currentPdf = await pdfLib.PDFDocument.load(file, { ignoreEncryption: true, password: "" }); diff --git a/src/pdf/pdfUtils.ts b/src/pdf/pdfUtils.ts index 4aa83758b..50e2c96d5 100644 --- a/src/pdf/pdfUtils.ts +++ b/src/pdf/pdfUtils.ts @@ -1,5 +1,6 @@ -import { PDFExtract, PDFExtractOptions, PDFExtractResult } from "pdf.js-extract"; +import type * as pdfJsExtractTypes from "pdf.js-extract"; import { MindeePdfError } from "@/errors/index.js"; +import { loadOptionalDependency } from "@/utils/index.js"; export interface PageTextInfo { @@ -34,16 +35,23 @@ function getConcatenatedText(pages: PageTextInfo[]): string { * @param pdfBuffer PDF handle, as a buffer. */ export async function extractTextFromPdf(pdfBuffer: Buffer): Promise { - const pdfExtract = new PDFExtract(); - const options: PDFExtractOptions = {}; + const pdfJsExtract = await loadOptionalDependency( + "pdf.js-extract", "PDF text extraction" + ); + const pdfJs = (pdfJsExtract as any).pdfjs || pdfJsExtract; + const pdfExtract: pdfJsExtractTypes.PDFExtract = new pdfJs.PDFExtract(); + const options: pdfJsExtractTypes.PDFExtractOptions = {}; - const pdf = await new Promise((resolve, reject) => { - pdfExtract.extractBuffer(pdfBuffer, options, (err, result) => { - if (err) reject(err); - if (result === undefined) - reject(new MindeePdfError("Couldn't process result.")); - else resolve(result); - }); + const pdf = await new Promise((resolve, reject) => { + pdfExtract.extractBuffer( + pdfBuffer, options, ( + err: Error | null, result: pdfJsExtractTypes.PDFExtractResult | undefined + ) => { + if (err) reject(err); + if (result === undefined) + reject(new MindeePdfError("Couldn't process result.")); + else resolve(result); + }); }); const pages = pdf.pages.map((page, index) => ({ diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 000000000..0c1c54b57 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1 @@ +export { loadOptionalDependency } from "./optionalLoader.js"; diff --git a/src/utils/optionalLoader.ts b/src/utils/optionalLoader.ts new file mode 100644 index 000000000..4d1644ef1 --- /dev/null +++ b/src/utils/optionalLoader.ts @@ -0,0 +1,18 @@ +/** + * Helper to load optional peer dependencies. + * @param packageName The name of the npm package to load + * @param featureName A human-readable name of the feature requiring this package + */ +export async function loadOptionalDependency(packageName: string, featureName: string): Promise { + try { + return await import(packageName); + } catch (error: any) { + if (error.code === "MODULE_NOT_FOUND" || error.message?.includes("Cannot find module")) { + throw new Error( + `The feature '${featureName}' requires the optional dependency '${packageName}'. ` + + `Please install it by running: npm install ${packageName}` + ); + } + throw error; + } +} diff --git a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts index e38916aa9..c5c6dabaa 100644 --- a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts +++ b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts @@ -1,22 +1,27 @@ -import { PDFDocument } from "@cantoo/pdf-lib"; +import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { MindeeError, MindeeInputSourceError } from "@/errors/index.js"; import { InvoiceSplitterV1 } from "@/v1/product/index.js"; import { LocalInputSource } from "@/input/index.js"; -import { ExtractedInvoiceSplitterImage } from "./extractedInvoiceSplitterImage.js"; +import { ExtractedInvoiceSplitterImage } from "@/v1/extraction/index.js"; +import { loadOptionalDependency } from "@/utils/index.js"; +const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); +const pdfLib = (pdfLibImport as any).default || pdfLibImport; -async function splitPdf(pdfDoc: PDFDocument, invoicePageGroups: number[][]): Promise { +async function splitPdf( + pdfDoc: pdfLibTypes.PDFDocument, + invoicePageGroups: number[][]): Promise { if (invoicePageGroups.length === 0) { return []; } const generatedPdfs: ExtractedInvoiceSplitterImage[] = []; for (let i = 0; i < invoicePageGroups.length; i++) { - const subdocument = await PDFDocument.create(); + const subdocument = await pdfLib.PDFDocument.create(); const fullIndexes = []; for (let j = invoicePageGroups[i][0]; j <= invoicePageGroups[i][invoicePageGroups[i].length - 1]; j++) { fullIndexes.push(j); } const copiedPages = await subdocument.copyPages(pdfDoc, fullIndexes); - copiedPages.map((page) => { + copiedPages.map((page: pdfLibTypes.PDFPage) => { subdocument.addPage(page); }); const subdocumentBytes = await subdocument.save(); @@ -29,7 +34,7 @@ async function splitPdf(pdfDoc: PDFDocument, invoicePageGroups: number[][]): Pro return generatedPdfs; } -async function getPdfDoc(inputFile: LocalInputSource): Promise { +async function getPdfDoc(inputFile: LocalInputSource): Promise { await inputFile.init(); if (!inputFile.isPdf()) { throw new MindeeInputSourceError( @@ -37,7 +42,7 @@ async function getPdfDoc(inputFile: LocalInputSource): Promise { ); } - const pdfDoc = await PDFDocument.load(inputFile.fileObject, { + const pdfDoc = await pdfLib.PDFDocument.load(inputFile.fileObject, { ignoreEncryption: true, password: "" }); diff --git a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts index 5ffb783d4..20920a2c5 100644 --- a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts +++ b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts @@ -1,11 +1,14 @@ -import { PDFDocument, PDFImage, PDFPage, degrees } from "@cantoo/pdf-lib"; +import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { MindeeError, MindeeInputSourceError } from "@/errors/index.js"; import { Polygon } from "@/geometry/index.js"; import { MultiReceiptsDetectorV1 } from "@/v1/product/index.js"; -import { ExtractedMultiReceiptImage } from "./extractedMultiReceiptImage.js"; +import { ExtractedMultiReceiptImage } from "@/v1/extraction/index.js"; import { LocalInputSource } from "@/input/index.js"; import { extractFromPage } from "@/image/index.js"; import { PositionField } from "@/v1/parsing/standard/index.js"; +import { loadOptionalDependency } from "@/utils/index.js"; +const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); +const pdfLib = (pdfLibImport as any).default || pdfLibImport; /** * Given a page and a set of coordinates, extracts & assigns individual receipts to an ExtractedMultiReceiptImage @@ -17,7 +20,7 @@ import { PositionField } from "@/v1/parsing/standard/index.js"; * pages. */ async function extractReceiptsFromPage( - pdfPage: PDFPage, + pdfPage: pdfLibTypes.PDFPage, boundingBoxes: Polygon[], pageId: number) { const extractedReceiptsRaw = await extractFromPage(pdfPage, boundingBoxes); @@ -29,7 +32,7 @@ async function extractReceiptsFromPage( } async function loadPdfDoc(inputFile: LocalInputSource) { - let pdfDoc: PDFDocument; + let pdfDoc: pdfLibTypes.PDFDocument; if (!["image/jpeg", "image/jpg", "image/png", "application/pdf"].includes(inputFile.mimeType)) { throw new MindeeInputSourceError( 'Unsupported file type "' + @@ -37,13 +40,13 @@ async function loadPdfDoc(inputFile: LocalInputSource) { '" Currently supported types are .png, .jpg and .pdf' ); } else if (inputFile.isPdf()) { - pdfDoc = await PDFDocument.load(inputFile.fileObject, { + pdfDoc = await pdfLib.PDFDocument.load(inputFile.fileObject, { ignoreEncryption: true, password: "" }); } else { - pdfDoc = await PDFDocument.create(); - let image: PDFImage; + pdfDoc = await pdfLib.PDFDocument.create(); + let image: pdfLibTypes.PDFImage; if (inputFile.mimeType === "image/png") { image = await pdfDoc.embedPng(inputFile.fileObject); } else { @@ -74,7 +77,7 @@ export async function extractReceipts( const pdfDoc = await loadPdfDoc(inputFile); for (let pageId = 0; pageId < pdfDoc.getPageCount(); pageId++) { const [page] = await pdfDoc.copyPages(pdfDoc, [pageId]); - page.setRotation(degrees(inference.pages[pageId].orientation?.value ?? 0)); + page.setRotation(pdfLib.degrees(inference.pages[pageId].orientation?.value ?? 0)); const receiptPositions = inference.pages[pageId].prediction.receipts.map( (receipt: PositionField) => receipt.boundingBox ); diff --git a/src/v2/client/pollingOptions.ts b/src/v2/client/pollingOptions.ts index 0a99a5e0c..917632849 100644 --- a/src/v2/client/pollingOptions.ts +++ b/src/v2/client/pollingOptions.ts @@ -1,5 +1,5 @@ /** - * Parameters for the internal polling loop in {@link v2.Client.enqueueAndGetInference | enqueueAndGetInference()}. + * Parameters for the internal polling loop in `enqueueAndGetInference()`. * * Default behavior: * - `initialDelaySec` = 2s From cf8bbf006197e9e974e0ba128807c741b0c0d7eb Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:32:19 +0100 Subject: [PATCH 2/6] fix tests + add CI --- .github/workflows/_test-integrations.yml | 40 +++- .github/workflows/_test-units.yml | 37 +++- package-lock.json | 76 ++----- package.json | 15 +- src/image/imageCompressor.ts | 2 + src/image/imageExtractor.ts | 15 +- src/pdf/pdfCompressor.ts | 18 +- src/pdf/pdfOperation.ts | 16 +- src/pdf/pdfUtils.ts | 6 +- src/utils/optionalLoader.ts | 9 +- .../invoiceSplitterExtractor.ts | 16 +- .../multiReceiptsExtractor.ts | 16 +- tests/input/compression.spec.ts | 6 +- tests/input/pageOperations.spec.ts | 2 +- tests/input/sources.spec.ts | 8 +- tests/misc/missingDependencies.integration.ts | 40 ++++ tests/misc/missingDependencies.spec.ts | 42 ++++ tests/pdf/pdfOperation.spec.ts | 2 +- tests/pdf/pdfTypes.spec.ts | 2 +- .../invoiceSplitterExtractor.spec.ts | 2 +- .../invoiceSplitterReconstruction.spec.ts | 3 +- .../extraction/multiReceiptsExtractor.spec.ts | 81 +++---- ...multiReceiptsReconstruction.integration.ts | 209 +++++++++--------- .../multiReceiptsReconstruction.spec.ts | 2 +- .../invoiceSplitter.integration.ts | 2 +- 25 files changed, 432 insertions(+), 235 deletions(-) create mode 100644 tests/misc/missingDependencies.integration.ts create mode 100644 tests/misc/missingDependencies.spec.ts diff --git a/.github/workflows/_test-integrations.yml b/.github/workflows/_test-integrations.yml index 558f29202..905299d7e 100644 --- a/.github/workflows/_test-integrations.yml +++ b/.github/workflows/_test-integrations.yml @@ -16,8 +16,8 @@ env: MINDEE_V2_SE_TESTS_OCR_MODEL_ID: ${{ secrets.MINDEE_V2_SE_TESTS_OCR_MODEL_ID }} jobs: - run-tests: - name: Run Integration Tests + run-tests-with-optional-dependencies: + name: Run Integration Tests With Optional Dependencies timeout-minutes: 30 strategy: matrix: @@ -65,3 +65,39 @@ jobs: - name: Test code run: npm run test-integration + + + run-tests-without-optional-dependencies: + name: Run Integration Tests Without Optional Dependencies + timeout-minutes: 30 + strategy: + matrix: + os: + - "ubuntu-latest" + - "windows-latest" + - "macos-latest" + node-version: + - "18" + - "24" + runs-on: ${{ matrix.os }} + + steps: + - name: Check out Git repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install Node.js dependencies + run: npm ci --omit=optional + + - name: Compilation + run: npm run build + + - name: Test code + run: npm run test-integration-light diff --git a/.github/workflows/_test-units.yml b/.github/workflows/_test-units.yml index 785708c3e..99e25d53e 100644 --- a/.github/workflows/_test-units.yml +++ b/.github/workflows/_test-units.yml @@ -4,8 +4,8 @@ on: workflow_call: jobs: - run-tests: - name: Run Tests + run-tests-with-optional-dependencies: + name: Run Tests With Optional Dependencies strategy: matrix: os: @@ -42,3 +42,36 @@ jobs: - name: Test code run: npm run test + run-tests-without-optional-dependencies: + name: Run Tests Without Optional Dependencies + strategy: + matrix: + os: + - "ubuntu-latest" + node-version: + - "18" + - "20" + - "22" + - "24" + runs-on: ${{ matrix.os }} + + steps: + - name: Check out Git repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install Node.js dependencies + run: npm ci --omit=optional + + - name: Compilation + run: npm run build + + - name: Test code + run: npm run test-light diff --git a/package-lock.json b/package-lock.json index 067c7e538..94d38729c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,6 @@ "mindee": "bin/mindee.js" }, "devDependencies": { - "@cantoo/pdf-lib": "^2.3.2", "@types/chai": "^5.2.3", "@types/mocha": "^10.0.10", "@types/node": "^18.19.130", @@ -31,8 +30,6 @@ "eslint": "^9.39.2", "eslint-plugin-jsdoc": "^50.6.17", "mocha": "^11.7.5", - "pdf.js-extract": "^0.2.1", - "sharp": "~0.34.5", "tsc-alias": "^1.8.16", "tsx": "^4.21.0", "typedoc": "~0.28.15", @@ -61,8 +58,8 @@ "version": "2.5.3", "resolved": "https://registry.npmjs.org/@cantoo/pdf-lib/-/pdf-lib-2.5.3.tgz", "integrity": "sha512-SBQp8i/XdWNUhLutn5P67Pwj4X9vU046BRpfOMODJZuYVrgChtsTfgdnlW2O7x8gdXs8j7NoTaWI/b78E2oVmQ==", - "dev": true, "license": "MIT", + "optional": true, "dependencies": { "@pdf-lib/standard-fonts": "^1.0.0", "@pdf-lib/upng": "^1.0.1", @@ -77,7 +74,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -802,8 +798,8 @@ "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", + "optional": true, "engines": { "node": ">=18" } @@ -815,7 +811,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -838,7 +833,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -861,7 +855,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -878,7 +871,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -895,7 +887,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -912,7 +903,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -929,7 +919,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -946,7 +935,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -963,7 +951,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -980,7 +967,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -997,7 +983,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1014,7 +999,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1031,7 +1015,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1054,7 +1037,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1077,7 +1059,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1100,7 +1081,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1123,7 +1103,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1146,7 +1125,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1169,7 +1147,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1192,7 +1169,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1215,7 +1191,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { @@ -1235,7 +1210,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1255,7 +1229,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1275,7 +1248,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1348,8 +1320,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", - "dev": true, "license": "MIT", + "optional": true, "dependencies": { "pako": "^1.0.6" } @@ -1358,8 +1330,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", - "dev": true, "license": "MIT", + "optional": true, "dependencies": { "pako": "^1.0.10" } @@ -2072,8 +2044,8 @@ "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", + "optional": true, "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" @@ -2086,7 +2058,7 @@ "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, + "devOptional": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2099,15 +2071,15 @@ "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, + "devOptional": true, "license": "MIT" }, "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", + "optional": true, "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -2158,8 +2130,8 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/debug": { "version": "4.4.3", @@ -2203,8 +2175,8 @@ "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", + "optional": true, "engines": { "node": ">=8" } @@ -2236,8 +2208,8 @@ "version": "0.0.24", "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-0.0.24.tgz", "integrity": "sha512-PatEhAW5pIHr28MvFQGV5iiHNloqvecQZlxs7/8s/eulLqZI3uVqPkrO7YDuqsebovr/9mmcWDSWzVG4amEZgQ==", - "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/eastasianwidth": { "version": "0.2.0", @@ -2915,7 +2887,6 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", - "dev": true, "funding": [ { "type": "github", @@ -2926,7 +2897,8 @@ "url": "https://patreon.com/mdevils" } ], - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/ieee754": { "version": "1.2.1", @@ -2989,8 +2961,8 @@ "version": "0.3.4", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -3421,8 +3393,8 @@ "version": "1.5.8", "resolved": "https://registry.npmjs.org/node-html-better-parser/-/node-html-better-parser-1.5.8.tgz", "integrity": "sha512-t/wAKvaTSKco43X+yf9+76RiMt18MtMmzd4wc7rKj+fWav6DV4ajDEKdWlLzSE8USDF5zr/06uGj0Wr/dGAFtw==", - "dev": true, "license": "MIT", + "optional": true, "dependencies": { "html-entities": "^2.3.2" } @@ -3514,8 +3486,8 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true, - "license": "(MIT AND Zlib)" + "license": "(MIT AND Zlib)", + "optional": true }, "node_modules/parent-module": { "version": "1.0.1", @@ -3598,8 +3570,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/pdf.js-extract/-/pdf.js-extract-0.2.1.tgz", "integrity": "sha512-oUs5KaTVCelIyiBajCx3zAZKurkN9oVwRdqbSeDqeofddxNuwJRur86fCETvKZ/tX5nZJUSZWq3ie76PsArz7A==", - "dev": true, "license": "MIT", + "optional": true, "dependencies": { "dommatrix": "0.0.24", "web-streams-polyfill": "3.2.0" @@ -3851,9 +3823,9 @@ "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", + "optional": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", @@ -3932,8 +3904,8 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "dev": true, "license": "MIT", + "optional": true, "dependencies": { "is-arrayish": "^0.3.1" } @@ -4402,8 +4374,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", - "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">= 8" } diff --git a/package.json b/package.json index 0955ac184..9557b18d7 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,14 @@ "build": "tsc --build && tsc-alias", "build-for-dist": "tsc --build && tsc-alias && cp LICENSE README.md CHANGELOG.md ./dist", "clean": "rm -rf ./dist ./docs/_build", - "test": "mocha \"tests/**/*.spec.ts\"", - "test-integration": "mocha \"tests/**/*.integration.ts\"", - "test-v1": "mocha \"tests/v1/**/*.spec.ts\"", - "test-v2": "mocha \"tests/v2/**/*.spec.ts\"", + "test": "mocha --grep \"#lightDeps\" --invert \"tests/**/*.spec.ts\"", + "test-light": "mocha --grep \"#extraDeps\" --invert \"tests/**/*.spec.ts\"", + "test-integration": "mocha --grep \"#lightDeps\" --invert \"tests/**/*.integration.ts\"", + "test-integration-light": "mocha --grep \"#extraDeps\" --invert \"tests/**/*.integration.ts\"", + "test-v1": "mocha --grep \"#lightDeps\" --invert \"tests/v1/**/*.spec.ts\"", + "test-v1-light": "mocha --grep \"#extraDeps\" --invert \"tests/v1/**/*.spec.ts\"", + "test-v2": "mocha --grep \"#lightDeps\" --invert \"tests/v2/**/*.spec.ts\"", + "test-v2-light": "mocha --grep \"#extraDeps\" --invert \"tests/v2/**/*.spec.ts\"", "lint": "tsc --noEmit && eslint './src/**/*.ts' --report-unused-disable-directives && echo 'Your .ts files look good.'", "lint-fix": "eslint './src/**/*.ts' --fix", "docs": "typedoc --out docs/_build ./src/index.ts", @@ -43,9 +47,6 @@ }, "homepage": "https://mindee.com/", "devDependencies": { - "sharp": "~0.34.5", - "@cantoo/pdf-lib": "^2.3.2", - "pdf.js-extract": "^0.2.1", "@types/chai": "^5.2.3", "@types/mocha": "^10.0.10", "@types/node": "^18.19.130", diff --git a/src/image/imageCompressor.ts b/src/image/imageCompressor.ts index 216e44420..d49d46069 100644 --- a/src/image/imageCompressor.ts +++ b/src/image/imageCompressor.ts @@ -1,4 +1,6 @@ import { loadOptionalDependency } from "@/utils/index.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error import type * as SharpTypes from "sharp"; import { MindeeImageError } from "@/errors/index.js"; diff --git a/src/image/imageExtractor.ts b/src/image/imageExtractor.ts index e0909e5ea..6401698e6 100644 --- a/src/image/imageExtractor.ts +++ b/src/image/imageExtractor.ts @@ -1,9 +1,19 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { getMinMaxX, getMinMaxY, Polygon } from "@/geometry/index.js"; import { adjustForRotation } from "@/geometry/polygonUtils.js"; import { loadOptionalDependency } from "@/utils/index.js"; -const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); -const pdfLib = (pdfLibImport as any).default || pdfLibImport; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib() { + if (!pdfLib) { + const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); + pdfLib = (pdfLibImport as any).default || pdfLibImport; + } + return pdfLib; +} /** * Extracts elements from a page based off of a list of bounding boxes. @@ -14,6 +24,7 @@ const pdfLib = (pdfLibImport as any).default || pdfLibImport; export async function extractFromPage( pdfPage: pdfLibTypes.PDFPage, polygons: Polygon[]) { + const pdfLib = await getPdfLib(); const { width, height } = pdfPage.getSize(); const extractedElements :Uint8Array[] = []; // Manual upscale. diff --git a/src/pdf/pdfCompressor.ts b/src/pdf/pdfCompressor.ts index c096469ad..9d0662ded 100644 --- a/src/pdf/pdfCompressor.ts +++ b/src/pdf/pdfCompressor.ts @@ -3,11 +3,21 @@ import tmp from "tmp"; import { ExtractedPdfInfo, extractTextFromPdf, hasSourceText } from "./pdfUtils.js"; import * as fs from "node:fs"; import type * as popplerTypes from "node-poppler"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { compressImage } from "@/image/index.js"; import { loadOptionalDependency } from "@/utils/index.js"; -const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); -const pdfLib = (pdfLibImport as any).default || pdfLibImport; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib() { + if (!pdfLib) { + const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); + pdfLib = (pdfLibImport as any).default || pdfLibImport; + } + return pdfLib; +} /** * Compresses each page of a provided PDF buffer. @@ -131,6 +141,7 @@ async function compressPagesWithQuality( disableSourceText: boolean, extractedText: ExtractedPdfInfo | null ): Promise { + const pdfLib = await getPdfLib(); const pdfDoc = await pdfLib.PDFDocument.load(pdfData, { ignoreEncryption: true, password: "" @@ -183,6 +194,7 @@ function isCompressionSuccessful(totalCompressedSize: number, originalSize: numb * @returns A Promise resolving to the new PDF as a Buffer. */ async function createNewPdfFromCompressedPages(compressedPages: Buffer[]): Promise { + const pdfLib = await getPdfLib(); const newPdfDoc = await pdfLib.PDFDocument.create(); for (const compressedPage of compressedPages) { @@ -207,6 +219,7 @@ async function addTextToPdfPage( if (textInfo === null) { return; } + const pdfLib = await getPdfLib(); for (const textPages of textInfo.pages) { for (const textPage of textPages.content) { page.drawText(textPage.str, { @@ -221,6 +234,7 @@ async function addTextToPdfPage( } async function getFontFromName(fontName: string): Promise { + const pdfLib = await getPdfLib(); const pdfDoc = await pdfLib.PDFDocument.create(); let font: pdfLibTypes.PDFFont; const standardFontValues = Object.values(pdfLib.StandardFonts) as string[]; diff --git a/src/pdf/pdfOperation.ts b/src/pdf/pdfOperation.ts index ac796bc1b..253e86e1d 100644 --- a/src/pdf/pdfOperation.ts +++ b/src/pdf/pdfOperation.ts @@ -1,11 +1,21 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { errorHandler } from "@/errors/handler.js"; import { PageOptions, PageOptionsOperation } from "@/input/pageOptions.js"; import { MindeeError } from "@/errors/index.js"; import { logger } from "@/logger.js"; import { loadOptionalDependency } from "@/utils/index.js"; -const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); -const pdfLib = (pdfLibImport as any).default || pdfLibImport; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib() { + if (!pdfLib) { + const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); + pdfLib = (pdfLibImport as any).default || pdfLibImport; + } + return pdfLib; +} export interface SplitPdf { file: Buffer; @@ -22,6 +32,7 @@ export async function extractPages( file: Buffer, pageOptions: PageOptions ): Promise { + const pdfLib = await getPdfLib(); const currentPdf = await pdfLib.PDFDocument.load(file, { ignoreEncryption: true, password: "" @@ -93,6 +104,7 @@ export async function extractPages( * @returns the number of pages in the file. */ export async function countPages(file: Buffer): Promise { + const pdfLib = await getPdfLib(); const currentPdf = await pdfLib.PDFDocument.load(file, { ignoreEncryption: true, password: "" diff --git a/src/pdf/pdfUtils.ts b/src/pdf/pdfUtils.ts index 50e2c96d5..2ace75c69 100644 --- a/src/pdf/pdfUtils.ts +++ b/src/pdf/pdfUtils.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error import type * as pdfJsExtractTypes from "pdf.js-extract"; import { MindeePdfError } from "@/errors/index.js"; import { loadOptionalDependency } from "@/utils/index.js"; @@ -54,9 +56,9 @@ export async function extractTextFromPdf(pdfBuffer: Buffer): Promise ({ + const pages = pdf.pages.map((page: pdfJsExtractTypes.PDFExtractPage, index: number) => ({ pageNumber: index + 1, - content: page.content.map(item => ({ + content: page.content.map((item: pdfJsExtractTypes.PDFExtractText) => ({ str: item.str, x: item.x, y: item.y, diff --git a/src/utils/optionalLoader.ts b/src/utils/optionalLoader.ts index 4d1644ef1..6b6e6467a 100644 --- a/src/utils/optionalLoader.ts +++ b/src/utils/optionalLoader.ts @@ -5,9 +5,14 @@ */ export async function loadOptionalDependency(packageName: string, featureName: string): Promise { try { - return await import(packageName); + return await (new Function("specifier", "return import(specifier)")(packageName)); } catch (error: any) { - if (error.code === "MODULE_NOT_FOUND" || error.message?.includes("Cannot find module")) { + if ( + error.code === "MODULE_NOT_FOUND" || + error.code === "ERR_MODULE_NOT_FOUND" || + error.message?.includes("Cannot find module") || + error.message?.includes("Cannot find package") + ) { throw new Error( `The feature '${featureName}' requires the optional dependency '${packageName}'. ` + `Please install it by running: npm install ${packageName}` diff --git a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts index c5c6dabaa..4ee08633d 100644 --- a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts +++ b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts @@ -1,15 +1,26 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { MindeeError, MindeeInputSourceError } from "@/errors/index.js"; import { InvoiceSplitterV1 } from "@/v1/product/index.js"; import { LocalInputSource } from "@/input/index.js"; import { ExtractedInvoiceSplitterImage } from "@/v1/extraction/index.js"; import { loadOptionalDependency } from "@/utils/index.js"; -const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); -const pdfLib = (pdfLibImport as any).default || pdfLibImport; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib() { + if (!pdfLib) { + const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); + pdfLib = (pdfLibImport as any).default || pdfLibImport; + } + return pdfLib; +} async function splitPdf( pdfDoc: pdfLibTypes.PDFDocument, invoicePageGroups: number[][]): Promise { + const pdfLib = await getPdfLib(); if (invoicePageGroups.length === 0) { return []; } @@ -35,6 +46,7 @@ async function splitPdf( } async function getPdfDoc(inputFile: LocalInputSource): Promise { + const pdfLib = await getPdfLib(); await inputFile.init(); if (!inputFile.isPdf()) { throw new MindeeInputSourceError( diff --git a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts index 20920a2c5..17c553a68 100644 --- a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts +++ b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { MindeeError, MindeeInputSourceError } from "@/errors/index.js"; import { Polygon } from "@/geometry/index.js"; @@ -7,8 +9,16 @@ import { LocalInputSource } from "@/input/index.js"; import { extractFromPage } from "@/image/index.js"; import { PositionField } from "@/v1/parsing/standard/index.js"; import { loadOptionalDependency } from "@/utils/index.js"; -const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); -const pdfLib = (pdfLibImport as any).default || pdfLibImport; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib() { + if (!pdfLib) { + const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); + pdfLib = (pdfLibImport as any).default || pdfLibImport; + } + return pdfLib; +} /** * Given a page and a set of coordinates, extracts & assigns individual receipts to an ExtractedMultiReceiptImage @@ -32,6 +42,7 @@ async function extractReceiptsFromPage( } async function loadPdfDoc(inputFile: LocalInputSource) { + const pdfLib = await getPdfLib(); let pdfDoc: pdfLibTypes.PDFDocument; if (!["image/jpeg", "image/jpg", "image/png", "application/pdf"].includes(inputFile.mimeType)) { throw new MindeeInputSourceError( @@ -70,6 +81,7 @@ export async function extractReceipts( inputFile: LocalInputSource, inference: MultiReceiptsDetectorV1 ): Promise { + const pdfLib = await getPdfLib(); const images: ExtractedMultiReceiptImage[] = []; if (!inference.prediction.receipts) { throw new MindeeError("No possible receipts candidates found for MultiReceipts extraction."); diff --git a/tests/input/compression.spec.ts b/tests/input/compression.spec.ts index 8359b08c2..932ce35bd 100644 --- a/tests/input/compression.spec.ts +++ b/tests/input/compression.spec.ts @@ -4,14 +4,13 @@ import { import * as fs from "fs"; import * as path from "path"; import { expect } from "chai"; -import sharp from "sharp"; import { compressImage } from "@/image/index.js"; import { compressPdf } from "@/pdf/index.js"; import { extractTextFromPdf } from "@/pdf/pdfUtils.js"; import { logger } from "@/logger.js"; import { RESOURCE_PATH, V1_PRODUCT_PATH } from "../index.js"; -describe("Input Sources - compression and resize", () => { +describe("Input Sources - compression and resize #extraDeps", () => { const outputPath = path.join(RESOURCE_PATH, "output"); before(async () => { @@ -66,7 +65,8 @@ describe("Input Sources - compression and resize", () => { const initialFileStats = await fs.promises.stat(path.join(RESOURCE_PATH, "file_types/receipt.jpg")); const renderedFileStats = await fs.promises.stat(path.join(outputPath, "resize_indirect.jpg")); expect(renderedFileStats.size).to.be.lessThan(initialFileStats.size); - const metadata = await sharp(imageResizeInput.fileObject).metadata(); + const sharp = await import("sharp"); + const metadata = await sharp.default(imageResizeInput.fileObject).metadata(); expect(metadata.width).to.equal(250); expect(metadata.height).to.equal(333); }); diff --git a/tests/input/pageOperations.spec.ts b/tests/input/pageOperations.spec.ts index cc8720e74..d2e516c70 100644 --- a/tests/input/pageOperations.spec.ts +++ b/tests/input/pageOperations.spec.ts @@ -8,7 +8,7 @@ import * as path from "path"; import { expect } from "chai"; import { RESOURCE_PATH } from "../index.js"; -describe("Input Sources - high level multi-page operations", () => { +describe("Input Sources - high level multi-page operations #extraDeps", () => { it("should cut a PDF", async () => { const input = new PathInput({ inputPath: path.join(RESOURCE_PATH, "file_types/pdf/multipage.pdf"), diff --git a/tests/input/sources.spec.ts b/tests/input/sources.spec.ts index ca4fd9d1a..5df8fc380 100644 --- a/tests/input/sources.spec.ts +++ b/tests/input/sources.spec.ts @@ -3,13 +3,13 @@ import { Base64Input, BufferInput, BytesInput, - PathInput, - StreamInput, INPUT_TYPE_BASE64, INPUT_TYPE_BUFFER, INPUT_TYPE_BYTES, INPUT_TYPE_PATH, INPUT_TYPE_STREAM, + PathInput, + StreamInput, } from "@/input/index.js"; import * as fs from "fs"; import * as path from "path"; @@ -234,7 +234,9 @@ describe("Input Sources: - load different types of input", () => { expect(inputSource.inputType).to.equals(INPUT_TYPE_BUFFER); expect(inputSource.filename).to.equals(filename); expect(inputSource.isPdf()).to.be.true; - expect(await inputSource.getPageCount()).to.equals(10); + it("#extraDeps", async () => { + expect(await inputSource.getPageCount()).to.equals(10); + }); expect(inputSource.fileObject).to.be.instanceOf(Buffer); }); diff --git a/tests/misc/missingDependencies.integration.ts b/tests/misc/missingDependencies.integration.ts new file mode 100644 index 000000000..49925ee80 --- /dev/null +++ b/tests/misc/missingDependencies.integration.ts @@ -0,0 +1,40 @@ +import * as mindee from "@/index.js"; +import { InvoiceSplitterV1 } from "@/v1/product/index.js"; +import { expect } from "chai"; +import path from "path"; +import { V1_PRODUCT_PATH } from "../index.js"; + +describe("Light Environment Sanity Check #lightDeps", function () { + let client: mindee.v1.Client; + + beforeEach(() => { + client = new mindee.v1.Client(); + }); + + it("should NOT be able to split invoices", async function () { + try { + const sample = new mindee.PathInput({ + inputPath: path.join(V1_PRODUCT_PATH, "invoice_splitter/default_sample.pdf") + }); + + const response = await client.enqueueAndParse( + mindee.v1.product.InvoiceSplitterV1, sample + ); + const invoiceSplitterInference = response.document?.inference; + expect(invoiceSplitterInference).to.be.an.instanceof(InvoiceSplitterV1); + await mindee.v1.extraction.extractInvoices( + sample, + invoiceSplitterInference as InvoiceSplitterV1 + ); + } catch (error: any) { + const isModuleNotFound = error.code === "ERR_MODULE_NOT_FOUND"; + const isBinaryMissing = error.message && error.message.includes("Could not load the \"@cantoo/pdf-lib\" module"); + const isOptionalDependencyMissing = error.message && + error.message.includes("requires the optional dependency '@cantoo/pdf-lib'"); + + if (!isModuleNotFound && !isBinaryMissing && !isOptionalDependencyMissing) { + throw error; + } + } + }).timeout(60000); +}); diff --git a/tests/misc/missingDependencies.spec.ts b/tests/misc/missingDependencies.spec.ts new file mode 100644 index 000000000..821c9dabc --- /dev/null +++ b/tests/misc/missingDependencies.spec.ts @@ -0,0 +1,42 @@ +import { expect } from "chai"; + +describe("Light Environment Sanity Check #lightDeps", function () { + + it("should NOT have sharp installed", async function () { + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + await import("sharp"); + expect.fail("sharp should not be installed in this environment, but it was found!"); + } catch (error: any) { + const isModuleNotFound = error.code === "ERR_MODULE_NOT_FOUND"; + const isSharpBinaryMissing = error.message && error.message.includes("Could not load the \"sharp\" module"); + + if (!isModuleNotFound && !isSharpBinaryMissing) { + throw error; + } + } + }); + + it("should NOT have pdf.js-extract installed", async function () { + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + await import("pdf.js-extract"); + expect.fail("pdf.js-extract should not be installed, but it was found!"); + } catch (error: any) { + expect(error.code).to.equal("ERR_MODULE_NOT_FOUND"); + } + }); + + it("should NOT have @cantoo/pdf-lib installed", async function () { + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + await import("@cantoo/pdf-lib"); + expect.fail("@cantoo/pdf-lib should not be installed, but it was found!"); + } catch (error: any) { + expect(error.code).to.equal("ERR_MODULE_NOT_FOUND"); + } + }); +}); diff --git a/tests/pdf/pdfOperation.spec.ts b/tests/pdf/pdfOperation.spec.ts index c632ee2fc..d7cdb881a 100644 --- a/tests/pdf/pdfOperation.spec.ts +++ b/tests/pdf/pdfOperation.spec.ts @@ -6,7 +6,7 @@ import { PageOptions, PageOptionsOperation } from "@/index.js"; import { PathInput } from "@/index.js"; import { RESOURCE_PATH } from "../index.js"; -describe("Test pdf operation", () => { +describe("Test pdf operation #extraDeps", () => { it("should cut a PDF to get 2 pages", async () => { const inputSource = new PathInput({ inputPath: path.join(RESOURCE_PATH, "file_types/pdf/multipage.pdf"), diff --git a/tests/pdf/pdfTypes.spec.ts b/tests/pdf/pdfTypes.spec.ts index dac586fb9..18c38f25f 100644 --- a/tests/pdf/pdfTypes.spec.ts +++ b/tests/pdf/pdfTypes.spec.ts @@ -5,7 +5,7 @@ import { PageOptions } from "@/input/index.js"; import { PageOptionsOperation, PathInput } from "@/index.js"; import { RESOURCE_PATH } from "../index.js"; -describe("Test pdf lib", () => { +describe("Test pdf lib #extraDeps", () => { it("should open a simple XFA form PDF.", async () => { const inputDoc = new PathInput( diff --git a/tests/v1/extraction/invoiceSplitterExtractor.spec.ts b/tests/v1/extraction/invoiceSplitterExtractor.spec.ts index d3d81a87e..bae0550b2 100644 --- a/tests/v1/extraction/invoiceSplitterExtractor.spec.ts +++ b/tests/v1/extraction/invoiceSplitterExtractor.spec.ts @@ -11,7 +11,7 @@ const dataPath = { fileSample: path.join(V1_PRODUCT_PATH, "invoice_splitter/invoice_5p.pdf"), }; -describe("A multi-page invoice document", () => { +describe("A multi-page invoice document #extraDeps", () => { it("should be split properly.", async () => { const jsonDataNA = await fs.readFile(path.resolve(dataPath.complete)); const response = JSON.parse(jsonDataNA.toString()); diff --git a/tests/v1/extraction/invoiceSplitterReconstruction.spec.ts b/tests/v1/extraction/invoiceSplitterReconstruction.spec.ts index d87f91066..71768ca50 100644 --- a/tests/v1/extraction/invoiceSplitterReconstruction.spec.ts +++ b/tests/v1/extraction/invoiceSplitterReconstruction.spec.ts @@ -7,8 +7,7 @@ import { extractInvoices } from "@/v1/extraction/index.js"; import { PathInput } from "@/index.js"; import { V1_PRODUCT_PATH } from "../../index.js"; -describe("MindeeV1 - A Multipage Invoice Document", () => { - +describe("MindeeV1 - A Multipage Invoice Document #extraDeps", () => { it("should be split into the proper invoices", async () => { const jsonData = await fs.readFile( path.join(V1_PRODUCT_PATH, "invoice_splitter/response_v1/complete.json") diff --git a/tests/v1/extraction/multiReceiptsExtractor.spec.ts b/tests/v1/extraction/multiReceiptsExtractor.spec.ts index 218da8f4e..d85f592c3 100644 --- a/tests/v1/extraction/multiReceiptsExtractor.spec.ts +++ b/tests/v1/extraction/multiReceiptsExtractor.spec.ts @@ -12,51 +12,52 @@ const dataPath = { completeMultiPage: path.join(V1_PRODUCT_PATH, "multi_receipts_detector/response_v1/multipage_sample.json"), multiPageSample: path.join(V1_PRODUCT_PATH, "multi_receipts_detector/multipage_sample.pdf"), }; - -describe("A single-page multi-receipts document", () => { - it("should be split properly.", async () => { - const jsonDataNA = await fs.readFile(path.resolve(dataPath.complete)); - const response = JSON.parse(jsonDataNA.toString()); - const doc = new MultiReceiptsDetectorV1(response.document.inference); - const inputSample = new PathInput({ inputPath: dataPath.fileSample }); - await inputSample.init(); - const extractedReceipts = await extractReceipts(inputSample, doc); - expect(extractedReceipts.length).to.be.equals(6); - for (let i = 0; i < extractedReceipts.length; i++) { - expect(extractedReceipts[i].buffer).to.be.not.null; - expect(extractedReceipts[i].pageId).to.be.equals(0); - expect(extractedReceipts[i].receiptId).to.be.equals(i); - } +describe("Multi-Receipt #extraDeps", () => { + describe("A single-page multi-receipts document", () => { + it("should be split properly.", async () => { + const jsonDataNA = await fs.readFile(path.resolve(dataPath.complete)); + const response = JSON.parse(jsonDataNA.toString()); + const doc = new MultiReceiptsDetectorV1(response.document.inference); + const inputSample = new PathInput({ inputPath: dataPath.fileSample }); + await inputSample.init(); + const extractedReceipts = await extractReceipts(inputSample, doc); + expect(extractedReceipts.length).to.be.equals(6); + for (let i = 0; i < extractedReceipts.length; i++) { + expect(extractedReceipts[i].buffer).to.be.not.null; + expect(extractedReceipts[i].pageId).to.be.equals(0); + expect(extractedReceipts[i].receiptId).to.be.equals(i); + } + }); }); -}); -describe("A multi-page multi-receipts document", () => { - it("should be split properly.", async () => { - const jsonDataNA = await fs.readFile(path.resolve(dataPath.completeMultiPage)); - const response = JSON.parse(jsonDataNA.toString()); - const doc = new MultiReceiptsDetectorV1(response.document.inference); - const inputSample = new PathInput({ inputPath: dataPath.multiPageSample }); - await inputSample.init(); - const extractedReceipts = await extractReceipts(inputSample, doc); - expect(extractedReceipts.length).to.be.equals(5); + describe("A multi-page multi-receipts document", () => { + it("should be split properly.", async () => { + const jsonDataNA = await fs.readFile(path.resolve(dataPath.completeMultiPage)); + const response = JSON.parse(jsonDataNA.toString()); + const doc = new MultiReceiptsDetectorV1(response.document.inference); + const inputSample = new PathInput({ inputPath: dataPath.multiPageSample }); + await inputSample.init(); + const extractedReceipts = await extractReceipts(inputSample, doc); + expect(extractedReceipts.length).to.be.equals(5); - expect(extractedReceipts[0].buffer).to.be.not.null; - expect(extractedReceipts[0].pageId).to.be.equals(0); - expect(extractedReceipts[0].receiptId).to.be.equals(0); + expect(extractedReceipts[0].buffer).to.be.not.null; + expect(extractedReceipts[0].pageId).to.be.equals(0); + expect(extractedReceipts[0].receiptId).to.be.equals(0); - expect(extractedReceipts[1].buffer).to.be.not.null; - expect(extractedReceipts[1].pageId).to.be.equals(0); - expect(extractedReceipts[1].receiptId).to.be.equals(1); + expect(extractedReceipts[1].buffer).to.be.not.null; + expect(extractedReceipts[1].pageId).to.be.equals(0); + expect(extractedReceipts[1].receiptId).to.be.equals(1); - expect(extractedReceipts[2].buffer).to.be.not.null; - expect(extractedReceipts[2].pageId).to.be.equals(0); - expect(extractedReceipts[2].receiptId).to.be.equals(2); + expect(extractedReceipts[2].buffer).to.be.not.null; + expect(extractedReceipts[2].pageId).to.be.equals(0); + expect(extractedReceipts[2].receiptId).to.be.equals(2); - expect(extractedReceipts[3].buffer).to.be.not.null; - expect(extractedReceipts[3].pageId).to.be.equals(1); - expect(extractedReceipts[3].receiptId).to.be.equals(0); + expect(extractedReceipts[3].buffer).to.be.not.null; + expect(extractedReceipts[3].pageId).to.be.equals(1); + expect(extractedReceipts[3].receiptId).to.be.equals(0); - expect(extractedReceipts[4].buffer).to.be.not.null; - expect(extractedReceipts[4].pageId).to.be.equals(1); - expect(extractedReceipts[4].receiptId).to.be.equals(1); + expect(extractedReceipts[4].buffer).to.be.not.null; + expect(extractedReceipts[4].pageId).to.be.equals(1); + expect(extractedReceipts[4].receiptId).to.be.equals(1); + }); }); }); diff --git a/tests/v1/extraction/multiReceiptsReconstruction.integration.ts b/tests/v1/extraction/multiReceiptsReconstruction.integration.ts index 04b77c00d..385ae3109 100644 --- a/tests/v1/extraction/multiReceiptsReconstruction.integration.ts +++ b/tests/v1/extraction/multiReceiptsReconstruction.integration.ts @@ -10,124 +10,125 @@ import { setTimeout } from "node:timers/promises"; const apiKey = process.env.MINDEE_API_KEY; let client: Client; let sourceDoc: LocalInputSource; - -describe("MindeeV1 - A Multi-Receipt Image", () => { - before(async () => { - sourceDoc = new PathInput({ - inputPath: path.join(V1_PRODUCT_PATH, "multi_receipts_detector/default_sample.jpg"), +describe("MindeeV1 - #extraDeps", () => { + describe("MindeeV1 - A Multi-Receipt Image", () => { + before(async () => { + sourceDoc = new PathInput({ + inputPath: path.join(V1_PRODUCT_PATH, "multi_receipts_detector/default_sample.jpg"), + }); + await sourceDoc.init(); + client = new Client({ apiKey }); }); - await sourceDoc.init(); - client = new Client({ apiKey }); - }); - // NOTE: rotation causes flakiness in receipt order, causing the test to fail. - // it("should send to the server and cut properly", async () => { - // const multiReceiptResult = await client.parse(MultiReceiptsDetectorV1, sourceDoc); - // expect(multiReceiptResult.document?.inference.prediction.receipts.length).to.be.equals(6); - // expect(multiReceiptResult.document?.inference.pages[0].orientation?.value).to.be.equals(90); - // const receipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); - // expect(receipts.length).to.be.equals(6); - // const extractedReceipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); - // expect(extractedReceipts.length).to.be.equals(6); - // const receiptsResults = []; - // for (const extractedReceipt of extractedReceipts) { - // const localInput = extractedReceipt.asSource(); - // receiptsResults.push(await client.parse(ReceiptV5, localInput)); - // await setTimeout(1000); - // } - // - // expect(receiptsResults[0].document.inference.prediction.lineItems.length).to.be.equals(0); - // - // expect(receiptsResults[1].document.inference.prediction.lineItems.length).to.be.equals(1); - // expect(receiptsResults[1].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(21.5); - // - // expect(receiptsResults[2].document.inference.prediction.lineItems.length).to.be.equals(2); - // expect(receiptsResults[2].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(11.5); - // expect(receiptsResults[2].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(2); - // - // expect(receiptsResults[3].document.inference.prediction.lineItems.length).to.be.equals(1); - // expect(receiptsResults[3].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(16.5); - // - // expect(receiptsResults[4].document.inference.prediction.lineItems.length).to.be.equals(2); - // expect(receiptsResults[4].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(10.5); - // expect(receiptsResults[4].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(4); - // - // expect(receiptsResults[5].document.inference.prediction.lineItems.length).to.be.equals(0); - // }).timeout(60000); -}); + // NOTE: rotation causes flakiness in receipt order, causing the test to fail. + // it("should send to the server and cut properly", async () => { + // const multiReceiptResult = await client.parse(MultiReceiptsDetectorV1, sourceDoc); + // expect(multiReceiptResult.document?.inference.prediction.receipts.length).to.be.equals(6); + // expect(multiReceiptResult.document?.inference.pages[0].orientation?.value).to.be.equals(90); + // const receipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); + // expect(receipts.length).to.be.equals(6); + // const extractedReceipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); + // expect(extractedReceipts.length).to.be.equals(6); + // const receiptsResults = []; + // for (const extractedReceipt of extractedReceipts) { + // const localInput = extractedReceipt.asSource(); + // receiptsResults.push(await client.parse(ReceiptV5, localInput)); + // await setTimeout(1000); + // } + // + // expect(receiptsResults[0].document.inference.prediction.lineItems.length).to.be.equals(0); + // + // expect(receiptsResults[1].document.inference.prediction.lineItems.length).to.be.equals(1); + // expect(receiptsResults[1].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(21.5); + // + // expect(receiptsResults[2].document.inference.prediction.lineItems.length).to.be.equals(2); + // expect(receiptsResults[2].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(11.5); + // expect(receiptsResults[2].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(2); + // + // expect(receiptsResults[3].document.inference.prediction.lineItems.length).to.be.equals(1); + // expect(receiptsResults[3].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(16.5); + // + // expect(receiptsResults[4].document.inference.prediction.lineItems.length).to.be.equals(2); + // expect(receiptsResults[4].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(10.5); + // expect(receiptsResults[4].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(4); + // + // expect(receiptsResults[5].document.inference.prediction.lineItems.length).to.be.equals(0); + // }).timeout(60000); + }); -describe("MindeeV1 - A Multi-Receipt Document", () => { - before(async () => { - sourceDoc = new PathInput({ - inputPath: path.join(V1_PRODUCT_PATH, "multi_receipts_detector/multipage_sample.pdf"), + describe("MindeeV1 - A Multi-Receipt Document", () => { + before(async () => { + sourceDoc = new PathInput({ + inputPath: path.join(V1_PRODUCT_PATH, "multi_receipts_detector/multipage_sample.pdf"), + }); + await sourceDoc.init(); + client = new Client({ apiKey }); }); - await sourceDoc.init(); - client = new Client({ apiKey }); - }); - it("should send to the server and cut properly", async () => { - const multiReceiptResult = await client.parse(MultiReceiptsDetectorV1, sourceDoc); - expect(multiReceiptResult.document?.inference.prediction.receipts.length).to.be.equals(5); - const extractedReceipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); - expect(extractedReceipts.length).to.be.equals(5); - expect(multiReceiptResult.document?.inference.pages[0].orientation?.value).to.be.equals(0); - expect(multiReceiptResult.document?.inference.pages[1].orientation?.value).to.be.equals(0); - const receiptsResults = []; - for (const extractedReceipt of extractedReceipts) { - const localInput = extractedReceipt.asSource(); - receiptsResults.push(await client.parse(ReceiptV5, localInput)); - await setTimeout(1000); - } - expect(receiptsResults[0].document.inference.prediction.lineItems.length).to.be.equals(5); - expect(receiptsResults[0].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(70); - expect(receiptsResults[0].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(12); - expect(receiptsResults[0].document.inference.prediction.lineItems[2].totalAmount).to.be.equals(14); - expect(receiptsResults[0].document.inference.prediction.lineItems[3].totalAmount).to.be.equals(11); - expect(receiptsResults[0].document.inference.prediction.lineItems[4].totalAmount).to.be.equals(5.6); + it("should send to the server and cut properly", async () => { + const multiReceiptResult = await client.parse(MultiReceiptsDetectorV1, sourceDoc); + expect(multiReceiptResult.document?.inference.prediction.receipts.length).to.be.equals(5); + const extractedReceipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); + expect(extractedReceipts.length).to.be.equals(5); + expect(multiReceiptResult.document?.inference.pages[0].orientation?.value).to.be.equals(0); + expect(multiReceiptResult.document?.inference.pages[1].orientation?.value).to.be.equals(0); + const receiptsResults = []; + for (const extractedReceipt of extractedReceipts) { + const localInput = extractedReceipt.asSource(); + receiptsResults.push(await client.parse(ReceiptV5, localInput)); + await setTimeout(1000); + } + expect(receiptsResults[0].document.inference.prediction.lineItems.length).to.be.equals(5); + expect(receiptsResults[0].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(70); + expect(receiptsResults[0].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(12); + expect(receiptsResults[0].document.inference.prediction.lineItems[2].totalAmount).to.be.equals(14); + expect(receiptsResults[0].document.inference.prediction.lineItems[3].totalAmount).to.be.equals(11); + expect(receiptsResults[0].document.inference.prediction.lineItems[4].totalAmount).to.be.equals(5.6); - expect(receiptsResults[1].document.inference.prediction.lineItems.length).to.be.equals(7); - expect(receiptsResults[1].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(6); - expect(receiptsResults[1].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(11); - expect(receiptsResults[1].document.inference.prediction.lineItems[2].totalAmount).to.be.equals(67.2); - expect(receiptsResults[1].document.inference.prediction.lineItems[3].totalAmount).to.be.equals(19.2); - expect(receiptsResults[1].document.inference.prediction.lineItems[4].totalAmount).to.be.equals(7); - expect(receiptsResults[1].document.inference.prediction.lineItems[5].totalAmount).to.be.equals(5.5); - expect(receiptsResults[1].document.inference.prediction.lineItems[6].totalAmount).to.be.equals(36); + expect(receiptsResults[1].document.inference.prediction.lineItems.length).to.be.equals(7); + expect(receiptsResults[1].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(6); + expect(receiptsResults[1].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(11); + expect(receiptsResults[1].document.inference.prediction.lineItems[2].totalAmount).to.be.equals(67.2); + expect(receiptsResults[1].document.inference.prediction.lineItems[3].totalAmount).to.be.equals(19.2); + expect(receiptsResults[1].document.inference.prediction.lineItems[4].totalAmount).to.be.equals(7); + expect(receiptsResults[1].document.inference.prediction.lineItems[5].totalAmount).to.be.equals(5.5); + expect(receiptsResults[1].document.inference.prediction.lineItems[6].totalAmount).to.be.equals(36); - expect(receiptsResults[2].document.inference.prediction.lineItems.length).to.be.equals(1); - expect(receiptsResults[2].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(275); + expect(receiptsResults[2].document.inference.prediction.lineItems.length).to.be.equals(1); + expect(receiptsResults[2].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(275); - expect(receiptsResults[3].document.inference.prediction.lineItems.length).to.be.equals(2); - expect(receiptsResults[3].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(11.5); - expect(receiptsResults[3].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(2); + expect(receiptsResults[3].document.inference.prediction.lineItems.length).to.be.equals(2); + expect(receiptsResults[3].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(11.5); + expect(receiptsResults[3].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(2); - expect(receiptsResults[4].document.inference.prediction.lineItems.length).to.be.equals(1); - expect(receiptsResults[4].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(16.5); + expect(receiptsResults[4].document.inference.prediction.lineItems.length).to.be.equals(1); + expect(receiptsResults[4].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(16.5); - }).timeout(60000); -}); + }).timeout(60000); + }); -describe("MindeeV1 - A Single-Receipt Image", () => { - before(async () => { - sourceDoc = new PathInput({ - inputPath: path.join(V1_PRODUCT_PATH, "expense_receipts/default_sample.jpg"), + describe("MindeeV1 - A Single-Receipt Image", () => { + before(async () => { + sourceDoc = new PathInput({ + inputPath: path.join(V1_PRODUCT_PATH, "expense_receipts/default_sample.jpg"), + }); + await sourceDoc.init(); + client = new Client({ apiKey }); }); - await sourceDoc.init(); - client = new Client({ apiKey }); - }); - it("should send to the server and cut properly", async () => { - const multiReceiptResult = await client.parse(MultiReceiptsDetectorV1, sourceDoc); - expect(multiReceiptResult.document?.inference.prediction.receipts.length).to.be.equals(1); - const receipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); - expect(receipts.length).to.be.equals(1); - const receiptResult = await client.parse(ReceiptV5, receipts[0].asSource()); - expect(receiptResult.document.inference.prediction.lineItems.length).to.be.equals(1); - expect(receiptResult.document.inference.prediction.lineItems[0].totalAmount).to.be.equals(10.2); - expect(receiptResult.document.inference.prediction.taxes.length).to.be.equals(1); - expect(receiptResult.document.inference.prediction.taxes[0].value).to.be.equals(1.7); - }).timeout(60000); + it("should send to the server and cut properly", async () => { + const multiReceiptResult = await client.parse(MultiReceiptsDetectorV1, sourceDoc); + expect(multiReceiptResult.document?.inference.prediction.receipts.length).to.be.equals(1); + const receipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); + expect(receipts.length).to.be.equals(1); + const receiptResult = await client.parse(ReceiptV5, receipts[0].asSource()); + expect(receiptResult.document.inference.prediction.lineItems.length).to.be.equals(1); + expect(receiptResult.document.inference.prediction.lineItems[0].totalAmount).to.be.equals(10.2); + expect(receiptResult.document.inference.prediction.taxes.length).to.be.equals(1); + expect(receiptResult.document.inference.prediction.taxes[0].value).to.be.equals(1.7); + }).timeout(60000); + }); }); diff --git a/tests/v1/extraction/multiReceiptsReconstruction.spec.ts b/tests/v1/extraction/multiReceiptsReconstruction.spec.ts index 3cc3efec1..aca5ac26d 100644 --- a/tests/v1/extraction/multiReceiptsReconstruction.spec.ts +++ b/tests/v1/extraction/multiReceiptsReconstruction.spec.ts @@ -15,7 +15,7 @@ const rotations = [ ]; rotations.forEach(({ angle, suffix }) => { - describe(`Multi-Receipt Document - ${angle}° rotation`, () => { + describe(`Multi-Receipt Document - ${angle}° rotation #extraDeps`, () => { let extractedReceipts: any[]; let sourceDoc: PathInput; diff --git a/tests/v1/product/invoiceSplitter/invoiceSplitter.integration.ts b/tests/v1/product/invoiceSplitter/invoiceSplitter.integration.ts index 4e497ed15..c0daa894e 100644 --- a/tests/v1/product/invoiceSplitter/invoiceSplitter.integration.ts +++ b/tests/v1/product/invoiceSplitter/invoiceSplitter.integration.ts @@ -6,7 +6,7 @@ import { promises as fs } from "fs"; import path from "path"; import { V1_PRODUCT_PATH } from "../../../index.js"; -describe("MindeeV1 - InvoiceSplitterV1 Integration Tests", async () => { +describe("MindeeV1 - InvoiceSplitterV1 Integration Tests #extraDeps", async () => { let client: mindee.v1.Client; beforeEach(() => { From 3a98eaad76ab8972d6cc6bf5356fe5d6a63d1806 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:43:29 +0100 Subject: [PATCH 3/6] fix lint, comments & tests --- src/image/imageCompressor.ts | 2 +- src/image/imageExtractor.ts | 6 ++-- src/pdf/pdfCompressor.ts | 6 ++-- src/pdf/pdfOperation.ts | 6 ++-- src/pdf/pdfUtils.ts | 2 +- .../invoiceSplitterExtractor.ts | 6 ++-- .../multiReceiptsExtractor.ts | 6 ++-- tests/misc/missingDependencies.spec.ts | 15 ++++---- ...multiReceiptsReconstruction.integration.ts | 35 ------------------- 9 files changed, 23 insertions(+), 61 deletions(-) diff --git a/src/image/imageCompressor.ts b/src/image/imageCompressor.ts index d49d46069..c927d4ed1 100644 --- a/src/image/imageCompressor.ts +++ b/src/image/imageCompressor.ts @@ -1,6 +1,6 @@ import { loadOptionalDependency } from "@/utils/index.js"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error +// @ts-ignore import type * as SharpTypes from "sharp"; import { MindeeImageError } from "@/errors/index.js"; diff --git a/src/image/imageExtractor.ts b/src/image/imageExtractor.ts index 6401698e6..e1af2c516 100644 --- a/src/image/imageExtractor.ts +++ b/src/image/imageExtractor.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error +// @ts-ignore import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { getMinMaxX, getMinMaxY, Polygon } from "@/geometry/index.js"; import { adjustForRotation } from "@/geometry/polygonUtils.js"; @@ -7,12 +7,12 @@ import { loadOptionalDependency } from "@/utils/index.js"; let pdfLib: typeof pdfLibTypes | null = null; -async function getPdfLib() { +async function getPdfLib(): Promise { if (!pdfLib) { const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); pdfLib = (pdfLibImport as any).default || pdfLibImport; } - return pdfLib; + return pdfLib!; } /** diff --git a/src/pdf/pdfCompressor.ts b/src/pdf/pdfCompressor.ts index 9d0662ded..800692552 100644 --- a/src/pdf/pdfCompressor.ts +++ b/src/pdf/pdfCompressor.ts @@ -4,19 +4,19 @@ import { ExtractedPdfInfo, extractTextFromPdf, hasSourceText } from "./pdfUtils. import * as fs from "node:fs"; import type * as popplerTypes from "node-poppler"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error +// @ts-ignore import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { compressImage } from "@/image/index.js"; import { loadOptionalDependency } from "@/utils/index.js"; let pdfLib: typeof pdfLibTypes | null = null; -async function getPdfLib() { +async function getPdfLib(): Promise { if (!pdfLib) { const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); pdfLib = (pdfLibImport as any).default || pdfLibImport; } - return pdfLib; + return pdfLib!; } /** diff --git a/src/pdf/pdfOperation.ts b/src/pdf/pdfOperation.ts index 253e86e1d..467303656 100644 --- a/src/pdf/pdfOperation.ts +++ b/src/pdf/pdfOperation.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error +// @ts-ignore import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { errorHandler } from "@/errors/handler.js"; import { PageOptions, PageOptionsOperation } from "@/input/pageOptions.js"; @@ -9,12 +9,12 @@ import { loadOptionalDependency } from "@/utils/index.js"; let pdfLib: typeof pdfLibTypes | null = null; -async function getPdfLib() { +async function getPdfLib(): Promise { if (!pdfLib) { const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); pdfLib = (pdfLibImport as any).default || pdfLibImport; } - return pdfLib; + return pdfLib!; } export interface SplitPdf { diff --git a/src/pdf/pdfUtils.ts b/src/pdf/pdfUtils.ts index 2ace75c69..c09a989a2 100644 --- a/src/pdf/pdfUtils.ts +++ b/src/pdf/pdfUtils.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error +// @ts-ignore import type * as pdfJsExtractTypes from "pdf.js-extract"; import { MindeePdfError } from "@/errors/index.js"; import { loadOptionalDependency } from "@/utils/index.js"; diff --git a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts index 4ee08633d..e2059e14b 100644 --- a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts +++ b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error +// @ts-ignore import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { MindeeError, MindeeInputSourceError } from "@/errors/index.js"; import { InvoiceSplitterV1 } from "@/v1/product/index.js"; @@ -9,12 +9,12 @@ import { loadOptionalDependency } from "@/utils/index.js"; let pdfLib: typeof pdfLibTypes | null = null; -async function getPdfLib() { +async function getPdfLib(): Promise { if (!pdfLib) { const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); pdfLib = (pdfLibImport as any).default || pdfLibImport; } - return pdfLib; + return pdfLib!; } async function splitPdf( diff --git a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts index 17c553a68..56388e9ec 100644 --- a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts +++ b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error +// @ts-ignore import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { MindeeError, MindeeInputSourceError } from "@/errors/index.js"; import { Polygon } from "@/geometry/index.js"; @@ -12,12 +12,12 @@ import { loadOptionalDependency } from "@/utils/index.js"; let pdfLib: typeof pdfLibTypes | null = null; -async function getPdfLib() { +async function getPdfLib(): Promise { if (!pdfLib) { const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); pdfLib = (pdfLibImport as any).default || pdfLibImport; } - return pdfLib; + return pdfLib!; } /** diff --git a/tests/misc/missingDependencies.spec.ts b/tests/misc/missingDependencies.spec.ts index 821c9dabc..3b6ee0290 100644 --- a/tests/misc/missingDependencies.spec.ts +++ b/tests/misc/missingDependencies.spec.ts @@ -4,9 +4,8 @@ describe("Light Environment Sanity Check #lightDeps", function () { it("should NOT have sharp installed", async function () { try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - await import("sharp"); + const moduleName = "sharp"; + await import(moduleName); expect.fail("sharp should not be installed in this environment, but it was found!"); } catch (error: any) { const isModuleNotFound = error.code === "ERR_MODULE_NOT_FOUND"; @@ -20,9 +19,8 @@ describe("Light Environment Sanity Check #lightDeps", function () { it("should NOT have pdf.js-extract installed", async function () { try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - await import("pdf.js-extract"); + const moduleName = "pdf.js-extract"; + await import(moduleName); expect.fail("pdf.js-extract should not be installed, but it was found!"); } catch (error: any) { expect(error.code).to.equal("ERR_MODULE_NOT_FOUND"); @@ -31,9 +29,8 @@ describe("Light Environment Sanity Check #lightDeps", function () { it("should NOT have @cantoo/pdf-lib installed", async function () { try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - await import("@cantoo/pdf-lib"); + const moduleName = "@cantoo/pdf-lib"; + await import(moduleName); expect.fail("@cantoo/pdf-lib should not be installed, but it was found!"); } catch (error: any) { expect(error.code).to.equal("ERR_MODULE_NOT_FOUND"); diff --git a/tests/v1/extraction/multiReceiptsReconstruction.integration.ts b/tests/v1/extraction/multiReceiptsReconstruction.integration.ts index 385ae3109..5e893656a 100644 --- a/tests/v1/extraction/multiReceiptsReconstruction.integration.ts +++ b/tests/v1/extraction/multiReceiptsReconstruction.integration.ts @@ -19,41 +19,6 @@ describe("MindeeV1 - #extraDeps", () => { await sourceDoc.init(); client = new Client({ apiKey }); }); - - // NOTE: rotation causes flakiness in receipt order, causing the test to fail. - // it("should send to the server and cut properly", async () => { - // const multiReceiptResult = await client.parse(MultiReceiptsDetectorV1, sourceDoc); - // expect(multiReceiptResult.document?.inference.prediction.receipts.length).to.be.equals(6); - // expect(multiReceiptResult.document?.inference.pages[0].orientation?.value).to.be.equals(90); - // const receipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); - // expect(receipts.length).to.be.equals(6); - // const extractedReceipts = await extractReceipts(sourceDoc, multiReceiptResult.document!.inference); - // expect(extractedReceipts.length).to.be.equals(6); - // const receiptsResults = []; - // for (const extractedReceipt of extractedReceipts) { - // const localInput = extractedReceipt.asSource(); - // receiptsResults.push(await client.parse(ReceiptV5, localInput)); - // await setTimeout(1000); - // } - // - // expect(receiptsResults[0].document.inference.prediction.lineItems.length).to.be.equals(0); - // - // expect(receiptsResults[1].document.inference.prediction.lineItems.length).to.be.equals(1); - // expect(receiptsResults[1].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(21.5); - // - // expect(receiptsResults[2].document.inference.prediction.lineItems.length).to.be.equals(2); - // expect(receiptsResults[2].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(11.5); - // expect(receiptsResults[2].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(2); - // - // expect(receiptsResults[3].document.inference.prediction.lineItems.length).to.be.equals(1); - // expect(receiptsResults[3].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(16.5); - // - // expect(receiptsResults[4].document.inference.prediction.lineItems.length).to.be.equals(2); - // expect(receiptsResults[4].document.inference.prediction.lineItems[0].totalAmount).to.be.equals(10.5); - // expect(receiptsResults[4].document.inference.prediction.lineItems[1].totalAmount).to.be.equals(4); - // - // expect(receiptsResults[5].document.inference.prediction.lineItems.length).to.be.equals(0); - // }).timeout(60000); }); From f294df28d421f208ff61f1d1a16617e0471f5431 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:54:27 +0100 Subject: [PATCH 4/6] apply fixes --- package.json | 16 ++++++++-------- src/utils/optionalLoader.ts | 2 +- tests/input/compression.spec.ts | 2 +- tests/input/pageOperations.spec.ts | 2 +- tests/input/sources.spec.ts | 2 +- tests/misc/missingDependencies.integration.ts | 2 +- tests/misc/missingDependencies.spec.ts | 2 +- tests/pdf/pdfOperation.spec.ts | 2 +- tests/pdf/pdfTypes.spec.ts | 2 +- .../extraction/invoiceSplitterExtractor.spec.ts | 2 +- .../invoiceSplitterReconstruction.spec.ts | 2 +- .../v1/extraction/multiReceiptsExtractor.spec.ts | 2 +- .../multiReceiptsReconstruction.integration.ts | 2 +- .../multiReceiptsReconstruction.spec.ts | 2 +- .../invoiceSplitter.integration.ts | 2 +- 15 files changed, 22 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 9557b18d7..7250ec09c 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,14 @@ "build": "tsc --build && tsc-alias", "build-for-dist": "tsc --build && tsc-alias && cp LICENSE README.md CHANGELOG.md ./dist", "clean": "rm -rf ./dist ./docs/_build", - "test": "mocha --grep \"#lightDeps\" --invert \"tests/**/*.spec.ts\"", - "test-light": "mocha --grep \"#extraDeps\" --invert \"tests/**/*.spec.ts\"", - "test-integration": "mocha --grep \"#lightDeps\" --invert \"tests/**/*.integration.ts\"", - "test-integration-light": "mocha --grep \"#extraDeps\" --invert \"tests/**/*.integration.ts\"", - "test-v1": "mocha --grep \"#lightDeps\" --invert \"tests/v1/**/*.spec.ts\"", - "test-v1-light": "mocha --grep \"#extraDeps\" --invert \"tests/v1/**/*.spec.ts\"", - "test-v2": "mocha --grep \"#lightDeps\" --invert \"tests/v2/**/*.spec.ts\"", - "test-v2-light": "mocha --grep \"#extraDeps\" --invert \"tests/v2/**/*.spec.ts\"", + "test": "mocha --grep \"#omitOptionalDeps\" --invert \"tests/**/*.spec.ts\"", + "test-light": "mocha --grep \"#includeOptionalDeps\" --invert \"tests/**/*.spec.ts\"", + "test-integration": "mocha --grep \"#omitOptionalDeps\" --invert \"tests/**/*.integration.ts\"", + "test-integration-light": "mocha --grep \"#includeOptionalDeps\" --invert \"tests/**/*.integration.ts\"", + "test-v1": "mocha --grep \"#omitOptionalDeps\" --invert \"tests/v1/**/*.spec.ts\"", + "test-v1-light": "mocha --grep \"#includeOptionalDeps\" --invert \"tests/v1/**/*.spec.ts\"", + "test-v2": "mocha --grep \"#omitOptionalDeps\" --invert \"tests/v2/**/*.spec.ts\"", + "test-v2-light": "mocha --grep \"#includeOptionalDeps\" --invert \"tests/v2/**/*.spec.ts\"", "lint": "tsc --noEmit && eslint './src/**/*.ts' --report-unused-disable-directives && echo 'Your .ts files look good.'", "lint-fix": "eslint './src/**/*.ts' --fix", "docs": "typedoc --out docs/_build ./src/index.ts", diff --git a/src/utils/optionalLoader.ts b/src/utils/optionalLoader.ts index 6b6e6467a..1d3a831f6 100644 --- a/src/utils/optionalLoader.ts +++ b/src/utils/optionalLoader.ts @@ -15,7 +15,7 @@ export async function loadOptionalDependency(packageName: string, featureName ) { throw new Error( `The feature '${featureName}' requires the optional dependency '${packageName}'. ` + - `Please install it by running: npm install ${packageName}` + "Please install optional dependencies: `npm install --include=optional`" ); } throw error; diff --git a/tests/input/compression.spec.ts b/tests/input/compression.spec.ts index 932ce35bd..a08f134c0 100644 --- a/tests/input/compression.spec.ts +++ b/tests/input/compression.spec.ts @@ -10,7 +10,7 @@ import { extractTextFromPdf } from "@/pdf/pdfUtils.js"; import { logger } from "@/logger.js"; import { RESOURCE_PATH, V1_PRODUCT_PATH } from "../index.js"; -describe("Input Sources - compression and resize #extraDeps", () => { +describe("Input Sources - compression and resize #includeOptionalDeps", () => { const outputPath = path.join(RESOURCE_PATH, "output"); before(async () => { diff --git a/tests/input/pageOperations.spec.ts b/tests/input/pageOperations.spec.ts index d2e516c70..790843660 100644 --- a/tests/input/pageOperations.spec.ts +++ b/tests/input/pageOperations.spec.ts @@ -8,7 +8,7 @@ import * as path from "path"; import { expect } from "chai"; import { RESOURCE_PATH } from "../index.js"; -describe("Input Sources - high level multi-page operations #extraDeps", () => { +describe("Input Sources - high level multi-page operations #includeOptionalDeps", () => { it("should cut a PDF", async () => { const input = new PathInput({ inputPath: path.join(RESOURCE_PATH, "file_types/pdf/multipage.pdf"), diff --git a/tests/input/sources.spec.ts b/tests/input/sources.spec.ts index 5df8fc380..672e8d3af 100644 --- a/tests/input/sources.spec.ts +++ b/tests/input/sources.spec.ts @@ -234,7 +234,7 @@ describe("Input Sources: - load different types of input", () => { expect(inputSource.inputType).to.equals(INPUT_TYPE_BUFFER); expect(inputSource.filename).to.equals(filename); expect(inputSource.isPdf()).to.be.true; - it("#extraDeps", async () => { + it("#includeOptionalDeps", async () => { expect(await inputSource.getPageCount()).to.equals(10); }); expect(inputSource.fileObject).to.be.instanceOf(Buffer); diff --git a/tests/misc/missingDependencies.integration.ts b/tests/misc/missingDependencies.integration.ts index 49925ee80..9494d461c 100644 --- a/tests/misc/missingDependencies.integration.ts +++ b/tests/misc/missingDependencies.integration.ts @@ -4,7 +4,7 @@ import { expect } from "chai"; import path from "path"; import { V1_PRODUCT_PATH } from "../index.js"; -describe("Light Environment Sanity Check #lightDeps", function () { +describe("Light Environment Sanity Check #omitOptionalDeps", function () { let client: mindee.v1.Client; beforeEach(() => { diff --git a/tests/misc/missingDependencies.spec.ts b/tests/misc/missingDependencies.spec.ts index 3b6ee0290..cfc135a20 100644 --- a/tests/misc/missingDependencies.spec.ts +++ b/tests/misc/missingDependencies.spec.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; -describe("Light Environment Sanity Check #lightDeps", function () { +describe("Light Environment Sanity Check #omitOptionalDeps", function () { it("should NOT have sharp installed", async function () { try { diff --git a/tests/pdf/pdfOperation.spec.ts b/tests/pdf/pdfOperation.spec.ts index d7cdb881a..4a00c7b6e 100644 --- a/tests/pdf/pdfOperation.spec.ts +++ b/tests/pdf/pdfOperation.spec.ts @@ -6,7 +6,7 @@ import { PageOptions, PageOptionsOperation } from "@/index.js"; import { PathInput } from "@/index.js"; import { RESOURCE_PATH } from "../index.js"; -describe("Test pdf operation #extraDeps", () => { +describe("Test pdf operation #includeOptionalDeps", () => { it("should cut a PDF to get 2 pages", async () => { const inputSource = new PathInput({ inputPath: path.join(RESOURCE_PATH, "file_types/pdf/multipage.pdf"), diff --git a/tests/pdf/pdfTypes.spec.ts b/tests/pdf/pdfTypes.spec.ts index 18c38f25f..4bd6238c2 100644 --- a/tests/pdf/pdfTypes.spec.ts +++ b/tests/pdf/pdfTypes.spec.ts @@ -5,7 +5,7 @@ import { PageOptions } from "@/input/index.js"; import { PageOptionsOperation, PathInput } from "@/index.js"; import { RESOURCE_PATH } from "../index.js"; -describe("Test pdf lib #extraDeps", () => { +describe("Test pdf lib #includeOptionalDeps", () => { it("should open a simple XFA form PDF.", async () => { const inputDoc = new PathInput( diff --git a/tests/v1/extraction/invoiceSplitterExtractor.spec.ts b/tests/v1/extraction/invoiceSplitterExtractor.spec.ts index bae0550b2..d79c63014 100644 --- a/tests/v1/extraction/invoiceSplitterExtractor.spec.ts +++ b/tests/v1/extraction/invoiceSplitterExtractor.spec.ts @@ -11,7 +11,7 @@ const dataPath = { fileSample: path.join(V1_PRODUCT_PATH, "invoice_splitter/invoice_5p.pdf"), }; -describe("A multi-page invoice document #extraDeps", () => { +describe("A multi-page invoice document #includeOptionalDeps", () => { it("should be split properly.", async () => { const jsonDataNA = await fs.readFile(path.resolve(dataPath.complete)); const response = JSON.parse(jsonDataNA.toString()); diff --git a/tests/v1/extraction/invoiceSplitterReconstruction.spec.ts b/tests/v1/extraction/invoiceSplitterReconstruction.spec.ts index 71768ca50..1f7d08a55 100644 --- a/tests/v1/extraction/invoiceSplitterReconstruction.spec.ts +++ b/tests/v1/extraction/invoiceSplitterReconstruction.spec.ts @@ -7,7 +7,7 @@ import { extractInvoices } from "@/v1/extraction/index.js"; import { PathInput } from "@/index.js"; import { V1_PRODUCT_PATH } from "../../index.js"; -describe("MindeeV1 - A Multipage Invoice Document #extraDeps", () => { +describe("MindeeV1 - A Multipage Invoice Document #includeOptionalDeps", () => { it("should be split into the proper invoices", async () => { const jsonData = await fs.readFile( path.join(V1_PRODUCT_PATH, "invoice_splitter/response_v1/complete.json") diff --git a/tests/v1/extraction/multiReceiptsExtractor.spec.ts b/tests/v1/extraction/multiReceiptsExtractor.spec.ts index d85f592c3..16f7b4271 100644 --- a/tests/v1/extraction/multiReceiptsExtractor.spec.ts +++ b/tests/v1/extraction/multiReceiptsExtractor.spec.ts @@ -12,7 +12,7 @@ const dataPath = { completeMultiPage: path.join(V1_PRODUCT_PATH, "multi_receipts_detector/response_v1/multipage_sample.json"), multiPageSample: path.join(V1_PRODUCT_PATH, "multi_receipts_detector/multipage_sample.pdf"), }; -describe("Multi-Receipt #extraDeps", () => { +describe("Multi-Receipt #includeOptionalDeps", () => { describe("A single-page multi-receipts document", () => { it("should be split properly.", async () => { const jsonDataNA = await fs.readFile(path.resolve(dataPath.complete)); diff --git a/tests/v1/extraction/multiReceiptsReconstruction.integration.ts b/tests/v1/extraction/multiReceiptsReconstruction.integration.ts index 5e893656a..699485ad2 100644 --- a/tests/v1/extraction/multiReceiptsReconstruction.integration.ts +++ b/tests/v1/extraction/multiReceiptsReconstruction.integration.ts @@ -10,7 +10,7 @@ import { setTimeout } from "node:timers/promises"; const apiKey = process.env.MINDEE_API_KEY; let client: Client; let sourceDoc: LocalInputSource; -describe("MindeeV1 - #extraDeps", () => { +describe("MindeeV1 - #includeOptionalDeps", () => { describe("MindeeV1 - A Multi-Receipt Image", () => { before(async () => { sourceDoc = new PathInput({ diff --git a/tests/v1/extraction/multiReceiptsReconstruction.spec.ts b/tests/v1/extraction/multiReceiptsReconstruction.spec.ts index aca5ac26d..c0ede55cd 100644 --- a/tests/v1/extraction/multiReceiptsReconstruction.spec.ts +++ b/tests/v1/extraction/multiReceiptsReconstruction.spec.ts @@ -15,7 +15,7 @@ const rotations = [ ]; rotations.forEach(({ angle, suffix }) => { - describe(`Multi-Receipt Document - ${angle}° rotation #extraDeps`, () => { + describe(`Multi-Receipt Document - ${angle}° rotation #includeOptionalDeps`, () => { let extractedReceipts: any[]; let sourceDoc: PathInput; diff --git a/tests/v1/product/invoiceSplitter/invoiceSplitter.integration.ts b/tests/v1/product/invoiceSplitter/invoiceSplitter.integration.ts index c0daa894e..7a1023647 100644 --- a/tests/v1/product/invoiceSplitter/invoiceSplitter.integration.ts +++ b/tests/v1/product/invoiceSplitter/invoiceSplitter.integration.ts @@ -6,7 +6,7 @@ import { promises as fs } from "fs"; import path from "path"; import { V1_PRODUCT_PATH } from "../../../index.js"; -describe("MindeeV1 - InvoiceSplitterV1 Integration Tests #extraDeps", async () => { +describe("MindeeV1 - InvoiceSplitterV1 Integration Tests #includeOptionalDeps", async () => { let client: mindee.v1.Client; beforeEach(() => { From b56594d0677c75b4bbccbbac178ce34633706eec Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:14:58 +0100 Subject: [PATCH 5/6] utils => dependency --- src/{utils => dependency}/index.ts | 0 src/{utils => dependency}/optionalLoader.ts | 0 src/image/extractedImage.ts | 2 +- src/image/imageCompressor.ts | 2 +- src/image/imageExtractor.ts | 2 +- src/pdf/pdfCompressor.ts | 2 +- src/pdf/pdfOperation.ts | 2 +- src/pdf/pdfUtils.ts | 2 +- .../invoiceSplitterExtractor/invoiceSplitterExtractor.ts | 2 +- .../extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts | 2 +- 10 files changed, 8 insertions(+), 8 deletions(-) rename src/{utils => dependency}/index.ts (100%) rename src/{utils => dependency}/optionalLoader.ts (100%) diff --git a/src/utils/index.ts b/src/dependency/index.ts similarity index 100% rename from src/utils/index.ts rename to src/dependency/index.ts diff --git a/src/utils/optionalLoader.ts b/src/dependency/optionalLoader.ts similarity index 100% rename from src/utils/optionalLoader.ts rename to src/dependency/optionalLoader.ts diff --git a/src/image/extractedImage.ts b/src/image/extractedImage.ts index 05c5c5fc5..f2a267c35 100644 --- a/src/image/extractedImage.ts +++ b/src/image/extractedImage.ts @@ -6,7 +6,7 @@ import { logger } from "@/logger.js"; import { BufferInput, MIMETYPES } from "@/input/index.js"; import type * as popplerTypes from "node-poppler"; import { writeFile } from "fs/promises"; -import { loadOptionalDependency } from "@/utils/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; /** * Generic class for image extraction diff --git a/src/image/imageCompressor.ts b/src/image/imageCompressor.ts index c927d4ed1..729a542ff 100644 --- a/src/image/imageCompressor.ts +++ b/src/image/imageCompressor.ts @@ -1,4 +1,4 @@ -import { loadOptionalDependency } from "@/utils/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import type * as SharpTypes from "sharp"; diff --git a/src/image/imageExtractor.ts b/src/image/imageExtractor.ts index e1af2c516..b6d9d3a34 100644 --- a/src/image/imageExtractor.ts +++ b/src/image/imageExtractor.ts @@ -3,7 +3,7 @@ import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { getMinMaxX, getMinMaxY, Polygon } from "@/geometry/index.js"; import { adjustForRotation } from "@/geometry/polygonUtils.js"; -import { loadOptionalDependency } from "@/utils/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; let pdfLib: typeof pdfLibTypes | null = null; diff --git a/src/pdf/pdfCompressor.ts b/src/pdf/pdfCompressor.ts index 800692552..645cd6295 100644 --- a/src/pdf/pdfCompressor.ts +++ b/src/pdf/pdfCompressor.ts @@ -7,7 +7,7 @@ import type * as popplerTypes from "node-poppler"; // @ts-ignore import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { compressImage } from "@/image/index.js"; -import { loadOptionalDependency } from "@/utils/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; let pdfLib: typeof pdfLibTypes | null = null; diff --git a/src/pdf/pdfOperation.ts b/src/pdf/pdfOperation.ts index 467303656..ebc22bb42 100644 --- a/src/pdf/pdfOperation.ts +++ b/src/pdf/pdfOperation.ts @@ -5,7 +5,7 @@ import { errorHandler } from "@/errors/handler.js"; import { PageOptions, PageOptionsOperation } from "@/input/pageOptions.js"; import { MindeeError } from "@/errors/index.js"; import { logger } from "@/logger.js"; -import { loadOptionalDependency } from "@/utils/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; let pdfLib: typeof pdfLibTypes | null = null; diff --git a/src/pdf/pdfUtils.ts b/src/pdf/pdfUtils.ts index c09a989a2..ea32f595c 100644 --- a/src/pdf/pdfUtils.ts +++ b/src/pdf/pdfUtils.ts @@ -2,7 +2,7 @@ // @ts-ignore import type * as pdfJsExtractTypes from "pdf.js-extract"; import { MindeePdfError } from "@/errors/index.js"; -import { loadOptionalDependency } from "@/utils/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; export interface PageTextInfo { diff --git a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts index e2059e14b..b6de0479e 100644 --- a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts +++ b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts @@ -5,7 +5,7 @@ import { MindeeError, MindeeInputSourceError } from "@/errors/index.js"; import { InvoiceSplitterV1 } from "@/v1/product/index.js"; import { LocalInputSource } from "@/input/index.js"; import { ExtractedInvoiceSplitterImage } from "@/v1/extraction/index.js"; -import { loadOptionalDependency } from "@/utils/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; let pdfLib: typeof pdfLibTypes | null = null; diff --git a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts index 56388e9ec..c49e37b58 100644 --- a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts +++ b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts @@ -8,7 +8,7 @@ import { ExtractedMultiReceiptImage } from "@/v1/extraction/index.js"; import { LocalInputSource } from "@/input/index.js"; import { extractFromPage } from "@/image/index.js"; import { PositionField } from "@/v1/parsing/standard/index.js"; -import { loadOptionalDependency } from "@/utils/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; let pdfLib: typeof pdfLibTypes | null = null; From 514d08177db1a46d3e283098f815a4275640fef0 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:33:05 +0100 Subject: [PATCH 6/6] test/misc => dependency --- tests/{misc => dependency}/missingDependencies.integration.ts | 0 tests/{misc => dependency}/missingDependencies.spec.ts | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/{misc => dependency}/missingDependencies.integration.ts (100%) rename tests/{misc => dependency}/missingDependencies.spec.ts (100%) diff --git a/tests/misc/missingDependencies.integration.ts b/tests/dependency/missingDependencies.integration.ts similarity index 100% rename from tests/misc/missingDependencies.integration.ts rename to tests/dependency/missingDependencies.integration.ts diff --git a/tests/misc/missingDependencies.spec.ts b/tests/dependency/missingDependencies.spec.ts similarity index 100% rename from tests/misc/missingDependencies.spec.ts rename to tests/dependency/missingDependencies.spec.ts