diff --git a/.changeset/ocr-runtime-hardening.md b/.changeset/ocr-runtime-hardening.md new file mode 100644 index 0000000..5c13b79 --- /dev/null +++ b/.changeset/ocr-runtime-hardening.md @@ -0,0 +1,8 @@ +--- +'@happyvertical/pdf': patch +--- + +Improve Node OCR runtime handling for scanned PDFs by auto-detecting Tesseract +`tessdata`, surfacing actionable OCR dependency errors, routing explicit +external OCR providers like `onnx` through the `unpdf` pipeline, and rendering +OCR pages directly with `pdfjs-dist` to avoid worker-version mismatches. diff --git a/.gitignore b/.gitignore index d75668c..57368aa 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ pnpm-debug.log* .env .env.local .env.*.local + +# OCR runtime cache +.kreuzberg/ diff --git a/biome.json b/biome.json index f02befb..586e9b3 100644 --- a/biome.json +++ b/biome.json @@ -2,9 +2,21 @@ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "files": { "include": [ - "src/**/*.{js,ts,tsx,jsx}", - "test/**/*.{js,ts,tsx,jsx}", - "*.{js,ts,tsx,jsx}" + "src/**/*.js", + "src/**/*.jsx", + "src/**/*.ts", + "src/**/*.tsx", + "test/**/*.js", + "test/**/*.jsx", + "test/**/*.ts", + "test/**/*.tsx", + "scripts/**/*.js", + "scripts/**/*.ts", + "*.js", + "*.jsx", + "*.ts", + "*.tsx", + "*.json" ], "ignoreUnknown": true }, diff --git a/commitlint.config.js b/commitlint.config.js index f75e2bb..7f0b151 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -13,17 +13,17 @@ export default { 2, 'always', [ - 'feat', // New feature - 'fix', // Bug fix - 'docs', // Documentation changes - 'style', // Code style changes (formatting, missing semicolons, etc.) - 'refactor', // Code refactoring (neither fixes a bug nor adds a feature) - 'perf', // Performance improvements - 'test', // Adding or updating tests - 'build', // Changes to build system or dependencies - 'ci', // Changes to CI configuration files and scripts - 'chore', // Other changes that don't modify src or test files - 'revert', // Reverts a previous commit + 'feat', // New feature + 'fix', // Bug fix + 'docs', // Documentation changes + 'style', // Code style changes (formatting, missing semicolons, etc.) + 'refactor', // Code refactoring (neither fixes a bug nor adds a feature) + 'perf', // Performance improvements + 'test', // Adding or updating tests + 'build', // Changes to build system or dependencies + 'ci', // Changes to CI configuration files and scripts + 'chore', // Other changes that don't modify src or test files + 'revert', // Reverts a previous commit ], ], @@ -35,17 +35,13 @@ export default { 'pdf', 'ocr', 'unpdf', - 'deps', // Dependency updates - 'release', // Release-related changes + 'deps', // Dependency updates + 'release', // Release-related changes ], ], // Subject case - allow sentence-case, lowercase, kebab-case, etc. - 'subject-case': [ - 2, - 'never', - ['upper-case', 'pascal-case', 'start-case'], - ], + 'subject-case': [2, 'never', ['upper-case', 'pascal-case', 'start-case']], // Subject full stop - no period at the end 'subject-full-stop': [2, 'never', '.'], @@ -73,5 +69,5 @@ export default { // Body max line length - disable for semantic-release commits 'body-max-line-length': [0], - ], + }, }; diff --git a/lefthook.yml b/lefthook.yml index 2968ffc..cf1e75a 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -5,7 +5,7 @@ pre-commit: commands: format: - glob: "*.{js,jsx,ts,tsx}" + glob: "**/*.{js,jsx,ts,tsx}" run: pnpm biome check --write {staged_files} stage_fixed: true diff --git a/package.json b/package.json index f7de620..0167b53 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,7 @@ "pdfjs-dist": "5.4.296" } }, - "files": [ - "dist", - "README.md", - "LICENSE" - ], + "files": ["dist", "README.md", "LICENSE"], "publishConfig": { "registry": "https://npm.pkg.github.com", "access": "public" @@ -45,7 +41,7 @@ "build": "vite build", "build:watch": "vite build --watch", "typecheck": "tsc --noEmit", - "lint": "biome check", + "lint": "biome check .", "format": "biome format --write src/", "clean": "rm -rf dist *.tsbuildinfo", "dev": "npm run build:watch & npm run test:watch", @@ -56,9 +52,10 @@ "prepare": "lefthook install" }, "dependencies": { - "@happyvertical/ocr": "^0.60.4", - "@happyvertical/utils": "^0.71.0", - "pdf-to-png-converter": "^3.10.0", + "@happyvertical/ocr": "^0.60.33", + "@happyvertical/utils": "^0.71.20", + "@napi-rs/canvas": "0.1.97", + "pdfjs-dist": "5.4.296", "unpdf": "^1.4.0" }, "optionalDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7589bd9..e66ebc6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,17 +12,20 @@ importers: .: dependencies: '@happyvertical/ocr': - specifier: ^0.60.4 - version: 0.60.4 + specifier: ^0.60.33 + version: 0.60.33(ws@8.20.0) '@happyvertical/utils': - specifier: ^0.71.0 - version: 0.71.1 - pdf-to-png-converter: - specifier: ^3.10.0 - version: 3.11.0 + specifier: ^0.71.20 + version: 0.71.20 + '@napi-rs/canvas': + specifier: 0.1.97 + version: 0.1.97 + pdfjs-dist: + specifier: 5.4.296 + version: 5.4.296 unpdf: specifier: ^1.4.0 - version: 1.4.0(@napi-rs/canvas@0.1.84) + version: 1.4.0(@napi-rs/canvas@0.1.97) devDependencies: '@biomejs/biome': specifier: 1.9.4 @@ -64,6 +67,152 @@ importers: packages: + '@anthropic-ai/sdk@0.82.0': + resolution: {integrity: sha512-xdHTjL1GlUlDugHq/I47qdOKp/ROPvuHl7ROJCgUQigbvPu7asf9KcAcU1EqdrP2LuVhEKaTs7Z+ShpZDRzHdQ==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-bedrock-runtime@3.997.0': + resolution: {integrity: sha512-yEgCc/HvI7dLeXQLCuc4cnbzwE/NbNpKX8NmSSWTy3jnjiMZwrNKdHMBgPoNvaEb0klHhnTyO+JCHVVCPI/eYw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.973.27': + resolution: {integrity: sha512-CUZ5m8hwMCH6OYI4Li/WgMfIEx10Q2PLI9Y3XOUTPGZJ53aZ0007jCv+X/ywsaERyKPdw5MRZWk877roQksQ4A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.25': + resolution: {integrity: sha512-6QfI0wv4jpG5CrdO/AO0JfZ2ux+tKwJPrUwmvxXF50vI5KIypKVGNF6b4vlkYEnKumDTI1NX2zUBi8JoU5QU3A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.27': + resolution: {integrity: sha512-3V3Usj9Gs93h865DqN4M2NWJhC5kXU9BvZskfN3+69omuYlE3TZxOEcVQtBGLOloJB7BVfJKXVLqeNhOzHqSlQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.29': + resolution: {integrity: sha512-SiBuAnXecCbT/OpAf3vqyI/AVE3mTaYr9ShXLybxZiPLBiPCCOIWSGAtYYGQWMRvobBTiqOewaB+wcgMMZI2Aw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.29': + resolution: {integrity: sha512-OGOslTbOlxXexKMqhxCEbBQbUIfuhGxU5UXw3Fm56ypXHvrXH4aTt/xb5Y884LOoteP1QST1lVZzHfcTnWhiPQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.30': + resolution: {integrity: sha512-FMnAnWxc8PG+ZrZ2OBKzY4luCUJhe9CG0B9YwYr4pzrYGLXBS2rl+UoUvjGbAwiptxRL6hyA3lFn03Bv1TLqTw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.25': + resolution: {integrity: sha512-HR7ynNRdNhNsdVCOCegy1HsfsRzozCOPtD3RzzT1JouuaHobWyRfJzCBue/3jP7gECHt+kQyZUvwg/cYLWurNQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.29': + resolution: {integrity: sha512-HWv4SEq3jZDYPlwryZVef97+U8CxxRos5mK8sgGO1dQaFZpV5giZLzqGE5hkDmh2csYcBO2uf5XHjPTpZcJlig==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.29': + resolution: {integrity: sha512-PdMBza1WEKEUPFEmMGCfnU2RYCz9MskU2e8JxjyUOsMKku7j9YaDKvbDi2dzC0ihFoM6ods2SbhfAAro+Gwlew==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/eventstream-handler-node@3.972.13': + resolution: {integrity: sha512-2Pi1kD0MDkMAxDHqvpi/hKMs9hXUYbj2GLEjCwy+0jzfLChAsF50SUYnOeTI+RztA+Ic4pnLAdB03f1e8nggxQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-eventstream@3.972.9': + resolution: {integrity: sha512-ypgOvpWxQTCnQyDHGxnTviqqANE7FIIzII7VczJnTPCJcJlu17hMQXnvE47aKSKsawVJAaaRsyOEbHQuLJF9ng==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-host-header@3.972.9': + resolution: {integrity: sha512-je5vRdNw4SkuTnmRbFZLdye4sQ0faLt8kwka5wnnSU30q1mHO4X+idGEJOOE+Tn1ME7Oryn05xxkDvIb3UaLaQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-logger@3.972.9': + resolution: {integrity: sha512-HsVgDrruhqI28RkaXALm8grJ7Agc1wF6Et0xh6pom8NdO2VdO/SD9U/tPwUjewwK/pVoka+EShBxyCvgsPCtog==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.972.10': + resolution: {integrity: sha512-RVQQbq5orQ/GHUnXvqEOj2HHPBJm+mM+ySwZKS5UaLBwra5ugRtiH09PLUoOZRl7a1YzaOzXSuGbn9iD5j60WQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-user-agent@3.972.29': + resolution: {integrity: sha512-f/sIRzuTfEjg6NsbMYvye2VsmnQoNgntntleQyx5uGacUYzszbfIlO3GcI6G6daWUmTm0IDZc11qMHWwF0o0mQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-websocket@3.972.15': + resolution: {integrity: sha512-hsZ35FORQsN5hwNdMD6zWmHCphbXkDxO6j+xwCUiuMb0O6gzS/PWgttQNl1OAn7h/uqZAMUG4yOS0wY/yhAieg==} + engines: {node: '>= 14.0.0'} + + '@aws-sdk/nested-clients@3.996.19': + resolution: {integrity: sha512-uFkmCDXvmQYLanlYdOFS0+MQWkrj9wPMt/ZCc/0J0fjPim6F5jBVBmEomvGY/j77ILW6GTPwN22Jc174Mhkw6Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/region-config-resolver@3.972.11': + resolution: {integrity: sha512-6Q8B1dcx6BBqUTY1Mc/eROKA0FImEEY5VPSd6AGPEUf0ErjExz4snVqa9kNJSoVDV1rKaNf3qrWojgcKW+SdDg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1026.0': + resolution: {integrity: sha512-Ieq/HiRrbEtrYP387Nes0XlR7H1pJiJOZKv+QyQzMYpvTiDs0VKy2ZB3E2Zf+aFovWmeE7lRE4lXyF7dYM6GgA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.997.0': + resolution: {integrity: sha512-UdG36F7lU9aTqGFRieEyuRUJlgEJBqKeKKekC0esH21DbUSKhPR1kZBah214kYasIaWe1hLJLaqUigoTa5hZAQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.7': + resolution: {integrity: sha512-reXRwoJ6CfChoqAsBszUYajAF8Z2LRE+CRcKocvFSMpIiLOtYU3aJ9trmn6VVPAzbbY5LXF+FfmUslbXk1SYFg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-endpoints@3.996.6': + resolution: {integrity: sha512-2nUQ+2ih7CShuKHpGSIYvvAIOHy52dOZguYG36zptBukhw6iFwcvGfG0tes0oZFWQqEWvgZe9HLWaNlvXGdOrg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-format-url@3.972.9': + resolution: {integrity: sha512-fNJXHrs0ZT7Wx0KGIqKv7zLxlDXt2vqjx9z6oKUQFmpE5o4xxnSryvVHfHpIifYHWKz94hFccIldJ0YSZjlCBw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-user-agent-browser@3.972.9': + resolution: {integrity: sha512-sn/LMzTbGjYqCCF24390WxPd6hkpoSptiUn5DzVp4cD71yqw+yGEGm1YCxyEoPXyc8qciM8UzLJcZBFslxo5Uw==} + + '@aws-sdk/util-user-agent-node@3.973.15': + resolution: {integrity: sha512-fYn3s9PtKdgQkczGZCFMgkNEe8aq1JCVbnRqjqN9RSVW43xn2RV9xdcZ3z01a48Jpkuh/xCmBKJxdLOo4Ozg7w==} + engines: {node: '>=20.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/xml-builder@3.972.17': + resolution: {integrity: sha512-Ra7hjqAZf1OXRRMueB13qex7mFJRDK/pgCvdSFemXBT8KCGnQDPoKzHY1SjN+TjJVmnpSF14W5tJ1vDamFu+Gg==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -85,6 +234,10 @@ packages: resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} @@ -270,12 +423,12 @@ packages: resolution: {integrity: sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==} engines: {node: '>=v18'} - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} - '@emnapi/runtime@1.8.1': resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/runtime@1.9.2': + resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} + '@esbuild/aix-ppc64@0.27.1': resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} engines: {node: '>=18'} @@ -432,6 +585,15 @@ packages: cpu: [x64] os: [win32] + '@google/genai@1.48.0': + resolution: {integrity: sha512-plonYK4ML2PrxsRD9SeqmFt76eREWkQdPCglOA6aYDzL1AAbE+7PUnT54SvpWGfws13L0AZEqGSpL7+1IPnTxQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.25.2 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + '@gutenye/ocr-common@1.4.8': resolution: {integrity: sha512-J1Xc9xld9UWCTsj8m/FZiQK+fdvtC0K18RYFuYpdc76qw5eOCvZ2mcetAl9EgWDAmObE2NixLnoqCoa2fP8MFA==} @@ -441,15 +603,16 @@ packages: '@gutenye/ocr-node@1.4.8': resolution: {integrity: sha512-Ej6hHQSrBLCY74Qb2rRYZ0v97kp3h1Q8obLdQDM208ft9BOyqj+aUN2yTYQ/1c/BO695OWDgufRSiWiLB92KLw==} - '@happyvertical/ocr@0.60.4': - resolution: {integrity: sha512-wokfS4JaMnzEPhLv4DfCHHfb/Le+gyLPsWSWumfOFK30sJm+2mbTbH8eKr5gq06eTI0sTNgGeeIpeO0SBBeKdg==, tarball: https://npm.pkg.github.com/download/@happyvertical/ocr/0.60.4/04e5a0a9ad56783515416f6d1ec32ac53543c83a} - engines: {node: '>=24', pnpm: '>=9'} + '@happyvertical/ai@0.71.20': + resolution: {integrity: sha512-sn/y1wFoHimKuUiTjZZwd0CsakWYGzKien80J3ngV99whkd8Kglb9pzT+ihi/IldNJwlFLf8ZujBgdn5VZBlfw==, tarball: https://npm.pkg.github.com/download/@happyvertical/ai/0.71.20/27b4fc0eb1b21f04958d5d579d1860a37888a1b9} + hasBin: true - '@happyvertical/utils@0.60.2': - resolution: {integrity: sha512-O545bmkf++TAfCP/MbXV/U0ucx+6TRi4dsiOpOO92xnu6N8kNPBVsZ2Kt+M1XBwhRLuhE0Y0lBBpXFm021+18A==, tarball: https://npm.pkg.github.com/download/@happyvertical/utils/0.60.2/0e25ff142cbf8c3e24c7cfa014b0a1155896fe8f} + '@happyvertical/ocr@0.60.33': + resolution: {integrity: sha512-uMXObBBKcN8D8E/XTi6dS/R4EtuBHxK/35cmEyG874x2umg5vyXo+Ss+tSJlOryFw9f1Y0LDPXtqckMxJ6ecrQ==, tarball: https://npm.pkg.github.com/download/@happyvertical/ocr/0.60.33/dd3de5edd1ff28cb222b0564df0f8f80f8768a1a} + engines: {node: '>=24', pnpm: '>=9'} - '@happyvertical/utils@0.71.1': - resolution: {integrity: sha512-3yBZStwZOAzpSY2+TA9e2EEOJCV2lELW4snUSVRK2d3J7CNUfXHM7YDVZcW/XkM0BXrydCze2PbE7mGrAESYfw==, tarball: https://npm.pkg.github.com/download/@happyvertical/utils/0.71.1/d08a452bc80da14d16b9aed633ba5e86f9908bf5} + '@happyvertical/utils@0.71.20': + resolution: {integrity: sha512-5ZjBVRX9PyaATk6JLBkUZJnX8gyW76K0HfvQkOsFn1xnSsTHenOBsNDXct6L0HoaSTO4m6WQXoKSe2h/SPvc5A==, tarball: https://npm.pkg.github.com/download/@happyvertical/utils/0.71.20/3ca16b11c6716e9bfc05fec985a4e46c79ebafa3} hasBin: true '@img/sharp-darwin-arm64@0.33.5': @@ -639,73 +802,79 @@ packages: '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} - '@napi-rs/canvas-android-arm64@0.1.84': - resolution: {integrity: sha512-pdvuqvj3qtwVryqgpAGornJLV6Ezpk39V6wT4JCnRVGy8I3Tk1au8qOalFGrx/r0Ig87hWslysPpHBxVpBMIww==} + '@napi-rs/canvas-android-arm64@0.1.97': + resolution: {integrity: sha512-V1c/WVw+NzH8vk7ZK/O8/nyBSCQimU8sfMsB/9qeSvdkGKNU7+mxy/bIF0gTgeBFmHpj30S4E9WHMSrxXGQuVQ==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@napi-rs/canvas-darwin-arm64@0.1.84': - resolution: {integrity: sha512-A8IND3Hnv0R6abc6qCcCaOCujTLMmGxtucMTZ5vbQUrEN/scxi378MyTLtyWg+MRr6bwQJ6v/orqMS9datIcww==} + '@napi-rs/canvas-darwin-arm64@0.1.97': + resolution: {integrity: sha512-ok+SCEF4YejcxuJ9Rm+WWunHHpf2HmiPxfz6z1a/NFQECGXtsY7A4B8XocK1LmT1D7P174MzwPF9Wy3AUAwEPw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@napi-rs/canvas-darwin-x64@0.1.84': - resolution: {integrity: sha512-AUW45lJhYWwnA74LaNeqhvqYKK/2hNnBBBl03KRdqeCD4tKneUSrxUqIv8d22CBweOvrAASyKN3W87WO2zEr/A==} + '@napi-rs/canvas-darwin-x64@0.1.97': + resolution: {integrity: sha512-PUP6e6/UGlclUvAQNnuXCcnkpdUou6VYZfQOQxExLp86epOylmiwLkqXIvpFmjoTEDmPmXrI+coL/9EFU1gKPA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.84': - resolution: {integrity: sha512-8zs5ZqOrdgs4FioTxSBrkl/wHZB56bJNBqaIsfPL4ZkEQCinOkrFF7xIcXiHiKp93J3wUtbIzeVrhTIaWwqk+A==} + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.97': + resolution: {integrity: sha512-XyXH2L/cic8eTNtbrXCcvqHtMX/nEOxN18+7rMrAM2XtLYC/EB5s0wnO1FsLMWmK+04ZSLN9FBGipo7kpIkcOw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@napi-rs/canvas-linux-arm64-gnu@0.1.84': - resolution: {integrity: sha512-i204vtowOglJUpbAFWU5mqsJgH0lVpNk/Ml4mQtB4Lndd86oF+Otr6Mr5KQnZHqYGhlSIKiU2SYnUbhO28zGQA==} + '@napi-rs/canvas-linux-arm64-gnu@0.1.97': + resolution: {integrity: sha512-Kuq/M3djq0K8ktgz6nPlK7Ne5d4uWeDxPpyKWOjWDK2RIOhHVtLtyLiJw2fuldw7Vn4mhw05EZXCEr4Q76rs9w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@napi-rs/canvas-linux-arm64-musl@0.1.84': - resolution: {integrity: sha512-VyZq0EEw+OILnWk7G3ZgLLPaz1ERaPP++jLjeyLMbFOF+Tr4zHzWKiKDsEV/cT7btLPZbVoR3VX+T9/QubnURQ==} + '@napi-rs/canvas-linux-arm64-musl@0.1.97': + resolution: {integrity: sha512-kKmSkQVnWeqg7qdsiXvYxKhAFuHz3tkBjW/zyQv5YKUPhotpaVhpBGv5LqCngzyuRV85SXoe+OFj+Tv0a0QXkQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@napi-rs/canvas-linux-riscv64-gnu@0.1.84': - resolution: {integrity: sha512-PSMTh8DiThvLRsbtc/a065I/ceZk17EXAATv9uNvHgkgo7wdEfTh2C3aveNkBMGByVO3tvnvD5v/YFtZL07cIg==} + '@napi-rs/canvas-linux-riscv64-gnu@0.1.97': + resolution: {integrity: sha512-Jc7I3A51jnEOIAXeLsN/M/+Z28LUeakcsXs07FLq9prXc0eYOtVwsDEv913Gr+06IRo34gJJVgT0TXvmz+N2VA==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] libc: [glibc] - '@napi-rs/canvas-linux-x64-gnu@0.1.84': - resolution: {integrity: sha512-N1GY3noO1oqgEo3rYQIwY44kfM11vA0lDbN0orTOHfCSUZTUyiYCY0nZ197QMahZBm1aR/vYgsWpV74MMMDuNA==} + '@napi-rs/canvas-linux-x64-gnu@0.1.97': + resolution: {integrity: sha512-iDUBe7AilfuBSRbSa8/IGX38Mf+iCSBqoVKLSQ5XaY2JLOaqz1TVyPFEyIck7wT6mRQhQt5sN6ogfjIDfi74tg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@napi-rs/canvas-linux-x64-musl@0.1.84': - resolution: {integrity: sha512-vUZmua6ADqTWyHyei81aXIt9wp0yjeNwTH0KdhdeoBb6azHmFR8uKTukZMXfLCC3bnsW0t4lW7K78KNMknmtjg==} + '@napi-rs/canvas-linux-x64-musl@0.1.97': + resolution: {integrity: sha512-AKLFd/v0Z5fvgqBDqhvqtAdx+fHMJ5t9JcUNKq4FIZ5WH+iegGm8HPdj00NFlCSnm83Fp3Ln8I2f7uq1aIiWaA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@napi-rs/canvas-win32-x64-msvc@0.1.84': - resolution: {integrity: sha512-YSs8ncurc1xzegUMNnQUTYrdrAuaXdPMOa+iYYyAxydOtg0ppV386hyYMsy00Yip1NlTgLCseRG4sHSnjQx6og==} + '@napi-rs/canvas-win32-arm64-msvc@0.1.97': + resolution: {integrity: sha512-u883Yr6A6fO7Vpsy9YE4FVCIxzzo5sO+7pIUjjoDLjS3vQaNMkVzx5bdIpEL+ob+gU88WDK4VcxYMZ6nmnoX9A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/canvas-win32-x64-msvc@0.1.97': + resolution: {integrity: sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@napi-rs/canvas@0.1.84': - resolution: {integrity: sha512-88FTNFs4uuiFKP0tUrPsEXhpe9dg7za9ILZJE08pGdUveMIDeana1zwfVkqRHJDPJFAmGY3dXmJ99dzsy57YnA==} + '@napi-rs/canvas@0.1.97': + resolution: {integrity: sha512-8cFniXvrIEnVwuNSRCW9wirRZbHvrD3JVujdS2P5n5xiJZNZMOZcfOvJ1pb66c7jXMKHHglJEDVJGbm8XWFcXQ==} engines: {node: '>= 10'} '@noble/hashes@2.0.1': @@ -724,14 +893,40 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@paralleldrive/cuid2@3.0.4': - resolution: {integrity: sha512-sM6M2PWrByOEpN2QYAdulhEbSZmChwj0e52u4hpwB7u4PznFiNAavtE6m7O8tWUlzX+jT2eKKtc5/ZgX+IHrtg==} - hasBin: true - '@paralleldrive/cuid2@3.3.0': resolution: {integrity: sha512-OqiFvSOF0dBSesELYY2CAMa4YINvlLpvKOz/rv6NeZEqiyttlHgv98Juwv4Ch+GrEV7IZ8jfI2VcEoYUjXXCjw==} hasBin: true + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -892,6 +1087,194 @@ packages: '@rushstack/ts-command-line@5.1.5': resolution: {integrity: sha512-YmrFTFUdHXblYSa+Xc9OO9FsL/XFcckZy0ycQ6q7VSBsVs5P0uD9vcges5Q9vctGlVdu27w+Ct6IuJ458V0cTQ==} + '@smithy/config-resolver@4.4.14': + resolution: {integrity: sha512-N55f8mPEccpzKetUagdvmAy8oohf0J5cuj9jLI1TaSceRlq0pJsIZepY3kmAXAhyxqXPV6hDerDQhqQPKWgAoQ==} + engines: {node: '>=18.0.0'} + + '@smithy/core@3.23.14': + resolution: {integrity: sha512-vJ0IhpZxZAkFYOegMKSrxw7ujhhT2pass/1UEcZ4kfl5srTAqtPU5I7MdYQoreVas3204ykCiNhY1o7Xlz6Yyg==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.2.13': + resolution: {integrity: sha512-wboCPijzf6RJKLOvnjDAiBxGSmSnGXj35o5ZAWKDaHa/cvQ5U3ZJ13D4tMCE8JG4dxVAZFy/P0x/V9CwwdfULQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-codec@4.2.13': + resolution: {integrity: sha512-vYahwBAtRaAcFbOmE9aLr12z7RiHYDSLcnogSdxfm7kKfsNa3wH+NU5r7vTeB5rKvLsWyPjVX8iH94brP7umiQ==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-browser@4.2.13': + resolution: {integrity: sha512-wwybfcOX0tLqCcBP378TIU9IqrDuZq/tDV48LlZNydMpCnqnYr+hWBAYbRE+rFFf/p7IkDJySM3bgiMKP2ihPg==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-config-resolver@4.3.13': + resolution: {integrity: sha512-ied1lO559PtAsMJzg2TKRlctLnEi1PfkNeMMpdwXDImk1zV9uvS/Oxoy/vcy9uv1GKZAjDAB5xT6ziE9fzm5wA==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-node@4.2.13': + resolution: {integrity: sha512-hFyK+ORJrxAN3RYoaD6+gsGDQjeix8HOEkosoajvXYZ4VeqonM3G4jd9IIRm/sWGXUKmudkY9KdYjzosUqdM8A==} + engines: {node: '>=18.0.0'} + + '@smithy/eventstream-serde-universal@4.2.13': + resolution: {integrity: sha512-kRrq4EKLGeOxhC2CBEhRNcu1KSzNJzYY7RK3S7CxMPgB5dRrv55WqQOtRwQxQLC04xqORFLUgnDlc6xrNUULaA==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.3.16': + resolution: {integrity: sha512-nYDRUIvNd4mFmuXraRWt6w5UsZTNqtj4hXJA/iiOD4tuseIdLP9Lq38teH/SZTcIFCa2f+27o7hYpIsWktJKEQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.2.13': + resolution: {integrity: sha512-4/oy9h0jjmY80a2gOIo75iLl8TOPhmtx4E2Hz+PfMjvx/vLtGY4TMU/35WRyH2JHPfT5CVB38u4JRow7gnmzJA==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.13': + resolution: {integrity: sha512-jvC0RB/8BLj2SMIkY0Npl425IdnxZJxInpZJbu563zIRnVjpDMXevU3VMCRSabaLB0kf/eFIOusdGstrLJ8IDg==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.2.13': + resolution: {integrity: sha512-IPMLm/LE4AZwu6qiE8Rr8vJsWhs9AtOdySRXrOM7xnvclp77Tyh7hMs/FRrMf26kgIe67vFJXXOSmVxS7oKeig==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.4.29': + resolution: {integrity: sha512-R9Q/58U+qBiSARGWbAbFLczECg/RmysRksX6Q8BaQEpt75I7LI6WGDZnjuC9GXSGKljEbA7N118LhGaMbfrTXw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.5.0': + resolution: {integrity: sha512-/NzISn4grj/BRFVua/xnQwF+7fakYZgimpw2dfmlPgcqecBMKxpB9g5mLYRrmBD5OrPoODokw4Vi1hrSR4zRyw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.2.17': + resolution: {integrity: sha512-0T2mcaM6v9W1xku86Dk0bEW7aEseG6KenFkPK98XNw0ZhOqOiD1MrMsdnQw9QsL3/Oa85T53iSMlm0SZdSuIEQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.2.13': + resolution: {integrity: sha512-g72jN/sGDLyTanrCLH9fhg3oysO3f7tQa6eWWsMyn2BiYNCgjF24n4/I9wff/5XidFvjj9ilipAoQrurTUrLvw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.3.13': + resolution: {integrity: sha512-iGxQ04DsKXLckbgnX4ipElrOTk+IHgTyu0q0WssZfYhDm9CQWHmu6cOeI5wmWRxpXbBDhIIfXMWz5tPEtcVqbw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.5.2': + resolution: {integrity: sha512-/oD7u8M0oj2ZTFw7GkuuHWpIxtWdLlnyNkbrWcyVYhd5RJNDuczdkb0wfnQICyNFrVPlr8YHOhamjNy3zidhmA==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.2.13': + resolution: {integrity: sha512-bGzUCthxRmezuxkbu9wD33wWg9KX3hJpCXpQ93vVkPrHn9ZW6KNNdY5xAUWNuRCwQ+VyboFuWirG1lZhhkcyRQ==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.3.13': + resolution: {integrity: sha512-+HsmuJUF4u8POo6s8/a2Yb/AQ5t/YgLovCuHF9oxbocqv+SZ6gd8lC2duBFiCA/vFHoHQhoq7QjqJqZC6xOxxg==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.2.13': + resolution: {integrity: sha512-tG4aOYFCZdPMjbgfhnIQ322H//ojujldp1SrHPHpBSb3NqgUp3dwiUGRJzie87hS1DYwWGqDuPaowoDF+rYCbQ==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.2.13': + resolution: {integrity: sha512-hqW3Q4P+CDzUyQ87GrboGMeD7XYNMOF+CuTwu936UQRB/zeYn3jys8C3w+wMkDfY7CyyyVwZQ5cNFoG0x1pYmA==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.2.13': + resolution: {integrity: sha512-a0s8XZMfOC/qpqq7RCPvJlk93rWFrElH6O++8WJKz0FqnA4Y7fkNi/0mnGgSH1C4x6MFsuBA8VKu4zxFrMe5Vw==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.4.8': + resolution: {integrity: sha512-VZCZx2bZasxdqxVgEAhREvDSlkatTPnkdWy1+Kiy8w7kYPBosW0V5IeDwzDUMvWBt56zpK658rx1cOBFOYaPaw==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.3.13': + resolution: {integrity: sha512-YpYSyM0vMDwKbHD/JA7bVOF6kToVRpa+FM5ateEVRpsTNu564g1muBlkTubXhSKKYXInhpADF46FPyrZcTLpXg==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.12.9': + resolution: {integrity: sha512-ovaLEcTU5olSeHcRXcxV6viaKtpkHZumn6Ps0yn7dRf2rRSfy794vpjOtrWDO0d1auDSvAqxO+lyhERSXQ03EQ==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.14.0': + resolution: {integrity: sha512-OWgntFLW88kx2qvf/c/67Vno1yuXm/f9M7QFAtVkkO29IJXGBIg0ycEaBTH0kvCtwmvZxRujrgP5a86RvsXJAQ==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.2.13': + resolution: {integrity: sha512-2G03yoboIRZlZze2+PT4GZEjgwQsJjUgn6iTsvxA02bVceHR6vp4Cuk7TUnPFWKF+ffNUk3kj4COwkENS2K3vw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.3.45': + resolution: {integrity: sha512-ag9sWc6/nWZAuK3Wm9KlFJUnRkXLrXn33RFjIAmCTFThqLHY+7wCst10BGq56FxslsDrjhSie46c8OULS+BiIw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.2.49': + resolution: {integrity: sha512-jlN6vHwE8gY5AfiFBavtD3QtCX2f7lM3BKkz7nFKSNfFR5nXLXLg6sqXTJEEyDwtxbztIDBQCfjsGVXlIru2lQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.3.4': + resolution: {integrity: sha512-BKoR/ubPp9KNKFxPpg1J28N1+bgu8NGAtJblBP7yHy8yQPBWhIAv9+l92SlQLpolGm71CVO+btB60gTgzT0wog==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.2.13': + resolution: {integrity: sha512-GTooyrlmRTqvUen4eK7/K1p6kryF7bnDfq6XsAbIsf2mo51B/utaH+XThY6dKgNCWzMAaH/+OLmqaBuLhLWRow==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.3.0': + resolution: {integrity: sha512-tSOPQNT/4KfbvqeMovWC3g23KSYy8czHd3tlN+tOYVNIDLSfxIsrPJihYi5TpNcoV789KWtgChUVedh2y6dDPg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.5.22': + resolution: {integrity: sha512-3H8iq/0BfQjUs2/4fbHZ9aG9yNzcuZs24LPkcX1Q7Z+qpqaGM8+qbGmE8zo9m2nCRgamyvS98cHdcWvR6YUsew==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} + engines: {node: '>=18.0.0'} + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -919,6 +1302,9 @@ packages: '@types/node@24.12.2': resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + '@vitest/expect@4.0.15': resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} @@ -986,10 +1372,14 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - adm-zip@0.5.16: - resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} + adm-zip@0.5.17: + resolution: {integrity: sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==} engines: {node: '>=12.0'} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -1047,6 +1437,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -1061,6 +1454,9 @@ packages: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -1068,6 +1464,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1155,6 +1554,10 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} @@ -1201,6 +1604,9 @@ packages: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1267,6 +1673,9 @@ packages: exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -1277,6 +1686,13 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-xml-builder@1.1.4: + resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} + + fast-xml-parser@5.5.8: + resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} + hasBin: true + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -1289,6 +1705,10 @@ packages: picomatch: optional: true + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1301,6 +1721,10 @@ packages: resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} engines: {node: '>=18'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fs-extra@11.3.2: resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} @@ -1321,6 +1745,14 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gaxios@7.1.4: + resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==} + engines: {node: '>=18'} + + gcp-metadata@8.1.2: + resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} + engines: {node: '>=18'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1353,6 +1785,14 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + google-auth-library@10.6.2: + resolution: {integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==} + engines: {node: '>=18'} + + google-logging-utils@1.1.3: + resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} + engines: {node: '>=14'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -1375,6 +1815,10 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + human-id@4.1.3: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true @@ -1481,9 +1925,16 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -1500,6 +1951,12 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} @@ -1602,6 +2059,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -1654,6 +2114,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -1663,6 +2128,10 @@ packages: encoding: optional: true + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -1670,13 +2139,25 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - onnxruntime-common@1.23.2: - resolution: {integrity: sha512-5LFsC9Dukzp2WV6kNHYLNzp8sT6V02IubLCbzw2Xd6X5GOlr65gAX6xiJwyi2URJol/s71gaQLC5F2C25AAR2w==} + onnxruntime-common@1.24.3: + resolution: {integrity: sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==} - onnxruntime-node@1.23.2: - resolution: {integrity: sha512-OBTsG0W8ddBVOeVVVychpVBS87A9YV5sa2hJ6lc025T97Le+J4v++PwSC4XFs1C62SWyNdof0Mh4KvnZgtt4aw==} + onnxruntime-node@1.24.3: + resolution: {integrity: sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==} os: [win32, darwin, linux] + openai@6.33.0: + resolution: {integrity: sha512-xAYN1W3YsDXJWA5F277135YfkEk6H7D3D6vWwRhJ3OEkzRgcyK8z/P5P9Gyi/wB4N8kK9kM5ZjprfvyHagKmpw==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + opencollective-postinstall@2.0.3: resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==} hasBin: true @@ -1708,6 +2189,10 @@ packages: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -1734,6 +2219,10 @@ packages: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + path-expression-matcher@1.4.0: + resolution: {integrity: sha512-s4DQMxIdhj3jLFWd9LxHOplj4p9yQ4ffMGowFf3cpEgrrJjEhN0V5nxw4Ye1EViAGDoL4/1AeO6qHpqYPOzE4Q==} + engines: {node: '>=14.0.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -1748,10 +2237,6 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pdf-to-png-converter@3.11.0: - resolution: {integrity: sha512-9V4seNenywRMGKX2AQX2IHdxUxTBffqfGNtozQjx2RokePG/TjvwABGfr7tozmWkmc4oAsO2q2RL6lFp5Bqxhw==} - engines: {node: '>=20'} - pdfjs-dist@5.4.296: resolution: {integrity: sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==} engines: {node: '>=20.16.0 || >=22.3.0'} @@ -1794,6 +2279,10 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1835,6 +2324,10 @@ packages: engines: {node: '>= 0.4'} hasBin: true + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1851,6 +2344,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -1867,6 +2363,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} @@ -1944,6 +2445,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strnum@2.2.3: + resolution: {integrity: sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==} + supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} @@ -1956,8 +2460,8 @@ packages: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} - tesseract.js-core@6.0.0: - resolution: {integrity: sha512-1Qncm/9oKM7xgrQXZXNB+NRh19qiXGhxlrR8EwFbK5SaUbPZnS5OMtP/ghtqfd23hsr1ZvZbZjeuAGcMxd/ooA==} + tesseract.js-core@6.1.2: + resolution: {integrity: sha512-pv4GjmramjdObhDyR1q85Td8X60Puu/lGQn7Kw2id05LLgHhAcWgnz6xSdMCSxBMWjQDmMyDXPTC2aqADdpiow==} tesseract.js@6.0.1: resolution: {integrity: sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==} @@ -1994,6 +2498,9 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2138,6 +2645,10 @@ packages: wasm-feature-detect@1.8.0: resolution: {integrity: sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -2163,6 +2674,18 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2187,39 +2710,433 @@ packages: snapshots: - '@babel/code-frame@7.27.1': + '@anthropic-ai/sdk@0.82.0': dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 + json-schema-to-ts: 3.1.1 - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.28.5': {} + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.7 + tslib: 2.8.1 - '@babel/parser@7.28.5': + '@aws-crypto/sha256-browser@5.2.0': dependencies: - '@babel/types': 7.28.5 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 - '@babel/runtime@7.28.4': {} + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.7 + tslib: 2.8.1 - '@babel/types@7.28.5': + '@aws-crypto/supports-web-crypto@5.2.0': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + tslib: 2.8.1 - '@biomejs/biome@1.9.4': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 1.9.4 - '@biomejs/cli-darwin-x64': 1.9.4 - '@biomejs/cli-linux-arm64': 1.9.4 - '@biomejs/cli-linux-arm64-musl': 1.9.4 - '@biomejs/cli-linux-x64': 1.9.4 - '@biomejs/cli-linux-x64-musl': 1.9.4 - '@biomejs/cli-win32-arm64': 1.9.4 - '@biomejs/cli-win32-x64': 1.9.4 + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 - '@biomejs/cli-darwin-arm64@1.9.4': + '@aws-sdk/client-bedrock-runtime@3.997.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/credential-provider-node': 3.972.30 + '@aws-sdk/eventstream-handler-node': 3.972.13 + '@aws-sdk/middleware-eventstream': 3.972.9 + '@aws-sdk/middleware-host-header': 3.972.9 + '@aws-sdk/middleware-logger': 3.972.9 + '@aws-sdk/middleware-recursion-detection': 3.972.10 + '@aws-sdk/middleware-user-agent': 3.972.29 + '@aws-sdk/middleware-websocket': 3.972.15 + '@aws-sdk/region-config-resolver': 3.972.11 + '@aws-sdk/token-providers': 3.997.0 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-endpoints': 3.996.6 + '@aws-sdk/util-user-agent-browser': 3.972.9 + '@aws-sdk/util-user-agent-node': 3.973.15 + '@smithy/config-resolver': 4.4.14 + '@smithy/core': 3.23.14 + '@smithy/eventstream-serde-browser': 4.2.13 + '@smithy/eventstream-serde-config-resolver': 4.3.13 + '@smithy/eventstream-serde-node': 4.2.13 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/hash-node': 4.2.13 + '@smithy/invalid-dependency': 4.2.13 + '@smithy/middleware-content-length': 4.2.13 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-retry': 4.5.0 + '@smithy/middleware-serde': 4.2.17 + '@smithy/middleware-stack': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/node-http-handler': 4.5.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.45 + '@smithy/util-defaults-mode-node': 4.2.49 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.0 + '@smithy/util-stream': 4.5.22 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.973.27': + dependencies: + '@aws-sdk/types': 3.973.7 + '@aws-sdk/xml-builder': 3.972.17 + '@smithy/core': 3.23.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/signature-v4': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.25': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.27': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/node-http-handler': 4.5.2 + '@smithy/property-provider': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-stream': 4.5.22 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.29': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/credential-provider-env': 3.972.25 + '@aws-sdk/credential-provider-http': 3.972.27 + '@aws-sdk/credential-provider-login': 3.972.29 + '@aws-sdk/credential-provider-process': 3.972.25 + '@aws-sdk/credential-provider-sso': 3.972.29 + '@aws-sdk/credential-provider-web-identity': 3.972.29 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-login@3.972.29': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.972.30': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.25 + '@aws-sdk/credential-provider-http': 3.972.27 + '@aws-sdk/credential-provider-ini': 3.972.29 + '@aws-sdk/credential-provider-process': 3.972.25 + '@aws-sdk/credential-provider-sso': 3.972.29 + '@aws-sdk/credential-provider-web-identity': 3.972.29 + '@aws-sdk/types': 3.973.7 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.25': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.29': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/token-providers': 3.1026.0 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.972.29': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/eventstream-handler-node@3.972.13': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/eventstream-codec': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-eventstream@3.972.9': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-host-header@3.972.9': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.972.9': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.972.10': + dependencies: + '@aws-sdk/types': 3.973.7 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.972.29': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-endpoints': 3.996.6 + '@smithy/core': 3.23.14 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-retry': 4.3.0 + tslib: 2.8.1 + + '@aws-sdk/middleware-websocket@3.972.15': + dependencies: + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-format-url': 3.972.9 + '@smithy/eventstream-codec': 4.2.13 + '@smithy/eventstream-serde-browser': 4.2.13 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/protocol-http': 5.3.13 + '@smithy/signature-v4': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.996.19': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.973.27 + '@aws-sdk/middleware-host-header': 3.972.9 + '@aws-sdk/middleware-logger': 3.972.9 + '@aws-sdk/middleware-recursion-detection': 3.972.10 + '@aws-sdk/middleware-user-agent': 3.972.29 + '@aws-sdk/region-config-resolver': 3.972.11 + '@aws-sdk/types': 3.973.7 + '@aws-sdk/util-endpoints': 3.996.6 + '@aws-sdk/util-user-agent-browser': 3.972.9 + '@aws-sdk/util-user-agent-node': 3.973.15 + '@smithy/config-resolver': 4.4.14 + '@smithy/core': 3.23.14 + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/hash-node': 4.2.13 + '@smithy/invalid-dependency': 4.2.13 + '@smithy/middleware-content-length': 4.2.13 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-retry': 4.5.0 + '@smithy/middleware-serde': 4.2.17 + '@smithy/middleware-stack': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/node-http-handler': 4.5.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.45 + '@smithy/util-defaults-mode-node': 4.2.49 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.0 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.972.11': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/config-resolver': 4.4.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1026.0': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/token-providers@3.997.0': + dependencies: + '@aws-sdk/core': 3.973.27 + '@aws-sdk/nested-clients': 3.996.19 + '@aws-sdk/types': 3.973.7 + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.7': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.996.6': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-endpoints': 3.3.4 + tslib: 2.8.1 + + '@aws-sdk/util-format-url@3.972.9': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/querystring-builder': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.5': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.972.9': + dependencies: + '@aws-sdk/types': 3.973.7 + '@smithy/types': 4.14.0 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.973.15': + dependencies: + '@aws-sdk/middleware-user-agent': 3.972.29 + '@aws-sdk/types': 3.973.7 + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-config-provider': 4.2.2 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.17': + dependencies: + '@smithy/types': 4.14.0 + fast-xml-parser: 5.5.8 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.4': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/runtime@7.28.4': {} + + '@babel/runtime@7.29.2': {} + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@biomejs/biome@1.9.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.9.4 + '@biomejs/cli-darwin-x64': 1.9.4 + '@biomejs/cli-linux-arm64': 1.9.4 + '@biomejs/cli-linux-arm64-musl': 1.9.4 + '@biomejs/cli-linux-x64': 1.9.4 + '@biomejs/cli-linux-x64-musl': 1.9.4 + '@biomejs/cli-win32-arm64': 1.9.4 + '@biomejs/cli-win32-x64': 1.9.4 + + '@biomejs/cli-darwin-arm64@1.9.4': optional: true '@biomejs/cli-darwin-x64@1.9.4': @@ -2497,12 +3414,12 @@ snapshots: '@types/conventional-commits-parser': 5.0.2 chalk: 5.6.2 - '@emnapi/runtime@1.7.1': + '@emnapi/runtime@1.8.1': dependencies: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.8.1': + '@emnapi/runtime@1.9.2': dependencies: tslib: 2.8.1 optional: true @@ -2585,6 +3502,17 @@ snapshots: '@esbuild/win32-x64@0.27.1': optional: true + '@google/genai@1.48.0': + dependencies: + google-auth-library: 10.6.2 + p-retry: 4.6.2 + protobufjs: 7.5.4 + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@gutenye/ocr-common@1.4.8': dependencies: '@techstark/opencv-js': 4.9.0-release.3 @@ -2597,27 +3525,44 @@ snapshots: dependencies: '@gutenye/ocr-common': 1.4.8 '@gutenye/ocr-models': 1.4.2 - onnxruntime-node: 1.23.2 + onnxruntime-node: 1.24.3 sharp: 0.33.5 - '@happyvertical/ocr@0.60.4': + '@happyvertical/ai@0.71.20(ws@8.20.0)': + dependencies: + '@anthropic-ai/sdk': 0.82.0 + '@aws-sdk/client-bedrock-runtime': 3.997.0 + '@google/genai': 1.48.0 + '@happyvertical/utils': 0.71.20 + openai: 6.33.0(ws@8.20.0) + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@happyvertical/ocr@0.60.33(ws@8.20.0)': dependencies: '@gutenye/ocr-node': 1.4.8 - '@happyvertical/utils': 0.60.2 + '@happyvertical/ai': 0.71.20(ws@8.20.0) + '@happyvertical/utils': 0.71.20 jpeg-js: 0.4.4 pngjs: 7.0.0 tesseract.js: 6.0.1 transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - aws-crt + - bufferutil - encoding + - supports-color + - utf-8-validate + - ws + - zod - '@happyvertical/utils@0.60.2': - dependencies: - '@paralleldrive/cuid2': 3.0.4 - date-fns: 4.1.0 - pluralize: 8.0.0 - uuid: 13.0.0 - - '@happyvertical/utils@0.71.1': + '@happyvertical/utils@0.71.20': dependencies: '@paralleldrive/cuid2': 3.3.0 date-fns: 4.1.0 @@ -2690,7 +3635,7 @@ snapshots: '@img/sharp-wasm32@0.33.5': dependencies: - '@emnapi/runtime': 1.7.1 + '@emnapi/runtime': 1.9.2 optional: true '@img/sharp-win32-ia32@0.33.5': @@ -2789,48 +3734,52 @@ snapshots: '@microsoft/tsdoc@0.16.0': {} - '@napi-rs/canvas-android-arm64@0.1.84': + '@napi-rs/canvas-android-arm64@0.1.97': optional: true - '@napi-rs/canvas-darwin-arm64@0.1.84': + '@napi-rs/canvas-darwin-arm64@0.1.97': optional: true - '@napi-rs/canvas-darwin-x64@0.1.84': + '@napi-rs/canvas-darwin-x64@0.1.97': optional: true - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.84': + '@napi-rs/canvas-linux-arm-gnueabihf@0.1.97': optional: true - '@napi-rs/canvas-linux-arm64-gnu@0.1.84': + '@napi-rs/canvas-linux-arm64-gnu@0.1.97': optional: true - '@napi-rs/canvas-linux-arm64-musl@0.1.84': + '@napi-rs/canvas-linux-arm64-musl@0.1.97': optional: true - '@napi-rs/canvas-linux-riscv64-gnu@0.1.84': + '@napi-rs/canvas-linux-riscv64-gnu@0.1.97': optional: true - '@napi-rs/canvas-linux-x64-gnu@0.1.84': + '@napi-rs/canvas-linux-x64-gnu@0.1.97': optional: true - '@napi-rs/canvas-linux-x64-musl@0.1.84': + '@napi-rs/canvas-linux-x64-musl@0.1.97': optional: true - '@napi-rs/canvas-win32-x64-msvc@0.1.84': + '@napi-rs/canvas-win32-arm64-msvc@0.1.97': optional: true - '@napi-rs/canvas@0.1.84': + '@napi-rs/canvas-win32-x64-msvc@0.1.97': + optional: true + + '@napi-rs/canvas@0.1.97': optionalDependencies: - '@napi-rs/canvas-android-arm64': 0.1.84 - '@napi-rs/canvas-darwin-arm64': 0.1.84 - '@napi-rs/canvas-darwin-x64': 0.1.84 - '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.84 - '@napi-rs/canvas-linux-arm64-gnu': 0.1.84 - '@napi-rs/canvas-linux-arm64-musl': 0.1.84 - '@napi-rs/canvas-linux-riscv64-gnu': 0.1.84 - '@napi-rs/canvas-linux-x64-gnu': 0.1.84 - '@napi-rs/canvas-linux-x64-musl': 0.1.84 - '@napi-rs/canvas-win32-x64-msvc': 0.1.84 + '@napi-rs/canvas-android-arm64': 0.1.97 + '@napi-rs/canvas-darwin-arm64': 0.1.97 + '@napi-rs/canvas-darwin-x64': 0.1.97 + '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.97 + '@napi-rs/canvas-linux-arm64-gnu': 0.1.97 + '@napi-rs/canvas-linux-arm64-musl': 0.1.97 + '@napi-rs/canvas-linux-riscv64-gnu': 0.1.97 + '@napi-rs/canvas-linux-x64-gnu': 0.1.97 + '@napi-rs/canvas-linux-x64-musl': 0.1.97 + '@napi-rs/canvas-win32-arm64-msvc': 0.1.97 + '@napi-rs/canvas-win32-x64-msvc': 0.1.97 '@noble/hashes@2.0.1': {} @@ -2846,17 +3795,34 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@paralleldrive/cuid2@3.0.4': + '@paralleldrive/cuid2@3.3.0': dependencies: '@noble/hashes': 2.0.1 bignumber.js: 9.3.1 error-causes: 3.0.2 - '@paralleldrive/cuid2@3.3.0': + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': dependencies: - '@noble/hashes': 2.0.1 - bignumber.js: 9.3.1 - error-causes: 3.0.2 + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} '@rollup/pluginutils@5.3.0(rollup@4.53.3)': dependencies: @@ -2971,6 +3937,306 @@ snapshots: transitivePeerDependencies: - '@types/node' + '@smithy/config-resolver@4.4.14': + dependencies: + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.4 + '@smithy/util-middleware': 4.2.13 + tslib: 2.8.1 + + '@smithy/core@3.23.14': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-stream': 4.5.22 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.2.13': + dependencies: + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.2.13': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.14.0 + '@smithy/util-hex-encoding': 4.2.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.2.13': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.3.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.2.13': + dependencies: + '@smithy/eventstream-serde-universal': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.2.13': + dependencies: + '@smithy/eventstream-codec': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.3.16': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/querystring-builder': 4.2.13 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + tslib: 2.8.1 + + '@smithy/hash-node@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.2.13': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.4.29': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/middleware-serde': 4.2.17 + '@smithy/node-config-provider': 4.3.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + '@smithy/url-parser': 4.2.13 + '@smithy/util-middleware': 4.2.13 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.5.0': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/node-config-provider': 4.3.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/service-error-classification': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-retry': 4.3.0 + '@smithy/uuid': 1.1.2 + tslib: 2.8.1 + + '@smithy/middleware-serde@4.2.17': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.3.13': + dependencies: + '@smithy/property-provider': 4.2.13 + '@smithy/shared-ini-file-loader': 4.4.8 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.5.2': + dependencies: + '@smithy/protocol-http': 5.3.13 + '@smithy/querystring-builder': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/property-provider@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/protocol-http@5.3.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + '@smithy/util-uri-escape': 4.2.2 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + + '@smithy/shared-ini-file-loader@4.4.8': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/signature-v4@5.3.13': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.13 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/smithy-client@4.12.9': + dependencies: + '@smithy/core': 3.23.14 + '@smithy/middleware-endpoint': 4.4.29 + '@smithy/middleware-stack': 4.2.13 + '@smithy/protocol-http': 5.3.13 + '@smithy/types': 4.14.0 + '@smithy/util-stream': 4.5.22 + tslib: 2.8.1 + + '@smithy/types@4.14.0': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.2.13': + dependencies: + '@smithy/querystring-parser': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/util-base64@4.3.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.2.3': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.2.2': + dependencies: + '@smithy/is-array-buffer': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.3.45': + dependencies: + '@smithy/property-provider': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/util-defaults-mode-node@4.2.49': + dependencies: + '@smithy/config-resolver': 4.4.14 + '@smithy/credential-provider-imds': 4.2.13 + '@smithy/node-config-provider': 4.3.13 + '@smithy/property-provider': 4.2.13 + '@smithy/smithy-client': 4.12.9 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/util-endpoints@3.3.4': + dependencies: + '@smithy/node-config-provider': 4.3.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/util-hex-encoding@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-middleware@4.2.13': + dependencies: + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/util-retry@4.3.0': + dependencies: + '@smithy/service-error-classification': 4.2.13 + '@smithy/types': 4.14.0 + tslib: 2.8.1 + + '@smithy/util-stream@4.5.22': + dependencies: + '@smithy/fetch-http-handler': 5.3.16 + '@smithy/node-http-handler': 4.5.2 + '@smithy/types': 4.14.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 + tslib: 2.8.1 + + '@smithy/util-uri-escape@4.2.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@4.2.2': + dependencies: + '@smithy/util-buffer-from': 4.2.2 + tslib: 2.8.1 + + '@smithy/uuid@1.1.2': + dependencies: + tslib: 2.8.1 + '@standard-schema/spec@1.0.0': {} '@techstark/opencv-js@4.9.0-release.3': {} @@ -2996,6 +4262,8 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/retry@0.12.0': {} + '@vitest/expect@4.0.15': dependencies: '@standard-schema/spec': 1.0.0 @@ -3087,7 +4355,9 @@ snapshots: acorn@8.15.0: {} - adm-zip@0.5.16: {} + adm-zip@0.5.17: {} + + agent-base@7.1.4: {} ajv-draft-04@1.0.0(ajv@8.13.0): optionalDependencies: @@ -3135,6 +4405,8 @@ snapshots: balanced-match@1.0.2: {} + base64-js@1.5.1: {} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 @@ -3145,6 +4417,8 @@ snapshots: boolean@3.2.0: {} + bowser@2.14.1: {} + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -3153,6 +4427,8 @@ snapshots: dependencies: fill-range: 7.1.1 + buffer-equal-constant-time@1.0.1: {} + callsites@3.1.0: {} chai@6.2.1: {} @@ -3235,6 +4511,8 @@ snapshots: dargs@8.1.0: {} + data-uri-to-buffer@4.0.1: {} + date-fns@4.1.0: {} de-indent@1.0.2: {} @@ -3271,6 +4549,10 @@ snapshots: dependencies: is-obj: 2.0.0 + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + emoji-regex@8.0.0: {} enquirer@2.4.1: @@ -3341,6 +4623,8 @@ snapshots: exsolve@1.0.8: {} + extend@3.0.2: {} + extendable-error@0.1.7: {} fast-deep-equal@3.1.3: {} @@ -3353,6 +4637,16 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-xml-builder@1.1.4: + dependencies: + path-expression-matcher: 1.4.0 + + fast-xml-parser@5.5.8: + dependencies: + fast-xml-builder: 1.1.4 + path-expression-matcher: 1.4.0 + strnum: 2.2.3 + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -3361,6 +4655,11 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -3376,6 +4675,10 @@ snapshots: path-exists: 5.0.0 unicorn-magic: 0.1.0 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fs-extra@11.3.2: dependencies: graceful-fs: 4.2.11 @@ -3399,6 +4702,22 @@ snapshots: function-bind@1.1.2: {} + gaxios@7.1.4: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + transitivePeerDependencies: + - supports-color + + gcp-metadata@8.1.2: + dependencies: + gaxios: 7.1.4 + google-logging-utils: 1.1.3 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + get-caller-file@2.0.5: {} get-tsconfig@4.13.0: @@ -3421,7 +4740,7 @@ snapshots: es6-error: 4.1.1 matcher: 3.0.0 roarr: 2.15.4 - semver: 7.7.3 + semver: 7.7.4 serialize-error: 7.0.1 global-directory@4.0.1: @@ -3442,6 +4761,19 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + google-auth-library@10.6.2: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 7.1.4 + gcp-metadata: 8.1.2 + google-logging-utils: 1.1.3 + jws: 4.0.1 + transitivePeerDependencies: + - supports-color + + google-logging-utils@1.1.3: {} + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -3458,6 +4790,13 @@ snapshots: he@1.2.0: {} + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + human-id@4.1.3: {} iconv-lite@0.7.0: @@ -3535,8 +4874,17 @@ snapshots: dependencies: argparse: 2.0.1 + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + json-parse-even-better-errors@2.3.1: {} + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.29.2 + ts-algebra: 2.0.0 + json-schema-traverse@1.0.0: {} json-stringify-safe@5.0.1: {} @@ -3553,6 +4901,17 @@ snapshots: jsonparse@1.3.1: {} + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + kolorist@1.8.0: {} lefthook-darwin-arm64@1.13.6: @@ -3634,6 +4993,8 @@ snapshots: lodash@4.17.21: {} + long@5.3.2: {} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 @@ -3680,21 +5041,33 @@ snapshots: nanoid@3.3.11: {} + node-domexception@1.0.0: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + object-keys@1.1.1: {} obug@2.1.1: {} - onnxruntime-common@1.23.2: {} + onnxruntime-common@1.24.3: {} - onnxruntime-node@1.23.2: + onnxruntime-node@1.24.3: dependencies: - adm-zip: 0.5.16 + adm-zip: 0.5.17 global-agent: 3.0.0 - onnxruntime-common: 1.23.2 + onnxruntime-common: 1.24.3 + + openai@6.33.0(ws@8.20.0): + optionalDependencies: + ws: 8.20.0 opencollective-postinstall@2.0.3: {} @@ -3722,6 +5095,11 @@ snapshots: p-map@2.1.0: {} + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + p-try@2.2.0: {} package-manager-detector@0.2.11: @@ -3745,6 +5123,8 @@ snapshots: path-exists@5.0.0: {} + path-expression-matcher@1.4.0: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -3753,14 +5133,9 @@ snapshots: pathe@2.0.3: {} - pdf-to-png-converter@3.11.0: - dependencies: - '@napi-rs/canvas': 0.1.84 - pdfjs-dist: 5.4.296 - pdfjs-dist@5.4.296: optionalDependencies: - '@napi-rs/canvas': 0.1.84 + '@napi-rs/canvas': 0.1.97 picocolors@1.1.1: {} @@ -3794,6 +5169,21 @@ snapshots: prettier@2.8.8: {} + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 24.12.2 + long: 5.3.2 + punycode@2.3.1: {} quansync@0.2.11: {} @@ -3825,6 +5215,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + retry@0.13.1: {} + reusify@1.1.0: {} roarr@2.15.4: @@ -3868,6 +5260,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 + safe-buffer@5.2.1: {} + safer-buffer@2.1.2: {} semver-compare@1.0.0: {} @@ -3878,6 +5272,8 @@ snapshots: semver@7.7.3: {} + semver@7.7.4: {} + serialize-error@7.0.1: dependencies: type-fest: 0.13.1 @@ -3886,7 +5282,7 @@ snapshots: dependencies: color: 4.2.3 detect-libc: 2.1.2 - semver: 7.7.3 + semver: 7.7.4 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 @@ -3959,6 +5355,8 @@ snapshots: strip-json-comments@3.1.1: {} + strnum@2.2.3: {} + supports-color@8.1.1: dependencies: has-flag: 4.0.0 @@ -3967,7 +5365,7 @@ snapshots: term-size@2.2.1: {} - tesseract.js-core@6.0.0: {} + tesseract.js-core@6.1.2: {} tesseract.js@6.0.1: dependencies: @@ -3977,7 +5375,7 @@ snapshots: node-fetch: 2.7.0 opencollective-postinstall: 2.0.3 regenerator-runtime: 0.13.11 - tesseract.js-core: 6.0.0 + tesseract.js-core: 6.1.2 wasm-feature-detect: 1.8.0 zlibjs: 0.3.1 transitivePeerDependencies: @@ -4006,8 +5404,9 @@ snapshots: tr46@0.0.3: {} - tslib@2.8.1: - optional: true + ts-algebra@2.0.0: {} + + tslib@2.8.1: {} tsx@4.21.0: dependencies: @@ -4032,9 +5431,9 @@ snapshots: universalify@2.0.1: {} - unpdf@1.4.0(@napi-rs/canvas@0.1.84): + unpdf@1.4.0(@napi-rs/canvas@0.1.97): optionalDependencies: - '@napi-rs/canvas': 0.1.84 + '@napi-rs/canvas': 0.1.97 uri-js@4.4.1: dependencies: @@ -4116,6 +5515,8 @@ snapshots: wasm-feature-detect@1.8.0: {} + web-streams-polyfill@3.3.3: {} + webidl-conversions@3.0.1: {} whatwg-url@5.0.0: @@ -4143,6 +5544,8 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + ws@8.20.0: {} + y18n@5.0.8: {} yallist@4.0.0: {} diff --git a/scripts/auto-changeset.ts b/scripts/auto-changeset.ts index d813288..b86631c 100644 --- a/scripts/auto-changeset.ts +++ b/scripts/auto-changeset.ts @@ -54,7 +54,10 @@ function getCommitsSinceLastRelease(): string[] { if (!commits) return []; // Split on null byte (not newline) to handle multi-line commit bodies - return commits.split('\x00').map((s) => s.trim()).filter(Boolean); + return commits + .split('\x00') + .map((s) => s.trim()) + .filter(Boolean); } function parseConventionalCommit(commitLine: string): ParsedCommit | null { @@ -103,9 +106,7 @@ function determineVersionBump( const hasFeature = commits.some((c) => c.type === 'feat'); const hasFix = commits.some((c) => ['fix', 'perf'].includes(c.type)); - const hasDeps = commits.some( - (c) => c.type === 'chore' && c.scope === 'deps', - ); + const hasDeps = commits.some((c) => c.type === 'chore' && c.scope === 'deps'); if (hasFeature || hasFix || hasDeps) return 'patch'; @@ -119,46 +120,44 @@ function generateChangesetContent( const features = commits.filter((c) => c.type === 'feat'); const fixes = commits.filter((c) => c.type === 'fix'); const breaking = commits.filter((c) => c.breaking); - const deps = commits.filter( - (c) => c.type === 'chore' && c.scope === 'deps', - ); + const deps = commits.filter((c) => c.type === 'chore' && c.scope === 'deps'); - let content = `---\n`; + let content = '---\n'; content += `"${PACKAGE_NAME}": ${bump}\n`; - content += `---\n\n`; + content += '---\n\n'; if (breaking.length > 0) { - content += `### Breaking Changes\n\n`; - breaking.forEach((c) => { + content += '### Breaking Changes\n\n'; + for (const c of breaking) { content += `- ${c.message}${c.scope ? ` (${c.scope})` : ''}\n`; - }); - content += `\n`; + } + content += '\n'; } if (features.length > 0) { - content += `### Features\n\n`; - features.forEach((c) => { + content += '### Features\n\n'; + for (const c of features) { content += `- ${c.message}${c.scope ? ` (${c.scope})` : ''}\n`; - }); - content += `\n`; + } + content += '\n'; } if (fixes.length > 0) { - content += `### Bug Fixes\n\n`; - fixes.forEach((c) => { + content += '### Bug Fixes\n\n'; + for (const c of fixes) { content += `- ${c.message}${c.scope ? ` (${c.scope})` : ''}\n`; - }); - content += `\n`; + } + content += '\n'; } if (deps.length > 0) { - content += `### Dependencies\n\n`; - deps.forEach((c) => { + content += '### Dependencies\n\n'; + for (const c of deps) { content += `- ${c.message}\n`; - }); + } } - return content.trim() + '\n'; + return `${content.trim()}\n`; } function hasExistingChangesets(): boolean { diff --git a/scripts/benchmark-providers.ts b/scripts/benchmark-providers.ts index 2627e04..3fc051b 100644 --- a/scripts/benchmark-providers.ts +++ b/scripts/benchmark-providers.ts @@ -7,15 +7,21 @@ * Usage: npx tsx scripts/benchmark-providers.ts [pdf-path] */ -import { getPDFReader } from '../src/index.js'; -import { readFileSync, existsSync } from 'node:fs'; +import { existsSync, readFileSync } from 'node:fs'; import { resolve } from 'node:path'; +import { getPDFReader } from '../src/index.js'; const testPdfs = [ // Small PDF (~100KB) - resolve(import.meta.dirname, '../test/Signed-Meeting-Minutes-July-9-2024-Regular-Meeting-of-Council.pdf'), + resolve( + import.meta.dirname, + '../test/Signed-Meeting-Minutes-July-9-2024-Regular-Meeting-of-Council.pdf', + ), // Medium PDF (~9MB) - resolve(import.meta.dirname, '../test/Agenda-Package-August-27-2024-Regular-Council-Meeting.pdf'), + resolve( + import.meta.dirname, + '../test/Agenda-Package-August-27-2024-Regular-Council-Meeting.pdf', + ), ]; type Provider = 'unpdf' | 'kreuzberg'; @@ -36,13 +42,13 @@ async function measureMemory(): Promise { if (global.gc) { global.gc(); } - await new Promise(r => setTimeout(r, 100)); + await new Promise((r) => setTimeout(r, 100)); return process.memoryUsage().heapUsed / 1024 / 1024; } async function benchmarkProvider( provider: Provider, - pdfPath: string + pdfPath: string, ): Promise { const fileName = pdfPath.split('/').pop() || pdfPath; const fileSize = existsSync(pdfPath) @@ -110,8 +116,12 @@ async function runBenchmarks(pdfPaths: string[]) { if (result.success) { console.log(` Duration: ${result.duration}ms`); - console.log(` Memory: +${result.memoryUsedMB}MB (peak: ${result.peakMemoryMB}MB)`); - console.log(` Text extracted: ${result.textLength.toLocaleString()} chars`); + console.log( + ` Memory: +${result.memoryUsedMB}MB (peak: ${result.peakMemoryMB}MB)`, + ); + console.log( + ` Text extracted: ${result.textLength.toLocaleString()} chars`, + ); } else { console.log(` FAILED: ${result.error}`); } @@ -129,7 +139,7 @@ async function runBenchmarks(pdfPaths: string[]) { for (const r of results) { const status = r.success ? '' : ' [FAILED]'; console.log( - `| ${r.file.slice(0, 30)}... | ${r.provider.padEnd(10)} | ${String(r.duration).padStart(6)}ms | ${String(r.memoryUsedMB).padStart(6)}MB | ${r.textLength.toLocaleString().padStart(11)} |${status}` + `| ${r.file.slice(0, 30)}... | ${r.provider.padEnd(10)} | ${String(r.duration).padStart(6)}ms | ${String(r.memoryUsedMB).padStart(6)}MB | ${r.textLength.toLocaleString().padStart(11)} |${status}`, ); } @@ -143,26 +153,41 @@ async function runBenchmarks(pdfPaths: string[]) { } for (const [file, fileResults] of byFile) { - const unpdf = fileResults.find(r => r.provider === 'unpdf'); - const kreuzberg = fileResults.find(r => r.provider === 'kreuzberg'); + const unpdf = fileResults.find((r) => r.provider === 'unpdf'); + const kreuzberg = fileResults.find((r) => r.provider === 'kreuzberg'); if (unpdf?.success && kreuzberg?.success) { - const speedDiff = ((unpdf.duration - kreuzberg.duration) / unpdf.duration * 100).toFixed(1); - const memDiff = ((unpdf.memoryUsedMB - kreuzberg.memoryUsedMB) / (unpdf.memoryUsedMB || 1) * 100).toFixed(1); + const speedDiff = ( + ((unpdf.duration - kreuzberg.duration) / unpdf.duration) * + 100 + ).toFixed(1); + const memDiff = ( + ((unpdf.memoryUsedMB - kreuzberg.memoryUsedMB) / + (unpdf.memoryUsedMB || 1)) * + 100 + ).toFixed(1); console.log(`\n ${file}:`); - console.log(` Speed: kreuzberg is ${Number(speedDiff) > 0 ? speedDiff + '% faster' : Math.abs(Number(speedDiff)) + '% slower'}`); - console.log(` Memory: kreuzberg uses ${Number(memDiff) > 0 ? memDiff + '% less' : Math.abs(Number(memDiff)) + '% more'}`); + console.log( + ` Speed: kreuzberg is ${Number(speedDiff) > 0 ? `${speedDiff}% faster` : `${Math.abs(Number(speedDiff))}% slower`}`, + ); + console.log( + ` Memory: kreuzberg uses ${Number(memDiff) > 0 ? `${memDiff}% less` : `${Math.abs(Number(memDiff))}% more`}`, + ); } } } // Main const customPdf = process.argv[2]; -const pdfsToTest = customPdf ? [resolve(customPdf)] : testPdfs.filter(existsSync); +const pdfsToTest = customPdf + ? [resolve(customPdf)] + : testPdfs.filter(existsSync); if (pdfsToTest.length === 0) { - console.error('No test PDFs found. Provide a PDF path or ensure test PDFs exist.'); + console.error( + 'No test PDFs found. Provide a PDF path or ensure test PDFs exist.', + ); process.exit(1); } diff --git a/src/extraction.test.ts b/src/extraction.test.ts index db3a2e1..7222f3b 100644 --- a/src/extraction.test.ts +++ b/src/extraction.test.ts @@ -61,9 +61,9 @@ describe('PDF Content Extraction', () => { expect(typeof text).toBe('string'); // Must extract meaningful content from 3-page document - expect(text!.length).toBeGreaterThan(100); + expect(text?.length).toBeGreaterThan(100); // Should contain "Bentley" (the town name in the document) - expect(text!.toLowerCase()).toContain('bentley'); + expect(text?.toLowerCase()).toContain('bentley'); }, 60000); }); diff --git a/src/factory.test.ts b/src/factory.test.ts index 85805b4..29d1ad6 100644 --- a/src/factory.test.ts +++ b/src/factory.test.ts @@ -24,6 +24,25 @@ describe('PDF Factory Tests', () => { expect(reader.constructor.name).toBe('CombinedNodeProvider'); }); + it('should route explicit onnx OCR requests to unpdf provider', async () => { + const reader = await getPDFReader({ + provider: 'auto', + ocrProvider: 'onnx', + }); + expect(reader.constructor.name).toBe('CombinedNodeProvider'); + }); + + it('should reject unsupported Kreuzberg OCR backends early', async () => { + await expect( + getPDFReader({ + provider: 'kreuzberg', + ocrProvider: 'onnx', + }), + ).rejects.toThrow( + "kreuzberg provider does not support OCR provider 'onnx'", + ); + }); + it('should reject invalid provider for Node.js environment', async () => { await expect(getPDFReader({ provider: 'pdfjs' as any })).rejects.toThrow( 'pdfjs provider is only available in browser environments', diff --git a/src/index.ts b/src/index.ts index eca2df6..183c0e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,7 @@ export { export * from './shared/types'; // Legacy compatibility exports for backward compatibility with existing code -import { getPDFReader } from './shared/factory'; +import { getPDFReader, initializeProviders } from './shared/factory'; /** * Extract text from a PDF file (legacy compatibility) @@ -68,11 +68,9 @@ export async function checkOCRDependencies() { return reader.checkDependencies(); } -// Initialize providers on module load -import('./shared/factory.js').then(({ initializeProviders }) => { - initializeProviders().catch(() => { - // Ignore initialization errors - providers will fail when used - }); +// Initialize providers on module load. +void initializeProviders().catch(() => { + // Ignore initialization errors - providers will fail when used }); // Default export for convenience diff --git a/src/legacy.test.ts b/src/legacy.test.ts index f35a27c..e8a8754 100644 --- a/src/legacy.test.ts +++ b/src/legacy.test.ts @@ -5,8 +5,8 @@ import { checkOCRDependencies, extractImagesFromPDF, extractTextFromPDF, - performOCROnImages, getPDFReader, + performOCROnImages, } from './index'; describe('Legacy Compatibility Functions', () => { diff --git a/src/node/combined.test.ts b/src/node/combined.test.ts new file mode 100644 index 0000000..3846344 --- /dev/null +++ b/src/node/combined.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it, vi } from 'vitest'; +import { CombinedNodeProvider } from './combined'; +import { UnpdfProvider } from './unpdf'; + +describe('CombinedNodeProvider', () => { + it('should report unavailable when unpdf dependencies are missing', async () => { + const reader = new CombinedNodeProvider() as any; + + reader.unpdfProvider = { + checkDependencies: vi.fn().mockResolvedValue({ + available: false, + error: 'page rendering unavailable: worker mismatch', + details: { + unpdf: true, + pageRendering: false, + }, + }), + }; + reader.ocrFactory = { + isOCRAvailable: vi.fn().mockResolvedValue(true), + getProvidersInfo: vi.fn().mockResolvedValue([{ provider: 'onnx' }]), + }; + + await expect(reader.checkDependencies()).resolves.toMatchObject({ + available: false, + error: 'page rendering unavailable: worker mismatch', + details: { + unpdf: true, + pageRendering: false, + ocr: true, + ocrProviders: 1, + }, + }); + }); +}); + +describe('UnpdfProvider', () => { + it('should surface page rendering dependency failures', async () => { + const reader = new UnpdfProvider() as any; + + reader.loadUnpdf = vi.fn().mockResolvedValue({}); + reader.verifyRenderDependencies = vi + .fn() + .mockRejectedValue(new Error('worker mismatch')); + + await expect(reader.checkDependencies()).resolves.toMatchObject({ + available: false, + error: 'page rendering unavailable: worker mismatch', + details: { + unpdf: true, + pageRendering: false, + }, + }); + }); +}); diff --git a/src/node/combined.ts b/src/node/combined.ts index 2b54dbd..5ca23b5 100644 --- a/src/node/combined.ts +++ b/src/node/combined.ts @@ -159,8 +159,10 @@ export class CombinedNodeProvider extends BasePDFReader { ...ocrDetails, }; - // At least one provider should be available for this to be considered available - const available = unpdfDeps.available || ocrAvailable; + // This provider depends on both core unpdf parsing and page rendering. + const available = + unpdfDeps.details.unpdf === true && + unpdfDeps.details.pageRendering === true; let error: string | undefined; if (!available) { diff --git a/src/node/kreuzberg.ts b/src/node/kreuzberg.ts index 1800781..c3893cf 100644 --- a/src/node/kreuzberg.ts +++ b/src/node/kreuzberg.ts @@ -19,6 +19,7 @@ import type { PDFSource, } from '../shared/types'; import { PDFDependencyError, PDFUnsupportedError } from '../shared/types'; +import { ensureTessdataPrefix, formatPdfOcrRuntimeIssue } from './ocr-runtime'; /** * Configuration options for the Kreuzberg provider @@ -125,6 +126,14 @@ export class KreuzbergProvider extends BasePDFReader { return Object.keys(config).length > 0 ? config : null; } + private async prepareOCRRuntime(options?: ExtractTextOptions) { + if (options?.skipOCRFallback || this.options.ocrBackend !== 'tesseract') { + return null; + } + + return ensureTessdataPrefix(this.options.ocrLanguage); + } + /** * Extract text content from a PDF using Kreuzberg * @@ -147,7 +156,10 @@ export class KreuzbergProvider extends BasePDFReader { return null; } + let tessdata = null; + try { + tessdata = await this.prepareOCRRuntime(options); const kreuzberg = await this.loadKreuzberg(); const config = this.buildConfig(options); @@ -172,7 +184,12 @@ export class KreuzbergProvider extends BasePDFReader { return result.content; } catch (error) { - console.error('Kreuzberg text extraction failed:', error); + const message = formatPdfOcrRuntimeIssue(error, { + backend: this.options.ocrBackend, + language: this.options.ocrLanguage, + tessdata, + }); + console.error('Kreuzberg text extraction failed:', message); return null; } } @@ -318,12 +335,70 @@ export class KreuzbergProvider extends BasePDFReader { */ async checkDependencies(): Promise { try { - await this.loadKreuzberg(); + const kreuzberg = await this.loadKreuzberg(); + const availableBackends = + typeof kreuzberg.listOcrBackends === 'function' + ? kreuzberg.listOcrBackends() + : []; + + if ( + this.options.ocrBackend && + availableBackends.length > 0 && + !availableBackends.includes(this.options.ocrBackend) + ) { + return { + available: false, + error: formatPdfOcrRuntimeIssue( + new Error( + `OCR backend '${this.options.ocrBackend}' not registered`, + ), + { + backend: this.options.ocrBackend, + }, + ), + details: { + kreuzberg: true, + ocrBackend: this.options.ocrBackend, + ocrBackends: availableBackends, + }, + }; + } + + const tessdata = + this.options.ocrBackend === 'tesseract' + ? await ensureTessdataPrefix(this.options.ocrLanguage) + : null; + + if (this.options.ocrBackend === 'tesseract' && !tessdata?.path) { + return { + available: false, + error: formatPdfOcrRuntimeIssue( + new Error( + `Failed to initialize language '${this.options.ocrLanguage}'`, + ), + { + backend: this.options.ocrBackend, + language: this.options.ocrLanguage, + tessdata, + }, + ), + details: { + kreuzberg: true, + ocrBackend: this.options.ocrBackend, + ocrBackends: availableBackends, + tessdataChecked: tessdata?.checked || [], + tessdataPrefix: process.env.TESSDATA_PREFIX || null, + }, + }; + } return { available: true, details: { kreuzberg: true, + ocrBackend: this.options.ocrBackend, + ocrBackends: availableBackends, + tessdataPrefix: process.env.TESSDATA_PREFIX || null, }, }; } catch (error) { diff --git a/src/node/ocr-runtime.test.ts b/src/node/ocr-runtime.test.ts new file mode 100644 index 0000000..7590956 --- /dev/null +++ b/src/node/ocr-runtime.test.ts @@ -0,0 +1,113 @@ +import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { + formatPdfOcrRuntimeIssue, + normalizeTessdataDirectory, + parseTessdataDirectoryFromTesseractOutput, +} from './ocr-runtime'; + +const tempDirectories: string[] = []; + +describe('OCR Runtime Helpers', () => { + afterEach(async () => { + await Promise.allSettled( + tempDirectories + .splice(0) + .map((directory) => rm(directory, { force: true, recursive: true })), + ); + vi.resetModules(); + Reflect.deleteProperty(process.env, 'TESSDATA_PREFIX'); + }); + + it('should parse tessdata directory from tesseract output', () => { + const output = + 'List of available languages in "/usr/share/tessdata/" (2):\neng\nfra\n'; + expect(parseTessdataDirectoryFromTesseractOutput(output)).toBe( + '/usr/share/tessdata/', + ); + }); + + it('should parse unquoted tessdata directory from tesseract output', () => { + const output = + 'List of available languages in /usr/share/tessdata/ (2):\neng\nfra\n'; + expect(parseTessdataDirectoryFromTesseractOutput(output)).toBe( + '/usr/share/tessdata/', + ); + }); + + it('should normalize a parent directory that contains tessdata', async () => { + const parent = await mkdtemp(join(tmpdir(), 'pdf-tessdata-')); + tempDirectories.push(parent); + const tessdataDirectory = join(parent, 'tessdata'); + await mkdir(tessdataDirectory); + await writeFile(join(tessdataDirectory, 'eng.traineddata'), 'stub'); + + await expect(normalizeTessdataDirectory(parent)).resolves.toBe( + tessdataDirectory, + ); + }); + + it('should validate cached tessdata directories per language', async () => { + const parent = await mkdtemp(join(tmpdir(), 'pdf-tessdata-')); + tempDirectories.push(parent); + const tessdataDirectory = join(parent, 'tessdata'); + await mkdir(tessdataDirectory); + await writeFile(join(tessdataDirectory, 'eng.traineddata'), 'stub'); + process.env.TESSDATA_PREFIX = parent; + + const { ensureTessdataPrefix } = await import('./ocr-runtime'); + + await expect(ensureTessdataPrefix('eng')).resolves.toMatchObject({ + path: tessdataDirectory, + source: 'env', + }); + + await expect( + ensureTessdataPrefix('__codex_missing_language__'), + ).resolves.toMatchObject({ + reason: 'Unable to find __codex_missing_language__.traineddata', + }); + }); + + it('should format missing tessdata errors with actionable guidance', () => { + const message = formatPdfOcrRuntimeIssue( + new Error("Failed to initialize language 'eng'"), + { + language: 'eng', + tessdata: { + checked: ['/usr/share/tessdata'], + reason: 'Unable to find eng.traineddata', + }, + }, + ); + + expect(message).toContain("Install the 'eng' traineddata package"); + expect(message).toContain('/usr/share/tessdata'); + }); + + it('should format pdfjs worker mismatch errors clearly', () => { + const message = formatPdfOcrRuntimeIssue( + new Error( + 'The API version "5.4.624" does not match the Worker version "5.4.296".', + ), + ); + + expect(message).toContain('pdfjs-dist API 5.4.624'); + expect(message).toContain('worker 5.4.296'); + expect(message).toContain('unpdf/pdfjs-dist runtime'); + }); + + it('should explain unsupported Kreuzberg OCR backends', () => { + const message = formatPdfOcrRuntimeIssue( + new Error("Plugin error in 'onnx': OCR backend 'onnx' not registered"), + { + backend: 'onnx', + }, + ); + + expect(message).toContain("Kreuzberg OCR backend 'onnx' is not registered"); + expect(message).toContain('@happyvertical/ocr'); + }); +}); diff --git a/src/node/ocr-runtime.ts b/src/node/ocr-runtime.ts new file mode 100644 index 0000000..55d4418 --- /dev/null +++ b/src/node/ocr-runtime.ts @@ -0,0 +1,238 @@ +import { execFile as execFileCallback } from 'node:child_process'; +import { constants } from 'node:fs'; +import { access } from 'node:fs/promises'; +import { join } from 'node:path'; +import { promisify } from 'node:util'; + +const execFile = promisify(execFileCallback); + +const COMMON_TESSDATA_DIRECTORIES = [ + '/usr/share/tesseract-ocr/5/tessdata', + '/usr/share/tesseract-ocr/4.00/tessdata', + '/usr/share/tessdata', + '/usr/local/share/tessdata', + '/opt/homebrew/share/tessdata', + '/opt/local/share/tessdata', +]; + +export interface TessdataResolution { + checked: string[]; + path?: string; + reason?: string; + source?: 'cache' | 'common-path' | 'env' | 'tesseract'; +} + +const cachedTessdataDirectories = new Map(); + +async function isReadableFile(path: string): Promise { + try { + await access(path, constants.R_OK); + return true; + } catch { + return false; + } +} + +export function parseTessdataDirectoryFromTesseractOutput( + output: string, +): string | null { + if (!output.trim()) { + return null; + } + + const lines = output + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean); + + const firstLine = lines[0]; + if (!firstLine) { + return null; + } + + const tessdataMatch = firstLine.match( + /List of available languages in "?(.+?)"? \(\d+\):/, + ); + if (tessdataMatch?.[1]) { + return tessdataMatch[1]; + } + + if (firstLine.startsWith('/') || /^[A-Za-z]:[\\/]/.test(firstLine)) { + return firstLine; + } + + return null; +} + +export async function normalizeTessdataDirectory( + candidate: string | null | undefined, + language = 'eng', +): Promise { + if (!candidate?.trim()) { + return null; + } + + const trimmed = candidate.trim().replace(/^['"]|['"]$/g, ''); + const candidates = [trimmed, join(trimmed, 'tessdata')]; + + for (const directory of candidates) { + const traineddataPath = join(directory, `${language}.traineddata`); + if (await isReadableFile(traineddataPath)) { + return directory; + } + } + + return null; +} + +async function resolveTessdataFromTesseractBinary( + language: string, + checked: string[], +): Promise { + try { + const { stdout } = await execFile('tesseract', ['--list-langs'], { + env: process.env, + timeout: 5000, + }); + const candidate = parseTessdataDirectoryFromTesseractOutput(stdout); + if (candidate) { + checked.push(candidate); + return normalizeTessdataDirectory(candidate, language); + } + } catch { + // Ignore subprocess errors and fall back to common install locations. + } + + return null; +} + +export async function ensureTessdataPrefix( + language = 'eng', +): Promise { + const checked: string[] = []; + const cachedTessdataDirectory = cachedTessdataDirectories.get(language); + + if (cachedTessdataDirectory) { + const normalized = await normalizeTessdataDirectory( + cachedTessdataDirectory, + language, + ); + if (normalized) { + process.env.TESSDATA_PREFIX = normalized; + return { + checked, + path: normalized, + source: 'cache', + }; + } + + cachedTessdataDirectories.delete(language); + } + + const envCandidate = process.env.TESSDATA_PREFIX; + if (envCandidate) { + checked.push(envCandidate); + const normalized = await normalizeTessdataDirectory(envCandidate, language); + if (normalized) { + cachedTessdataDirectories.set(language, normalized); + process.env.TESSDATA_PREFIX = normalized; + return { + checked, + path: normalized, + source: 'env', + }; + } + } + + const fromTesseract = await resolveTessdataFromTesseractBinary( + language, + checked, + ); + if (fromTesseract) { + cachedTessdataDirectories.set(language, fromTesseract); + process.env.TESSDATA_PREFIX = fromTesseract; + return { + checked, + path: fromTesseract, + source: 'tesseract', + }; + } + + for (const directory of COMMON_TESSDATA_DIRECTORIES) { + checked.push(directory); + const normalized = await normalizeTessdataDirectory(directory, language); + if (normalized) { + cachedTessdataDirectories.set(language, normalized); + process.env.TESSDATA_PREFIX = normalized; + return { + checked, + path: normalized, + source: 'common-path', + }; + } + } + + return { + checked, + reason: `Unable to find ${language}.traineddata`, + }; +} + +function getErrorMessage(error: unknown): string { + if (error instanceof Error) { + return error.message; + } + + return String(error); +} + +export function formatPdfOcrRuntimeIssue( + error: unknown, + context: { + backend?: string; + language?: string; + tessdata?: TessdataResolution | null; + } = {}, +): string { + const message = getErrorMessage(error); + + if ( + /Failed to initialize language|TESSDATA_PREFIX|traineddata|Tesseract couldn't load any languages/i.test( + message, + ) + ) { + const language = context.language || 'eng'; + const tessdata = context.tessdata; + const detectedPath = tessdata?.path + ? ` Auto-detected tessdata path: ${tessdata.path}.` + : ''; + const checkedPaths = + tessdata?.checked?.length && !tessdata?.path + ? ` Checked: ${tessdata.checked.join(', ')}.` + : ''; + + return `Kreuzberg could not initialize Tesseract language '${language}'. Install the '${language}' traineddata package and point TESSDATA_PREFIX at the tessdata directory.${detectedPath}${checkedPaths} Original error: ${message}`; + } + + const versionMismatchMatch = message.match( + /API version "([^"]+)" does not match the Worker version "([^"]+)"/i, + ); + if (versionMismatchMatch) { + const [, apiVersion, workerVersion] = versionMismatchMatch; + return `PDF page rendering failed because pdfjs-dist API ${apiVersion} does not match worker ${workerVersion}. Ensure the installed @happyvertical/pdf and unpdf/pdfjs-dist runtime resolve compatible pdfjs-dist builds. Original error: ${message}`; + } + + const backendMatch = message.match( + /OCR backend ['"]([^'"]+)['"] not registered/i, + ); + if (backendMatch) { + const backend = context.backend || backendMatch[1]; + return `Kreuzberg OCR backend '${backend}' is not registered in this runtime. Use the built-in 'tesseract' backend, or route OCR through @happyvertical/ocr for providers like 'onnx'. Original error: ${message}`; + } + + if (/libonnxruntime|ONNX Runtime dylib/i.test(message)) { + return `The requested ONNX OCR runtime is missing its native library. Install the ONNX runtime for the target image, or use the @happyvertical/ocr 'onnx' provider instead of a Kreuzberg backend. Original error: ${message}`; + } + + return message; +} diff --git a/src/node/unpdf.ts b/src/node/unpdf.ts index 7103c5c..ffc2594 100644 --- a/src/node/unpdf.ts +++ b/src/node/unpdf.ts @@ -2,11 +2,10 @@ * @happyvertical/pdf - unpdf provider for Node.js PDF processing */ -import { randomBytes } from 'node:crypto'; import { promises as fs } from 'node:fs'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { pdfToPng } from 'pdf-to-png-converter'; +import { createRequire } from 'node:module'; +import { pathToFileURL } from 'node:url'; +import { Canvas } from '@napi-rs/canvas'; import { BasePDFReader } from '../shared/base'; import type { DependencyCheckResult, @@ -19,6 +18,9 @@ import type { RenderPagesOptions, } from '../shared/types'; import { PDFDependencyError } from '../shared/types'; +import { formatPdfOcrRuntimeIssue } from './ocr-runtime'; + +const require = createRequire(import.meta.url); /** * PDF reader implementation using unpdf library for Node.js @@ -31,6 +33,23 @@ import { PDFDependencyError } from '../shared/types'; export class UnpdfProvider extends BasePDFReader { protected name = 'unpdf'; private unpdf: any = null; + private configuredPdfjsWorkerSrc: string | null = null; + + private rgbaToRgbBuffer(data: Uint8ClampedArray): Buffer { + const rgbData = Buffer.alloc((data.length / 4) * 3); + + for ( + let sourceIndex = 0, targetIndex = 0; + sourceIndex < data.length; + sourceIndex += 4 + ) { + rgbData[targetIndex++] = data[sourceIndex]; + rgbData[targetIndex++] = data[sourceIndex + 1]; + rgbData[targetIndex++] = data[sourceIndex + 2]; + } + + return rgbData; + } /** * Lazy load unpdf dependencies @@ -48,24 +67,61 @@ export class UnpdfProvider extends BasePDFReader { } } + private async ensurePdfjsWorkerConfigured() { + const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs'); + const workerPath = require.resolve( + 'pdfjs-dist/legacy/build/pdf.worker.mjs', + ); + const workerSrc = pathToFileURL(workerPath).href; + + pdfjs.GlobalWorkerOptions.workerPort = null; + + if (this.configuredPdfjsWorkerSrc !== workerSrc) { + pdfjs.GlobalWorkerOptions.workerSrc = workerSrc; + this.configuredPdfjsWorkerSrc = workerSrc; + } + } + + private async verifyRenderDependencies() { + await this.ensurePdfjsWorkerConfigured(); + + const canvas = new Canvas(1, 1); + const context = canvas.getContext('2d'); + + context.fillStyle = '#ffffff'; + context.fillRect(0, 0, 1, 1); + canvas.width = 0; + canvas.height = 0; + } + /** * Override normalizeSource to handle file reading in Node.js */ - protected async normalizeSource(source: PDFSource): Promise { + protected async normalizeSource(source: PDFSource): Promise { if (typeof source === 'string') { try { const buffer = await fs.readFile(source); - return buffer; + return new Uint8Array( + buffer.buffer, + buffer.byteOffset, + buffer.byteLength, + ); } catch (error) { throw new Error(`Failed to read PDF file: ${(error as Error).message}`); } } else if (source instanceof Buffer) { - return source; + return new Uint8Array( + source.buffer, + source.byteOffset, + source.byteLength, + ); + } else if (source instanceof ArrayBuffer) { + return new Uint8Array(source); } else if (source instanceof Uint8Array) { - return Buffer.from(source); + return source; } else { throw new Error( - 'Invalid PDF source: must be file path, Buffer, or Uint8Array', + 'Invalid PDF source: must be file path, Buffer, ArrayBuffer, or Uint8Array', ); } } @@ -97,7 +153,7 @@ export class UnpdfProvider extends BasePDFReader { throw new Error('Invalid PDF data'); } - const pdf = await unpdf.getDocumentProxy(new Uint8Array(buffer)); + const pdf = await unpdf.getDocumentProxy(buffer); const totalPages = pdf.numPages; // Normalize pages to extract @@ -153,7 +209,7 @@ export class UnpdfProvider extends BasePDFReader { throw new Error('Invalid PDF data'); } - const pdf = await unpdf.getDocumentProxy(new Uint8Array(buffer)); + const pdf = await unpdf.getDocumentProxy(buffer); const metadata = await pdf.getMetadata(); return { @@ -179,7 +235,7 @@ export class UnpdfProvider extends BasePDFReader { try { const unpdf = await this.loadUnpdf(); const buffer = await this.normalizeSource(source); - const pdf = await unpdf.getDocumentProxy(new Uint8Array(buffer)); + const pdf = await unpdf.getDocumentProxy(buffer); return this.createDefaultMetadata(pdf.numPages); } catch { return this.createDefaultMetadata(0); @@ -224,7 +280,7 @@ export class UnpdfProvider extends BasePDFReader { throw new Error('Invalid PDF data'); } - const pdf = await unpdf.getDocumentProxy(new Uint8Array(buffer)); + const pdf = await unpdf.getDocumentProxy(buffer); const allImages: PDFImage[] = []; // Extract from all pages @@ -303,69 +359,66 @@ export class UnpdfProvider extends BasePDFReader { } try { + await this.ensurePdfjsWorkerConfigured(); + const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs'); const buffer = await this.normalizeSource(source); if (!this.validatePDFData(buffer)) { throw new Error('Invalid PDF data'); } - // pdf-to-png-converter needs a file path, so write buffer to temp file - const tempPdfPath = join( - tmpdir(), - `pdf-render-${randomBytes(8).toString('hex')}.pdf`, - ); - - let pdfPath: string; - if (typeof source === 'string') { - // Already have a file path - pdfPath = source; - } else { - // Write buffer to temp file - await fs.writeFile(tempPdfPath, buffer); - pdfPath = tempPdfPath; - } + const document = await pdfjs.getDocument({ + data: buffer, + }).promise; try { - // Determine output folder - const outputFolder = options?.outputFolder || '/tmp/pdf-rendered-pages'; - - // Determine pages to render - let pagesToProcess: number[] | undefined; - if (options?.pages && options.pages.length > 0) { - pagesToProcess = options.pages; - } + const pagesToRender = this.normalizePages( + options?.pages, + document.numPages, + ); + const images: PDFImage[] = []; - // Render pages using pdf-to-png-converter - const pngPages = await pdfToPng(pdfPath, { - outputFolder, - viewportScale: options?.scale || 2.0, // Default 2x for OCR quality - pagesToProcess, - verbosityLevel: 0, // Quiet mode - }); - - // Convert to PDFImage format - const images: PDFImage[] = pngPages.map((page) => ({ - data: Buffer.from(page.content as Uint8Array), - format: 'png', - pageNumber: page.pageNumber, - width: undefined, // Will be determined by OCR provider - height: undefined, - channels: undefined, - })); + for (const pageNumber of pagesToRender) { + const page = await document.getPage(pageNumber); + const viewport = page.getViewport({ scale: options?.scale || 2.0 }); + const width = Math.ceil(viewport.width); + const height = Math.ceil(viewport.height); + const canvas = new Canvas(width, height); + const context = canvas.getContext('2d'); - return images; - } finally { - // Clean up temp file if we created one - if (typeof source !== 'string') { try { - await fs.unlink(tempPdfPath); - } catch { - // Ignore cleanup errors + await page.render({ + canvas: canvas as any, + canvasContext: context as any, + viewport, + }).promise; + + const imageData = context.getImageData(0, 0, width, height); + + images.push({ + data: this.rgbaToRgbBuffer(imageData.data), + format: 'rgb', + pageNumber, + width, + height, + channels: 3, + }); + } finally { + page.cleanup(); + canvas.width = 0; + canvas.height = 0; } } + + return images; + } finally { + await document.destroy(); } } catch (error) { - console.error('unpdf page rendering failed:', error); + console.error( + 'unpdf page rendering failed:', + formatPdfOcrRuntimeIssue(error), + ); return []; } } @@ -375,11 +428,12 @@ export class UnpdfProvider extends BasePDFReader { */ async checkCapabilities(): Promise { const deps = await this.checkDependencies(); + const coreUnpdfAvailable = deps.details.unpdf === true; return { - canExtractText: deps.available, - canExtractMetadata: deps.available, - canExtractImages: deps.available, + canExtractText: coreUnpdfAvailable, + canExtractMetadata: coreUnpdfAvailable, + canExtractImages: coreUnpdfAvailable, canPerformOCR: false, // unpdf doesn't do OCR supportedFormats: ['pdf'], maxFileSize: undefined, // No explicit limit @@ -391,23 +445,36 @@ export class UnpdfProvider extends BasePDFReader { * Check if unpdf dependencies are available */ async checkDependencies(): Promise { + let unpdfAvailable = false; + let pageRenderingAvailable = false; + const errors: string[] = []; + try { await this.loadUnpdf(); - return { - available: true, - details: { - unpdf: true, - }, - }; + unpdfAvailable = true; } catch (error) { - return { - available: false, - error: `unpdf dependency not available: ${(error as Error).message}`, - details: { - unpdf: false, - }, - }; + errors.push(`unpdf import unavailable: ${(error as Error).message}`); + } + + if (unpdfAvailable) { + try { + await this.verifyRenderDependencies(); + pageRenderingAvailable = true; + } catch (error) { + errors.push( + `page rendering unavailable: ${formatPdfOcrRuntimeIssue(error)}`, + ); + } } + + return { + available: unpdfAvailable && pageRenderingAvailable, + error: errors.length > 0 ? errors.join('; ') : undefined, + details: { + unpdf: unpdfAvailable, + pageRendering: pageRenderingAvailable, + }, + }; } /** @@ -422,7 +489,7 @@ export class UnpdfProvider extends BasePDFReader { throw new Error('Invalid PDF data'); } - const pdf = await unpdf.getDocumentProxy(new Uint8Array(buffer)); + const pdf = await unpdf.getDocumentProxy(buffer); const metadata = await pdf.getMetadata(); // Quick analysis of document structure diff --git a/src/ocr-integration.test.ts b/src/ocr-integration.test.ts index e7d29e3..18fe4f8 100644 --- a/src/ocr-integration.test.ts +++ b/src/ocr-integration.test.ts @@ -1,6 +1,6 @@ import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { beforeAll, describe, expect, it } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { checkOCRDependencies, getPDFReader } from './index'; import type { PDFReader } from './shared/types'; @@ -8,12 +8,18 @@ describe.skipIf(process.env.CI === 'true')( 'OCR Integration with Real PDF', () => { let reader: PDFReader; + let onnxReader: PDFReader; let unpdfReader: PDFReader; let ocrAvailable = false; + const originalTessdataPrefix = process.env.TESSDATA_PREFIX; beforeAll(async () => { // Default reader (kreuzberg if available) reader = await getPDFReader(); + onnxReader = await getPDFReader({ + provider: 'auto', + ocrProvider: 'onnx', + }); // unpdf reader for modular operations (extractImages, performOCR) unpdfReader = await getPDFReader({ provider: 'unpdf' }); @@ -34,6 +40,14 @@ describe.skipIf(process.env.CI === 'true')( } }); + afterAll(() => { + if (originalTessdataPrefix) { + process.env.TESSDATA_PREFIX = originalTessdataPrefix; + } else { + Reflect.deleteProperty(process.env, 'TESSDATA_PREFIX'); + } + }); + it('should extract text from scanned PDF using OCR', async () => { const pdfPath = join( fileURLToPath(new URL('.', import.meta.url)), @@ -42,6 +56,9 @@ describe.skipIf(process.env.CI === 'true')( 'Signed-Meeting-Minutes-October-8-2024-Regular-Council-Meeting-1.pdf', ); + // Exercise tessdata auto-detection instead of relying on shell env state. + Reflect.deleteProperty(process.env, 'TESSDATA_PREFIX'); + // Extract text - this MUST work for scanned PDFs // Both kreuzberg and unpdf handle OCR, just differently const text = await reader.extractText(pdfPath); @@ -52,16 +69,31 @@ describe.skipIf(process.env.CI === 'true')( expect(typeof text).toBe('string'); // Must extract meaningful content (at least 100 chars from a 3-page document) - expect(text!.length).toBeGreaterThan(100); + expect(text?.length).toBeGreaterThan(100); // Should contain expected content from the meeting minutes // (Bentley is the town name that appears in the document) - expect(text!.toLowerCase()).toContain('bentley'); + expect(text?.toLowerCase()).toContain('bentley'); - console.log(`✅ OCR extracted ${text!.length} characters`); + console.log(`✅ OCR extracted ${text?.length} characters`); console.log( - `Preview: ${text!.substring(0, 200).replace(/\s+/g, ' ').trim()}...`, + `Preview: ${text?.substring(0, 200).replace(/\s+/g, ' ').trim()}...`, + ); + }, 60000); + + it('should extract text from scanned PDF using unpdf plus ONNX OCR', async () => { + const pdfPath = join( + fileURLToPath(new URL('.', import.meta.url)), + '..', + 'test', + 'Signed-Meeting-Minutes-October-8-2024-Regular-Council-Meeting-1.pdf', ); + + const text = await onnxReader.extractText(pdfPath); + + expect(text).not.toBeNull(); + expect(text?.length).toBeGreaterThan(100); + expect(text?.toLowerCase()).toContain('bentley'); }, 60000); it('should perform OCR on extracted images (unpdf modular workflow)', async () => { diff --git a/src/shared/factory.ts b/src/shared/factory.ts index 37eb877..fabc250 100644 --- a/src/shared/factory.ts +++ b/src/shared/factory.ts @@ -17,6 +17,30 @@ async function isKreuzbergAvailable(): Promise { } } +function requiresExternalOCRProvider(ocrProvider?: string): boolean { + return Boolean( + ocrProvider && ocrProvider !== 'auto' && ocrProvider !== 'tesseract', + ); +} + +async function canUseKreuzberg(options: PDFReaderOptions): Promise { + if (!(await isKreuzbergAvailable())) { + return false; + } + + try { + const { KreuzbergProvider } = await import('../node/kreuzberg.js'); + const reader = new KreuzbergProvider({ + ocrBackend: options.ocrProvider, + ocrLanguage: options.defaultOCROptions?.language, + }); + const deps = await reader.checkDependencies(); + return deps.available; + } catch { + return false; + } +} + /** * Create a PDF reader instance with intelligent provider selection and configuration * @@ -81,7 +105,7 @@ export async function getPDFReader( options: PDFReaderOptions = {}, ): Promise { // Load configuration from environment variables, with user options taking precedence - const config = loadEnvConfig(options, { + const config = loadEnvConfig(options, { packageName: 'pdf', schema: { enableOCR: 'boolean', @@ -92,7 +116,10 @@ export async function getPDFReader( }, }); - const { provider = 'auto', ...readerOptions } = config; + const provider = (config.provider ?? 'auto') as NonNullable< + PDFReaderOptions['provider'] + >; + const readerOptions: PDFReaderOptions = { ...config, provider }; // Environment detection for automatic provider selection const isNode = @@ -106,10 +133,14 @@ export async function getPDFReader( let selectedProvider = provider; if (provider === 'auto') { if (isNode) { - // Prefer kreuzberg for memory efficiency, fall back to unpdf - selectedProvider = await isKreuzbergAvailable() - ? 'kreuzberg' - : 'unpdf'; + // Route non-Tesseract OCR providers through the unpdf + @happyvertical/ocr path. + if (requiresExternalOCRProvider(readerOptions.ocrProvider)) { + selectedProvider = 'unpdf'; + } else { + selectedProvider = (await canUseKreuzberg(readerOptions)) + ? 'kreuzberg' + : 'unpdf'; + } } else if (isBrowser) { selectedProvider = 'pdfjs'; // Use PDF.js for browser } else { @@ -142,6 +173,12 @@ export async function getPDFReader( ); } + if (requiresExternalOCRProvider(readerOptions.ocrProvider)) { + throw new Error( + `kreuzberg provider does not support OCR provider '${readerOptions.ocrProvider}'. Use provider: 'unpdf' instead.`, + ); + } + // Dynamic import to avoid bundling Node.js code in browser const { KreuzbergProvider } = await import('../node/kreuzberg.js'); return new KreuzbergProvider({ diff --git a/vite.config.ts b/vite.config.ts index ee41aae..88e14bd 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -50,8 +50,9 @@ export default defineConfig({ // External dependencies - don't bundle these 'unpdf', - 'pdf-to-png-converter', 'pdfjs-dist', + /^pdfjs-dist\//, + '@napi-rs/canvas', '@kreuzberg/node', /^@kreuzberg\//,