{"_id":"@duyquangnvx/webnovel-downloader","_rev":"8-be792dd4f1a5459029358be6c2fa4ed8","name":"@duyquangnvx/webnovel-downloader","dist-tags":{"latest":"0.6.0"},"versions":{"0.1.0":{"name":"@duyquangnvx/webnovel-downloader","version":"0.1.0","license":"MIT","_id":"@duyquangnvx/webnovel-downloader@0.1.0","maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"dist":{"shasum":"fb0d1e8315c76fd6d570e5daab3fb32d9dde2d7a","tarball":"https://registry.npmjs.org/@duyquangnvx/webnovel-downloader/-/webnovel-downloader-0.1.0.tgz","fileCount":14,"integrity":"sha512-DyXV5Q2rIa5w1wk2HAz7ZAJCi/UmTlnYYQNBpkiwK3SM6F0AiMfgEu09R1tnlyzVI8b+kvpaPhGqaTYuAJqsGw==","signatures":[{"sig":"MEQCIEzWpiFOjBGW98rtQeECYGaQ6tDkL9OgDIcb4K/Bsx5CAiBnUddNW36vcX+uF5SwA8gn4DFIG16KVXMkSNDi1xUu1w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":343174},"main":"./dist/index.js","type":"module","_from":"file:duyquangnvx-webnovel-downloader-0.1.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dump":"tsx examples/dump.ts","lint":"eslint .","test":"vitest run","build":"tsup","format":"prettier --write .","example":"tsx examples/basic.ts","release":"pnpm publish --access public","typecheck":"tsc -p tsconfig.test.json --noEmit","smoke:live":"tsx scripts/live-smoke.ts","test:watch":"vitest","format:check":"prettier --check .","test:browser":"vitest run test/adapters/wikicv/browser-integration.test.ts","fixtures:refresh":"tsx scripts/refresh-fixtures.ts"},"_npmUser":{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"},"_resolved":"/private/var/folders/hr/4rd2g3hn3cxc_nz46pzqry6w0000gn/T/84998582bad1aaafb61996e1c94d9301/duyquangnvx-webnovel-downloader-0.1.0.tgz","_integrity":"sha512-DyXV5Q2rIa5w1wk2HAz7ZAJCi/UmTlnYYQNBpkiwK3SM6F0AiMfgEu09R1tnlyzVI8b+kvpaPhGqaTYuAJqsGw==","_npmVersion":"10.9.2","description":"A pluggable, type-safe webnovel downloader for Node.js.","directories":{},"_nodeVersion":"22.16.0","dependencies":{"zod":"^3.23.0","pino":"^9.0.0","undici":"^6.0.0","cheerio":"^1.0.0","p-queue":"^8.0.1","slugify":"^1.6.6","html-to-text":"^10.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"latest","eslint":"latest","vitest":"latest","prettier":"latest","@eslint/js":"latest","patchright":"^1.59.4","typescript":"^5.7.0","@types/node":"latest","@types/turndown":"latest","typescript-eslint":"latest","@types/html-to-text":"^9.0.4","eslint-config-prettier":"latest"},"peerDependencies":{"turndown":"^7.0.0","patchright":"^1.0.0","playwright":"^1.40.0"},"peerDependenciesMeta":{"turndown":{"optional":true},"patchright":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/webnovel-downloader_0.1.0_1781662114725_0.9532165583733121","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@duyquangnvx/webnovel-downloader","version":"0.2.0","license":"MIT","_id":"@duyquangnvx/webnovel-downloader@0.2.0","maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"dist":{"shasum":"b3bd8ee1db12d3d634a3e26d3f9554f632280e33","tarball":"https://registry.npmjs.org/@duyquangnvx/webnovel-downloader/-/webnovel-downloader-0.2.0.tgz","fileCount":17,"integrity":"sha512-1z+hBJ3vw7fkmSJv6dlkP46E0141RAwPH8KhVOL84zFet7BaN+qJ+3rx4wkHwGeAvQFn0E6u4SfeXv5gBY4MOg==","signatures":[{"sig":"MEUCIQDACGXqCaCJ+DTCJYm0T7zANOOTaAbvEcs5PEvJeAKdLgIgGzDjJOrI9IS65P9JIylSYUSp2/6bPuhJR/8bvYE5344=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":744038},"main":"./dist/index.cjs","type":"module","_from":"file:duyquangnvx-webnovel-downloader-0.2.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dump":"tsx examples/dump.ts","lint":"eslint .","test":"vitest run","build":"tsup","format":"prettier --write .","example":"tsx examples/basic.ts","release":"pnpm publish --access public","check:cjs":"node scripts/check-cjs.cjs","typecheck":"tsc -p tsconfig.test.json --noEmit","smoke:live":"tsx scripts/live-smoke.ts","test:watch":"vitest","format:check":"prettier --check .","test:browser":"vitest run test/adapters/wikicv/browser-integration.test.ts","fixtures:refresh":"tsx scripts/refresh-fixtures.ts"},"_npmUser":{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"},"_resolved":"/private/var/folders/hr/4rd2g3hn3cxc_nz46pzqry6w0000gn/T/08d6a2d3f0ee93efbc046a1b0c73f5ce/duyquangnvx-webnovel-downloader-0.2.0.tgz","_integrity":"sha512-1z+hBJ3vw7fkmSJv6dlkP46E0141RAwPH8KhVOL84zFet7BaN+qJ+3rx4wkHwGeAvQFn0E6u4SfeXv5gBY4MOg==","_npmVersion":"10.9.2","description":"A pluggable, type-safe webnovel downloader for Node.js.","directories":{},"_nodeVersion":"22.16.0","dependencies":{"zod":"^3.23.0","pino":"^9.0.0","undici":"^6.0.0","cheerio":"^1.0.0","p-queue":"^8.0.1","slugify":"^1.6.6","html-to-text":"^10.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"latest","eslint":"latest","vitest":"latest","prettier":"latest","@eslint/js":"latest","patchright":"^1.59.4","typescript":"^5.7.0","@types/node":"latest","@types/turndown":"latest","typescript-eslint":"latest","@types/html-to-text":"^9.0.4","eslint-config-prettier":"latest"},"peerDependencies":{"turndown":"^7.0.0","patchright":"^1.0.0","playwright":"^1.40.0"},"peerDependenciesMeta":{"turndown":{"optional":true},"patchright":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/webnovel-downloader_0.2.0_1782014165412_0.9752128777674565","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@duyquangnvx/webnovel-downloader","version":"0.3.0","license":"MIT","_id":"@duyquangnvx/webnovel-downloader@0.3.0","maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"dist":{"shasum":"7840cb494907b72f41ce4fac4fbfab4c807c375c","tarball":"https://registry.npmjs.org/@duyquangnvx/webnovel-downloader/-/webnovel-downloader-0.3.0.tgz","fileCount":17,"integrity":"sha512-1Q2IuGpMgwSEzA5ZWH8tn5Gezw0FR6uWCNr+rYzFIUdP5jWJha+mXdiV/BGbCfQzfgb3MDYmUyBTabmysfCONg==","signatures":[{"sig":"MEQCIH5vFaIkC+R57ALadFHLP/mniPiXMxnDIRgVDBrTdLcjAiAtPJJhUFQVN02Z0rNuC3x5gdBpjHB3W65fVm82sNos/A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":745134},"main":"./dist/index.cjs","type":"module","_from":"file:duyquangnvx-webnovel-downloader-0.3.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dump":"tsx examples/dump.ts","lint":"eslint .","test":"vitest run","build":"tsup","format":"prettier --write .","example":"tsx examples/basic.ts","release":"pnpm publish --access public","check:cjs":"node scripts/check-cjs.cjs","typecheck":"tsc -p tsconfig.test.json --noEmit","smoke:live":"tsx scripts/live-smoke.ts","test:watch":"vitest","format:check":"prettier --check .","test:browser":"vitest run test/adapters/wikicv/browser-integration.test.ts","fixtures:refresh":"tsx scripts/refresh-fixtures.ts"},"_npmUser":{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"},"_resolved":"/private/var/folders/hr/4rd2g3hn3cxc_nz46pzqry6w0000gn/T/1e1c4701f4a0f7263adab8fe89601bdf/duyquangnvx-webnovel-downloader-0.3.0.tgz","_integrity":"sha512-1Q2IuGpMgwSEzA5ZWH8tn5Gezw0FR6uWCNr+rYzFIUdP5jWJha+mXdiV/BGbCfQzfgb3MDYmUyBTabmysfCONg==","_npmVersion":"11.13.0","description":"A pluggable, type-safe webnovel downloader for Node.js.","directories":{},"_nodeVersion":"24.17.0","dependencies":{"zod":"^3.23.0","pino":"^9.0.0","undici":"^6.0.0","cheerio":"^1.0.0","p-queue":"^8.0.1","slugify":"^1.6.6","html-to-text":"^10.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"latest","eslint":"latest","vitest":"latest","prettier":"latest","@eslint/js":"latest","patchright":"^1.59.4","typescript":"^5.7.0","@types/node":"latest","@types/turndown":"latest","typescript-eslint":"latest","@types/html-to-text":"^9.0.4","eslint-config-prettier":"latest"},"peerDependencies":{"turndown":"^7.0.0","patchright":"^1.0.0","playwright":"^1.40.0"},"peerDependenciesMeta":{"turndown":{"optional":true},"patchright":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/webnovel-downloader_0.3.0_1782354500814_0.8789785823302869","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@duyquangnvx/webnovel-downloader","version":"0.4.0","license":"MIT","_id":"@duyquangnvx/webnovel-downloader@0.4.0","maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"dist":{"shasum":"beaf4513713684ed562479ec8b59fd8060de82b2","tarball":"https://registry.npmjs.org/@duyquangnvx/webnovel-downloader/-/webnovel-downloader-0.4.0.tgz","fileCount":17,"integrity":"sha512-N7hkSwpX/XEYvjoTDtMAFK2gt6xicuW3d8BDVu4JhTvCt492kSySM0CrdFUG/YefLVEkMLkaiLedDPfWBDqhyA==","signatures":[{"sig":"MEUCIQD3sN51KEmNzA3jr5vHHDbS+RAG2T5x3F9ldefc4XW8LgIgfHrvvqamlD8ODXOLWuv0ONfpcL1GcFSK/GYOPOtSoRI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":761529},"main":"./dist/index.cjs","type":"module","_from":"file:duyquangnvx-webnovel-downloader-0.4.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dump":"tsx examples/dump.ts","lint":"eslint .","test":"vitest run","build":"tsup","format":"prettier --write .","example":"tsx examples/basic.ts","release":"pnpm publish --access public","check:cjs":"node scripts/check-cjs.cjs","typecheck":"tsc -p tsconfig.test.json --noEmit","smoke:live":"tsx scripts/live-smoke.ts","test:watch":"vitest","format:check":"prettier --check .","test:browser":"vitest run test/adapters/wikicv/browser-integration.test.ts","fixtures:refresh":"tsx scripts/refresh-fixtures.ts"},"_npmUser":{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"},"_resolved":"/private/var/folders/hr/4rd2g3hn3cxc_nz46pzqry6w0000gn/T/7fae4ebfe24aa10728660ea4620f0346/duyquangnvx-webnovel-downloader-0.4.0.tgz","_integrity":"sha512-N7hkSwpX/XEYvjoTDtMAFK2gt6xicuW3d8BDVu4JhTvCt492kSySM0CrdFUG/YefLVEkMLkaiLedDPfWBDqhyA==","_npmVersion":"11.13.0","description":"A pluggable, type-safe webnovel downloader for Node.js.","directories":{},"_nodeVersion":"24.17.0","dependencies":{"zod":"^3.23.0","pino":"^9.0.0","undici":"^6.0.0","cheerio":"^1.0.0","p-queue":"^8.0.1","slugify":"^1.6.6","html-to-text":"^10.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"latest","eslint":"latest","vitest":"latest","prettier":"latest","@eslint/js":"latest","patchright":"^1.59.4","typescript":"^5.7.0","@types/node":"latest","@types/turndown":"latest","typescript-eslint":"latest","@types/html-to-text":"^9.0.4","eslint-config-prettier":"latest"},"peerDependencies":{"turndown":"^7.0.0","patchright":"^1.0.0","playwright":"^1.40.0"},"peerDependenciesMeta":{"turndown":{"optional":true},"patchright":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/webnovel-downloader_0.4.0_1782361894956_0.6296322634219578","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@duyquangnvx/webnovel-downloader","version":"0.4.1","license":"MIT","_id":"@duyquangnvx/webnovel-downloader@0.4.1","maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"dist":{"shasum":"bdcf4831c68e9abcde8523d54364968f6b2e8c16","tarball":"https://registry.npmjs.org/@duyquangnvx/webnovel-downloader/-/webnovel-downloader-0.4.1.tgz","fileCount":17,"integrity":"sha512-brMkd8lLptUJhAkpFztlkEeKKtoLZwnZAB3pZhz1AAodQrbW5gSvDl0wRA2qsPpyywblZT2UN9dxcgo4t4iSag==","signatures":[{"sig":"MEQCICDJy4hU1GMcPwbeWw9gfhbfHncByVeqAWk16TbKQLqUAiAcGl7IsOp97LzjTplFO3dGo7yYKOBu3EzJFO+nEU18lg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":769911},"main":"./dist/index.cjs","type":"module","_from":"file:duyquangnvx-webnovel-downloader-0.4.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dump":"tsx examples/dump.ts","lint":"eslint .","test":"vitest run","build":"tsup","format":"prettier --write .","example":"tsx examples/basic.ts","release":"pnpm publish --access public","check:cjs":"node scripts/check-cjs.cjs","typecheck":"tsc -p tsconfig.test.json --noEmit","smoke:live":"tsx scripts/live-smoke.ts","test:watch":"vitest","format:check":"prettier --check .","test:browser":"vitest run test/adapters/wikicv/browser-integration.test.ts","fixtures:refresh":"tsx scripts/refresh-fixtures.ts"},"_npmUser":{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"},"_resolved":"/private/var/folders/hr/4rd2g3hn3cxc_nz46pzqry6w0000gn/T/e53c760636ad6bf7793f1ee82013b108/duyquangnvx-webnovel-downloader-0.4.1.tgz","_integrity":"sha512-brMkd8lLptUJhAkpFztlkEeKKtoLZwnZAB3pZhz1AAodQrbW5gSvDl0wRA2qsPpyywblZT2UN9dxcgo4t4iSag==","_npmVersion":"11.13.0","description":"A pluggable, type-safe webnovel downloader for Node.js.","directories":{},"_nodeVersion":"24.17.0","dependencies":{"zod":"^3.23.0","pino":"^9.0.0","undici":"^6.0.0","cheerio":"^1.0.0","p-queue":"^8.0.1","slugify":"^1.6.6","html-to-text":"^10.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"latest","eslint":"latest","vitest":"latest","prettier":"latest","@eslint/js":"latest","patchright":"^1.59.4","typescript":"^5.7.0","@types/node":"latest","@types/turndown":"latest","typescript-eslint":"latest","@types/html-to-text":"^9.0.4","eslint-config-prettier":"latest"},"peerDependencies":{"turndown":"^7.0.0","patchright":"^1.0.0","playwright":"^1.40.0"},"peerDependenciesMeta":{"turndown":{"optional":true},"patchright":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/webnovel-downloader_0.4.1_1783070443667_0.15151578268900012","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@duyquangnvx/webnovel-downloader","version":"0.4.2","license":"MIT","_id":"@duyquangnvx/webnovel-downloader@0.4.2","maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"dist":{"shasum":"5f41cb4d98e04855a60e5ee19706ccd119efbe31","tarball":"https://registry.npmjs.org/@duyquangnvx/webnovel-downloader/-/webnovel-downloader-0.4.2.tgz","fileCount":17,"integrity":"sha512-mwZzSX97ke4DsAuB3W9yvLd61iYY9Ne+2LLlEfmzIDKvI0HIb6zExX+ybz8aI/9iTmbXh1tLTtFZq9/3/F15+Q==","signatures":[{"sig":"MEUCIH64DGGW+BL72dofNFvzQ77Qdf+F9cRTwLGLoI/lTkrVAiEAvwORBSdzvdlIjxDzbLrrRB3ntE7cQywtpjMlFqF8O8c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":774799},"main":"./dist/index.cjs","type":"module","_from":"file:duyquangnvx-webnovel-downloader-0.4.2.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dump":"tsx examples/dump.ts","lint":"eslint .","test":"vitest run","build":"tsup","format":"prettier --write .","example":"tsx examples/basic.ts","release":"pnpm publish --access public","check:cjs":"node scripts/check-cjs.cjs","typecheck":"tsc -p tsconfig.test.json --noEmit","smoke:live":"tsx scripts/live-smoke.ts","test:watch":"vitest","format:check":"prettier --check .","test:browser":"vitest run test/adapters/wikicv/browser-integration.test.ts","fixtures:refresh":"tsx scripts/refresh-fixtures.ts"},"_npmUser":{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"},"_resolved":"/private/var/folders/hr/4rd2g3hn3cxc_nz46pzqry6w0000gn/T/60f18ef6bc75e7bfd6495f0a6fb73162/duyquangnvx-webnovel-downloader-0.4.2.tgz","_integrity":"sha512-mwZzSX97ke4DsAuB3W9yvLd61iYY9Ne+2LLlEfmzIDKvI0HIb6zExX+ybz8aI/9iTmbXh1tLTtFZq9/3/F15+Q==","_npmVersion":"11.13.0","description":"A pluggable, type-safe webnovel downloader for Node.js.","directories":{},"_nodeVersion":"24.17.0","dependencies":{"zod":"^3.23.0","pino":"^9.0.0","undici":"^6.0.0","cheerio":"^1.0.0","p-queue":"^8.0.1","slugify":"^1.6.6","html-to-text":"^10.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"latest","eslint":"latest","vitest":"latest","prettier":"latest","@eslint/js":"latest","patchright":"^1.59.4","typescript":"^5.7.0","@types/node":"latest","@types/turndown":"latest","typescript-eslint":"latest","@types/html-to-text":"^9.0.4","eslint-config-prettier":"latest"},"peerDependencies":{"turndown":"^7.0.0","patchright":"^1.0.0","playwright":"^1.40.0"},"peerDependenciesMeta":{"turndown":{"optional":true},"patchright":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/webnovel-downloader_0.4.2_1783070934297_0.5206916317277102","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@duyquangnvx/webnovel-downloader","version":"0.5.0","license":"MIT","_id":"@duyquangnvx/webnovel-downloader@0.5.0","maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"dist":{"shasum":"070c2b1534dc07f2e9c4a79034789e93f5a824fe","tarball":"https://registry.npmjs.org/@duyquangnvx/webnovel-downloader/-/webnovel-downloader-0.5.0.tgz","fileCount":17,"integrity":"sha512-JQ9xHEMGCAfHIoUFscuUot2AsJYAQVEhtQPGKU2pv3Fn6QN0BQfnYmrcQDZfiV/xydpJ2+WH+NVtF8teABAF0Q==","signatures":[{"sig":"MEQCIHU5qmWYp9+EUMjQQYTkGnrynEqjktwloBSZ3dWJS0P4AiB5++/gL9s9Rv5VDDKMpdZfuyFWGIJoTOCEB0V+/tST/g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":783621},"main":"./dist/index.cjs","type":"module","_from":"file:duyquangnvx-webnovel-downloader-0.5.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"dump":"tsx examples/dump.ts","lint":"eslint .","test":"vitest run","build":"tsup","format":"prettier --write .","example":"tsx examples/basic.ts","release":"pnpm publish --access public","check:cjs":"node scripts/check-cjs.cjs","typecheck":"tsc -p tsconfig.test.json --noEmit","smoke:live":"tsx scripts/live-smoke.ts","test:watch":"vitest","format:check":"prettier --check .","test:browser":"vitest run test/adapters/wikicv/browser-integration.test.ts","fixtures:refresh":"tsx scripts/refresh-fixtures.ts"},"_npmUser":{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"},"_resolved":"/private/var/folders/hr/4rd2g3hn3cxc_nz46pzqry6w0000gn/T/83be8b7a7fff2099816dbdc8f3e9a1d8/duyquangnvx-webnovel-downloader-0.5.0.tgz","_integrity":"sha512-JQ9xHEMGCAfHIoUFscuUot2AsJYAQVEhtQPGKU2pv3Fn6QN0BQfnYmrcQDZfiV/xydpJ2+WH+NVtF8teABAF0Q==","_npmVersion":"11.13.0","description":"A pluggable, type-safe webnovel downloader for Node.js.","directories":{},"_nodeVersion":"24.17.0","dependencies":{"zod":"^3.23.0","pino":"^9.0.0","undici":"^6.0.0","cheerio":"^1.0.0","p-queue":"^8.0.1","slugify":"^1.6.6","html-to-text":"^10.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","tsup":"latest","eslint":"latest","vitest":"latest","prettier":"latest","@eslint/js":"latest","patchright":"^1.59.4","typescript":"^5.7.0","@types/node":"latest","@types/turndown":"latest","typescript-eslint":"latest","@types/html-to-text":"^9.0.4","eslint-config-prettier":"latest"},"peerDependencies":{"turndown":"^7.0.0","patchright":"^1.0.0","playwright":"^1.40.0"},"peerDependenciesMeta":{"turndown":{"optional":true},"patchright":{"optional":true},"playwright":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/webnovel-downloader_0.5.0_1783136470322_0.20390647716314625","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@duyquangnvx/webnovel-downloader","version":"0.6.0","license":"MIT","description":"A pluggable, type-safe webnovel downloader for Node.js.","type":"module","engines":{"node":">=20"},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"publishConfig":{"access":"public"},"dependencies":{"cheerio":"^1.0.0","html-to-text":"^10.0.0","p-queue":"^8.0.1","pino":"^9.0.0","slugify":"^1.6.6","undici":"^6.0.0","zod":"^3.23.0"},"peerDependencies":{"patchright":"^1.0.0","playwright":"^1.40.0","turndown":"^7.0.0"},"peerDependenciesMeta":{"patchright":{"optional":true},"playwright":{"optional":true},"turndown":{"optional":true}},"devDependencies":{"@eslint/js":"latest","@types/html-to-text":"^9.0.4","@types/node":"latest","@types/turndown":"latest","eslint":"latest","eslint-config-prettier":"latest","patchright":"^1.59.4","prettier":"latest","tsup":"latest","tsx":"^4.21.0","typescript":"^5.7.0","typescript-eslint":"latest","vitest":"latest"},"scripts":{"build":"tsup","check:cjs":"node scripts/check-cjs.cjs","release":"pnpm publish --access public","typecheck":"tsc -p tsconfig.test.json --noEmit","test":"vitest run","test:watch":"vitest","lint":"eslint .","format":"prettier --write .","format:check":"prettier --check .","fixtures:refresh":"tsx scripts/refresh-fixtures.ts","smoke:live":"tsx scripts/live-smoke.ts","example":"tsx examples/basic.ts","dump":"tsx examples/dump.ts","test:browser":"vitest run test/adapters/wikicv/browser-integration.test.ts"},"_id":"@duyquangnvx/webnovel-downloader@0.6.0","_integrity":"sha512-Bn3FAoZu0r17H9V/vXJ+rWH63iI2Q7CuNwgqs1Xz4cC8jY1xNQkJYdBuAWpJ0wd2MnWu6I7FxiNv1x9LFt2Q5Q==","_resolved":"C:\\Users\\QuangVD\\AppData\\Local\\Temp\\79ba847d7774be0275dfc99de37c6bc8\\duyquangnvx-webnovel-downloader-0.6.0.tgz","_from":"file:duyquangnvx-webnovel-downloader-0.6.0.tgz","_nodeVersion":"24.17.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-Bn3FAoZu0r17H9V/vXJ+rWH63iI2Q7CuNwgqs1Xz4cC8jY1xNQkJYdBuAWpJ0wd2MnWu6I7FxiNv1x9LFt2Q5Q==","shasum":"b7dbc86d369ecfcb971aef21bc344bd4d0c8d922","tarball":"https://registry.npmjs.org/@duyquangnvx/webnovel-downloader/-/webnovel-downloader-0.6.0.tgz","fileCount":17,"unpackedSize":817291,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGKdwA50AO21tnqkjPjTcP7Uv1LaxJV9tgCtAzmx2RvDAiEAg1h+wVlTXTeFT6leD0G4kCobbl4FgK7y9hh43cJS4i0="}]},"_npmUser":{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"},"directories":{},"maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/webnovel-downloader_0.6.0_1783281360575_0.8353826015475219"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-17T02:08:34.626Z","modified":"2026-07-05T19:56:00.885Z","0.1.0":"2026-06-17T02:08:34.895Z","0.2.0":"2026-06-21T03:56:05.574Z","0.3.0":"2026-06-25T02:28:20.970Z","0.4.0":"2026-06-25T04:31:35.093Z","0.4.1":"2026-07-03T09:20:43.843Z","0.4.2":"2026-07-03T09:28:54.452Z","0.5.0":"2026-07-04T03:41:10.469Z","0.6.0":"2026-07-05T19:56:00.772Z"},"license":"MIT","description":"A pluggable, type-safe webnovel downloader for Node.js.","maintainers":[{"name":"duyquangnvx","email":"duyquangnvx@gmail.com"}],"readme":"# webnovel-downloader\n\n[![npm version](https://img.shields.io/npm/v/@duyquangnvx/webnovel-downloader.svg)](https://www.npmjs.com/package/@duyquangnvx/webnovel-downloader)\n[![license: MIT](https://img.shields.io/npm/l/@duyquangnvx/webnovel-downloader.svg)](./LICENSE)\n[![node](https://img.shields.io/node/v/@duyquangnvx/webnovel-downloader.svg)](https://nodejs.org)\n[![types: included](https://img.shields.io/npm/types/@duyquangnvx/webnovel-downloader.svg)](./dist)\n\nA pluggable, type-safe webnovel downloader for Node.js. Point it at a novel's\nURL on a supported site and get back structured data — metadata plus ordered,\nnormalized chapters — ready to serialize into EPUB, TXT, JSON, or Markdown with\na formatter of your choice.\n\nPart of the [`webnovel-studio`](https://github.com/duyquangnvx/webnovel-studio)\ntoolkit, but usable standalone.\n\n> **Status:** Pre-alpha. The public API may still change between minor versions.\n\n## Features\n\n- **Pluggable adapters** — each site is a self-contained `SiteAdapter`, resolved automatically from the URL.\n- **Type-safe end to end** — every external response is validated with [Zod](https://zod.dev); results are discriminated unions you narrow with `switch` (no `any`, no casts).\n- **Concurrent, but polite** — parallel chapter fetches behind a per-host token-bucket rate limiter (default ~2 req/s) with automatic retries and exponential backoff.\n- **Resumable** — crash-safe state; re-run to skip finished chapters and retry only the failures, or resume a `partial` run from a token.\n- **Browser tier on demand** — transparently escalates to a real (headless) browser for Cloudflare-fronted or JS-rendered sites; plain HTTP everywhere else.\n- **Format-agnostic output** — normalized plain-text chapters; you own the file writing.\n- **Ships ESM + CommonJS** with full type declarations. `import` and `require` both work. Node ≥ 20.\n\n## Install\n\n```bash\nnpm install @duyquangnvx/webnovel-downloader\n# optional — only needed for Cloudflare / JS-rendered sites (e.g. wikicv):\nnpm install patchright\n```\n\n`patchright` (or `playwright`) is an optional peer dependency for the browser\ntransport tier. `truyenfull` and `metruyenchu` work over plain HTTP without it.\n\n## Quick start\n\n```ts\nimport { downloader } from \"@duyquangnvx/webnovel-downloader\";\n\nconst result = await downloader.download(\"https://truyenfull.today/tien-nghich/\");\n\nif (result.status === \"success\") {\n  const { metadata, chapters } = result.data;\n  console.log(`${metadata.title} — ${chapters.length} chapters`);\n}\n```\n\n`downloader` is a shared, process-wide singleton with sensible defaults (auto\ntransport, headless, ~2 req/s per host). Use it for the common case. When you\nneed custom throttling, retries, a headed browser, or deterministic teardown,\nbuild your own instance with [`createDownloader()`](#configuration).\n\n## Supported sites\n\nPass the novel's **landing page** (its table-of-contents URL). The adapter is\nresolved from the host automatically — you never name it.\n\n| Site | Adapter id | Transport | Notes |\n|---|---|---|---|\n| `truyenfull.today` (+ `truyenfull.vision`, `truyenfull.vn`) | `truyenfull` | HTTP | Legacy `.vision` / `.vn` hosts auto-rewrite to `.today`. |\n| `metruyenchu.com.vn` | `metruyenchu-com-vn` | HTTP | Distinct from the dead `metruyenchu.com` brand. |\n| `wikicv.net` (+ `truyenwikidich.net`) | `wikicv` | **Browser required** | Needs `patchright`/`playwright`. May hit Cloudflare. `truyenwikidich.net` auto-rewrites. |\n| `tangthuvien.net` | `tangthuvien` | — | Scaffold only — parsers TBD, not yet functional. |\n\nCheck support at runtime with `downloader.canHandle(url)` (boolean) or enumerate\nsites with `downloader.supportedSites()`.\n\n## Core API\n\nMethods on a `Downloader` (the `downloader` singleton or a `createDownloader()` instance):\n\n| Method | Returns | Description |\n|---|---|---|\n| `download(url, options?)` | `Promise<DownloadResult>` | Fetch the whole novel. Returns a result envelope; throws only on abort or browser setup failure. |\n| `fetchMetadata(url, options?)` | `Promise<NovelMetadata>` | Metadata only (title, author, cover, `totalChapters`, …). Returns a bare value and **throws** on failure. |\n| `fetchChapterList(url, options?)` | `Promise<readonly ChapterRef[]>` | The table of contents (index/title/url) **without** downloading any chapter bodies. Honors `chapterRange`. **Throws** on failure. |\n| `canHandle(url)` | `boolean` | Whether a registered adapter can handle this URL. |\n| `supportedSites()` | `{ id, displayName, hostnames }[]` | Plain, serializable list of registered sites. |\n| `dispose()` | `Promise<void>` | Release held resources (browser pool, HTTP sockets). Call on `createDownloader()` instances when done. |\n\n### The result envelope\n\n`download()` returns a discriminated union — always narrow on `status` before\nreading `data`:\n\n```ts\nconst result = await downloader.download(url);\n\nswitch (result.status) {\n  case \"success\":\n    // Every in-range chapter was fetched.\n    console.log(result.data.chapters.length);\n    break;\n  case \"partial\":\n    // Some chapters permanently failed. `data` has the rest (with gaps at the\n    // failed indices); `failures` lists what broke. Resumable when you passed\n    // `resume` — narrow on `resumable` before reading `resumeToken`.\n    console.warn(`${result.failures.length} chapters failed`);\n    if (result.resumable) {\n      await downloader.download(url, { resume: { token: result.resumeToken } });\n    }\n    break;\n  case \"error\":\n    // No novel produced (bad/unknown URL, invalid range, metadata/TOC failure).\n    // No `data`. `error.code` is a stable string you can switch on.\n    console.error(result.error.code, result.error.message);\n    break;\n}\n```\n\n`download()` **throws** only when it can't reach the envelope stage: an abort via\n`options.signal` (`CancelledError`), or a browser-tier site whose browser can't\nstart — no `patchright`/`playwright` installed (`BrowserModuleNotInstalledError`)\nor `transport: \"http-only\"` (`ParseError`). Wrap those in `try/catch`.\n\n### Output shape\n\n```ts\nNovelData    = { metadata: NovelMetadata; chapters: readonly Chapter[] }\nNovelMetadata = { sourceUrl, sourceSite, title, author, description,\n                  coverUrl?, genres, status, totalChapters?, fetchedAt }\nChapter       = { index, title, url, volume?, content, wordCount, fetchedAt }\n```\n\n`chapters` is sorted by source `index` (0-based); `content` is normalized plain\ntext with paragraphs separated by blank lines. `totalChapters` is the site's\nreported count and may be absent — never inferred from what was fetched.\n\n## Options\n\nPer-call options for `download()` / `fetchMetadata()` / `fetchChapterList()` —\nall optional:\n\n| Option | Type | Purpose |\n|---|---|---|\n| `concurrency` | `number` | Parallel chapter fetches (default `4`). Lower it to be gentler. |\n| `chapterRange` | `{ from?, to? }` | Fetch a contiguous slice. **0-based, inclusive** — chapter 1 is index 0, so `{ from: 0, to: 9 }` is the first 10. Invalid bound ⇒ `{ status: \"error\" }`. |\n| `cache` | `{ dir, maxAgeMs? }` | On-disk HTTP response cache (ETag/304) so reruns skip refetching. |\n| `resume` | `true \\| { stateFile } \\| { token }` | Crash-safe resume (see below). |\n| `signal` | `AbortSignal` | Cancel mid-run; `download()` then rejects with `CancelledError`. |\n| `onEvent` | `(e: DownloadEvent) => void` | Progress/lifecycle callback (see below). |\n| `adapter` | `SiteAdapter` | Force a specific adapter instead of resolving from the URL. |\n\n`rateLimit`, `retry`, and `transport` are **instance-level**, not per-call — set\nthem via `createDownloader()`.\n\n### Progress events\n\nPass `onEvent` and discriminate on `type`:\n\n```ts\nawait downloader.download(url, {\n  onEvent: (e) => {\n    if (e.type === \"toc:complete\") console.log(`found ${e.total} chapters`);\n    if (e.type === \"progress\") console.log(`${e.completed}/${e.total}`);\n  },\n});\n```\n\nEvent types: `metadata:fetched`, `toc:progress`, `toc:complete`,\n`chapter:start`, `chapter:success`, `chapter:failed`, `progress`, plus transport\nsignals `cache:hit`, `rate-limit:wait`, and `http:retry`.\n\n## Configuration\n\n`createDownloader()` builds an instance wired with the built-in adapters, but\nlets you set transport, throttling, retries, and logging:\n\n```ts\nimport { createDownloader } from \"@duyquangnvx/webnovel-downloader\";\n\nconst dl = createDownloader({\n  rateLimit: { requestsPerSecond: 1 },        // per host; default 2\n  retry: { retries: 5, backoff: \"exponential\" },\n  transport: { mode: \"auto\" },                // \"auto\" | \"http-only\" | \"browser-required\"\n  logLevel: \"info\",                            // pino level shortcut\n});\ntry {\n  const result = await dl.download(url);\n} finally {\n  await dl.dispose();  // releases the browser pool and HTTP sockets\n}\n```\n\nIt also accepts `http` (a custom `HttpClient` replacing the whole transport\nstack), `logger` (a pino instance), and `adapters` (override the registered set —\ncombine `builtinAdapters()` with your own).\n\n## Transport tier (Cloudflare / JS-rendered sites)\n\nSites behind Cloudflare or rendering content via JS need a real browser. Install\none peer:\n\n```bash\nnpm install patchright   # recommended — built to slip past Cloudflare unattended\nnpm install playwright   # works for non-protected pages\n```\n\nThe module is resolved at runtime (`patchright` first, then `playwright`) and the\ndefault `auto` transport launches it **headless** only when an adapter needs it.\nIf a Cloudflare challenge blocks the headless run, launch a **headed** window and\nsolve the checkbox once (cookies then cache ~30 min within that instance):\n\n```ts\nconst dl = createDownloader({\n  transport: { mode: \"auto\", browserOptions: { headed: true } },\n});\n```\n\nHeaded mode needs a display — run it where a human can click, not on a headless\nCI box. Transport modes:\n\n- `\"auto\"` — undici first, escalate to the browser on a Cloudflare challenge.\n- `\"http-only\"` — never launch a browser; browser-only adapters throw a `ParseError`.\n- `\"browser-required\"` — route every request through the browser.\n\nA challenge the headless run can't clear returns `{ status: \"error\" }` with\n`error.code === \"CHALLENGE_UNRESOLVED\"` (and a `hint`) — do a headed solve.\n\n## Resume & partial downloads\n\n```ts\nawait downloader.download(url, { resume: true });\n```\n\nWith `resume: true`, state and per-chapter files live under the OS cache dir\n(`%LOCALAPPDATA%\\...` on Windows, `~/Library/Caches/...` on macOS,\n`$XDG_CACHE_HOME`/`~/.cache/...` on Linux — all under\n`webnovel-downloader/state/`). Re-running skips finished chapters and retries the\nfailed ones. To resume a specific `partial` run instead, pass its token:\n\n```ts\nconst result = await downloader.download(url, { resume: true });\nif (result.status === \"partial\" && result.resumable) {\n  await downloader.download(url, { resume: { token: result.resumeToken } });\n}\n```\n\n## Error handling\n\nEvery error carries a stable, machine-readable `code`. The error channel is a\ndiscriminated union, so a `switch` narrows to the concrete error and its typed\nfields:\n\n```ts\nif (result.status === \"error\") {\n  switch (result.error.code) {\n    case \"HTTP_ERROR\":\n      console.error(`HTTP ${result.error.status} for ${result.error.url}`);\n      break;\n    case \"RATE_LIMITED\":\n      console.error(`rate limited; retry after ${result.error.retryAfterMs}ms`);\n      break;\n    case \"CHALLENGE_UNRESOLVED\":\n      console.error(`Cloudflare challenge (${result.error.hint})`);\n      break;\n    default:\n      console.error(result.error.message);\n  }\n}\n```\n\nCodes: `ADAPTER_NOT_FOUND`, `HTTP_ERROR`, `RATE_LIMITED`, `PARSE_ERROR`,\n`CHAPTER_FETCH_FAILED`, `TIMEOUT`, `CANCELLED`, `BROWSER_MODULE_NOT_INSTALLED`,\n`CHALLENGE_UNRESOLVED`.\n\n## Examples\n\nRunnable scripts in [`examples/`](./examples):\n\n- [`basic.ts`](./examples/basic.ts) — download with live progress logging.\n- [`dump.ts`](./examples/dump.ts) — download and serialize to `.txt` files + `summary.json`.\n- [`custom-transport.ts`](./examples/custom-transport.ts) — plug in a SaaS render service via a custom `HttpClient`.\n\n```bash\npnpm example <url>          # basic.ts\npnpm dump <url> [outDir]    # dump.ts\n```\n\n## Documentation\n\nDeep dives live in [`docs/`](./docs):\n\n- [`architecture.md`](./docs/architecture.md) — layers and the big picture.\n- [`data-model.md`](./docs/data-model.md) — core types and contracts.\n- [`pipeline.md`](./docs/pipeline.md) — download flow, events, errors, resume.\n- [`adapter-spec.md`](./docs/adapter-spec.md) — how to add a new site.\n\nAdding a new site = implementing a single `SiteAdapter`; see `adapter-spec.md`.\n\n## Development\n\n```bash\npnpm install\npnpm test            # vitest\npnpm typecheck       # tsc --noEmit\npnpm build           # tsup → dist/ (ESM + CJS + d.ts)\npnpm smoke:live      # live end-to-end check across active adapters\n```\n\n## Publishing\n\nMaintainers only. Bump `version`, then `pnpm release` (runs `prepublishOnly` to\nbuild `dist/`, then `pnpm publish --access public`). Only `dist/` ships. Publish\nfrom a clean `main`.\n\n## License\n\nMIT — see [`LICENSE`](./LICENSE).\n","readmeFilename":"README.md"}