commit 638b2d9fc4b32bc935f2f1565074831f92d7c750 Author: itsskaiya Date: Wed Nov 26 15:10:53 2025 -0500 First dev release of v2.0.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..65c24ca --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2894 @@ +{ + "name": "waifu-board-(server)", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "waifu-board-(server)", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@fastify/static": "^8.3.0", + "fastify": "^5.6.2", + "sqlite3": "^5.1.7" + } + }, + "node_modules/@fastify/accept-negotiator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", + "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/ajv-compiler": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", + "integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0" + } + }, + "node_modules/@fastify/error": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", + "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", + "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^6.0.0" + } + }, + "node_modules/@fastify/forwarded": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz", + "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", + "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", + "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" + } + }, + "node_modules/@fastify/send": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.1.0.tgz", + "integrity": "sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@lukeed/ms": "^2.0.2", + "escape-html": "~1.0.3", + "fast-decode-uri-component": "^1.0.1", + "http-errors": "^2.0.0", + "mime": "^3" + } + }, + "node_modules/@fastify/static": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.3.0.tgz", + "integrity": "sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/accept-negotiator": "^2.0.0", + "@fastify/send": "^4.0.0", + "content-disposition": "^0.5.4", + "fastify-plugin": "^5.0.0", + "fastq": "^1.17.1", + "glob": "^11.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", + "optional": true + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/avvio": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz", + "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^4.0.0", + "fastq": "^1.17.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT", + "optional": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT", + "optional": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.0.tgz", + "integrity": "sha512-vXiThu1/rlos7EGu8TuNZQEg2e9TvhH9dmS4T4ZVzB7Ao1agEZ6EG3sn5n+hZRYUgduISd1HpngFzAZiDGm5vQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", + "optional": true + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stringify": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.1.1.tgz", + "integrity": "sha512-DbgptncYEXZqDUOEl4krff4mUiVrTZZVI7BBrQR/T3BqMj/eM1flTC1Uk2uUoLcWCxjT95xKulV/Lc6hhOZsBQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^3.0.0", + "json-schema-ref-resolver": "^3.0.0", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastify": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.6.2.tgz", + "integrity": "sha512-dPugdGnsvYkBlENLhCgX8yhyGCsCPrpA8lFWbTNU428l+YOnLgYHR69hzV8HWPC79n536EqzqQtvhtdaCE0dKg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^4.0.0", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^9.0.0", + "fast-json-stringify": "^6.0.0", + "find-my-way": "^9.0.0", + "light-my-request": "^6.0.0", + "pino": "^10.1.0", + "process-warning": "^5.0.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^4.0.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/fastify-plugin": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz", + "integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/find-my-way": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.3.0.tgz", + "integrity": "sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC", + "optional": true + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "optional": true + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", + "optional": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", + "optional": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/json-schema-ref-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz", + "integrity": "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/light-my-request": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz", + "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "dependencies": { + "cookie": "^1.0.1", + "process-warning": "^4.0.0", + "set-cookie-parser": "^2.6.0" + } + }, + "node_modules/light-my-request/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.85.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz", + "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pino": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz", + "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex2": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz", + "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ret": "~0.5.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "optional": true + }, + "node_modules/secure-json-parse": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5581fdf --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "waifu-board-(server)", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "@fastify/static": "^8.3.0", + "fastify": "^5.6.2", + "sqlite3": "^5.1.7" + } +} diff --git a/public/anime.css b/public/anime.css new file mode 100644 index 0000000..0ca2c0e --- /dev/null +++ b/public/anime.css @@ -0,0 +1,270 @@ +:root { + --bg-base: #09090b; + --bg-surface: #121215; + --bg-surface-hover: #1e1e22; + --accent: #8b5cf6; + --accent-glow: rgba(139, 92, 246, 0.4); + --text-primary: #ffffff; + --text-secondary: #a1a1aa; + --radius-md: 12px; + --radius-lg: 24px; + --radius-full: 9999px; +} + +body { + margin: 0; + background-color: var(--bg-base); + color: var(--text-primary); + font-family: 'Inter', system-ui, sans-serif; + overflow-x: hidden; +} + +/* --- BACK BUTTON --- */ +.back-btn { + position: fixed; + top: 2rem; + left: 2rem; + z-index: 100; + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.8rem 1.5rem; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: var(--radius-full); + color: white; + text-decoration: none; + font-weight: 600; + transition: all 0.2s ease; + cursor: pointer; +} + +.back-btn:hover { + background: rgba(255, 255, 255, 0.1); + transform: translateX(-5px); +} + +/* --- HERO VIDEO SECTION --- */ +.hero-wrapper { + position: relative; + width: 100%; + height: 85vh; /* Covers most of the screen */ + overflow: hidden; +} + +.video-background { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(1.35); /* Zoom to remove black bars */ + width: 100%; + height: 100%; + pointer-events: none; + z-index: 0; + opacity: 0.6; +} + +.hero-overlay { + position: absolute; + inset: 0; + background: radial-gradient(circle at center, transparent 0%, var(--bg-base) 120%), + linear-gradient(to top, var(--bg-base) 10%, rgba(9,9,11,0.8) 25%, transparent 60%); + z-index: 1; +} + +/* --- CONTENT LAYOUT --- */ +.content-container { + position: relative; + z-index: 10; + max-width: 1600px; + margin: -350px auto 0 auto; /* Pull content up over the video */ + padding: 0 3rem 4rem 3rem; + display: grid; + grid-template-columns: 280px 1fr; + gap: 3rem; + animation: slideUp 0.8s cubic-bezier(0.16, 1, 0.3, 1); +} + +/* LEFT SIDEBAR */ +.sidebar { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.poster-card { + width: 100%; + aspect-ratio: 2/3; + border-radius: var(--radius-lg); + overflow: hidden; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.8); + border: 1px solid rgba(255,255,255,0.1); +} + +.poster-card img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.info-grid { + background: var(--bg-surface); + border: 1px solid rgba(255,255,255,0.05); + border-radius: var(--radius-md); + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.info-item h4 { margin: 0 0 0.25rem 0; font-size: 0.85rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.5px; } +.info-item span { font-weight: 600; font-size: 1rem; color: var(--text-primary); } + +.character-list { + display: flex; + flex-direction: column; + gap: 0.75rem; +} +.character-item { display: flex; align-items: center; gap: 0.75rem; font-size: 0.95rem; } +.char-dot { width: 6px; height: 6px; background: var(--accent); border-radius: 50%; } + +/* RIGHT MAIN CONTENT */ +.main-content { + display: flex; + flex-direction: column; + justify-content: flex-end; /* Align header to bottom of hero area */ +} + +.anime-header { + margin-bottom: 2rem; +} + +.anime-title { + font-size: 4rem; + font-weight: 900; + line-height: 1; + margin: 0 0 1.5rem 0; + text-shadow: 0 4px 30px rgba(0,0,0,0.8); +} + +.meta-row { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1.5rem; + flex-wrap: wrap; +} + +.pill { + padding: 0.5rem 1.25rem; + background: rgba(255,255,255,0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255,255,255,0.1); + border-radius: var(--radius-full); + font-weight: 600; + font-size: 0.95rem; +} +.pill.score { background: rgba(34, 197, 94, 0.2); color: #4ade80; border-color: rgba(34, 197, 94, 0.2); } + +.action-row { + display: flex; + gap: 1rem; + margin-top: 1rem; +} + +.btn-watch { + padding: 1rem 3rem; + background: var(--text-primary); + color: var(--bg-base); + border-radius: var(--radius-full); + font-weight: 800; + font-size: 1.1rem; + border: none; + cursor: pointer; + display: flex; + align-items: center; + gap: 0.75rem; + transition: transform 0.2s, box-shadow 0.2s; +} + +.btn-watch:hover { + transform: scale(1.05); + box-shadow: 0 0 30px rgba(255, 255, 255, 0.25); +} + +.btn-secondary { + padding: 1rem 2rem; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + color: white; + border-radius: var(--radius-full); + font-weight: 700; + font-size: 1rem; + border: 1px solid rgba(255,255,255,0.2); + cursor: pointer; + transition: background 0.2s; +} +.btn-secondary:hover { background: rgba(255, 255, 255, 0.2); } + +.description-box { + margin-top: 3rem; + font-size: 1.15rem; + line-height: 1.8; + color: #e4e4e7; + max-width: 900px; + background: rgba(255,255,255,0.03); + padding: 2rem; + border-radius: var(--radius-md); + border: 1px solid rgba(255,255,255,0.05); +} + +/* EPISODES */ +.episodes-section { + margin-top: 4rem; +} +.section-title { font-size: 1.8rem; font-weight: 800; margin-bottom: 1.5rem; display: flex; align-items: center; gap: 0.8rem; } +.section-title::before { content: ''; width: 4px; height: 28px; background: var(--accent); border-radius: 2px; } + +.episodes-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 1rem; +} + +.episode-btn { + background: var(--bg-surface); + border: 1px solid rgba(255,255,255,0.1); + padding: 1.25rem 1rem; + border-radius: var(--radius-md); + cursor: pointer; + transition: 0.2s; + text-align: center; + font-weight: 600; + color: var(--text-secondary); +} + +.episode-btn:hover { + background: var(--bg-surface-hover); + color: white; + transform: translateY(-3px); + border-color: var(--accent); +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(60px); } + to { opacity: 1; transform: translateY(0); } +} + +@media (max-width: 1024px) { + .content-container { + grid-template-columns: 1fr; + margin-top: -100px; + padding: 0 1.5rem 4rem 1.5rem; + } + .poster-card { width: 220px; margin: 0 auto; box-shadow: 0 10px 30px rgba(0,0,0,0.5); } + .main-content { text-align: center; align-items: center; } + .anime-title { font-size: 2.5rem; } + .meta-row { justify-content: center; } + .sidebar { display: none; /* Hide sidebar on mobile for simplicity, or move to bottom */ } +} \ No newline at end of file diff --git a/public/book.css b/public/book.css new file mode 100644 index 0000000..eccefcb --- /dev/null +++ b/public/book.css @@ -0,0 +1,234 @@ +:root { + --bg-base: #09090b; + --bg-surface: #121215; + --bg-surface-hover: #1e1e22; + --accent: #8b5cf6; + --text-primary: #ffffff; + --text-secondary: #a1a1aa; + --radius-md: 12px; + --radius-lg: 24px; + --radius-full: 9999px; + --glass-border: 1px solid rgba(255, 255, 255, 0.1); + --glass-bg: rgba(20, 20, 23, 0.7); + --nav-height: 80px; +} + +/* Global Reset */ +* { box-sizing: border-box; outline: none; } + +body { + margin: 0; + background-color: var(--bg-base); + color: var(--text-primary); + font-family: 'Inter', system-ui, sans-serif; + overflow-x: hidden; +} + +/* Back Button */ +.back-btn { + position: fixed; + top: 2rem; left: 2rem; z-index: 100; + display: flex; align-items: center; gap: 0.5rem; + padding: 0.8rem 1.5rem; + background: var(--glass-bg); backdrop-filter: blur(12px); + border: var(--glass-border); border-radius: var(--radius-full); + color: white; text-decoration: none; font-weight: 600; + transition: all 0.2s ease; +} +.back-btn:hover { background: rgba(255, 255, 255, 0.15); transform: translateX(-5px); } + +/* Hero */ +.hero-wrapper { + position: relative; width: 100%; height: 60vh; overflow: hidden; +} +.hero-background { position: absolute; inset: 0; z-index: 0; } +.hero-background img { width: 100%; height: 100%; object-fit: cover; opacity: 0.4; filter: blur(8px); transform: scale(1.1); } +.hero-overlay { + position: absolute; inset: 0; z-index: 1; + background: linear-gradient(to bottom, transparent 0%, var(--bg-base) 100%); +} + +/* Content Layout - Pulled Up Higher */ +.content-container { + position: relative; z-index: 10; + max-width: 1600px; margin: -350px auto 0 auto; /* Pulled up significantly */ + padding: 0 3rem 4rem 3rem; + display: grid; + grid-template-columns: 260px 1fr; + gap: 3rem; + align-items: flex-start; /* Important for sticky sidebar */ + animation: slideUp 0.8s ease; +} + +/* Hero Content Overlay (Hidden) */ +.hero-content { display: none; } + +/* Left Sidebar - Sticky Poster */ +.sidebar { + display: flex; + flex-direction: column; + gap: 1.5rem; + position: sticky; + top: calc(var(--nav-height) + 2rem); /* Sticks to top when scrolling */ + align-self: flex-start; + z-index: 20; +} + +.poster-card { + width: 100%; aspect-ratio: 2/3; border-radius: var(--radius-lg); + overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0,0,0,0.8); + border: 1px solid rgba(255,255,255,0.1); + background: #1a1a1a; +} +.poster-card img { width: 100%; height: 100%; object-fit: cover; } + +.info-grid { + background: var(--bg-surface); border: var(--glass-border); + border-radius: var(--radius-md); padding: 1.25rem; + display: flex; flex-direction: column; gap: 1rem; +} +.info-item h4 { margin: 0 0 0.25rem 0; font-size: 0.8rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.5px; } +.info-item span { font-weight: 600; font-size: 0.95rem; } + +/* Main Content */ +.main-content { + display: flex; flex-direction: column; + padding-top: 4rem; /* Adjusted spacing */ + justify-content: flex-start; +} + +/* Header Section */ +.book-header { margin-bottom: 1.5rem; } +.book-title { font-size: 3.5rem; font-weight: 900; line-height: 1.1; margin: 0 0 1rem 0; text-shadow: 0 4px 30px rgba(0,0,0,0.8); } + +.meta-row { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem; flex-wrap: wrap; } +.pill { padding: 0.4rem 1rem; background: rgba(255,255,255,0.1); border-radius: 99px; font-size: 0.9rem; font-weight: 600; border: var(--glass-border); backdrop-filter: blur(10px); } +.pill.score { background: rgba(34, 197, 94, 0.2); color: #4ade80; border-color: rgba(34, 197, 94, 0.2); } + +/* Hidden elements as requested */ +#description { display: none; } /* Hides description box */ +#year { display: none; } /* Hides year pill */ + +.action-row { display: flex; gap: 1rem; } +.btn-primary { + padding: 0.8rem 2rem; background: white; color: black; border: none; border-radius: 99px; + font-weight: 800; cursor: pointer; transition: transform 0.2s; +} +.btn-primary:hover { transform: scale(1.05); } + +/* Fixed Add to Library Button (Matching books.html style) */ +.btn-secondary { + padding: 0.8rem 2rem; + background: rgba(255,255,255,0.1); + color: white; + border: 1px solid rgba(255,255,255,0.2); + border-radius: 99px; + font-weight: 700; + cursor: pointer; + transition: 0.2s; + backdrop-filter: blur(10px); +} +.btn-secondary:hover { + background: rgba(255,255,255,0.2); +} + +/* Legacy class support if HTML uses btn-blur */ +.btn-blur { + padding: 0.8rem 2rem; + background: rgba(255,255,255,0.1); + color: white; + border: 1px solid rgba(255,255,255,0.2); + border-radius: 99px; + font-weight: 700; + cursor: pointer; + transition: 0.2s; + backdrop-filter: blur(10px); +} +.btn-blur:hover { background: rgba(255,255,255,0.2); } + + +/* Chapters Section */ +.chapters-section { margin-top: 1rem; } +.section-title { display: flex; align-items: center; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 0.8rem; margin-bottom: 1.5rem; } +.section-title h2 { font-size: 1.5rem; margin: 0; border-left: 4px solid var(--accent); padding-left: 1rem; } + +.chapters-table-wrapper { + background: var(--bg-surface); border-radius: var(--radius-md); + border: 1px solid rgba(255,255,255,0.05); overflow: hidden; +} +.chapters-table { width: 100%; border-collapse: collapse; text-align: left; } +.chapters-table th { + padding: 0.8rem 1.2rem; background: rgba(255,255,255,0.03); + color: var(--text-secondary); font-weight: 600; font-size: 0.85rem; + text-transform: uppercase; letter-spacing: 0.5px; +} +.chapters-table td { + padding: 1rem 1.2rem; border-bottom: 1px solid rgba(255,255,255,0.05); + color: var(--text-primary); font-size: 0.95rem; +} +.chapters-table tr:last-child td { border-bottom: none; } +.chapters-table tr:hover { background: var(--bg-surface-hover); } + +.filter-select { + appearance: none; + -webkit-appearance: none; + background-color: var(--bg-surface); /* Dark background */ + color: var(--text-primary); /* White text */ + border: 1px solid rgba(255,255,255,0.1); + padding: 0.5rem 2rem 0.5rem 1rem; + border-radius: 99px; + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + outline: none; + background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 1rem center; +} + +.filter-select:hover { + border-color: var(--accent); + background-color: var(--bg-surface-hover); +} + +/* Force Dropdown Options Background (browser dependent but works on most modern ones) */ +.filter-select option { + background-color: var(--bg-surface); + color: var(--text-primary); +} + +.read-btn-small { + background: var(--accent); color: white; border: none; + padding: 0.4rem 0.9rem; border-radius: 6px; font-weight: 600; cursor: pointer; + font-size: 0.8rem; transition: 0.2s; +} +.read-btn-small:hover { background: #7c3aed; } + +/* Pagination */ +.pagination-controls { + display: flex; justify-content: center; gap: 1rem; margin-top: 1.5rem; align-items: center; +} +.page-btn { + background: var(--bg-surface); border: 1px solid rgba(255,255,255,0.1); + color: white; padding: 0.5rem 1rem; border-radius: 8px; cursor: pointer; font-size: 0.9rem; +} +.page-btn:disabled { opacity: 0.5; cursor: not-allowed; } +.page-btn:hover:not(:disabled) { border-color: var(--accent); } + +@keyframes slideUp { from { opacity: 0; transform: translateY(40px); } to { opacity: 1; transform: translateY(0); } } + +@media (max-width: 1024px) { + .hero-wrapper { height: 40vh; } + .content-container { grid-template-columns: 1fr; margin-top: -80px; padding: 0 1.5rem 4rem 1.5rem; } + .poster-card { display: none; } /* Hide large poster on mobile */ + + .main-content { padding-top: 0; align-items: center; text-align: center; } + .book-title { font-size: 2.2rem; } + .meta-row { justify-content: center; } + .action-row { justify-content: center; width: 100%; } + .btn-primary, .btn-blur { flex: 1; justify-content: center; } + + .sidebar { display: none; } + .chapters-table th:nth-child(3), .chapters-table td:nth-child(3) { display: none; } + .chapters-table th:nth-child(4), .chapters-table td:nth-child(4) { display: none; } +} \ No newline at end of file diff --git a/public/book.js b/public/book.js new file mode 100644 index 0000000..1fb99f4 --- /dev/null +++ b/public/book.js @@ -0,0 +1,210 @@ +const bookId = window.location.pathname.split('/').pop(); +let allChapters = []; // Stores all fetched chapters +let filteredChapters = []; // Stores currently displayed chapters (filtered) +let currentPage = 1; +const itemsPerPage = 12; + +async function init() { + try { + // 1. Load Metadata + const res = await fetch(`/api/book/${bookId}`); + const data = await res.json(); + + if (data.error) { + const titleEl = document.getElementById('title'); + if (titleEl) titleEl.innerText = "Book Not Found"; + return; + } + + // Populate Hero Elements + const title = data.title.english || data.title.romaji; + document.title = `${title} | StreamFlow Books`; + + const titleEl = document.getElementById('title'); + if (titleEl) titleEl.innerText = title; + + const descEl = document.getElementById('description'); + if (descEl) descEl.innerHTML = data.description || "No description available."; + + const scoreEl = document.getElementById('score'); + if (scoreEl) scoreEl.innerText = (data.averageScore || '?') + '% Score'; + + const pubEl = document.getElementById('published-date'); + if (pubEl) { + if (data.startDate && data.startDate.year) { + const y = data.startDate.year; + const m = data.startDate.month ? `-${data.startDate.month.toString().padStart(2, '0')}` : ''; + const d = data.startDate.day ? `-${data.startDate.day.toString().padStart(2, '0')}` : ''; + pubEl.innerText = `${y}${m}${d}`; + } else { + pubEl.innerText = '????'; + } + } + + const statusEl = document.getElementById('status'); + if (statusEl) statusEl.innerText = data.status || 'Unknown'; + + const formatEl = document.getElementById('format'); + if (formatEl) formatEl.innerText = data.format || 'MANGA'; + + const chaptersEl = document.getElementById('chapters'); + if (chaptersEl) chaptersEl.innerText = data.chapters || '?'; + + const genresEl = document.getElementById('genres'); + if(genresEl && data.genres) { + genresEl.innerText = data.genres.slice(0, 3).join(' • '); + } + + const img = data.coverImage.extraLarge || data.coverImage.large; + + const posterEl = document.getElementById('poster'); + if (posterEl) posterEl.src = img; + + const heroBgEl = document.getElementById('hero-bg'); + if (heroBgEl) heroBgEl.src = data.bannerImage || img; + + // 2. Load Chapters + loadChapters(); + + } catch (err) { + console.error("Metadata Error:", err); + } +} + +async function loadChapters() { + const tbody = document.getElementById('chapters-body'); + if (!tbody) return; + + tbody.innerHTML = 'Searching extensions for chapters...'; + + try { + const res = await fetch(`/api/book/${bookId}/chapters`); + const data = await res.json(); + + allChapters = data.chapters || []; + filteredChapters = [...allChapters]; // Initially, show all + + const totalEl = document.getElementById('total-chapters'); + + if (allChapters.length === 0) { + tbody.innerHTML = 'No chapters found on loaded extensions.'; + if (totalEl) totalEl.innerText = "0 Found"; + return; + } + + if (totalEl) totalEl.innerText = `${allChapters.length} Found`; + + // Populate Provider Filter + populateProviderFilter(); + + // Read Button Action (Start at filtered Ch 1) + const readBtn = document.getElementById('read-start-btn'); + if (readBtn && filteredChapters.length > 0) { + readBtn.onclick = () => openReader(filteredChapters[0].id); + } + + renderTable(); + + } catch (err) { + tbody.innerHTML = 'Error loading chapters.'; + console.error(err); + } +} + +function populateProviderFilter() { + const select = document.getElementById('provider-filter'); + if (!select) return; + + // Extract unique providers + const providers = [...new Set(allChapters.map(ch => ch.provider))]; + + // Only show filter if there are actual providers found + if (providers.length > 0) { + select.style.display = 'inline-block'; + + // Clear existing options except "All" + select.innerHTML = ''; + + providers.forEach(prov => { + const opt = document.createElement('option'); + opt.value = prov; + opt.innerText = prov; + select.appendChild(opt); + }); + + // Attach Event Listener + select.onchange = (e) => { + const selected = e.target.value; + if (selected === 'all') { + filteredChapters = [...allChapters]; + } else { + filteredChapters = allChapters.filter(ch => ch.provider === selected); + } + currentPage = 1; // Reset to page 1 on filter change + renderTable(); + }; + } +} + +function renderTable() { + const tbody = document.getElementById('chapters-body'); + if (!tbody) return; + + tbody.innerHTML = ''; + + if (filteredChapters.length === 0) { + tbody.innerHTML = 'No chapters match this filter.'; + updatePagination(); // Update to hide buttons + return; + } + + const start = (currentPage - 1) * itemsPerPage; + const end = start + itemsPerPage; + const pageItems = filteredChapters.slice(start, end); + + pageItems.forEach(ch => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${ch.number} + ${ch.title || `Chapter ${ch.number}`} + ${ch.provider} + + + + `; + tbody.appendChild(row); + }); + + updatePagination(); +} + +function updatePagination() { + const totalPages = Math.ceil(filteredChapters.length / itemsPerPage); + const pagination = document.getElementById('pagination'); + + if (!pagination) return; + + if (totalPages <= 1) { + pagination.style.display = 'none'; + return; + } + + pagination.style.display = 'flex'; + document.getElementById('page-info').innerText = `Page ${currentPage} of ${totalPages}`; + + const prevBtn = document.getElementById('prev-page'); + const nextBtn = document.getElementById('next-page'); + + prevBtn.disabled = currentPage === 1; + nextBtn.disabled = currentPage >= totalPages; + + prevBtn.onclick = () => { currentPage--; renderTable(); }; + nextBtn.onclick = () => { currentPage++; renderTable(); }; +} + +function openReader(chapterId) { + alert("Opening Reader for Chapter ID: " + chapterId); + // window.location.href = `/read/${bookId}/${chapterId}`; +} + +init(); \ No newline at end of file diff --git a/public/books.css b/public/books.css new file mode 100644 index 0000000..31a4d23 --- /dev/null +++ b/public/books.css @@ -0,0 +1,341 @@ +:root { + --bg-base: #09090b; + --bg-surface: #121215; + --bg-surface-hover: #1e1e22; + --accent: #8b5cf6; + --accent-glow: rgba(139, 92, 246, 0.4); + --text-primary: #ffffff; + --text-secondary: #a1a1aa; + --radius-md: 12px; + --radius-lg: 24px; + --nav-height: 80px; +} + +* { box-sizing: border-box; outline: none; } + +body { + margin: 0; + background-color: var(--bg-base); + color: var(--text-primary); + font-family: 'Inter', system-ui, sans-serif; + overflow-x: hidden; +} + +/* --- NAV --- */ +.navbar { + width: 100%; + height: var(--nav-height); + position: fixed; + top: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 3rem; + background: linear-gradient(to bottom, rgba(9,9,11,0.9) 0%, rgba(9,9,11,0) 100%); + transition: background 0.3s; +} + +.navbar.scrolled { + background: rgba(9, 9, 11, 0.85); + backdrop-filter: blur(12px); + border-bottom: 1px solid rgba(255,255,255,0.05); +} + +.nav-brand { + font-weight: 900; + font-size: 1.5rem; + display: flex; + align-items: center; + gap: 0.8rem; + letter-spacing: -0.5px; + min-width: 200px; + color: white; + text-decoration: none; + cursor: pointer; +} + +.brand-icon { + width: 36px; + height: 36px; + background: linear-gradient(135deg, #8b5cf6, #3b82f6); + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 20px var(--accent-glow); +} + +.nav-center { + display: flex; + gap: 0.5rem; + background: rgba(255,255,255,0.03); + padding: 0.4rem; + border-radius: 999px; + border: 1px solid rgba(255,255,255,0.05); +} + +.nav-button { + background: transparent; + border: none; + color: var(--text-secondary); + padding: 0.6rem 1.5rem; + border-radius: 999px; + cursor: pointer; + font-weight: 600; + transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); +} + +.nav-button:hover { color: white; } +.nav-button.active { + background: rgba(255, 255, 255, 0.1); + color: white; + box-shadow: 0 4px 15px rgba(0,0,0,0.2); + backdrop-filter: blur(8px); + border: 1px solid rgba(255,255,255,0.1); +} + +/* SEARCH BAR */ +.search-wrapper { + position: relative; + width: 300px; + z-index: 2000; +} + +.search-input { + width: 100%; + background: rgba(255,255,255,0.05); + border: 1px solid rgba(255,255,255,0.1); + padding: 0.7rem 1rem 0.7rem 2.5rem; + border-radius: 99px; + color: white; + font-family: inherit; + transition: 0.2s; +} + +.search-input:focus { + background: rgba(0, 0, 0, 0.8); + border-color: var(--accent); + box-shadow: 0 0 15px var(--accent-glow); + border-radius: 12px 12px 0 0; +} + +.search-icon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + pointer-events: none; + color: var(--text-secondary); +} + +/* Dropdown */ +.search-results { + position: absolute; + top: 100%; + left: 0; + right: 0; + background: rgba(15, 15, 18, 0.95); + backdrop-filter: blur(12px); + border: 1px solid rgba(255,255,255,0.1); + border-top: none; + border-radius: 0 0 12px 12px; + padding: 0.5rem; + display: none; + flex-direction: column; + gap: 0.25rem; + box-shadow: 0 10px 30px rgba(0,0,0,0.5); + max-height: 400px; + overflow-y: auto; +} + +.search-results.active { display: flex; } + +.search-item { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.5rem; + border-radius: 8px; + cursor: pointer; + transition: background 0.2s; + text-decoration: none; + color: inherit; +} + +.search-item:hover { background: rgba(255,255,255,0.1); } + +.search-poster { + width: 40px; + height: 56px; + border-radius: 4px; + object-fit: cover; + background: #222; +} + +.search-info { flex: 1; overflow: hidden; } + +.search-title { + font-size: 0.9rem; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--text-primary); +} + +.search-meta { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.75rem; + color: var(--text-secondary); + margin-top: 2px; +} + +.rating-pill { color: #4ade80; font-weight: 700; } + +/* --- HERO --- */ +.hero-wrapper { + position: relative; + height: 85vh; + width: 100%; + overflow: hidden; +} + +.hero-background { position: absolute; inset: 0; z-index: 0; } + +#hero-bg-media { + width: 100%; height: 100%; object-fit: cover; opacity: 0.6; + transform: scale(1.1); transition: opacity 1s ease; +} + +.hero-vignette { + position: absolute; inset: 0; + background: radial-gradient(circle at center, transparent 0%, var(--bg-base) 120%), + linear-gradient(to top, var(--bg-base) 10%, transparent 60%); + z-index: 1; +} + +.hero-content { + position: relative; z-index: 10; height: 100%; + max-width: 1600px; margin: 0 auto; padding: 0 3rem; + display: flex; align-items: flex-end; padding-bottom: 6rem; gap: 3rem; +} + +.hero-poster-card { + width: 260px; height: 380px; border-radius: var(--radius-lg); + overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7); + border: 1px solid rgba(255,255,255,0.1); flex-shrink: 0; + background: #1a1a1a; +} +.hero-poster-card img { width: 100%; height: 100%; object-fit: cover; } + +.hero-text { flex: 1; max-width: 800px; margin-bottom: 1rem; animation: fadeInUp 0.8s ease; } + +.hero-title { + font-size: 4rem; font-weight: 900; line-height: 1.1; margin: 0 0 1rem 0; + text-shadow: 0 4px 20px rgba(0,0,0,0.8); + display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; +} + +.hero-meta { display: flex; align-items: center; gap: 1.5rem; margin-bottom: 1.5rem; font-size: 1.1rem; font-weight: 600; } +.score-badge { color: #22c55e; background: rgba(34, 197, 94, 0.1); padding: 0.2rem 0.8rem; border-radius: 6px; border: 1px solid rgba(34, 197, 94, 0.2); } + +.hero-desc { + font-size: 1.1rem; line-height: 1.6; color: #e4e4e7; margin-bottom: 2rem; + max-width: 650px; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; + overflow: hidden; text-shadow: 0 2px 4px rgba(0,0,0,0.5); +} + +.hero-buttons { display: flex; gap: 1rem; } + +.btn-primary, .btn-blur { + padding: 1rem 2.5rem; border-radius: 999px; font-weight: 700; font-size: 1rem; + border: none; cursor: pointer; display: flex; align-items: center; gap: 0.5rem; + transition: transform 0.2s; +} +.btn-primary { background: white; color: black; } +.btn-primary:hover { transform: scale(1.05); } +.btn-blur { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); color: white; border: 1px solid rgba(255,255,255,0.2); } +.btn-blur:hover { background: rgba(255, 255, 255, 0.2); } + +/* --- SECTIONS --- */ +.section { max-width: 1700px; margin: 0 auto; padding: 2rem 3rem; } +.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } +.section-title { font-size: 1.8rem; font-weight: 800; } + +.carousel-wrapper { position: relative; } +.carousel { display: flex; gap: 1.25rem; overflow-x: auto; padding: 1rem 0; scroll-behavior: smooth; scrollbar-width: none; } +.carousel::-webkit-scrollbar { display: none; } + +.scroll-btn { + position: absolute; top: 50%; transform: translateY(-50%); width: 50px; height: 50px; + border-radius: 50%; background: rgba(20, 20, 23, 0.9); border: 1px solid rgba(255,255,255,0.1); + color: white; z-index: 20; cursor: pointer; display: flex; align-items: center; justify-content: center; + opacity: 0; transition: 0.3s; +} +.carousel-wrapper:hover .scroll-btn { opacity: 1; } +.scroll-btn:hover { background: var(--accent); } +.scroll-btn.left { left: -25px; } +.scroll-btn.right { right: -25px; } + +/* --- CARDS --- */ +.card { min-width: 220px; cursor: pointer; transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); } +.card:hover { transform: translateY(-8px); } +.card-img-wrap { width: 100%; aspect-ratio: 2/3; border-radius: var(--radius-md); overflow: hidden; position: relative; margin-bottom: 0.8rem; background: #222; } +.card-img-wrap img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s; } +.card:hover .card-img-wrap img { transform: scale(1.05); } + +.card-content h3 { margin: 0; font-size: 1rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.card-content p { margin: 4px 0 0 0; font-size: 0.85rem; color: var(--text-secondary); } + +.skeleton { + background: linear-gradient(90deg, #18181b 25%, #27272a 50%, #18181b 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: 6px; + display: inline-block; +} +.text-skeleton { height: 1em; width: 70%; margin-bottom: 0.5rem; } +.title-skeleton { height: 3em; width: 80%; margin-bottom: 1rem; } +.poster-skeleton { width: 100%; height: 100%; } + +@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } +@keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } + +/* --- MOBILE --- */ +@media (max-width: 768px) { + :root { --nav-height: auto; } + .navbar { padding: 1rem; flex-wrap: wrap; gap: 1rem; background: rgba(9,9,11,0.98); position: fixed; } + .nav-brand { flex: 1; min-width: auto; font-size: 1.3rem; } + .search-wrapper { order: 2; width: auto; flex: 1; min-width: 140px; } + .search-input { width: 100%; padding-left: 2.2rem; font-size: 0.9rem; } + .nav-center { + order: 3; width: 100%; padding: 0.5rem 0; overflow-x: auto; + justify-content: flex-start; gap: 0.8rem; background: transparent; border: none; + border-radius: 0; -webkit-overflow-scrolling: touch; scrollbar-width: none; + } + .nav-center::-webkit-scrollbar { display: none; } + .nav-button { flex-shrink: 0; background: rgba(255,255,255,0.05); padding: 0.5rem 1rem; font-size: 0.85rem; border-radius: 99px; } + + .hero-wrapper { height: 75vh; } + .hero-content { flex-direction: column; justify-content: flex-end; padding: 0 1.5rem 6rem 1.5rem; gap: 1rem; } + .hero-poster-card { display: none; } + .hero-text { width: 100%; text-align: center; margin-bottom: 0; } + .hero-title { font-size: 2.5rem; line-height: 1.1; } + .hero-meta { justify-content: center; flex-wrap: wrap; gap: 0.8rem; } + .hero-buttons { justify-content: center; width: 100%; } + .btn-primary, .btn-blur { flex: 1; justify-content: center; } + .hero-desc { text-align: center; -webkit-line-clamp: 3; font-size: 0.95rem; } + + .section { padding: 1.5rem 1rem; } + .section-title { font-size: 1.4rem; } + .scroll-btn { display: none; } + .card { min-width: 140px; } + + .search-results { + position: fixed; top: 120px; left: 0; right: 0; + width: 100%; border-radius: 0; max-height: 60vh; border: none; z-index: 2001; + } +} \ No newline at end of file diff --git a/public/books.js b/public/books.js new file mode 100644 index 0000000..39c9a57 --- /dev/null +++ b/public/books.js @@ -0,0 +1,189 @@ +let trendingBooks = []; +let currentHeroIndex = 0; +let heroInterval; + +// --- NAVBAR SCROLL --- +window.addEventListener('scroll', () => { + const nav = document.getElementById('navbar'); + if (window.scrollY > 50) nav.classList.add('scrolled'); + else nav.classList.remove('scrolled'); +}); + +// --- SEARCH LOGIC --- +const searchInput = document.getElementById('search-input'); +const searchResults = document.getElementById('search-results'); +let searchTimeout; + +searchInput.addEventListener('input', (e) => { + const query = e.target.value; + clearTimeout(searchTimeout); + + if (query.length < 2) { + searchResults.classList.remove('active'); + searchResults.innerHTML = ''; + searchInput.style.borderRadius = '99px'; + return; + } + + // Debounce 300ms + searchTimeout = setTimeout(() => { + fetchBookSearch(query); + }, 300); +}); + +// Hide results on outside click +document.addEventListener('click', (e) => { + if (!e.target.closest('.search-wrapper')) { + searchResults.classList.remove('active'); + searchInput.style.borderRadius = '99px'; + } +}); + +async function fetchBookSearch(query) { + try { + const res = await fetch(`/api/search/books?q=${encodeURIComponent(query)}`); + const data = await res.json(); + renderSearchResults(data.results || []); + } catch (err) { + console.error("Search Error:", err); + renderSearchResults([]); + } +} + +function renderSearchResults(results) { + searchResults.innerHTML = ''; + + if (!results || results.length === 0) { + searchResults.innerHTML = '
No results found
'; + } else { + results.forEach(book => { + const title = book.title.english || book.title.romaji || "Unknown"; + const img = (book.coverImage && (book.coverImage.medium || book.coverImage.large)) || ''; + const rating = book.averageScore ? `${book.averageScore}%` : 'N/A'; + const year = book.seasonYear || (book.startDate ? book.startDate.year : '') || '????'; + const format = book.format || 'MANGA'; + + const item = document.createElement('a'); + item.className = 'search-item'; + item.href = `/book/${book.id}`; // Direct navigation link + + item.innerHTML = ` + ${title} +
+
${title}
+
+ ${rating} + • ${year} + • ${format} +
+
+ `; + + searchResults.appendChild(item); + }); + } + + searchResults.classList.add('active'); + searchInput.style.borderRadius = '12px 12px 0 0'; +} + +// --- CAROUSEL LOGIC --- +function scrollCarousel(id, direction) { + const container = document.getElementById(id); + if(container) { + const scrollAmount = container.clientWidth * 0.75; + container.scrollBy({ left: direction * scrollAmount, behavior: 'smooth' }); + } +} + +// --- FETCH DATA --- +async function init() { + try { + // Fetch Trending + const res = await fetch('/api/books/trending'); + const data = await res.json(); + + if (data.results && data.results.length > 0) { + trendingBooks = data.results; + updateHeroUI(trendingBooks[0]); + renderList('trending', trendingBooks); + startHeroCycle(); + } + + // Fetch Popular + const resPop = await fetch('/api/books/popular'); + const dataPop = await resPop.json(); + if (dataPop.results) renderList('popular', dataPop.results); + + } catch (e) { + console.error("Books Error:", e); + } +} + +// --- HERO LOGIC --- +function startHeroCycle() { + if(heroInterval) clearInterval(heroInterval); + heroInterval = setInterval(() => { + if(trendingBooks.length > 0) { + currentHeroIndex = (currentHeroIndex + 1) % trendingBooks.length; + updateHeroUI(trendingBooks[currentHeroIndex]); + } + }, 8000); +} + +function updateHeroUI(book) { + if(!book) return; + const title = book.title.english || book.title.romaji; + const desc = book.description || "No description available."; + const poster = (book.coverImage && (book.coverImage.extraLarge || book.coverImage.large)) || ''; + const banner = book.bannerImage || poster; + + document.getElementById('hero-title').innerText = title; + document.getElementById('hero-desc').innerHTML = desc; + document.getElementById('hero-score').innerText = (book.averageScore || '?') + '% Score'; + document.getElementById('hero-year').innerText = (book.startDate && book.startDate.year) ? book.startDate.year : '????'; + document.getElementById('hero-type').innerText = book.format || 'MANGA'; + + const heroPoster = document.getElementById('hero-poster'); + if(heroPoster) heroPoster.src = poster; + + // Update background + const bg = document.getElementById('hero-bg-media'); + if(bg) bg.src = banner; + + // Setup Read Now Button + const readBtn = document.getElementById('read-btn'); + if (readBtn) { + readBtn.onclick = () => window.location.href = `/book/${book.id}`; + } +} + +// --- RENDER LIST --- +function renderList(id, list) { + const container = document.getElementById(id); + container.innerHTML = ''; + + list.forEach(book => { + const title = book.title.english || book.title.romaji; + const cover = book.coverImage ? book.coverImage.large : ''; + const score = book.averageScore || '--'; + const type = book.format || 'Book'; + + const el = document.createElement('div'); + el.className = 'card'; + el.onclick = () => { + // Navigate to book page + window.location.href = `/book/${book.id}`; + }; + el.innerHTML = ` +
+
+

${title}

+

${score}% • ${type}

+
+ `; + container.appendChild(el); + }); +} + +init(); \ No newline at end of file diff --git a/public/home.css b/public/home.css new file mode 100644 index 0000000..e1aa4a4 --- /dev/null +++ b/public/home.css @@ -0,0 +1,352 @@ +:root { + --bg-base: #09090b; + --bg-surface: #121215; + --bg-surface-hover: #1e1e22; + --accent: #8b5cf6; + --accent-glow: rgba(139, 92, 246, 0.4); + --text-primary: #ffffff; + --text-secondary: #a1a1aa; + --radius-md: 12px; + --radius-lg: 24px; + --nav-height: 80px; +} + +* { box-sizing: border-box; outline: none; } + +body { + margin: 0; + background-color: var(--bg-base); + color: var(--text-primary); + font-family: 'Inter', system-ui, sans-serif; + overflow-x: hidden; +} + +/* --- NAV --- */ +.navbar { + width: 100%; + height: var(--nav-height); + position: fixed; + top: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 3rem; + background: linear-gradient(to bottom, rgba(9,9,11,0.9) 0%, rgba(9,9,11,0) 100%); + transition: background 0.3s; +} + +.navbar.scrolled { + background: rgba(9, 9, 11, 0.85); + backdrop-filter: blur(12px); + border-bottom: 1px solid rgba(255,255,255,0.05); +} + +.nav-brand { + font-weight: 900; + font-size: 1.5rem; + display: flex; + align-items: center; + gap: 0.8rem; + letter-spacing: -0.5px; + min-width: 200px; +} + +.brand-icon { + width: 36px; + height: 36px; + background: linear-gradient(135deg, #8b5cf6, #3b82f6); + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 20px var(--accent-glow); +} + +.nav-center { + display: flex; + gap: 0.5rem; + background: rgba(255,255,255,0.03); + padding: 0.4rem; + border-radius: 999px; + border: 1px solid rgba(255,255,255,0.05); +} + +.nav-button { + background: transparent; + border: none; + color: var(--text-secondary); + padding: 0.6rem 1.5rem; + border-radius: 999px; + cursor: pointer; + font-weight: 600; + transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); +} + +.nav-button:hover { color: white; } +.nav-button.active { + background: rgba(255, 255, 255, 0.1); + color: white; + box-shadow: 0 4px 15px rgba(0,0,0,0.2); + backdrop-filter: blur(8px); + border: 1px solid rgba(255,255,255,0.1); +} + +/* SEARCH BAR */ +.search-wrapper { + position: relative; + width: 250px; +} +.search-input { + width: 100%; + background: rgba(255,255,255,0.05); + border: 1px solid rgba(255,255,255,0.1); + padding: 0.7rem 1rem 0.7rem 2.5rem; + border-radius: 99px; + color: white; + font-family: inherit; + transition: 0.2s; +} +.search-input:focus { + background: rgba(255,255,255,0.1); + border-color: var(--accent); + box-shadow: 0 0 15px var(--accent-glow); +} +.search-icon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + pointer-events: none; + color: var(--text-secondary); +} + +/* --- HERO --- */ +.hero-wrapper { + position: relative; + height: 85vh; + width: 100%; + overflow: hidden; +} + +.hero-background { position: absolute; inset: 0; z-index: 0; } + +#hero-bg-media { + width: 100%; height: 100%; object-fit: cover; opacity: 0.6; + transform: scale(1.1); transition: opacity 1s ease; +} + +.hero-vignette { + position: absolute; inset: 0; + background: radial-gradient(circle at center, transparent 0%, var(--bg-base) 120%), + linear-gradient(to top, var(--bg-base) 10%, transparent 60%); + z-index: 1; +} + +.hero-content { + position: relative; z-index: 10; height: 100%; + max-width: 1600px; margin: 0 auto; padding: 0 3rem; + display: flex; align-items: flex-end; padding-bottom: 6rem; gap: 3rem; +} + +.hero-poster-card { + width: 260px; height: 380px; border-radius: var(--radius-lg); + overflow: hidden; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7); + border: 1px solid rgba(255,255,255,0.1); flex-shrink: 0; + background: #1a1a1a; /* fallback */ +} +.hero-poster-card img { width: 100%; height: 100%; object-fit: cover; } + +.hero-text { flex: 1; max-width: 800px; margin-bottom: 1rem; animation: fadeInUp 0.8s ease; } + +.hero-title { + font-size: 4rem; font-weight: 900; line-height: 1.1; margin: 0 0 1rem 0; + text-shadow: 0 4px 20px rgba(0,0,0,0.8); + display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; +} + +.hero-meta { display: flex; align-items: center; gap: 1.5rem; margin-bottom: 1.5rem; font-size: 1.1rem; font-weight: 600; } +.score-badge { color: #22c55e; background: rgba(34, 197, 94, 0.1); padding: 0.2rem 0.8rem; border-radius: 6px; border: 1px solid rgba(34, 197, 94, 0.2); } + +.hero-desc { + font-size: 1.1rem; line-height: 1.6; color: #e4e4e7; margin-bottom: 2rem; + max-width: 650px; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; + overflow: hidden; text-shadow: 0 2px 4px rgba(0,0,0,0.5); +} + +.hero-buttons { display: flex; gap: 1rem; } + +.btn-primary, .btn-blur { + padding: 1rem 2.5rem; border-radius: 999px; font-weight: 700; font-size: 1rem; + border: none; cursor: pointer; display: flex; align-items: center; gap: 0.5rem; + transition: transform 0.2s; +} +.btn-primary { background: white; color: black; } +.btn-primary:hover { transform: scale(1.05); } +.btn-blur { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); color: white; border: 1px solid rgba(255,255,255,0.2); } +.btn-blur:hover { background: rgba(255, 255, 255, 0.2); } + +/* --- CAROUSELS --- */ +.section { max-width: 1700px; margin: 0 auto; padding: 2rem 3rem; } +.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } +.section-title { font-size: 1.8rem; font-weight: 800; } + +.carousel-wrapper { position: relative; } +.carousel { display: flex; gap: 1.25rem; overflow-x: auto; padding: 1rem 0; scroll-behavior: smooth; scrollbar-width: none; } +.carousel::-webkit-scrollbar { display: none; } + +.scroll-btn { + position: absolute; top: 50%; transform: translateY(-50%); width: 50px; height: 50px; + border-radius: 50%; background: rgba(20, 20, 23, 0.9); border: 1px solid rgba(255,255,255,0.1); + color: white; z-index: 20; cursor: pointer; display: flex; align-items: center; justify-content: center; + opacity: 0; transition: 0.3s; +} +.carousel-wrapper:hover .scroll-btn { opacity: 1; } +.scroll-btn:hover { background: var(--accent); } +.scroll-btn.left { left: -25px; } +.scroll-btn.right { right: -25px; } + +/* --- CARDS --- */ +.card { min-width: 220px; cursor: pointer; transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); } +.card:hover { transform: translateY(-8px); } +.card-img-wrap { width: 100%; aspect-ratio: 2/3; border-radius: var(--radius-md); overflow: hidden; position: relative; margin-bottom: 0.8rem; background: #222; } +.card-img-wrap img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s; } +.card:hover .card-img-wrap img { transform: scale(1.05); } + +.card-content h3 { margin: 0; font-size: 1rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.card-content p { margin: 4px 0 0 0; font-size: 0.85rem; color: var(--text-secondary); } + +/* SKELETON LOADING (The "Non-Dumb" Metadata fix) */ +.skeleton { + background: linear-gradient(90deg, #18181b 25%, #27272a 50%, #18181b 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: 6px; + display: inline-block; +} +.text-skeleton { height: 1em; width: 70%; margin-bottom: 0.5rem; } +.title-skeleton { height: 3em; width: 80%; margin-bottom: 1rem; } +.poster-skeleton { width: 100%; height: 100%; } + +@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } +@keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } + +.search-wrapper { + position: relative; + width: 300px; + z-index: 2000; /* Ensure it sits above hero content */ +} + +.search-input { + width: 100%; + background: rgba(255,255,255,0.05); + border: 1px solid rgba(255,255,255,0.1); + padding: 0.7rem 1rem 0.7rem 2.5rem; + border-radius: 99px; + color: white; + font-family: inherit; + transition: 0.2s; +} + +.search-input:focus { + background: rgba(0, 0, 0, 0.8); + border-color: var(--accent); + box-shadow: 0 0 15px var(--accent-glow); + border-radius: 12px 12px 0 0; /* Flatten bottom corners when open */ +} + +.search-icon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + pointer-events: none; + color: var(--text-secondary); +} + +/* The Dropdown Container */ +.search-results { + position: absolute; + top: 100%; + left: 0; + right: 0; + background: rgba(15, 15, 18, 0.95); + backdrop-filter: blur(12px); + border: 1px solid rgba(255,255,255,0.1); + border-top: none; + border-radius: 0 0 12px 12px; + padding: 0.5rem; + display: none; /* Hidden by default */ + flex-direction: column; + gap: 0.25rem; + box-shadow: 0 10px 30px rgba(0,0,0,0.5); + max-height: 400px; + overflow-y: auto; +} + +.search-results.active { + display: flex; +} + +/* Individual Result Item */ +.search-item { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.5rem; + border-radius: 8px; + cursor: pointer; + transition: background 0.2s; + text-decoration: none; + color: inherit; +} + +.search-item:hover { + background: rgba(255,255,255,0.1); +} + +.search-poster { + width: 40px; + height: 56px; + border-radius: 4px; + object-fit: cover; + background: #222; +} + +.search-info { + flex: 1; + overflow: hidden; +} + +.search-title { + font-size: 0.9rem; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: var(--text-primary); +} + +.search-meta { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.75rem; + color: var(--text-secondary); + margin-top: 2px; +} + +.rating-pill { + color: #4ade80; + font-weight: 700; +} + +/* Scrollbar for results */ +.search-results::-webkit-scrollbar { + width: 6px; +} +.search-results::-webkit-scrollbar-thumb { + background: rgba(255,255,255,0.2); + border-radius: 3px; +} + diff --git a/public/watch.css b/public/watch.css new file mode 100644 index 0000000..1882e6f --- /dev/null +++ b/public/watch.css @@ -0,0 +1,418 @@ +:root { + --bg-base: #000000; + --bg-overlay: #101012; + --accent: #8b5cf6; /* Keeping your purple accent for brand consistency */ + --accent-dark: #7c3aed; + --text-primary: #ffffff; + --text-secondary: #a1a1aa; + --radius-full: 9999px; + --radius-md: 16px; + --glass-border: 1px solid rgba(255, 255, 255, 0.1); + --glass-bg: rgba(20, 20, 23, 0.7); + + /* Plyr Theme Variables - YouTube Style */ + --plyr-color-main: #8b5cf6; + --plyr-video-control-color: #ffffff; + --plyr-video-control-background-hover: rgba(255, 255, 255, 0.1); /* Subtle hover like YT */ + --plyr-menu-background: rgba(28, 28, 30, 0.95); + --plyr-menu-color: #ffffff; + --plyr-menu-border-color: rgba(255, 255, 255, 0.1); + --plyr-font-family: 'Inter', sans-serif; + + /* Custom YT-like sizing */ + --plyr-control-icon-size: 18px; + --plyr-control-spacing: 10px; +} + +body { + margin: 0; + background-color: var(--bg-base); + color: var(--text-primary); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + height: 100vh; + overflow: hidden; + display: flex; + flex-direction: column; +} + +/* --- TOP BAR --- */ +.top-bar { + padding: 1.5rem 2rem; + display: flex; + justify-content: space-between; + align-items: center; + position: fixed; + top: 0; left: 0; right: 0; + z-index: 100; + background: linear-gradient(to bottom, rgba(0,0,0,0.8) 0%, transparent 100%); + pointer-events: none; +} + +.back-btn { + pointer-events: auto; + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.8rem 1.8rem; + background: var(--glass-bg); + backdrop-filter: blur(12px); + border: var(--glass-border); + border-radius: var(--radius-full); + color: white; + text-decoration: none; + font-weight: 600; + font-size: 0.95rem; + transition: all 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); + box-shadow: 0 4px 20px rgba(0,0,0,0.2); +} + +.back-btn:hover { + background: rgba(255, 255, 255, 0.15); + transform: scale(1.02); + box-shadow: 0 0 15px rgba(139, 92, 246, 0.3); + border-color: rgba(139, 92, 246, 0.3); +} + +/* --- THEATER LAYOUT --- */ +.theater-container { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + max-width: 1300px; + margin: 0 auto; + padding: 0 2rem; + position: relative; + z-index: 10; +} + +/* --- TOOLBAR (Extensions + Toggles) --- */ +.player-toolbar { + width: 100%; + display: flex; + justify-content: flex-end; + align-items: center; + gap: 1rem; + margin-bottom: 1rem; + position: relative; + z-index: 50; +} + +/* Modern Pill Dropdown */ +.extension-select { + appearance: none; + -webkit-appearance: none; + background-color: var(--glass-bg); + backdrop-filter: blur(12px); + border: var(--glass-border); + color: var(--text-primary); + padding: 0.7rem 2.5rem 0.7rem 1.5rem; + border-radius: var(--radius-full); + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + outline: none; + min-width: 180px; + transition: all 0.2s ease; + background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 1rem center; + box-shadow: 0 4px 10px rgba(0,0,0,0.3); +} + +.extension-select:hover { + background-color: rgba(255,255,255,0.1); + border-color: var(--accent); +} + +/* SUB / DUB TOGGLE */ +.sd-toggle { + display: flex; + background: var(--glass-bg); + border: var(--glass-border); + border-radius: var(--radius-full); + padding: 4px; + position: relative; + cursor: pointer; + backdrop-filter: blur(12px); +} + +.sd-option { + padding: 0.5rem 1.2rem; + font-size: 0.85rem; + font-weight: 700; + color: var(--text-secondary); + z-index: 2; + transition: color 0.3s; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.sd-option.active { + color: white; +} + +/* The sliding pill background */ +.sd-bg { + position: absolute; + top: 4px; + left: 4px; + bottom: 4px; + width: calc(50% - 4px); + background: var(--accent); + border-radius: var(--radius-full); + transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); + box-shadow: 0 2px 10px rgba(139, 92, 246, 0.4); + z-index: 1; +} + +/* Toggle State Logic handled via class on parent */ +.sd-toggle[data-state="dub"] .sd-bg { + transform: translateX(100%); +} + +/* --- VIDEO WRAPPER --- */ +.video-wrapper { + width: 100%; + aspect-ratio: 16/9; + background: #000; + border-radius: 20px; + overflow: hidden; + box-shadow: 0 20px 60px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.08); + position: relative; + transition: box-shadow 0.3s ease; +} + +.video-wrapper:hover { + box-shadow: 0 25px 70px rgba(139, 92, 246, 0.15), 0 0 0 1px rgba(139, 92, 246, 0.3); +} + +video { + width: 100%; + height: 100%; + object-fit: contain; +} + +/* Loading Overlay */ +.loading-overlay { + position: absolute; + inset: 0; + background: #000; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 20; + color: var(--text-secondary); + gap: 1rem; +} + +.spinner { + width: 40px; + height: 40px; + border: 3px solid rgba(255,255,255,0.1); + border-radius: 50%; + border-top-color: var(--accent); + animation: spin 1s linear infinite; +} + +@keyframes spin { 100% { transform: rotate(360deg); } } + +/* --- CONTROLS --- */ +.controls-area { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 1.5rem; + padding: 0 0.5rem; +} + +.episode-info h2 { + margin: 0; + font-size: 1.4rem; + font-weight: 700; + letter-spacing: -0.5px; + text-shadow: 0 2px 10px rgba(0,0,0,0.5); +} + +.episode-info span { + color: var(--accent); + font-size: 0.95rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + margin-top: 4px; + display: block; +} + +.nav-controls { + display: flex; + gap: 1rem; +} + +.nav-btn { + background: rgba(255,255,255,0.05); + border: 1px solid rgba(255,255,255,0.1); + color: white; + padding: 0.8rem 1.6rem; + border-radius: var(--radius-full); + font-weight: 600; + cursor: pointer; + display: flex; + align-items: center; + gap: 0.6rem; + transition: all 0.2s ease; + backdrop-filter: blur(10px); +} + +.nav-btn:hover:not(:disabled) { + background: var(--accent); + border-color: var(--accent); + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(139, 92, 246, 0.3); +} + +.nav-btn:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +/* --- PLYR YOUTUBE-STYLE OVERRIDES --- */ + +/* 1. Base Video Container */ +.plyr--video { + border-radius: 20px; + font-family: 'Inter', sans-serif; +} + +/* 2. Controls Container (Gradient Overlay) */ +.plyr__controls { + background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.4) 50%, transparent 100%) !important; + padding: 10px 20px 20px 20px !important; + margin: 0 !important; + border-radius: 0 0 20px 20px !important; +} + +/* 3. Progress Bar (YouTube Style) */ +.plyr__progress input[type=range] { + cursor: pointer; +} + +/* The track */ +.plyr--full-ui input[type=range] { + color: var(--accent); /* Your brand color instead of YouTube red */ + height: 4px; /* Thinner by default */ + transition: height 0.1s ease; +} + +/* Expand progress bar on hover like YT */ +.plyr__progress__container:hover input[type=range] { + height: 6px; +} + +/* 4. Buttons (Clean Icons, No Background) */ +.plyr__control { + background: transparent !important; + border-radius: 4px; + transition: transform 0.1s, opacity 0.2s; + padding: 7px !important; +} + +.plyr__control:hover { + background: rgba(255,255,255,0.1) !important; + opacity: 1; +} + +.plyr__control svg { + width: 24px; + height: 24px; + filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5)); +} + +/* 5. Center Play Button (Large, Pulsing) */ +.plyr__control--overlaid { + background: rgba(0, 0, 0, 0.6) !important; + border: 2px solid white; /* Optional: White ring for classic look */ + border-radius: 50%; + padding: 1.5rem !important; + opacity: 0.9; + transition: all 0.2s ease; +} + +.plyr__control--overlaid svg { + width: 32px; + height: 32px; +} + +.plyr__control--overlaid:hover { + background: var(--accent) !important; + border-color: var(--accent); + transform: scale(1.1); +} + +/* 6. Time Display */ +.plyr__time { + font-size: 13px; + font-weight: 500; + text-shadow: 0 1px 2px rgba(0,0,0,0.8); +} + +/* 7. Menus (Floating Glass Panels) */ +.plyr__menu__container { + background: rgba(28, 28, 30, 0.95) !important; + backdrop-filter: blur(12px); + border: 1px solid rgba(255,255,255,0.1); + border-radius: 12px !important; + padding: 8px; + box-shadow: 0 10px 40px rgba(0,0,0,0.5) !important; + bottom: 60px !important; /* Lift above controls */ +} + +.plyr__menu__container .plyr__control { + font-size: 13px; + font-weight: 500; + padding: 8px 12px !important; + border-radius: 6px; + justify-content: flex-start; +} + +.plyr__menu__container .plyr__control:hover { + background: rgba(255,255,255,0.1) !important; +} + +/* Active menu item */ +.plyr__menu__container .plyr__control[aria-checked="true"] { + color: var(--accent); +} +.plyr__menu__container .plyr__control[aria-checked="true"]::after { + background: var(--accent); /* The checkmark */ +} + +/* 8. Tooltips (Time preview) */ +.plyr__tooltip { + background: rgba(28, 28, 30, 0.9); + border: 1px solid rgba(255,255,255,0.1); + border-radius: 4px; + font-size: 12px; + font-weight: 600; + padding: 4px 8px; + box-shadow: 0 2px 10px rgba(0,0,0,0.5); +} + +/* 9. Subtitles (Cinematic) */ +.plyr__cues { + margin-bottom: 50px !important; /* Push subtitles up so controls don't overlap */ +} + +.plyr__cues span { + background-color: rgba(0, 0, 0, 0.75) !important; + font-family: 'Inter', sans-serif; + font-weight: 600; + font-size: 18px; + padding: 4px 12px; + border-radius: 4px; + text-shadow: 0 2px 4px rgba(0,0,0,0.8); +} \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..58af515 --- /dev/null +++ b/server.js @@ -0,0 +1,507 @@ +const fastify = require('fastify')({ logger: true }); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); +const { animeMetadata } = require('./src/metadata/anilist'); +const sqlite3 = require('sqlite3').verbose(); + +// --- DATABASE CONNECTION --- +const DB_PATH = path.join(__dirname, 'src', 'metadata', 'anilist_anime.db'); +const db = new sqlite3.Database(DB_PATH, sqlite3.OPEN_READONLY, (err) => { + if (err) console.error("Database Error:", err.message); + else console.log("Connected to local AniList database."); +}); + +// --- EXTENSION LOADER --- +const extensions = new Map(); + +async function loadExtensions() { + const homeDir = os.homedir(); + const extensionsDir = path.join(homeDir, 'WaifuBoards', 'extensions'); + + if (!fs.existsSync(extensionsDir)) { + return; + } + + try { + const files = await fs.promises.readdir(extensionsDir); + for (const file of files) { + if (file.endsWith('.js')) { + const filePath = path.join(extensionsDir, file); + try { + delete require.cache[require.resolve(filePath)]; + const ExtensionClass = require(filePath); + const instance = typeof ExtensionClass === 'function' + ? new ExtensionClass() + : (ExtensionClass.default ? new ExtensionClass.default() : null); + + if (instance && (instance.type === "anime-board" || instance.type === "book-board")) { + const name = instance.constructor.name; + extensions.set(name, instance); + console.log(`Loaded Extension: ${name}`); + } + } catch (e) { + console.error(`Failed to load extension ${file}:`, e); + } + } + } + } catch (err) { + console.error("Extension Scan Error:", err); + } +} + +loadExtensions(); + +// --- STATIC & VIEWS --- +fastify.register(require('@fastify/static'), { + root: path.join(__dirname, 'public'), + prefix: '/public/', +}); + +fastify.get('/', (req, reply) => { + const stream = fs.createReadStream(path.join(__dirname, 'views', 'index.html')); + reply.type('text/html').send(stream); +}); + +// NEW: Books Page +fastify.get('/books', (req, reply) => { + const stream = fs.createReadStream(path.join(__dirname, 'views', 'books.html')); + reply.type('text/html').send(stream); +}); + +fastify.get('/anime/:id', (req, reply) => { + const stream = fs.createReadStream(path.join(__dirname, 'views', 'anime.html')); + reply.type('text/html').send(stream); +}); + +fastify.get('/watch/:id/:episode', (req, reply) => { + const stream = fs.createReadStream(path.join(__dirname, 'views', 'watch.html')); + reply.type('text/html').send(stream); +}); + +// --- API ENDPOINTS --- + +// NEW: Books API (Manga) +fastify.get('/api/books/trending', (req, reply) => { + return new Promise((resolve) => { + db.all("SELECT full_data FROM trending_books ORDER BY rank ASC LIMIT 10", [], (err, rows) => { + if (err || !rows) resolve({ results: [] }); + else resolve({ results: rows.map(r => JSON.parse(r.full_data)) }); + }); + }); +}); + +fastify.get('/api/books/popular', (req, reply) => { + return new Promise((resolve) => { + db.all("SELECT full_data FROM popular_books ORDER BY rank ASC LIMIT 10", [], (err, rows) => { + if (err || !rows) resolve({ results: [] }); + else resolve({ results: rows.map(r => JSON.parse(r.full_data)) }); + }); + }); +}); + +// ... [Keep previous Anime/Proxy APIs] ... +// 1. Proxy +fastify.get('/api/proxy', async (req, reply) => { + const { url, referer, origin, userAgent } = req.query; + if (!url) return reply.code(400).send("No URL provided"); + + const headers = { + 'User-Agent': userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", + 'Accept': '*/*', + 'Accept-Language': 'en-US,en;q=0.9' + }; + if (referer) headers['Referer'] = referer; + if (origin) headers['Origin'] = origin; + + try { + const response = await fetch(url, { headers, redirect: 'follow' }); + if (!response.ok) return reply.code(response.status).send(`Proxy Error: ${response.statusText}`); + + reply.header('Access-Control-Allow-Origin', '*'); + reply.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); + const contentType = response.headers.get('content-type'); + if (contentType) reply.header('Content-Type', contentType); + + const isM3U8 = (contentType && (contentType.includes('mpegurl'))) || url.includes('.m3u8'); + + if (isM3U8) { + const text = await response.text(); + const baseUrl = new URL(response.url); + const newText = text.replace(/^(?!#)(?!\s*$).+/gm, (line) => { + line = line.trim(); + let absoluteUrl; + try { absoluteUrl = new URL(line, baseUrl).href; } catch(e) { return line; } + const proxyParams = new URLSearchParams(); + proxyParams.set('url', absoluteUrl); + if (referer) proxyParams.set('referer', referer); + if (origin) proxyParams.set('origin', origin); + if (userAgent) proxyParams.set('userAgent', userAgent); + return `/api/proxy?${proxyParams.toString()}`; + }); + return newText; + } else { + const { Readable } = require('stream'); + return reply.send(Readable.fromWeb(response.body)); + } + } catch (err) { + fastify.log.error(err); + return reply.code(500).send("Internal Server Error"); + } +}); + +// Extensions +fastify.get('/api/extensions', async (req, reply) => { + return { extensions: Array.from(extensions.keys()) }; +}); + +fastify.get('/api/extension/:name/settings', async (req, reply) => { + const name = req.params.name; + const ext = extensions.get(name); + if (!ext) return { error: "Extension not found" }; + if (!ext.getSettings) return { episodeServers: ["default"], supportsDub: false }; + return ext.getSettings(); +}); + +fastify.get('/api/watch/stream', async (req, reply) => { + const { animeId, episode, server, category, ext } = req.query; + const extension = extensions.get(ext); + if (!extension) return { error: "Extension not found" }; + + const animeData = await new Promise((resolve) => { + db.get("SELECT full_data FROM anime WHERE id = ?", [animeId], (err, row) => { + if (err || !row) resolve(null); + else resolve(JSON.parse(row.full_data)); + }); + }); + + if (!animeData) return { error: "Anime metadata not found" }; + + try { + const searchOptions = { + query: animeData.title.english || animeData.title.romaji, + dub: category === 'dub', + media: { + romajiTitle: animeData.title.romaji, + englishTitle: animeData.title.english || "", + startDate: animeData.startDate || { year: 0, month: 0, day: 0 } + } + }; + + const searchResults = await extension.search(searchOptions); + if (!searchResults || searchResults.length === 0) return { error: "Anime not found on provider" }; + + const bestMatch = searchResults[0]; + const episodes = await extension.findEpisodes(bestMatch.id); + const targetEp = episodes.find(e => e.number === parseInt(episode)); + + if (!targetEp) return { error: "Episode not found" }; + + const serverName = server || "default"; + const streamData = await extension.findEpisodeServer(targetEp, serverName); + return streamData; + } catch (err) { + return { error: err.message }; + } +}); + +fastify.get('/api/anime/:id', (req, reply) => { + const id = req.params.id; + return new Promise((resolve) => { + db.get("SELECT full_data FROM anime WHERE id = ?", [id], (err, row) => { + if(err) resolve({ error: "Database error" }); + else if (!row) resolve({ error: "Anime not found" }); + else resolve(JSON.parse(row.full_data)); + }); + }); +}); + +fastify.get('/api/search/books', async (req, reply) => { + const query = req.query.q; + if (!query || query.length < 2) return { results: [] }; + + // A. Local DB Search (Prioritized) + const dbResults = await new Promise((resolve) => { + const sql = `SELECT full_data FROM books WHERE full_data LIKE ? LIMIT 50`; + db.all(sql, [`%${query}%`], (err, rows) => { + if (err || !rows) resolve([]); + else { + try { + const results = rows.map(row => JSON.parse(row.full_data)); + const clean = results.filter(book => { + const searchTerms = [ + book.title.english, + book.title.romaji, + book.title.native, + ...(book.synonyms || []) + ].filter(Boolean).map(t => t.toLowerCase()); + return searchTerms.some(term => term.includes(query.toLowerCase())); + }); + resolve(clean.slice(0, 10)); + } catch (e) { resolve([]); } + } + }); + }); + + if (dbResults.length > 0) { + return { results: dbResults }; + } + + // B. Live AniList Fallback (If Local DB is empty/missing data) + try { + console.log(`[Books] Local DB miss for "${query}", fetching live...`); + const gql = ` + query ($search: String) { + Page(page: 1, perPage: 5) { + media(search: $search, type: MANGA, isAdult: false) { + id title { romaji english native } + coverImage { extraLarge large } + bannerImage description averageScore format + seasonYear startDate { year } + } + } + }`; + + const response = await fetch('https://graphql.anilist.co', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, + body: JSON.stringify({ query: gql, variables: { search: query } }) + }); + + const liveData = await response.json(); + if (liveData.data && liveData.data.Page.media.length > 0) { + return { results: liveData.data.Page.media }; + } + } catch(e) { + console.error("Live Search Error:", e.message); + } + + // C. Extensions Fallback (If not on AniList at all) + let extResults = []; + for (const [name, ext] of extensions) { + // UPDATED: Check for 'book-board' or 'manga-board' + if ((ext.type === 'book-board' || ext.type === 'manga-board') && ext.search) { + try { + console.log(`[${name}] Searching for book: ${query}`); + const matches = await ext.search({ + query: query, + media: { + romajiTitle: query, + englishTitle: query, + startDate: { year: 0, month: 0, day: 0 } + } + }); + + if (matches && matches.length > 0) { + extResults = matches.map(m => ({ + id: m.id, + title: { romaji: m.title, english: m.title }, + coverImage: { large: m.image || '' }, + // UPDATED: Try to get score from extension if available + averageScore: m.rating || m.score || null, + format: 'MANGA', + seasonYear: null, + isExtensionResult: true + })); + break; + } + } catch (e) { + console.error(`Extension search failed for ${name}:`, e); + } + } + } + + return { results: extResults }; +}); + +fastify.get('/api/book/:id/chapters', async (req, reply) => { + const id = req.params.id; + + // Helper to get metadata (Local or Live) + let bookData = await new Promise((resolve) => { + db.get("SELECT full_data FROM books WHERE id = ?", [id], (err, row) => { + if (err || !row) resolve(null); + else resolve(JSON.parse(row.full_data)); + }); + }); + + if (!bookData) { + try { + const query = `query ($id: Int) { Media(id: $id, type: MANGA) { title { romaji english } startDate { year month day } } }`; + const res = await fetch('https://graphql.anilist.co', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query, variables: { id: parseInt(id) } }) + }); + const d = await res.json(); + if(d.data?.Media) bookData = d.data.Media; + } catch(e) {} + } + + if (!bookData) return { chapters: [] }; + + const titles = [bookData.title.english, bookData.title.romaji].filter(t => t); + const searchTitle = titles[0]; // Prefer English, fallback to Romaji + + const allChapters = []; + + // Create an array of promises for all matching extensions + const searchPromises = Array.from(extensions.entries()) + .filter(([name, ext]) => (ext.type === 'book-board' || ext.type === 'manga-board') && ext.search && ext.findChapters) + .map(async ([name, ext]) => { + try { + console.log(`[${name}] Searching chapters for: ${searchTitle}`); + + // Pass strict search options + const matches = await ext.search({ + query: searchTitle, + media: { + romajiTitle: bookData.title.romaji, + englishTitle: bookData.title.english, + startDate: bookData.startDate + } + }); + + if (matches && matches.length > 0) { + // Use the first match to find chapters + const best = matches[0]; + const chaps = await ext.findChapters(best.id); + + if (chaps && chaps.length > 0) { + console.log(`[${name}] Found ${chaps.length} chapters.`); + chaps.forEach(ch => { + const num = parseFloat(ch.number); + // Add to aggregator with provider tag + allChapters.push({ + id: ch.id, + number: num, + title: ch.title, + date: ch.releaseDate, + provider: name + }); + }); + } + } else { + console.log(`[${name}] No matches found for book.`); + } + } catch (e) { + console.error(`Failed to fetch chapters from ${name}:`, e.message); + } + }); + + // Wait for all providers to finish (in parallel) + await Promise.all(searchPromises); + + // Sort all aggregated chapters by number + const sortedChapters = allChapters.sort((a, b) => a.number - b.number); + + return { chapters: sortedChapters }; +}); + +fastify.get('/api/book/:id', async (req, reply) => { + const id = req.params.id; + + // 1. Try Local DB + const bookData = await new Promise((resolve) => { + db.get("SELECT full_data FROM books WHERE id = ?", [id], (err, row) => { + if(err || !row) resolve(null); + else resolve(JSON.parse(row.full_data)); + }); + }); + + if (bookData) return bookData; + + // 2. Live Fallback (If not in DB) + try { + console.log(`[Book] Local miss for ID ${id}, fetching live...`); + const query = ` + query ($id: Int) { + Media(id: $id, type: MANGA) { + id idMal title { romaji english native userPreferred } type format status description + startDate { year month day } endDate { year month day } season seasonYear seasonInt + episodes duration chapters volumes countryOfOrigin isLicensed source hashtag + trailer { id site thumbnail } updatedAt coverImage { extraLarge large medium color } + bannerImage genres synonyms averageScore meanScore popularity isLocked trending favourites + tags { id name description category rank isGeneralSpoiler isMediaSpoiler isAdult userId } + relations { edges { relationType node { id title { romaji } } } } + characters(page: 1, perPage: 10) { nodes { id name { full } } } + studios { nodes { id name isAnimationStudio } } + isAdult nextAiringEpisode { airingAt timeUntilAiring episode } + externalLinks { url site } + rankings { id rank type format year season allTime context } + } + }`; + + // CRITICAL FIX: Ensure ID is parsed as Integer for AniList GraphQL + const response = await fetch('https://graphql.anilist.co', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, + body: JSON.stringify({ query, variables: { id: parseInt(id) } }) + }); + + const data = await response.json(); + if (data.data && data.data.Media) { + return data.data.Media; + } + return { error: "Book not found on AniList" }; + } catch(e) { + fastify.log.error(e); + return { error: "Fetch error" }; + } +}); + +fastify.get('/book/:id', (req, reply) => { + const stream = fs.createReadStream(path.join(__dirname, 'views', 'book.html')); + reply.type('text/html').send(stream); +}); + +fastify.get('/api/search/local', (req, reply) => { + const query = req.query.q; + if (!query || query.length < 2) return { results: [] }; + // Increased limit to 50 here as well for consistency + const sql = `SELECT full_data FROM anime WHERE full_data LIKE ? LIMIT 50`; + return new Promise((resolve) => { + db.all(sql, [`%${query}%`], (err, rows) => { + if (err) resolve({ results: [] }); + else { + try { + const results = rows.map(row => JSON.parse(row.full_data)); + const cleanResults = results.filter(anime => { + const q = query.toLowerCase(); + const titles = [ + anime.title.english, + anime.title.romaji, + anime.title.native, + ...(anime.synonyms || []) + ].filter(Boolean).map(t => t.toLowerCase()); + return titles.some(t => t.includes(q)); + }); + resolve({ results: cleanResults.slice(0, 10) }); + } catch (e) { + resolve({ results: [] }); + } + } + }); + }); +}); + +fastify.get('/api/trending', (req, reply) => { + return new Promise((resolve) => db.all("SELECT full_data FROM trending ORDER BY rank ASC LIMIT 10", [], (err, rows) => resolve({ results: rows ? rows.map(r => JSON.parse(r.full_data)) : [] }))); +}); + +fastify.get('/api/top-airing', (req, reply) => { + return new Promise((resolve) => db.all("SELECT full_data FROM top_airing ORDER BY rank ASC LIMIT 10", [], (err, rows) => resolve({ results: rows ? rows.map(r => JSON.parse(r.full_data)) : [] }))); +}); + +const start = async () => { + try { + await fastify.listen({ port: 3000, host: '0.0.0.0' }); + console.log(`Server running at http://localhost:3000`); + animeMetadata(); + } catch (err) { + fastify.log.error(err); + process.exit(1); + } +}; + +start(); \ No newline at end of file diff --git a/src/metadata/anilist.js b/src/metadata/anilist.js new file mode 100644 index 0000000..44ab6ed --- /dev/null +++ b/src/metadata/anilist.js @@ -0,0 +1,360 @@ +const sqlite3 = require('sqlite3').verbose(); +const path = require('path'); +const fs = require('fs'); + +// --- CONFIGURATION --- +const DB_PATH = path.join(__dirname, 'anilist_anime.db'); +const REQUESTS_PER_MINUTE = 20; // 20 RPM is safe (AniList limit is 90) +const DELAY_MS = (60000 / REQUESTS_PER_MINUTE); +const FEATURED_REFRESH_RATE = 8 * 60 * 1000; // 8 Minutes + +// Ensure directory exists +const dir = path.dirname(DB_PATH); +if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); +} + +const db = new sqlite3.Database(DB_PATH); + +// --- DATABASE SETUP --- +function initDB() { + return new Promise((resolve, reject) => { + db.serialize(() => { + // 1. Anime Tables + db.run(`CREATE TABLE IF NOT EXISTS anime (id INTEGER PRIMARY KEY, title TEXT, updatedAt INTEGER, full_data JSON)`); + db.run(`CREATE TABLE IF NOT EXISTS trending (rank INTEGER PRIMARY KEY, id INTEGER, full_data JSON)`); + db.run(`CREATE TABLE IF NOT EXISTS top_airing (rank INTEGER PRIMARY KEY, id INTEGER, full_data JSON)`); + + // 2. Books Tables (Manga/LN) + db.run(`CREATE TABLE IF NOT EXISTS books (id INTEGER PRIMARY KEY, title TEXT, updatedAt INTEGER, full_data JSON)`); + db.run(`CREATE TABLE IF NOT EXISTS trending_books (rank INTEGER PRIMARY KEY, id INTEGER, full_data JSON)`); + db.run(`CREATE TABLE IF NOT EXISTS popular_books (rank INTEGER PRIMARY KEY, id INTEGER, full_data JSON)`, (err) => { + if (err) reject(err); + else resolve(); + }); + }); + }); +} + +// --- QUERIES --- + +// Exhaustive list of fields +const MEDIA_FIELDS = ` + id + idMal + title { romaji english native userPreferred } + type + format + status + description + startDate { year month day } + endDate { year month day } + season + seasonYear + seasonInt + episodes + duration + chapters + volumes + countryOfOrigin + isLicensed + source + hashtag + trailer { id site thumbnail } + updatedAt + coverImage { extraLarge large medium color } + bannerImage + genres + synonyms + averageScore + meanScore + popularity + isLocked + trending + favourites + isAdult + siteUrl + autoCreateForumThread + isRecommendationBlocked + isReviewBlocked + modNotes + + tags { + id name description category rank isGeneralSpoiler isMediaSpoiler isAdult userId + } + + relations { + edges { + relationType + node { id title { romaji } type format status } + } + } + + characters(page: 1, perPage: 25, sort: [ROLE, RELEVANCE]) { + edges { + role + name + voiceActors(language: JAPANESE, sort: [RELEVANCE, ID]) { id name { full } } + node { id name { full } image { large } } + } + } + + staff(page: 1, perPage: 10, sort: [RELEVANCE, ID]) { + edges { + role + node { id name { full } image { large } } + } + } + + studios { + edges { + isMain + node { id name isAnimationStudio } + } + } + + nextAiringEpisode { airingAt timeUntilAiring episode } + + airingSchedule(notYetAired: true, perPage: 1) { + nodes { airingAt timeUntilAiring episode } + } + + externalLinks { + id url site type language color icon notes + } + + streamingEpisodes { + title thumbnail url site + } + + rankings { + id rank type format year season allTime context + } + + stats { + scoreDistribution { score amount } + statusDistribution { status amount } + } + + recommendations(perPage: 7, sort: RATING_DESC) { + nodes { + mediaRecommendation { + id + title { romaji } + coverImage { medium } + format + type + } + } + } +`; + +const BULK_QUERY = ` +query ($page: Int, $type: MediaType) { + Page(page: $page, perPage: 50) { + pageInfo { total currentPage lastPage hasNextPage } + media(type: $type, sort: ID) { + ${MEDIA_FIELDS} + } + } +} +`; + +const FEATURED_QUERY = ` +query ($sort: [MediaSort], $type: MediaType, $status: MediaStatus) { + Page(page: 1, perPage: 20) { + media(type: $type, sort: $sort, status: $status, isAdult: false) { + ${MEDIA_FIELDS} + } + } +} +`; + +// --- NETWORK HELPERS --- +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +async function fetchGraphQL(query, variables) { + try { + const response = await fetch('https://graphql.anilist.co', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, + body: JSON.stringify({ query, variables }) + }); + + const remaining = response.headers.get('X-RateLimit-Remaining'); + const resetTime = response.headers.get('X-RateLimit-Reset'); + + if (remaining && parseInt(remaining) < 10) { + const now = Math.floor(Date.now() / 1000); + const waitSeconds = resetTime ? (parseInt(resetTime) - now) + 2 : 60; + console.warn(`āš ļø Rate Limit Approaching! Sleeping for ${waitSeconds} seconds...`); + await sleep(waitSeconds * 1000); + } + + if (!response.ok) { + if (response.status === 429) { + console.log("Hit absolute rate limit. Sleeping 1 minute..."); + await sleep(60000); + return fetchGraphQL(query, variables); + } + return null; + } + + const json = await response.json(); + return json.data ? json.data.Page : null; + + } catch (error) { + console.error("Fetch Error:", error.message); + return null; + } +} + +// --- FUNCTIONS --- + +function saveMediaBatch(tableName, mediaList) { + return new Promise((resolve, reject) => { + const stmt = db.prepare(` + INSERT INTO ${tableName} (id, title, updatedAt, full_data) + VALUES (?, ?, ?, ?) + ON CONFLICT(id) DO UPDATE SET + title=excluded.title, updatedAt=excluded.updatedAt, full_data=excluded.full_data + WHERE updatedAt < excluded.updatedAt OR title != excluded.title + `); + + db.serialize(() => { + db.run("BEGIN TRANSACTION"); + mediaList.forEach(media => { + const title = media.title.english || media.title.romaji || "Unknown"; + stmt.run(media.id, title, media.updatedAt, JSON.stringify(media)); + }); + db.run("COMMIT", (err) => { + stmt.finalize(); + if (err) reject(err); else resolve(mediaList.length); + }); + }); + }); +} + +function updateFeaturedTable(tableName, mediaList) { + return new Promise((resolve, reject) => { + db.serialize(() => { + db.run(`DELETE FROM ${tableName}`); + const stmt = db.prepare(`INSERT INTO ${tableName} (rank, id, full_data) VALUES (?, ?, ?)`); + + db.run("BEGIN TRANSACTION"); + mediaList.forEach((media, index) => { + stmt.run(index + 1, media.id, JSON.stringify(media)); + }); + db.run("COMMIT", (err) => { + stmt.finalize(); + if (err) reject(err); else resolve(); + }); + }); + }); +} + +function getLocalCount(tableName) { + return new Promise((resolve) => db.get(`SELECT COUNT(*) as count FROM ${tableName}`, (err, row) => resolve(row ? row.count : 0))); +} + +// --- LOOPS --- + +async function startFeaturedLoop() { + console.log(`✨ Starting Featured Content Loop (Refreshes every ${FEATURED_REFRESH_RATE / 60000} mins)`); + + const runUpdate = async () => { + console.log("šŸ”„ Refreshing Featured tables (Anime & Books)..."); + + // 1. Anime Trending + const animeTrending = await fetchGraphQL(FEATURED_QUERY, { sort: "TRENDING_DESC", type: "ANIME" }); + if (animeTrending && animeTrending.media) { + await updateFeaturedTable('trending', animeTrending.media); + console.log(` āœ… Updated Anime Trending.`); + } + + // 2. Anime Top Airing + const animeTop = await fetchGraphQL(FEATURED_QUERY, { sort: "SCORE_DESC", type: "ANIME", status: "RELEASING" }); + if (animeTop && animeTop.media) { + await updateFeaturedTable('top_airing', animeTop.media); + console.log(` āœ… Updated Anime Top Airing.`); + } + + // 3. Books Trending + const mangaTrending = await fetchGraphQL(FEATURED_QUERY, { sort: "TRENDING_DESC", type: "MANGA" }); + if (mangaTrending && mangaTrending.media) { + await updateFeaturedTable('trending_books', mangaTrending.media); + console.log(` āœ… Updated Books Trending.`); + } + + // 4. Books Popular + const mangaPop = await fetchGraphQL(FEATURED_QUERY, { sort: "POPULARITY_DESC", type: "MANGA" }); + if (mangaPop && mangaPop.media) { + await updateFeaturedTable('popular_books', mangaPop.media); + console.log(` āœ… Updated Books Popular.`); + } + }; + + await runUpdate(); + setInterval(runUpdate, FEATURED_REFRESH_RATE); +} + +async function startScraper(type, tableName) { + let page = 1; + let isCaughtUp = false; + + console.log(`šŸš€ Starting ${type} Scraper (Table: ${tableName})...`); + + while (true) { + if (isCaughtUp) { + console.log(`šŸ’¤ ${type} DB caught up. Sleeping 10 mins...`); + await sleep(10 * 60 * 1000); + console.log(`ā° Waking up ${type} scraper...`); + page = 1; + isCaughtUp = false; + } + + const data = await fetchGraphQL(BULK_QUERY, { page: page, type: type }); + + if (!data || !data.media || data.media.length === 0) { + if (data && data.pageInfo && !data.pageInfo.hasNextPage) { + console.log(`\nšŸŽ‰ ${type} Scraper reached the end!`); + isCaughtUp = true; + } else { + await sleep(5000); + } + continue; + } + + await saveMediaBatch(tableName, data.media); + const totalInDb = await getLocalCount(tableName); + const percent = data.pageInfo.total ? ((page * 50 / data.pageInfo.total) * 100).toFixed(2) : "??"; + + process.stdout.write(`\ršŸ“„ ${type}: Page ${data.pageInfo.currentPage} | DB Total: ${totalInDb} | ~${percent}%`); + + if (data.pageInfo.hasNextPage) { + page++; + await sleep(DELAY_MS); + } else { + console.log(`\nšŸŽ‰ ${type} Scraper reached the end!`); + isCaughtUp = true; + } + } +} + +// --- MAIN ENTRY --- +async function animeMetadata() { + await initDB(); + + // Start loops + startFeaturedLoop(); + startScraper('ANIME', 'anime'); + startScraper('MANGA', 'books'); +} + +if (require.main === module) { + animeMetadata(); +} + +module.exports = { animeMetadata }; \ No newline at end of file diff --git a/src/metadata/anilist_anime.db b/src/metadata/anilist_anime.db new file mode 100644 index 0000000..434162b Binary files /dev/null and b/src/metadata/anilist_anime.db differ diff --git a/views/anime.html b/views/anime.html new file mode 100644 index 0000000..71406f9 --- /dev/null +++ b/views/anime.html @@ -0,0 +1,485 @@ + + + + + + StreamFlow + + + + + + + + + + + + Back to Home + + + +
+
+
+
+
+
+ + +
+ + + + + +
+
+

Loading...

+ +
+
--% Score
+
----
+
Action
+
+ +
+ + +
+
+ + +
+
+ +
+ +
+ +
+
+

Episodes

+
+
+ +
+
+ +
+ + +
+ + Page 1 of 1 + +
+
+
+ +
+ + + + \ No newline at end of file diff --git a/views/book.html b/views/book.html new file mode 100644 index 0000000..dfa86bf --- /dev/null +++ b/views/book.html @@ -0,0 +1,114 @@ + + + + + + + StreamFlow Book + + + + + + + + Back to Books + + + +
+
+ +
+
+
+ +
+ + + + + +
+
+

Loading...

+ +
+
--% Score
+
Action
+
+ +
+ + +
+
+ +
+
+

Chapters

+
+ + +
+
+ +
+ + + + + + + + + + + + + +
#TitleProviderAction
+
+ + + +
+
+ +
+ + + + \ No newline at end of file diff --git a/views/books.html b/views/books.html new file mode 100644 index 0000000..718f714 --- /dev/null +++ b/views/books.html @@ -0,0 +1,82 @@ + + + + + + StreamFlow Books + + + + + + + + +
+
+ +
+
+ +
+
+ +
+
+

Loading...

+
+ + + +
+

+
+ + +
+
+
+
+ +
+
+
Trending Books
+ +
+ +
+
All Time Popular
+ +
+
+ + + \ No newline at end of file diff --git a/views/index.html b/views/index.html new file mode 100644 index 0000000..2525019 --- /dev/null +++ b/views/index.html @@ -0,0 +1,322 @@ + + + + + + StreamFlow + + + + + + + +
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
+ +
+ +
+
Trending This Season
+ +
+ + +
+
Top Airing Now
+ +
+
+ + + + \ No newline at end of file diff --git a/views/watch.html b/views/watch.html new file mode 100644 index 0000000..7a8d28f --- /dev/null +++ b/views/watch.html @@ -0,0 +1,296 @@ + + + + + + + StreamFlow Watch + + + + + + + + +
+ + + Back to Series + +
+ +
+ + +
+ + + + + +
+ + +
+ +
+
+ Select a source... +
+
+ + +
+
+

Loading...

+ Episode -- +
+ + +
+
+ + + + \ No newline at end of file