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 b151f8e56..94d38729c 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" @@ -40,6 +37,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": { @@ -57,6 +59,7 @@ "resolved": "https://registry.npmjs.org/@cantoo/pdf-lib/-/pdf-lib-2.5.3.tgz", "integrity": "sha512-SBQp8i/XdWNUhLutn5P67Pwj4X9vU046BRpfOMODJZuYVrgChtsTfgdnlW2O7x8gdXs8j7NoTaWI/b78E2oVmQ==", "license": "MIT", + "optional": true, "dependencies": { "@pdf-lib/standard-fonts": "^1.0.0", "@pdf-lib/upng": "^1.0.1", @@ -95,9 +98,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 +115,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 +132,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 +149,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 +166,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 +183,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 +200,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 +217,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 +234,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 +251,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 +268,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 +285,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 +302,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 +319,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 +336,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 +353,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 +370,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 +387,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 +404,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 +421,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 +438,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 +455,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 +472,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 +489,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 +506,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 +523,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 +729,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" } }, @@ -796,6 +799,7 @@ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", + "optional": true, "engines": { "node": ">=18" } @@ -1317,6 +1321,7 @@ "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", "license": "MIT", + "optional": true, "dependencies": { "pako": "^1.0.6" } @@ -1326,6 +1331,7 @@ "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", "license": "MIT", + "optional": true, "dependencies": { "pako": "^1.0.10" } @@ -1348,40 +1354,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 +1482,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 +1505,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 +1536,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 +1558,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 +1576,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 +1593,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 +1618,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 +1632,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 +1660,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 +1684,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": { @@ -2039,6 +2045,7 @@ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "license": "MIT", + "optional": true, "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" @@ -2051,6 +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==", + "devOptional": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2063,6 +2071,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true, "license": "MIT" }, "node_modules/color-string": { @@ -2070,6 +2079,7 @@ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", + "optional": true, "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -2120,7 +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==", - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/debug": { "version": "4.4.3", @@ -2165,6 +2176,7 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", + "optional": true, "engines": { "node": ">=8" } @@ -2196,7 +2208,8 @@ "version": "0.0.24", "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-0.0.24.tgz", "integrity": "sha512-PatEhAW5pIHr28MvFQGV5iiHNloqvecQZlxs7/8s/eulLqZI3uVqPkrO7YDuqsebovr/9mmcWDSWzVG4amEZgQ==", - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/eastasianwidth": { "version": "0.2.0", @@ -2226,9 +2239,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 +2252,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 +2772,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 +2788,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": { @@ -2883,7 +2897,8 @@ "url": "https://patreon.com/mdevils" } ], - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/ieee754": { "version": "1.2.1", @@ -2946,7 +2961,8 @@ "version": "0.3.4", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -3205,9 +3221,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": { @@ -3378,6 +3394,7 @@ "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==", "license": "MIT", + "optional": true, "dependencies": { "html-entities": "^2.3.2" } @@ -3469,7 +3486,8 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" + "license": "(MIT AND Zlib)", + "optional": true }, "node_modules/parent-module": { "version": "1.0.1", @@ -3553,6 +3571,7 @@ "resolved": "https://registry.npmjs.org/pdf.js-extract/-/pdf.js-extract-0.2.1.tgz", "integrity": "sha512-oUs5KaTVCelIyiBajCx3zAZKurkN9oVwRdqbSeDqeofddxNuwJRur86fCETvKZ/tX5nZJUSZWq3ie76PsArz7A==", "license": "MIT", + "optional": true, "dependencies": { "dommatrix": "0.0.24", "web-streams-polyfill": "3.2.0" @@ -3779,9 +3798,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" @@ -3806,6 +3825,7 @@ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, "license": "Apache-2.0", + "optional": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", @@ -3885,6 +3905,7 @@ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", "license": "MIT", + "optional": true, "dependencies": { "is-arrayish": "^0.3.1" } @@ -4354,6 +4375,7 @@ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", "license": "MIT", + "optional": true, "engines": { "node": ">= 8" } diff --git a/package.json b/package.json index 0aae7698b..7250ec09c 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 \"#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", @@ -59,16 +63,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/dependency/index.ts b/src/dependency/index.ts new file mode 100644 index 000000000..0c1c54b57 --- /dev/null +++ b/src/dependency/index.ts @@ -0,0 +1 @@ +export { loadOptionalDependency } from "./optionalLoader.js"; diff --git a/src/dependency/optionalLoader.ts b/src/dependency/optionalLoader.ts new file mode 100644 index 000000000..1d3a831f6 --- /dev/null +++ b/src/dependency/optionalLoader.ts @@ -0,0 +1,23 @@ +/** + * 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 (new Function("specifier", "return import(specifier)")(packageName)); + } catch (error: any) { + 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 optional dependencies: `npm install --include=optional`" + ); + } + throw error; + } +} diff --git a/src/image/extractedImage.ts b/src/image/extractedImage.ts index e568014c2..f2a267c35 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 "@/dependency/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..729a542ff 100644 --- a/src/image/imageCompressor.ts +++ b/src/image/imageCompressor.ts @@ -1,7 +1,11 @@ -import sharp from "sharp"; -import { Sharp, Metadata } from "sharp"; +import { loadOptionalDependency } from "@/dependency/index.js"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import type * as SharpTypes from "sharp"; + import { MindeeImageError } from "@/errors/index.js"; + /** * Compresses an image with the given parameters. * @@ -16,9 +20,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..b6d9d3a34 100644 --- a/src/image/imageExtractor.ts +++ b/src/image/imageExtractor.ts @@ -1,6 +1,19 @@ -import { PDFDocument, PDFPage, degrees } from "@cantoo/pdf-lib"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +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 "@/dependency/index.js"; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib(): Promise { + 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. @@ -9,8 +22,9 @@ 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 pdfLib = await getPdfLib(); const { width, height } = pdfPage.getSize(); const extractedElements :Uint8Array[] = []; // Manual upscale. @@ -21,7 +35,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 +79,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 +87,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 +95,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..645cd6295 100644 --- a/src/pdf/pdfCompressor.ts +++ b/src/pdf/pdfCompressor.ts @@ -2,9 +2,22 @@ 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"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import type * as pdfLibTypes from "@cantoo/pdf-lib"; import { compressImage } from "@/image/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib(): Promise { + 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. @@ -128,7 +141,8 @@ async function compressPagesWithQuality( disableSourceText: boolean, extractedText: ExtractedPdfInfo | null ): Promise { - const pdfDoc = await PDFDocument.load(pdfData, { + const pdfLib = await getPdfLib(); + const pdfDoc = await pdfLib.PDFDocument.load(pdfData, { ignoreEncryption: true, password: "" }); @@ -180,7 +194,8 @@ 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 pdfLib = await getPdfLib(); + const newPdfDoc = await pdfLib.PDFDocument.create(); for (const compressedPage of compressedPages) { const image = await newPdfDoc.embedJpg(compressedPage); @@ -198,32 +213,36 @@ async function createNewPdfFromCompressedPages(compressedPages: Buffer[]): Promi } async function addTextToPdfPage( - page: PDFPage, + page: pdfLibTypes.PDFPage, textInfo: ExtractedPdfInfo | null ): Promise { if (textInfo === null) { return; } + const pdfLib = await getPdfLib(); for (const textPages of textInfo.pages) { for (const textPage of textPages.content) { page.drawText(textPage.str, { 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 pdfLib = await getPdfLib(); + 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 +256,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 +274,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..ebc22bb42 100644 --- a/src/pdf/pdfOperation.ts +++ b/src/pdf/pdfOperation.ts @@ -1,8 +1,21 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +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 "@/dependency/index.js"; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib(): Promise { + if (!pdfLib) { + const pdfLibImport = await loadOptionalDependency("@cantoo/pdf-lib", "Text Embedding"); + pdfLib = (pdfLibImport as any).default || pdfLibImport; + } + return pdfLib!; +} export interface SplitPdf { file: Buffer; @@ -19,12 +32,13 @@ export async function extractPages( file: Buffer, pageOptions: PageOptions ): Promise { - const currentPdf = await PDFDocument.load(file, { + const pdfLib = await getPdfLib(); + 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 +79,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 +104,8 @@ 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 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 4aa83758b..ea32f595c 100644 --- a/src/pdf/pdfUtils.ts +++ b/src/pdf/pdfUtils.ts @@ -1,5 +1,8 @@ -import { PDFExtract, PDFExtractOptions, PDFExtractResult } from "pdf.js-extract"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import type * as pdfJsExtractTypes from "pdf.js-extract"; import { MindeePdfError } from "@/errors/index.js"; +import { loadOptionalDependency } from "@/dependency/index.js"; export interface PageTextInfo { @@ -34,21 +37,28 @@ 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) => ({ + 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/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts index e38916aa9..b6de0479e 100644 --- a/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts +++ b/src/v1/extraction/invoiceSplitterExtractor/invoiceSplitterExtractor.ts @@ -1,22 +1,38 @@ -import { PDFDocument } from "@cantoo/pdf-lib"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +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 "@/dependency/index.js"; -async function splitPdf(pdfDoc: PDFDocument, invoicePageGroups: number[][]): Promise { +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib(): Promise { + 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 []; } 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 +45,8 @@ async function splitPdf(pdfDoc: PDFDocument, invoicePageGroups: number[][]): Pro return generatedPdfs; } -async function getPdfDoc(inputFile: LocalInputSource): Promise { +async function getPdfDoc(inputFile: LocalInputSource): Promise { + const pdfLib = await getPdfLib(); await inputFile.init(); if (!inputFile.isPdf()) { throw new MindeeInputSourceError( @@ -37,7 +54,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..c49e37b58 100644 --- a/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts +++ b/src/v1/extraction/multiReceiptsExtractor/multiReceiptsExtractor.ts @@ -1,11 +1,24 @@ -import { PDFDocument, PDFImage, PDFPage, degrees } from "@cantoo/pdf-lib"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +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 "@/dependency/index.js"; + +let pdfLib: typeof pdfLibTypes | null = null; + +async function getPdfLib(): Promise { + 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 @@ -17,7 +30,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 +42,8 @@ async function extractReceiptsFromPage( } async function loadPdfDoc(inputFile: LocalInputSource) { - let pdfDoc: PDFDocument; + const pdfLib = await getPdfLib(); + let pdfDoc: pdfLibTypes.PDFDocument; if (!["image/jpeg", "image/jpg", "image/png", "application/pdf"].includes(inputFile.mimeType)) { throw new MindeeInputSourceError( 'Unsupported file type "' + @@ -37,13 +51,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 { @@ -67,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."); @@ -74,7 +89,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 diff --git a/tests/dependency/missingDependencies.integration.ts b/tests/dependency/missingDependencies.integration.ts new file mode 100644 index 000000000..9494d461c --- /dev/null +++ b/tests/dependency/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 #omitOptionalDeps", 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/dependency/missingDependencies.spec.ts b/tests/dependency/missingDependencies.spec.ts new file mode 100644 index 000000000..cfc135a20 --- /dev/null +++ b/tests/dependency/missingDependencies.spec.ts @@ -0,0 +1,39 @@ +import { expect } from "chai"; + +describe("Light Environment Sanity Check #omitOptionalDeps", function () { + + it("should NOT have sharp installed", async function () { + try { + 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"; + 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 { + 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"); + } + }); + + it("should NOT have @cantoo/pdf-lib installed", async function () { + try { + 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/input/compression.spec.ts b/tests/input/compression.spec.ts index 8359b08c2..a08f134c0 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 #includeOptionalDeps", () => { 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..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", () => { +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 ca4fd9d1a..672e8d3af 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("#includeOptionalDeps", async () => { + expect(await inputSource.getPageCount()).to.equals(10); + }); expect(inputSource.fileObject).to.be.instanceOf(Buffer); }); diff --git a/tests/pdf/pdfOperation.spec.ts b/tests/pdf/pdfOperation.spec.ts index c632ee2fc..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", () => { +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 dac586fb9..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", () => { +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 d3d81a87e..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", () => { +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 d87f91066..1f7d08a55 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 #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 218da8f4e..16f7b4271 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 #includeOptionalDeps", () => { + 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..699485ad2 100644 --- a/tests/v1/extraction/multiReceiptsReconstruction.integration.ts +++ b/tests/v1/extraction/multiReceiptsReconstruction.integration.ts @@ -10,124 +10,90 @@ 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 - #includeOptionalDeps", () => { + 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); -}); - -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..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`, () => { + 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 4e497ed15..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", async () => { +describe("MindeeV1 - InvoiceSplitterV1 Integration Tests #includeOptionalDeps", async () => { let client: mindee.v1.Client; beforeEach(() => {