{"_id":"sonolus-express","_rev":"55-edff0ee5a0a05fc81914a874dd3ebbf9","name":"sonolus-express","dist-tags":{"latest":"6.3.1","beta":"6.3.0","alpha":"6.4.0-alpha.5"},"versions":{"0.1.0":{"name":"sonolus-express","version":"0.1.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -p .","lint":"eslint --ext .ts src/"},"devDependencies":{"@types/express":"^4.17.12","@types/node":"^15.12.4","@typescript-eslint/eslint-plugin":"^4.27.0","@typescript-eslint/parser":"^4.27.0","eslint":"^7.28.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0","prettier":"^2.3.1","typescript":"^4.3.4"},"dependencies":{"ajv":"^8.6.0","express":"^4.17.1"},"gitHead":"6874aba6b5fc6664b3ee20a7b72a22d89c1fda29","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@0.1.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-y1awLv+wlsHZD60mudLv084ix1XIxlWlQiqsrJ8uhiNTwTGG13PBFFT38D2tz7676nsZbTGRv+YCo4SGhPOw+w==","shasum":"15225b5336e6b9ace82b8597292842ceab66f849","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-0.1.0.tgz","fileCount":101,"unpackedSize":71202,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg1iRXCRA9TVsSAnZWagAARgEP/3RNjtsomqJc/yq3Ccxy\n5rtMuOlgSdsrQcOAG+MGsE3pa1WnYI5quNysA3QIui8pMiWlKrwTbeScWwv+\nahZaJcITCGoDVQvcfzldRi4+tQN/FhFI3FGWE/ZRSIpo/Z709BHdVwTbm4mV\neDrMmK6oCi4jAnRTLUQ1GJUUtvHsl86eMYtaPneT3kM8GA+bZYn19szqm2AY\nVJZfdmPzVf4E9XOGWvWe9f9JYIPIIQI2tUx78K1rzgUwKFxd/YZrBt4rHpaK\nEALDmbDhIUTnoD7lHHnUajMtdYhqo1t6emlltYS7qsXOy2f/flTEtOKhKVho\nOvGhBkkTq+t8I9ET5iBd2isBHU7L7/OGTtUsatuvOT87Da8pEHLbJB/tObiE\n44VhbxV/fxpPdi3ssaBdzpES84Ke+n4+kyV5sUuObeF53fddM7vVARMU7tsG\nnivKNJemu2NPu0NKDAfzQWl4DI0B2mxIlriGoVVJTpEPHjoHfyzlqAiH+BXW\ngE1hDqP65chv0TMbOgQE1JkQM14WqmPhoq9edBj3P6zh3KG8IrO0Xbw//AWw\nZIgx5MO2m6fN2qYZtziBTvbOuh9csQG4/280cbAAJwNCPPj/LcG5TR2a3P53\nxmZ0lY2XVH4ffT9KSmm6xxQOEJ1ruucNNqMC5p1wGetJh6y/TmwlOQDJ/8u/\nI846\r\n=Wr5r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBNgVp52BxmZdQsU8xHMh3jO8eHsZloLxHaiStMm/IVwIhAIwlIFq6ISgdXd7K6z7HZEOjAHz126BwsCMK1LfLOD4P"}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_0.1.0_1624646743342_0.8362747375175013"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"0.1.1":{"name":"sonolus-express","version":"0.1.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -p .","lint":"eslint --ext .ts src/"},"devDependencies":{"@types/express":"^4.17.12","@types/node":"^15.12.4","@typescript-eslint/eslint-plugin":"^4.27.0","@typescript-eslint/parser":"^4.27.0","eslint":"^7.28.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0","prettier":"^2.3.1","typescript":"^4.3.4"},"dependencies":{"ajv":"^8.6.0","express":"^4.17.1"},"gitHead":"c63f6f4abc76a405c2d4ea695b6ee5ee9f2debf1","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@0.1.1","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-us/6Jr+BnaTO/7IjEmJG1GNs/pdIlgEHWSvYJiNN5tz0oTfay+weU+Qd7Jx+ACnvTuywT7er02RWBAuqyHBbkQ==","shasum":"85f913474700da1c3d3c9835ca289ab9cc3de188","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-0.1.1.tgz","fileCount":101,"unpackedSize":71523,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg1wsZCRA9TVsSAnZWagAA6hoQAJWk+KF3uCvpKuzugoOE\nkkcvkYtPqS4eZFV9VnoszrFzQz8PuSizd3yRsqt0xNE401CA02qEDt5uEqLG\nSamm+fQQcl0vDMHdatRSRNWj3vcgb1P/ggwS0IPnXLUC8yd3LloYo5oqgENO\nanWu+G22RiqjQHzXm3VRwLhMwi92rsx9fY8XYNOAK3KqaHUtMAbaKQ+8EvZ/\npiRaxgohe+7GhiQsuz2wjN8Qpc9ON3wb4wl+BFU91AdfRrNPFS9hxFdsGtiM\nrt2wrqBIiXHp0KiGTKqw/mY90tA4EsYexomIpOI9JD5ey7KfXDX0KTaUi/xM\nAteueGCMNSGiM1ID/0iVgG6rado5bV6EyB95R8JL0zQ4UaUYiHK0S4ZjYfBM\nqx/gH+hQCYpF38LNEr4bLOdsLH24z36Bjv1/LPz0F+zfu4cb4VLv6ScCo1r4\n03fJRkEFoJadNwfqH4kATlRPiC8gaWXtzNEdSCT/qlkPYiPjelTlbS9n8kV0\nWetcDJQySzcGSEf6VPc3auM2YAQxwWfIbPhsue7D79YdCgMoLXZuZ848KZrt\nIfH19Z9AIIdOW4xnWpTzZ1Yev+3ZCQkR0yS+TYj0zuZhFcYgMk8M7fapoHTD\nO6UCvkJ7Npg79rnPZcH+dj3klFIZ+R4zNUAcHe6XZXdZYhXl6E5lAeJy2G0k\nUwkn\r\n=aj2a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDiNZ+NPvi6raeY1ZMJEfQ/9ODRyOVLmILcQPermpPtNAIgagVDoHrRtcRXhKDnkeS7Q58g9GmHoY+QD2Dq/wEv4QM="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_0.1.1_1624705817238_0.3288165439726791"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"0.1.2":{"name":"sonolus-express","version":"0.1.2","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -p .","lint":"eslint --ext .ts src/"},"devDependencies":{"@types/express":"^4.17.12","@types/node":"^15.12.4","@typescript-eslint/eslint-plugin":"^4.27.0","@typescript-eslint/parser":"^4.27.0","eslint":"^7.28.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0","prettier":"^2.3.1","typescript":"^4.3.4"},"dependencies":{"ajv":"^8.6.0","express":"^4.17.1"},"gitHead":"cb07a668062148c13bad24d01d7483dfc3450e14","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@0.1.2","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-PDVgivkoi6hIAxi1/2eRt4UhlJ+msTenFOugvfKbYV8VHH9BMJzSbnKHCwgBJ2zcAyqbXot9YVs93PsA37AYrg==","shasum":"ce9a0794daad9a806a9fdbb9855abb34eb044c6c","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-0.1.2.tgz","fileCount":101,"unpackedSize":71604,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg13smCRA9TVsSAnZWagAADTQP/AxusDMRpPAWdr7pXCxX\n74FfiBeHD4nCICT2fSmDbgngN7fKNXPw/zNoZfgwO7sLphhToHgpoHyjlkUK\nfSSTyWN/Mzv1nYpgioSTmlw5IZWnF0kkJ3gf7W6vnPeJeTOlgxP6P9RTdGbo\niPW/tkxtdgXbciwTIWJP5gTwH88eontPYv4sxWtX2kpnEYix4b9avtq5sIw9\nbdeYxTiOF/Loe82Ak9oV71RGEyTaWY+FwUji9b4hgjN8PBcUK9805O7jhcz9\n29YAjjkGnKcqzqhKecI/OLm96YrDpg/VGNkTtpq2A6ThMhXBuFlN2kWP0kN4\nMSihqcs1FUdsENJgYg+vAbPATbzP0Db0FnFg1NbHahG9gnLdMv3KxJgvRuXD\nsOGWbOHpLnfFzKkyYdTgXjFfakEJzSitz4Aelsw2sEyQ/piDnIkTes5OcSc/\nnPkMED+tEKbvTPHBJErIhHNbYhESbRlqx/BZDEf4Lc1mcQJEHy7MsGVaQsZA\nFTxX1mjeJVCFwOdOKBuVGvbjWFP4dk74qHzHtpRjdwcIUQ3HNPQCiNKG5ynu\n/NRNfp5am1l/hOwlQz9iaXdhGTTXnRdsMuxl1BB98IFduK5QBwfpmvBOAsyK\nZSqLs11ZK3Iq7Zme4I6RcEak4aulTQ2huv0UyBiomViqdSGT0gkytXUhGqzi\n2WL+\r\n=T0xb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDeymc0dYqfL1m2OboB4GfB7lQA5zZr5XfM/ER3w/AkVAIhAPXyjUSh/1LaWRXPzGZi/Vvrsy7Ebb8jbZy7pKVlteyz"}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_0.1.2_1624734502271_0.13688896249086557"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"0.1.3":{"name":"sonolus-express","version":"0.1.3","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -p .","lint":"eslint --ext .ts src/"},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.4.3","@typescript-eslint/eslint-plugin":"^4.28.5","@typescript-eslint/parser":"^4.28.5","eslint":"^7.31.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0","prettier":"^2.3.2","typescript":"^4.3.5"},"dependencies":{"ajv":"^8.6.2"},"peerDependencies":{"express":"^4.17.1"},"gitHead":"131afb4717a5d79e47ca7269ffa06d60a3ea62f0","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@0.1.3","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-v2YyaLxtNyi0vDJAj+1yuZUXgcAZ5ibXVPULPFFlSoxv2HK6068oyg0v3r+fuLSbpuxY1KxKvPaWOUNVFumxXg==","shasum":"4e6e3ab81c0a37955b91665328a8bdccb9b9e27c","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-0.1.3.tgz","fileCount":101,"unpackedSize":71631,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg/5d4CRA9TVsSAnZWagAA3r0P/AnFklwmKucX4cjPRH2b\n98euq0jIR82Py6kVEc+AbZMjByQYVm6c0bI4zBgMnYjGFETDdfR6gX1jT6HO\n/5IPyucY0e2wr2Y+3k55TVJm3e/GO1U6UyAuODHSoNjIAlpK4UnzycSsLjZU\nEhL+Kcnk/nhdgyud4UZnymVst+D+T9CMLfZXNt5IZtPIQBMlvp8PPAZIuU7s\nQakfn6aXYR5KgcfgeqKVX27BQeMNS0b3Vmsypw5rsG3Q8N6r1LuQYN1RPw4J\nIoKIM9JWyiD1Ipt4Vd4xXU580QVsNFJCeXi+VbfqE7Vt7WDC1PtsKxKJA1ST\nJsLwZXcHS03EH3sP9kbhBB/KuDQnF4SpDggr5DHvVl1sGjm+Q2FAbj7Qybxe\n0HQEswCzMmtWOo6YwdeYzeKPNrHFkVHP0meqrgzaAb7c0tAVl8tktJw3tszO\n5gL+drXj9AMxyh0qpWNj2Jsc6hbRqma7qNswW65LJC18cxP3tJ82dHAUkaUC\n8tj9ENFt+4TvO1mC7ynvPMBNNvts9lXbwJlLSBNLmwunLQYBTXHFYuc7hCqX\nVjghiE7z0BEydRabWpQcPqGiRQ0DL5lgG+2Zzq6g5UmOjYuzo6uPYgfxrwq4\nZdoCAjEufiYzHwBZgO4HhdMSdulvQfQgnweF6YTUJbLyCu9dXEPIlG0uzeXE\n/bHr\r\n=Tuye\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2ZfeRGm3jqYvLC/MWJ3af/xAOEKNDmwVc7AfgST7LfQIgL8S340ruo28jZBvEikmzJvPfp943dwzELFe6W09uJJY="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_0.1.3_1627363192120_0.5181586023776692"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"0.1.4":{"name":"sonolus-express","version":"0.1.4","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"build":"tsc -p .","lint":"eslint --ext .ts src/"},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.4.3","@typescript-eslint/eslint-plugin":"^4.28.5","@typescript-eslint/parser":"^4.28.5","eslint":"^7.31.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0","prettier":"^2.3.2","typescript":"^4.3.5"},"dependencies":{"ajv":"^8.6.2"},"peerDependencies":{"express":"^4.17.1"},"gitHead":"4e60c2516b87ee839e785d9b38635861f8a08347","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@0.1.4","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-jdCt14h3xlGmJNWApq6TvSskLlaoQ84vsEpU365efrw0i9BJnhUN1h8DjpDfdURB8cgtopfW8lfA+O01kB4QoA==","shasum":"a7094808c28e9d0f58499b901c3405643197b117","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-0.1.4.tgz","fileCount":101,"unpackedSize":74256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg/57cCRA9TVsSAnZWagAABGMP/0tEEKrPKyje/HB/eNeg\nJvRYhiLryDS+g6HbcYvaCPrIj4fh2qkcu2vlNeQCQzfg9Gu+RKbVqDGnoPPf\nS4HuXLIK5jDBTiT4C0/MYNT+/pnAHz73mpAfg8u4NNoGvdSPrQximFxsIfcO\nWy+t9mvKnjdumlUokmrvnlAO+e5aQudMhbcaeyeB7VLlc6FnPOu04Z5+o0J3\n+n3LXMTfoyd0qvq2fhsNE5r0ZDTtug86WTjuSU2bH8Am3IcjXcYZh6IgkMCE\nYOfD0PoqVwDBBXsivzLdNuZH6+vMnpz6rLomJpMuHpnJMFAPKQVd8fZ1JbOU\nPhsu2mvGypn7VRVlp6/L14g80Kiq7ZTDVRHB0kfQu0zVxG0t5eXAZJKXhH0c\nkE0kp+ramojECT2Tkj19kh17UIknL/hGYcFisHu7hNrMeuTQgaDCfVmRBcyL\nXqUgbiM4Ch4R8QYko/S3wHHGSa0V2yOtv725hsLvy99qFtHtM/F7ntTcioW9\naGCsJSR4C/x3ZEt+i1+QWAjk/Ed/H1PpoOYsBUuTannncMve09Eu3dox7Eqx\nGySQSe5WiNLM34RUQi/VISzNU+JPqUtMaibLWpq8LMCNLAvYuYN64xrwsXwI\n5YFuRP8OqK+NUppDDvDVqzDydWiEQnnw+Edw9VgW0YRkkJNf0ifK4mTxUKa8\nVw9U\r\n=EqxE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC1DZYDUYJM41QDees5zroAWVGKqAGOwk628SzudjfAswIgbO693C74On+D8fPASipm+AC3kOVCX3Wfk5FA/m9y0fE="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_0.1.4_1627365084555_0.9496273826625006"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"0.2.0-beta.0":{"name":"sonolus-express","version":"0.2.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"build":"tsc -p .","lint":"eslint --ext .ts src/"},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.4.3","@typescript-eslint/eslint-plugin":"^4.28.5","@typescript-eslint/parser":"^4.28.5","eslint":"^7.31.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0","prettier":"^2.3.2","typescript":"^4.3.5"},"dependencies":{"ajv":"^8.6.2"},"peerDependencies":{"express":"^4.17.1"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://github.com/NonSpicyBurrito/sonolus-wiki)\n-   [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js --save\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.version`: allowed Sonolus version.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting . Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting . Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting . Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting . Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting . Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting . Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting . Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting . Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting . Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting . Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting . Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting . Defaults to `defaultEngineDetailsHandler`.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-express' {\n    interface LevelInfo {\n        meta: ... // user-defined meta information type\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"b869afb364a66fb80d7d8512e156151feb268f2f","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@0.2.0-beta.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-4MAlgneujtL6RAP0mROM3R5XV93Iaweu41fE+sws9NN5LZaPRwd5pcdKPdeG9VCgTJTT0ltKjaDNgTWmOqWTBA==","shasum":"cc58480653728a13255c6145c2146ecd66494f15","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-0.2.0-beta.0.tgz","fileCount":101,"unpackedSize":74510,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhDo6PCRA9TVsSAnZWagAAXUAP/iDwIjo7E5r2ga3XoKTa\nYh+FseoKg+C6j9cW/tGK/CaKC3vYF+tu4X766uZaxGmXLD1n4JbtldnA27wB\nGTmij5ySfF+PQai8ScNIxBL/yBAKl0YJyYBPzlUpndx120z+PVXp3ujlSAt5\n9WIlczf4u+/d/+gHIF7tix2yiQsdcSlXpzfS8C1K9j8bArGjm9aXxeiKQ+g3\ncjuG1CjrrISrltam5uCrHhHQiUtbDZTUAgcDVMO0uXQMCrnezhDEFBsW3e+8\n6UG7XbEG9lpjYZynDj33glIiZ/xolJRZlO0r79g8dIKCo9FrPujdEr0yrUvB\nIbcOi3yR7UAOoXW/gUZOOP9iVDgIm2BmdkmMyLHKwmdhxrevaI+XJjNBHm59\nE+ZfhTMqoI6xdgWM6scB8GtaWTGIL7QFXSFHK6GgqamqEbJe2sHAZaSO5Ylf\n4OJd0wbHThuz5soKkMKSRTlSlfcHJzVBPLAlbnppOPayPANDFQ+Poyz4NY37\nnF2Om2Rnsaq/v3bvnnn9/mOBF+dsWAA6rtaKqKVZpJ0dZBXNETkqtjFGtRK6\nElBHdvClC85JGnluBGT3Bd0TyaK9hsXAIPADXC7iRYPr7EOBwRkzMiMgOQuG\nSLU69ThpJssWRUeqjE+Wo9lnmbsg8yLOnJRXonnMeOFoDZa+oa0LrbTfRvfA\nM3RA\r\n=Gw3S\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFAHQs/diC3cKceXaVLYMDQ5WQlRa9m2KrzKwJPgICpmAiEAmYkx6lCjHB6C5/Xkd1WsrNNihApTFoclt8sPoFh10jA="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_0.2.0-beta.0_1628343950935_0.2406596688681646"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"0.2.0":{"name":"sonolus-express","version":"0.2.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"build":"tsc -p .","lint":"eslint --ext .ts src/"},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.4.3","@typescript-eslint/eslint-plugin":"^4.28.5","@typescript-eslint/parser":"^4.28.5","eslint":"^7.31.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0","prettier":"^2.3.2","typescript":"^4.3.5"},"dependencies":{"ajv":"^8.6.2"},"peerDependencies":{"express":"^4.17.1"},"gitHead":"a2fca2be158a6e8409a4499aefe7acf7bceb79f5","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@0.2.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-buBtXHJ/WIoM0aaPycGombnoR1uqqtXDv4bWXUY/9vfFnOT5ND9kgbL4n/KZe6/BqEcvZydYa4EOQ26KcXLgmw==","shasum":"be4af57bc5c8bb0fe65af4a382fa25278d07da75","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-0.2.0.tgz","fileCount":101,"unpackedSize":74503,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhECoMCRA9TVsSAnZWagAAZWMP/2xP7ocmZ+2K3CU84SK1\nWEUEN4Q5ytafi7OGoAsvQpBKUddlQj/t6+PesGUfAbxI8K6QXwPykYPV1TIz\nRqjY8dL5+/iTq/cjqZSxiR5MzfIZnwJdohxLMMGLxOJBlEaoNvkQrFJDilXK\nQ2N+dNRg77q2jOQ85JkkKB5FOnInfnIu4yI5YpQlvuB2+dkExQeZ/2Dl65nD\nSZ+UrEuuXwzJFH3myltOWVfGuWyrCWVCmYo0RFJHIrhcdWJ+ihCgT3CkjSGw\nG7JbsZlr3P4b1dIEynrLhCdjjK3GJVA9zWiC6eyKKRuHRZhrmjtdBIbb/XRW\nTVv2d8XJkTBEcsQbcBdYLOAUUGx1UDc116l4/qQi5x32KSW+9yBP9Fh5z8OK\nFiJl0t1rxWNDp+RaK5l4TiDv0HtVg56aUocfpSEJ0k22hbiOneoTGoiLWUln\nPa002ezx8Pt1lmLozWpKUihu+RqUe/QZzWD6gNuUrP6ViyJW6BOLy2n65AN0\nLnkQ/RskrqSk0gkk+dTf3rIAfbMdk7FbqVaRP/YKeoRfjvIP3UgpeW78ZK3R\nPKRL/ZxRjHzm6cZlUinq5BY7nSwcyks6hRvK7sy0R4qTrQvMXYI22QN+c9fP\nWv8CUlGJflEobspDXV9SDTMmZofg3QvFpZiCJcNy8g+sjfpxIR/SF0cVUcgv\nDiyV\r\n=KyVJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDX6J9PMzyyrbY6M/OnHNdVKJozaGqpWMEz3eCj1ZqOKwIgXmaTtI2yC12sPSHUgE/LL5disa9TUK3c5sUsOTdWJ8Q="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_0.2.0_1628449292728_0.23984069085666548"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"1.0.0":{"name":"sonolus-express","version":"1.0.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.10.1","@typescript-eslint/eslint-plugin":"^4.32.0","@typescript-eslint/parser":"^4.32.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.4.1","typescript":"^4.4.3"},"peerDependencies":{"express":"^4.17.1","sonolus-core":"~0.1.0"},"dependencies":{"express":"^4.17.1","sonolus-core":"^0.1.0","zod":"^3.9.4"},"gitHead":"558e3bbc892fe5fc19bb80e1092dc7444a278be7","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@1.0.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-ufl20S/7Dk2kOgXFSvqbflTDT2m7QrkHPmlL20YIqVcaVD5itI9VS3B8HJcvcwI7t86OU4z6tAZRHQbRVbaAfQ==","shasum":"93ef15bc65a105468c888c72ba6c2cd9374bb8cc","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-1.0.0.tgz","fileCount":103,"unpackedSize":88830,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEz6ZWqXcDFmmyP0vdzFtylHy4lTGFJWEQmx79JAPm5bAiBSFqSGTZSiGr2WdLEQAnz3iiF/F0kRuU6VvloaAwB+vA=="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_1.0.0_1632889825104_0.9042883879488597"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"1.1.0":{"name":"sonolus-express","version":"1.1.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/NonSpicyBurrito/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.10.2","@typescript-eslint/eslint-plugin":"^4.32.0","@typescript-eslint/parser":"^4.32.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.4.1","typescript":"^4.4.3"},"peerDependencies":{"express":"^4.17.1","sonolus-core":"~0.2.0"},"dependencies":{"express":"^4.17.1","sonolus-core":"~0.2.0","zod":"^3.9.5"},"gitHead":"e739c5d211f02290f304657f341df7cad0f4110f","bugs":{"url":"https://github.com/NonSpicyBurrito/sonolus-express/issues"},"homepage":"https://github.com/NonSpicyBurrito/sonolus-express#readme","_id":"sonolus-express@1.1.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-FmeXVGYaqYz300IqI/QhsAVTfimm5JwFLv3eoQw8FE60siobcKRj4np8Miafmlrd0SsktAB15yeMsfcCrhcN9A==","shasum":"23d5c4dbe99b454cdd868dbf7434719bf781a724","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-1.1.0.tgz","fileCount":103,"unpackedSize":88830,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCAGIs1KVdASODXt2fw28x/Y+1qHAxZeNLlyVtEMloM4AIhAKelp4fpYz9JeZMeeo3lAnWRdTo0qbrrtBmyenMeRkq4"}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_1.1.0_1633074668592_0.9679471647915769"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"1.1.1":{"name":"sonolus-express","version":"1.1.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.10.2","@typescript-eslint/eslint-plugin":"^4.32.0","@typescript-eslint/parser":"^4.32.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.4.1","typescript":"^4.4.3"},"peerDependencies":{"express":"^4.17.1","sonolus-core":"~0.2.0"},"dependencies":{"express":"^4.17.1","sonolus-core":"~0.2.0","zod":"^3.9.5"},"gitHead":"063e7b98f7d0c8dc0f20e7041ef196c0632c1531","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@1.1.1","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-kmVuPf2/G/GYRTdmXwdvLAMkj5hzCUuJszrX2opfII5BxCNwFaLJlBJA09zrsgXWpTcAkHxk6FXbMp0Uvw7Tlg==","shasum":"62910034c3fb936ed76261ae322e2d534c864532","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-1.1.1.tgz","fileCount":103,"unpackedSize":88931,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh27meCRA9TVsSAnZWagAA5M0P/0SnNQ+RpypAfPMW8S+G\noXxvxbvlka362DGeQK83akwFKdMhtvOQ7FfObu8xsobZUbYU+qA7TO91ZiAz\nALxp68LMIOMxWI3uyhzrtokTwaShQakKJqWII8Weq9KrmoIZ8ZA+4T9bFDD1\n1EVXq4w0EeHw4Zm3B1pdOKGKZXU0AZL5D8mGd+vzDv6YqkI+yO4Qpe9E8XsC\nR3mVVTY9+zpjAeTtTCGOH9GVQRHrXUPophYEMWDMTaP7eHG2na0arQ1VyuD2\nm4a6LwA75AtzdxPnSx12Ea3+/RcWYJ7d37fcOl4je9Rbf0E3BXV50tj1LKHY\nOuoYmOvxgr90Qx2rAEPlSskZ612fXwwyrbDacXZXYxCT/GkKP0lPjL3NL0tZ\n1r041MmcbnqWvwPfjRL5hrDOgCMCoLToG+G1F8K3m55jh8RWvcFfX3XRvO1h\nTgbQIpCyvVGH29wG5Z8Q3Z9Dk0gYTfPaWhUlkd4nk1O9z9SSwlZwlUU1q7qF\nQSWzd3vUcAspIu+gC1hW8c4XT4y7rfzOprhnMjF2fDpsMjSJIwi5FCvkGKR4\ne0s/1SM4Lpbv4tGdevLyYMkqhAqdyBaECi/RsetjrnUlMYsiNpSTk4/gc+dN\nlbGxIarWOpFyq84Ek2YiH46YynQ9BTK6LjsqPsGVcjKhBwS5q8TZ613YBPZ0\nliug\r\n=fkd2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH2b8z/BcbObMkJ8ppujVeEcFZlWbwBqgawh4g8BaH13AiEA29ZlS33rl+FAOPG9Tl/45+QW+gQj5zFfw5zwPorfAIM="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_1.1.1_1636638797447_0.19531944716841565"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.0.0-beta.0":{"name":"sonolus-express","version":"2.0.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.10.2","@typescript-eslint/eslint-plugin":"^4.32.0","@typescript-eslint/parser":"^4.32.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.4.1","typescript":"^4.4.3"},"peerDependencies":{"express":"^4.17.1","sonolus-core":"~1.0.0-beta.0"},"dependencies":{"express":"^4.17.1","sonolus-core":"~1.0.0-beta.0","zod":"^3.9.5"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.version`: allowed Sonolus version.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-express' {\n    interface LevelInfo {\n        meta: ... // user-defined meta information type\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"0bf5e74fa3a89884d8317f261e4edf4fff79d57b","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.0.0-beta.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-kvhSdWqh0NIqfB9tfzDI5Pmt8Mb+TwIlnEIGGks8kTTaVIHdBQ9C6Q744GEoLanCWoIJ8M5WpD/acGyLHBuL4A==","shasum":"395cf4c5b4b4bc5edbdaaab1e020b001a429e737","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.0.0-beta.0.tgz","fileCount":121,"unpackedSize":115932,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh6UCxCRA9TVsSAnZWagAAXjMQAJW+dehdc2l/WpS9b1lT\n7v3wSIJxRUBpBKwkV1e7tMRhn4QiTIRrGtJ2PeGA7CiFUP3N5EBoTtLdOi2N\ndwDHZFj6PEVVI/trGECGoBReTrUmXVnSeZF0m5lf6vnqnvc1kD9agKMjVnp4\n4GhwRd11qeVqV/xD/OTPQTViOINQgJLQQhAHsCUfuv4ljnGUo3SXMpNewHjl\n4AlIiiXyJDuMW3DfMP2ePurfZkmiI23iwYz9RZpUWLOJqW0DLIQ5eKxdMRFe\nVYI2SEcPA2efjT+Bjf0ZYwRY+tAiqBy74KvbUYP3rECwJVT/NhvxxpyoM9H4\niKnHNC4eRN35Emj5+EOGfOktf/NLCMzoXoF/DRzG9gdWoi5YmW+d9Hyn3cFn\nhRoLWe7+Ikr4nUBF34F3wuq3ukB2Ssyc0s4yCNZB1LHhj7QCuobcSRQZbw0+\njKDpb5R1jwhiaCkgy1D531hZDsbugtPX9JqS3jl9Ul50U73XXoM26UYg+hAu\nfbdPdJy+HlCKAKni0B6K56zPDeviyCBXU9T9HIulMHzToOUDO8ZHc6WYnJER\n8M2fO02kfdTYgudJHPyHR6lLpS3flWLkkt8ZBeOCLuFOR+CvlKqeWUV515lZ\nKmKXG9ajho383gGAfW1JBmAEZwVWWnJqGHMBghkLIV6G7ehYqA3wFC6mQWUr\nmAg8\r\n=TvFw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICalYg6yYaOy83JoTxyH0ITyIgt0gpBDrF3LXisFWbA1AiA+35pe1p2+c++2uh/PSnbCdMjZlECvrDER1FYcLtadDg=="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.0.0-beta.0_1642676401280_0.30046103802609836"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.0.0-beta.1":{"name":"sonolus-express","version":"2.0.0-beta.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.10.2","@typescript-eslint/eslint-plugin":"^4.32.0","@typescript-eslint/parser":"^4.32.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.4.1","typescript":"^4.4.3"},"peerDependencies":{"express":"^4.17.1","sonolus-core":"~1.0.0-beta.2"},"dependencies":{"express":"^4.17.1","sonolus-core":"~1.0.0-beta.2","zod":"^3.9.5"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: ... // user-defined meta information type\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"518196f40495b50cf250f133742b5cbe317f2887","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.0.0-beta.1","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-9g5y8NtnWSWxMNvXKR1lqHVzJyw931bLLyCkRpc4nNmXXw4v2W9OAotVBE7ANwyzT5DMNa7p0bti+h5rJTMErA==","shasum":"075a215b235db91834502647d982615cc33ed140","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.0.0-beta.1.tgz","fileCount":121,"unpackedSize":116017,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh63oFCRA9TVsSAnZWagAAQjIP/0ou6zzKfVxuhveke+gG\niUNNdQodOsVYNiLd9vNROxpP7qRAEO4h25lDe6KIZubI5il1YIu9SEv5ty8Y\nIpwONcbU0hMZojTs0nFrTljkcXhlc/58/TjE37P+x1f3tqxfLHbbSNbkZY4g\n8ygdXBMiGHhCkvrNupqZUwpmX+kHYwAcZctk86TTyOGNQIE0r/yCw9BCH/4R\njmmSObxnN1SYBd87yNQFE9EECOXb5pnkgRKW5ocCOL6UjQVmSKNbHBX/wvlV\nAcbWnhB8qKWpIkNMqCJFEBDk2U37bkyYJV0SD3KzyGqehlwYKU5DrWswIOpi\nPu2nCIZrUch+/lIdb0kqmXxumwwvLZZ4uCVv3geMc9fuPbbk9Kt9P12UIhLp\nq1TY8ZpwUfZimgifDm96TZWmp0/SyB1UOPmyJwI9MGfUfvLZmo9B36CEe5Hm\nF/HlKEsL0aTWi4MdihQbAslik5+NPQ5YrOXCP1WOjULVpMbm4GwWuSDYcD8h\n3tHlwgGgd4DhNae9+0IDL5i3nK40S8RJQgFlViLc3+xZLaPTmckAaXF07Z81\nZPwih7cA/srlMGSyzQAnu4+CDDFZZgaraPbj7uGb7xz7vOqHAjrr5VqA71v6\nSezaH7npNzT+VYB0+44GwXvpmZvMywP0czku3WrT1gCTSLORfXqRioepMmQU\nn84s\r\n=Re9M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCf9tyCqi2VV4hxYvWnuQuzcZXNKHlS7lbA38beTPcwYwIhAMnYBQ+1YbM3iI25oILSVGMosqgYs8kSIkIHoU6e6b34"}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.0.0-beta.1_1642822149131_0.5815776397963293"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.0.0-beta.2":{"name":"sonolus-express","version":"2.0.0-beta.2","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.10.2","@typescript-eslint/eslint-plugin":"^4.32.0","@typescript-eslint/parser":"^4.32.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.4.1","typescript":"^4.4.3"},"peerDependencies":{"express":"^4.17.1","sonolus-core":"~1.0.0-beta.2"},"dependencies":{"express":"^4.17.1","sonolus-core":"~1.0.0-beta.2","zod":"^3.9.5"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"44cb90936cdb967f4e84665b7d559ff33a285349","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.0.0-beta.2","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-mqx7Eueo7cHw8sBchqfemUMzxOPDM00MiRizLVegQO0shXz4W3Prvm3LPPemGJM3IgdcX72Cfo8hjFi/eZ75cw==","shasum":"03477258ec72fb529b49ca7ae987c4c304a8362c","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.0.0-beta.2.tgz","fileCount":121,"unpackedSize":119921,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh64mMCRA9TVsSAnZWagAA0gwP/iP60paa4IR5kzoB3hG/\ntMpC07MIJA5LVPgPZQGjq8yvMw07JKNBws+LRGGx/Aej4r2X2ssiz0bJnIGm\nL2fIQ0Ijt/jXdxPCRNCqKdY3xCe69Uw5K4x9Mo3fhQKmIuOojCyz99KejYmd\n4ZF3rqhHvMgUHcurrzGYjrVJoTsyG2pNz0cvZiivCMRbnJojoJsu2YWdJkXL\n4vjAlCRC6ExTnQvEcYm6Hoe/uXbdKsfRjITL7XoxUXe4K69seliYOYP9yZro\nAXmoIhoD6Xy1/vp29TM5MQMoAR64I+wxI5uxmJCekkY2wusdKeVchARl6T27\nMpCHmSe30UDfRKqIMlhX92dBcHGJd2otwfE5JfWKnmSv3jzmaQCLSp4eqXkT\nEQA/3p3xpqzzy8Ni9eNpfnmZ6tmwTwGI+9VLK0ALKi9b3eEbHz/qfvntVctO\nEchtXn5HKX1jlGAhmd8bASsp1eyUFftFzVzhd4tRA7kvPIYq6i3MFtKQmeCP\nTI0n0WleCH13hqAW/43bZV+83gt3hxj+i8e0RrzOSaruvkn9TUbcxDrbD7KV\npHt+mUzNg8g1r+s89g5xCZndKvwvuqFSGbL/C/CBAZT/do02e/KgYEuI4l5M\nPA9kSShromt4R94uBFxm00lPryc+UVL6JKOSqNi0BpHbgKKKVwUdax3iAS6J\nRk6y\r\n=mL+k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDaS8H+oDSiFZchJfvvpJReOtrvjHGZTSSSx4Wx2oqJ6QIhAOg4IJUXsC63nGCLFHUxuxVidBZm/4dshfjSYQvwtx6P"}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.0.0-beta.2_1642826123949_0.8680139918201117"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.0.0":{"name":"sonolus-express","version":"2.0.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^16.10.2","@typescript-eslint/eslint-plugin":"^4.32.0","@typescript-eslint/parser":"^4.32.0","eslint":"^7.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.4.1","typescript":"^4.4.3"},"peerDependencies":{"express":"^4.17.1","sonolus-core":"^1.0.0"},"dependencies":{"express":"^4.17.1","sonolus-core":"^1.0.0","zod":"^3.9.5"},"gitHead":"44a138f9485c3d37c2a69c26d5857037aa084d28","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.0.0","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-VsZERMLCdrdtJWcakBmHvz/UZcfbNSUTKHTzBSSx1kznza31ubwfOvMz11Lt36fO1crMGM3SUHVs1mt9ycVhug==","shasum":"95dd402d5889f79edd989b2d079044a030831399","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.0.0.tgz","fileCount":121,"unpackedSize":119900,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJh8o0JCRA9TVsSAnZWagAA3xoP/1k4AZ4dvZnZlhn64r2f\nTNmjsU/Ks9EdeXWTVd34m9BHPn1IRAS9yEVutbFLoWBiuhhm7qlsa+8siC4/\nLGLKR9WPraj6KLB5yOvNQ6aO51b04m9hI/j4nngRvOBNTJ1/DUBixMEplOLn\nA8BzhRAu9f6DnKMBqj+VUDSgoA4ax17d4d8NAlXx6jPE/H30ZDCyFECAmNOI\nZKohR6f2RkXJKQ9AOgLswVC/ftCP3OIt8gcwNxVwaNqprFqUKhQqV+Sd6PwU\nZ0XitjM85sNCsY0O8TrVDFJAom37UNB23CGr3QJXO3UVVz0z2K8pKIlVTiIi\nRpWh1AainuU0d83Rt15fTp1h2uHCA8hlvilUoeKXOpGO8LcA0jtS+xs1d/E/\nrqnz8QT5OVgPbSK9AJNEEPy531+SgL4XqHswX52M9pqGuveqDLHK+XnJ0+Fv\n/hzpKkA/7I9XuX18MHEg8SpuTXQ0Gspr59Gj/DkDkaXuxSMNWXbEqoarYvLy\nyZ4In1r6dA1WKIvZnB5mXgqXcyssPffG66q7k9QLU1lbfst7Ja2b1ZR5dnmc\nMNyj3GFLrDurnHcJzC4/Zv/oRp6AiZ1lRjWXfZCRLikab8hz4iKmg9y6OlDz\n9dk60Y2olYzAg2sROC3BcNTC/rjEYz6Zm32/jiFVa/d6jPZuCrMPLkqYJThP\n/YTK\r\n=eGFs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDOY16sVbBHQq1Xvl9q+rc5xh407GeU8ZrGDlCG3NR9RAiEA8FUbT/iz7S5HnD0glqCujncicXzxPTfN1w0yZXiD0/E="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.0.0_1643285769696_0.7279272483680106"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.1.0-beta.0":{"name":"sonolus-express","version":"2.1.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^1.1.0-beta.0"},"dependencies":{"zod":"^3.11.6"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"30870aa642a77ceb9010644c161c7b0d926f0a2b","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.1.0-beta.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-eRrZXOvQcH31WI4hY78RggOm9+H/7CTB8kTT7UEussHTaoN5oCeJYsUhralU1ehPWIJz7TNW3EPHuJu36YnGaA==","shasum":"aca95884e3e4a159cc3e06d3bcfb620840cfe9c4","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.1.0-beta.0.tgz","fileCount":121,"unpackedSize":120612,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiEh+kACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrrKQ//WzBIdCk6ySKKnS9XgcLSsjwu6VmioDPlrjALv0crtmKC1d5h\r\nDLSAlPWox/lo469yU6ce1ES1Gzxka+byzi+LtD5qfdra/QaRMbm0tfrwo6wB\r\nA15norZETXNLAzC91ZXwZQZt/YQkQqhqwuATVgwcUwExNsMsCgHRNVWv6Gw+\r\nhrf6NedEX8EKISS4cksCsQXEWmR2zjoY2ULEK+tV9Gn9hcWA/C31FhFCenkQ\r\nIGNqHYlglbrpE3NuX/EGDgDaCS8vlM9QE8neDuWlA2wbgHhPmwHgPp0nGXB5\r\nxcRRSuMh0ndZyYCot6/I5cNW73Pt4UAkCfMQ9hRJt8bf4ZpLR0JPH23PVe3n\r\nJsQ4vBeOFLt3HyV+qJ4gglINH6wEOPTCKEuB+x+SaZF440WE+Ri51nZ/z/qq\r\n5YhGe59RhWWI9GN1/KneXoIosFTVqzLTd05LOKATLd1Z0k/9/ormdGjbo8or\r\nTUel1gBXmkMIWjVgBDxf6Xx5A3MX063LRoOHpaRRd3vG96E0p+HkRGmvLwyN\r\nSx2d2mZXg3xi+CKHgyYkRpSAnnmAlz/DvfMUZqIDYEruqqG7JjEmxTkmkSXP\r\nInhcha3ZhivHSLLTP0lqNYC6FCbmSwIcWbjIntVqKe9CnOumYALxaG/04l0S\r\nsYhuiXKtL0fQodu9Vbq55MApK6p1SEHdqLE=\r\n=MI/x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwieirLGpjkdxpLuKFSnDyyZQkFVffQdUY37dMI/+oqgIhAKHBTky3aBx5mqiuz/p+V4uJg05yGWLMajiaeXstTQOZ"}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.1.0-beta.0_1645354916806_0.3721731899011449"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.1.0":{"name":"sonolus-express","version":"2.1.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^1.1.0"},"dependencies":{"zod":"^3.11.6"},"gitHead":"7eb9a5aaa323a169c86614a2f027ce2220252978","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.1.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-ILYpyASfRTOXPN1U2/WiG7K2MOuKSUQpmI3lUHFy+AcuBhYUl+xGeqWv9kp4kUM8YqAfT/WcmwNy46dsJt+BwQ==","shasum":"3fc6cef838506109a034b7f6f9d30ef493099200","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.1.0.tgz","fileCount":121,"unpackedSize":120598,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiFltjACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo5cg/+IKWvYztn15J+zj3pMp05Ul9t966ApBT467cLlSBsHyWGKdln\r\ngCL5zPbFnYrv94YkktgUO59PvM5jZeH6C9kGDcSgFnFulgMDH6bqrlccp5iJ\r\nEvPNaajUhJqLPau9xbLHZXpPEqctu4qeY1Qm21HOfXZaoYNupT7JuW+f9GYt\r\naVfkpcyGs/p/NAwZM70ooRRoUe0ZLKVulya3zG/wNhW9cyS7PX4jTVb/BTFs\r\naAttpl8NoAE6pc6CBfnFlwoV+dwH716pNHOmmkSguSOB3zH0JPv2DSG5aQ3H\r\nC6GwHl+MFICDtGJuIJCuoexIN8eKh8Qz0OdpdedPl3VnVZW7iGDEVnFKOrYB\r\nwvd1cdG7NdldFtdARw294Pg6JYgWyfOyYD0KClIwdLO5i8cRkMaHFMSU20UH\r\nt2iEhcz4r6ZagdAO5bWaj1kfycoJat9eMbGXBsFI3F4sBCpucZg4641Ou24Q\r\nmCveLWOEkcmTn6x1X7Rp5py1kIRe3ECjuCd49VvUlvRmh/jbbHbXOW0t3U/f\r\nT880h1A/PwKJj96zFxByubCWmhdalGgc9zsE9XqeWlTI5Qc8YEBKaVzbIVc2\r\nweOBFVKNacOtjjTgS2dKuMvjl4GG80tiN9NUfvLG3fi94rErFSm/E6sxG8L4\r\nM0ctQuYtAEI6uyzxKG182DHv5mUNP9rx8Bg=\r\n=0OPu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIApcZudDEmvBGw5dnEwBSSnRySMC5ABT8Rr/gQcC6FgVAiEAje9wiJDrjHzKbFpQ5+6qhg7jBK8dkthnVNQAJW/FGPQ="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.1.0_1645632355812_0.7956452338488607"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.1.1-beta.0":{"name":"sonolus-express","version":"2.1.1-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^1.1.2-beta.0"},"dependencies":{"zod":"^3.11.6"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"d5b5429b9f523c02a6334fef59f412a76f6bb9a4","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.1.1-beta.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-Fb1yye6kzeLf/yK8WfiZn7U8VMoUhIST4BQQYt8y5MYtWxPYlBljUr9R7OwLwEdw1rQQN9iO4ybXGWveWt+Y9g==","shasum":"2d917686d7e6701631d0539d9cd598ec0019f3f3","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.1.1-beta.0.tgz","fileCount":121,"unpackedSize":120612,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/9CcBT74OvZJxWfm44JGhu2b6ekoFayycqewG3nE8pAIhALUarapZLu8rQo5koOu2coqskdA1rkj5JhYou+bDjGKf"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiY8v/ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqkhRAAkpkqFObLd3mKwKP8SP8QvJiLVc7FtN2VWLj+hcMzPgj0jxlq\r\n5Eo3TWZHNq+tz63ZYHJ6bfiz6a1GinSPgArf6aA9QY7kb1TEFvwYxA8Bb2UJ\r\ngS2+hjBRbNwOK17GQ+V+r7rJ8I6dto8h94u1IVEtWZ3MP1eQF6gflysCil8+\r\nACRWb0PPZUYTp2nvPLHnNZRB7zIKzJcO+3EdfVUU3qLliiXMVt+9iKdX6/HU\r\nvwWDNDxDkE3bUGnoY+6MkCBAJtcdfNScNHcHLhS5G+qtRPoXusD77JpckeaM\r\nLVFQMxx37lhCZMqgdYswKgwdtqQzo4jvmmVbVdk1+Ae2zJdLxnsPbFkirzG2\r\nWWpL8GLJ7Ijd+bJo0PO5fsvXWftbjnwMz2z/RrMrXPFcBhtGRFPrnO2HjE9W\r\nVl3DrfoYpRxZcPB4vUJvaR3mqvMPWlgAt0m9oudj7YJXoL4AfpCOrzVYvA7i\r\nmv39y/62XR8UVI+vC/LdlLECRfEMFHNzrr2iZQzSkpCd/zIBiG3idjdEEwCf\r\ntHQTfPZ7bUla/CG4Bs/tXqlvIcwBz/zN78QQ47w+/FwyZGjtI5n4F7o1sjPv\r\nX6B25MhR6nNKfAXK3wJ7DfkRakZq2EKOGSigav7b2I53gvxe/u92H2dCPwzJ\r\n+lmHyfBs5EFs8J9JBf887DnqU3A7CE1Zao8=\r\n=hxub\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.1.1-beta.0_1650707455660_0.7400625969962529"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.1.1":{"name":"sonolus-express","version":"2.1.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^1.1.2"},"dependencies":{"zod":"^3.11.6"},"gitHead":"bb25f711cbea8927a76b3d9ac317b8184c50aefe","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.1.1","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-+Da7+zLlz+YsS7U26IpNSz+e7ZnIoQacOSzM/qvHKbFQX3cwlrT0xEx0Em4oq4+I+16GHdn/2xJu8jORosmAJg==","shasum":"740b1173afbd42a78bfbd8de6ca5947fa5f748e1","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.1.1.tgz","fileCount":121,"unpackedSize":120598,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEwrMb3YiVsq7bjlJRQrDlmCaHlQ0pd+FzWK5iPYOyp2AiEAwnTy450ylVxDonUbNHe+QDxjoMAejWFCu5Xq2qadmQ4="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiZN/PACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqMGRAAhhCdlg8L87B4YD0rh7b5gOyKanmtYBbIlCSWl/GL0a5+ppB5\r\nL+x2wPYzxE9HSOfr9xkp3Nj89sjiV1yOU4RO6sQiu++ktutweD/nJ5Lcdb4i\r\noY0MRDqwLMcevavFV1RY8tudjiOccvwqulhzwhuxc2faY42d9akW1b3q3W7P\r\nSpbrzN3rd6WkrzkOef2/hOfu9gf0DQ+LZDg4k8Eb9n1vfpAM7RcdeDE/EnJP\r\nvqoU9QZsY9qce9a+apKEOjSttJ+1d3/Gt8LUreyPNpVxesteAFFVilzxLbG7\r\n0UDy9We/zexdpteQF0UA9sdHE/4c5Na6QjH52KHZoTZP32gDfUu0FOypEGIp\r\nxCTq9HSiCiz+5QqZZlG8eZ33HSC5xDXeenHUB/NUg26hTVjz5sZajVscnGER\r\nSm1CZrZPv0umOfjjFdbR2i6zLJLlRyNpfjdQwqx80CjjsPIxNe6QBfHwqPIT\r\nmdnRLtL1KIhiV0Slv4uSXVn0A72rZQDoK7nZq+EPsFTcAknoBB+ULccfVs8m\r\nnQ8PcwJ4kssT1I7Q3V39nvXf5o2ptIILvzXMg6BD3PFj87OGDjZXtUJdGaqc\r\nMwnmIGO806i8MvLLGiCxwbpyjku0d1BeXjpvuFFaieD0Q5wA85fW1hJpEEl5\r\nt+gsmcpxTqwzoOFBtHMo5enc2Id1q7yGppE=\r\n=leiK\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.1.1_1650778062810_0.3663953273566598"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.1.2-beta.0":{"name":"sonolus-express","version":"2.1.2-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^2.0.0-beta.1"},"dependencies":{"zod":"^3.11.6"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"aa2d141727f15627262963034305b719bfdd34a3","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.1.2-beta.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-eu4LwR4vlqqcD9sX9JX223q19WCGu7+ZKNnpNTOwzRAp+/fzWP1kkOsLx7u2tGyIbb/D5BrpgsrWEp7etWZzJw==","shasum":"65e8cca7a27b24422ca7d7afdf8a11af8682f244","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.1.2-beta.0.tgz","fileCount":121,"unpackedSize":121442,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDlcUDhxWpNa/3+yEzHbwGbcqG6B0mVqjyB5RclJNp8MAIhAIaAZ6fCb5G4D48th9KCdooIe79FCYnXDXop+OK5CBli"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi5ir9ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmooYg/+Ijr80VjF8BYL+DOb3SCmsY7n71Ulpoze2YmGLQCM/Z9NFP5S\r\nND0XHuU3fGTd6iHysoC+OlQVFbt3BxTaiQ431G7nKIP1Jo7EucEB/s9C4jEF\r\nArs+Qbt4/i1j+gyjiWw0HbE8bcK9YXRuGWkl2ATpZUcbca5Vm7uRWzgphGW2\r\nLc7QSiljRNttLnrpZbcD/A6hiClv1J4jdF0PopVoG+QJhEaxsWRVxVzIVdL2\r\nEnLwl0G8Z9HRT/4kZbtfzJ/bkQ6qyb5VWKbOe3NpOivySU2bV2sqTGR04Rk5\r\np4qSafF39xRI2cb6Iz9Zd4dXMwN9AMCn79bHz9KNdjx5CXHOxCLjr9l0kl7F\r\n0mnL7O7AuDvBCHHHTN569Z/JhHrckkbTdDGrnSNa9DZ2+fuL7nXPjSYi2ToN\r\nyynAHoEJG/E2l1Pwm6J83/jByoGqOskmbXl9LLDYa7u4JT/R59f3KOUIaZKo\r\nsHgzBxwwBhcB4N9F0y9BdDy/1SrJVt/wY9w2iPDwNrCgXIiENNDEeyW5tNt1\r\n/+6AO1wd/AwmsMCsZjTW1u4/l/QtA6RQ3TU/HX2YtEdUGxcFfDlRuH4bhiqQ\r\nT/QbvaTHbxj/S0PMU2XboPU3Gig/NIqDi4EHT0PFxc5MV81r25J6KZdUByaJ\r\ntsmDiALYiFVZLEirN2hl4gHx2crOzQS7wTI=\r\n=oWNb\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.1.2-beta.0_1659251453420_0.17327880946530128"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.1.2-beta.1":{"name":"sonolus-express","version":"2.1.2-beta.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^2.0.0-beta.1"},"dependencies":{"zod":"^3.11.6"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"ed81aa9b3a3c9e89ade94b486be18e1b92f63278","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.1.2-beta.1","_nodeVersion":"16.14.0","_npmVersion":"8.6.0","dist":{"integrity":"sha512-+7sRnBtngpebi56S43xGf3EHpgUUCz1U/zqCubdi9vxB91pR51znFJpQ6L5/iwF7KB4FhhLNZDmUZMVO7Fc31w==","shasum":"0fcba077b2a1db31dbcdc62fb57041174ead1cd1","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.1.2-beta.1.tgz","fileCount":121,"unpackedSize":121450,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIASxu6cvj6IRhLsJMbmnkEyrXwDATFGzpVM9vge0QqDsAiAVqqzbwQDMmcpugBhSRz6zbMwOeuZch9aMc8rHNigvhw=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi76bQACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrGzQ/9F/AZbekGZdN05NobTpQwXHWamk3br3s8jwspXNcwJSuOcVio\r\nhT6AcRfiu8Tj4vy4EMe6oNyXR7GoL1u0KHZbOX7lzOLUj2gukNGfzLPShd9l\r\nZX+8aRILfX3WMRm1hf6VAbB/ZDVlN0Gj7D59LmufH1t/ytBKnKckibt3niMG\r\nb0U4olX18MpvluxR9JFLQGvmhYC3xvt/7wjZEoWdiovx83upFFYVS5S5atBY\r\nWKds0OyQdf1GN5gbItSjmZMF0+jrusFjpFYmZQ1ETwZ246e9BCmSeRxTjzj3\r\nWTHEqoRtyjiYP9dAiZTWUEtCVyL0NOVzKC4qJON7aOyrg0p0limm43299rwB\r\nBcWMykeZy9YgaTwdu2WN/BJLlhYZwqFGyP+1EcSJ6SZujl2EBvdz5xWmXJZf\r\n8pa2gcSO8SJ0qZxllk0/ZFWcL0hYGecOwMkYMV/fk9k3fUdfnw9OV+f2GIXs\r\n0/qZlVBw4oBE94h4FJW9QTNGFkD7S//CivJ7+twcjlZNJzxSkEufeaIdcKOi\r\nFukjXv/h9ICUYhKtmRx56cQ7zm5eaZz233UJVWvi7BkmOf5rLVOCemdjQNSm\r\nuowxeW/PTgZmvEdCNBWoklSS1axx4aj9tSD7U/+3aEHX72MzqsTRLefA9bgx\r\n/g+5HOFZAlmIw3Q62MZTKJe+QWUUgxvP/2U=\r\n=Tu2J\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.1.2-beta.1_1659872976035_0.326433055134403"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"2.1.2":{"name":"sonolus-express","version":"2.1.2","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^2.0.0"},"dependencies":{"zod":"^3.11.6"},"gitHead":"bcc22c33e9e9270222f898e3dc1e80df1a49818f","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@2.1.2","_nodeVersion":"16.14.0","_npmVersion":"8.6.0","dist":{"integrity":"sha512-bL3Pijs1arHoI0UGrh3kq3R0m++tx1Vg8KW7IoNfdiS6VTUn4OayfLaeVGzlpAwCg4J1L4Fk4jT1ZfGmxfKy5w==","shasum":"2d5aea4141f9d6c4499d1bba685aed3261832cfc","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-2.1.2.tgz","fileCount":121,"unpackedSize":121437,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDgFRMbW2uUQVpcw8vrL/wtaL+sFtSarATMRMeGOH0HMAIhANrF9Q6vjhFdEMOofdI8SbRgZwqooHGAkLMmnjtgvsmU"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJi8jBUACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoU3w//Q5GnuXLrajQTlld3RgWVUHTQTMia4S593DCldAGIO6W4vCD3\r\ns/DFVfUbu5AuhB1p3KUklgPjt4U6yWFxLL50/hArh2fYaPthNwNfYQe1DS3Y\r\nnBeLRlMSbU5Kw9PhQQU/p681TDiSpVbek9ZRosdze0mwpIVkecUzYrKT3+la\r\n4RtqTY7vklnrzPtKA04abUMk9p6pNK44QHVu1kt/+zCrM9oYrIsB5RzrJ/D7\r\n8BX4aE6R4YiIi9iXRj6wWpJQXYtxpMHpa42ORlNPU/TBJiY3sL5QKu+xfVnC\r\n2oQbUCWYcISYloCppFQIV4raTC0xEDqWB8h2z8IzkSSZ0GCtrF9PM+AlRjm1\r\nJu8uf3FpiSrc8uo3HntIj9QZa7mrzot9bMQwyBQtMArXnt928FO3KFfMrlfS\r\nJ5S7xzVhm83hXwo0ebt6kuWCvyuSDKaZykglIalemUc417OQJ0b+BOyu3n1R\r\nc9zWSQvfCMM0VjH8FPfl7vejO3skdsaEHFEMaPTO9NxE3X/77afSHByqK6CI\r\n+QjUnzS0X9XHYek9kGhszYZe/CFc6bqllhXlCt3tpffhynF9ksFdI7v+F7fz\r\n7UBqk7Swg3gi2QjuGqoiRbJUL2z2xa948SMcXQzxHeZ6lGUR9C2cxCtaWvPq\r\nT+mir+E4SsV3nHq3XvD9EGls5OTxhp2N5gw=\r\n=s3NK\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_2.1.2_1660039251827_0.3435044059232677"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"3.0.0":{"name":"sonolus-express","version":"3.0.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^4.0.0"},"dependencies":{"zod":"^3.18.0"},"gitHead":"684e21e5ad6a9d389ef9ddf3e670e15558b0946a","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@3.0.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-vhSmxiXF+aFds1cTzAphuAwX27wLtBvI/RBGmJXP1li8MUzXdEflVNrRCZwCEbXeB5U8hT5khmhRhiIt2HTP/g==","shasum":"a09598c0f648bdef284ac906424862174752cdf4","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-3.0.0.tgz","fileCount":121,"unpackedSize":122599,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC7VvWpXDVRsIazRCj/1Q3O1rcGZOYwO8G4pGxbkHVthAIhAK7lFbsVBiMKDD659TzK/dGT2cadU5T3MHXyi4m2zvX/"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjFf7CACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpETBAAg1fkZYtNTmdVQSGOUhCz7yW7bHPrKoHHJTIjaGnRKkOU51XF\r\n9N9V8HjOuQB+pY277i1Aenvld41fyp/mcxbt5OphZUJc3b3zPTePx3IeJoHE\r\nWNZfydCoahUKLSQ/6G92Id9aw5cL/8F0qRhwit/3iVaW3lNtY+pdtmdZ9CE8\r\n3bhXuR/cynbSPkEq/JbeYBFBhEmEFHc4gI7GgxwJcCLFsIQB/YqT5qmgjs2i\r\no7F3EzNif4UiHAFzAHAz+ebeFaPynldfcQBp0bdyWZRjMFuokAz9Q9HaUodg\r\nwDxWfBDO8hqGgkzDey/fHd5huM4fRMADvYUx7xc/HQQT/S5ZFMq7FM83ncQu\r\npi3nQmNRYqFF4YtT45nrHyMBtw5yxsbLIJMqThtNMbz1p0cVg1LXqk3m0KXn\r\npdjOBy5rwX6H6dindSbzJ5blYZtqjmjVFXjDsra0WU55I2JWUmBhX3aO1pgi\r\nY4VPco+BMAW3FS0yXeEowsJZw4PpHLqqUKffDrAwlRogwaXpLXDyo7HlDCR6\r\nxfkVytraipNl4975elDTb/lYljtpLg0Y7Gd0z9flNwcpoJrp3yVyv2KFo60i\r\nY6h326GPZCjpiJqyV8HEBDWqB7FhVUnuq7UR9xpJTOoesCYuZal/b19G4OCi\r\nQeEaySDbnQzw6amKkffdAy2VzoGTJAPGvNs=\r\n=kCP2\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_3.0.0_1662385858139_0.3014550460364034"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"4.0.0-beta.0":{"name":"sonolus-express","version":"4.0.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^5.0.0-beta.0"},"dependencies":{"zod":"^3.18.0"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"4655b3a9a4cb3ea9df14014119bd9357037458ce","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@4.0.0-beta.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-m0CEy09qaNE64JOJz9SmODkCvAL23V50Rt40bEjJmkwX5wZfBcEZorfeieDyrDx4eXuQsTu/6WPaOJK/5UqUyQ==","shasum":"25841595dad0838eb9bd770d8d27789ab21515a5","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-4.0.0-beta.0.tgz","fileCount":121,"unpackedSize":123332,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC9SpWnx59VwnbBwhoyD1J370iYtigNR141GPSe4vLF6wIhAO9xOuafjTvhnxjP8VTga6ugBmCoP5n6NneoZCR1aYS4"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjG/W6ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrnwQ//RtwRPy7vnJ4Qi1Jtl83QPGCiKKB7qGm+p75KYIh8may/M8z0\r\nSMJOLbvKmEIsOAgfT/jE7TvSw2JuwBW5TcXctGtUPawkMq+WhRnfh4ELfvbF\r\nt3lG3JRBzRb3UL6ppk3uxWvMTD1q70V47W0CCPUn6W8eimJriauAknAEq1MJ\r\nHNJP+xVQn8h7hw2m5Iuc3WNVOY6FgAew23uD4xYEhF3lPMtcklSbbQF54Fyv\r\nG7SlGWubUQC3jnbJsP6IhVL1rVo7m+8joY37vnayU6WxbiH8bwLoZTlK04Rk\r\ndmskwiBSe9d1V7DsydvePB8NlwnCjCqYSnahpTLUzsSZ4vEO9tOQPW7lg8aW\r\nZYPZuBppyGwQAX+6hkfV/efoani4SSmcLkqAh/eQ7qlhYgbDapzaG1JoXPcF\r\nnsIl14Dq8JI5SY1/Lz7otRs190ySbdv4Xaq7f15nWIwBd5laJCGnmNT/x2lc\r\n9RQ0t8inZq28prhleUS8DGntjPTCqJnLdlZGbjXdjSDBkZJWVm56HVasHn7P\r\n1j+65Hu9L4B4AwzlHQ5+eJ31fTtNqU8SAlEa3NprKgWp6KVMnlLYHFYfts/8\r\nbJ1MDzmTe9c+oByQZu8wDOLU4RWuQGE2VI3t9+EHg4sZ7Tvoay3fDEBubGTI\r\nw6uC5AwPqRJ2+1uLJ/9Hx6WQRc3qxongKHQ=\r\n=Yw49\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_4.0.0-beta.0_1662776762041_0.8005876643377987"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"4.0.0-beta.1":{"name":"sonolus-express","version":"4.0.0-beta.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^5.0.0-beta.0"},"dependencies":{"zod":"^3.18.0"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"58c25322bd3ca016447e7a9a50720d9e167259ce","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@4.0.0-beta.1","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-ZNbFvtLnZlx+j52roCPdAVQd4H4vkucbnf7B4c3X+GcDAe7B8ivQwk139gIVyePIxmcy8AwXf7q0MxdsoWUN7g==","shasum":"98f9aa87333fad914d9f9a8ad6d32324d90bdffd","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-4.0.0-beta.1.tgz","fileCount":121,"unpackedSize":125283,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDJ/ikCwsfeWW8qy0H1vfh0lugIlzDK7RuFPkh5Bkp7igIhANWHqY9YDjtGloVfD7Pohz8r6Yh2EF+6UtYEJ++q12i6"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJu/GACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqn8g/8DDSQwedSSFcGiyG29bhpDZVKmVNziq3cAcp4HFt83pjAW2sD\r\nr5o1m6nshjx0Xa+rVky9ZMvH6UIUZjX1YMwCxJ+gOD2kLFpXocNKm0IE3Ht/\r\nWISbMFz5W0SLfRlUvRv8aNHGYUPgjzo/P1E3qQyVy6hMXkuoF4sDFzAup2nn\r\nPKbP3PTx9rllnQn5KiktMrAz1SU+3HxKwhSpYV9agGeROSmp/L7Z8Nz14Ahy\r\nofdPZZcBZsvXVTE+aYcJKJ70hIrjyKH/Y5g5lHdBCPMi32Y24hRyLTVaC3h5\r\n5rm3pgpcY9chsusalgNooYhcbOb+5WNtXR8muvHBGfGqRSlrXeL0vCaVIBzR\r\nX5DMAzbVZX10HSQyPRBPsOVPf2mse29LPWh5zfo15NmVQtCkACt95cfu8j+0\r\nbyPCq5FTgg6cE8efo3SY1IudXhR2G/ww93WFwh7xzkdD4tcy+b1UUj49Lolx\r\ncABT/YcjU0NE+IGC7kfs9quyE85TVbmHu1YEzEAwjgTjPdcWsoe7YTRHlPK8\r\nQCFsokmtdxonYM2JNqamDOMo3HZuLkZroICIeUJM2T6D2Z2PEzd/A+pDYoW+\r\nAjSnQzqmSdf9V7+kFenAYAp8A0hm5mp70IJr1hPHoHu9msA01yGqxTYEAxbZ\r\nPiEvJy4ivr9eDUv9ShJfKyV4j5qwfMu2q9o=\r\n=BFsH\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_4.0.0-beta.1_1663496134457_0.358443047843495"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"4.0.0":{"name":"sonolus-express","version":"4.0.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.13","@types/node":"^17.0.18","@typescript-eslint/eslint-plugin":"^5.12.0","@typescript-eslint/parser":"^5.12.0","eslint":"^8.9.0","eslint-config-prettier":"^8.4.0","eslint-plugin-prettier":"^4.0.0","prettier":"^2.5.1","typescript":"^4.5.5"},"peerDependencies":{"express":"^4.17.3","sonolus-core":"^5.0.0"},"dependencies":{"zod":"^3.18.0"},"gitHead":"356c749e38224c7c13d3bb27b7b0485fd633d231","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@4.0.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-guieeCr0WfQ5rv2F9VpfB+SF07NZlKU41j4hpoQEfu7M15y4ennbtyj+DI2eYfiLFFFtFvoymDhpKHWxHLAZ0Q==","shasum":"f37c05586a42f4c8295ff6962a58c2411572b830","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-4.0.0.tgz","fileCount":121,"unpackedSize":125269,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCdDq4FjJwGr0hmAuFAZ2gRToyDWbrd5Aa+MfGb66c/FQIgDzKqcVRytSkY3Ky0v7khXWzgMnkCyCLCPAhxU58zJ5A="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjOYBCACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrztA/+KC9hac3bEPTRjnHfVIvTLiDwaObOZyZASBB3KuQuMJulbH+1\r\n83JoKVJxT4eJkpJPWQta3AVYu6YHvUewBgN7VJ6rUclK4+AQ6euFqZZJnjCY\r\nQFxu9jH/Vzw0YTdIjHMStiU87QPKB2kgSSkCvHqvc2SQ4gskMiXJ28BHCdAY\r\nHCjBH02otfHgPGm5re3OTogaBM+LxRonsZJaSZHIG2gfHvgi2USbm9pdkH72\r\nwjtwoKsT2levoUEwDTbdtvPUmGQMYjomKf70Qkmn7qMDusGl98UN25X4xhWb\r\nS7LAV1m6F7qAw6XNgJc3gfIrs8iB5UxHi8RJMK4IlL3HUpk1T5F0Itznn/tw\r\naVV6sQv6O1acNDjG3P8zNvENM0hyZw0cynXUOdeOgAdlul4k4l2hF17gwWfE\r\nmYUG1fuZbJPr8FTTm71tLFTv8l5KrKtpgedw4kbCRRlWn0kFu5haFW1B+/kE\r\nkuLhtzHrM5mf8vWgWQ7osJ1HtbFOvLHOqEEFfIu+7ct7Fgrd/3wNU4OWtnYg\r\nJZMxJk+ibZ9PjcxjA1FZ55WYdiPrxhIYa2dJ3RPEiHdUdh/kdIZaPmfo8Vkx\r\nGrVvK50bF5Wgk5L8+PDFrM0+TwOMqPFJ5GpLEHZ4U6KFtooF37t1dxF2vzgb\r\nW68UYFCl4ZrpPBDk+rF3EQimuHMtMlwAoFw=\r\n=8nme\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_4.0.0_1664712770180_0.49664455164963295"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"4.1.0-beta.0":{"name":"sonolus-express","version":"4.1.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.14","@types/node":"^16.18.2","@typescript-eslint/eslint-plugin":"^5.41.0","@typescript-eslint/parser":"^5.41.0","eslint":"^8.26.0","eslint-config-prettier":"^8.5.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.7.1","typescript":"^4.8.4"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^5.1.0-beta.0"},"dependencies":{"zod":"^3.19.1"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"e3a9128f89d1290b4b0289666a7528f283fcb8da","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@4.1.0-beta.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-fPozLRZc5MMvsDZYI+RiCM40D7eal96w52XrlLO7NM40V+y6dioV5zY8Z6or4PNrwr/3j62pisrhmaIFFpKNgQ==","shasum":"df4faa60cb7dce77ebde3661039bdda5bd6f28f4","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-4.1.0-beta.0.tgz","fileCount":127,"unpackedSize":136250,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDTHPDRhM233K6Dljwmtp3gYTWVbj2jRgxe9tiv4uvDVgIgVhWB3I0QiFU6A5BnoF0gd5D6yx4v4lVaPta5d23TJgc="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjYq0JACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrxMRAAlMve/0YcsKZO8TniSqCyyVFP794VAVj1MjhjBXGjPmwGZNs0\r\nYJgDd6gpiKBLlX5v7bTJKbwZ9Tk3wQX2P3OWebrgMw7s63qW2pkNc7TkSwjH\r\nR3JrLqVvFmyY3Nf+2GBUqMhGgIObwB7CycfEpk1cke9X7am+wc0mkuo1x12d\r\n9TkZels98wiv52f60qWShzE5PTpxxrw3p1sJj4xjSnSbuXkextvgQKDT6sQF\r\n8JsbpuS4LrtLw562spgFXj5cqTkzKdluKpCTsGKi/dmlkVqS/h81IBRfZcMY\r\n8ycqV9at2A8gCCm4+tsaGSPf9RPlHb8xoj+/mo/NXQ1x3G8/bmYeEFnfSxqQ\r\nuk8czZ+ZL1aIi4L8oE90XydLZS334axOapTrnN8pS9wQqX0Jcj58z67+eR3p\r\nQRUxFqm3Dd9t4jlaPzzioe5Q/idrWmY5uAatoM+jA4q7Cm/8ktBc4EvgW/fX\r\nYrFjqvDNsFqqy7vD8vKwqwRa0V+et09tDQSalERfGY8gqUenfFhi5+AiY1+6\r\n899bX4fBy3rB1B6Anp+SAqmnVtmwRtjcoHtSV3hF4lE9L1guHCqnZzG/KjQh\r\n5JdyVsWrJozSr2e/AGTPlR1Ws9LuNIWsfcJAuXnXWo8MqMUKliOvvOR3oAru\r\ngZhOqdWptOCY0UzjtsWUzVWgv1Xx7ozq7uk=\r\n=mE95\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_4.1.0-beta.0_1667411208937_0.3163409596625475"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"4.1.0-beta.1":{"name":"sonolus-express","version":"4.1.0-beta.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.14","@types/node":"^16.18.2","@typescript-eslint/eslint-plugin":"^5.41.0","@typescript-eslint/parser":"^5.41.0","eslint":"^8.26.0","eslint-config-prettier":"^8.5.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.7.1","typescript":"^4.8.4"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^5.1.0-beta.0"},"dependencies":{"zod":"^3.19.1"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"b9c536c74985fa018b0e4e773db11461805610ec","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@4.1.0-beta.1","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-+gEddrg7gh8MPvquTr9HjTjJTJK+kdNGf1fEOSI09WLPPcAgZ9qY4+mKWZ+vrCzMZY5yOLCY/qS2N6dFzAbh+Q==","shasum":"34104628d0789314081d5d3366ddfc304b46acbe","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-4.1.0-beta.1.tgz","fileCount":127,"unpackedSize":136353,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC28QoWLtrc2wQ2VVGfz4dMHj191WXXsWhEkFg5DX9YTAiEAv1uXhRd/dK3XgDQ3kjO6ZfmkaMgQGhGPVf4IA1eSVto="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjYudjACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqkxQ//dilwJrRRfwQDELRIcFCFSTUE75WgC+j21qJAWa8ZzDNcL9Un\r\nerm5zRLg9xz5RIJvZ8GAN8WGDSR2mSkW79I1pUyMG755hMt7SxWqgO0SEQfq\r\nCOXQhPjvyI3t8AJnEOQQNBFGulDoOESPIjl+gkeCbJ6QCb9XpiRcT1xq7Wq6\r\neaW/vEhOLBRQMEwnYJRve00t5dwGXFl/sbtadY+0DlBsXdMim8HqHNumVsSm\r\nxghhomzRjQBmwpbHDf8t0r++3OgEu7uMolE9fi0goOlfpnNFPYmC857S4Bf5\r\n7DL1Ggr6iMfl6Mx9Z7I0hhWqZ5OOlxWSqXdp/A6ilDjfureMQHDPnXkVe38D\r\nDiraIHEo0Fvpv2BqQaWprD6/y9vwvDFcH/qQe+UYV2D5j7VOqTMi2WS6V8Cb\r\n+0OAA444tQs1u5YiIigiNy/o+u0jhGdqu/t6JcnWZlVZzC0A7sEicCcYnyy3\r\nUo7xg2geqBRhGpNLcUmcJSvlWhbNpoNtI3uMVun6EFF1xv6pKzHb+nBPwGZs\r\n3WdDFNh3ArroTSnIcXv21H/s2HwljXtOfP60q+V/ge1qIsfMWnUa5SYtQ6Kr\r\nWQhVD8r+jeSaC6g449Ow9kdWb3csQ7U3FyZi5F6S6Q9qOoCx3QutbIowSnfY\r\n6EXSGw8K3ZlGbiKVWj1ZQ24EczhmlLi3hD8=\r\n=nY+e\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_4.1.0-beta.1_1667426147044_0.23080294768718623"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"4.1.0-beta.3":{"name":"sonolus-express","version":"4.1.0-beta.3","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.14","@types/node":"^16.18.2","@typescript-eslint/eslint-plugin":"^5.41.0","@typescript-eslint/parser":"^5.41.0","eslint":"^8.26.0","eslint-config-prettier":"^8.5.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.7.1","typescript":"^4.8.4"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^5.1.0-beta.0"},"dependencies":{"zod":"^3.19.1"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"41baa0ca1f930d01dc2c0fd1f9f587fc6938da1e","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@4.1.0-beta.3","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-s3+mKWPz8kRuv90N1d7rIuU12F/d47gjaaWr9JulxaAxH4Ke4kAcdAs8ZAGOe/wcKZlDgxrmQMOH1vAUrwKW/A==","shasum":"727e111028e1f255d601097b66a0458dcbcbbfe1","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-4.1.0-beta.3.tgz","fileCount":127,"unpackedSize":136506,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVta0vHInIbWzIagOykkHlJmMiLtCA2U1Tz427FdJOLAIgS+aksTJf2NIoRBUdZcNWOwX35WLcXC4jZKwBfT2MCYo="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjZBF1ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq2Bg/9Ho+a3bvr6TuYXPccAuWQ811p2XdK1T0bX9vXkJyI7j7uYzyS\r\n8zgYyaxyKsXM80fYrxw2xVsRWDNQt2haxHxj7rSNV6kvRD8vL/MxZeGhogrw\r\nWdUxzwaSopmzzuGN/ItCOCCYRdpPI1ElAUMifa2S6ZmRJ3y0cwCVRbpqTt6t\r\nWCSMG7MxpHF2LG8fJqeN6AEVSMIEI4ub6XoOwcfeS0VVVOrKiJP5j0lM3p/o\r\na+FasHPVrj+UipDTDHhp5z1pqKVYIxkPf9j7j/wVWSQY9mb9zvcq0vSABlo+\r\ne8BV7rms3hzsUnjYVFLRQAdSI9JVZTcxxq2eAtJP4i6ILW3yEmfJRMn9vCmD\r\nm+/kV/1q1A3whfATs7aSjrj5vA31DWI6FWQiR1IINtZQeoF2yRSOCD6KHHgd\r\n5AaZRL+FyHuVpelgebrQpskOQYbCji9Rr/mNHARHZzM1a+DJx7iGQFixM7EY\r\n/i4XYV9/sugPuqSZTCHHK26SMStloS4T9XbhSSQbD6gh4TB1iGQW9yV338km\r\n8iSBDGsahcZF3Cpwm2KWUpHHxtoJv6c/rPIzn1Gl93K+aaLIR2gFKIfPHnX/\r\n5Vvo7cuihZ97OY+1q090NAQYBoxH6MKapg9M1ZdXs+655sFV59SW5MzGvhj5\r\nSgPXAjmKc6ZLjVW8U8bWh9epwu5tr2SP7VI=\r\n=YBi4\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_4.1.0-beta.3_1667502452903_0.04825175266269688"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"4.1.0-beta.4":{"name":"sonolus-express","version":"4.1.0-beta.4","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.14","@types/node":"^16.18.2","@typescript-eslint/eslint-plugin":"^5.41.0","@typescript-eslint/parser":"^5.41.0","eslint":"^8.26.0","eslint-config-prettier":"^8.5.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.7.1","typescript":"^4.8.4"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^5.1.0-beta.0"},"dependencies":{"zod":"^3.19.1"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"71b519ac03d4232cc68cd4fd432b0a66165d5ad5","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@4.1.0-beta.4","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-KDfk9IA4EIV/t9JrlYkhKbGCfN4flsfWGwtTMvWGlRDJuvjaABFcu9thwwOU7rSmRAdcIH5VKr3Qn/AE88cedw==","shasum":"4d45adb49928fd09f060ffc3355df9c375188a4b","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-4.1.0-beta.4.tgz","fileCount":127,"unpackedSize":136551,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDbEuZkU+3kq5Cp7ttfGH/+F1vg+p9yjPIor5WrQYB6LAiEAwnmi/gM29m6I8I3hfu9z5apkL1NjqmoF1+esUYMHLqU="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjaDtyACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpS7g/+LDcr/Ell+o2X43VNzzkWB5gnP6uUm9dBTdgFzFGgAEOEQtO5\r\nd9NjTUooVPUsZ+4D2s7/5fyxYgdinMclkoQxiScodF55PGTZ0g8NdRqkbOL/\r\ncrOEbV0rS87Ylyc99rW/8Jase/oYKiRNDw/wrzNpBwRmKp8GkLI8v/BpVBvK\r\nrN4+ehjzpbcoOS/C/ppBk+jH0YHvCcipRYR60ggEPjLsaB2QIb/NifRNgryZ\r\nrJ5WINp/5Ej4GGUIZYdC8005sxuWAjMyTPPqWwzR0JXZg5iZeVQ5L2fGl5az\r\n9Zflj13cf3FhRtYbl9RNeetR+889N8yXg9R4l3FRckFINuw1cEwrLIUXdtCL\r\nyB7lA2M1oVRAqczPfvNsYFtlvYmAwbtMbx6cq4Uhs+/w34A1ih+z4NxDW1fk\r\nwE2+XZHDhRNgv0t3X+9wcaKYHEMnMpaxivmu1IxN3oZSHscIIoaAWSx8Ndd0\r\nPoCxNPZTnDSoDCnMRzIFj/2MyAjYKE1HN8jXL8E33KAMXLfTs//xa0eeZ8RZ\r\nl/Q9distualeuARODUJVywKv/ybFOD+82siADVduR8b/W0fmhx6iV6dnnbXK\r\n7aI40Trh9umWeIQlKBTPw5Kl8/VdpKioSgz7Vc54s6+MOz2nIk0I0Ieky/2E\r\nlZREMM47Baz+0AyBWWHouDBmeAmcWkHl8ws=\r\n=AoSg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_4.1.0-beta.4_1667775346513_0.49392975379009707"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"4.1.0":{"name":"sonolus-express","version":"4.1.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.14","@types/node":"^16.18.2","@typescript-eslint/eslint-plugin":"^5.41.0","@typescript-eslint/parser":"^5.41.0","eslint":"^8.26.0","eslint-config-prettier":"^8.5.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.7.1","typescript":"^4.8.4"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^5.1.0"},"dependencies":{"zod":"^3.19.1"},"gitHead":"b5e6fa2984f2a3ce7c4202769e6af645f3d73e66","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@4.1.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-iqdyrEi3vD3bbKUVfHZdx9bIj1c/jx88PR8OZgAVXygmMY7yyO33lp+yVfxzruxl8x6Uy1bTVTkIV2FEXK6eIA==","shasum":"5f8f1f23f6d0f479569ca08919dd7bcd0991454d","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-4.1.0.tgz","fileCount":127,"unpackedSize":136537,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwReZol358HeAu+hDh6HZO3ubhXg7xDAq51QIQjgIvNwIgJSTltSOTm/G+4BLR8JP4u9x7+XMCOn/FKWcoUL0oBsg="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjaPeuACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmonbQ//clZ1nhwa3Cu2FCo32VHeuHdKfX39UyjKEblsDQoss7szUCGV\r\nmd8KzuB8eUlFYMhYu8WN4ydJgRo6nwZlWssCUD2/ydsn6jnEw6QSZLSyZm0K\r\nK98Z6zDnlBOIqhaqSmjBJ6LzkkMfOkDsoREfF2OZgTkrLlrolGIcuz4IRszS\r\nbpKohHDhXbmkam6vsdjd0GGpLm/6QewLogFV6tP0vfrSVdLwH9dDTO0IJ/i+\r\ncXIN3upeGFQInfGry0RujWq0SpNwfXDoBwS3XkmAH+OmaYl+qRBI/GGrbKtx\r\nSbnXgSFlKdEqiAcIUj4u5LT8QWE2KqS3iTCzpZthRnLXuTn3ZlwNKl0h3jZi\r\noUmHnheqBLOW5vlhNTWUG5OgdH1CJcwjC21rFXPT2APktKHdx7UZaJc5rEHj\r\n9IaNd01L4Ps8Chq/Ez4mLQ07r6puUauUbHlLhtxq0GyDT/V7y4gkOdJwlHsY\r\nn46axBx1CcWgDSrIh30fidFgwRMIWqjNvTYj5sA6kluONZTTsEo1Y2lVcsiZ\r\ne+SOBhbkjiwuINRzo3S57tasihsXaoJW0+8KXttgXHVJ3eCa4JMuxhY56O9N\r\n1ED4y5k4RDVdBneyln49Zpc4vuidJ4SKPxDi/8yhSWCOotM6y87KvBuwJdT7\r\nIHbfhc3bnGEKZGKF7vP2qKay9tQxvOiAWRM=\r\n=6vEp\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_4.1.0_1667823534654_0.7601663107519074"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"5.0.0-beta.0":{"name":"sonolus-express","version":"5.0.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.14","@types/node":"^16.18.2","@typescript-eslint/eslint-plugin":"^5.41.0","@typescript-eslint/parser":"^5.41.0","eslint":"^8.26.0","eslint-config-prettier":"^8.5.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.7.1","typescript":"^4.8.4"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^6.0.0-beta.0"},"dependencies":{"zod":"^3.19.1"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"02407d66a9454996adae30f4def4572eb0d975a0","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@5.0.0-beta.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-eIQ9P77ExyiPolGqfjqks1JKXsj2pfBg5ceUm31cgp3Xc3kWUJFYea8MFrZefe3/fl2wl/Hhp1m94rqJad6oow==","shasum":"fbb360895cd5ad2a3c1582868de9046256c7a8f3","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-5.0.0-beta.0.tgz","fileCount":127,"unpackedSize":136547,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE6v6Wk0JXRSy+L3R9hKc7s3yXzmlgl+NVn9/uxPLIhVAiEA212zYsEi7VbldeyFOIfwyFlZ8pQJAHa2DsbhuLbV1fM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjgc9iACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoZ1RAAoWJeV6BUG+V3W8q/8+fO+iVhOnZ7YifitVqirnaF8G6cMKIO\r\nuSjvgYD8jRicoHFlwZXucWgwxYUvdrMH6Y6v5wbi6zukLgqcKGurtUt1qnSz\r\nmOSCDtd8jR2RKYmnyCMRQ4JsWWT6WUmjXmcr5ovccbHIt1GMuvEwjYLixINu\r\nnaoNzn0f/9i0Gu4GqR68LPVGeORQvHqbrQG8TfVI1AmoxlP25DHtXvDeIPV8\r\nm9AOeJaztrBOKog7h64Ib62/8UFYGqubB/1WQQ6cMyThSRD+dHjAkwCFxC3v\r\n0hkOmfpNfiSwyJhEbE8qhtlF/qrqVtH7KvyM6XZefGKPx8D720Z9v+HMkOCG\r\nKS3VNIp9Mp5wZQHBMyZIeJYRteA3ztBEsq3bOIhBtx5VrHYn+3Sri4cwHyH4\r\nhhNbsILI84HBNeaEFvnGysdg2JyohY9cXY/eiMZya+JbdDHRa6iGCNGSMV9m\r\nvO7vjxU28faXDwvAYDkkf1GpshqknqILeBf3YBJdPxnD7+NsZ8UT4PFDAF7C\r\nNvclyA1lo81SH2vgYn87dDrXAapJLDt/U2i+zcP/Fnjo31Z92+vLVI8oyH83\r\n/hHp6XWEaFZbD1kR7w6Idkfqz9XcAWlxjLsaVOl/rYXBlaxAvrQV6G4Jwdrj\r\ndmaRTifbN1nwq9hEzQD5AiKzypX2l0RshVg=\r\n=RcU6\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_5.0.0-beta.0_1669451618161_0.6823012780550388"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"5.0.0-beta.1":{"name":"sonolus-express","version":"5.0.0-beta.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.14","@types/node":"^16.18.2","@typescript-eslint/eslint-plugin":"^5.41.0","@typescript-eslint/parser":"^5.41.0","eslint":"^8.26.0","eslint-config-prettier":"^8.5.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.7.1","typescript":"^4.8.4"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^6.0.0-beta.3"},"dependencies":{"zod":"^3.19.1"},"gitHead":"aafe4634e77efbf5fb5c342c5a424e3718345924","readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@5.0.0-beta.1","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-ad167DjqQGQb4ZtLsuU07GRQMvMw8I6SUUmT4rK1axO4CMI/i2NXoMyCxfkhEbT+O3LW0gZwP6g9Jde4XVHL3w==","shasum":"18a3d9db47fa5bc5462440721586e184f37c2ce9","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-5.0.0-beta.1.tgz","fileCount":127,"unpackedSize":136547,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCAiM5fO+1gCagR5MbxMdgAN48gTgrpUYg+oChINYu4jwIgO+nzexiDUgGG8SaHEXDTSBA7gitaDuivjnQyTqKJ5c0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjgyXmACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmovvw//ZzYaFRk1sQEGHkKrSQxkKVicSccj25noJ0E+6a3cGtwuCpJR\r\n79yjBbnkad7Ks+bk655QnWFx274iLg5krBNnAArpBmQ7suVqvSOvIwaYGUwW\r\nFQj+mU2BgijkipIFmUCiOTYq2FPlfoExXcVtVr1mcK1R6QAbZk+V227IrKHM\r\nX066nBqce8yc3aNweGoHJwHARKvAKjUMRO5liegmGFmxPxUmFiNrBxdBN8Ae\r\nE2Q0kkfrzrdNEHw0p3yRnGmlOymS/bto4LQu/JAiHFS/b/BYGcgtw2IRZpgs\r\nipzhlQX04bLw6UVoH8aR8/wZWBIA6EpYzDvi4M8ldkWBOuP7s/9yLaOnpJjf\r\nzGkPgOjiwr4OwNLDHeZ9BHlI8Qy6+4gSxmABZKbhxSxpAlmZyhERpASOCYI9\r\ncxvgmIAfZpvlwRCc7nFo75F2giKuov29clgvk9KWEAkj5YcXcToi1FY+Niyi\r\nK6MN9Jqva/+LaJ5uerfJZ5ErSP7QyT6a+qoUQYc2kj37DbyO+cwPlezqXHv9\r\n2eUTppMJrJL+S1g0FNkj4Rh1EISIs5VhF9+w4Ejynt6B8zPODsSrPoyHBNkM\r\n3VIuDc68QW/wqQUVtz7jnQsXeszZe7nGXwDUyYNVRwLM5vPai7CduWZTvJPd\r\nH9npmWCcHYZxbpfZcnc1TaSvIhjXGIwTKOE=\r\n=Mog+\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_5.0.0-beta.1_1669539302285_0.01480810647730424"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"5.0.0":{"name":"sonolus-express","version":"5.0.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.14","@types/node":"^16.18.2","@typescript-eslint/eslint-plugin":"^5.41.0","@typescript-eslint/parser":"^5.41.0","eslint":"^8.26.0","eslint-config-prettier":"^8.5.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.7.1","typescript":"^4.8.4"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^6.0.0"},"dependencies":{"zod":"^3.19.1"},"gitHead":"208efb2dfbe631975b0772ad624b453ecebed177","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@5.0.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-vO1CYWq9rJpXGPYzkjicZoBnAJpndIr3iHWIu4mXza+nN8jxhCy+2JZ0e+Q1n4Gg4JxsfQlEKYfRL4uyttAX0w==","shasum":"d6dd784d8e9bd6128c5a08ce5f9ce230ff71a0e2","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-5.0.0.tgz","fileCount":127,"unpackedSize":136533,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCUx3cIHdgH2ae7LPeUNTCYs6Eqb4iM687pjz1jO+2RygIhAIiGHyNhKgVR36i89QVGwSsduLbRKVc6ckrksklIAnB4"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjkbt3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmogCg//fInXVcYK5u61/4VuIfObDYmtVJ+vWhId3hSCpBeUoxGt42f7\r\ns5FhmLL9CJbxsszEptE5d/B+Qb1EtiOpg1+N0tEPFiBKT8sr/EkRq8hT++ug\r\nKGBtN17SMXt+CFwZJ0PlXQmwgTCcFd4j4TXM4eoGeYO/EZrOAtdVJfiiBqi6\r\nfNQjWoro6Q/bhR7xRSn3T2pC5+H1TSJ0FUuJ/VVvgqQrltY5HKvPc9aQHdoy\r\nFByfebbI0fa1CBlwZhfXvnXkqZQmdSEkYJP8LZ5wi7f6F/b71DxbKpVMY95P\r\nqb8DFHSZxp4sX7zKHyrLKVR4caet9Js5WyoWMSqW0QWXR9LxJm4ZtlB0jOM/\r\npD5FMDrdCQkv6xa61wqqQsTfPQoDWPe+JtqT2kEv2fqOjt/a1mYUEN9XtOfs\r\n3hVp2N5Y+lReqxcRV3CJ9zhAPP0DFbCj06Q8GkG5hsxfrvH6n3sHWrLEnPTo\r\nnMxcRQm1RA6N2YXoTuyW+GW6h++OmouqTST/k0R47SuSB2lz3yUNCYEaSLL5\r\n8/eIbpqxzeu/dPbmi9PPJ4pmLSnLdfGIo6UD8t4EuhdP7cZV04Vy2NhRYqCt\r\nMJdnfkbZ9aUcWW831YzaPfiHSQ58/HSMHUiSQpp88fXAnijKZZKlpZpyQ+qQ\r\nVoHGU3Ke6INqU1sWbpuK8PuHijQpFDDj2YM=\r\n=UH1g\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_5.0.0_1670495095388_0.5954873136620857"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.0.0-beta.0":{"name":"sonolus-express","version":"6.0.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"lint":"tsc -p . --noEmit && eslint --ext .ts src/","build":"tsc -p ."},"devDependencies":{"@tsconfig/node16":"^1.0.3","@types/express":"^4.17.17","@types/node":"^16.18.23","@typescript-eslint/eslint-plugin":"^5.57.1","@typescript-eslint/parser":"^5.57.1","eslint":"^8.37.0","eslint-config-prettier":"^8.8.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.8.7","prettier-plugin-organize-imports":"^3.2.2","typescript":"^5.0.3"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"^7.0.0-beta.0"},"dependencies":{"zod":"^3.21.4"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus.js\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"791e6fbbc7e8c6bc71c11eac12004db5081dc465","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@6.0.0-beta.0","_nodeVersion":"16.14.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-i1/aE1TsxckSKhIMVqZUIrocwuCy1JuJmzUO8H0hMviomdNtdZdfV2ktsAnVL2LrxU9OZM/mfrK3uTlL33xYlg==","shasum":"8fbb09d2ef51f09637bf1c16ba3fdd5260600277","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.0.0-beta.0.tgz","fileCount":127,"unpackedSize":133512,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDe1F2L64uh/JWtGqhaQxEuSbuBUz9NhLSuqG6z3/ITEgIgeIE7BAqPU4hp3JThIyPLp+j1jeRAR8nb2XE3tYC7Hdo="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkRXDzACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmq0iBAAmfhU0h/4GJArm6XcscPtGyRo2GH/5PmxMzPFMgjBcZFjSSf2\r\nCRG0KltvMI3ZVvQkFNgDZUCJWTwSMc2OA7ffWgzHozy94dJ2x+dysJ0mCgbb\r\n4dcy/bruOCL1ffhiQ2XyxCO454jqQTT9HQO8/wquLDxbxCYHR9Otpmh9psKe\r\nRFetKp565o/aVzFcs8emFu2zTMVQzUGdM1FhwF24vYKk/Qwv6Uw1wqf8lPnV\r\nt15QXdfAhzzMDk8/+fVZ7VbMyZtEONoPAwXFZQ9h9JX2sq9R+P/Il4w2rJ35\r\ndMQhkNAduf1n6CpnwoJ7VC5BBlxAiNm96Cp+UJIfkkykjz8Uhp9W4XOFNhRy\r\n3UV7+qGERPQG2MTBU2p8B70DVr2hszdMyOH0zDyKFU827QqLgX/2nNk1+EkK\r\n96vJcpA7acc4ILXDo4xP9jbF8OTxirGGTbrlrMDPspQrHgK5xtGI8m7tU4SF\r\nPhoCddWfkjX5C237+mQlwZ3xnFyIUGdEZxoSyzEA0vdK+eu2e0HxK99bXwhc\r\nWGUXXzzbZDogwQnw6r8tlJSA8dHROlgdsVo8eFjWIufjLoIEn+HNF3Ers0Fh\r\nQHXKst54PU38LmIaFEqWdQsrtOIAkYQm5gDQKMrmFmRMlObKtqVJgI7OvOah\r\nU/qgIpzwlWMU+8cdlthTUztnutE0z6dTZRI=\r\n=5cd5\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.0.0-beta.0_1682272499356_0.8818662433560849"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.0.0":{"name":"sonolus-express","version":"6.0.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint --ext .ts ./src","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.17","@types/node":"^16.18.35","@typescript-eslint/eslint-plugin":"^5.59.9","@typescript-eslint/parser":"^5.59.9","eslint":"^8.42.0","eslint-config-prettier":"^8.8.0","eslint-plugin-prettier":"^4.2.1","prettier":"^2.8.8","prettier-plugin-organize-imports":"^3.2.2","typescript":"~5.1.3"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"~7.0.0"},"dependencies":{"zod":"^3.21.4"},"gitHead":"c5b7cb9900f417242ce77bba19229c589b0582a1","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@6.0.0","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-su7aoFx71CuXvNYoSKwGp5PExjK3TGQs5DvFErB/pZYQ+t8cXDF/t2bZ7bGrYoTyAjLaT2AHkOUiLpeMxbYBhg==","shasum":"bfa762be63abbaac4d4469edba2523684438abcf","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.0.0.tgz","fileCount":127,"unpackedSize":132354,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDgxSlZNj3MlUWigd+OdgYfnRTp9Eatrk1a+Qekp7joBwIhAI6+YMVBAe/ZVV3PgrPhGgW+f3eSOlZkrKfSSW3Dj3Jk"}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.0.0_1686509298640_0.07463086050198586"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.1.0-beta.0":{"name":"sonolus-express","version":"6.1.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint --ext .ts ./src","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.17","@types/node":"^16.18.39","@typescript-eslint/eslint-plugin":"^6.2.0","@typescript-eslint/parser":"^6.2.0","eslint":"^8.45.0","eslint-config-prettier":"^8.8.0","eslint-plugin-prettier":"^5.0.0","prettier":"^3.0.0","prettier-plugin-organize-imports":"^3.2.3","typescript":"~5.1.6"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"~7.2.0-beta.0"},"dependencies":{"zod":"^3.21.4"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"da318a69b92706c010acad724fe81e73dc075efb","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@6.1.0-beta.0","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-Y1dYLJ2PEJWlkKcrnI2L249jo391zwjKltDxzJnhGS+CbzrectWH+656ta8tddqR8vBbY+eDVduWboT7omDB3w==","shasum":"c3267dcc0847ba6e41a6d170033528749d337ad3","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.1.0-beta.0.tgz","fileCount":127,"unpackedSize":133668,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHu4cCf6366F6tAuHUJytrxuhzWF05TKpt2hvTKDrtpJAiEAqKS9bYp1mga7N9CFBGGbUkbwlMxWZGvJQ71bsHhbwPc="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.1.0-beta.0_1690408331337_0.2853156541192694"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.1.0":{"name":"sonolus-express","version":"6.1.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint --ext .ts ./src","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.17","@types/node":"^16.18.40","@typescript-eslint/eslint-plugin":"^6.3.0","@typescript-eslint/parser":"^6.3.0","eslint":"^8.47.0","eslint-config-prettier":"^9.0.0","eslint-plugin-prettier":"^5.0.0","prettier":"^3.0.1","prettier-plugin-organize-imports":"^3.2.3","typescript":"~5.1.6"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"~7.2.0"},"dependencies":{"zod":"^3.21.4"},"gitHead":"c546686dac20a03a22e81212f6d7d98ed1988d09","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@6.1.0","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-qMfNirTLmygo3L4UF28wUWItMRJkMcJjTbbzJ51RQaGJHIbgbByAPqSlFBZ8oPJp/oHTbSscGX8tJ2CopughzA==","shasum":"e2087c720852b0e586096af9578bd732acc53b67","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.1.0.tgz","fileCount":127,"unpackedSize":133552,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICIbvO7moWMQomZahl70MKRDt+zmwgBOdpZIj8h4D4t/AiEA+9tF1la8ZYuco1WvdteQYMgWeC5qKMNJ+AazBM31kTA="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.1.0_1691822297997_0.6498223542552926"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.2.0-beta.0":{"name":"sonolus-express","version":"6.2.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint --ext .ts ./src","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.17","@types/node":"^16.18.40","@typescript-eslint/eslint-plugin":"^6.3.0","@typescript-eslint/parser":"^6.3.0","eslint":"^8.47.0","eslint-config-prettier":"^9.0.0","eslint-plugin-prettier":"^5.0.0","prettier":"^3.0.1","prettier-plugin-organize-imports":"^3.2.3","typescript":"~5.1.6"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"~7.3.0-beta.0"},"dependencies":{"zod":"^3.21.4"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"f67d3016630c107eb1b54957b4fbdb28162abde2","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@6.2.0-beta.0","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-QEyvmuFgDCOWzxOOt8YV/jhqwQfYJzw5pTnML6tl+VLQSCNTrubg1vxHae9fYRLXd/ufAho5a5gPqRP+7WKpaw==","shasum":"b14c3eb292a22026985ad09e2d26be9872425909","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.2.0-beta.0.tgz","fileCount":127,"unpackedSize":134339,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRndXhaG0/no5Kbgcl6Fz6IinCAlOo49DwyUSVzO61hQIgBRGij0f/RbAP7BnFEYSgbZta7FYjnIUwS3o1EI/GeHk="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.2.0-beta.0_1693218823413_0.8921755046275877"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.2.0":{"name":"sonolus-express","version":"6.2.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.17","@types/node":"^16.18.48","@typescript-eslint/eslint-plugin":"^6.5.0","@typescript-eslint/parser":"^6.5.0","eslint":"^8.48.0","eslint-config-prettier":"^9.0.0","prettier":"^3.0.3","prettier-plugin-organize-imports":"^3.2.3","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"~7.3.0"},"dependencies":{"zod":"^3.22.2"},"gitHead":"610a1c01c8bd450165aa865e92f37e7ec32da907","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@6.2.0","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-nVyQ2aE8M7nD5ceyASwn3nBiiWTfLNVtASwaUBN+vkjy4VNO+sHY94IqyHixXsLp8elmikmEmpin6XiR8BJV1g==","shasum":"e6ed4cf122e996f4a1756e4ea7b805a100c3c54b","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.2.0.tgz","fileCount":127,"unpackedSize":134293,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD5AgchT1jKOGj5m2yr+cFdCoHvfQWRVFL+jA0VTz358gIgDSU6jlIhlhrdUgqB7whabWhX+LyfwTec+NWie4X+G98="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.2.0_1693753065410_0.4819223378426032"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.3.0-beta.0":{"name":"sonolus-express","version":"6.3.0-beta.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.17","@types/node":"^16.18.48","@typescript-eslint/eslint-plugin":"^6.5.0","@typescript-eslint/parser":"^6.5.0","eslint":"^8.48.0","eslint-config-prettier":"^9.0.0","prettier":"^3.0.3","prettier-plugin-organize-imports":"^3.2.3","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"~7.4.0-beta.0"},"dependencies":{"zod":"^3.22.2"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"bcbe0c5510abe387b8950c8de558c16d5411526a","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@6.3.0-beta.0","_nodeVersion":"18.17.1","_npmVersion":"9.6.7","dist":{"integrity":"sha512-59QxISxRXKuk1j8zfMNonuSdVnM/EdirtTVKHMNgcZOoOySMuhQxq5SeCPyFcb1VeJY4EfvOym8dasG21dix6A==","shasum":"c9dd3fa8462239417e616bdf2fef2ac5513298ef","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.3.0-beta.0.tgz","fileCount":127,"unpackedSize":135047,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDlFvhyn20OPAzzrwhW7szkwNVvKbRUpSop+V0WrPYFZQIhALhnd2RmHBvQLso8Qt0yBvTblz9qciewaY1k9L2Ttudg"}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.3.0-beta.0_1700361613061_0.5462696448270146"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.3.0":{"name":"sonolus-express","version":"6.3.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^16.18.65","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","eslint-config-prettier":"^9.0.0","prettier":"^3.1.0","prettier-plugin-organize-imports":"^3.2.4","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"~7.4.0"},"dependencies":{"zod":"^3.22.4"},"readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"5d477dac84e287626564bbb4c0e38e46480e5dce","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_id":"sonolus-express@6.3.0","_nodeVersion":"18.17.1","_npmVersion":"9.6.7","dist":{"integrity":"sha512-wY10Jy4Jtt4uMJ/Tl24S96u+qEC88CHpj06lqAB+8xx1QbkJ2cjaV3FAXVZBkmt2oS3refoLTBM7c/dIH9bRiw==","shasum":"bbd02191ee31a91410e952c821da155428062c5a","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.3.0.tgz","fileCount":127,"unpackedSize":135035,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBRNKyI0M2KFvxynLdWu8BHVmdHtOCMpz8tvur8dk+DQAiEAnrGsX9jg4If93sKOM0C45hYfxNP+qWOThyPwonYJb+o="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.3.0_1701006583012_0.8561998598289142"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.3.1":{"name":"sonolus-express","version":"6.3.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^16.18.65","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","eslint-config-prettier":"^9.0.0","prettier":"^3.1.0","prettier-plugin-organize-imports":"^3.2.4","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"~7.4.0"},"dependencies":{"zod":"^3.22.4"},"_id":"sonolus-express@6.3.1","gitHead":"f958e7be16fb44e609d5d361cc8ac5d51b79e1c2","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-bHPS/NnPxO38RS7CoMvcZswThA0/Ijov5K851jcyYpsG2R846981pWAQOH4zQtIxUoxST7/kJ/Z3jNa/WeNEHA==","shasum":"2f3f07054ea510f9441c139e51d2788a8f76cb03","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.3.1.tgz","fileCount":127,"unpackedSize":135083,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGNR3ZTUrchJLvxsffSNUz8alGUCg96cEO7h4gfyUYxSAiEA3a3vqfeXtkaM2QUeAev8GKpssSVW8Tp3y8qJq1aTRco="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.3.1_1703875282275_0.9565153285840764"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.4.0-alpha.0":{"name":"sonolus-express","version":"6.4.0-alpha.0","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^16.18.65","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","eslint-config-prettier":"^9.0.0","prettier":"^3.1.0","prettier-plugin-organize-imports":"^3.2.4","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"7.5.0-alpha.0"},"dependencies":{"zod":"^3.22.4"},"_id":"sonolus-express@6.4.0-alpha.0","readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.basePath`: base path of the Sonolus server (should not have ending `/`).\n-   `options.authentication`: if Sonolus server requires authentication.\n-   `options.sessionAddress`: session address to be used in authentication.\n-   `options.sessionDuration`: session duration to be used in authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.levels`: configurations for levels.\n-   `options.skins`: configurations for skins.\n-   `options.backgrounds`: configurations for backgrounds.\n-   `options.effects`: configurations for effects.\n-   `options.particles`: configurations for particles.\n-   `options.engines`: configurations for engines.\n\n#### `sessionAddress`\n\nSession address. As passed in from `constructor(app, options)`.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `createSessionHandler`\n\nHandler for creating an authentication session.\n\n#### `findSessionHandler`\n\nHandler for finding an authentication session.\n\n#### `checkSessionHandler`\n\nHandler for checking an authentication session.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `levelListHandler`\n\nHandler for requesting level list. Defaults to `defaultLevelListHandler`.\n\n#### `skinListHandler`\n\nHandler for requesting skin list. Defaults to `defaultSkinListHandler`.\n\n#### `backgroundListHandler`\n\nHandler for requesting background list. Defaults to `defaultBackgroundListHandler`.\n\n#### `effectListHandler`\n\nHandler for requesting effect list. Defaults to `defaultEffectListHandler`.\n\n#### `particleListHandler`\n\nHandler for requesting particle list. Defaults to `defaultParticleListHandler`.\n\n#### `engineListHandler`\n\nHandler for requesting engine list. Defaults to `defaultEngineListHandler`.\n\n#### `levelDetailsHandler`\n\nHandler for requesting level details. Defaults to `defaultLevelDetailsHandler`.\n\n#### `skinDetailsHandler`\n\nHandler for requesting skin details. Defaults to `defaultSkinDetailsHandler`.\n\n#### `backgroundDetailsHandler`\n\nHandler for requesting background details. Defaults to `defaultBackgroundDetailsHandler`.\n\n#### `effectDetailsHandler`\n\nHandler for requesting effect details. Defaults to `defaultEffectDetailsHandler`.\n\n#### `particleDetailsHandler`\n\nHandler for requesting particle details. Defaults to `defaultParticleDetailsHandler`.\n\n#### `engineDetailsHandler`\n\nHandler for requesting engine details. Defaults to `defaultEngineDetailsHandler`.\n\n### `filterLevelInfosByKeywords(infos, keywords)`\n\nFilter level infos by keywords on `name`, `rating`, `title`, `artists`, `author`, and `description`.\n\n-   `infos`: level infos.\n-   `keywords`: keywords.\n\n### `filterSkinInfosByKeywords(infos, keywords)`\n\nFilter skin infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: skin infos.\n-   `keywords`: keywords.\n\n### `filterBackgroundInfosByKeywords(infos, keywords)`\n\nFilter background infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: background infos.\n-   `keywords`: keywords.\n\n### `filterEffectInfosByKeywords(infos, keywords)`\n\nFilter effect infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: effect infos.\n-   `keywords`: keywords.\n\n### `filterParticleInfosByKeywords(infos, keywords)`\n\nFilter particle infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: particle infos.\n-   `keywords`: keywords.\n\n### `filterEngineInfosByKeywords(infos, keywords)`\n\nFilter engine infos by keywords on `name`, `title`, `subtitle`, `author`, and `description`.\n\n-   `infos`: engine infos.\n-   `keywords`: keywords.\n\n### `filterInfosByKeywords(infos, props, keywords)`\n\nFilter item infos by keywords on specified props.\n\nRecommended to use functions for the specific info type instead.\n\n-   `infos`: item infos.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateInfos(infos, page, perPage)`\n\nPaginate item infos by page and per page size.\n\n-   `infos`: item infos.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface LevelInfo {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"3e8c6568cd30c11601a6a2e45cd75d37d5b1d31e","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-+OABH0Lt3Sl8G1X5doh7tIXSysc63Iuijtp6xW4Qs6DaD/7ZnqUS3qR+6ZJnN+4+TeAMkGHhL0Lnu43073cdZQ==","shasum":"fefd41fe6d3bcca5564f186f57a56ec941cb8073","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.4.0-alpha.0.tgz","fileCount":137,"unpackedSize":147676,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH56h5Iv0BWFQ0H1hUArV73weZkzpWFk9xc/cLt7WiqpAiArcRAjhIpKa1C3naZsOT3JVpqebCYzDjgRjEr36xxmhA=="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.4.0-alpha.0_1705808467186_0.9049473572094586"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.4.0-alpha.1":{"name":"sonolus-express","version":"6.4.0-alpha.1","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^16.18.65","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","eslint-config-prettier":"^9.0.0","prettier":"^3.1.0","prettier-plugin-organize-imports":"^3.2.4","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"7.5.0-alpha.1"},"dependencies":{"zod":"^3.22.4"},"_id":"sonolus-express@6.4.0-alpha.1","readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.address`: address of server (should not have ending `/`).\n-   `options.basePath`: base path of server (should not have ending `/`).\n-   `options.authentication`: if server has authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.postsConfig`: configurations for posts.\n-   `options.levelsConfig`: configurations for levels.\n-   `options.skinsConfig`: configurations for skins.\n-   `options.backgroundsConfig`: configurations for backgrounds.\n-   `options.effectsConfig`: configurations for effects.\n-   `options.particlesConfig`: configurations for particles.\n-   `options.enginesConfig`: configurations for engines.\n-   `options.replaysConfig`: configurations for replays.\n\n#### `address`\n\nAs passed in from `constructor(app, options)`.\n\n#### `authentication`\n\nAs passed in from `constructor(app, options)`.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.posts`: posts.\n-   `db.playlists`: playlists.\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n-   `db.replays`: replays.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `authenticateHandler`\n\nHandler for authentication. Defaults to `defaultAuthenticateHandler`.\n\nReturning `AuthenticateServerResponse` causes authentication to succeed, and returning `undefined` rejects the authentication attempt.\n\n#### `sessionHandler`\n\nHandler for authentication session. Defaults to `defaultSessionHandler`.\n\nReturning `true` to allow request to proceed, and returning `false` rejects the request.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `posts`, `playlists`, `levels`, etc\n\nConfig for items.\n\n-   `*.searches`: searches.\n-   `*.infoHandler`: Handler for requesting item info. Defaults to `default*InfoHandler`.\n-   `*.listHandler`: Handler for requesting item list. Defaults to `default*ListHandler`.\n-   `*.detailsHandler`: Handler for requesting item details. Defaults to `default*DetailsHandler`.\n\n### `filter*ItemsByKeywords(items, keywords)`\n\nFilter items by keywords.\n\n-   `items`: items.\n-   `keywords`: keywords.\n\n### `filterItemsByKeywords(items, props, keywords)`\n\nFilter items by keywords on specified props.\n\nRecommended to use functions for the specific item type (`filter*ItemsByKeywords`) instead.\n\n-   `items`: items.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateItems(items, page, perPage)`\n\nPaginate items by page and per page size.\n\n-   `items`: items.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface DatabaseLevelItem {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"827e7163ba315b62ca9fccc81698bbb93f1e1f4b","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-Nn9KpbwpW1HECzA5vPytCVwBAR0vdJzI4m7XB0Va9TUoP3tn7i5R4H3xvVpOknBb2EOFi3qZRrB4MeGeI9WF9A==","shasum":"b48a232e5aa066d056406aaedb30a974f48a9609","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.4.0-alpha.1.tgz","fileCount":189,"unpackedSize":182424,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICY8LOLouSCGOB7+I5T9DYfpsuTTCL/gB4Uc4HAsW1H3AiEAjwrqI4ECWLDXqxspWPRFeeKBZ944v7vJzpwIy4xFXdk="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.4.0-alpha.1_1707135840203_0.9635178731163796"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.4.0-alpha.2":{"name":"sonolus-express","version":"6.4.0-alpha.2","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^16.18.65","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","eslint-config-prettier":"^9.0.0","prettier":"^3.1.0","prettier-plugin-organize-imports":"^3.2.4","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"7.5.0-alpha.2"},"dependencies":{"zod":"^3.22.4"},"_id":"sonolus-express@6.4.0-alpha.2","readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.address`: address of server (should not have ending `/`).\n-   `options.basePath`: base path of server (should not have ending `/`).\n-   `options.authentication`: if server has authentication.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.postsConfig`: configurations for posts.\n-   `options.levelsConfig`: configurations for levels.\n-   `options.skinsConfig`: configurations for skins.\n-   `options.backgroundsConfig`: configurations for backgrounds.\n-   `options.effectsConfig`: configurations for effects.\n-   `options.particlesConfig`: configurations for particles.\n-   `options.enginesConfig`: configurations for engines.\n-   `options.replaysConfig`: configurations for replays.\n\n#### `address`\n\nAs passed in from `constructor(app, options)`.\n\n#### `authentication`\n\nAs passed in from `constructor(app, options)`.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.posts`: posts.\n-   `db.playlists`: playlists.\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n-   `db.replays`: replays.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `authenticateHandler`\n\nHandler for authentication. Defaults to `defaultAuthenticateHandler`.\n\nReturning `AuthenticateServerResponse` causes authentication to succeed, and returning `undefined` rejects the authentication attempt.\n\n#### `sessionHandler`\n\nHandler for authentication session. Defaults to `defaultSessionHandler`.\n\nReturning `true` to allow request to proceed, and returning `false` rejects the request.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `posts`, `playlists`, `levels`, etc\n\nConfig for items.\n\n-   `*.searches`: searches.\n-   `*.infoHandler`: Handler for requesting item info. Defaults to `default*InfoHandler`.\n-   `*.listHandler`: Handler for requesting item list. Defaults to `default*ListHandler`.\n-   `*.detailsHandler`: Handler for requesting item details. Defaults to `default*DetailsHandler`.\n\n### `filter*ItemsByKeywords(items, keywords)`\n\nFilter items by keywords.\n\n-   `items`: items.\n-   `keywords`: keywords.\n\n### `filterItemsByKeywords(items, props, keywords)`\n\nFilter items by keywords on specified props.\n\nRecommended to use functions for the specific item type (`filter*ItemsByKeywords`) instead.\n\n-   `items`: items.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateItems(items, page, perPage)`\n\nPaginate items by page and per page size.\n\n-   `items`: items.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface DatabaseLevelItem {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"10e73bb907db506722b246b46c3cfd7ef643ed9c","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-/zJzQpalIoJFSLegivLzsLQNwjEzP0TbQ/6x2Zg0n336IlI1qK9QdOua8eVA9KnAxQI9XzJXRGh3sof/t43K7g==","shasum":"923fe12503a06b88b0caa1a04dd07cf26bac3ca6","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.4.0-alpha.2.tgz","fileCount":189,"unpackedSize":174289,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDke2TDG77aCvid2lg7mE4OmDIcpLDCj/HeDqNKB8HgTAiAnI8bw+f6BJMZ6fyoIrAg2YQPaY+ZjSESrgEQI7oQJkg=="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.4.0-alpha.2_1709791877393_0.026405693157138144"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.4.0-alpha.3":{"name":"sonolus-express","version":"6.4.0-alpha.3","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^16.18.65","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","eslint-config-prettier":"^9.0.0","prettier":"^3.1.0","prettier-plugin-organize-imports":"^3.2.4","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"7.5.0-alpha.2"},"dependencies":{"zod":"^3.22.4"},"_id":"sonolus-express@6.4.0-alpha.3","readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.address`: address of server (should not have ending `/`).\n-   `options.basePath`: base path of server (should not have ending `/`).\n-   `options.authentication`: if server has authentication.\n-   `options.multiplayer`: if server has multiplayer.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.redirectPath`: path to use for redirect when `options.mode` is `redirect`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.postSearches`: searches for posts.\n-   `options.levelSearches`: searches for levels.\n-   `options.skinSearches`: searches for skins.\n-   `options.backgroundSearches`: searches for backgrounds.\n-   `options.effectSearches`: searches for effects.\n-   `options.particleSearches`: searches for particles.\n-   `options.engineSearches`: searches for engines.\n-   `options.replaySearches`: searches for replays.\n-   `options.roomSearches`: searches for rooms.\n-   `options.roomCreates`: creates for rooms.\n\n#### `address`\n\nAs passed in from `constructor(app, options)`.\n\n#### `authentication`\n\nAs passed in from `constructor(app, options)`.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.posts`: posts.\n-   `db.playlists`: playlists.\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n-   `db.replays`: replays.\n-   `db.rooms`: multiplayer rooms.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `authenticateHandler`\n\nHandler for authentication. Defaults to `defaultAuthenticateHandler`.\n\nReturning `AuthenticateServerResponse` causes authentication to succeed, and returning `undefined` rejects the authentication attempt.\n\n#### `sessionHandler`\n\nHandler for authentication session. Defaults to `defaultSessionHandler`.\n\nReturning `true` to allow request to proceed, and returning `false` rejects the request.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `postsConfig`, `playlistsConfig`, `levelsConfig`, etc\n\nConfig for items.\n\n-   `*.searches`: searches.\n-   `*.infoHandler`: handler for requesting item info. Defaults to `default*InfoHandler`.\n-   `*.listHandler`: handler for requesting item list. Defaults to `default*ListHandler`.\n-   `*.detailsHandler`: handler for requesting item details. Defaults to `default*DetailsHandler`.\n\n#### `multiplayerConfig`\n\nConfig for multiplayer.\n\n-   `multiplayerConfig.creates`: creates.\n-   `multiplayerConfig.createRoomHandler`: handler for requesting server info. Defaults to `defaultServerInfoHandler`.\n-   `multiplayerConfig.joinRoomHandler`: handler for joining room. Defaults to `defaultJoinRoomHandler`.\n\n### `filter*ItemsByKeywords(items, keywords)`\n\nFilter items by keywords.\n\n-   `items`: items.\n-   `keywords`: keywords.\n\n### `filterItemsByKeywords(items, props, keywords)`\n\nFilter items by keywords on specified props.\n\nRecommended to use functions for the specific item type (`filter*ItemsByKeywords`) instead.\n\n-   `items`: items.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateItems(items, page, perPage)`\n\nPaginate items by page and per page size.\n\n-   `items`: items.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface DatabaseLevelItem {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"b3972b6ad9e48c0cc15c184ec0fcea885423a5c7","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-xhojtpMQ/3CHJPo3LkCopzcJ/lVneWkD5xeSUu02mfpEJbHA1l/PttZS7xMigHpX8/piond3IcG+xvMiQj5Tgg==","shasum":"4b0330bfe1046f6dee5fe8ee0037972a70f0599b","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.4.0-alpha.3.tgz","fileCount":205,"unpackedSize":190837,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGXaObCKwP+CXj7qgJciv5QoZKJds2+1hlak82w/YF+/AiBTwszJUoP0xhIhdz8CS6mr7mkAxpM551H+CrGRdx1aVw=="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.4.0-alpha.3_1710593485462_0.3980129647359172"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.4.0-alpha.4":{"name":"sonolus-express","version":"6.4.0-alpha.4","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^16.18.65","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","eslint-config-prettier":"^9.0.0","prettier":"^3.1.0","prettier-plugin-organize-imports":"^3.2.4","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"7.5.0-alpha.4"},"dependencies":{"zod":"^3.22.4"},"_id":"sonolus-express@6.4.0-alpha.4","readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.address`: address of server (should not have ending `/`).\n-   `options.basePath`: base path of server (should not have ending `/`).\n-   `options.authentication`: if server has authentication.\n-   `options.multiplayer`: if server has multiplayer.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.redirectPath`: path to use for redirect when `options.mode` is `redirect`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.postSearches`: searches for posts.\n-   `options.levelSearches`: searches for levels.\n-   `options.skinSearches`: searches for skins.\n-   `options.backgroundSearches`: searches for backgrounds.\n-   `options.effectSearches`: searches for effects.\n-   `options.particleSearches`: searches for particles.\n-   `options.engineSearches`: searches for engines.\n-   `options.replaySearches`: searches for replays.\n-   `options.roomSearches`: searches for rooms.\n-   `options.roomCreates`: creates for rooms.\n\n#### `address`\n\nAs passed in from `constructor(app, options)`.\n\n#### `authentication`\n\nAs passed in from `constructor(app, options)`.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.posts`: posts.\n-   `db.playlists`: playlists.\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n-   `db.replays`: replays.\n-   `db.rooms`: multiplayer rooms.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `authenticateHandler`\n\nHandler for authentication. Defaults to `defaultAuthenticateHandler`.\n\nReturning `AuthenticateServerResponse` causes authentication to succeed, and returning `undefined` rejects the authentication attempt.\n\n#### `sessionHandler`\n\nHandler for authentication session. Defaults to `defaultSessionHandler`.\n\nReturning `true` to allow request to proceed, and returning `false` rejects the request.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `postsConfig`, `playlistsConfig`, `levelsConfig`, etc\n\nConfig for items.\n\n-   `*.searches`: searches.\n-   `*.infoHandler`: handler for requesting item info. Defaults to `default*InfoHandler`.\n-   `*.listHandler`: handler for requesting item list. Defaults to `default*ListHandler`.\n-   `*.detailsHandler`: handler for requesting item details. Defaults to `default*DetailsHandler`.\n\n#### `multiplayerConfig`\n\nConfig for multiplayer.\n\n-   `multiplayerConfig.creates`: creates.\n-   `multiplayerConfig.createRoomHandler`: handler for requesting server info. Defaults to `defaultServerInfoHandler`.\n-   `multiplayerConfig.joinRoomHandler`: handler for joining room. Defaults to `defaultJoinRoomHandler`.\n\n### `filter*ItemsByKeywords(items, keywords)`\n\nFilter items by keywords.\n\n-   `items`: items.\n-   `keywords`: keywords.\n\n### `filterItemsByKeywords(items, props, keywords)`\n\nFilter items by keywords on specified props.\n\nRecommended to use functions for the specific item type (`filter*ItemsByKeywords`) instead.\n\n-   `items`: items.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateItems(items, page, perPage)`\n\nPaginate items by page and per page size.\n\n-   `items`: items.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface DatabaseLevelItem {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"c57f260b37f13e425939fbfcd06498c48e96d221","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-dvSsPV1Soo+dKt2HHKPFGUsSCHtGntauFQ9ekD5REK/1ve1v8podM8gPn9jf64iLXA4qcTOm4JRrOa+HCHcglw==","shasum":"2ab9d4090b20986f31156c90dd0e07b1926ff0a7","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.4.0-alpha.4.tgz","fileCount":205,"unpackedSize":190871,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFXqAEPk50PSroPbXQM35pNqZDpfnruhUygFoZkN2qc4AiEA3Flyd8BT917TiS++WFg4FtA67fyyoowjbMTIZ6h7Nr8="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.4.0-alpha.4_1710791064069_0.19074989136752385"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"},"6.4.0-alpha.5":{"name":"sonolus-express","version":"6.4.0-alpha.5","description":"TypeScript based developer toolkit for Sonolus servers with Express.js","author":{"name":"NonSpicyBurrito"},"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"license":"MIT","keywords":["Sonolus"],"main":"dist","scripts":{"prebuild":"tsc -p . --noEmit && eslint . && prettier . --check","build":"tsc -p ."},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^16.18.65","@typescript-eslint/eslint-plugin":"^6.12.0","@typescript-eslint/parser":"^6.12.0","eslint":"^8.54.0","eslint-config-prettier":"^9.0.0","prettier":"^3.1.0","prettier-plugin-organize-imports":"^3.2.4","typescript":"~5.2.2"},"peerDependencies":{"express":"^4.18.2","sonolus-core":"7.5.0-alpha.4"},"dependencies":{"zod":"^3.22.4"},"_id":"sonolus-express@6.4.0-alpha.5","readme":"# sonolus-express\n\nTypeScript based developer toolkit for Sonolus servers with [Express.js](https://expressjs.com).\n\n## Links\n\n-   [Sonolus Website](https://sonolus.com)\n-   [Sonolus Wiki](https://wiki.sonolus.com)\n-   [sonolus-pack](https://github.com/Sonolus/sonolus-pack)\n\n## Features\n\n-   Setup all necessary routes automatically, compliant with Sonolus server specifications.\n-   Provide handlers to interact with each route.\n-   Load packs created by [sonolus-pack](https://github.com/Sonolus/sonolus-pack).\n\n## Installation\n\n```\nnpm install sonolus-express\n```\n\nA compatible version of [sonolus-core](https://github.com/Sonolus/sonolus-core) is also required as peer dependency. If it isn't installed automatically, you can do so with:\n\n```\nnpm install sonolus-core\n```\n\n## Example\n\n```ts\nconst port = 3000\nconst app = express()\n\nnew Sonolus(app).load('pack')\n\napp.listen(port, () => {\n    console.log('Server listening at port', port)\n})\n```\n\n## Documentation\n\n### `Sonolus`\n\n#### `constructor(app, options)`\n\nCreate a Sonolus server on `app` with `options`.\n\n-   `app`: [Express.js](https://expressjs.com) app.\n-   `options.address`: address of server (should not have ending `/`).\n-   `options.basePath`: base path of server (should not have ending `/`).\n-   `options.authentication`: if server has authentication.\n-   `options.multiplayer`: if server has multiplayer.\n-   `options.fallbackLocale`: fallback locale when user's preferred locale cannot be used.\n-   `options.mode`: share link handling mode, can be `custom`, `redirect`, or `spa`.\n-   `options.redirectPath`: path to use for redirect when `options.mode` is `redirect`.\n-   `options.spaRoot`: root of static SPA files to serve when `options.mode` is `spa`.\n-   `options.postSearches`: searches for posts.\n-   `options.levelSearches`: searches for levels.\n-   `options.skinSearches`: searches for skins.\n-   `options.backgroundSearches`: searches for backgrounds.\n-   `options.effectSearches`: searches for effects.\n-   `options.particleSearches`: searches for particles.\n-   `options.engineSearches`: searches for engines.\n-   `options.replaySearches`: searches for replays.\n-   `options.roomSearches`: searches for rooms.\n-   `options.roomCreates`: creates for rooms.\n\n#### `address`\n\nAs passed in from `constructor(app, options)`.\n\n#### `authentication`\n\nAs passed in from `constructor(app, options)`.\n\n#### `db`\n\nSonolus database. Can be modified to dynamically add/remove/reorder items.\n\n-   `db.posts`: posts.\n-   `db.playlists`: playlists.\n-   `db.levels`: levels.\n-   `db.skins`: skins.\n-   `db.backgrounds`: backgrounds.\n-   `db.effects`: effects.\n-   `db.particles`: particles.\n-   `db.engines`: engines.\n-   `db.replays`: replays.\n-   `db.rooms`: multiplayer rooms.\n\n#### `router`\n\nExpress router.\n\nInstalling more routes under `options.basePath` may not work due to SPA mode catch all routes. Instead install onto `router` rather than the original Express app.\n\n#### `load(path)`\n\nLoad a pack created by [sonolus-pack](https://github.com/NonSpicyBurrito/sonolus-pack).\n\n-   `path`: file path to the pack.\n\n#### `add(type, data, hash?)`\n\nAdd a resource. Returns `SRL` which can be used to access the resource.\n\n-   `type`: resource type.\n-   `data`: data (`Buffer`) or file path (`string`) to the data.\n-   `hash`: (optional) hash of the resource, will be calculated automatically when omitted.\n\n#### `localize(text, locale)`\n\nLocalize text using target and fallback locales.\n\n-   `text`: text to localize.\n-   `locale`: target locale.\n\n#### `authenticateHandler`\n\nHandler for authentication. Defaults to `defaultAuthenticateHandler`.\n\nReturning `AuthenticateServerResponse` causes authentication to succeed, and returning `undefined` rejects the authentication attempt.\n\n#### `sessionHandler`\n\nHandler for authentication session. Defaults to `defaultSessionHandler`.\n\nReturning `true` to allow request to proceed, and returning `false` rejects the request.\n\n#### `serverInfoHandler`\n\nHandler for requesting server info. Defaults to `defaultServerInfoHandler`.\n\n#### `postsConfig`, `playlistsConfig`, `levelsConfig`, etc\n\nConfig for items.\n\n-   `*.searches`: searches.\n-   `*.infoHandler`: handler for requesting item info. Defaults to `default*InfoHandler`.\n-   `*.listHandler`: handler for requesting item list. Defaults to `default*ListHandler`.\n-   `*.detailsHandler`: handler for requesting item details. Defaults to `default*DetailsHandler`.\n\n#### `multiplayerConfig`\n\nConfig for multiplayer.\n\n-   `multiplayerConfig.creates`: creates.\n-   `multiplayerConfig.createRoomHandler`: handler for requesting server info. Defaults to `defaultServerInfoHandler`.\n-   `multiplayerConfig.joinRoomHandler`: handler for joining room. Defaults to `defaultJoinRoomHandler`.\n\n### `filter*ItemsByKeywords(items, keywords)`\n\nFilter items by keywords.\n\n-   `items`: items.\n-   `keywords`: keywords.\n\n### `filterItemsByKeywords(items, props, keywords)`\n\nFilter items by keywords on specified props.\n\nRecommended to use functions for the specific item type (`filter*ItemsByKeywords`) instead.\n\n-   `items`: items.\n-   `props`: props to filter keywords on.\n-   `keywords`: keywords.\n\n### `paginateItems(items, page, perPage)`\n\nPaginate items by page and per page size.\n\n-   `items`: items.\n-   `page`: page.\n-   `perPage`: per page size, defaults to 20.\n\n### Using `meta` in TypeScript\n\nUse [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html):\n\n```ts\ndeclare module 'sonolus-core' {\n    interface DatabaseLevelItem {\n        meta: {\n            // user-defined meta information\n        }\n    }\n}\n```\n","readmeFilename":"README.md","gitHead":"ffba5fefe3254e5303762a2cd58ac55e85376eeb","bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"homepage":"https://github.com/Sonolus/sonolus-express#readme","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-malqH+qFp7tiJUTxAI3PtsWXVWwQq/J0SSxyCQl94ktSOjeZRON+z+Mhl0cNzutEoU6TcZgJw3TZu72yGT3EDA==","shasum":"059233b78f74683ac31d1015794f7555693838ad","tarball":"https://registry.npmjs.org/sonolus-express/-/sonolus-express-6.4.0-alpha.5.tgz","fileCount":205,"unpackedSize":190939,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBl4wpM442Rc1Z6dENhrXdYOgt8hRjLIdGhQzV/b+jc/AiEAqaUdRJ07g7uPj2tnG4YJDSUQn6Kp6TOCsWzAX1dYtbQ="}]},"_npmUser":{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"},"directories":{},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sonolus-express_6.4.0-alpha.5_1710796380901_0.40248583787270786"},"_hasShrinkwrap":false,"deprecated":"Moved to @sonolus/express"}},"time":{"created":"2021-06-25T18:45:43.341Z","0.1.0":"2021-06-25T18:45:43.506Z","modified":"2024-04-09T21:57:52.806Z","0.1.1":"2021-06-26T11:10:17.372Z","0.1.2":"2021-06-26T19:08:22.413Z","0.1.3":"2021-07-27T05:19:52.367Z","0.1.4":"2021-07-27T05:51:24.741Z","0.2.0-beta.0":"2021-08-07T13:45:51.066Z","0.2.0":"2021-08-08T19:01:32.919Z","1.0.0":"2021-09-29T04:30:25.241Z","1.1.0":"2021-10-01T07:51:08.818Z","1.1.1":"2021-11-11T13:53:17.605Z","2.0.0-beta.0":"2022-01-20T11:00:01.504Z","2.0.0-beta.1":"2022-01-22T03:29:09.292Z","2.0.0-beta.2":"2022-01-22T04:35:24.127Z","2.0.0":"2022-01-27T12:16:09.922Z","2.1.0-beta.0":"2022-02-20T11:01:56.973Z","2.1.0":"2022-02-23T16:05:55.977Z","2.1.1-beta.0":"2022-04-23T09:50:55.839Z","2.1.1":"2022-04-24T05:27:42.977Z","2.1.2-beta.0":"2022-07-31T07:10:53.637Z","2.1.2-beta.1":"2022-08-07T11:49:36.217Z","2.1.2":"2022-08-09T10:00:52.058Z","3.0.0":"2022-09-05T13:50:58.352Z","4.0.0-beta.0":"2022-09-10T02:26:02.281Z","4.0.0-beta.1":"2022-09-18T10:15:34.606Z","4.0.0":"2022-10-02T12:12:50.460Z","4.1.0-beta.0":"2022-11-02T17:46:49.057Z","4.1.0-beta.1":"2022-11-02T21:55:47.287Z","4.1.0-beta.3":"2022-11-03T19:07:33.168Z","4.1.0-beta.4":"2022-11-06T22:55:46.675Z","4.1.0":"2022-11-07T12:18:54.851Z","5.0.0-beta.0":"2022-11-26T08:33:38.397Z","5.0.0-beta.1":"2022-11-27T08:55:02.466Z","5.0.0":"2022-12-08T10:24:55.759Z","6.0.0-beta.0":"2023-04-23T17:54:59.542Z","6.0.0":"2023-06-11T18:48:18.805Z","6.1.0-beta.0":"2023-07-26T21:52:11.491Z","6.1.0":"2023-08-12T06:38:18.160Z","6.2.0-beta.0":"2023-08-28T10:33:43.591Z","6.2.0":"2023-09-03T14:57:45.565Z","6.3.0-beta.0":"2023-11-19T02:40:13.268Z","6.3.0":"2023-11-26T13:49:43.244Z","6.3.1":"2023-12-29T18:41:22.476Z","6.4.0-alpha.0":"2024-01-21T03:41:07.312Z","6.4.0-alpha.1":"2024-02-05T12:24:00.396Z","6.4.0-alpha.2":"2024-03-07T06:11:17.623Z","6.4.0-alpha.3":"2024-03-16T12:51:25.612Z","6.4.0-alpha.4":"2024-03-18T19:44:24.269Z","6.4.0-alpha.5":"2024-03-18T21:13:01.123Z"},"maintainers":[{"name":"nonspicyburrito","email":"burritoyangster@gmail.com"}],"description":"TypeScript based developer toolkit for Sonolus servers with Express.js","homepage":"https://github.com/Sonolus/sonolus-express#readme","keywords":["Sonolus"],"repository":{"type":"git","url":"git+https://github.com/Sonolus/sonolus-express.git"},"author":{"name":"NonSpicyBurrito"},"bugs":{"url":"https://github.com/Sonolus/sonolus-express/issues"},"license":"MIT","readme":"","readmeFilename":""}