继续完善规则引擎
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
"@upstash/redis": "^1.25.0",
|
||||
"@vercel/postgres": "^0.10.0",
|
||||
"@vidstack/react": "^1.12.13",
|
||||
"@xmldom/xmldom": "^0.9.10",
|
||||
"anime4k-webgpu": "^1.0.0",
|
||||
"artplayer": "^5.3.0",
|
||||
"artplayer-plugin-danmuku": "^5.2.0",
|
||||
@@ -75,6 +76,7 @@
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"vidstack": "^0.6.15",
|
||||
"xml2js": "^0.6.2",
|
||||
"xpath": "^0.0.34",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Generated
+21
-504
@@ -41,6 +41,9 @@ importers:
|
||||
'@vidstack/react':
|
||||
specifier: ^1.12.13
|
||||
version: 1.12.13(@types/[email protected])([email protected])
|
||||
'@xmldom/xmldom':
|
||||
specifier: ^0.9.10
|
||||
version: 0.9.10
|
||||
anime4k-webgpu:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(@webgpu/[email protected])
|
||||
@@ -158,6 +161,9 @@ importers:
|
||||
xml2js:
|
||||
specifier: ^0.6.2
|
||||
version: 0.6.2
|
||||
xpath:
|
||||
specifier: ^0.0.34
|
||||
version: 0.0.34
|
||||
zod:
|
||||
specifier: ^3.24.1
|
||||
version: 3.25.76
|
||||
@@ -3141,9 +3147,6 @@ packages:
|
||||
'@types/[email protected]':
|
||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||
|
||||
'@types/[email protected]':
|
||||
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
|
||||
|
||||
'@types/[email protected]':
|
||||
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
||||
|
||||
@@ -3549,6 +3552,10 @@ packages:
|
||||
'@webgpu/[email protected]':
|
||||
resolution: {integrity: sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA==}
|
||||
|
||||
'@xmldom/[email protected]':
|
||||
resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==}
|
||||
engines: {node: '>=14.6'}
|
||||
|
||||
'@xtuc/[email protected]':
|
||||
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
|
||||
|
||||
@@ -4437,10 +4444,6 @@ packages:
|
||||
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4658,10 +4661,6 @@ packages:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
|
||||
engines: {node: '>=6.0'}
|
||||
@@ -5935,9 +5934,6 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -5949,33 +5945,9 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==}
|
||||
|
||||
@@ -5985,24 +5957,15 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
||||
|
||||
@@ -6044,150 +6007,66 @@ packages:
|
||||
engines: {node: '>= 8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
|
||||
|
||||
@@ -6837,6 +6716,7 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
|
||||
hasBin: true
|
||||
|
||||
[email protected]:
|
||||
@@ -7064,7 +6944,6 @@ packages:
|
||||
resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==}
|
||||
hasBin: true
|
||||
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
|
||||
|
||||
@@ -7151,10 +7030,6 @@ packages:
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
|
||||
engines: {node: '>=0.4'}
|
||||
@@ -7873,9 +7748,6 @@ packages:
|
||||
resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
|
||||
|
||||
@@ -7883,30 +7755,18 @@ packages:
|
||||
resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
|
||||
|
||||
@@ -7963,11 +7823,6 @@ packages:
|
||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||
hasBin: true
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||
|
||||
@@ -7988,15 +7843,9 @@ packages:
|
||||
engines: {node: '>= 18'}
|
||||
hasBin: true
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
|
||||
|
||||
@@ -8068,10 +7917,12 @@ packages:
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
|
||||
deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
|
||||
engines: {node: '>=18'}
|
||||
deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
|
||||
@@ -8278,6 +8129,10 @@ packages:
|
||||
resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
|
||||
engines: {node: '>=0.4'}
|
||||
@@ -12563,10 +12418,6 @@ snapshots:
|
||||
|
||||
'@types/[email protected]': {}
|
||||
|
||||
'@types/[email protected]':
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
|
||||
'@types/[email protected]':
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
@@ -13187,6 +13038,8 @@ snapshots:
|
||||
|
||||
'@webgpu/[email protected]': {}
|
||||
|
||||
'@xmldom/[email protected]': {}
|
||||
|
||||
'@xtuc/[email protected]': {}
|
||||
|
||||
'@xtuc/[email protected]': {}
|
||||
@@ -14075,8 +13928,6 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
@@ -14430,8 +14281,6 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
esprima: 4.0.1
|
||||
@@ -16128,8 +15977,6 @@ snapshots:
|
||||
dependencies:
|
||||
tmpl: 1.0.5
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
@@ -16143,30 +15990,6 @@ snapshots:
|
||||
crypt: 0.0.2
|
||||
is-buffer: 1.1.6
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
escape-string-regexp: 5.0.0
|
||||
unist-util-is: 5.2.1
|
||||
unist-util-visit-parents: 5.1.3
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
'@types/unist': 2.0.11
|
||||
decode-named-character-reference: 1.2.0
|
||||
mdast-util-to-string: 3.2.0
|
||||
micromark: 3.2.0
|
||||
micromark-util-decode-numeric-character-reference: 1.1.0
|
||||
micromark-util-decode-string: 1.1.0
|
||||
micromark-util-normalize-identifier: 1.1.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
unist-util-stringify-position: 3.0.3
|
||||
uvu: 0.5.6
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
@@ -16184,50 +16007,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
ccount: 2.0.1
|
||||
mdast-util-find-and-replace: 2.2.2
|
||||
micromark-util-character: 1.2.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
mdast-util-to-markdown: 1.5.0
|
||||
micromark-util-normalize-identifier: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
mdast-util-to-markdown: 1.5.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
markdown-table: 3.0.4
|
||||
mdast-util-from-markdown: 1.3.1
|
||||
mdast-util-to-markdown: 1.5.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
mdast-util-to-markdown: 1.5.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
mdast-util-from-markdown: 1.3.1
|
||||
mdast-util-gfm-autolink-literal: 1.0.3
|
||||
mdast-util-gfm-footnote: 1.0.2
|
||||
mdast-util-gfm-strikethrough: 1.0.3
|
||||
mdast-util-gfm-table: 1.0.7
|
||||
mdast-util-gfm-task-list-item: 1.0.2
|
||||
mdast-util-to-markdown: 1.5.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/estree-jsx': 1.0.5
|
||||
@@ -16267,11 +16046,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
unist-util-is: 5.2.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
@@ -16289,17 +16063,6 @@ snapshots:
|
||||
unist-util-visit: 5.0.0
|
||||
vfile: 6.0.3
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
'@types/unist': 2.0.11
|
||||
longest-streak: 3.1.0
|
||||
mdast-util-phrasing: 3.0.1
|
||||
mdast-util-to-string: 3.2.0
|
||||
micromark-util-decode-string: 1.1.0
|
||||
unist-util-visit: 4.1.2
|
||||
zwitch: 2.0.4
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
@@ -16312,10 +16075,6 @@ snapshots:
|
||||
unist-util-visit: 5.0.0
|
||||
zwitch: 2.0.4
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 3.0.15
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
@@ -16344,25 +16103,6 @@ snapshots:
|
||||
content-type: 1.0.4
|
||||
raw-body: 2.4.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
decode-named-character-reference: 1.2.0
|
||||
micromark-factory-destination: 1.1.0
|
||||
micromark-factory-label: 1.1.0
|
||||
micromark-factory-space: 1.1.0
|
||||
micromark-factory-title: 1.1.0
|
||||
micromark-factory-whitespace: 1.1.0
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-chunked: 1.1.0
|
||||
micromark-util-classify-character: 1.1.0
|
||||
micromark-util-html-tag-name: 1.2.0
|
||||
micromark-util-normalize-identifier: 1.1.0
|
||||
micromark-util-resolve-all: 1.1.0
|
||||
micromark-util-subtokenize: 1.1.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
uvu: 0.5.6
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
decode-named-character-reference: 1.2.0
|
||||
@@ -16382,83 +16122,12 @@ snapshots:
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-sanitize-uri: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-core-commonmark: 1.1.0
|
||||
micromark-factory-space: 1.1.0
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-normalize-identifier: 1.1.0
|
||||
micromark-util-sanitize-uri: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
uvu: 0.5.6
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-chunked: 1.1.0
|
||||
micromark-util-classify-character: 1.1.0
|
||||
micromark-util-resolve-all: 1.1.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
uvu: 0.5.6
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-factory-space: 1.1.0
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
uvu: 0.5.6
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-factory-space: 1.1.0
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
uvu: 0.5.6
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-extension-gfm-autolink-literal: 1.0.5
|
||||
micromark-extension-gfm-footnote: 1.1.2
|
||||
micromark-extension-gfm-strikethrough: 1.0.7
|
||||
micromark-extension-gfm-table: 1.0.7
|
||||
micromark-extension-gfm-tagfilter: 1.0.2
|
||||
micromark-extension-gfm-task-list-item: 1.0.5
|
||||
micromark-util-combine-extensions: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 2.1.1
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
uvu: 0.5.6
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
devlop: 1.1.0
|
||||
@@ -16466,23 +16135,11 @@ snapshots:
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 2.1.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-factory-space: 1.1.0
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-factory-space: 2.0.1
|
||||
@@ -16490,13 +16147,6 @@ snapshots:
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-factory-space: 1.1.0
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-factory-space: 2.0.1
|
||||
@@ -16504,61 +16154,30 @@ snapshots:
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-symbol: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-symbol: 2.0.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 2.1.1
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-chunked: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-chunked: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-symbol: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-symbol: 2.0.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
decode-named-character-reference: 1.2.0
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-decode-numeric-character-reference: 1.1.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
decode-named-character-reference: 1.2.0
|
||||
@@ -16566,49 +16185,24 @@ snapshots:
|
||||
micromark-util-decode-numeric-character-reference: 2.0.2
|
||||
micromark-util-symbol: 2.0.1
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-symbol: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-symbol: 2.0.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-types: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-encode: 1.1.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-character: 2.1.1
|
||||
micromark-util-encode: 2.0.1
|
||||
micromark-util-symbol: 2.0.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
micromark-util-chunked: 1.1.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
uvu: 0.5.6
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
devlop: 1.1.0
|
||||
@@ -16616,36 +16210,10 @@ snapshots:
|
||||
micromark-util-symbol: 2.0.1
|
||||
micromark-util-types: 2.0.2
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/debug': 4.1.12
|
||||
debug: 4.4.3
|
||||
decode-named-character-reference: 1.2.0
|
||||
micromark-core-commonmark: 1.1.0
|
||||
micromark-factory-space: 1.1.0
|
||||
micromark-util-character: 1.2.0
|
||||
micromark-util-chunked: 1.1.0
|
||||
micromark-util-combine-extensions: 1.1.0
|
||||
micromark-util-decode-numeric-character-reference: 1.1.0
|
||||
micromark-util-encode: 1.1.0
|
||||
micromark-util-normalize-identifier: 1.1.0
|
||||
micromark-util-resolve-all: 1.1.0
|
||||
micromark-util-sanitize-uri: 1.2.0
|
||||
micromark-util-subtokenize: 1.1.0
|
||||
micromark-util-symbol: 1.1.0
|
||||
micromark-util-types: 1.1.0
|
||||
uvu: 0.5.6
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/debug': 4.1.12
|
||||
@@ -17471,7 +17039,6 @@ snapshots:
|
||||
dependencies:
|
||||
jsesc: 3.1.0
|
||||
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
@@ -17576,10 +17143,6 @@ snapshots:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
mri: 1.2.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
@@ -18427,16 +17990,6 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
bail: 2.0.2
|
||||
extend: 3.0.2
|
||||
is-buffer: 2.0.5
|
||||
is-plain-obj: 4.1.0
|
||||
trough: 2.2.0
|
||||
vfile: 5.3.7
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
@@ -18451,10 +18004,6 @@ snapshots:
|
||||
dependencies:
|
||||
crypto-random-string: 2.0.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
@@ -18463,30 +18012,15 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
unist-util-is: 5.2.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
unist-util-is: 6.0.1
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
unist-util-is: 5.2.1
|
||||
unist-util-visit-parents: 5.1.3
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
@@ -18560,13 +18094,6 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
diff: 5.2.0
|
||||
kleur: 4.1.5
|
||||
sade: 1.8.1
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]:
|
||||
@@ -18615,23 +18142,11 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
unist-util-stringify-position: 3.0.3
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
unist-util-stringify-position: 4.0.0
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
is-buffer: 2.0.5
|
||||
unist-util-stringify-position: 3.0.3
|
||||
vfile-message: 3.1.4
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
@@ -19013,6 +18528,8 @@ snapshots:
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
[email protected]: {}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { legadoClient } from '@/lib/legado.client';
|
||||
import crypto from 'crypto';
|
||||
import { validateProxyUrlServerSide } from '@/lib/server/ssrf';
|
||||
|
||||
import { getAuthorizedBooksUsername } from '../_utils';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
const imageCache = new Map<string, { expiresAt: number; contentType: string; data: Uint8Array }>();
|
||||
|
||||
function asObjectHeader(value?: string | Record<string, string>): Record<string, string> {
|
||||
if (!value) return {};
|
||||
if (typeof value === 'object') return value;
|
||||
@@ -33,19 +36,52 @@ export async function GET(request: NextRequest) {
|
||||
if (!sourceId || !url) return NextResponse.json({ error: '缺少 sourceId 或 url' }, { status: 400 });
|
||||
if (!(await validateProxyUrlServerSide(url))) return NextResponse.json({ error: '图片地址未通过安全校验' }, { status: 400 });
|
||||
const source = await legadoClient.getSourceById(sourceId);
|
||||
let imageOptions: any = {};
|
||||
try {
|
||||
const rawOptions = searchParams.get('options') || '';
|
||||
imageOptions = rawOptions ? JSON.parse(rawOptions) : {};
|
||||
} catch {
|
||||
imageOptions = {};
|
||||
}
|
||||
const headers: Record<string, string> = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0 Safari/537.36',
|
||||
Referer: source.legado?.bookSourceUrl || source.url,
|
||||
...asObjectHeader(source.legado?.header),
|
||||
...(imageOptions.headers && typeof imageOptions.headers === 'object' ? imageOptions.headers : {}),
|
||||
};
|
||||
delete headers.Host;
|
||||
delete headers.host;
|
||||
const res = await fetch(url, { headers, cache: 'no-store' });
|
||||
const cacheKey = crypto.createHash('sha1').update(`${sourceId}|${url}|${JSON.stringify(imageOptions)}`).digest('hex');
|
||||
const cached = imageCache.get(cacheKey);
|
||||
if (cached && cached.expiresAt > Date.now() && !request.headers.get('range')) {
|
||||
return new NextResponse(cached.data, { headers: { 'Content-Type': cached.contentType, 'Cache-Control': 'public, max-age=86400' } });
|
||||
}
|
||||
const range = request.headers.get('range');
|
||||
if (range) headers.Range = range;
|
||||
const res = await fetch(url, { method: imageOptions.method || 'GET', headers, body: imageOptions.body, cache: 'no-store', redirect: 'manual' });
|
||||
if ([301, 302, 303, 307, 308].includes(res.status)) {
|
||||
const location = res.headers.get('location');
|
||||
if (!location) return NextResponse.json({ error: '图片重定向缺少地址' }, { status: 502 });
|
||||
const redirected = new URL(location, url).toString();
|
||||
if (!(await validateProxyUrlServerSide(redirected))) return NextResponse.json({ error: '图片重定向地址未通过安全校验' }, { status: 400 });
|
||||
const second = await fetch(redirected, { method: imageOptions.method || 'GET', headers, body: imageOptions.body, cache: 'no-store' });
|
||||
if (!second.ok) return NextResponse.json({ error: `图片请求失败: ${second.status}` }, { status: second.status });
|
||||
const data = new Uint8Array(await second.arrayBuffer());
|
||||
const contentType = second.headers.get('content-type') || 'image/jpeg';
|
||||
if (!range) imageCache.set(cacheKey, { data, contentType, expiresAt: Date.now() + 86400_000 });
|
||||
return new NextResponse(data, { status: second.status, headers: { 'Content-Type': contentType, 'Cache-Control': 'public, max-age=86400' } });
|
||||
}
|
||||
if (!res.ok) return NextResponse.json({ error: `图片请求失败: ${res.status}` }, { status: res.status });
|
||||
return new NextResponse(res.body, {
|
||||
const data = new Uint8Array(await res.arrayBuffer());
|
||||
const contentType = res.headers.get('content-type') || 'image/jpeg';
|
||||
if (!range) imageCache.set(cacheKey, { data, contentType, expiresAt: Date.now() + 86400_000 });
|
||||
return new NextResponse(data, {
|
||||
status: res.status,
|
||||
headers: {
|
||||
'Content-Type': res.headers.get('content-type') || 'image/jpeg',
|
||||
'Content-Type': contentType,
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
...(res.headers.get('content-range') ? { 'Content-Range': res.headers.get('content-range') as string } : {}),
|
||||
...(res.headers.get('accept-ranges') ? { 'Accept-Ranges': res.headers.get('accept-ranges') as string } : {}),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -17,8 +17,11 @@ export interface LegadoRuleSearch {
|
||||
bookUrl?: string;
|
||||
kind?: string;
|
||||
lastChapter?: string;
|
||||
checkKeyWord?: string;
|
||||
wordCount?: string;
|
||||
}
|
||||
|
||||
|
||||
export type LegadoRuleExplore = LegadoRuleSearch;
|
||||
|
||||
export interface LegadoRuleBookInfo {
|
||||
@@ -29,6 +32,7 @@ export interface LegadoRuleBookInfo {
|
||||
tocUrl?: string;
|
||||
kind?: string;
|
||||
lastChapter?: string;
|
||||
wordCount?: string;
|
||||
}
|
||||
|
||||
export interface LegadoRuleToc {
|
||||
@@ -37,11 +41,18 @@ export interface LegadoRuleToc {
|
||||
chapterUrl?: string;
|
||||
isVip?: string;
|
||||
isPay?: string;
|
||||
nextTocUrl?: string;
|
||||
updateTime?: string;
|
||||
wordCount?: string;
|
||||
chapterInfo?: string;
|
||||
}
|
||||
|
||||
export interface LegadoRuleContent {
|
||||
content?: string;
|
||||
nextContentUrl?: string;
|
||||
imageStyle?: string;
|
||||
sourceRegex?: string;
|
||||
payAction?: string;
|
||||
}
|
||||
|
||||
export interface LegadoBookSourceRule {
|
||||
@@ -49,13 +60,23 @@ export interface LegadoBookSourceRule {
|
||||
bookSourceUrl?: string;
|
||||
bookSourceGroup?: string;
|
||||
bookSourceType?: number;
|
||||
customButton?: boolean | unknown;
|
||||
eventListener?: boolean | unknown;
|
||||
variable?: string | Record<string, unknown>;
|
||||
loginUi?: string | unknown;
|
||||
concurrentRate?: string | number;
|
||||
respondTime?: number;
|
||||
lastUpdateTime?: string | number;
|
||||
enabled?: boolean;
|
||||
enabledCookieJar?: boolean;
|
||||
enabledExplore?: boolean;
|
||||
exploreUrl?: string;
|
||||
header?: string | Record<string, string>;
|
||||
loginUrl?: string;
|
||||
searchUrl?: string;
|
||||
bookInfoUrl?: string;
|
||||
bookInfoInit?: string;
|
||||
bookUrlPattern?: string;
|
||||
tocUrl?: string;
|
||||
chapterUrl?: string;
|
||||
ruleSearch?: LegadoRuleSearch;
|
||||
@@ -63,6 +84,7 @@ export interface LegadoBookSourceRule {
|
||||
ruleBookInfo?: LegadoRuleBookInfo;
|
||||
ruleToc?: LegadoRuleToc;
|
||||
ruleContent?: LegadoRuleContent;
|
||||
ruleReview?: unknown;
|
||||
customOrder?: number;
|
||||
weight?: number;
|
||||
}
|
||||
|
||||
+403
-45
@@ -2,6 +2,9 @@
|
||||
import * as cheerio from 'cheerio/slim';
|
||||
import crypto from 'crypto';
|
||||
import he from 'he';
|
||||
import vm from 'vm';
|
||||
import { DOMParser } from '@xmldom/xmldom';
|
||||
import xpath from 'xpath';
|
||||
|
||||
import { getConfig } from './config';
|
||||
import {
|
||||
@@ -36,6 +39,19 @@ const detailCache = new Map<string, { expiresAt: number; data: BookDetail }>();
|
||||
const tocCache = new Map<string, { expiresAt: number; data: BookChapter[] }>();
|
||||
const chapterCache = new Map<string, { expiresAt: number; data: BookChapterContent }>();
|
||||
|
||||
interface RequestOptions {
|
||||
url: string;
|
||||
method?: string;
|
||||
body?: string;
|
||||
headers?: Record<string, string>;
|
||||
charset?: string;
|
||||
retry?: number;
|
||||
}
|
||||
|
||||
const cookieJar = new Map<string, string>();
|
||||
const variableStore = new Map<string, any>();
|
||||
const imageMemoryCache = new Map<string, { expiresAt: number; contentType: string; data: Uint8Array }>();
|
||||
|
||||
function stableId(input: string) {
|
||||
return crypto.createHash('sha1').update(input).digest('hex').slice(0, 16);
|
||||
}
|
||||
@@ -87,18 +103,84 @@ function encodeRuleParam(value: string) {
|
||||
return encodeURIComponent(value).replace(/%20/g, '+');
|
||||
}
|
||||
|
||||
function safeEvalTemplateExpression(expr: string, keyword: string, page: number) {
|
||||
const key = keyword;
|
||||
const searchTerms = keyword;
|
||||
const java = {
|
||||
base64Encode: (value: unknown) => Buffer.from(String(value ?? ''), 'utf8').toString('base64'),
|
||||
base64Decode: (value: unknown) => Buffer.from(String(value ?? ''), 'base64').toString('utf8'),
|
||||
encodeURI: (value: unknown) => encodeURIComponent(String(value ?? '')),
|
||||
encodeURIComponent: (value: unknown) => encodeURIComponent(String(value ?? '')),
|
||||
};
|
||||
try {
|
||||
// eslint-disable-next-line no-new-func
|
||||
const fn = new Function('key', 'keyword', 'searchTerms', 'page', 'java', `return (${expr});`);
|
||||
return jsonPrimitiveToString(fn(key, keyword, searchTerms, page, java));
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function renderTemplateExpressions(raw: string, keyword = '', page = 1) {
|
||||
return raw.replace(/<([^,<>]*),\s*\{\{(.*?)\}\}>/g, (_, prefix, expr) => {
|
||||
const value = safeEvalTemplateExpression(String(expr).trim(), keyword, page);
|
||||
return value ? `${prefix || ''}${value}` : '';
|
||||
}).replace(/\{\{(.*?)\}\}/g, (_, expr) => {
|
||||
const value = String(expr).trim();
|
||||
if (/^(key|keyword|searchTerms)$/.test(value)) return encodeRuleParam(keyword || '');
|
||||
if (/^(page|pageIndex)$/.test(value)) return String(page);
|
||||
const evaluated = safeEvalTemplateExpression(value, keyword, page);
|
||||
return encodeRuleParam(evaluated);
|
||||
});
|
||||
}
|
||||
|
||||
function runJsSnippet(code: string, context: Record<string, any>, timeout = 1000): string {
|
||||
const java = {
|
||||
base64Encode: (value: unknown) => Buffer.from(String(value ?? ''), 'utf8').toString('base64'),
|
||||
base64Decode: (value: unknown) => Buffer.from(String(value ?? ''), 'base64').toString('utf8'),
|
||||
md5Encode: (value: unknown) => crypto.createHash('md5').update(String(value ?? '')).digest('hex'),
|
||||
encodeURI: (value: unknown) => encodeURIComponent(String(value ?? '')),
|
||||
encodeURIComponent: (value: unknown) => encodeURIComponent(String(value ?? '')),
|
||||
put: (key: string, value: any) => { variableStore.set(key, value); return value; },
|
||||
get: (key: string) => variableStore.get(key),
|
||||
htmlFormat: (value: unknown) => he.decode(String(value ?? '')).replace(/<br\s*\/?/gi, '\n').replace(/<[^>]+>/g, ''),
|
||||
};
|
||||
const sandbox: Record<string, any> = { ...context, java, Buffer, JSON, String, Number, Math, Array, Object, console: { log: () => undefined } };
|
||||
try {
|
||||
const script = new vm.Script(`(function(){ ${code.replace(/^@js:/, '')}\n})()`);
|
||||
const result = script.runInNewContext(sandbox, { timeout });
|
||||
return jsonPrimitiveToString(result ?? sandbox.result ?? '');
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function applyPutGetRules(rule: string, value: string) {
|
||||
const putMatch = rule.match(/@put:\s*\{([\s\S]*?)\}/);
|
||||
if (putMatch) {
|
||||
try {
|
||||
const obj = JSON.parse(`{${putMatch[1]}}`);
|
||||
Object.entries(obj).forEach(([key, val]) => variableStore.set(key, val));
|
||||
} catch {
|
||||
const pair = putMatch[1].match(/([A-Za-z0-9_$-]+)\s*:\s*['"]?([^,'"]+)['"]?/);
|
||||
if (pair) variableStore.set(pair[1], value || pair[2]);
|
||||
}
|
||||
}
|
||||
const getMatch = rule.match(/@get:\s*\{\s*([A-Za-z0-9_$-]+)\s*\}/);
|
||||
if (getMatch) return jsonPrimitiveToString(variableStore.get(getMatch[1]));
|
||||
return value;
|
||||
}
|
||||
|
||||
function buildUrlFromTemplate(template: string, source: BookSource, keyword?: string, page = 1, baseOverride?: string) {
|
||||
const base = baseOverride || sourceBase(source);
|
||||
let raw = template || base;
|
||||
raw = raw.replace(/\{\{(?:key|keyword|searchTerms)\}\}/g, encodeRuleParam(keyword || ''));
|
||||
raw = raw.replace(/\{\{(?:page|pageIndex)\}\}/g, String(page));
|
||||
raw = renderTemplateExpressions(raw, keyword || '', page);
|
||||
raw = raw
|
||||
.replace(/\{searchTerms\}/g, encodeRuleParam(keyword || ''))
|
||||
.replace(/\{key\}/g, encodeRuleParam(keyword || ''))
|
||||
.replace(/\{keyword\}/g, encodeRuleParam(keyword || ''))
|
||||
.replace(/\{page\}/g, String(page))
|
||||
.replace(/\{pageIndex\}/g, String(page));
|
||||
if (raw.includes('{{') && keyword) raw = raw.replace(/\{\{.*?\}\}/g, encodeRuleParam(keyword));
|
||||
return normalizeUrl(base, raw);
|
||||
}
|
||||
|
||||
@@ -129,33 +211,54 @@ function readJsonPath(input: any, path?: string): any {
|
||||
if (normalized.startsWith('-@json:')) normalized = normalized.slice(7);
|
||||
if (!normalized || normalized === '$') return input;
|
||||
|
||||
const recursive = normalized.match(/^\$\.\.([A-Za-z0-9_$-]+)\[\*\]$/);
|
||||
const filterMatch = normalized.match(/^(.*)\[\?\(@\.([A-Za-z0-9_$-]+)\s*(==|=|!=)\s*['"]?([^'"\]]+)['"]?\)\](.*)$/);
|
||||
if (filterMatch) {
|
||||
const base = readJsonPath(input, filterMatch[1] || '$');
|
||||
const list = Array.isArray(base) ? base : [];
|
||||
const filtered = list.filter((item) => {
|
||||
const actual = jsonPrimitiveToString(item?.[filterMatch[2]]);
|
||||
return filterMatch[3] === '!=' ? actual !== filterMatch[4] : actual === filterMatch[4];
|
||||
});
|
||||
return filterMatch[5] ? readJsonPath(filtered, `$${filterMatch[5]}`) : filtered;
|
||||
}
|
||||
|
||||
const recursive = normalized.match(/^\$\.\.([A-Za-z0-9_$-]+)(.*)$/);
|
||||
if (recursive) {
|
||||
const key = recursive[1];
|
||||
const rest = recursive[2] || '';
|
||||
const out: any[] = [];
|
||||
const walk = (node: any) => {
|
||||
if (!node || typeof node !== 'object') return;
|
||||
if (Array.isArray(node)) {
|
||||
node.forEach(walk);
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(node[key])) out.push(...node[key]);
|
||||
if (Array.isArray(node)) return node.forEach(walk);
|
||||
if (node[key] !== undefined) out.push(node[key]);
|
||||
Object.values(node).forEach(walk);
|
||||
};
|
||||
walk(input);
|
||||
return out;
|
||||
return rest ? readJsonPath(out.flat(), `$${rest}`) : out.flat();
|
||||
}
|
||||
|
||||
normalized = normalized.replace(/^\$\.?/, '');
|
||||
const tokens = normalized.match(/[^.[\]]+|\[\*\]|\[\d+\]/g) || [];
|
||||
const tokens = normalized.match(/[^.[\]]+|\[\*\]|\[-?\d+\]|\[-?\d*:-?\d*(?::-?\d+)?\]|\[['"][^\]]+['"](?:,\s*['"][^\]]+['"])*\]/g) || [];
|
||||
let current = input;
|
||||
for (const token of tokens) {
|
||||
if (current === undefined || current === null) return undefined;
|
||||
if (token === '[*]') {
|
||||
current = Array.isArray(current) ? current.flat() : [];
|
||||
} else if (/^\[-?\d+\]$/.test(token)) {
|
||||
const idx = Number(token.slice(1, -1));
|
||||
current = Array.isArray(current) ? current[idx < 0 ? current.length + idx : idx] : undefined;
|
||||
} else if (/^\[-?\d*:-?\d*/.test(token)) {
|
||||
if (!Array.isArray(current)) return [];
|
||||
current = current.flat();
|
||||
} else if (/^\[\d+\]$/.test(token)) {
|
||||
current = Array.isArray(current) ? current[Number(token.slice(1, -1))] : undefined;
|
||||
const parts = token.slice(1, -1).split(':').map((item) => item === '' ? undefined : Number(item));
|
||||
const start = parts[0] === undefined ? 0 : parts[0] < 0 ? current.length + parts[0] : parts[0];
|
||||
const end = parts[1] === undefined ? current.length : parts[1] < 0 ? current.length + parts[1] : parts[1];
|
||||
const step = parts[2] || 1;
|
||||
const sliced = current.slice(start, end);
|
||||
current = step === 1 ? sliced : sliced.filter((_, index) => index % Math.abs(step) === 0);
|
||||
} else if (/^\[/.test(token)) {
|
||||
const keys = Array.from(token.matchAll(/['"]([^'"]+)['"]/g)).map((m) => m[1]);
|
||||
if (Array.isArray(current)) current = current.map((item) => keys.map((key) => item?.[key])).flat().filter((item) => item !== undefined);
|
||||
else current = keys.map((key) => current?.[key]).filter((item) => item !== undefined);
|
||||
} else if (Array.isArray(current)) {
|
||||
current = current.map((item) => item?.[token]).filter((item) => item !== undefined);
|
||||
} else {
|
||||
@@ -201,7 +304,15 @@ function readJsonRule(json: any, rule?: string, source?: BookSource, baseUrl?: s
|
||||
.filter(Boolean)
|
||||
.join('');
|
||||
}
|
||||
return '';
|
||||
const resultText = typeof json === 'string' ? json : JSON.stringify(json);
|
||||
if (/java\.base64Decode/.test(trimmed)) return Buffer.from(resultText, 'base64').toString('utf8');
|
||||
if (/java\.base64Encode/.test(trimmed)) return Buffer.from(resultText, 'utf8').toString('base64');
|
||||
if (/java\.md5Encode/.test(trimmed)) return crypto.createHash('md5').update(resultText).digest('hex');
|
||||
const replaceMatch = trimmed.match(/result\.replace\(\s*\/([^/]+)\/[gimuy]*\s*,\s*['"]([^'"]*)['"]\s*\)/);
|
||||
if (replaceMatch) return resultText.replace(new RegExp(replaceMatch[1], 'g'), replaceMatch[2]);
|
||||
const matchMatch = trimmed.match(/result\.match\(\s*\/([^/]+)\/[gimuy]*\s*\)/);
|
||||
if (matchMatch) return resultText.match(new RegExp(matchMatch[1]))?.[1] || resultText.match(new RegExp(matchMatch[1]))?.[0] || '';
|
||||
return runJsSnippet(trimmed, { result: json, baseUrl, src: json });
|
||||
}
|
||||
const value = readJsonPath(json, trimmed);
|
||||
const text = jsonPrimitiveToString(value);
|
||||
@@ -233,7 +344,7 @@ function applyRuleFilters(value: string, filters: string[]) {
|
||||
const replacement = filters[index + 1] ?? '';
|
||||
if (!pattern) continue;
|
||||
try {
|
||||
result = result.replace(new RegExp(pattern, 'g'), replacement);
|
||||
result = result.replace(new RegExp(pattern, 'g'), replacement.replace(/\$(\d+)/g, '$$$$1'));
|
||||
} catch {
|
||||
result = result.split(pattern).join(replacement);
|
||||
}
|
||||
@@ -241,8 +352,37 @@ function applyRuleFilters(value: string, filters: string[]) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function readAllInOneList(raw: string, rule?: string): Array<Record<string, string>> {
|
||||
const trimmed = (rule || '').trim();
|
||||
if (!trimmed.startsWith(':')) return [];
|
||||
const pattern = trimmed.slice(1);
|
||||
try {
|
||||
const regex = new RegExp(pattern, 'gs');
|
||||
const out: Array<Record<string, string>> = [];
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = regex.exec(raw)) !== null) {
|
||||
const groups = (match.groups || {}) as Record<string, string>;
|
||||
const item: Record<string, string> = { _0: match[0] };
|
||||
Array.from(match).forEach((value: string | undefined, index: number) => { item[`_${index}`] = value || ''; });
|
||||
Object.entries(groups).forEach(([key, value]) => { item[key] = String(value || ''); });
|
||||
out.push(item);
|
||||
if (match[0] === '') regex.lastIndex += 1;
|
||||
}
|
||||
return out;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function readRegexItem(item: Record<string, string>, rule?: string, baseUrl?: string) {
|
||||
const key = (rule || '').trim().replace(/^\$?\{?/, '').replace(/\}?$/, '');
|
||||
const value = item[key] || item[`_${key}`] || '';
|
||||
if ((/url|href|src|cover/i.test(key) || /^https?:\/\//i.test(value)) && value && baseUrl) return normalizeUrl(baseUrl, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
function isLegadoAttrToken(value: string) {
|
||||
return /^(href|src|title|alt|text|textNodes|html|content|value|data-[\w-]+)$/i.test(value.trim());
|
||||
return /^(href|src|title|alt|text|textNodes|ownText|all|html|content|value|data-[\w-]+)$/i.test(value.trim());
|
||||
}
|
||||
|
||||
function normalizeLegadoSelector(selector: string) {
|
||||
@@ -272,7 +412,7 @@ function parseStep(step: string): { selector: string; attr: string } {
|
||||
};
|
||||
}
|
||||
|
||||
const attrMatch = trimmed.match(/(?:@|::)(text|textNodes|html|href|src|title|alt|content|value|data-[\w-]+)$/i);
|
||||
const attrMatch = trimmed.match(/(?:@|::)(text|textNodes|ownText|all|html|href|src|title|alt|content|value|data-[\w-]+)$/i);
|
||||
if (attrMatch) {
|
||||
return { selector: normalizeLegadoSelector(trimmed.slice(0, attrMatch.index).trim()), attr: attrMatch[1] };
|
||||
}
|
||||
@@ -285,28 +425,125 @@ function stripFilters(rule: string) {
|
||||
return splitRuleFilters(rule).base;
|
||||
}
|
||||
|
||||
function applyLegadoIndexSelector(current: cheerio.Cheerio<any>, selector: string): { current: cheerio.Cheerio<any>; selector: string } {
|
||||
let normalized = selector.trim();
|
||||
const exclude = normalized.match(/\[!(-?\d+)\]$/);
|
||||
if (exclude) {
|
||||
normalized = normalized.slice(0, exclude.index).trim();
|
||||
current = normalized ? current.find(normalized) : current;
|
||||
const idx = Number(exclude[1]);
|
||||
const real = idx < 0 ? current.length + idx : idx;
|
||||
return { current: current.filter((index) => index !== real), selector: '' };
|
||||
}
|
||||
const range = normalized.match(/\[(-?\d*):(-?\d*)(?::(-?\d+))?\]$/);
|
||||
if (range) {
|
||||
normalized = normalized.slice(0, range.index).trim();
|
||||
current = normalized ? current.find(normalized) : current;
|
||||
const length = current.length;
|
||||
const start = range[1] ? Number(range[1]) : 0;
|
||||
const end = range[2] ? Number(range[2]) : length;
|
||||
const realStart = start < 0 ? length + start : start;
|
||||
const realEnd = end < 0 ? length + end : end;
|
||||
return { current: current.slice(realStart, realEnd), selector: '' };
|
||||
}
|
||||
const indexMatch = normalized.match(/(?:\[(-?\d+)\]|\.(-?\d+))$/);
|
||||
if (indexMatch) {
|
||||
normalized = normalized.slice(0, indexMatch.index).trim();
|
||||
current = normalized ? current.find(normalized) : current;
|
||||
const idx = Number(indexMatch[1] ?? indexMatch[2]);
|
||||
const real = idx < 0 ? current.length + idx : idx;
|
||||
return { current: current.eq(real), selector: '' };
|
||||
}
|
||||
return { current, selector: normalized };
|
||||
}
|
||||
|
||||
function selectElements($: cheerio.CheerioAPI, root: cheerio.Cheerio<any>, rule?: string): cheerio.Cheerio<any> {
|
||||
const normalized = stripFilters(rule || '');
|
||||
if (!normalized) return root;
|
||||
const steps = normalized.split(/&&|@css:/).map((item) => item.trim()).filter(Boolean);
|
||||
const reverse = normalized.trim().startsWith('-') && !normalized.trim().startsWith('-@');
|
||||
const effective = reverse ? normalized.trim().slice(1).trim() : normalized;
|
||||
if (!effective) return root;
|
||||
const steps = effective.split(/&&|@css:/).map((item) => item.trim()).filter(Boolean);
|
||||
let current = root;
|
||||
for (const rawStep of steps) {
|
||||
const { selector, attr } = parseStep(rawStep);
|
||||
if (!selector || attr) break;
|
||||
current = current.find(selector);
|
||||
if (attr) break;
|
||||
if (!selector) continue;
|
||||
if (/^children$/i.test(selector)) {
|
||||
current = current.children();
|
||||
continue;
|
||||
}
|
||||
const indexed = applyLegadoIndexSelector(current, selector);
|
||||
current = indexed.selector ? current.find(indexed.selector) : indexed.current;
|
||||
}
|
||||
if (reverse) current = $(current.toArray().reverse());
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
function selectXPath($: cheerio.CheerioAPI, root: cheerio.Cheerio<any>, rule: string): { nodes: cheerio.Cheerio<any>; attr: string; value?: string } {
|
||||
const expr = rule.trim().replace(/^@XPath:/i, '');
|
||||
try {
|
||||
const html = $.html(root);
|
||||
const doc = new DOMParser({ errorHandler: () => undefined }).parseFromString(html, 'text/html');
|
||||
const selected = xpath.select(expr, doc as any) as any;
|
||||
const list = Array.isArray(selected) ? selected : [selected];
|
||||
const values = list.map((node) => {
|
||||
if (node === undefined || node === null) return '';
|
||||
if (typeof node === 'string' || typeof node === 'number' || typeof node === 'boolean') return String(node);
|
||||
if (node.nodeType === 2) return node.nodeValue || '';
|
||||
if (node.nodeType === 3 || node.nodeType === 4) return node.nodeValue || '';
|
||||
return node.textContent || '';
|
||||
}).filter(Boolean);
|
||||
if (values.length > 0) return { nodes: root, attr: '', value: values.join('\n') };
|
||||
} catch {
|
||||
// fallback below
|
||||
}
|
||||
|
||||
let fallbackExpr = expr;
|
||||
let attr = '';
|
||||
const attrMatch = fallbackExpr.match(/\/@([A-Za-z0-9_-]+)$/);
|
||||
if (attrMatch) {
|
||||
attr = attrMatch[1];
|
||||
fallbackExpr = fallbackExpr.slice(0, attrMatch.index);
|
||||
} else if (/\/text\(\)$/.test(fallbackExpr)) {
|
||||
attr = 'text';
|
||||
fallbackExpr = fallbackExpr.replace(/\/text\(\)$/, '');
|
||||
}
|
||||
const parts = fallbackExpr.split('/').filter(Boolean).map((part) => {
|
||||
const match = part.match(/^([A-Za-z0-9_*.-]+)(?:\[@([A-Za-z0-9_-]+)=['"]([^'"]+)['"]\])?(?:\[(\d+)\])?$/);
|
||||
if (!match) return '';
|
||||
const tag = match[1] === '*' ? '*' : match[1];
|
||||
const filter = match[2] ? `[${match[2]}="${match[3]}"]` : '';
|
||||
const index = match[4] ? `:nth-of-type(${match[4]})` : '';
|
||||
return `${tag}${filter}${index}`;
|
||||
}).filter(Boolean);
|
||||
return { nodes: parts.length ? root.find(parts.join(' ')) : root, attr };
|
||||
}
|
||||
|
||||
function readValue($: cheerio.CheerioAPI, root: cheerio.Cheerio<any>, rule?: string, baseUrl?: string): string {
|
||||
for (const alternative of splitAlternatives(rule)) {
|
||||
const normalized = stripFilters(alternative);
|
||||
if (/^@?XPath:/i.test(normalized) || normalized.startsWith('//')) {
|
||||
const { nodes, attr, value: xpathValue } = selectXPath($, root, normalized);
|
||||
const node = nodes.first();
|
||||
let value = xpathValue !== undefined ? xpathValue : attr === 'text' || !attr ? node.text() : node.attr(attr) || '';
|
||||
value = he.decode(value || '').replace(/\u00a0/g, ' ').trim();
|
||||
value = applyRuleFilters(value, splitRuleFilters(alternative).filters);
|
||||
if (value) return value;
|
||||
continue;
|
||||
}
|
||||
const steps = normalized.split(/&&|@css:/).map((item) => item.trim()).filter(Boolean);
|
||||
let current = root;
|
||||
let attr = '';
|
||||
for (const rawStep of steps) {
|
||||
const parsed = parseStep(rawStep);
|
||||
if (parsed.selector) current = current.find(parsed.selector);
|
||||
if (parsed.selector) {
|
||||
if (/^children$/i.test(parsed.selector)) current = current.children();
|
||||
else {
|
||||
const indexed = applyLegadoIndexSelector(current, parsed.selector);
|
||||
current = indexed.selector ? current.find(indexed.selector) : indexed.current;
|
||||
}
|
||||
}
|
||||
if (parsed.attr) attr = parsed.attr;
|
||||
}
|
||||
if (current.length === 0 && steps.length === 1) {
|
||||
@@ -317,11 +554,14 @@ function readValue($: cheerio.CheerioAPI, root: cheerio.Cheerio<any>, rule?: str
|
||||
let value = '';
|
||||
const normalizedAttr = attr.toLowerCase();
|
||||
if (!attr || normalizedAttr === 'text' || normalizedAttr === 'textnodes') value = node.text();
|
||||
else if (normalizedAttr === 'owntext') value = node.clone().children().remove().end().text();
|
||||
else if (normalizedAttr === 'all') value = current.toArray().map((el) => $(el).text()).join('\n');
|
||||
else if (normalizedAttr === 'html') value = node.html() || '';
|
||||
else value = node.attr(attr) || '';
|
||||
value = he.decode(value || '').replace(/\u00a0/g, ' ').trim();
|
||||
if ((normalizedAttr === 'href' || normalizedAttr === 'src') && value && baseUrl) value = normalizeUrl(baseUrl, value);
|
||||
value = applyRuleFilters(value, splitRuleFilters(alternative).filters);
|
||||
value = applyPutGetRules(alternative, value);
|
||||
if (value) return value;
|
||||
}
|
||||
return '';
|
||||
@@ -335,7 +575,13 @@ function readValues($: cheerio.CheerioAPI, root: cheerio.Cheerio<any>, rule?: st
|
||||
let attr = '';
|
||||
for (const rawStep of steps) {
|
||||
const parsed = parseStep(rawStep);
|
||||
if (parsed.selector) current = current.find(parsed.selector);
|
||||
if (parsed.selector) {
|
||||
if (/^children$/i.test(parsed.selector)) current = current.children();
|
||||
else {
|
||||
const indexed = applyLegadoIndexSelector(current, parsed.selector);
|
||||
current = indexed.selector ? current.find(indexed.selector) : indexed.current;
|
||||
}
|
||||
}
|
||||
if (parsed.attr) attr = parsed.attr;
|
||||
}
|
||||
if (current.length === 0 && steps.length === 1) {
|
||||
@@ -347,6 +593,8 @@ function readValues($: cheerio.CheerioAPI, root: cheerio.Cheerio<any>, rule?: st
|
||||
const node = $(element);
|
||||
let value = '';
|
||||
if (!attr || normalizedAttr === 'text' || normalizedAttr === 'textnodes') value = node.text();
|
||||
else if (normalizedAttr === 'owntext') value = node.clone().children().remove().end().text();
|
||||
else if (normalizedAttr === 'all') value = node.text();
|
||||
else if (normalizedAttr === 'html') value = node.html() || '';
|
||||
else value = node.attr(attr) || '';
|
||||
value = he.decode(value || '').replace(/\u00a0/g, ' ').trim();
|
||||
@@ -367,7 +615,7 @@ function applyContentJsRule(value: string, jsRule: string): string {
|
||||
.map((src) => `<img src="${src}" style="max-width:100%; display:block;" referrerpolicy="no-referrer">`)
|
||||
.join('');
|
||||
}
|
||||
return value;
|
||||
return runJsSnippet(jsRule, { result: value, src: value }) || value;
|
||||
}
|
||||
|
||||
function contentFromRule(raw: string, rule?: string, baseUrl?: string): string {
|
||||
@@ -406,9 +654,12 @@ function proxyChapterImages(content: string, source: BookSource) {
|
||||
if (!/<img\b/i.test(content)) return content;
|
||||
const rule = source.legado;
|
||||
if (rule?.bookSourceType !== 2 && source.legado?.bookSourceType !== 2) return content;
|
||||
return content.replace(/<img\b([^>]*?)\bsrc=(['"])(.*?)\2([^>]*)>/gi, (match, before, quote, src, after) => {
|
||||
if (!src || src.startsWith('/api/books/image')) return match;
|
||||
const proxied = `/api/books/image?sourceId=${encodeURIComponent(source.id)}&url=${encodeURIComponent(src)}`;
|
||||
return content.replace(/<img\b([^>]*?)\bsrc=(['"])(.*?)\2([^>]*)>/gi, (match, before, quote, rawSrc, after) => {
|
||||
if (!rawSrc || rawSrc.startsWith('/api/books/image')) return match;
|
||||
const optionIndex = rawSrc.indexOf(',{');
|
||||
const src = optionIndex > 0 ? rawSrc.slice(0, optionIndex) : rawSrc;
|
||||
const options = optionIndex > 0 ? rawSrc.slice(optionIndex + 1) : '';
|
||||
const proxied = `/api/books/image?sourceId=${encodeURIComponent(source.id)}&url=${encodeURIComponent(src)}${options ? `&options=${encodeURIComponent(options)}` : ''}`;
|
||||
return `<img${before}src=${quote}${proxied}${quote}${after}>`;
|
||||
});
|
||||
}
|
||||
@@ -488,27 +739,83 @@ function wait(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function splitUrlOptions(input: string): RequestOptions {
|
||||
const raw = input.trim();
|
||||
const comma = raw.indexOf(',');
|
||||
if (comma <= 0) return { url: raw };
|
||||
const candidate = raw.slice(comma + 1).trim();
|
||||
if (!candidate.startsWith('{')) return { url: raw };
|
||||
try {
|
||||
const options = JSON.parse(candidate);
|
||||
return {
|
||||
url: raw.slice(0, comma).trim(),
|
||||
method: options.method,
|
||||
body: options.body,
|
||||
headers: options.headers && typeof options.headers === 'object' ? options.headers : undefined,
|
||||
charset: options.charset,
|
||||
retry: Number.isFinite(Number(options.retry)) ? Number(options.retry) : undefined,
|
||||
};
|
||||
} catch {
|
||||
return { url: raw };
|
||||
}
|
||||
}
|
||||
|
||||
function getCookieHeader(sourceId: string) {
|
||||
return cookieJar.get(sourceId) || '';
|
||||
}
|
||||
|
||||
function mergeSetCookie(sourceId: string, setCookie: string | null) {
|
||||
if (!setCookie) return;
|
||||
const current = new Map<string, string>();
|
||||
(cookieJar.get(sourceId) || '').split(/;\s*/).filter(Boolean).forEach((item) => {
|
||||
const idx = item.indexOf('=');
|
||||
if (idx > 0) current.set(item.slice(0, idx), item.slice(idx + 1));
|
||||
});
|
||||
setCookie.split(/,(?=\s*[^;,]+=)/).forEach((cookie) => {
|
||||
const pair = cookie.split(';')[0]?.trim();
|
||||
const idx = pair?.indexOf('=') ?? -1;
|
||||
if (idx > 0) current.set(pair.slice(0, idx), pair.slice(idx + 1));
|
||||
});
|
||||
cookieJar.set(sourceId, Array.from(current.entries()).map(([key, value]) => `${key}=${value}`).join('; '));
|
||||
}
|
||||
|
||||
async function fetchText(source: BookSource, url: string): Promise<string> {
|
||||
if (!url?.trim()) throw new Error('书源请求地址为空');
|
||||
const safe = await validateProxyUrlServerSide(url);
|
||||
if (!safe) throw new Error(`书源地址未通过安全校验: ${url}`);
|
||||
const cacheKey = `${LEGADO_CACHE_VERSION}|text|${source.id}|${url}`;
|
||||
const request = splitUrlOptions(url);
|
||||
const safe = await validateProxyUrlServerSide(request.url);
|
||||
if (!safe) throw new Error(`书源地址未通过安全校验: ${request.url}`);
|
||||
const cacheKey = `${LEGADO_CACHE_VERSION}|text|${source.id}|${request.method || 'GET'}|${request.url}|${request.body || ''}`;
|
||||
const cached = textCache.get(cacheKey);
|
||||
const { cacheTTL } = await resolveLegadoConfig();
|
||||
if (cached && cached.expiresAt > Date.now()) return cached.data;
|
||||
|
||||
let lastError: unknown;
|
||||
for (let attempt = 0; attempt < 3; attempt += 1) {
|
||||
const maxAttempts = Math.max(1, (request.retry ?? 2) + 1);
|
||||
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
||||
try {
|
||||
const response = await fetch(url, { headers: buildHeaders(source), signal: controller.signal, cache: 'no-store' });
|
||||
const headers: Record<string, string> = {
|
||||
...(buildHeaders(source) as Record<string, string>),
|
||||
...(request.headers || {}),
|
||||
};
|
||||
const cookie = getCookieHeader(source.id);
|
||||
if (source.legado?.enabledCookieJar && cookie) headers.Cookie = cookie;
|
||||
const method = (request.method || (request.body ? 'POST' : 'GET')).toUpperCase();
|
||||
const response = await fetch(request.url, {
|
||||
method,
|
||||
headers,
|
||||
body: method === 'GET' || method === 'HEAD' ? undefined : request.body,
|
||||
signal: controller.signal,
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (source.legado?.enabledCookieJar) mergeSetCookie(source.id, response.headers.get('set-cookie'));
|
||||
if (!response.ok) throw new Error(`请求失败: ${response.status}`);
|
||||
const contentLength = Number(response.headers.get('content-length') || '0');
|
||||
if (contentLength > MAX_TEXT_BYTES) throw new Error('响应内容过大');
|
||||
const buffer = await response.arrayBuffer();
|
||||
const contentType = response.headers.get('content-type') || '';
|
||||
const charset = contentType.match(/charset=([^;]+)/i)?.[1]?.trim().replace(/^['"]|['"]$/g, '');
|
||||
const charset = request.charset || contentType.match(/charset=([^;]+)/i)?.[1]?.trim().replace(/^['"]|['"]$/g, '');
|
||||
const decoderName = /gbk|gb2312|gb18030/i.test(charset || '') ? 'gb18030' : (charset || 'utf-8');
|
||||
let text: string;
|
||||
try {
|
||||
@@ -521,7 +828,7 @@ async function fetchText(source: BookSource, url: string): Promise<string> {
|
||||
return text;
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
if (attempt < 2) await wait(300 * (attempt + 1));
|
||||
if (attempt < maxAttempts - 1) await wait(300 * (attempt + 1));
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
@@ -573,8 +880,8 @@ function parseExploreUrl(exploreUrl?: string): Array<{ title: string; template:
|
||||
const json = parseJsonMaybe(raw);
|
||||
if (Array.isArray(json)) {
|
||||
return json
|
||||
.map((item) => ({ title: jsonPrimitiveToString(item?.title), template: jsonPrimitiveToString(item?.url) }))
|
||||
.filter((item) => !!item.title && !!item.template);
|
||||
.map((item) => ({ title: jsonPrimitiveToString(item?.title), template: jsonPrimitiveToString(item?.url) || `__group__:${jsonPrimitiveToString(item?.title)}` }))
|
||||
.filter((item) => !!item.title);
|
||||
}
|
||||
return raw
|
||||
.split('&&')
|
||||
@@ -642,6 +949,9 @@ export class LegadoClient {
|
||||
for (let page = 1; page <= DEFAULT_LEGADO_SEARCH_PAGES; page += 1) {
|
||||
const targetUrl = buildUrlFromTemplate(rule.searchUrl, source, q, page);
|
||||
const html = await fetchText(source, targetUrl);
|
||||
if (page === 1 && rule.ruleSearch.checkKeyWord && !html.includes(rule.ruleSearch.checkKeyWord) && !html.includes(q)) {
|
||||
throw new Error('搜索结果校验失败');
|
||||
}
|
||||
let pageCount = 0;
|
||||
const json = parseJsonMaybe(html);
|
||||
if (json && ruleIsJson(rule.ruleSearch.bookList)) {
|
||||
@@ -666,6 +976,27 @@ export class LegadoClient {
|
||||
tags: readJsonRule(item, rule.ruleSearch?.kind, source, targetUrl).split(/[,,\s]+/).filter(Boolean),
|
||||
}));
|
||||
});
|
||||
} else if (rule.ruleSearch.bookList.trim().startsWith(':')) {
|
||||
const items = readAllInOneList(html, rule.ruleSearch.bookList);
|
||||
pageCount = items.length;
|
||||
items.forEach((item) => {
|
||||
const detailHref = readRegexItem(item, rule.ruleSearch?.bookUrl, targetUrl);
|
||||
const title = readRegexItem(item, rule.ruleSearch?.name, targetUrl);
|
||||
if (!title && !detailHref) return;
|
||||
const cover = readRegexItem(item, rule.ruleSearch?.coverUrl, targetUrl);
|
||||
const dedupeKey = detailHref || `${title}|${cover}`;
|
||||
if (dedupeKey && seen.has(dedupeKey)) return;
|
||||
if (dedupeKey) seen.add(dedupeKey);
|
||||
results.push(makeItem(source, {
|
||||
id: detailHref || undefined,
|
||||
title,
|
||||
author: readRegexItem(item, rule.ruleSearch?.author, targetUrl),
|
||||
summary: readRegexItem(item, rule.ruleSearch?.intro, targetUrl),
|
||||
cover: cover || undefined,
|
||||
detailHref,
|
||||
tags: readRegexItem(item, rule.ruleSearch?.kind, targetUrl).split(/[,,\s]+/).filter(Boolean),
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
const $ = cheerio.load(html);
|
||||
const items = selectElements($, $.root(), rule.ruleSearch.bookList);
|
||||
@@ -722,8 +1053,8 @@ export class LegadoClient {
|
||||
const target = decodeExploreTarget(href) || null;
|
||||
const navigation = categories.map((item) => ({
|
||||
title: item.title,
|
||||
href: encodeExploreTarget({ title: item.title, template: item.template, page: 1 }),
|
||||
rel: 'legado:explore',
|
||||
href: item.template.startsWith('__group__:') ? '' : encodeExploreTarget({ title: item.title, template: item.template, page: 1 }),
|
||||
rel: item.template.startsWith('__group__:') ? 'legado:group' : 'legado:explore',
|
||||
type: 'application/x-legado-explore',
|
||||
}));
|
||||
|
||||
@@ -731,6 +1062,9 @@ export class LegadoClient {
|
||||
return { sourceId: source.id, sourceName: source.name, title: source.name, subtitle: '请选择分类', href: href || source.url, entries: [], navigation };
|
||||
}
|
||||
|
||||
if (target.template.startsWith('__group__:')) {
|
||||
return { sourceId: source.id, sourceName: source.name, title: target.title, href: href || '', entries: [], navigation };
|
||||
}
|
||||
const targetUrl = buildExploreTargetUrl(source, target);
|
||||
const html = await fetchText(source, targetUrl);
|
||||
const exploreRule = rule.ruleExplore || rule.ruleSearch;
|
||||
@@ -823,12 +1157,15 @@ export class LegadoClient {
|
||||
if (detailHref && rule.ruleBookInfo) {
|
||||
const targetUrl = rule.bookInfoUrl ? buildUrlFromTemplate(rule.bookInfoUrl, source, undefined, 1, detailHref).replace(/\{bookUrl\}/g, encodeURIComponent(detailHref)) : detailHref;
|
||||
const html = await fetchText(source, targetUrl);
|
||||
const json = parseJsonMaybe(html);
|
||||
const $ = json ? null : cheerio.load(html);
|
||||
const initData = rule.bookInfoInit?.trim().startsWith(':') ? readAllInOneList(html, rule.bookInfoInit)[0] : null;
|
||||
const json = initData || parseJsonMaybe(html);
|
||||
const $ = json && initData ? null : json ? null : cheerio.load(html);
|
||||
const root = $?.root();
|
||||
const read = (itemRule?: string) => json
|
||||
? readJsonRule(json, itemRule, source, targetUrl)
|
||||
: readValue($ as cheerio.CheerioAPI, root as cheerio.Cheerio<any>, itemRule, targetUrl);
|
||||
const read = (itemRule?: string) => initData
|
||||
? readRegexItem(initData, itemRule, targetUrl)
|
||||
: json
|
||||
? readJsonRule(json, itemRule, source, targetUrl)
|
||||
: readValue($ as cheerio.CheerioAPI, root as cheerio.Cheerio<any>, itemRule, targetUrl);
|
||||
const tocUrl = read(rule.ruleBookInfo.tocUrl) || (rule.tocUrl ? buildUrlFromTemplate(rule.tocUrl, source, undefined, 1, detailHref).replace(/\{bookUrl\}/g, encodeURIComponent(detailHref)) : targetUrl);
|
||||
const cover = read(rule.ruleBookInfo.coverUrl) || fallback?.cover;
|
||||
const title = read(rule.ruleBookInfo.name) || fallback?.title || '未命名电子书';
|
||||
@@ -908,6 +1245,27 @@ export class LegadoClient {
|
||||
chapters.push({ id: stableId(`${source.id}|${normalizedHref}`), title, href: normalizedHref, order: index });
|
||||
});
|
||||
}
|
||||
if (rule.ruleToc.nextTocUrl) {
|
||||
let nextTocUrl = contentFromRule(html, rule.ruleToc.nextTocUrl, targetUrl);
|
||||
const visited = new Set([targetUrl]);
|
||||
for (let page = 0; page < 8 && nextTocUrl; page += 1) {
|
||||
const normalizedNext = normalizeUrl(targetUrl, nextTocUrl);
|
||||
if (!normalizedNext || visited.has(normalizedNext)) break;
|
||||
visited.add(normalizedNext);
|
||||
const nextHtml = await fetchText(source, normalizedNext);
|
||||
const $next = cheerio.load(nextHtml);
|
||||
const nextItems = selectElements($next, $next.root(), rule.ruleToc.chapterList);
|
||||
nextItems.each((index, element) => {
|
||||
const root = $next(element);
|
||||
const title = readValue($next, root, rule.ruleToc?.chapterName, normalizedNext) || `第 ${chapters.length + index + 1} 章`;
|
||||
const href = readValue($next, root, rule.ruleToc?.chapterUrl, normalizedNext) || root.attr('href') || '';
|
||||
if (!href) return;
|
||||
const normalizedHref = normalizeUrl(normalizedNext, href);
|
||||
chapters.push({ id: stableId(`${source.id}|${normalizedHref}`), title, href: normalizedHref, order: chapters.length });
|
||||
});
|
||||
nextTocUrl = contentFromRule(nextHtml, rule.ruleToc.nextTocUrl, normalizedNext);
|
||||
}
|
||||
}
|
||||
tocCache.set(cacheKey, { data: chapters, expiresAt: Date.now() + cacheTTL });
|
||||
return chapters;
|
||||
}
|
||||
@@ -941,7 +1299,7 @@ export class LegadoClient {
|
||||
id: stableId(`${source.id}|${targetUrl}`),
|
||||
title: index >= 0 ? chapters[index].title : '',
|
||||
href: targetUrl,
|
||||
content: proxyChapterImages(cleanContent(rawContent), source),
|
||||
content: proxyChapterImages(cleanContent(rule.ruleContent.sourceRegex ? applyRuleFilters(rawContent, [rule.ruleContent.sourceRegex, '']) : rawContent), source),
|
||||
previousHref: index > 0 ? chapters[index - 1].href : undefined,
|
||||
nextHref: index >= 0 && index + 1 < chapters.length ? chapters[index + 1].href : undefined,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user