merge: branch 'feat/backend' into main
This commit is contained in:
commit
23917b2976
2
.gitignore
vendored
2
.gitignore
vendored
@ -40,3 +40,5 @@ dist/
|
|||||||
build/
|
build/
|
||||||
|
|
||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
|
|
||||||
|
ucaptcha-config.yaml
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="TypeScriptCompiler">
|
|
||||||
<option name="useTypesFromServer" value="true" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -28,6 +28,8 @@
|
|||||||
<excludeFolder url="file://$MODULE_DIR$/packages/crawler/logs" />
|
<excludeFolder url="file://$MODULE_DIR$/packages/crawler/logs" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/data" />
|
<excludeFolder url="file://$MODULE_DIR$/data" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/redis" />
|
<excludeFolder url="file://$MODULE_DIR$/redis" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/ml" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/src" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
136
bun.lock
136
bun.lock
@ -19,6 +19,7 @@
|
|||||||
"hono": "^4.7.8",
|
"hono": "^4.7.8",
|
||||||
"hono-rate-limiter": "^0.4.2",
|
"hono-rate-limiter": "^0.4.2",
|
||||||
"ioredis": "^5.6.1",
|
"ioredis": "^5.6.1",
|
||||||
|
"limiter": "^3.0.0",
|
||||||
"postgres": "^3.4.5",
|
"postgres": "^3.4.5",
|
||||||
"rate-limit-redis": "^4.2.0",
|
"rate-limit-redis": "^4.2.0",
|
||||||
"yup": "^1.6.1",
|
"yup": "^1.6.1",
|
||||||
@ -32,6 +33,7 @@
|
|||||||
"packages/core": {
|
"packages/core": {
|
||||||
"name": "core",
|
"name": "core",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@koshnic/ratelimit": "^1.0.3",
|
||||||
"chalk": "^5.4.1",
|
"chalk": "^5.4.1",
|
||||||
"ioredis": "^5.6.1",
|
"ioredis": "^5.6.1",
|
||||||
"logform": "^2.7.0",
|
"logform": "^2.7.0",
|
||||||
@ -233,6 +235,8 @@
|
|||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||||
|
|
||||||
|
"@koshnic/ratelimit": ["@koshnic/ratelimit@1.0.3", "", { "dependencies": { "@types/chai": "^4.3.9", "@types/mocha": "^10.0.3", "chai": "^4.3.10", "ioredis": "^5.3.2", "mocha": "^10.2.0" } }, "sha512-cfDcSc+I+M4hNM+/4M+lfn8UuTq4OEFKl78ThOcGNaO7g8tWb1vm2qVpV1p1loYao1mqk00NBNwHQu2E/qFq2g=="],
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
|
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
|
||||||
|
|
||||||
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
|
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
|
||||||
@ -369,6 +373,8 @@
|
|||||||
|
|
||||||
"@types/bun": ["@types/bun@1.2.11", "", { "dependencies": { "bun-types": "1.2.11" } }, "sha512-ZLbbI91EmmGwlWTRWuV6J19IUiUC5YQ3TCEuSHI3usIP75kuoA8/0PVF+LTrbEnVc8JIhpElWOxv1ocI1fJBbw=="],
|
"@types/bun": ["@types/bun@1.2.11", "", { "dependencies": { "bun-types": "1.2.11" } }, "sha512-ZLbbI91EmmGwlWTRWuV6J19IUiUC5YQ3TCEuSHI3usIP75kuoA8/0PVF+LTrbEnVc8JIhpElWOxv1ocI1fJBbw=="],
|
||||||
|
|
||||||
|
"@types/chai": ["@types/chai@4.3.20", "", {}, "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ=="],
|
||||||
|
|
||||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||||
@ -381,6 +387,8 @@
|
|||||||
|
|
||||||
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||||
|
|
||||||
|
"@types/mocha": ["@types/mocha@10.0.10", "", {}, "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q=="],
|
||||||
|
|
||||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||||
|
|
||||||
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
||||||
@ -417,6 +425,8 @@
|
|||||||
|
|
||||||
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
|
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
|
||||||
|
|
||||||
|
"ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
|
||||||
|
|
||||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
@ -455,6 +465,8 @@
|
|||||||
|
|
||||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||||
|
|
||||||
|
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||||
|
|
||||||
"blob-to-buffer": ["blob-to-buffer@1.2.9", "", {}, "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA=="],
|
"blob-to-buffer": ["blob-to-buffer@1.2.9", "", {}, "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA=="],
|
||||||
|
|
||||||
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
|
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
|
||||||
@ -463,10 +475,14 @@
|
|||||||
|
|
||||||
"boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="],
|
"boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="],
|
||||||
|
|
||||||
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
"brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||||
|
|
||||||
|
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||||
|
|
||||||
"brotli": ["brotli@1.3.3", "", { "dependencies": { "base64-js": "^1.1.2" } }, "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg=="],
|
"brotli": ["brotli@1.3.3", "", { "dependencies": { "base64-js": "^1.1.2" } }, "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg=="],
|
||||||
|
|
||||||
|
"browser-stdout": ["browser-stdout@1.3.1", "", {}, "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="],
|
||||||
|
|
||||||
"browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
|
"browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
|
||||||
|
|
||||||
"bullmq": ["bullmq@5.52.1", "", { "dependencies": { "cron-parser": "^4.9.0", "ioredis": "^5.4.1", "msgpackr": "^1.11.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", "tslib": "^2.0.0", "uuid": "^9.0.0" } }, "sha512-u7CSV9wID3MBEX2DNubEErbAlrADgm8abUBAi6h8rQTnuTkhhgMs2iD7uhqplK8lIgUOkBIW3sDJWaMSInH47A=="],
|
"bullmq": ["bullmq@5.52.1", "", { "dependencies": { "cron-parser": "^4.9.0", "ioredis": "^5.4.1", "msgpackr": "^1.11.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", "tslib": "^2.0.0", "uuid": "^9.0.0" } }, "sha512-u7CSV9wID3MBEX2DNubEErbAlrADgm8abUBAi6h8rQTnuTkhhgMs2iD7uhqplK8lIgUOkBIW3sDJWaMSInH47A=="],
|
||||||
@ -499,7 +515,7 @@
|
|||||||
|
|
||||||
"check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
|
"check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="],
|
||||||
|
|
||||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||||
|
|
||||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||||
|
|
||||||
@ -567,6 +583,8 @@
|
|||||||
|
|
||||||
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
"debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||||
|
|
||||||
|
"decamelize": ["decamelize@4.0.0", "", {}, "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ=="],
|
||||||
|
|
||||||
"decode-named-character-reference": ["decode-named-character-reference@1.1.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w=="],
|
"decode-named-character-reference": ["decode-named-character-reference@1.1.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w=="],
|
||||||
|
|
||||||
"dedent-js": ["dedent-js@1.0.1", "", {}, "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="],
|
"dedent-js": ["dedent-js@1.0.1", "", {}, "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ=="],
|
||||||
@ -677,8 +695,14 @@
|
|||||||
|
|
||||||
"filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="],
|
"filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="],
|
||||||
|
|
||||||
|
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
|
|
||||||
"finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="],
|
"finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="],
|
||||||
|
|
||||||
|
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
||||||
|
|
||||||
|
"flat": ["flat@5.0.2", "", { "bin": { "flat": "cli.js" } }, "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="],
|
||||||
|
|
||||||
"flatbuffers": ["flatbuffers@25.2.10", "", {}, "sha512-7JlN9ZvLDG1McO3kbX0k4v+SUAg48L1rIwEvN6ZQl/eCtgJz9UylTMzE9wrmYrcorgxm3CX/3T/w5VAub99UUw=="],
|
"flatbuffers": ["flatbuffers@25.2.10", "", {}, "sha512-7JlN9ZvLDG1McO3kbX0k4v+SUAg48L1rIwEvN6ZQl/eCtgJz9UylTMzE9wrmYrcorgxm3CX/3T/w5VAub99UUw=="],
|
||||||
|
|
||||||
"flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="],
|
"flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="],
|
||||||
@ -699,6 +723,8 @@
|
|||||||
|
|
||||||
"frontend": ["frontend@workspace:packages/frontend"],
|
"frontend": ["frontend@workspace:packages/frontend"],
|
||||||
|
|
||||||
|
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
@ -707,13 +733,17 @@
|
|||||||
|
|
||||||
"get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="],
|
"get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="],
|
||||||
|
|
||||||
|
"get-func-name": ["get-func-name@2.0.2", "", {}, "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="],
|
||||||
|
|
||||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||||
|
|
||||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||||
|
|
||||||
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
||||||
|
|
||||||
"glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
"glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="],
|
||||||
|
|
||||||
|
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"glob-regex": ["glob-regex@0.3.2", "", {}, "sha512-m5blUd3/OqDTWwzBBtWBPrGlAzatRywHameHeekAZyZrskYouOGdNB8T/q6JucucvJXtOuyHIn0/Yia7iDasDw=="],
|
"glob-regex": ["glob-regex@0.3.2", "", {}, "sha512-m5blUd3/OqDTWwzBBtWBPrGlAzatRywHameHeekAZyZrskYouOGdNB8T/q6JucucvJXtOuyHIn0/Yia7iDasDw=="],
|
||||||
|
|
||||||
@ -761,6 +791,8 @@
|
|||||||
|
|
||||||
"hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="],
|
"hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="],
|
||||||
|
|
||||||
|
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||||
|
|
||||||
"hono": ["hono@4.7.8", "", {}, "sha512-PCibtFdxa7/Ldud9yddl1G81GjYaeMYYTq4ywSaNsYbB1Lug4mwtOMJf2WXykL0pntYwmpRJeOI3NmoDgD+Jxw=="],
|
"hono": ["hono@4.7.8", "", {}, "sha512-PCibtFdxa7/Ldud9yddl1G81GjYaeMYYTq4ywSaNsYbB1Lug4mwtOMJf2WXykL0pntYwmpRJeOI3NmoDgD+Jxw=="],
|
||||||
|
|
||||||
"hono-rate-limiter": ["hono-rate-limiter@0.4.2", "", { "peerDependencies": { "hono": "^4.1.1" } }, "sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw=="],
|
"hono-rate-limiter": ["hono-rate-limiter@0.4.2", "", { "peerDependencies": { "hono": "^4.1.1" } }, "sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw=="],
|
||||||
@ -777,6 +809,8 @@
|
|||||||
|
|
||||||
"import-meta-resolve": ["import-meta-resolve@4.1.0", "", {}, "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw=="],
|
"import-meta-resolve": ["import-meta-resolve@4.1.0", "", {}, "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw=="],
|
||||||
|
|
||||||
|
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
|
||||||
|
|
||||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||||
|
|
||||||
"ioredis": ["ioredis@5.6.1", "", { "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA=="],
|
"ioredis": ["ioredis@5.6.1", "", { "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA=="],
|
||||||
@ -787,13 +821,21 @@
|
|||||||
|
|
||||||
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
|
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
|
||||||
|
|
||||||
|
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
||||||
|
|
||||||
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||||
|
|
||||||
|
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||||
|
|
||||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||||
|
|
||||||
|
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||||
|
|
||||||
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
|
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
|
||||||
|
|
||||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||||
|
|
||||||
|
"is-plain-obj": ["is-plain-obj@2.1.0", "", {}, "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="],
|
||||||
|
|
||||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||||
|
|
||||||
@ -803,6 +845,8 @@
|
|||||||
|
|
||||||
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||||
|
|
||||||
|
"is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="],
|
||||||
|
|
||||||
"is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
"is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
||||||
|
|
||||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
@ -845,16 +889,22 @@
|
|||||||
|
|
||||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="],
|
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="],
|
||||||
|
|
||||||
|
"limiter": ["limiter@3.0.0", "", {}, "sha512-hev7DuXojsTFl2YwyzUJMDnZ/qBDd3yZQLSH3aD4tdL1cqfc3TMnoecEJtWFaQFdErZsKoFMBTxF/FBSkgDbEg=="],
|
||||||
|
|
||||||
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
||||||
|
|
||||||
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
||||||
|
|
||||||
|
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||||
|
|
||||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||||
|
|
||||||
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
||||||
|
|
||||||
"lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="],
|
"lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="],
|
||||||
|
|
||||||
|
"log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
|
||||||
|
|
||||||
"logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="],
|
"logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="],
|
||||||
|
|
||||||
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
||||||
@ -971,7 +1021,7 @@
|
|||||||
|
|
||||||
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||||
|
|
||||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
"minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
||||||
|
|
||||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||||
|
|
||||||
@ -981,6 +1031,8 @@
|
|||||||
|
|
||||||
"mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
|
"mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
|
||||||
|
|
||||||
|
"mocha": ["mocha@10.8.2", "", { "dependencies": { "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", "chokidar": "^3.5.3", "debug": "^4.3.5", "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^8.1.0", "he": "^1.2.0", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^5.1.6", "ms": "^2.1.3", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^6.5.1", "yargs": "^16.2.0", "yargs-parser": "^20.2.9", "yargs-unparser": "^2.0.0" }, "bin": { "mocha": "bin/mocha.js", "_mocha": "bin/_mocha" } }, "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg=="],
|
||||||
|
|
||||||
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
@ -1047,6 +1099,8 @@
|
|||||||
|
|
||||||
"p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
|
"p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
|
||||||
|
|
||||||
|
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||||
|
|
||||||
"p-queue": ["p-queue@8.1.0", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw=="],
|
"p-queue": ["p-queue@8.1.0", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw=="],
|
||||||
|
|
||||||
"p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
|
"p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
|
||||||
@ -1065,6 +1119,8 @@
|
|||||||
|
|
||||||
"pascal-case": ["pascal-case@3.1.2", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g=="],
|
"pascal-case": ["pascal-case@3.1.2", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g=="],
|
||||||
|
|
||||||
|
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||||
|
|
||||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||||
|
|
||||||
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
@ -1135,6 +1191,8 @@
|
|||||||
|
|
||||||
"radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
|
"radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
|
||||||
|
|
||||||
|
"randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="],
|
||||||
|
|
||||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||||
|
|
||||||
"rate-limit-redis": ["rate-limit-redis@4.2.0", "", { "peerDependencies": { "express-rate-limit": ">= 6" } }, "sha512-wV450NQyKC24NmPosJb2131RoczLdfIJdKCReNwtVpm5998U8SgKrAZrIHaN/NfQgqOHaan8Uq++B4sa5REwjA=="],
|
"rate-limit-redis": ["rate-limit-redis@4.2.0", "", { "peerDependencies": { "express-rate-limit": ">= 6" } }, "sha512-wV450NQyKC24NmPosJb2131RoczLdfIJdKCReNwtVpm5998U8SgKrAZrIHaN/NfQgqOHaan8Uq++B4sa5REwjA=="],
|
||||||
@ -1143,7 +1201,7 @@
|
|||||||
|
|
||||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||||
|
|
||||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|
||||||
"recrawl-sync": ["recrawl-sync@2.2.3", "", { "dependencies": { "@cush/relative": "^1.0.0", "glob-regex": "^0.3.0", "slash": "^3.0.0", "sucrase": "^3.20.3", "tslib": "^1.9.3" } }, "sha512-vSaTR9t+cpxlskkdUFrsEpnf67kSmPk66yAGT1fZPrDudxQjoMzPgQhSMImQ0pAw5k0NPirefQfhopSjhdUtpQ=="],
|
"recrawl-sync": ["recrawl-sync@2.2.3", "", { "dependencies": { "@cush/relative": "^1.0.0", "glob-regex": "^0.3.0", "slash": "^3.0.0", "sucrase": "^3.20.3", "tslib": "^1.9.3" } }, "sha512-vSaTR9t+cpxlskkdUFrsEpnf67kSmPk66yAGT1fZPrDudxQjoMzPgQhSMImQ0pAw5k0NPirefQfhopSjhdUtpQ=="],
|
||||||
|
|
||||||
@ -1211,6 +1269,8 @@
|
|||||||
|
|
||||||
"serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="],
|
"serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="],
|
||||||
|
|
||||||
|
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
|
||||||
|
|
||||||
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
|
"serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="],
|
||||||
|
|
||||||
"server-destroy": ["server-destroy@1.0.1", "", {}, "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="],
|
"server-destroy": ["server-destroy@1.0.1", "", {}, "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="],
|
||||||
@ -1279,6 +1339,8 @@
|
|||||||
|
|
||||||
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
|
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
|
||||||
|
|
||||||
|
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||||
|
|
||||||
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
|
"sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="],
|
||||||
|
|
||||||
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||||
@ -1315,6 +1377,8 @@
|
|||||||
|
|
||||||
"tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="],
|
"tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="],
|
||||||
|
|
||||||
|
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||||
|
|
||||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||||
|
|
||||||
"toposort": ["toposort@2.0.2", "", {}, "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="],
|
"toposort": ["toposort@2.0.2", "", {}, "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="],
|
||||||
@ -1337,6 +1401,8 @@
|
|||||||
|
|
||||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
|
"type-detect": ["type-detect@4.1.0", "", {}, "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw=="],
|
||||||
|
|
||||||
"type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
|
"type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
|
||||||
|
|
||||||
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||||
@ -1429,6 +1495,8 @@
|
|||||||
|
|
||||||
"winston-transport": ["winston-transport@4.9.0", "", { "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" } }, "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A=="],
|
"winston-transport": ["winston-transport@4.9.0", "", { "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" } }, "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A=="],
|
||||||
|
|
||||||
|
"workerpool": ["workerpool@6.5.1", "", {}, "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA=="],
|
||||||
|
|
||||||
"wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="],
|
"wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="],
|
||||||
|
|
||||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
@ -1447,6 +1515,8 @@
|
|||||||
|
|
||||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||||
|
|
||||||
|
"yargs-unparser": ["yargs-unparser@2.0.0", "", { "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" } }, "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA=="],
|
||||||
|
|
||||||
"yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="],
|
"yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="],
|
||||||
|
|
||||||
"yocto-spinner": ["yocto-spinner@0.2.2", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-21rPcM3e4vCpOXThiFRByX8amU5By1R0wNS8Oex+DP3YgC8xdU0vEJ/K8cbPLiIJVosSSysgcFof6s6MSD5/Vw=="],
|
"yocto-spinner": ["yocto-spinner@0.2.2", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-21rPcM3e4vCpOXThiFRByX8amU5By1R0wNS8Oex+DP3YgC8xdU0vEJ/K8cbPLiIJVosSSysgcFof6s6MSD5/Vw=="],
|
||||||
@ -1473,6 +1543,8 @@
|
|||||||
|
|
||||||
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||||
|
|
||||||
|
"@koshnic/ratelimit/chai": ["chai@4.5.0", "", { "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", "deep-eql": "^4.1.3", "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", "type-detect": "^4.1.0" } }, "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw=="],
|
||||||
|
|
||||||
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
||||||
@ -1503,28 +1575,42 @@
|
|||||||
|
|
||||||
"concurrently/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"concurrently/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
"filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
|
||||||
|
|
||||||
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
|
||||||
|
|
||||||
"hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="],
|
"hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="],
|
||||||
|
|
||||||
"jake/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"jake/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
|
"jake/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
|
"log-symbols/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||||
|
|
||||||
|
"mocha/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="],
|
||||||
|
|
||||||
|
"mocha/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
|
||||||
|
|
||||||
"onnxruntime-web/onnxruntime-common": ["onnxruntime-common@1.22.0-dev.20250409-89f8206ba4", "", {}, "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ=="],
|
"onnxruntime-web/onnxruntime-common": ["onnxruntime-common@1.22.0-dev.20250409-89f8206ba4", "", {}, "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ=="],
|
||||||
|
|
||||||
|
"p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||||
|
|
||||||
"pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
"pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||||
|
|
||||||
"prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
"prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
||||||
|
|
||||||
|
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"recrawl-sync/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
"recrawl-sync/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
|
||||||
|
|
||||||
"serialize-error/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
|
"serialize-error/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
|
||||||
|
|
||||||
|
"sucrase/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
||||||
|
|
||||||
|
"unified/is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||||
|
|
||||||
|
"unstorage/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
"widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
"widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||||
|
|
||||||
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||||
@ -1533,6 +1619,8 @@
|
|||||||
|
|
||||||
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||||
|
|
||||||
|
"yargs-unparser/camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="],
|
||||||
|
|
||||||
"@huggingface/transformers/onnxruntime-node/onnxruntime-common": ["onnxruntime-common@1.21.0", "", {}, "sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ=="],
|
"@huggingface/transformers/onnxruntime-node/onnxruntime-common": ["onnxruntime-common@1.21.0", "", {}, "sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ=="],
|
||||||
|
|
||||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||||
@ -1541,6 +1629,16 @@
|
|||||||
|
|
||||||
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||||
|
|
||||||
|
"@koshnic/ratelimit/chai/assertion-error": ["assertion-error@1.1.0", "", {}, "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="],
|
||||||
|
|
||||||
|
"@koshnic/ratelimit/chai/check-error": ["check-error@1.0.3", "", { "dependencies": { "get-func-name": "^2.0.2" } }, "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg=="],
|
||||||
|
|
||||||
|
"@koshnic/ratelimit/chai/deep-eql": ["deep-eql@4.1.4", "", { "dependencies": { "type-detect": "^4.0.0" } }, "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg=="],
|
||||||
|
|
||||||
|
"@koshnic/ratelimit/chai/loupe": ["loupe@2.3.7", "", { "dependencies": { "get-func-name": "^2.0.1" } }, "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA=="],
|
||||||
|
|
||||||
|
"@koshnic/ratelimit/chai/pathval": ["pathval@1.1.1", "", {}, "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ=="],
|
||||||
|
|
||||||
"astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
|
"astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
|
||||||
|
|
||||||
"astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
|
"astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
|
||||||
@ -1587,14 +1685,18 @@
|
|||||||
|
|
||||||
"concurrently/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
"concurrently/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||||
|
|
||||||
"filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
|
||||||
|
|
||||||
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||||
|
|
||||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
|
||||||
|
|
||||||
"jake/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
"jake/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||||
|
|
||||||
|
"jake/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||||
|
|
||||||
|
"log-symbols/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||||
|
|
||||||
|
"mocha/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="],
|
||||||
|
|
||||||
|
"p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||||
|
|
||||||
"pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
"pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||||
|
|
||||||
"pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
"pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.0", "", {}, "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="],
|
||||||
@ -1603,6 +1705,10 @@
|
|||||||
|
|
||||||
"pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
"pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||||
|
|
||||||
|
"sucrase/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
|
"unstorage/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
|
|
||||||
"widest-line/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
|
"widest-line/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="],
|
||||||
|
|
||||||
"widest-line/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
"widest-line/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||||
@ -1615,6 +1721,8 @@
|
|||||||
|
|
||||||
"colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
"colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||||
|
|
||||||
|
"mocha/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
"widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
"widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
deno.json
16
deno.json
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"lock": false,
|
|
||||||
"workspace": ["./packages/crawler", "./packages/core"],
|
|
||||||
"nodeModulesDir": "auto",
|
|
||||||
"tasks": {
|
|
||||||
"crawler": "deno task --filter 'crawler' all",
|
|
||||||
"backend": "deno task --filter 'backend' start"
|
|
||||||
},
|
|
||||||
"fmt": {
|
|
||||||
"useTabs": true,
|
|
||||||
"lineWidth": 120,
|
|
||||||
"indentWidth": 4,
|
|
||||||
"semiColons": true,
|
|
||||||
"proseWrap": "always"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
const requiredEnvVars = ["DB_HOST", "DB_NAME", "DB_USER", "DB_PASSWORD", "DB_PORT", "DB_NAME_CRED"];
|
|
||||||
|
|
||||||
const unsetVars = requiredEnvVars.filter((key) => process.env[key] === undefined);
|
|
||||||
|
|
||||||
if (unsetVars.length > 0) {
|
|
||||||
throw new Error(`Missing required environment variables: ${unsetVars.join(", ")}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const databaseHost = process.env["DB_HOST"]!;
|
|
||||||
const databaseName = process.env["DB_NAME"];
|
|
||||||
const databaseNameCred = process.env["DB_NAME_CRED"]!;
|
|
||||||
const databaseUser = process.env["DB_USER"]!;
|
|
||||||
const databasePassword = process.env["DB_PASSWORD"]!;
|
|
||||||
const databasePort = process.env["DB_PORT"]!;
|
|
||||||
|
|
||||||
export const postgresConfig = {
|
|
||||||
hostname: databaseHost,
|
|
||||||
port: parseInt(databasePort),
|
|
||||||
database: databaseName,
|
|
||||||
user: databaseUser,
|
|
||||||
password: databasePassword
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postgresConfigNpm = {
|
|
||||||
host: databaseHost,
|
|
||||||
port: parseInt(databasePort),
|
|
||||||
database: databaseName,
|
|
||||||
username: databaseUser,
|
|
||||||
password: databasePassword
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postgresCredConfigNpm = {
|
|
||||||
host: databaseHost,
|
|
||||||
port: parseInt(databasePort),
|
|
||||||
database: databaseNameCred,
|
|
||||||
username: databaseUser,
|
|
||||||
password: databasePassword
|
|
||||||
}
|
|
||||||
|
|
||||||
export const postgresConfigCred = {
|
|
||||||
hostname: databaseHost,
|
|
||||||
port: parseInt(databasePort),
|
|
||||||
database: databaseNameCred,
|
|
||||||
user: databaseUser,
|
|
||||||
password: databasePassword
|
|
||||||
};
|
|
@ -1,5 +0,0 @@
|
|||||||
import postgres from "postgres";
|
|
||||||
import { postgresConfigNpm, postgresCredConfigNpm } from "./config";
|
|
||||||
|
|
||||||
export const sql = postgres(postgresConfigNpm);
|
|
||||||
export const sqlCred = postgres(postgresCredConfigNpm)
|
|
@ -1,4 +1,4 @@
|
|||||||
import { sql } from "./db";
|
import { sql } from "@core/db/dbNew";
|
||||||
import type { VideoSnapshotType } from "@core/db/schema.d.ts";
|
import type { VideoSnapshotType } from "@core/db/schema.d.ts";
|
||||||
|
|
||||||
export async function getVideoSnapshots(
|
export async function getVideoSnapshots(
|
62
packages/backend/lib/auth/captchaDifficulty.ts
Normal file
62
packages/backend/lib/auth/captchaDifficulty.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { Psql } from "@core/db/psql";
|
||||||
|
import { SlidingWindow } from "@core/mq/slidingWindow.ts";
|
||||||
|
import { redis } from "@core/db/redis.ts";
|
||||||
|
import { getIdentifier } from "@/middleware/rateLimiters.ts";
|
||||||
|
import { Context } from "hono";
|
||||||
|
|
||||||
|
type seconds = number;
|
||||||
|
|
||||||
|
export interface CaptchaDifficultyConfig {
|
||||||
|
global: boolean;
|
||||||
|
duration: seconds;
|
||||||
|
threshold: number;
|
||||||
|
difficulty: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCaptchaDifficultyConfigByRoute = async (sql: Psql, route: string): Promise<CaptchaDifficultyConfig[]> => {
|
||||||
|
return sql<CaptchaDifficultyConfig[]>`
|
||||||
|
SELECT duration, threshold, difficulty, global
|
||||||
|
FROM captcha_difficulty_settings
|
||||||
|
WHERE CONCAT(method, '-', path) = ${route}
|
||||||
|
ORDER BY duration
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCaptchaConfigMaxDuration = async (sql: Psql, route: string): Promise<seconds> => {
|
||||||
|
const rows = await sql<{max: number}[]>`
|
||||||
|
SELECT MAX(duration)
|
||||||
|
FROM captcha_difficulty_settings
|
||||||
|
WHERE CONCAT(method, '-', path) = ${route}
|
||||||
|
`;
|
||||||
|
if (rows.length < 1){
|
||||||
|
return Number.MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
return rows[0].max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const getCurrentCaptchaDifficulty = async (sql: Psql, c: Context | string): Promise<number | null> => {
|
||||||
|
const isRoute = typeof c === "string";
|
||||||
|
const route = isRoute ? c : `${c.req.method}-${c.req.path}`
|
||||||
|
const configs = await getCaptchaDifficultyConfigByRoute(sql, route);
|
||||||
|
if (configs.length < 1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
else if (configs.length == 1) {
|
||||||
|
return configs[0].difficulty
|
||||||
|
}
|
||||||
|
const maxDuration = configs.reduce((max, config) =>
|
||||||
|
Math.max(max, config.duration), 0);
|
||||||
|
const slidingWindow = new SlidingWindow(redis, maxDuration);
|
||||||
|
for (let i = 1; i < configs.length; i++) {
|
||||||
|
const config = configs[i];
|
||||||
|
const lastConfig = configs[i - 1];
|
||||||
|
const identifier = isRoute ? c : getIdentifier(c, config.global);
|
||||||
|
const count = await slidingWindow.count(`captcha-${identifier}`, config.duration);
|
||||||
|
if (count >= config.threshold) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return lastConfig.difficulty
|
||||||
|
}
|
||||||
|
return configs[configs.length-1].difficulty;
|
||||||
|
}
|
13
packages/backend/lib/auth/getJWTsecret.ts
Normal file
13
packages/backend/lib/auth/getJWTsecret.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { ErrorResponse } from "src/schema";
|
||||||
|
|
||||||
|
export const getJWTsecret = () => {
|
||||||
|
const secret = process.env["JWT_SECRET"];
|
||||||
|
if (!secret) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "JWT_SECRET is not set",
|
||||||
|
code: "SERVER_ERROR"
|
||||||
|
};
|
||||||
|
return [response, true];
|
||||||
|
}
|
||||||
|
return [secret, null];
|
||||||
|
}
|
117
packages/backend/middleware/captcha.ts
Normal file
117
packages/backend/middleware/captcha.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { Context, Next } from "hono";
|
||||||
|
import { ErrorResponse } from "src/schema";
|
||||||
|
import { SlidingWindow } from "@core/mq/slidingWindow.ts";
|
||||||
|
import { getCaptchaConfigMaxDuration, getCurrentCaptchaDifficulty } from "@/lib/auth/captchaDifficulty.ts";
|
||||||
|
import { sqlCred } from "@core/db/dbNew.ts";
|
||||||
|
import { redis } from "@core/db/redis.ts";
|
||||||
|
import { verify } from 'hono/jwt';
|
||||||
|
import { JwtTokenInvalid, JwtTokenExpired } from "hono/utils/jwt/types";
|
||||||
|
import { getJWTsecret } from "@/lib/auth/getJWTsecret.ts";
|
||||||
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
|
import { object, string, number, ValidationError } from "yup";
|
||||||
|
import { getIdentifier } from "@/middleware/rateLimiters.ts";
|
||||||
|
|
||||||
|
const tokenSchema = object({
|
||||||
|
exp: number().integer(),
|
||||||
|
id: string().length(6),
|
||||||
|
difficulty: number().integer().moreThan(0)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const captchaMiddleware = async (c: Context, next: Next) => {
|
||||||
|
const authHeader = c.req.header("Authorization");
|
||||||
|
|
||||||
|
if (!authHeader) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "'Authorization' header is missing.",
|
||||||
|
code: "UNAUTHORIZED"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const authIsBearer = authHeader.startsWith("Bearer ");
|
||||||
|
if (!authIsBearer || authHeader.length < 8) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "'Authorization' header is invalid.",
|
||||||
|
code: "INVALID_HEADER"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [r, err] = getJWTsecret();
|
||||||
|
if (err) {
|
||||||
|
return c.json<ErrorResponse>(r as ErrorResponse, 500);
|
||||||
|
}
|
||||||
|
const jwtSecret = r as string;
|
||||||
|
|
||||||
|
const token = authHeader.substring(7);
|
||||||
|
|
||||||
|
const path = c.req.path;
|
||||||
|
const method = c.req.method;
|
||||||
|
const route = `${method}-${path}`;
|
||||||
|
|
||||||
|
const requiredDifficulty = await getCurrentCaptchaDifficulty(sqlCred, c);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decodedPayload = await verify(token, jwtSecret);
|
||||||
|
const payload = await tokenSchema.validate(decodedPayload);
|
||||||
|
const difficulty = payload.difficulty;
|
||||||
|
const tokenID = payload.id;
|
||||||
|
const consumed = await lockManager.isLocked(tokenID);
|
||||||
|
if (consumed) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "Token has already been used.",
|
||||||
|
code: "INVALID_CREDENTIALS"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 401);
|
||||||
|
}
|
||||||
|
if (difficulty < requiredDifficulty) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "Token too weak.",
|
||||||
|
code: "UNAUTHORIZED"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 401);
|
||||||
|
}
|
||||||
|
const EXPIRE_FIVE_MINUTES = 300;
|
||||||
|
await lockManager.acquireLock(tokenID, EXPIRE_FIVE_MINUTES);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (e instanceof JwtTokenInvalid) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "Failed to verify the token.",
|
||||||
|
code: "INVALID_CREDENTIALS"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 400);
|
||||||
|
}
|
||||||
|
else if (e instanceof JwtTokenExpired) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "Token expired.",
|
||||||
|
code: "INVALID_CREDENTIALS"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 400);
|
||||||
|
}
|
||||||
|
else if (e instanceof ValidationError) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
code: "INVALID_QUERY_PARAMS",
|
||||||
|
message: "Invalid query parameters",
|
||||||
|
errors: e.errors
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 400);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "Unknown error.",
|
||||||
|
code: "UNKNOWN_ERROR"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const duration = await getCaptchaConfigMaxDuration(sqlCred, route);
|
||||||
|
const window = new SlidingWindow(redis, duration);
|
||||||
|
|
||||||
|
const identifierWithIP = getIdentifier(c, true);
|
||||||
|
const identifier = getIdentifier(c, false);
|
||||||
|
await window.event(`captcha-${identifier}`);
|
||||||
|
await window.event(`captcha-${identifierWithIP}`);
|
||||||
|
|
||||||
|
await next();
|
||||||
|
};
|
@ -1,19 +1,21 @@
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { Variables } from "hono/types";
|
import { Variables } from "hono/types";
|
||||||
import { bodyLimitForPing } from "./bodyLimits.ts";
|
import { bodyLimitForPing } from "./bodyLimits.ts";
|
||||||
import { pingHandler } from "../routes/ping.ts";
|
import { pingHandler } from "routes/ping";
|
||||||
import { registerRateLimiter } from "./rateLimiters.ts";
|
import { registerRateLimiter } from "./rateLimiters.ts";
|
||||||
import { preetifyResponse } from "./preetifyResponse.ts";
|
import { preetifyResponse } from "./preetifyResponse.ts";
|
||||||
import { logger } from "./logger.ts";
|
import { logger } from "./logger.ts";
|
||||||
import { timing } from "hono/timing";
|
import { timing } from "hono/timing";
|
||||||
import { contentType } from "./contentType.ts";
|
import { contentType } from "./contentType.ts";
|
||||||
|
import { captchaMiddleware } from "./captcha.ts";
|
||||||
|
|
||||||
export function configureMiddleWares(app: Hono<{Variables: Variables }>) {
|
export function configureMiddleWares(app: Hono<{ Variables: Variables }>) {
|
||||||
app.use("*", contentType);
|
app.use("*", contentType);
|
||||||
app.use(timing());
|
app.use(timing());
|
||||||
app.use("*", preetifyResponse);
|
app.use("*", preetifyResponse);
|
||||||
app.use("*", logger({}));
|
app.use("*", logger({}));
|
||||||
|
|
||||||
app.post("/user", registerRateLimiter);
|
app.post("/user", registerRateLimiter);
|
||||||
|
app.post("/user", captchaMiddleware);
|
||||||
app.all("/ping", bodyLimitForPing, ...pingHandler);
|
app.all("/ping", bodyLimitForPing, ...pingHandler);
|
||||||
}
|
}
|
@ -77,7 +77,7 @@ const defaultFormatter = (params) => {
|
|||||||
`${methodColor} ${params.method.padEnd(6)}${reset} ${params.path}`
|
`${methodColor} ${params.method.padEnd(6)}${reset} ${params.path}`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
type Ctx = Context
|
type Ctx = Context;
|
||||||
export const logger = (config) => {
|
export const logger = (config) => {
|
||||||
const { formatter = defaultFormatter, output = console, skipPaths = [], skip = null } = config;
|
const { formatter = defaultFormatter, output = console, skipPaths = [], skip = null } = config;
|
||||||
|
|
||||||
|
@ -1,27 +1,44 @@
|
|||||||
import { rateLimiter, Store } from "hono-rate-limiter";
|
|
||||||
import type { BlankEnv } from "hono/types";
|
import type { BlankEnv } from "hono/types";
|
||||||
import { MINUTE } from "@core/const/time.ts";
|
|
||||||
import { getConnInfo } from "hono/bun";
|
import { getConnInfo } from "hono/bun";
|
||||||
import type { Context } from "hono";
|
import { Context, Next } from "hono";
|
||||||
|
import { generateRandomId } from "@core/lib/randomID.ts";
|
||||||
|
import { RateLimiter } from "@koshnic/ratelimit";
|
||||||
|
import { ErrorResponse } from "@/src/schema";
|
||||||
import { redis } from "@core/db/redis.ts";
|
import { redis } from "@core/db/redis.ts";
|
||||||
import { RedisStore } from "rate-limit-redis";
|
|
||||||
|
|
||||||
export const registerRateLimiter = rateLimiter<BlankEnv, "/user", {}>({
|
export const getIdentifier = (c: Context, includeIP: boolean = true) => {
|
||||||
windowMs: 60 * MINUTE,
|
let ipAddr = generateRandomId(6);
|
||||||
limit: 10,
|
const info = getConnInfo(c);
|
||||||
standardHeaders: "draft-6",
|
if (info.remote && info.remote.address) {
|
||||||
keyGenerator: (c) => {
|
ipAddr = info.remote.address;
|
||||||
const info = getConnInfo(c as unknown as Context<BlankEnv, "/user", {}>);
|
}
|
||||||
if (!info.remote || !info.remote.address) {
|
const forwardedFor = c.req.header("X-Forwarded-For");
|
||||||
return crypto.randomUUID();
|
if (forwardedFor) {
|
||||||
}
|
ipAddr = forwardedFor.split(",")[0];
|
||||||
const addr = info.remote.address;
|
}
|
||||||
const path = new URL(c.req.url).pathname;
|
const path = c.req.path;
|
||||||
const method = c.req.method;
|
const method = c.req.method;
|
||||||
return `${method}-${path}@${addr}`;
|
const ipIdentifier = includeIP ? `@${ipAddr}` : "";
|
||||||
},
|
return `${method}-${path}${ipIdentifier}`;
|
||||||
store: new RedisStore({
|
};
|
||||||
// @ts-expect-error - Known issue: the `c`all` function is not present in @types/ioredis
|
|
||||||
sendCommand: (...args: string[]) => redis.call(...args)
|
export const registerRateLimiter = async (c: Context<BlankEnv, "/user", {}>, next: Next) => {
|
||||||
}) as unknown as Store
|
const limiter = new RateLimiter(redis);
|
||||||
});
|
const identifier = getIdentifier(c, true);
|
||||||
|
const { allowed, retryAfter } = await limiter.allow(identifier, {
|
||||||
|
burst: 5,
|
||||||
|
ratePerPeriod: 5,
|
||||||
|
period: 120,
|
||||||
|
cost: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!allowed) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: `Too many requests, please retry after ${Math.round(retryAfter)} seconds.`,
|
||||||
|
code: "RATE_LIMIT_EXCEEDED"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 429);
|
||||||
|
}
|
||||||
|
|
||||||
|
await next();
|
||||||
|
};
|
@ -10,6 +10,7 @@
|
|||||||
"hono": "^4.7.8",
|
"hono": "^4.7.8",
|
||||||
"hono-rate-limiter": "^0.4.2",
|
"hono-rate-limiter": "^0.4.2",
|
||||||
"ioredis": "^5.6.1",
|
"ioredis": "^5.6.1",
|
||||||
|
"limiter": "^3.0.0",
|
||||||
"postgres": "^3.4.5",
|
"postgres": "^3.4.5",
|
||||||
"rate-limit-redis": "^4.2.0",
|
"rate-limit-redis": "^4.2.0",
|
||||||
"yup": "^1.6.1",
|
"yup": "^1.6.1",
|
||||||
|
88
packages/backend/routes/captcha/[id]/result/GET.ts
Normal file
88
packages/backend/routes/captcha/[id]/result/GET.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { Context } from "hono";
|
||||||
|
import { Bindings, BlankEnv } from "hono/types";
|
||||||
|
import { ErrorResponse } from "src/schema";
|
||||||
|
import { createHandlers } from "src/utils.ts";
|
||||||
|
import { sign } from 'hono/jwt'
|
||||||
|
import { generateRandomId } from "@core/lib/randomID.ts";
|
||||||
|
import { getJWTsecret } from "lib/auth/getJWTsecret.ts";
|
||||||
|
|
||||||
|
interface CaptchaResponse {
|
||||||
|
success: boolean;
|
||||||
|
difficulty?: number;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getChallengeVerificationResult = async (id: string, ans: string) => {
|
||||||
|
const baseURL = process.env["UCAPTCHA_URL"];
|
||||||
|
const url = new URL(baseURL);
|
||||||
|
url.pathname = `/challenge/${id}/validation`;
|
||||||
|
return await fetch(url.toString(), {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
y: ans
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const verifyChallengeHandler = createHandlers(
|
||||||
|
async (c: Context<BlankEnv & { Bindings: Bindings }, "/captcha/:id/result">) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const ans = c.req.query("ans");
|
||||||
|
if (!ans) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "Missing required query parameter: ans",
|
||||||
|
code: "INVALID_QUERY_PARAMS"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 400);
|
||||||
|
}
|
||||||
|
const res = await getChallengeVerificationResult(id, ans);
|
||||||
|
const data: CaptchaResponse = await res.json();
|
||||||
|
if (data.error && res.status === 404) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: data.error,
|
||||||
|
code: "ENTITY_NOT_FOUND"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 401);
|
||||||
|
} else if (data.error && res.status === 400) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: data.error,
|
||||||
|
code: "INVALID_QUERY_PARAMS"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 400);
|
||||||
|
} else if (data.error) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: data.error,
|
||||||
|
code: "UNKNOWN_ERROR"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 500);
|
||||||
|
}
|
||||||
|
if (!data.success) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
message: "Incorrect answer",
|
||||||
|
code: "INVALID_CREDENTIALS"
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [r, err] = getJWTsecret();
|
||||||
|
if (err) {
|
||||||
|
return c.json<ErrorResponse>(r as ErrorResponse, 500);
|
||||||
|
}
|
||||||
|
const jwtSecret = r as string;
|
||||||
|
|
||||||
|
const tokenID = generateRandomId(6);
|
||||||
|
const NOW = Math.floor(Date.now() / 1000)
|
||||||
|
const FIVE_MINUTES_LATER = NOW + 60 * 5;
|
||||||
|
const jwt = await sign({
|
||||||
|
difficulty: data.difficulty!,
|
||||||
|
id: tokenID,
|
||||||
|
exp: FIVE_MINUTES_LATER
|
||||||
|
}, jwtSecret);
|
||||||
|
return c.json({
|
||||||
|
token: jwt
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
43
packages/backend/routes/captcha/difficulty/GET.ts
Normal file
43
packages/backend/routes/captcha/difficulty/GET.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { createHandlers } from "src/utils.ts";
|
||||||
|
import { object, string, ValidationError } from "yup";
|
||||||
|
import { ErrorResponse } from "src/schema";
|
||||||
|
import { getCurrentCaptchaDifficulty } from "@/lib/auth/captchaDifficulty.ts";
|
||||||
|
import { sqlCred } from "@core/db/dbNew.ts";
|
||||||
|
|
||||||
|
const queryParamsSchema = object({
|
||||||
|
route: string().matches(/(?:GET|POST|PUT|PATCH|DELETE)-\/.*/g)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCaptchaDifficultyHandler = createHandlers(async (c) => {
|
||||||
|
try {
|
||||||
|
const queryParams = await queryParamsSchema.validate(c.req.query());
|
||||||
|
const { route } = queryParams;
|
||||||
|
const difficulty = await getCurrentCaptchaDifficulty(sqlCred, route);
|
||||||
|
if (!difficulty) {
|
||||||
|
const response: ErrorResponse<unknown> = {
|
||||||
|
code: "ENTITY_NOT_FOUND",
|
||||||
|
message: "No difficulty configs found for this route."
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse<unknown>>(response, 404);
|
||||||
|
}
|
||||||
|
return c.json({
|
||||||
|
"difficulty": difficulty
|
||||||
|
});
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ValidationError) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
code: "INVALID_QUERY_PARAMS",
|
||||||
|
message: "Invalid query parameters",
|
||||||
|
errors: e.errors
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 400);
|
||||||
|
} else {
|
||||||
|
const response: ErrorResponse<unknown> = {
|
||||||
|
code: "UNKNOWN_ERROR",
|
||||||
|
message: "Unknown error",
|
||||||
|
errors: [e]
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse<unknown>>(response, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
2
packages/backend/routes/captcha/index.ts
Normal file
2
packages/backend/routes/captcha/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./session/POST.ts";
|
||||||
|
export * from "./[id]/result/GET.ts";
|
59
packages/backend/routes/captcha/session/POST.ts
Normal file
59
packages/backend/routes/captcha/session/POST.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { createHandlers } from "src/utils.ts";
|
||||||
|
import { getCurrentCaptchaDifficulty } from "@/lib/auth/captchaDifficulty.ts";
|
||||||
|
import { sqlCred } from "@core/db/dbNew.ts";
|
||||||
|
import { object, string, ValidationError } from "yup";
|
||||||
|
import { ErrorResponse } from "@/src/schema";
|
||||||
|
import type { ContentfulStatusCode } from "hono/utils/http-status";
|
||||||
|
|
||||||
|
const bodySchema = object({
|
||||||
|
route: string().matches(/(?:GET|POST|PUT|PATCH|DELETE)-\/.*/g)
|
||||||
|
});
|
||||||
|
|
||||||
|
interface CaptchaSessionResponse {
|
||||||
|
success: boolean;
|
||||||
|
id: string;
|
||||||
|
g: string;
|
||||||
|
n: string;
|
||||||
|
t: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createNewChallenge = async (difficulty: number) => {
|
||||||
|
const baseURL = process.env["UCAPTCHA_URL"];
|
||||||
|
const url = new URL(baseURL);
|
||||||
|
url.pathname = "/challenge";
|
||||||
|
return await fetch(url.toString(), {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
difficulty: difficulty,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createCaptchaSessionHandler = createHandlers(async (c) => {
|
||||||
|
try {
|
||||||
|
const requestBody = await bodySchema.validate(await c.req.json());
|
||||||
|
const { route } = requestBody;
|
||||||
|
const difficuly = await getCurrentCaptchaDifficulty(sqlCred, route)
|
||||||
|
const res = await createNewChallenge(difficuly);
|
||||||
|
return c.json<CaptchaSessionResponse|unknown>(await res.json(), res.status as ContentfulStatusCode);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ValidationError) {
|
||||||
|
const response: ErrorResponse = {
|
||||||
|
code: "INVALID_QUERY_PARAMS",
|
||||||
|
message: "Invalid query parameters",
|
||||||
|
errors: e.errors
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse>(response, 400);
|
||||||
|
} else {
|
||||||
|
const response: ErrorResponse<unknown> = {
|
||||||
|
code: "UNKNOWN_ERROR",
|
||||||
|
message: "Unknown error",
|
||||||
|
errors: [e]
|
||||||
|
};
|
||||||
|
return c.json<ErrorResponse<unknown>>(response, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -1,17 +1,29 @@
|
|||||||
import { rootHandler } from "./root/root.ts";
|
import { getSingerForBirthday, pickSinger, pickSpecialSinger, type Singer } from "lib/const/singers.ts";
|
||||||
import { pingHandler } from "./ping.ts";
|
import { VERSION } from "src/main.ts";
|
||||||
import { getSnapshotsHanlder } from "./snapshots.ts";
|
import { createHandlers } from "src/utils.ts";
|
||||||
import { registerHandler } from "./user.ts";
|
|
||||||
import { videoInfoHandler } from "db/videoInfo.ts";
|
|
||||||
import { Hono } from "hono";
|
|
||||||
import { Variables } from "hono/types";
|
|
||||||
|
|
||||||
export function configureRoutes(app: Hono<{Variables: Variables }>) {
|
export const rootHandler = createHandlers((c) => {
|
||||||
app.get("/", ...rootHandler);
|
let singer: Singer | Singer[];
|
||||||
app.all("/ping", ...pingHandler);
|
const shouldShowSpecialSinger = Math.random() < 0.016;
|
||||||
|
if (getSingerForBirthday().length !== 0) {
|
||||||
app.get("/video/:id/snapshots", ...getSnapshotsHanlder);
|
singer = JSON.parse(JSON.stringify(getSingerForBirthday())) as Singer[];
|
||||||
app.post("/user", ...registerHandler);
|
for (const s of singer) {
|
||||||
|
delete s.birthday;
|
||||||
app.get("/video/:id/info", ...videoInfoHandler);
|
s.message = `祝${s.name}生日快乐~`;
|
||||||
}
|
}
|
||||||
|
} else if (shouldShowSpecialSinger) {
|
||||||
|
singer = pickSpecialSinger();
|
||||||
|
} else {
|
||||||
|
singer = pickSinger();
|
||||||
|
}
|
||||||
|
return c.json({
|
||||||
|
project: {
|
||||||
|
name: "中V档案馆",
|
||||||
|
motto: "一起唱吧,心中的歌!"
|
||||||
|
},
|
||||||
|
status: 200,
|
||||||
|
version: VERSION,
|
||||||
|
time: Date.now(),
|
||||||
|
singer: singer
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
import { getClientIP } from "middleware/logger.ts";
|
|
||||||
import { createHandlers } from "../src/utils.ts";
|
|
||||||
import { VERSION } from "../src/main.ts";
|
|
||||||
|
|
||||||
export const pingHandler = createHandlers(async (c) => {
|
|
||||||
const requestHeaders = c.req.raw.headers;
|
|
||||||
return c.json({
|
|
||||||
"message": "pong",
|
|
||||||
"request": {
|
|
||||||
"headers": requestHeaders,
|
|
||||||
"ip": getClientIP(c),
|
|
||||||
"mode": c.req.raw.mode,
|
|
||||||
"method": c.req.method,
|
|
||||||
"query": new URL(c.req.url).searchParams,
|
|
||||||
"body": await c.req.text(),
|
|
||||||
"url": c.req.raw.url
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"time": new Date().getTime(),
|
|
||||||
"status": 200,
|
|
||||||
"version": VERSION,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
24
packages/backend/routes/ping/index.ts
Normal file
24
packages/backend/routes/ping/index.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { getClientIP } from "middleware/logger.ts";
|
||||||
|
import { createHandlers } from "src/utils.ts";
|
||||||
|
import { VERSION } from "src/main.ts";
|
||||||
|
|
||||||
|
export const pingHandler = createHandlers(async (c) => {
|
||||||
|
const requestHeaders = c.req.raw.headers;
|
||||||
|
return c.json({
|
||||||
|
message: "pong",
|
||||||
|
request: {
|
||||||
|
headers: requestHeaders,
|
||||||
|
ip: getClientIP(c),
|
||||||
|
mode: c.req.raw.mode,
|
||||||
|
method: c.req.method,
|
||||||
|
query: new URL(c.req.url).searchParams,
|
||||||
|
body: await c.req.text(),
|
||||||
|
url: c.req.raw.url
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
time: new Date().getTime(),
|
||||||
|
status: 200,
|
||||||
|
version: VERSION
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -1,29 +0,0 @@
|
|||||||
import { getSingerForBirthday, pickSinger, pickSpecialSinger, type Singer } from "./singers.ts";
|
|
||||||
import { VERSION } from "../../src/main.ts";
|
|
||||||
import { createHandlers } from "../../src/utils.ts";
|
|
||||||
|
|
||||||
export const rootHandler = createHandlers((c) => {
|
|
||||||
let singer: Singer | Singer[];
|
|
||||||
const shouldShowSpecialSinger = Math.random() < 0.016;
|
|
||||||
if (getSingerForBirthday().length !== 0) {
|
|
||||||
singer = JSON.parse(JSON.stringify(getSingerForBirthday())) as Singer[];
|
|
||||||
for (const s of singer) {
|
|
||||||
delete s.birthday;
|
|
||||||
s.message = `祝${s.name}生日快乐~`;
|
|
||||||
}
|
|
||||||
} else if (shouldShowSpecialSinger) {
|
|
||||||
singer = pickSpecialSinger();
|
|
||||||
} else {
|
|
||||||
singer = pickSinger();
|
|
||||||
}
|
|
||||||
return c.json({
|
|
||||||
project: {
|
|
||||||
name: "中V档案馆",
|
|
||||||
motto: "一起唱吧,心中的歌!"
|
|
||||||
},
|
|
||||||
status: 200,
|
|
||||||
version: VERSION,
|
|
||||||
time: Date.now(),
|
|
||||||
singer: singer
|
|
||||||
});
|
|
||||||
});
|
|
1
packages/backend/routes/user/index.ts
Normal file
1
packages/backend/routes/user/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./register.ts";
|
@ -3,7 +3,7 @@ import Argon2id from "@rabbit-company/argon2id";
|
|||||||
import { object, string, ValidationError } from "yup";
|
import { object, string, ValidationError } from "yup";
|
||||||
import type { Context } from "hono";
|
import type { Context } from "hono";
|
||||||
import type { Bindings, BlankEnv, BlankInput } from "hono/types";
|
import type { Bindings, BlankEnv, BlankInput } from "hono/types";
|
||||||
import { sqlCred } from "db/db.ts";
|
import { sqlCred } from "@core/db/dbNew.ts";
|
||||||
import { ErrorResponse, StatusResponse } from "src/schema";
|
import { ErrorResponse, StatusResponse } from "src/schema";
|
||||||
|
|
||||||
const RegistrationBodySchema = object({
|
const RegistrationBodySchema = object({
|
||||||
@ -44,7 +44,7 @@ export const registerHandler = createHandlers(async (c: ContextType) => {
|
|||||||
|
|
||||||
const response: StatusResponse = {
|
const response: StatusResponse = {
|
||||||
message: `User '${username}' registered successfully.`
|
message: `User '${username}' registered successfully.`
|
||||||
}
|
};
|
||||||
|
|
||||||
return c.json<StatusResponse>(response, 201);
|
return c.json<StatusResponse>(response, 201);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -53,21 +53,21 @@ export const registerHandler = createHandlers(async (c: ContextType) => {
|
|||||||
message: "Invalid registration data.",
|
message: "Invalid registration data.",
|
||||||
errors: e.errors,
|
errors: e.errors,
|
||||||
code: "INVALID_PAYLOAD"
|
code: "INVALID_PAYLOAD"
|
||||||
}
|
};
|
||||||
return c.json<ErrorResponse<string>>(response, 400);
|
return c.json<ErrorResponse<string>>(response, 400);
|
||||||
} else if (e instanceof SyntaxError) {
|
} else if (e instanceof SyntaxError) {
|
||||||
const response: ErrorResponse<string> = {
|
const response: ErrorResponse<string> = {
|
||||||
message: "Invalid JSON payload.",
|
message: "Invalid JSON payload.",
|
||||||
errors: [e.message],
|
errors: [e.message],
|
||||||
code: "INVALID_FORMAT"
|
code: "INVALID_FORMAT"
|
||||||
}
|
};
|
||||||
return c.json<ErrorResponse<string>>(response, 400);
|
return c.json<ErrorResponse<string>>(response, 400);
|
||||||
} else {
|
} else {
|
||||||
const response: ErrorResponse<string> = {
|
const response: ErrorResponse<string> = {
|
||||||
message: "Invalid JSON payload.",
|
message: "Unknown error.",
|
||||||
errors: [(e as Error).message],
|
errors: [(e as Error).message],
|
||||||
code: "UNKNOWN_ERR"
|
code: "UNKNOWN_ERROR"
|
||||||
}
|
};
|
||||||
return c.json<ErrorResponse<string>>(response, 500);
|
return c.json<ErrorResponse<string>>(response, 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,15 @@
|
|||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { redis } from "@core/db/redis.ts";
|
import { redis } from "@core/db/redis.ts";
|
||||||
import { sql } from "./db.ts";
|
import { sql } from "@core/db/dbNew.ts";
|
||||||
import { number, ValidationError } from "yup";
|
import { number, ValidationError } from "yup";
|
||||||
import { createHandlers } from "../src/utils.ts";
|
import { createHandlers } from "@/src/utils.ts";
|
||||||
import { getVideoInfo, getVideoInfoByBV } from "@core/net/getVideoInfo.ts";
|
import { getVideoInfo, getVideoInfoByBV } from "@core/net/getVideoInfo.ts";
|
||||||
import { idSchema } from "../routes/snapshots.ts";
|
import { idSchema } from "./snapshots.ts";
|
||||||
import { NetSchedulerError } from "@core/net/delegate.ts";
|
import { NetSchedulerError } from "@core/net/delegate.ts";
|
||||||
import type { Context } from "hono";
|
import type { Context } from "hono";
|
||||||
import type { BlankEnv, BlankInput } from "hono/types";
|
import type { BlankEnv, BlankInput } from "hono/types";
|
||||||
import type { VideoInfoData } from "@core/net/bilibili.d.ts";
|
import type { VideoInfoData } from "@core/net/bilibili.d.ts";
|
||||||
import { startTime, endTime } from 'hono/timing'
|
import { startTime, endTime } from "hono/timing";
|
||||||
|
|
||||||
const CACHE_EXPIRATION_SECONDS = 60;
|
const CACHE_EXPIRATION_SECONDS = 60;
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ async function insertVideoSnapshot(data: VideoInfoData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const videoInfoHandler = createHandlers(async (c: ContextType) => {
|
export const videoInfoHandler = createHandlers(async (c: ContextType) => {
|
||||||
startTime(c, 'parse', 'Parse the request');
|
startTime(c, "parse", "Parse the request");
|
||||||
try {
|
try {
|
||||||
const id = await idSchema.validate(c.req.param("id"));
|
const id = await idSchema.validate(c.req.param("id"));
|
||||||
let videoId: string | number = id as string;
|
let videoId: string | number = id as string;
|
||||||
@ -45,33 +45,33 @@ export const videoInfoHandler = createHandlers(async (c: ContextType) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = `cvsa:videoInfo:${videoId}`;
|
const cacheKey = `cvsa:videoInfo:${videoId}`;
|
||||||
endTime(c, 'parse');
|
endTime(c, "parse");
|
||||||
startTime(c, 'cache', 'Check for cached data');
|
startTime(c, "cache", "Check for cached data");
|
||||||
const cachedData = await redis.get(cacheKey);
|
const cachedData = await redis.get(cacheKey);
|
||||||
endTime(c, 'cache');
|
endTime(c, "cache");
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
return c.json(JSON.parse(cachedData));
|
return c.json(JSON.parse(cachedData));
|
||||||
}
|
}
|
||||||
startTime(c, 'net', 'Fetch data');
|
startTime(c, "net", "Fetch data");
|
||||||
let result: VideoInfoData | number;
|
let result: VideoInfoData | number;
|
||||||
if (typeof videoId === "number") {
|
if (typeof videoId === "number") {
|
||||||
result = await getVideoInfo(videoId, "getVideoInfo");
|
result = await getVideoInfo(videoId, "getVideoInfo");
|
||||||
} else {
|
} else {
|
||||||
result = await getVideoInfoByBV(videoId, "getVideoInfo");
|
result = await getVideoInfoByBV(videoId, "getVideoInfo");
|
||||||
}
|
}
|
||||||
endTime(c, 'net');
|
endTime(c, "net");
|
||||||
|
|
||||||
if (typeof result === "number") {
|
if (typeof result === "number") {
|
||||||
return c.json({ message: "Error fetching video info", code: result }, 500);
|
return c.json({ message: "Error fetching video info", code: result }, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime(c, 'db', 'Write data to database');
|
startTime(c, "db", "Write data to database");
|
||||||
|
|
||||||
await redis.setex(cacheKey, CACHE_EXPIRATION_SECONDS, JSON.stringify(result));
|
await redis.setex(cacheKey, CACHE_EXPIRATION_SECONDS, JSON.stringify(result));
|
||||||
|
|
||||||
await insertVideoSnapshot(result);
|
await insertVideoSnapshot(result);
|
||||||
|
|
||||||
endTime(c, 'db');
|
endTime(c, "db");
|
||||||
return c.json(result);
|
return c.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ValidationError) {
|
if (e instanceof ValidationError) {
|
@ -1,10 +1,10 @@
|
|||||||
import type { Context } from "hono";
|
import type { Context } from "hono";
|
||||||
import { createHandlers } from "../src/utils.ts";
|
import { createHandlers } from "src/utils.ts";
|
||||||
import type { BlankEnv, BlankInput } from "hono/types";
|
import type { BlankEnv, BlankInput } from "hono/types";
|
||||||
import { getVideoSnapshots, getVideoSnapshotsByBV } from "../db/videoSnapshot.ts";
|
import { getVideoSnapshots, getVideoSnapshotsByBV } from "db/snapshots.ts";
|
||||||
import type { VideoSnapshotType } from "@core/db/schema.d.ts";
|
import type { VideoSnapshotType } from "@core/db/schema.d.ts";
|
||||||
import { boolean, mixed, number, object, ValidationError } from "yup";
|
import { boolean, mixed, number, object, ValidationError } from "yup";
|
||||||
import { ErrorResponse } from "../src/schema";
|
import { ErrorResponse } from "src/schema";
|
||||||
import { startTime, endTime } from "hono/timing";
|
import { startTime, endTime } from "hono/timing";
|
||||||
|
|
||||||
const SnapshotQueryParamsSchema = object({
|
const SnapshotQueryParamsSchema = object({
|
||||||
@ -96,7 +96,7 @@ export const getSnapshotsHanlder = createHandlers(async (c: ContextType) => {
|
|||||||
return c.json<ErrorResponse<string>>(response, 400);
|
return c.json<ErrorResponse<string>>(response, 400);
|
||||||
} else {
|
} else {
|
||||||
const response: ErrorResponse<unknown> = {
|
const response: ErrorResponse<unknown> = {
|
||||||
code: "UNKNOWN_ERR",
|
code: "UNKNOWN_ERROR",
|
||||||
message: "Unhandled error",
|
message: "Unhandled error",
|
||||||
errors: [e]
|
errors: [e]
|
||||||
};
|
};
|
2
packages/backend/routes/video/index.ts
Normal file
2
packages/backend/routes/video/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./[id]/info";
|
||||||
|
export * from "./[id]/snapshots";
|
@ -1,7 +1,7 @@
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import type { TimingVariables } from "hono/timing";
|
import type { TimingVariables } from "hono/timing";
|
||||||
import { startServer } from "./startServer.ts";
|
import { startServer } from "./startServer.ts";
|
||||||
import { configureRoutes } from "routes";
|
import { configureRoutes } from "./routing.ts";
|
||||||
import { configureMiddleWares } from "middleware";
|
import { configureMiddleWares } from "middleware";
|
||||||
import { notFoundRoute } from "routes/404.ts";
|
import { notFoundRoute } from "routes/404.ts";
|
||||||
|
|
||||||
|
23
packages/backend/src/routing.ts
Normal file
23
packages/backend/src/routing.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { rootHandler } from "routes";
|
||||||
|
import { pingHandler } from "routes/ping";
|
||||||
|
import { registerHandler } from "routes/user";
|
||||||
|
import { videoInfoHandler, getSnapshotsHanlder } from "routes/video";
|
||||||
|
import { Hono } from "hono";
|
||||||
|
import { Variables } from "hono/types";
|
||||||
|
import { createCaptchaSessionHandler, verifyChallengeHandler } from "routes/captcha";
|
||||||
|
import { getCaptchaDifficultyHandler } from "../routes/captcha/difficulty/GET.ts";
|
||||||
|
|
||||||
|
export function configureRoutes(app: Hono<{ Variables: Variables }>) {
|
||||||
|
app.get("/", ...rootHandler);
|
||||||
|
app.all("/ping", ...pingHandler);
|
||||||
|
|
||||||
|
app.get("/video/:id/snapshots", ...getSnapshotsHanlder);
|
||||||
|
app.post("/user", ...registerHandler);
|
||||||
|
|
||||||
|
app.get("/video/:id/info", ...videoInfoHandler);
|
||||||
|
|
||||||
|
app.post("/captcha/session", ...createCaptchaSessionHandler);
|
||||||
|
app.get("/captcha/:id/result", ...verifyChallengeHandler);
|
||||||
|
|
||||||
|
app.get("/captcha/difficulty", ...getCaptchaDifficultyHandler)
|
||||||
|
}
|
19
packages/backend/src/schema.d.ts
vendored
19
packages/backend/src/schema.d.ts
vendored
@ -1,9 +1,20 @@
|
|||||||
type ErrorCode = "INVALID_QUERY_PARAMS" | "UNKNOWN_ERR" | "INVALID_PAYLOAD" | "INVALID_FORMAT" | "BODY_TOO_LARGE";
|
type ErrorCode =
|
||||||
|
| "INVALID_QUERY_PARAMS"
|
||||||
|
| "UNKNOWN_ERROR"
|
||||||
|
| "INVALID_PAYLOAD"
|
||||||
|
| "INVALID_FORMAT"
|
||||||
|
| "INVALID_HEADER"
|
||||||
|
| "BODY_TOO_LARGE"
|
||||||
|
| "UNAUTHORIZED"
|
||||||
|
| "INVALID_CREDENTIALS"
|
||||||
|
| "ENTITY_NOT_FOUND"
|
||||||
|
| "SERVER_ERROR"
|
||||||
|
| "RATE_LIMIT_EXCEEDED";
|
||||||
|
|
||||||
export interface ErrorResponse<E> {
|
export interface ErrorResponse<E=string> {
|
||||||
code: ErrorCode
|
code: ErrorCode;
|
||||||
message: string;
|
message: string;
|
||||||
errors: E[];
|
errors?: E[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusResponse {
|
export interface StatusResponse {
|
||||||
|
@ -32,7 +32,7 @@ function logStartup(hostname: string, port: number, wasAutoIncremented: boolean,
|
|||||||
console.log("\nPress Ctrl+C to quit.");
|
console.log("\nPress Ctrl+C to quit.");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startServer(app: Hono<{Variables: Variables }>) {
|
export async function startServer(app: Hono<{ Variables: Variables }>) {
|
||||||
const NODE_ENV = process.env.NODE_ENV || "production";
|
const NODE_ENV = process.env.NODE_ENV || "production";
|
||||||
const HOST = process.env.HOST ?? (NODE_ENV === "development" ? "0.0.0.0" : "127.0.0.1");
|
const HOST = process.env.HOST ?? (NODE_ENV === "development" ? "0.0.0.0" : "127.0.0.1");
|
||||||
const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;
|
const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@core/*": ["../core/*"],
|
"@core/*": ["../core/*"],
|
||||||
|
"@/*": ["./*"],
|
||||||
"@crawler/*": ["../crawler/*"]
|
"@crawler/*": ["../crawler/*"]
|
||||||
},
|
},
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import postgres from "postgres";
|
import postgres from "postgres";
|
||||||
import { postgresConfigNpm } from "./pgConfigNew";
|
import { postgresConfigCred, postgresConfig } from "./pgConfigNew";
|
||||||
|
|
||||||
export const sql = postgres(postgresConfigNpm);
|
export const sql = postgres(postgresConfig);
|
||||||
|
|
||||||
export const sqlTest = postgres(postgresConfigNpm);
|
export const sqlCred = postgres(postgresConfigCred);
|
||||||
|
|
||||||
|
export const sqlTest = postgres(postgresConfig);
|
@ -18,14 +18,6 @@ const databasePassword = getEnvVar("DB_PASSWORD")!;
|
|||||||
const databasePort = getEnvVar("DB_PORT")!;
|
const databasePort = getEnvVar("DB_PORT")!;
|
||||||
|
|
||||||
export const postgresConfig = {
|
export const postgresConfig = {
|
||||||
hostname: databaseHost,
|
|
||||||
port: parseInt(databasePort),
|
|
||||||
database: databaseName,
|
|
||||||
user: databaseUser,
|
|
||||||
password: databasePassword
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postgresConfigNpm = {
|
|
||||||
host: databaseHost,
|
host: databaseHost,
|
||||||
port: parseInt(databasePort),
|
port: parseInt(databasePort),
|
||||||
database: databaseName,
|
database: databaseName,
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import type postgres from "postgres";
|
import type postgres from "postgres";
|
||||||
|
|
||||||
export type Psql = postgres.Sql<{}>;
|
export type Psql = postgres.Sql;
|
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cvsa/core",
|
|
||||||
"exports": "./main.ts",
|
|
||||||
"imports": {
|
|
||||||
"ioredis": "npm:ioredis",
|
|
||||||
"log/": "./log/",
|
|
||||||
"db/": "./db/",
|
|
||||||
"$std/": "https://deno.land/std@0.216.0/",
|
|
||||||
"mq/": "./mq/",
|
|
||||||
"chalk": "npm:chalk",
|
|
||||||
"winston": "npm:winston",
|
|
||||||
"logform": "npm:logform",
|
|
||||||
"@core/": "./",
|
|
||||||
"child_process": "node:child_process",
|
|
||||||
"util": "node:util"
|
|
||||||
}
|
|
||||||
}
|
|
15
packages/core/lib/randomID.ts
Normal file
15
packages/core/lib/randomID.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export function generateRandomId(length: number): string {
|
||||||
|
const characters = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||||
|
const charactersLength = characters.length;
|
||||||
|
const randomBytes = new Uint8Array(length);
|
||||||
|
|
||||||
|
crypto.getRandomValues(randomBytes);
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const randomIndex = randomBytes[i] % charactersLength;
|
||||||
|
result += characters.charAt(randomIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
import { redis } from "../../core/db/redis.ts";
|
import { redis } from "@core/db/redis.ts";
|
||||||
|
|
||||||
class LockManager {
|
class LockManager {
|
||||||
private redis: Redis;
|
private redis: Redis;
|
55
packages/core/mq/multipleRateLimiter.ts
Normal file
55
packages/core/mq/multipleRateLimiter.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { RateLimiter as Limiter } from "@koshnic/ratelimit";
|
||||||
|
import { redis } from "@core/db/redis.ts";
|
||||||
|
|
||||||
|
export interface RateLimiterConfig {
|
||||||
|
duration: number;
|
||||||
|
max: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RateLimiterError extends Error {
|
||||||
|
public code: string;
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = "RateLimiterError";
|
||||||
|
this.code = "RATE_LIMIT_EXCEEDED";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MultipleRateLimiter {
|
||||||
|
private readonly name: string;
|
||||||
|
private readonly configs: RateLimiterConfig[] = [];
|
||||||
|
private readonly limiter: Limiter;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @param name The name of the rate limiter
|
||||||
|
* @param configs The configuration of the rate limiter, containing:
|
||||||
|
* - duration: The duration of window in seconds
|
||||||
|
* - max: The maximum number of tokens allowed in the window
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
name: string,
|
||||||
|
configs: RateLimiterConfig[]
|
||||||
|
) {
|
||||||
|
this.configs = configs;
|
||||||
|
this.limiter = new Limiter(redis);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trigger an event in the rate limiter
|
||||||
|
*/
|
||||||
|
async trigger(shouldThrow = true): Promise<void> {
|
||||||
|
for (let i = 0; i < this.configs.length; i++) {
|
||||||
|
const { duration, max } = this.configs[i];
|
||||||
|
const { allowed } = await this.limiter.allow(`cvsa:${this.name}_${i}`, {
|
||||||
|
burst: max,
|
||||||
|
ratePerPeriod: max,
|
||||||
|
period: duration,
|
||||||
|
cost: 1
|
||||||
|
});
|
||||||
|
if (!allowed && shouldThrow) {
|
||||||
|
throw new RateLimiterError("Rate limit exceeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
import type { SlidingWindow } from "./slidingWindow.ts";
|
|
||||||
|
|
||||||
export interface RateLimiterConfig {
|
|
||||||
window: SlidingWindow;
|
|
||||||
max: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RateLimiter {
|
|
||||||
private readonly configs: RateLimiterConfig[];
|
|
||||||
private readonly configEventNames: string[];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @param name The name of the rate limiter
|
|
||||||
* @param configs The configuration of the rate limiter, containing:
|
|
||||||
* - window: The sliding window to use
|
|
||||||
* - max: The maximum number of events allowed in the window
|
|
||||||
*/
|
|
||||||
constructor(name: string, configs: RateLimiterConfig[]) {
|
|
||||||
this.configs = configs;
|
|
||||||
this.configEventNames = configs.map((_, index) => `${name}_config_${index}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if the event has reached the rate limit
|
|
||||||
*/
|
|
||||||
async getAvailability(): Promise<boolean> {
|
|
||||||
for (let i = 0; i < this.configs.length; i++) {
|
|
||||||
const config = this.configs[i];
|
|
||||||
const eventName = this.configEventNames[i];
|
|
||||||
const count = await config.window.count(eventName);
|
|
||||||
if (count >= config.max) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Trigger an event in the rate limiter
|
|
||||||
*/
|
|
||||||
async trigger(): Promise<void> {
|
|
||||||
for (let i = 0; i < this.configs.length; i++) {
|
|
||||||
const config = this.configs[i];
|
|
||||||
const eventName = this.configEventNames[i];
|
|
||||||
await config.window.event(eventName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async clear(): Promise<void> {
|
|
||||||
for (let i = 0; i < this.configs.length; i++) {
|
|
||||||
const config = this.configs[i];
|
|
||||||
const eventName = this.configEventNames[i];
|
|
||||||
await config.window.clear(eventName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,14 +32,20 @@ export class SlidingWindow {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Count the number of events in the sliding window
|
* Count the number of events in the sliding window
|
||||||
* @param eventName The name of the event
|
* @param {string} eventName The name of the event
|
||||||
|
* @param {number} [duration] The duration of the window in seconds
|
||||||
*/
|
*/
|
||||||
async count(eventName: string): Promise<number> {
|
async count(eventName: string, duration?: number): Promise<number> {
|
||||||
const key = `cvsa:sliding_window:${eventName}`;
|
const key = `cvsa:sliding_window:${eventName}`;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
// Remove timestamps outside the window
|
// Remove timestamps outside the window
|
||||||
await this.redis.zremrangebyscore(key, 0, now - this.windowSize);
|
await this.redis.zremrangebyscore(key, 0, now - this.windowSize);
|
||||||
|
|
||||||
|
if (duration) {
|
||||||
|
return this.redis.zcount(key, now - duration * 1000, now);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the number of timestamps in the window
|
// Get the number of timestamps in the window
|
||||||
return this.redis.zcard(key);
|
return this.redis.zcard(key);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { RateLimiter, type RateLimiterConfig } from "mq/rateLimiter.ts";
|
import { MultipleRateLimiter, RateLimiterError, type RateLimiterConfig } from "@core/mq/multipleRateLimiter.ts";
|
||||||
import { SlidingWindow } from "mq/slidingWindow.ts";
|
|
||||||
import { redis } from "db/redis.ts";
|
|
||||||
import { ReplyError } from "ioredis";
|
import { ReplyError } from "ioredis";
|
||||||
import { SECOND } from "../const/time.ts";
|
import { SECOND } from "@core/const/time.ts";
|
||||||
import { spawn, SpawnOptions } from "child_process";
|
import { spawn, SpawnOptions } from "child_process";
|
||||||
|
|
||||||
export function spawnPromise(
|
export function spawnPromise(
|
||||||
@ -73,11 +71,11 @@ export class NetSchedulerError extends Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LimiterMap = {
|
type LimiterMap = {
|
||||||
[name: string]: RateLimiter;
|
[name: string]: MultipleRateLimiter;
|
||||||
};
|
};
|
||||||
|
|
||||||
type OptionalLimiterMap = {
|
type OptionalLimiterMap = {
|
||||||
[name: string]: RateLimiter | null;
|
[name: string]: MultipleRateLimiter | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TaskMap = {
|
type TaskMap = {
|
||||||
@ -121,20 +119,23 @@ class NetworkDelegate {
|
|||||||
const proxies = this.getTaskProxies(taskName);
|
const proxies = this.getTaskProxies(taskName);
|
||||||
for (const proxyName of proxies) {
|
for (const proxyName of proxies) {
|
||||||
const limiterId = "proxy-" + proxyName + "-" + taskName;
|
const limiterId = "proxy-" + proxyName + "-" + taskName;
|
||||||
this.proxyLimiters[limiterId] = config ? new RateLimiter(limiterId, config) : null;
|
this.proxyLimiters[limiterId] = config ? new MultipleRateLimiter(limiterId, config) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async triggerLimiter(task: string, proxy: string): Promise<void> {
|
async triggerLimiter(task: string, proxy: string, force: boolean = false): Promise<void> {
|
||||||
const limiterId = "proxy-" + proxy + "-" + task;
|
const limiterId = "proxy-" + proxy + "-" + task;
|
||||||
const providerLimiterId = "provider-" + proxy + "-" + this.tasks[task].provider;
|
const providerLimiterId = "provider-" + proxy + "-" + this.tasks[task].provider;
|
||||||
try {
|
try {
|
||||||
await this.proxyLimiters[limiterId]?.trigger();
|
await this.proxyLimiters[limiterId]?.trigger(!force);
|
||||||
await this.providerLimiters[providerLimiterId]?.trigger();
|
await this.providerLimiters[providerLimiterId]?.trigger(!force);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = e as Error;
|
const error = e as Error;
|
||||||
if (e instanceof ReplyError) {
|
if (e instanceof ReplyError) {
|
||||||
logger.error(error, "redis");
|
logger.error(error, "redis");
|
||||||
|
} else if (e instanceof RateLimiterError) {
|
||||||
|
// Re-throw it to ensure this.request can catch it
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
logger.warn(`Unhandled error: ${error.message}`, "mq", "proxyRequest");
|
logger.warn(`Unhandled error: ${error.message}`, "mq", "proxyRequest");
|
||||||
}
|
}
|
||||||
@ -149,7 +150,7 @@ class NetworkDelegate {
|
|||||||
}
|
}
|
||||||
for (const proxyName of bindProxies) {
|
for (const proxyName of bindProxies) {
|
||||||
const limiterId = "provider-" + proxyName + "-" + providerName;
|
const limiterId = "provider-" + proxyName + "-" + providerName;
|
||||||
this.providerLimiters[limiterId] = new RateLimiter(limiterId, config);
|
this.providerLimiters[limiterId] = new MultipleRateLimiter(limiterId, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,9 +169,15 @@ class NetworkDelegate {
|
|||||||
// find a available proxy
|
// find a available proxy
|
||||||
const proxiesNames = this.getTaskProxies(task);
|
const proxiesNames = this.getTaskProxies(task);
|
||||||
for (const proxyName of shuffleArray(proxiesNames)) {
|
for (const proxyName of shuffleArray(proxiesNames)) {
|
||||||
if (await this.getProxyAvailability(proxyName, task)) {
|
try {
|
||||||
return await this.proxyRequest<R>(url, proxyName, task, method);
|
return await this.proxyRequest<R>(url, proxyName, task, method);
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (e instanceof RateLimiterError) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw new NetSchedulerError("No proxy is available currently.", "NO_PROXY_AVAILABLE");
|
throw new NetSchedulerError("No proxy is available currently.", "NO_PROXY_AVAILABLE");
|
||||||
}
|
}
|
||||||
@ -202,16 +209,8 @@ class NetworkDelegate {
|
|||||||
throw new NetSchedulerError(`Proxy "${proxyName}" not found`, "PROXY_NOT_FOUND");
|
throw new NetSchedulerError(`Proxy "${proxyName}" not found`, "PROXY_NOT_FOUND");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!force) {
|
await this.triggerLimiter(task, proxyName, force);
|
||||||
const isAvailable = await this.getProxyAvailability(proxyName, task);
|
|
||||||
const limiter = "proxy-" + proxyName + "-" + task;
|
|
||||||
if (!isAvailable) {
|
|
||||||
throw new NetSchedulerError(`Proxy "${limiter}" is rate limited`, "PROXY_RATE_LIMITED");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await this.makeRequest<R>(url, proxy, method);
|
const result = await this.makeRequest<R>(url, proxy, method);
|
||||||
await this.triggerLimiter(task, proxyName);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,32 +225,6 @@ class NetworkDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getProxyAvailability(proxyName: string, taskName: string): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
const task = this.tasks[taskName];
|
|
||||||
const provider = task.provider;
|
|
||||||
const proxyLimiterId = "proxy-" + proxyName + "-" + task;
|
|
||||||
const providerLimiterId = "provider-" + proxyName + "-" + provider;
|
|
||||||
if (!this.proxyLimiters[proxyLimiterId]) {
|
|
||||||
const providerLimiter = this.providerLimiters[providerLimiterId];
|
|
||||||
return await providerLimiter.getAvailability();
|
|
||||||
}
|
|
||||||
const proxyLimiter = this.proxyLimiters[proxyLimiterId];
|
|
||||||
const providerLimiter = this.providerLimiters[providerLimiterId];
|
|
||||||
const providerAvailable = await providerLimiter.getAvailability();
|
|
||||||
const proxyAvailable = await proxyLimiter.getAvailability();
|
|
||||||
return providerAvailable && proxyAvailable;
|
|
||||||
} catch (e) {
|
|
||||||
const error = e as Error;
|
|
||||||
if (e instanceof ReplyError) {
|
|
||||||
logger.error(error, "redis");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
logger.error(error, "mq", "getProxyAvailability");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async nativeRequest<R>(url: string, method: string): Promise<R> {
|
private async nativeRequest<R>(url: string, method: string): Promise<R> {
|
||||||
try {
|
try {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
@ -316,37 +289,37 @@ class NetworkDelegate {
|
|||||||
const networkDelegate = new NetworkDelegate();
|
const networkDelegate = new NetworkDelegate();
|
||||||
const videoInfoRateLimiterConfig: RateLimiterConfig[] = [
|
const videoInfoRateLimiterConfig: RateLimiterConfig[] = [
|
||||||
{
|
{
|
||||||
window: new SlidingWindow(redis, 0.3),
|
duration: 0.3,
|
||||||
max: 1,
|
max: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
window: new SlidingWindow(redis, 3),
|
duration: 3,
|
||||||
max: 5,
|
max: 5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
window: new SlidingWindow(redis, 30),
|
duration: 30,
|
||||||
max: 30,
|
max: 30,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
window: new SlidingWindow(redis, 2 * 60),
|
duration: 2 * 60,
|
||||||
max: 50,
|
max: 50,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const biliLimiterConfig: RateLimiterConfig[] = [
|
const biliLimiterConfig: RateLimiterConfig[] = [
|
||||||
{
|
{
|
||||||
window: new SlidingWindow(redis, 1),
|
duration: 1,
|
||||||
max: 6,
|
max: 6,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
window: new SlidingWindow(redis, 5),
|
duration: 5,
|
||||||
max: 20,
|
max: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
window: new SlidingWindow(redis, 30),
|
duration: 30,
|
||||||
max: 100,
|
max: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
window: new SlidingWindow(redis, 5 * 60),
|
duration: 5 * 60,
|
||||||
max: 200,
|
max: 200,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "core",
|
"name": "core",
|
||||||
"dependencies": {
|
"scripts": {
|
||||||
"chalk": "^5.4.1",
|
"test": "bun --env-file=.env.test run vitest"
|
||||||
"ioredis": "^5.6.1",
|
},
|
||||||
"logform": "^2.7.0",
|
"dependencies": {
|
||||||
"postgres": "^3.4.5",
|
"@koshnic/ratelimit": "^1.0.3",
|
||||||
"winston": "^3.17.0"
|
"chalk": "^5.4.1",
|
||||||
},
|
"ioredis": "^5.6.1",
|
||||||
"devDependencies": {
|
"logform": "^2.7.0",
|
||||||
"@types/ioredis": "^5.0.0"
|
"postgres": "^3.4.5",
|
||||||
}
|
"winston": "^3.17.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/ioredis": "^5.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
18
packages/core/test/lib/randomID.test.ts
Normal file
18
packages/core/test/lib/randomID.test.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { generateRandomId } from "@core/lib/randomID.ts";
|
||||||
|
|
||||||
|
describe("generateRandomId", () => {
|
||||||
|
it("should generate an ID of the specified length", () => {
|
||||||
|
const length = 15;
|
||||||
|
const id = generateRandomId(length);
|
||||||
|
expect(id).toHaveLength(length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should generate an ID containing only allowed characters", () => {
|
||||||
|
const allowedChars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
||||||
|
const id = generateRandomId(20);
|
||||||
|
for (const char of id) {
|
||||||
|
expect(allowedChars).toContain(char);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +1,4 @@
|
|||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/psql.d.ts";
|
||||||
import { AllDataType, BiliUserType } from "@core/db/schema";
|
import { AllDataType, BiliUserType } from "@core/db/schema";
|
||||||
import { AkariModelVersion } from "ml/const";
|
import { AkariModelVersion } from "ml/const";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { LatestSnapshotType } from "@core/db/schema";
|
import { LatestSnapshotType } from "@core/db/schema";
|
||||||
import { SnapshotNumber } from "mq/task/getVideoStats.ts";
|
import { SnapshotNumber } from "mq/task/getVideoStats.ts";
|
||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/psql.d.ts";
|
||||||
|
|
||||||
export async function getVideosNearMilestone(sql: Psql) {
|
export async function getVideosNearMilestone(sql: Psql) {
|
||||||
const queryResult = await sql<LatestSnapshotType[]>`
|
const queryResult = await sql<LatestSnapshotType[]>`
|
||||||
|
@ -4,7 +4,7 @@ import { MINUTE } from "@core/const/time.ts";
|
|||||||
import { redis } from "@core/db/redis.ts";
|
import { redis } from "@core/db/redis.ts";
|
||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
import { parseTimestampFromPsql } from "../utils/formatTimestampToPostgre.ts";
|
import { parseTimestampFromPsql } from "../utils/formatTimestampToPostgre.ts";
|
||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/psql.d.ts";
|
||||||
|
|
||||||
const REDIS_KEY = "cvsa:snapshot_window_counts";
|
const REDIS_KEY = "cvsa:snapshot_window_counts";
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/psql.d.ts";
|
||||||
import { parseTimestampFromPsql } from "utils/formatTimestampToPostgre.ts";
|
import { parseTimestampFromPsql } from "utils/formatTimestampToPostgre.ts";
|
||||||
|
|
||||||
export async function getNotCollectedSongs(sql: Psql) {
|
export async function getNotCollectedSongs(sql: Psql) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Job } from "bullmq";
|
import { Job } from "bullmq";
|
||||||
import { getAllVideosWithoutActiveSnapshotSchedule, scheduleSnapshot } from "db/snapshotSchedule.ts";
|
import { getAllVideosWithoutActiveSnapshotSchedule, scheduleSnapshot } from "db/snapshotSchedule.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { getLatestVideoSnapshot } from "db/snapshot.ts";
|
import { getLatestVideoSnapshot } from "db/snapshot.ts";
|
||||||
import { HOUR, MINUTE } from "@core/const/time.ts";
|
import { HOUR, MINUTE } from "@core/const/time.ts";
|
||||||
import { sql } from "@core/db/dbNew";
|
import { sql } from "@core/db/dbNew";
|
||||||
|
@ -3,7 +3,7 @@ import { getUnlabelledVideos, getVideoInfoFromAllData, insertVideoLabel } from "
|
|||||||
import Akari from "ml/akari.ts";
|
import Akari from "ml/akari.ts";
|
||||||
import { ClassifyVideoQueue } from "mq/index.ts";
|
import { ClassifyVideoQueue } from "mq/index.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { aidExistsInSongs } from "db/songs.ts";
|
import { aidExistsInSongs } from "db/songs.ts";
|
||||||
import { insertIntoSongs } from "mq/task/collectSongs.ts";
|
import { insertIntoSongs } from "mq/task/collectSongs.ts";
|
||||||
import { scheduleSnapshot } from "db/snapshotSchedule.ts";
|
import { scheduleSnapshot } from "db/snapshotSchedule.ts";
|
||||||
|
@ -4,7 +4,7 @@ import { truncate } from "utils/truncate.ts";
|
|||||||
import { getVideosWithoutActiveSnapshotSchedule, scheduleSnapshot } from "db/snapshotSchedule.ts";
|
import { getVideosWithoutActiveSnapshotSchedule, scheduleSnapshot } from "db/snapshotSchedule.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { HOUR, MINUTE, WEEK } from "@core/const/time.ts";
|
import { HOUR, MINUTE, WEEK } from "@core/const/time.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { getRegularSnapshotInterval } from "mq/task/regularSnapshotInterval.ts";
|
import { getRegularSnapshotInterval } from "mq/task/regularSnapshotInterval.ts";
|
||||||
import { sql } from "@core/db/dbNew.ts";
|
import { sql } from "@core/db/dbNew.ts";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { Job } from "bullmq";
|
|||||||
import { scheduleSnapshot, setSnapshotStatus, snapshotScheduleExists } from "db/snapshotSchedule.ts";
|
import { scheduleSnapshot, setSnapshotStatus, snapshotScheduleExists } from "db/snapshotSchedule.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { HOUR, MINUTE, SECOND } from "@core/const/time.ts";
|
import { HOUR, MINUTE, SECOND } from "@core/const/time.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { getBiliVideoStatus, setBiliVideoStatus } from "../../db/bilibili_metadata.ts";
|
import { getBiliVideoStatus, setBiliVideoStatus } from "../../db/bilibili_metadata.ts";
|
||||||
import { insertVideoSnapshot } from "mq/task/getVideoStats.ts";
|
import { insertVideoSnapshot } from "mq/task/getVideoStats.ts";
|
||||||
import { getSongsPublihsedAt } from "db/songs.ts";
|
import { getSongsPublihsedAt } from "db/songs.ts";
|
||||||
|
@ -2,7 +2,7 @@ import { findClosestSnapshot, getLatestSnapshot, hasAtLeast2Snapshots } from "db
|
|||||||
import { truncate } from "utils/truncate.ts";
|
import { truncate } from "utils/truncate.ts";
|
||||||
import { closetMilestone } from "./exec/snapshotTick.ts";
|
import { closetMilestone } from "./exec/snapshotTick.ts";
|
||||||
import { HOUR, MINUTE } from "@core/const/time.ts";
|
import { HOUR, MINUTE } from "@core/const/time.ts";
|
||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/global.d.ts";
|
||||||
|
|
||||||
const log = (value: number, base: number = 10) => Math.log(value) / Math.log(base);
|
const log = (value: number, base: number = 10) => Math.log(value) / Math.log(base);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { aidExistsInSongs, getNotCollectedSongs } from "db/songs.ts";
|
|||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { scheduleSnapshot } from "db/snapshotSchedule.ts";
|
import { scheduleSnapshot } from "db/snapshotSchedule.ts";
|
||||||
import { MINUTE } from "@core/const/time.ts";
|
import { MINUTE } from "@core/const/time.ts";
|
||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/psql.d.ts";
|
||||||
|
|
||||||
export async function collectSongs() {
|
export async function collectSongs() {
|
||||||
const aids = await getNotCollectedSongs(sql);
|
const aids = await getNotCollectedSongs(sql);
|
||||||
|
@ -4,7 +4,7 @@ import logger from "@core/log/logger.ts";
|
|||||||
import { ClassifyVideoQueue } from "mq/index.ts";
|
import { ClassifyVideoQueue } from "mq/index.ts";
|
||||||
import { userExistsInBiliUsers, videoExistsInAllData } from "../../db/bilibili_metadata.ts";
|
import { userExistsInBiliUsers, videoExistsInAllData } from "../../db/bilibili_metadata.ts";
|
||||||
import { HOUR, SECOND } from "@core/const/time.ts";
|
import { HOUR, SECOND } from "@core/const/time.ts";
|
||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/psql.d.ts";
|
||||||
|
|
||||||
export async function insertVideoInfo(sql: Psql, aid: number) {
|
export async function insertVideoInfo(sql: Psql, aid: number) {
|
||||||
const videoExists = await videoExistsInAllData(sql, aid);
|
const videoExists = await videoExistsInAllData(sql, aid);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { getVideoInfo } from "@core/net/getVideoInfo.ts";
|
import { getVideoInfo } from "@core/net/getVideoInfo.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/psql.d.ts";
|
||||||
|
|
||||||
export interface SnapshotNumber {
|
export interface SnapshotNumber {
|
||||||
time: number;
|
time: number;
|
||||||
|
@ -4,7 +4,7 @@ import { sleep } from "utils/sleep.ts";
|
|||||||
import { SECOND } from "@core/const/time.ts";
|
import { SECOND } from "@core/const/time.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { LatestVideosQueue } from "mq/index.ts";
|
import { LatestVideosQueue } from "mq/index.ts";
|
||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/psql.d.ts";
|
||||||
|
|
||||||
export async function queueLatestVideos(
|
export async function queueLatestVideos(
|
||||||
sql: Psql,
|
sql: Psql,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { findClosestSnapshot, findSnapshotBefore, getLatestSnapshot } from "db/snapshotSchedule.ts";
|
import { findClosestSnapshot, findSnapshotBefore, getLatestSnapshot } from "db/snapshotSchedule.ts";
|
||||||
import { HOUR } from "@core/const/time.ts";
|
import { HOUR } from "@core/const/time.ts";
|
||||||
import type { Psql } from "global.d.ts";
|
import type { Psql } from "@core/db/global.d.ts";
|
||||||
|
|
||||||
export const getRegularSnapshotInterval = async (sql: Psql, aid: number) => {
|
export const getRegularSnapshotInterval = async (sql: Psql, aid: number) => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
@ -3,7 +3,7 @@ import { redis } from "@core/db/redis.ts";
|
|||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { classifyVideosWorker, classifyVideoWorker } from "mq/exec/classifyVideo.ts";
|
import { classifyVideosWorker, classifyVideoWorker } from "mq/exec/classifyVideo.ts";
|
||||||
import { WorkerError } from "mq/schema.ts";
|
import { WorkerError } from "mq/schema.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import Akari from "ml/akari.ts";
|
import Akari from "ml/akari.ts";
|
||||||
|
|
||||||
const shutdown = async (signal: string) => {
|
const shutdown = async (signal: string) => {
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
} from "mq/exec/executors.ts";
|
} from "mq/exec/executors.ts";
|
||||||
import { redis } from "@core/db/redis.ts";
|
import { redis } from "@core/db/redis.ts";
|
||||||
import logger from "@core/log/logger.ts";
|
import logger from "@core/log/logger.ts";
|
||||||
import { lockManager } from "mq/lockManager.ts";
|
import { lockManager } from "@core/mq/lockManager.ts";
|
||||||
import { WorkerError } from "mq/schema.ts";
|
import { WorkerError } from "mq/schema.ts";
|
||||||
|
|
||||||
const releaseLockForJob = async (name: string) => {
|
const releaseLockForJob = async (name: string) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user