├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .cursorrules 3 | 4 | node_modules/ 5 | 6 | dist/ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Steph Ango (@kepano) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Defuddle CLI 2 | 3 | Command line interface for [Defuddle](https://github.com/kepano/defuddle). Extract clean HTML or Markdown from pages. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | npm install -g defuddle-cli 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```bash 14 | defuddle parse [options] 15 | ``` 16 | 17 | ### Arguments 18 | 19 | - `source`: HTML file path or URL to parse 20 | 21 | ### Options 22 | 23 | - `-o, --output `: Output file path (default: stdout) 24 | - `-m, --markdown, --md`: Convert content to markdown 25 | - `-j, --json`: Output as JSON with both HTML and markdown content 26 | - `-p, --property `: Extract a specific property (e.g., title, description, domain) 27 | - `--debug`: Enable debug mode 28 | - `-h, --help`: Display help for command 29 | 30 | ### Examples 31 | 32 | Parse a local HTML file (outputs HTML): 33 | ```bash 34 | defuddle parse article.html 35 | ``` 36 | 37 | Parse a URL and convert to markdown: 38 | ```bash 39 | defuddle parse https://example.com/article --md 40 | ``` 41 | 42 | Parse and get the full JSON response from Defuddle: 43 | ```bash 44 | defuddle parse article.html --json 45 | ``` 46 | 47 | Save markdown output to a file: 48 | ```bash 49 | defuddle parse article.html --md -o output.md 50 | ``` 51 | 52 | Extract specific properties: 53 | ```bash 54 | # Get just the title 55 | defuddle parse article.html --property title 56 | 57 | # Get the description 58 | defuddle parse article.html -p description 59 | 60 | # Get the domain 61 | defuddle parse article.html --property domain 62 | ``` 63 | 64 | ## Development 65 | 66 | ```bash 67 | # Install dependencies 68 | npm install 69 | 70 | # Build 71 | npm run build 72 | 73 | # Run in development mode 74 | npm run dev 75 | ``` 76 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "defuddle-cli", 3 | "version": "0.6.4", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "defuddle-cli", 9 | "version": "0.6.4", 10 | "license": "MIT", 11 | "dependencies": { 12 | "chalk": "^5.3.0", 13 | "commander": "^12.0.0", 14 | "defuddle": "^0.6.4", 15 | "jsdom": "^24.0.0" 16 | }, 17 | "bin": { 18 | "defuddle": "dist/index.js" 19 | }, 20 | "devDependencies": { 21 | "@types/jsdom": "^21.1.6", 22 | "@types/node": "^20.0.0", 23 | "typescript": "^5.3.3" 24 | } 25 | }, 26 | "../defuddle": { 27 | "version": "0.3.8", 28 | "extraneous": true, 29 | "license": "MIT", 30 | "devDependencies": { 31 | "@types/node": "^20.0.0", 32 | "concurrently": "^8.2.2", 33 | "terser-webpack-plugin": "^5.3.14", 34 | "ts-loader": "^9.5.1", 35 | "typescript": "^5.3.3", 36 | "undici-types": "^5.0.0", 37 | "webpack": "^5.90.3", 38 | "webpack-cli": "^5.1.4" 39 | }, 40 | "optionalDependencies": { 41 | "mathml-to-latex": "^1.4.3", 42 | "temml": "^0.11.2" 43 | } 44 | }, 45 | "node_modules/@asamuzakjp/css-color": { 46 | "version": "3.1.1", 47 | "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.1.tgz", 48 | "integrity": "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==", 49 | "dependencies": { 50 | "@csstools/css-calc": "^2.1.2", 51 | "@csstools/css-color-parser": "^3.0.8", 52 | "@csstools/css-parser-algorithms": "^3.0.4", 53 | "@csstools/css-tokenizer": "^3.0.3", 54 | "lru-cache": "^10.4.3" 55 | } 56 | }, 57 | "node_modules/@csstools/color-helpers": { 58 | "version": "5.0.2", 59 | "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", 60 | "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", 61 | "funding": [ 62 | { 63 | "type": "github", 64 | "url": "https://github.com/sponsors/csstools" 65 | }, 66 | { 67 | "type": "opencollective", 68 | "url": "https://opencollective.com/csstools" 69 | } 70 | ], 71 | "engines": { 72 | "node": ">=18" 73 | } 74 | }, 75 | "node_modules/@csstools/css-calc": { 76 | "version": "2.1.2", 77 | "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", 78 | "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", 79 | "funding": [ 80 | { 81 | "type": "github", 82 | "url": "https://github.com/sponsors/csstools" 83 | }, 84 | { 85 | "type": "opencollective", 86 | "url": "https://opencollective.com/csstools" 87 | } 88 | ], 89 | "engines": { 90 | "node": ">=18" 91 | }, 92 | "peerDependencies": { 93 | "@csstools/css-parser-algorithms": "^3.0.4", 94 | "@csstools/css-tokenizer": "^3.0.3" 95 | } 96 | }, 97 | "node_modules/@csstools/css-color-parser": { 98 | "version": "3.0.8", 99 | "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", 100 | "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", 101 | "funding": [ 102 | { 103 | "type": "github", 104 | "url": "https://github.com/sponsors/csstools" 105 | }, 106 | { 107 | "type": "opencollective", 108 | "url": "https://opencollective.com/csstools" 109 | } 110 | ], 111 | "dependencies": { 112 | "@csstools/color-helpers": "^5.0.2", 113 | "@csstools/css-calc": "^2.1.2" 114 | }, 115 | "engines": { 116 | "node": ">=18" 117 | }, 118 | "peerDependencies": { 119 | "@csstools/css-parser-algorithms": "^3.0.4", 120 | "@csstools/css-tokenizer": "^3.0.3" 121 | } 122 | }, 123 | "node_modules/@csstools/css-parser-algorithms": { 124 | "version": "3.0.4", 125 | "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", 126 | "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", 127 | "funding": [ 128 | { 129 | "type": "github", 130 | "url": "https://github.com/sponsors/csstools" 131 | }, 132 | { 133 | "type": "opencollective", 134 | "url": "https://opencollective.com/csstools" 135 | } 136 | ], 137 | "engines": { 138 | "node": ">=18" 139 | }, 140 | "peerDependencies": { 141 | "@csstools/css-tokenizer": "^3.0.3" 142 | } 143 | }, 144 | "node_modules/@csstools/css-tokenizer": { 145 | "version": "3.0.3", 146 | "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", 147 | "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", 148 | "funding": [ 149 | { 150 | "type": "github", 151 | "url": "https://github.com/sponsors/csstools" 152 | }, 153 | { 154 | "type": "opencollective", 155 | "url": "https://opencollective.com/csstools" 156 | } 157 | ], 158 | "engines": { 159 | "node": ">=18" 160 | } 161 | }, 162 | "node_modules/@mixmark-io/domino": { 163 | "version": "2.2.0", 164 | "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", 165 | "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==", 166 | "optional": true 167 | }, 168 | "node_modules/@types/jsdom": { 169 | "version": "21.1.7", 170 | "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", 171 | "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", 172 | "dev": true, 173 | "dependencies": { 174 | "@types/node": "*", 175 | "@types/tough-cookie": "*", 176 | "parse5": "^7.0.0" 177 | } 178 | }, 179 | "node_modules/@types/node": { 180 | "version": "20.17.27", 181 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.27.tgz", 182 | "integrity": "sha512-U58sbKhDrthHlxHRJw7ZLiLDZGmAUOZUbpw0S6nL27sYUdhvgBLCRu/keSd6qcTsfArd1sRFCCBxzWATGr/0UA==", 183 | "dev": true, 184 | "dependencies": { 185 | "undici-types": "~6.19.2" 186 | } 187 | }, 188 | "node_modules/@types/tough-cookie": { 189 | "version": "4.0.5", 190 | "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", 191 | "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", 192 | "dev": true 193 | }, 194 | "node_modules/agent-base": { 195 | "version": "7.1.3", 196 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", 197 | "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", 198 | "engines": { 199 | "node": ">= 14" 200 | } 201 | }, 202 | "node_modules/asynckit": { 203 | "version": "0.4.0", 204 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 205 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 206 | }, 207 | "node_modules/call-bind-apply-helpers": { 208 | "version": "1.0.2", 209 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 210 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 211 | "dependencies": { 212 | "es-errors": "^1.3.0", 213 | "function-bind": "^1.1.2" 214 | }, 215 | "engines": { 216 | "node": ">= 0.4" 217 | } 218 | }, 219 | "node_modules/chalk": { 220 | "version": "5.4.1", 221 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", 222 | "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", 223 | "engines": { 224 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 225 | }, 226 | "funding": { 227 | "url": "https://github.com/chalk/chalk?sponsor=1" 228 | } 229 | }, 230 | "node_modules/combined-stream": { 231 | "version": "1.0.8", 232 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 233 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 234 | "dependencies": { 235 | "delayed-stream": "~1.0.0" 236 | }, 237 | "engines": { 238 | "node": ">= 0.8" 239 | } 240 | }, 241 | "node_modules/commander": { 242 | "version": "12.1.0", 243 | "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", 244 | "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", 245 | "engines": { 246 | "node": ">=18" 247 | } 248 | }, 249 | "node_modules/cssstyle": { 250 | "version": "4.3.0", 251 | "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz", 252 | "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==", 253 | "dependencies": { 254 | "@asamuzakjp/css-color": "^3.1.1", 255 | "rrweb-cssom": "^0.8.0" 256 | }, 257 | "engines": { 258 | "node": ">=18" 259 | } 260 | }, 261 | "node_modules/cssstyle/node_modules/rrweb-cssom": { 262 | "version": "0.8.0", 263 | "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", 264 | "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==" 265 | }, 266 | "node_modules/data-urls": { 267 | "version": "5.0.0", 268 | "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", 269 | "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", 270 | "dependencies": { 271 | "whatwg-mimetype": "^4.0.0", 272 | "whatwg-url": "^14.0.0" 273 | }, 274 | "engines": { 275 | "node": ">=18" 276 | } 277 | }, 278 | "node_modules/debug": { 279 | "version": "4.4.0", 280 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 281 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 282 | "dependencies": { 283 | "ms": "^2.1.3" 284 | }, 285 | "engines": { 286 | "node": ">=6.0" 287 | }, 288 | "peerDependenciesMeta": { 289 | "supports-color": { 290 | "optional": true 291 | } 292 | } 293 | }, 294 | "node_modules/decimal.js": { 295 | "version": "10.5.0", 296 | "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", 297 | "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" 298 | }, 299 | "node_modules/defuddle": { 300 | "version": "0.6.4", 301 | "resolved": "https://registry.npmjs.org/defuddle/-/defuddle-0.6.4.tgz", 302 | "integrity": "sha512-T8qopOu0hLaFHIVX/cKrNxwbcRWi9rCg002YUApNNdfHm7kFSn9719+diEIaSu61AJp6nbyAyK8E0nW20jc/PA==", 303 | "optionalDependencies": { 304 | "mathml-to-latex": "^1.4.3", 305 | "temml": "^0.11.2", 306 | "turndown": "^7.2.0" 307 | }, 308 | "peerDependencies": { 309 | "jsdom": "^24.0.0" 310 | } 311 | }, 312 | "node_modules/delayed-stream": { 313 | "version": "1.0.0", 314 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 315 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 316 | "engines": { 317 | "node": ">=0.4.0" 318 | } 319 | }, 320 | "node_modules/dunder-proto": { 321 | "version": "1.0.1", 322 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 323 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 324 | "dependencies": { 325 | "call-bind-apply-helpers": "^1.0.1", 326 | "es-errors": "^1.3.0", 327 | "gopd": "^1.2.0" 328 | }, 329 | "engines": { 330 | "node": ">= 0.4" 331 | } 332 | }, 333 | "node_modules/entities": { 334 | "version": "4.5.0", 335 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", 336 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", 337 | "engines": { 338 | "node": ">=0.12" 339 | }, 340 | "funding": { 341 | "url": "https://github.com/fb55/entities?sponsor=1" 342 | } 343 | }, 344 | "node_modules/es-define-property": { 345 | "version": "1.0.1", 346 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 347 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 348 | "engines": { 349 | "node": ">= 0.4" 350 | } 351 | }, 352 | "node_modules/es-errors": { 353 | "version": "1.3.0", 354 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 355 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 356 | "engines": { 357 | "node": ">= 0.4" 358 | } 359 | }, 360 | "node_modules/es-object-atoms": { 361 | "version": "1.1.1", 362 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 363 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 364 | "dependencies": { 365 | "es-errors": "^1.3.0" 366 | }, 367 | "engines": { 368 | "node": ">= 0.4" 369 | } 370 | }, 371 | "node_modules/es-set-tostringtag": { 372 | "version": "2.1.0", 373 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 374 | "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 375 | "dependencies": { 376 | "es-errors": "^1.3.0", 377 | "get-intrinsic": "^1.2.6", 378 | "has-tostringtag": "^1.0.2", 379 | "hasown": "^2.0.2" 380 | }, 381 | "engines": { 382 | "node": ">= 0.4" 383 | } 384 | }, 385 | "node_modules/form-data": { 386 | "version": "4.0.2", 387 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", 388 | "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", 389 | "dependencies": { 390 | "asynckit": "^0.4.0", 391 | "combined-stream": "^1.0.8", 392 | "es-set-tostringtag": "^2.1.0", 393 | "mime-types": "^2.1.12" 394 | }, 395 | "engines": { 396 | "node": ">= 6" 397 | } 398 | }, 399 | "node_modules/function-bind": { 400 | "version": "1.1.2", 401 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 402 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 403 | "funding": { 404 | "url": "https://github.com/sponsors/ljharb" 405 | } 406 | }, 407 | "node_modules/get-intrinsic": { 408 | "version": "1.3.0", 409 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 410 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 411 | "dependencies": { 412 | "call-bind-apply-helpers": "^1.0.2", 413 | "es-define-property": "^1.0.1", 414 | "es-errors": "^1.3.0", 415 | "es-object-atoms": "^1.1.1", 416 | "function-bind": "^1.1.2", 417 | "get-proto": "^1.0.1", 418 | "gopd": "^1.2.0", 419 | "has-symbols": "^1.1.0", 420 | "hasown": "^2.0.2", 421 | "math-intrinsics": "^1.1.0" 422 | }, 423 | "engines": { 424 | "node": ">= 0.4" 425 | }, 426 | "funding": { 427 | "url": "https://github.com/sponsors/ljharb" 428 | } 429 | }, 430 | "node_modules/get-proto": { 431 | "version": "1.0.1", 432 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 433 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 434 | "dependencies": { 435 | "dunder-proto": "^1.0.1", 436 | "es-object-atoms": "^1.0.0" 437 | }, 438 | "engines": { 439 | "node": ">= 0.4" 440 | } 441 | }, 442 | "node_modules/gopd": { 443 | "version": "1.2.0", 444 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 445 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 446 | "engines": { 447 | "node": ">= 0.4" 448 | }, 449 | "funding": { 450 | "url": "https://github.com/sponsors/ljharb" 451 | } 452 | }, 453 | "node_modules/has-symbols": { 454 | "version": "1.1.0", 455 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 456 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 457 | "engines": { 458 | "node": ">= 0.4" 459 | }, 460 | "funding": { 461 | "url": "https://github.com/sponsors/ljharb" 462 | } 463 | }, 464 | "node_modules/has-tostringtag": { 465 | "version": "1.0.2", 466 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 467 | "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 468 | "dependencies": { 469 | "has-symbols": "^1.0.3" 470 | }, 471 | "engines": { 472 | "node": ">= 0.4" 473 | }, 474 | "funding": { 475 | "url": "https://github.com/sponsors/ljharb" 476 | } 477 | }, 478 | "node_modules/hasown": { 479 | "version": "2.0.2", 480 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 481 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 482 | "dependencies": { 483 | "function-bind": "^1.1.2" 484 | }, 485 | "engines": { 486 | "node": ">= 0.4" 487 | } 488 | }, 489 | "node_modules/html-encoding-sniffer": { 490 | "version": "4.0.0", 491 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", 492 | "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", 493 | "dependencies": { 494 | "whatwg-encoding": "^3.1.1" 495 | }, 496 | "engines": { 497 | "node": ">=18" 498 | } 499 | }, 500 | "node_modules/http-proxy-agent": { 501 | "version": "7.0.2", 502 | "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", 503 | "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", 504 | "dependencies": { 505 | "agent-base": "^7.1.0", 506 | "debug": "^4.3.4" 507 | }, 508 | "engines": { 509 | "node": ">= 14" 510 | } 511 | }, 512 | "node_modules/https-proxy-agent": { 513 | "version": "7.0.6", 514 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", 515 | "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", 516 | "dependencies": { 517 | "agent-base": "^7.1.2", 518 | "debug": "4" 519 | }, 520 | "engines": { 521 | "node": ">= 14" 522 | } 523 | }, 524 | "node_modules/iconv-lite": { 525 | "version": "0.6.3", 526 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 527 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 528 | "dependencies": { 529 | "safer-buffer": ">= 2.1.2 < 3.0.0" 530 | }, 531 | "engines": { 532 | "node": ">=0.10.0" 533 | } 534 | }, 535 | "node_modules/is-potential-custom-element-name": { 536 | "version": "1.0.1", 537 | "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", 538 | "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" 539 | }, 540 | "node_modules/jsdom": { 541 | "version": "24.1.3", 542 | "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.3.tgz", 543 | "integrity": "sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==", 544 | "dependencies": { 545 | "cssstyle": "^4.0.1", 546 | "data-urls": "^5.0.0", 547 | "decimal.js": "^10.4.3", 548 | "form-data": "^4.0.0", 549 | "html-encoding-sniffer": "^4.0.0", 550 | "http-proxy-agent": "^7.0.2", 551 | "https-proxy-agent": "^7.0.5", 552 | "is-potential-custom-element-name": "^1.0.1", 553 | "nwsapi": "^2.2.12", 554 | "parse5": "^7.1.2", 555 | "rrweb-cssom": "^0.7.1", 556 | "saxes": "^6.0.0", 557 | "symbol-tree": "^3.2.4", 558 | "tough-cookie": "^4.1.4", 559 | "w3c-xmlserializer": "^5.0.0", 560 | "webidl-conversions": "^7.0.0", 561 | "whatwg-encoding": "^3.1.1", 562 | "whatwg-mimetype": "^4.0.0", 563 | "whatwg-url": "^14.0.0", 564 | "ws": "^8.18.0", 565 | "xml-name-validator": "^5.0.0" 566 | }, 567 | "engines": { 568 | "node": ">=18" 569 | }, 570 | "peerDependencies": { 571 | "canvas": "^2.11.2" 572 | }, 573 | "peerDependenciesMeta": { 574 | "canvas": { 575 | "optional": true 576 | } 577 | } 578 | }, 579 | "node_modules/lru-cache": { 580 | "version": "10.4.3", 581 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 582 | "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" 583 | }, 584 | "node_modules/math-intrinsics": { 585 | "version": "1.1.0", 586 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 587 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 588 | "engines": { 589 | "node": ">= 0.4" 590 | } 591 | }, 592 | "node_modules/mime-db": { 593 | "version": "1.52.0", 594 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 595 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 596 | "engines": { 597 | "node": ">= 0.6" 598 | } 599 | }, 600 | "node_modules/mime-types": { 601 | "version": "2.1.35", 602 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 603 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 604 | "dependencies": { 605 | "mime-db": "1.52.0" 606 | }, 607 | "engines": { 608 | "node": ">= 0.6" 609 | } 610 | }, 611 | "node_modules/ms": { 612 | "version": "2.1.3", 613 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 614 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 615 | }, 616 | "node_modules/nwsapi": { 617 | "version": "2.2.19", 618 | "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.19.tgz", 619 | "integrity": "sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA==" 620 | }, 621 | "node_modules/parse5": { 622 | "version": "7.2.1", 623 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", 624 | "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", 625 | "dependencies": { 626 | "entities": "^4.5.0" 627 | }, 628 | "funding": { 629 | "url": "https://github.com/inikulin/parse5?sponsor=1" 630 | } 631 | }, 632 | "node_modules/psl": { 633 | "version": "1.15.0", 634 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", 635 | "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", 636 | "dependencies": { 637 | "punycode": "^2.3.1" 638 | }, 639 | "funding": { 640 | "url": "https://github.com/sponsors/lupomontero" 641 | } 642 | }, 643 | "node_modules/punycode": { 644 | "version": "2.3.1", 645 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 646 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 647 | "engines": { 648 | "node": ">=6" 649 | } 650 | }, 651 | "node_modules/querystringify": { 652 | "version": "2.2.0", 653 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", 654 | "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" 655 | }, 656 | "node_modules/requires-port": { 657 | "version": "1.0.0", 658 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 659 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" 660 | }, 661 | "node_modules/rrweb-cssom": { 662 | "version": "0.7.1", 663 | "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", 664 | "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==" 665 | }, 666 | "node_modules/safer-buffer": { 667 | "version": "2.1.2", 668 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 669 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 670 | }, 671 | "node_modules/saxes": { 672 | "version": "6.0.0", 673 | "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", 674 | "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", 675 | "dependencies": { 676 | "xmlchars": "^2.2.0" 677 | }, 678 | "engines": { 679 | "node": ">=v12.22.7" 680 | } 681 | }, 682 | "node_modules/symbol-tree": { 683 | "version": "3.2.4", 684 | "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", 685 | "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" 686 | }, 687 | "node_modules/temml": { 688 | "version": "0.11.6", 689 | "resolved": "https://registry.npmjs.org/temml/-/temml-0.11.6.tgz", 690 | "integrity": "sha512-XGgTdyP2tLse1IuCaGcHvbNja/yA7M6cNzOXB+slW1tnTuKReNx3egFUi25z9Rdg2wmhjZXDRLv1UV5BsaKjcw==", 691 | "optional": true, 692 | "engines": { 693 | "node": ">=18.13.0" 694 | } 695 | }, 696 | "node_modules/tough-cookie": { 697 | "version": "4.1.4", 698 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", 699 | "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", 700 | "dependencies": { 701 | "psl": "^1.1.33", 702 | "punycode": "^2.1.1", 703 | "universalify": "^0.2.0", 704 | "url-parse": "^1.5.3" 705 | }, 706 | "engines": { 707 | "node": ">=6" 708 | } 709 | }, 710 | "node_modules/tr46": { 711 | "version": "5.1.0", 712 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", 713 | "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", 714 | "dependencies": { 715 | "punycode": "^2.3.1" 716 | }, 717 | "engines": { 718 | "node": ">=18" 719 | } 720 | }, 721 | "node_modules/turndown": { 722 | "version": "7.2.0", 723 | "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.0.tgz", 724 | "integrity": "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==", 725 | "optional": true, 726 | "dependencies": { 727 | "@mixmark-io/domino": "^2.2.0" 728 | } 729 | }, 730 | "node_modules/typescript": { 731 | "version": "5.8.2", 732 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 733 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 734 | "dev": true, 735 | "bin": { 736 | "tsc": "bin/tsc", 737 | "tsserver": "bin/tsserver" 738 | }, 739 | "engines": { 740 | "node": ">=14.17" 741 | } 742 | }, 743 | "node_modules/undici-types": { 744 | "version": "6.19.8", 745 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 746 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 747 | "dev": true 748 | }, 749 | "node_modules/universalify": { 750 | "version": "0.2.0", 751 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", 752 | "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", 753 | "engines": { 754 | "node": ">= 4.0.0" 755 | } 756 | }, 757 | "node_modules/url-parse": { 758 | "version": "1.5.10", 759 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", 760 | "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", 761 | "dependencies": { 762 | "querystringify": "^2.1.1", 763 | "requires-port": "^1.0.0" 764 | } 765 | }, 766 | "node_modules/w3c-xmlserializer": { 767 | "version": "5.0.0", 768 | "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", 769 | "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", 770 | "dependencies": { 771 | "xml-name-validator": "^5.0.0" 772 | }, 773 | "engines": { 774 | "node": ">=18" 775 | } 776 | }, 777 | "node_modules/webidl-conversions": { 778 | "version": "7.0.0", 779 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 780 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", 781 | "engines": { 782 | "node": ">=12" 783 | } 784 | }, 785 | "node_modules/whatwg-encoding": { 786 | "version": "3.1.1", 787 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", 788 | "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", 789 | "dependencies": { 790 | "iconv-lite": "0.6.3" 791 | }, 792 | "engines": { 793 | "node": ">=18" 794 | } 795 | }, 796 | "node_modules/whatwg-mimetype": { 797 | "version": "4.0.0", 798 | "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", 799 | "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", 800 | "engines": { 801 | "node": ">=18" 802 | } 803 | }, 804 | "node_modules/whatwg-url": { 805 | "version": "14.2.0", 806 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", 807 | "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", 808 | "dependencies": { 809 | "tr46": "^5.1.0", 810 | "webidl-conversions": "^7.0.0" 811 | }, 812 | "engines": { 813 | "node": ">=18" 814 | } 815 | }, 816 | "node_modules/ws": { 817 | "version": "8.18.1", 818 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", 819 | "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", 820 | "engines": { 821 | "node": ">=10.0.0" 822 | }, 823 | "peerDependencies": { 824 | "bufferutil": "^4.0.1", 825 | "utf-8-validate": ">=5.0.2" 826 | }, 827 | "peerDependenciesMeta": { 828 | "bufferutil": { 829 | "optional": true 830 | }, 831 | "utf-8-validate": { 832 | "optional": true 833 | } 834 | } 835 | }, 836 | "node_modules/xml-name-validator": { 837 | "version": "5.0.0", 838 | "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", 839 | "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", 840 | "engines": { 841 | "node": ">=18" 842 | } 843 | }, 844 | "node_modules/xmlchars": { 845 | "version": "2.2.0", 846 | "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", 847 | "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" 848 | } 849 | } 850 | } 851 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "defuddle-cli", 3 | "version": "0.6.4", 4 | "description": "Command line interface for Defuddle - extract article content from web pages", 5 | "main": "dist/index.js", 6 | "bin": { 7 | "defuddle": "dist/index.js" 8 | }, 9 | "type": "module", 10 | "scripts": { 11 | "build": "tsc", 12 | "dev": "tsc --watch", 13 | "start": "node dist/index.js" 14 | }, 15 | "keywords": [ 16 | "defuddle", 17 | "cli" 18 | ], 19 | "author": "kepano", 20 | "license": "MIT", 21 | "dependencies": { 22 | "chalk": "^5.3.0", 23 | "commander": "^12.0.0", 24 | "defuddle": "^0.6.4", 25 | "jsdom": "^24.0.0" 26 | }, 27 | "devDependencies": { 28 | "@types/jsdom": "^21.1.6", 29 | "@types/node": "^20.0.0", 30 | "typescript": "^5.3.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Command } from 'commander'; 4 | import { JSDOM } from 'jsdom'; 5 | import { Defuddle } from 'defuddle/node'; 6 | import chalk from 'chalk'; 7 | import { writeFile } from 'fs/promises'; 8 | import { fileURLToPath } from 'url'; 9 | import { dirname, resolve } from 'path'; 10 | 11 | interface ParseOptions { 12 | output?: string; 13 | markdown?: boolean; 14 | md?: boolean; 15 | json?: boolean; 16 | debug?: boolean; 17 | property?: string; 18 | } 19 | 20 | const __filename = fileURLToPath(import.meta.url); 21 | const __dirname = dirname(__filename); 22 | 23 | const program = new Command(); 24 | 25 | program 26 | .name('defuddle') 27 | .description('Extract article content from web pages') 28 | .version('0.1.0'); 29 | 30 | program 31 | .command('parse') 32 | .description('Parse HTML content from a file or URL') 33 | .argument('', 'HTML file path or URL to parse') 34 | .option('-o, --output ', 'Output file path (default: stdout)') 35 | .option('-m, --markdown', 'Convert content to markdown format') 36 | .option('--md', 'Alias for --markdown') 37 | .option('-j, --json', 'Output as JSON with metadata and content') 38 | .option('-p, --property ', 'Extract a specific property (e.g., title, description, domain)') 39 | .option('--debug', 'Enable debug mode') 40 | .action(async (source: string, options: ParseOptions) => { 41 | try { 42 | // Handle --md alias 43 | if (options.md) { 44 | options.markdown = true; 45 | } 46 | let dom: JSDOM; 47 | 48 | try { 49 | // Determine if source is a URL or file path 50 | if (source.startsWith('http://') || source.startsWith('https://')) { 51 | dom = await JSDOM.fromURL(source); 52 | } else { 53 | const filePath = resolve(process.cwd(), source); 54 | dom = await JSDOM.fromFile(filePath); 55 | } 56 | 57 | // Parse content with debug mode if enabled 58 | try { 59 | const result = await Defuddle(dom, source.startsWith('http') ? source : undefined, { 60 | debug: options.debug, 61 | markdown: options.markdown 62 | }); 63 | 64 | // If in debug mode, don't show content output 65 | if (options.debug) { 66 | process.exit(0); 67 | } 68 | 69 | // Format output 70 | let output: string; 71 | 72 | // Format the response based on options 73 | if (options.property) { 74 | // Extract specific property 75 | const property = options.property.toLowerCase(); 76 | if (property in result) { 77 | output = result[property as keyof typeof result]?.toString() || ''; 78 | } else { 79 | console.error(chalk.red(`Error: Property "${property}" not found in response`)); 80 | process.exit(1); 81 | } 82 | } else if (options.json) { 83 | const jsonObj: any = { 84 | content: result.content, 85 | title: result.title, 86 | description: result.description, 87 | domain: result.domain, 88 | favicon: result.favicon, 89 | image: result.image, 90 | metaTags: result.metaTags, 91 | parseTime: result.parseTime, 92 | published: result.published, 93 | author: result.author, 94 | site: result.site, 95 | schemaOrgData: result.schemaOrgData, 96 | wordCount: result.wordCount 97 | }; 98 | 99 | output = JSON.stringify(jsonObj, null, 2) 100 | .replace(/"([^"]+)":/g, chalk.cyan('"$1":')) 101 | .replace(/: "([^"]+)"/g, chalk.yellow(': "$1"')) 102 | .replace(/: (\d+)/g, chalk.yellow(': $1')) 103 | .replace(/: (true|false|null)/g, chalk.magenta(': $1')); 104 | } else { 105 | output = result.content; 106 | } 107 | 108 | // Handle output 109 | if (options.output) { 110 | const outputPath = resolve(process.cwd(), options.output); 111 | await writeFile(outputPath, output, 'utf-8'); 112 | console.log(chalk.green(`Output written to ${options.output}`)); 113 | } else { 114 | console.log(output); 115 | } 116 | 117 | process.exit(0); 118 | } catch (error) { 119 | console.error(chalk.red('Error during parsing:'), error); 120 | process.exit(1); 121 | } 122 | } catch (error) { 123 | console.error(chalk.red('Error loading content:'), error instanceof Error ? error.message : 'Unknown error occurred'); 124 | process.exit(1); 125 | } 126 | 127 | } catch (error) { 128 | console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error occurred'); 129 | process.exit(1); 130 | } 131 | }); 132 | 133 | program.parse(); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "outDir": "dist", 11 | "rootDir": "src", 12 | "declaration": true 13 | }, 14 | "include": ["src/**/*"], 15 | "exclude": ["node_modules", "dist"] 16 | } --------------------------------------------------------------------------------