feat: Add unit tests for resolvers and services, update dependencies, and remove unused scripts.
This commit is contained in:
10
package.json
10
package.json
@@ -7,17 +7,15 @@
|
||||
"scripts": {
|
||||
"dev": "wrangler dev src/index.ts --port 8080",
|
||||
"deploy": "wrangler deploy --minify src/index.ts",
|
||||
"env:generate": "tsx src/scripts/generateEnv.ts",
|
||||
"env:verify": "tsx src/scripts/verifyEnv.ts",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
"test": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"test:ui": "vitest --ui",
|
||||
"coverage": "vitest --coverage",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@consumet/extensions": "github:consumet/consumet.ts#3dd0ccb",
|
||||
"@haverstack/axios-fetch-adapter": "^0.12.0",
|
||||
"@hono/swagger-ui": "^0.5.1",
|
||||
"@hono/zod-openapi": "^0.19.5",
|
||||
"@hono/zod-validator": "^0.2.2",
|
||||
@@ -39,16 +37,16 @@
|
||||
"devDependencies": {
|
||||
"@0no-co/graphqlsp": "^1.12.16",
|
||||
"@cloudflare/vitest-pool-workers": "^0.10.7",
|
||||
"@cloudflare/workers-types": "^4.20250423.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/lodash.isequal": "^4.5.8",
|
||||
"@types/lodash.mapkeys": "^4.6.9",
|
||||
"@types/luxon": "^3.6.2",
|
||||
"@types/node": "^22.10.1",
|
||||
"@types/pngjs": "^6.0.5",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vitest/coverage-istanbul": "3.2.4",
|
||||
"@vitest/runner": "^3.2.4",
|
||||
"@vitest/snapshot": "^3.2.4",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"cloudflare": "^5.2.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"drizzle-kit": "^0.31.7",
|
||||
|
||||
474
pnpm-lock.yaml
generated
474
pnpm-lock.yaml
generated
@@ -15,9 +15,6 @@ importers:
|
||||
"@consumet/extensions":
|
||||
specifier: github:consumet/consumet.ts#3dd0ccb
|
||||
version: https://codeload.github.com/consumet/consumet.ts/tar.gz/3dd0ccb
|
||||
"@haverstack/axios-fetch-adapter":
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.0(axios@0.27.2)
|
||||
"@hono/swagger-ui":
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.2(hono@4.10.4)
|
||||
@@ -32,7 +29,7 @@ importers:
|
||||
version: 2.0.5(patch_hash=814a3f2d2a39f286e4f86929789e0ada33593d88cf2fb1eb3cf2cc2425c7dfaf)
|
||||
drizzle-orm:
|
||||
specifier: ^0.44.7
|
||||
version: 0.44.7(@cloudflare/workers-types@4.20251014.0)(@libsql/client@0.15.4)(bun-types@1.3.1(@types/react@19.2.2))
|
||||
version: 0.44.7(@libsql/client@0.15.4)(bun-types@1.3.1(@types/react@19.2.2))
|
||||
gql.tada:
|
||||
specifier: ^1.8.10
|
||||
version: 1.8.13(graphql@16.12.0)(typescript@5.9.3)
|
||||
@@ -75,10 +72,7 @@ importers:
|
||||
version: 1.15.0(graphql@16.12.0)(typescript@5.9.3)
|
||||
"@cloudflare/vitest-pool-workers":
|
||||
specifier: ^0.10.7
|
||||
version: 0.10.7(@cloudflare/workers-types@4.20251014.0)(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))
|
||||
"@cloudflare/workers-types":
|
||||
specifier: ^4.20250423.0
|
||||
version: 4.20251014.0
|
||||
version: 0.10.7(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4)
|
||||
"@trivago/prettier-plugin-sort-imports":
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.0(prettier@3.6.2)
|
||||
@@ -97,15 +91,18 @@ importers:
|
||||
"@types/pngjs":
|
||||
specifier: ^6.0.5
|
||||
version: 6.0.5
|
||||
"@vitest/coverage-v8":
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(vitest@3.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))
|
||||
"@vitest/coverage-istanbul":
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4(vitest@3.2.4)
|
||||
"@vitest/runner":
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4
|
||||
"@vitest/snapshot":
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4
|
||||
"@vitest/ui":
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(vitest@3.2.4)
|
||||
cloudflare:
|
||||
specifier: ^5.2.0
|
||||
version: 5.2.0
|
||||
@@ -153,10 +150,10 @@ importers:
|
||||
version: 5.1.4(typescript@5.9.3)(vite@7.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))
|
||||
vitest:
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
||||
version: 3.2.4(@types/node@22.18.13)(@vitest/ui@3.2.4)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
||||
wrangler:
|
||||
specifier: ^4.51.0
|
||||
version: 4.51.0(@cloudflare/workers-types@4.20251014.0)
|
||||
version: 4.51.0
|
||||
zx:
|
||||
specifier: 8.1.5
|
||||
version: 8.1.5
|
||||
@@ -182,13 +179,6 @@ packages:
|
||||
graphql: ^15.5.0 || ^16.0.0 || ^17.0.0
|
||||
typescript: ^5.0.0
|
||||
|
||||
"@ampproject/remapping@2.3.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==,
|
||||
}
|
||||
engines: { node: ">=6.0.0" }
|
||||
|
||||
"@asteasolutions/zod-to-openapi@7.3.4":
|
||||
resolution:
|
||||
{
|
||||
@@ -204,6 +194,20 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/compat-data@7.28.5":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/core@7.28.5":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/generator@7.17.7":
|
||||
resolution:
|
||||
{
|
||||
@@ -218,6 +222,13 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/helper-compilation-targets@7.27.2":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/helper-environment-visitor@7.24.7":
|
||||
resolution:
|
||||
{
|
||||
@@ -232,6 +243,13 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/helper-globals@7.28.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/helper-hoist-variables@7.24.7":
|
||||
resolution:
|
||||
{
|
||||
@@ -239,6 +257,22 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/helper-module-imports@7.27.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/helper-module-transforms@7.28.3":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.0.0
|
||||
|
||||
"@babel/helper-split-export-declaration@7.24.7":
|
||||
resolution:
|
||||
{
|
||||
@@ -260,6 +294,20 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/helper-validator-option@7.27.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/helpers@7.28.4":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/parser@7.28.5":
|
||||
resolution:
|
||||
{
|
||||
@@ -282,6 +330,13 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/traverse@7.28.5":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@babel/types@7.17.0":
|
||||
resolution:
|
||||
{
|
||||
@@ -296,13 +351,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
"@bcoe/v8-coverage@1.0.2":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==,
|
||||
}
|
||||
engines: { node: ">=18" }
|
||||
|
||||
"@cloudflare/kv-asset-handler@0.4.0":
|
||||
resolution:
|
||||
{
|
||||
@@ -441,12 +489,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
"@cloudflare/workers-types@4.20251014.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-tEW98J/kOa0TdylIUOrLKRdwkUw0rvvYVlo+Ce0mqRH3c8kSoxLzUH9gfCvwLe0M89z1RkzFovSKAW2Nwtyn3w==,
|
||||
}
|
||||
|
||||
"@consumet/extensions@https://codeload.github.com/consumet/consumet.ts/tar.gz/3dd0ccb":
|
||||
resolution:
|
||||
{
|
||||
@@ -1496,14 +1538,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">=18.0.0" }
|
||||
|
||||
"@haverstack/axios-fetch-adapter@0.12.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-+9WzqzeIvEC6Qrs6ImSqaX5P+eCrWbhzR+GoB7+p8/yqxmq59CPEo0uFuu0wINU9DQuJMzyER9WYdpYVwZV9rw==,
|
||||
}
|
||||
peerDependencies:
|
||||
axios: ^0.21.1
|
||||
|
||||
"@hono/swagger-ui@0.5.2":
|
||||
resolution:
|
||||
{
|
||||
@@ -1722,6 +1756,12 @@ packages:
|
||||
integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==,
|
||||
}
|
||||
|
||||
"@jridgewell/remapping@2.3.5":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==,
|
||||
}
|
||||
|
||||
"@jridgewell/resolve-uri@3.1.2":
|
||||
resolution:
|
||||
{
|
||||
@@ -1890,6 +1930,12 @@ packages:
|
||||
}
|
||||
engines: { node: ">=14" }
|
||||
|
||||
"@polka/url@1.0.0-next.29":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==,
|
||||
}
|
||||
|
||||
"@poppinss/colors@4.1.5":
|
||||
resolution:
|
||||
{
|
||||
@@ -2223,17 +2269,13 @@ packages:
|
||||
integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==,
|
||||
}
|
||||
|
||||
"@vitest/coverage-v8@3.2.4":
|
||||
"@vitest/coverage-istanbul@3.2.4":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==,
|
||||
integrity: sha512-IDlpuFJiWU9rhcKLkpzj8mFu/lpe64gVgnV15ZOrYx1iFzxxrxCzbExiUEKtwwXRvEiEMUS6iZeYgnMxgbqbxQ==,
|
||||
}
|
||||
peerDependencies:
|
||||
"@vitest/browser": 3.2.4
|
||||
vitest: 3.2.4
|
||||
peerDependenciesMeta:
|
||||
"@vitest/browser":
|
||||
optional: true
|
||||
|
||||
"@vitest/expect@3.2.4":
|
||||
resolution:
|
||||
@@ -2279,6 +2321,14 @@ packages:
|
||||
integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==,
|
||||
}
|
||||
|
||||
"@vitest/ui@3.2.4":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==,
|
||||
}
|
||||
peerDependencies:
|
||||
vitest: 3.2.4
|
||||
|
||||
"@vitest/utils@3.2.4":
|
||||
resolution:
|
||||
{
|
||||
@@ -2419,12 +2469,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">=12" }
|
||||
|
||||
ast-v8-to-istanbul@0.3.8:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==,
|
||||
}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -2450,6 +2494,13 @@ packages:
|
||||
integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==,
|
||||
}
|
||||
|
||||
baseline-browser-mapping@2.9.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ZCQ9GEWl73BVm8bu5Fts8nt7MHdbt5vY9bP6WGnUh+r3l8M7CgfyTlwsgCbMC66BNxPr6Xoce3j66Ms5YUQTNA==,
|
||||
}
|
||||
hasBin: true
|
||||
|
||||
birpc@0.2.14:
|
||||
resolution:
|
||||
{
|
||||
@@ -2487,6 +2538,14 @@ packages:
|
||||
}
|
||||
engines: { node: ">=8" }
|
||||
|
||||
browserslist@4.28.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==,
|
||||
}
|
||||
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
|
||||
hasBin: true
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -2535,6 +2594,12 @@ packages:
|
||||
}
|
||||
engines: { node: ">= 0.4" }
|
||||
|
||||
caniuse-lite@1.0.30001759:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==,
|
||||
}
|
||||
|
||||
chai@5.3.3:
|
||||
resolution:
|
||||
{
|
||||
@@ -2653,6 +2718,12 @@ packages:
|
||||
integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==,
|
||||
}
|
||||
|
||||
convert-source-map@2.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==,
|
||||
}
|
||||
|
||||
cookie@1.0.2:
|
||||
resolution:
|
||||
{
|
||||
@@ -2912,6 +2983,12 @@ packages:
|
||||
integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==,
|
||||
}
|
||||
|
||||
electron-to-chromium@1.5.266:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==,
|
||||
}
|
||||
|
||||
emoji-regex@10.6.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -3037,6 +3114,13 @@ packages:
|
||||
engines: { node: ">=18" }
|
||||
hasBin: true
|
||||
|
||||
escalade@3.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==,
|
||||
}
|
||||
engines: { node: ">=6" }
|
||||
|
||||
estree-walker@3.0.3:
|
||||
resolution:
|
||||
{
|
||||
@@ -3115,6 +3199,12 @@ packages:
|
||||
}
|
||||
engines: { node: ^12.20 || >= 14.13 }
|
||||
|
||||
fflate@0.8.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==,
|
||||
}
|
||||
|
||||
fill-range@7.1.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -3122,6 +3212,12 @@ packages:
|
||||
}
|
||||
engines: { node: ">=8" }
|
||||
|
||||
flatted@3.3.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==,
|
||||
}
|
||||
|
||||
follow-redirects@1.15.11:
|
||||
resolution:
|
||||
{
|
||||
@@ -3203,6 +3299,13 @@ packages:
|
||||
}
|
||||
engines: { node: ">= 0.4" }
|
||||
|
||||
gensync@1.0.0-beta.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==,
|
||||
}
|
||||
engines: { node: ">=6.9.0" }
|
||||
|
||||
get-east-asian-width@1.4.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -3521,6 +3624,13 @@ packages:
|
||||
}
|
||||
engines: { node: ">=8" }
|
||||
|
||||
istanbul-lib-instrument@6.0.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==,
|
||||
}
|
||||
engines: { node: ">=10" }
|
||||
|
||||
istanbul-lib-report@3.0.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -3600,6 +3710,14 @@ packages:
|
||||
engines: { node: ">=6" }
|
||||
hasBin: true
|
||||
|
||||
json5@2.2.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==,
|
||||
}
|
||||
engines: { node: ">=6" }
|
||||
hasBin: true
|
||||
|
||||
jwa@2.0.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -3687,6 +3805,12 @@ packages:
|
||||
integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==,
|
||||
}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==,
|
||||
}
|
||||
|
||||
luxon@3.7.2:
|
||||
resolution:
|
||||
{
|
||||
@@ -3814,6 +3938,13 @@ packages:
|
||||
engines: { node: ">=10" }
|
||||
hasBin: true
|
||||
|
||||
mrmime@2.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==,
|
||||
}
|
||||
engines: { node: ">=10" }
|
||||
|
||||
ms@2.1.3:
|
||||
resolution:
|
||||
{
|
||||
@@ -3855,6 +3986,12 @@ packages:
|
||||
}
|
||||
engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
|
||||
|
||||
node-releases@2.0.27:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==,
|
||||
}
|
||||
|
||||
npm-run-path@5.3.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -4095,6 +4232,13 @@ packages:
|
||||
integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==,
|
||||
}
|
||||
|
||||
semver@6.3.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==,
|
||||
}
|
||||
hasBin: true
|
||||
|
||||
semver@7.7.3:
|
||||
resolution:
|
||||
{
|
||||
@@ -4150,6 +4294,13 @@ packages:
|
||||
integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==,
|
||||
}
|
||||
|
||||
sirv@3.0.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==,
|
||||
}
|
||||
engines: { node: ">=18" }
|
||||
|
||||
slice-ansi@5.0.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -4348,6 +4499,13 @@ packages:
|
||||
}
|
||||
engines: { node: ">=8.0" }
|
||||
|
||||
totalist@3.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==,
|
||||
}
|
||||
engines: { node: ">=6" }
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution:
|
||||
{
|
||||
@@ -4427,6 +4585,15 @@ packages:
|
||||
integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==,
|
||||
}
|
||||
|
||||
update-browserslist-db@1.2.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==,
|
||||
}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
browserslist: ">= 4.21.0"
|
||||
|
||||
urlpattern-polyfill@10.1.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -4696,6 +4863,12 @@ packages:
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
yallist@3.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==,
|
||||
}
|
||||
|
||||
yaml@2.8.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -4747,11 +4920,6 @@ snapshots:
|
||||
graphql: 16.12.0
|
||||
typescript: 5.9.3
|
||||
|
||||
"@ampproject/remapping@2.3.0":
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping": 0.3.13
|
||||
"@jridgewell/trace-mapping": 0.3.31
|
||||
|
||||
"@asteasolutions/zod-to-openapi@7.3.4(zod@3.25.76)":
|
||||
dependencies:
|
||||
openapi3-ts: 4.5.0
|
||||
@@ -4763,6 +4931,28 @@ snapshots:
|
||||
js-tokens: 4.0.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
"@babel/compat-data@7.28.5": {}
|
||||
|
||||
"@babel/core@7.28.5":
|
||||
dependencies:
|
||||
"@babel/code-frame": 7.27.1
|
||||
"@babel/generator": 7.28.5
|
||||
"@babel/helper-compilation-targets": 7.27.2
|
||||
"@babel/helper-module-transforms": 7.28.3(@babel/core@7.28.5)
|
||||
"@babel/helpers": 7.28.4
|
||||
"@babel/parser": 7.28.5
|
||||
"@babel/template": 7.27.2
|
||||
"@babel/traverse": 7.28.5
|
||||
"@babel/types": 7.28.5
|
||||
"@jridgewell/remapping": 2.3.5
|
||||
convert-source-map: 2.0.0
|
||||
debug: 4.4.3
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.3
|
||||
semver: 6.3.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
"@babel/generator@7.17.7":
|
||||
dependencies:
|
||||
"@babel/types": 7.17.0
|
||||
@@ -4777,6 +4967,14 @@ snapshots:
|
||||
"@jridgewell/trace-mapping": 0.3.31
|
||||
jsesc: 3.1.0
|
||||
|
||||
"@babel/helper-compilation-targets@7.27.2":
|
||||
dependencies:
|
||||
"@babel/compat-data": 7.28.5
|
||||
"@babel/helper-validator-option": 7.27.1
|
||||
browserslist: 4.28.1
|
||||
lru-cache: 5.1.1
|
||||
semver: 6.3.1
|
||||
|
||||
"@babel/helper-environment-visitor@7.24.7":
|
||||
dependencies:
|
||||
"@babel/types": 7.28.5
|
||||
@@ -4786,10 +4984,28 @@ snapshots:
|
||||
"@babel/template": 7.27.2
|
||||
"@babel/types": 7.28.5
|
||||
|
||||
"@babel/helper-globals@7.28.0": {}
|
||||
|
||||
"@babel/helper-hoist-variables@7.24.7":
|
||||
dependencies:
|
||||
"@babel/types": 7.28.5
|
||||
|
||||
"@babel/helper-module-imports@7.27.1":
|
||||
dependencies:
|
||||
"@babel/traverse": 7.28.5
|
||||
"@babel/types": 7.28.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
"@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)":
|
||||
dependencies:
|
||||
"@babel/core": 7.28.5
|
||||
"@babel/helper-module-imports": 7.27.1
|
||||
"@babel/helper-validator-identifier": 7.28.5
|
||||
"@babel/traverse": 7.28.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
"@babel/helper-split-export-declaration@7.24.7":
|
||||
dependencies:
|
||||
"@babel/types": 7.28.5
|
||||
@@ -4798,6 +5014,13 @@ snapshots:
|
||||
|
||||
"@babel/helper-validator-identifier@7.28.5": {}
|
||||
|
||||
"@babel/helper-validator-option@7.27.1": {}
|
||||
|
||||
"@babel/helpers@7.28.4":
|
||||
dependencies:
|
||||
"@babel/template": 7.27.2
|
||||
"@babel/types": 7.28.5
|
||||
|
||||
"@babel/parser@7.28.5":
|
||||
dependencies:
|
||||
"@babel/types": 7.28.5
|
||||
@@ -4823,6 +5046,18 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
"@babel/traverse@7.28.5":
|
||||
dependencies:
|
||||
"@babel/code-frame": 7.27.1
|
||||
"@babel/generator": 7.28.5
|
||||
"@babel/helper-globals": 7.28.0
|
||||
"@babel/parser": 7.28.5
|
||||
"@babel/template": 7.27.2
|
||||
"@babel/types": 7.28.5
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
"@babel/types@7.17.0":
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier": 7.28.5
|
||||
@@ -4833,8 +5068,6 @@ snapshots:
|
||||
"@babel/helper-string-parser": 7.27.1
|
||||
"@babel/helper-validator-identifier": 7.28.5
|
||||
|
||||
"@bcoe/v8-coverage@1.0.2": {}
|
||||
|
||||
"@cloudflare/kv-asset-handler@0.4.0":
|
||||
dependencies:
|
||||
mime: 3.0.0
|
||||
@@ -4855,7 +5088,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
workerd: 1.20251125.0
|
||||
|
||||
"@cloudflare/vitest-pool-workers@0.10.7(@cloudflare/workers-types@4.20251014.0)(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))":
|
||||
"@cloudflare/vitest-pool-workers@0.10.7(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4)":
|
||||
dependencies:
|
||||
"@vitest/runner": 3.2.4
|
||||
"@vitest/snapshot": 3.2.4
|
||||
@@ -4864,8 +5097,8 @@ snapshots:
|
||||
devalue: 5.5.0
|
||||
miniflare: 4.20251109.1
|
||||
semver: 7.7.3
|
||||
vitest: 3.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
||||
wrangler: 4.48.0(@cloudflare/workers-types@4.20251014.0)
|
||||
vitest: 3.2.4(@types/node@22.18.13)(@vitest/ui@3.2.4)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
||||
wrangler: 4.48.0
|
||||
zod: 3.25.76
|
||||
transitivePeerDependencies:
|
||||
- "@cloudflare/workers-types"
|
||||
@@ -4902,8 +5135,6 @@ snapshots:
|
||||
"@cloudflare/workerd-windows-64@1.20251125.0":
|
||||
optional: true
|
||||
|
||||
"@cloudflare/workers-types@4.20251014.0": {}
|
||||
|
||||
"@consumet/extensions@https://codeload.github.com/consumet/consumet.ts/tar.gz/3dd0ccb":
|
||||
dependencies:
|
||||
ascii-url-encoder: 1.2.0
|
||||
@@ -5316,10 +5547,6 @@ snapshots:
|
||||
"@repeaterjs/repeater": 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
"@haverstack/axios-fetch-adapter@0.12.0(axios@0.27.2)":
|
||||
dependencies:
|
||||
axios: 0.27.2
|
||||
|
||||
"@hono/swagger-ui@0.5.2(hono@4.10.4)":
|
||||
dependencies:
|
||||
hono: 4.10.4
|
||||
@@ -5433,6 +5660,11 @@ snapshots:
|
||||
"@jridgewell/sourcemap-codec": 1.5.5
|
||||
"@jridgewell/trace-mapping": 0.3.31
|
||||
|
||||
"@jridgewell/remapping@2.3.5":
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping": 0.3.13
|
||||
"@jridgewell/trace-mapping": 0.3.31
|
||||
|
||||
"@jridgewell/resolve-uri@3.1.2": {}
|
||||
|
||||
"@jridgewell/source-map@0.3.11":
|
||||
@@ -5538,6 +5770,8 @@ snapshots:
|
||||
"@pkgjs/parseargs@0.11.0":
|
||||
optional: true
|
||||
|
||||
"@polka/url@1.0.0-next.29": {}
|
||||
|
||||
"@poppinss/colors@4.1.5":
|
||||
dependencies:
|
||||
kleur: 4.1.5
|
||||
@@ -5706,22 +5940,19 @@ snapshots:
|
||||
"@types/node": 22.18.13
|
||||
optional: true
|
||||
|
||||
"@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))":
|
||||
"@vitest/coverage-istanbul@3.2.4(vitest@3.2.4)":
|
||||
dependencies:
|
||||
"@ampproject/remapping": 2.3.0
|
||||
"@bcoe/v8-coverage": 1.0.2
|
||||
ast-v8-to-istanbul: 0.3.8
|
||||
"@istanbuljs/schema": 0.1.3
|
||||
debug: 4.4.3
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
istanbul-lib-instrument: 6.0.3
|
||||
istanbul-lib-report: 3.0.1
|
||||
istanbul-lib-source-maps: 5.0.6
|
||||
istanbul-reports: 3.2.0
|
||||
magic-string: 0.30.21
|
||||
magicast: 0.3.5
|
||||
std-env: 3.10.0
|
||||
test-exclude: 7.0.1
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
||||
vitest: 3.2.4(@types/node@22.18.13)(@vitest/ui@3.2.4)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -5761,6 +5992,17 @@ snapshots:
|
||||
dependencies:
|
||||
tinyspy: 4.0.4
|
||||
|
||||
"@vitest/ui@3.2.4(vitest@3.2.4)":
|
||||
dependencies:
|
||||
"@vitest/utils": 3.2.4
|
||||
fflate: 0.8.2
|
||||
flatted: 3.3.3
|
||||
pathe: 2.0.3
|
||||
sirv: 3.0.2
|
||||
tinyglobby: 0.2.15
|
||||
tinyrainbow: 2.0.0
|
||||
vitest: 3.2.4(@types/node@22.18.13)(@vitest/ui@3.2.4)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
|
||||
|
||||
"@vitest/utils@3.2.4":
|
||||
dependencies:
|
||||
"@vitest/pretty-format": 3.2.4
|
||||
@@ -5835,12 +6077,6 @@ snapshots:
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
ast-v8-to-istanbul@0.3.8:
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping": 0.3.31
|
||||
estree-walker: 3.0.3
|
||||
js-tokens: 9.0.1
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
@@ -5856,6 +6092,8 @@ snapshots:
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
baseline-browser-mapping@2.9.4: {}
|
||||
|
||||
birpc@0.2.14: {}
|
||||
|
||||
blake3-wasm@2.1.5: {}
|
||||
@@ -5873,6 +6111,14 @@ snapshots:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
browserslist@4.28.1:
|
||||
dependencies:
|
||||
baseline-browser-mapping: 2.9.4
|
||||
caniuse-lite: 1.0.30001759
|
||||
electron-to-chromium: 1.5.266
|
||||
node-releases: 2.0.27
|
||||
update-browserslist-db: 1.2.2(browserslist@4.28.1)
|
||||
|
||||
buffer-equal-constant-time@1.0.1: {}
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
@@ -5902,6 +6148,8 @@ snapshots:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
caniuse-lite@1.0.30001759: {}
|
||||
|
||||
chai@5.3.3:
|
||||
dependencies:
|
||||
assertion-error: 2.0.1
|
||||
@@ -5989,6 +6237,8 @@ snapshots:
|
||||
commander@2.20.3:
|
||||
optional: true
|
||||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
cookie@1.0.2: {}
|
||||
|
||||
cross-inspect@1.0.1:
|
||||
@@ -6069,9 +6319,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
drizzle-orm@0.44.7(@cloudflare/workers-types@4.20251014.0)(@libsql/client@0.15.4)(bun-types@1.3.1(@types/react@19.2.2)):
|
||||
drizzle-orm@0.44.7(@libsql/client@0.15.4)(bun-types@1.3.1(@types/react@19.2.2)):
|
||||
optionalDependencies:
|
||||
"@cloudflare/workers-types": 4.20251014.0
|
||||
"@libsql/client": 0.15.4
|
||||
bun-types: 1.3.1(@types/react@19.2.2)
|
||||
|
||||
@@ -6087,6 +6336,8 @@ snapshots:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
electron-to-chromium@1.5.266: {}
|
||||
|
||||
emoji-regex@10.6.0: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
@@ -6241,6 +6492,8 @@ snapshots:
|
||||
"@esbuild/win32-ia32": 0.27.0
|
||||
"@esbuild/win32-x64": 0.27.0
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
"@types/estree": 1.0.8
|
||||
@@ -6289,10 +6542,14 @@ snapshots:
|
||||
web-streams-polyfill: 3.3.3
|
||||
optional: true
|
||||
|
||||
fflate@0.8.2: {}
|
||||
|
||||
fill-range@7.1.1:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
|
||||
flatted@3.3.3: {}
|
||||
|
||||
follow-redirects@1.15.11: {}
|
||||
|
||||
for-each@0.3.5:
|
||||
@@ -6342,6 +6599,8 @@ snapshots:
|
||||
|
||||
generator-function@2.0.1: {}
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
get-east-asian-width@1.4.0: {}
|
||||
|
||||
get-intrinsic@1.3.0:
|
||||
@@ -6532,6 +6791,16 @@ snapshots:
|
||||
|
||||
istanbul-lib-coverage@3.2.2: {}
|
||||
|
||||
istanbul-lib-instrument@6.0.3:
|
||||
dependencies:
|
||||
"@babel/core": 7.28.5
|
||||
"@babel/parser": 7.28.5
|
||||
"@istanbuljs/schema": 0.1.3
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
semver: 7.7.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
istanbul-lib-report@3.0.1:
|
||||
dependencies:
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
@@ -6574,6 +6843,8 @@ snapshots:
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
json5@2.2.3: {}
|
||||
|
||||
jwa@2.0.1:
|
||||
dependencies:
|
||||
buffer-equal-constant-time: 1.0.1
|
||||
@@ -6647,6 +6918,10 @@ snapshots:
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
luxon@3.7.2: {}
|
||||
|
||||
magic-string@0.30.21:
|
||||
@@ -6730,6 +7005,8 @@ snapshots:
|
||||
|
||||
mkdirp@3.0.1: {}
|
||||
|
||||
mrmime@2.0.1: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
@@ -6747,6 +7024,8 @@ snapshots:
|
||||
formdata-polyfill: 4.0.10
|
||||
optional: true
|
||||
|
||||
node-releases@2.0.27: {}
|
||||
|
||||
npm-run-path@5.3.0:
|
||||
dependencies:
|
||||
path-key: 4.0.0
|
||||
@@ -6882,6 +7161,8 @@ snapshots:
|
||||
|
||||
safer-buffer@2.1.2: {}
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.7.3: {}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
@@ -6933,6 +7214,12 @@ snapshots:
|
||||
dependencies:
|
||||
is-arrayish: 0.3.4
|
||||
|
||||
sirv@3.0.2:
|
||||
dependencies:
|
||||
"@polka/url": 1.0.0-next.29
|
||||
mrmime: 2.0.1
|
||||
totalist: 3.0.1
|
||||
|
||||
slice-ansi@5.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.3
|
||||
@@ -7035,6 +7322,8 @@ snapshots:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
ts-morph@22.0.0:
|
||||
@@ -7069,6 +7358,12 @@ snapshots:
|
||||
dependencies:
|
||||
pathe: 2.0.3
|
||||
|
||||
update-browserslist-db@1.2.2(browserslist@4.28.1):
|
||||
dependencies:
|
||||
browserslist: 4.28.1
|
||||
escalade: 3.2.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
urlpattern-polyfill@10.1.0: {}
|
||||
|
||||
util@0.12.5:
|
||||
@@ -7128,7 +7423,7 @@ snapshots:
|
||||
tsx: 4.20.6
|
||||
yaml: 2.8.1
|
||||
|
||||
vitest@3.2.4(@types/node@22.18.13)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1):
|
||||
vitest@3.2.4(@types/node@22.18.13)(@vitest/ui@3.2.4)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1):
|
||||
dependencies:
|
||||
"@types/chai": 5.2.3
|
||||
"@vitest/expect": 3.2.4
|
||||
@@ -7155,6 +7450,7 @@ snapshots:
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
"@types/node": 22.18.13
|
||||
"@vitest/ui": 3.2.4(vitest@3.2.4)
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- less
|
||||
@@ -7222,7 +7518,7 @@ snapshots:
|
||||
"@cloudflare/workerd-linux-arm64": 1.20251125.0
|
||||
"@cloudflare/workerd-windows-64": 1.20251125.0
|
||||
|
||||
wrangler@4.48.0(@cloudflare/workers-types@4.20251014.0):
|
||||
wrangler@4.48.0:
|
||||
dependencies:
|
||||
"@cloudflare/kv-asset-handler": 0.4.0
|
||||
"@cloudflare/unenv-preset": 2.7.10(unenv@2.0.0-rc.24)(workerd@1.20251109.0)
|
||||
@@ -7233,13 +7529,12 @@ snapshots:
|
||||
unenv: 2.0.0-rc.24
|
||||
workerd: 1.20251109.0
|
||||
optionalDependencies:
|
||||
"@cloudflare/workers-types": 4.20251014.0
|
||||
fsevents: 2.3.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
wrangler@4.51.0(@cloudflare/workers-types@4.20251014.0):
|
||||
wrangler@4.51.0:
|
||||
dependencies:
|
||||
"@cloudflare/kv-asset-handler": 0.4.1
|
||||
"@cloudflare/unenv-preset": 2.7.11(unenv@2.0.0-rc.24)(workerd@1.20251125.0)
|
||||
@@ -7250,7 +7545,6 @@ snapshots:
|
||||
unenv: 2.0.0-rc.24
|
||||
workerd: 1.20251125.0
|
||||
optionalDependencies:
|
||||
"@cloudflare/workers-types": 4.20251014.0
|
||||
fsevents: 2.3.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
@@ -7279,6 +7573,8 @@ snapshots:
|
||||
ws@8.18.3:
|
||||
optional: true
|
||||
|
||||
yallist@3.1.1: {}
|
||||
|
||||
yaml@2.8.1: {}
|
||||
|
||||
youch-core@0.3.3:
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { ANIME, META } from "@consumet/extensions";
|
||||
import fetchAdapter from "@haverstack/axios-fetch-adapter";
|
||||
|
||||
const gogoAnime = new ANIME.Gogoanime(undefined, undefined, fetchAdapter);
|
||||
export const aniList = new META.Anilist(gogoAnime, undefined, fetchAdapter);
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { HonoRequest } from "hono";
|
||||
|
||||
export function getCurrentDomain(req: HonoRequest): string | undefined;
|
||||
export function getCurrentDomain(
|
||||
req: HonoRequest,
|
||||
avoidLocalhost: false,
|
||||
): string;
|
||||
export function getCurrentDomain(
|
||||
req: HonoRequest,
|
||||
avoidLocalhost: true,
|
||||
): string | undefined;
|
||||
export function getCurrentDomain(req: HonoRequest, avoidLocalhost = true) {
|
||||
let domain = req.url.replace(req.path, "");
|
||||
if (domain.includes("?")) {
|
||||
domain = domain.split("?")[0];
|
||||
}
|
||||
|
||||
if (avoidLocalhost) {
|
||||
if (
|
||||
domain.includes("localhost") ||
|
||||
domain.includes("127.0.0.1") ||
|
||||
domain.includes("192.168.1")
|
||||
) {
|
||||
console.log("Domain is localhost, returning undefined");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
export async function logStep<T = void>(
|
||||
inProgressText: string,
|
||||
step: () => Promise<T> | T,
|
||||
): Promise<T>;
|
||||
export async function logStep<T = void>(
|
||||
inProgressText: string,
|
||||
step: () => Promise<T> | T,
|
||||
doneText: string,
|
||||
): Promise<T>;
|
||||
|
||||
export async function logStep<T = void>(
|
||||
inProgressText: string,
|
||||
step: () => Promise<T> | T,
|
||||
doneText: string = `Completed step "${inProgressText}"`,
|
||||
) {
|
||||
console.time(doneText);
|
||||
console.log(`${inProgressText}...`);
|
||||
|
||||
return Promise.resolve(step()).then((value) => {
|
||||
console.timeEnd(doneText);
|
||||
return value;
|
||||
});
|
||||
}
|
||||
@@ -30,6 +30,6 @@ describe("readEnvVariable", () => {
|
||||
});
|
||||
|
||||
it("env not defined, returns default value", () => {
|
||||
expect(readEnvVariable<boolean>("ENABLE_ANIFY", undefined)).toBe(true);
|
||||
expect(readEnvVariable<boolean>("ENABLE_ANIFY", {})).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
// import { createClient } from "@libsql/client";
|
||||
import { env as cloudflareEnv } from "cloudflare:workers";
|
||||
import { drizzle } from "drizzle-orm/d1";
|
||||
|
||||
type Db = ReturnType<typeof drizzle>;
|
||||
// let db: Db | null = null;
|
||||
|
||||
export function getDb(env: Cloudflare.Env = cloudflareEnv): Db {
|
||||
// if (db) {
|
||||
// return db;
|
||||
// }
|
||||
|
||||
const db = drizzle(env.DB, { logger: true });
|
||||
return db;
|
||||
}
|
||||
|
||||
109
src/resolvers/mutations/markEpisodeAsWatched.spec.ts
Normal file
109
src/resolvers/mutations/markEpisodeAsWatched.spec.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { GraphQLError } from "graphql";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { markEpisodeAsWatched } from "~/services/episodes/markEpisodeAsWatched/anilist";
|
||||
|
||||
import { markEpisodeAsWatchedMutation } from "./markEpisodeAsWatched";
|
||||
|
||||
vi.mock("~/services/episodes/markEpisodeAsWatched/anilist", () => ({
|
||||
markEpisodeAsWatched: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("~/services/watch-status", () => ({
|
||||
updateWatchStatus: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("markEpisodeAsWatched mutation", () => {
|
||||
it("should throw GraphQLError if aniListToken is missing", async () => {
|
||||
await expect(
|
||||
markEpisodeAsWatchedMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, episodeNumber: 1, isComplete: false } },
|
||||
{ aniListToken: undefined } as any,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError(
|
||||
"AniList token is required. Please provide X-AniList-Token header.",
|
||||
{
|
||||
extensions: { code: "UNAUTHORIZED" },
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("should call markEpisodeAsWatched service", async () => {
|
||||
vi.mocked(markEpisodeAsWatched).mockResolvedValue({} as any);
|
||||
|
||||
await markEpisodeAsWatchedMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, episodeNumber: 1, isComplete: false } },
|
||||
{ aniListToken: "token" } as any,
|
||||
);
|
||||
|
||||
expect(markEpisodeAsWatched).toHaveBeenCalledWith("token", 1, 1, false);
|
||||
});
|
||||
|
||||
it("should update watch status locally if isComplete is true and deviceId is present", async () => {
|
||||
vi.mocked(markEpisodeAsWatched).mockResolvedValue({} as any);
|
||||
const { updateWatchStatus } = await import("~/services/watch-status");
|
||||
|
||||
await markEpisodeAsWatchedMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, episodeNumber: 1, isComplete: true } },
|
||||
{ aniListToken: "token", deviceId: "device-id" } as any,
|
||||
);
|
||||
|
||||
expect(updateWatchStatus).toHaveBeenCalledWith("device-id", 1, "COMPLETED");
|
||||
});
|
||||
|
||||
it("should not update watch status locally if deviceId is missing", async () => {
|
||||
vi.mocked(markEpisodeAsWatched).mockResolvedValue({} as any);
|
||||
const { updateWatchStatus } = await import("~/services/watch-status");
|
||||
vi.mocked(updateWatchStatus).mockClear();
|
||||
|
||||
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
|
||||
await markEpisodeAsWatchedMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, episodeNumber: 1, isComplete: true } },
|
||||
{ aniListToken: "token" } as any,
|
||||
);
|
||||
|
||||
expect(updateWatchStatus).not.toHaveBeenCalled();
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
"Device ID not found in context, skipping watch status update",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw GraphQLError if service return null", async () => {
|
||||
vi.mocked(markEpisodeAsWatched).mockResolvedValue(null as any);
|
||||
|
||||
await expect(
|
||||
markEpisodeAsWatchedMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, episodeNumber: 1, isComplete: false } },
|
||||
{ aniListToken: "token" } as any,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError("Failed to mark episode as watched", {
|
||||
extensions: { code: "INTERNAL_SERVER_ERROR" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should catch errors and throw GraphQLError", async () => {
|
||||
vi.mocked(markEpisodeAsWatched).mockRejectedValue(new Error("Foo"));
|
||||
|
||||
await expect(
|
||||
markEpisodeAsWatchedMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, episodeNumber: 1, isComplete: false } },
|
||||
{ aniListToken: "token" } as any,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError("Failed to mark episode as watched", {
|
||||
extensions: { code: "INTERNAL_SERVER_ERROR" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
55
src/resolvers/mutations/updateWatchStatus.spec.ts
Normal file
55
src/resolvers/mutations/updateWatchStatus.spec.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { GraphQLError } from "graphql";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { updateWatchStatus } from "~/services/watch-status";
|
||||
|
||||
import { updateWatchStatusMutation } from "./updateWatchStatus";
|
||||
|
||||
vi.mock("~/services/watch-status", () => ({
|
||||
updateWatchStatus: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("updateWatchStatus mutation", () => {
|
||||
it("should throw GraphQLError if deviceId is missing", async () => {
|
||||
await expect(
|
||||
updateWatchStatusMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, watchStatus: "CURRENT" } },
|
||||
{ deviceId: undefined } as any,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError(
|
||||
"Device ID is required. Please provide X-Device-ID header.",
|
||||
{
|
||||
extensions: { code: "BAD_REQUEST" },
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("should call updateWatchStatus service with correct parameters", async () => {
|
||||
await updateWatchStatusMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, watchStatus: "CURRENT" } },
|
||||
{ deviceId: "device-id" } as any,
|
||||
);
|
||||
|
||||
expect(updateWatchStatus).toHaveBeenCalledWith("device-id", 1, "CURRENT");
|
||||
});
|
||||
|
||||
it("should catch service errors and throw GraphQLError", async () => {
|
||||
vi.mocked(updateWatchStatus).mockRejectedValue(new Error("Service error"));
|
||||
|
||||
await expect(
|
||||
updateWatchStatusMutation(
|
||||
null,
|
||||
{ input: { titleId: 1, watchStatus: "CURRENT" } },
|
||||
{ deviceId: "device-id" } as any,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError("Failed to update watch status", {
|
||||
extensions: { code: "INTERNAL_SERVER_ERROR" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
91
src/resolvers/queries/home.spec.ts
Normal file
91
src/resolvers/queries/home.spec.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
import { GraphQLError } from "graphql";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { GraphQLContext } from "~/context";
|
||||
|
||||
import { home } from "./home";
|
||||
|
||||
enum HomeCategory {
|
||||
WATCHING,
|
||||
PLANNING,
|
||||
}
|
||||
|
||||
describe("home resolver", () => {
|
||||
const mockContext = {
|
||||
user: { name: "testuser" },
|
||||
aniListToken: "test-token",
|
||||
} as GraphQLContext;
|
||||
|
||||
it("should fetch WATCHING titles using CURRENT status filter", async () => {
|
||||
const mockResponse = { some: "data" };
|
||||
const mockStub = {
|
||||
getTitles: vi.fn().mockResolvedValue(mockResponse),
|
||||
};
|
||||
|
||||
// @ts-expect-error - Partial mock
|
||||
env.ANILIST_DO = {
|
||||
getByName: vi.fn().mockResolvedValue(mockStub),
|
||||
};
|
||||
|
||||
const result = await home(
|
||||
null,
|
||||
{ category: HomeCategory.WATCHING },
|
||||
mockContext,
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
expect(env.ANILIST_DO.getByName).toHaveBeenCalledWith("GLOBAL");
|
||||
expect(mockStub.getTitles).toHaveBeenCalledWith(
|
||||
"testuser",
|
||||
1,
|
||||
["CURRENT"],
|
||||
"test-token",
|
||||
);
|
||||
});
|
||||
|
||||
it("should fetch PLANNING titles using PLANNING, PAUSED, REPEATING status filters", async () => {
|
||||
const mockResponse = { some: "data" };
|
||||
const mockStub = {
|
||||
getTitles: vi.fn().mockResolvedValue(mockResponse),
|
||||
};
|
||||
|
||||
// @ts-expect-error - Partial mock
|
||||
env.ANILIST_DO = {
|
||||
getByName: vi.fn().mockResolvedValue(mockStub),
|
||||
};
|
||||
|
||||
const result = await home(
|
||||
null,
|
||||
{ category: HomeCategory.PLANNING, page: 2 },
|
||||
mockContext,
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
expect(mockStub.getTitles).toHaveBeenCalledWith(
|
||||
"testuser",
|
||||
2,
|
||||
["PLANNING", "PAUSED", "REPEATING"],
|
||||
"test-token",
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw GraphQLError if Durable Object response is null", async () => {
|
||||
const mockStub = {
|
||||
getTitles: vi.fn().mockResolvedValue(null),
|
||||
};
|
||||
|
||||
// @ts-expect-error - Partial mock
|
||||
env.ANILIST_DO = {
|
||||
getByName: vi.fn().mockResolvedValue(mockStub),
|
||||
};
|
||||
|
||||
await expect(
|
||||
home(null, { category: HomeCategory.WATCHING }, mockContext),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError("Failed to fetch 0 titles", {
|
||||
extensions: { code: "INTERNAL_SERVER_ERROR" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
51
src/resolvers/queries/popularBrowse.spec.ts
Normal file
51
src/resolvers/queries/popularBrowse.spec.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { GraphQLError } from "graphql";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { fetchPopularTitlesFromAnilist } from "~/services/popular/browse/anilist";
|
||||
|
||||
import { popularBrowse } from "./popularBrowse";
|
||||
|
||||
vi.mock("~/services/popular/browse/anilist", () => ({
|
||||
fetchPopularTitlesFromAnilist: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("popularBrowse resolver", () => {
|
||||
it("should fetch titles with default limit", async () => {
|
||||
const mockResponse = {
|
||||
trending: ["trending"],
|
||||
popular: ["popular"],
|
||||
upcoming: ["upcoming"],
|
||||
};
|
||||
vi.mocked(fetchPopularTitlesFromAnilist).mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await popularBrowse(null, {}, {} as any);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
expect(fetchPopularTitlesFromAnilist).toHaveBeenCalledWith(10);
|
||||
});
|
||||
|
||||
it("should fetch titles with provided limit", async () => {
|
||||
const mockResponse = {};
|
||||
vi.mocked(fetchPopularTitlesFromAnilist).mockResolvedValue(mockResponse);
|
||||
|
||||
await popularBrowse(null, { limit: 20 }, {} as any);
|
||||
|
||||
expect(fetchPopularTitlesFromAnilist).toHaveBeenCalledWith(20);
|
||||
});
|
||||
|
||||
it("should throw GraphQLError if service returns null", async () => {
|
||||
vi.mocked(fetchPopularTitlesFromAnilist).mockResolvedValue(undefined);
|
||||
|
||||
await expect(popularBrowse(null, {}, {} as any)).rejects.toThrow(
|
||||
new GraphQLError("Failed to fetch popular titles", {
|
||||
extensions: { code: "INTERNAL_SERVER_ERROR" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should map response correctly to trending, popular, and upcoming", async () => {
|
||||
vi.mocked(fetchPopularTitlesFromAnilist).mockResolvedValue({} as any);
|
||||
const result = await popularBrowse(null, {}, {} as any);
|
||||
expect(result).toEqual({ trending: [], popular: [], upcoming: [] });
|
||||
});
|
||||
});
|
||||
59
src/resolvers/queries/popularByCategory.spec.ts
Normal file
59
src/resolvers/queries/popularByCategory.spec.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { GraphQLError } from "graphql";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { fetchPopularTitlesFromAnilist } from "~/services/popular/category/anilist";
|
||||
|
||||
import { popularByCategory } from "./popularByCategory";
|
||||
|
||||
vi.mock("~/services/popular/category/anilist", () => ({
|
||||
fetchPopularTitlesFromAnilist: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("popularByCategory resolver", () => {
|
||||
it("should fetch titles for a specific category with page and limit", async () => {
|
||||
const mockResponse = { results: ["title"], hasNextPage: true };
|
||||
vi.mocked(fetchPopularTitlesFromAnilist).mockResolvedValue(
|
||||
mockResponse as any,
|
||||
);
|
||||
|
||||
const result = await popularByCategory(
|
||||
null,
|
||||
{ category: "trending", page: 2, limit: 20 },
|
||||
{} as any,
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
expect(fetchPopularTitlesFromAnilist).toHaveBeenCalledWith(
|
||||
"trending",
|
||||
2,
|
||||
20,
|
||||
);
|
||||
});
|
||||
|
||||
it("should use default page and limit", async () => {
|
||||
const mockResponse = { results: [], hasNextPage: false };
|
||||
vi.mocked(fetchPopularTitlesFromAnilist).mockResolvedValue(
|
||||
mockResponse as any,
|
||||
);
|
||||
|
||||
await popularByCategory(null, { category: "popular" }, {} as any);
|
||||
|
||||
expect(fetchPopularTitlesFromAnilist).toHaveBeenCalledWith(
|
||||
"popular",
|
||||
1,
|
||||
10,
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw GraphQLError if service returns null", async () => {
|
||||
vi.mocked(fetchPopularTitlesFromAnilist).mockResolvedValue(undefined);
|
||||
|
||||
await expect(
|
||||
popularByCategory(null, { category: "upcoming" }, {} as any),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError("Failed to fetch upcoming titles", {
|
||||
extensions: { code: "INTERNAL_SERVER_ERROR" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
44
src/resolvers/queries/user.spec.ts
Normal file
44
src/resolvers/queries/user.spec.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { GraphQLError } from "graphql";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { getUser } from "~/services/auth/anilist/getUser";
|
||||
|
||||
import { user } from "./user";
|
||||
|
||||
vi.mock("~/services/auth/anilist/getUser", () => ({
|
||||
getUser: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("user resolver", () => {
|
||||
it("should throw GraphQLError (UNAUTHORIZED) if aniListToken is missing", async () => {
|
||||
await expect(
|
||||
user(null, {}, { aniListToken: undefined } as any),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError("Unauthorized", {
|
||||
extensions: { code: "UNAUTHORIZED" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should fetch user if token is present", async () => {
|
||||
const mockUser = { id: 1, name: "test" };
|
||||
vi.mocked(getUser).mockResolvedValue(mockUser as any);
|
||||
|
||||
const result = await user(null, {}, { aniListToken: "token" } as any);
|
||||
|
||||
expect(result).toEqual(mockUser);
|
||||
expect(getUser).toHaveBeenCalledWith("token");
|
||||
});
|
||||
|
||||
it("should throw GraphQLError if user service returns null", async () => {
|
||||
vi.mocked(getUser).mockResolvedValue(null);
|
||||
|
||||
await expect(
|
||||
user(null, {}, { aniListToken: "token" } as any),
|
||||
).rejects.toThrow(
|
||||
new GraphQLError("Failed to fetch user", {
|
||||
extensions: { code: "INTERNAL_SERVER_ERROR" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
import { $ } from "bun";
|
||||
import { Project } from "ts-morph";
|
||||
|
||||
import { logStep } from "~/libs/logStep";
|
||||
|
||||
await logStep(
|
||||
'Re-generating "env.d.ts"',
|
||||
() => $`bunx wrangler types src/types/env.d.ts`.quiet(),
|
||||
"Generated env.d.ts",
|
||||
);
|
||||
|
||||
const secretNames = await logStep(
|
||||
"Fetching secrets from Cloudflare",
|
||||
async (): Promise<string[]> => {
|
||||
const { stdout } = await $`bunx wrangler secret list`.quiet();
|
||||
return JSON.parse(stdout.toString()).map(
|
||||
(secret: { name: string; type: "secret_text" }) => secret.name,
|
||||
);
|
||||
},
|
||||
"Fetched secrets",
|
||||
);
|
||||
|
||||
const project = new Project({});
|
||||
|
||||
const envSourceFile = project.addSourceFileAtPath("src/types/env.d.ts");
|
||||
envSourceFile.insertImportDeclaration(2, {
|
||||
isTypeOnly: true,
|
||||
moduleSpecifier: "hono",
|
||||
namedImports: ["Env as HonoEnv"],
|
||||
});
|
||||
envSourceFile
|
||||
.getInterfaceOrThrow("Env")
|
||||
.addExtends(["HonoEnv", "Record<string, unknown>"]);
|
||||
envSourceFile.getInterfaceOrThrow("Env").addProperties(
|
||||
secretNames.map((name) => ({
|
||||
name,
|
||||
type: `string`,
|
||||
})),
|
||||
);
|
||||
|
||||
await project.save();
|
||||
|
||||
await logStep(
|
||||
"Formatting env.d.ts",
|
||||
() => $`bunx prettier --write src/types/env.d.ts`.quiet(),
|
||||
"Formatted env.d.ts",
|
||||
);
|
||||
@@ -1,42 +0,0 @@
|
||||
import { $, sleep, spawn } from "bun";
|
||||
import { readFile } from "fs/promises";
|
||||
|
||||
import { logStep } from "~/libs/logStep";
|
||||
|
||||
await $`cp src/types/env.d.ts /tmp/env.d.ts`.quiet();
|
||||
|
||||
await logStep(
|
||||
'Generating "env.d.ts"',
|
||||
() => import("./generateEnv"),
|
||||
"Generated env.d.ts",
|
||||
);
|
||||
|
||||
await logStep("Comparing env.d.ts", async () => {
|
||||
function filterComments(content: Buffer) {
|
||||
return content
|
||||
.toString()
|
||||
.split("\n")
|
||||
.filter((line) => !line.trim().startsWith("//"))
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
const currentFileContent = filterComments(await readFile("/tmp/env.d.ts"));
|
||||
const generatedFileContent = filterComments(
|
||||
await readFile("src/types/env.d.ts"),
|
||||
);
|
||||
|
||||
if (currentFileContent === generatedFileContent) {
|
||||
console.log("env.d.ts is up to date");
|
||||
return;
|
||||
}
|
||||
|
||||
const isCI = process.env["IS_CI"] === "true";
|
||||
const vcsCommand = isCI ? "git" : "sl";
|
||||
spawn({
|
||||
cmd: [vcsCommand, "diff", "src/types/env.d.ts"],
|
||||
stdout: "inherit",
|
||||
});
|
||||
// add 1 second to make sure spawn completes
|
||||
await sleep(1000);
|
||||
throw new Error("env.d.ts is out of date");
|
||||
});
|
||||
54
src/services/auth/anilist/getUser.spec.ts
Normal file
54
src/services/auth/anilist/getUser.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { getUser } from "./getUser";
|
||||
|
||||
describe("getUser service", () => {
|
||||
const mockStub = {
|
||||
getUserProfile: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// @ts-expect-error - Partial mock
|
||||
env.ANILIST_DO = {
|
||||
idFromName: vi.fn().mockReturnValue("global-id"),
|
||||
get: vi.fn().mockReturnValue(mockStub),
|
||||
};
|
||||
});
|
||||
|
||||
it("should fetch user profile from Durable Object", async () => {
|
||||
const mockUser = { id: 1, name: "User", statistics: { anime: {} } };
|
||||
mockStub.getUserProfile.mockResolvedValue(mockUser);
|
||||
|
||||
const result = await getUser("token");
|
||||
|
||||
expect(result).toEqual({
|
||||
...mockUser,
|
||||
statistics: mockUser.statistics.anime,
|
||||
});
|
||||
expect(mockStub.getUserProfile).toHaveBeenCalledWith("token");
|
||||
});
|
||||
|
||||
it("should return null if DO throws 401 error", async () => {
|
||||
mockStub.getUserProfile.mockRejectedValue(new Error("401 Unauthorized"));
|
||||
|
||||
const result = await getUser("token");
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("should rethrow other DO errors", async () => {
|
||||
mockStub.getUserProfile.mockRejectedValue(new Error("Other Error"));
|
||||
|
||||
await expect(getUser("token")).rejects.toThrow("Other Error");
|
||||
});
|
||||
|
||||
it("should return null if DO returns null", async () => {
|
||||
mockStub.getUserProfile.mockResolvedValue(null);
|
||||
|
||||
const result = await getUser("token");
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
64
src/services/episodes/markEpisodeAsWatched/anilist.spec.ts
Normal file
64
src/services/episodes/markEpisodeAsWatched/anilist.spec.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { markEpisodeAsWatched } from "./anilist";
|
||||
|
||||
describe("markEpisodeAsWatched service", () => {
|
||||
const mockStub = {
|
||||
markTitleAsWatched: vi.fn(),
|
||||
markEpisodeAsWatched: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// @ts-expect-error - Partial mock
|
||||
env.ANILIST_DO = {
|
||||
idFromName: vi.fn().mockReturnValue("global-id"),
|
||||
get: vi.fn().mockReturnValue(mockStub),
|
||||
};
|
||||
});
|
||||
|
||||
it("should call markTitleAsWatched on DO if markTitleAsComplete is true", async () => {
|
||||
mockStub.markTitleAsWatched.mockResolvedValue({
|
||||
user: { id: 1, statistics: { anime: {} } },
|
||||
});
|
||||
|
||||
const result = await markEpisodeAsWatched("token", 1, 12, true);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(mockStub.markTitleAsWatched).toHaveBeenCalledWith(1, "token");
|
||||
expect(mockStub.markEpisodeAsWatched).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call markEpisodeAsWatched on DO if markTitleAsComplete is false", async () => {
|
||||
mockStub.markEpisodeAsWatched.mockResolvedValue({
|
||||
user: { id: 1, statistics: { anime: {} } },
|
||||
});
|
||||
|
||||
const result = await markEpisodeAsWatched("token", 1, 12, false);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(mockStub.markEpisodeAsWatched).toHaveBeenCalledWith(1, 12, "token");
|
||||
expect(mockStub.markTitleAsWatched).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw error if DO returns null", async () => {
|
||||
mockStub.markEpisodeAsWatched.mockResolvedValue(null);
|
||||
|
||||
await expect(markEpisodeAsWatched("token", 1, 12, false)).rejects.toThrow(
|
||||
"Failed to mark episode as watched",
|
||||
);
|
||||
});
|
||||
|
||||
it("should return formatted user data", async () => {
|
||||
const mockUser = { id: 1, statistics: { anime: { count: 10 } } };
|
||||
mockStub.markEpisodeAsWatched.mockResolvedValue({ user: mockUser });
|
||||
|
||||
const result = await markEpisodeAsWatched("token", 1, 12, false);
|
||||
|
||||
expect(result).toEqual({
|
||||
...mockUser,
|
||||
statistics: { count: 10 },
|
||||
});
|
||||
});
|
||||
});
|
||||
84
src/services/popular/browse/anilist.spec.ts
Normal file
84
src/services/popular/browse/anilist.spec.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { fetchPopularTitlesFromAnilist } from "./anilist";
|
||||
|
||||
// Mock getCurrentAndNextSeason
|
||||
vi.mock("~/libs/getCurrentAndNextSeason", () => ({
|
||||
getCurrentAndNextSeason: vi.fn(() => ({
|
||||
current: { season: "WINTER", year: 2024 },
|
||||
next: { season: "SPRING", year: 2024 },
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../mapTitle", () => ({
|
||||
mapTitle: vi.fn((title) => ({ id: title.id, title: title.title })),
|
||||
}));
|
||||
|
||||
describe("fetchPopularTitlesFromAnilist (Browse)", () => {
|
||||
it("should fetch popular titles from Durable Object with current/next season info", async () => {
|
||||
const mockReponse = {
|
||||
trending: { media: [{ id: 1, title: "Trending" }] },
|
||||
season: { media: [{ id: 2, title: "Popular" }] },
|
||||
nextSeason: {
|
||||
media: [
|
||||
{
|
||||
id: 3,
|
||||
title: "Upcoming",
|
||||
nextAiringEpisode: { airingAt: 123 },
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const mockNextSeasonResponse = {
|
||||
Page: { media: [{ id: 4, title: "Next Season Popular" }] },
|
||||
};
|
||||
|
||||
const mockStub = {
|
||||
browsePopular: vi.fn().mockResolvedValue(mockReponse),
|
||||
nextSeasonPopular: vi.fn().mockResolvedValue(mockNextSeasonResponse),
|
||||
};
|
||||
|
||||
// @ts-expect-error - Partial mock
|
||||
env.ANILIST_DO = {
|
||||
getByName: vi.fn().mockReturnValue(mockStub),
|
||||
};
|
||||
|
||||
const result = await fetchPopularTitlesFromAnilist(10);
|
||||
|
||||
expect(result.trending).toHaveLength(1);
|
||||
expect(result.popular).toHaveLength(1);
|
||||
expect(result.upcoming).toHaveLength(1);
|
||||
expect(mockStub.browsePopular).toHaveBeenCalledWith(
|
||||
"WINTER",
|
||||
2024,
|
||||
"SPRING",
|
||||
2024,
|
||||
10,
|
||||
);
|
||||
expect(mockStub.nextSeasonPopular).toHaveBeenCalledWith("SPRING", 2024, 10);
|
||||
});
|
||||
|
||||
it("should handle missing next season data gracefully (return only trending/popular)", async () => {
|
||||
const mockReponse = {
|
||||
trending: { media: [{ id: 1, title: "Trending" }] },
|
||||
season: { media: [{ id: 2, title: "Popular" }] },
|
||||
nextSeason: { media: [] },
|
||||
};
|
||||
|
||||
const mockStub = {
|
||||
browsePopular: vi.fn().mockResolvedValue(mockReponse),
|
||||
};
|
||||
|
||||
// @ts-expect-error - Partial mock
|
||||
env.ANILIST_DO = {
|
||||
getByName: vi.fn().mockReturnValue(mockStub),
|
||||
};
|
||||
|
||||
const result = await fetchPopularTitlesFromAnilist(10);
|
||||
|
||||
expect(result.trending).toHaveLength(1);
|
||||
expect(result.popular).toHaveLength(1);
|
||||
expect(result.upcoming).toBeUndefined(); // Or check implementation if it returns empty array or undefined
|
||||
});
|
||||
});
|
||||
90
src/services/popular/category/anilist.spec.ts
Normal file
90
src/services/popular/category/anilist.spec.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { fetchPopularTitlesFromAnilist } from "./anilist";
|
||||
|
||||
// Mock getCurrentAndNextSeason
|
||||
vi.mock("~/libs/getCurrentAndNextSeason", () => ({
|
||||
getCurrentAndNextSeason: vi.fn(() => ({
|
||||
current: { season: "WINTER", year: 2024 },
|
||||
next: { season: "SPRING", year: 2024 },
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../mapTitle", () => ({
|
||||
mapTitle: vi.fn((title) => ({ id: title.id, title: title.title })),
|
||||
}));
|
||||
|
||||
describe("fetchPopularTitlesFromAnilist (Category)", () => {
|
||||
const mockStub = {
|
||||
getTrendingTitles: vi.fn(),
|
||||
getPopularTitles: vi.fn(),
|
||||
nextSeasonPopular: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// @ts-expect-error - Partial mock
|
||||
env.ANILIST_DO = {
|
||||
idFromName: vi.fn().mockReturnValue("global-id"),
|
||||
get: vi.fn().mockReturnValue(mockStub),
|
||||
};
|
||||
});
|
||||
|
||||
it("should fetch 'trending' titles from Durable Object", async () => {
|
||||
mockStub.getTrendingTitles.mockResolvedValue({
|
||||
media: [{ id: 1, title: "Trending" }],
|
||||
pageInfo: { hasNextPage: true },
|
||||
});
|
||||
|
||||
const result = await fetchPopularTitlesFromAnilist("trending", 1, 10);
|
||||
|
||||
expect(result.results).toHaveLength(1);
|
||||
expect(result.hasNextPage).toBe(true);
|
||||
expect(mockStub.getTrendingTitles).toHaveBeenCalledWith(1, 10);
|
||||
});
|
||||
|
||||
it("should fetch 'popular' titles from Durable Object", async () => {
|
||||
mockStub.getPopularTitles.mockResolvedValue({
|
||||
media: [{ id: 2, title: "Popular" }],
|
||||
pageInfo: { hasNextPage: false },
|
||||
});
|
||||
|
||||
const result = await fetchPopularTitlesFromAnilist("popular", 1, 10);
|
||||
|
||||
expect(result.results).toHaveLength(1);
|
||||
expect(mockStub.getPopularTitles).toHaveBeenCalledWith(
|
||||
1,
|
||||
10,
|
||||
"WINTER",
|
||||
2024,
|
||||
);
|
||||
});
|
||||
|
||||
it("should fetch 'upcoming' titles from Durable Object", async () => {
|
||||
mockStub.nextSeasonPopular.mockResolvedValue({
|
||||
media: [{ id: 3, title: "Upcoming" }],
|
||||
pageInfo: { hasNextPage: true },
|
||||
});
|
||||
|
||||
const result = await fetchPopularTitlesFromAnilist("upcoming", 1, 10);
|
||||
|
||||
expect(result.results).toHaveLength(1);
|
||||
expect(mockStub.nextSeasonPopular).toHaveBeenCalledWith("SPRING", 2024, 10);
|
||||
});
|
||||
|
||||
it("should throw error for unknown category", async () => {
|
||||
await expect(
|
||||
fetchPopularTitlesFromAnilist("unknown" as any, 1, 10),
|
||||
).rejects.toThrow("Unknown category: unknown");
|
||||
});
|
||||
|
||||
it("should return empty results if DO returns null", async () => {
|
||||
mockStub.getTrendingTitles.mockResolvedValue(null);
|
||||
|
||||
const result = await fetchPopularTitlesFromAnilist("trending", 1, 10);
|
||||
|
||||
expect(result.results).toEqual([]);
|
||||
expect(result.hasNextPage).toBe(false);
|
||||
});
|
||||
});
|
||||
72
src/services/watch-status/anilist.spec.ts
Normal file
72
src/services/watch-status/anilist.spec.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { maybeUpdateWatchStatusOnAnilist } from "./anilist";
|
||||
|
||||
const mockRequest = vi.fn();
|
||||
vi.mock("graphql-request", () => ({
|
||||
GraphQLClient: vi.fn(() => ({
|
||||
request: mockRequest,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("~/libs/errors/TitleNotFound", () => ({
|
||||
AnilistTitleNotFoundError: class extends Error {
|
||||
constructor() {
|
||||
super("AnilistTitleNotFoundError");
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe("maybeUpdateWatchStatusOnAnilist service", () => {
|
||||
it("should return true immediately if token is missing", async () => {
|
||||
const result = await maybeUpdateWatchStatusOnAnilist(
|
||||
1,
|
||||
"CURRENT",
|
||||
undefined,
|
||||
);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it("should perform SaveMediaListEntry mutation if watch status is provided", async () => {
|
||||
mockRequest.mockResolvedValue({ SaveMediaListEntry: { id: 123 } });
|
||||
|
||||
const result = await maybeUpdateWatchStatusOnAnilist(1, "CURRENT", "token");
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockRequest).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{ titleId: 1, watchStatus: "CURRENT" },
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it("should perform DeleteMediaListEntry if watch status is null", async () => {
|
||||
mockRequest
|
||||
.mockResolvedValueOnce({ Media: { mediaListEntry: { id: 456 } } }) // Fetch ID
|
||||
.mockResolvedValueOnce({ DeleteMediaListEntry: { deleted: true } }); // Delete
|
||||
|
||||
const result = await maybeUpdateWatchStatusOnAnilist(1, null, "token");
|
||||
|
||||
expect(result).toBe(true);
|
||||
// First call to get ID
|
||||
expect(mockRequest).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{ titleId: 1 },
|
||||
expect.anything(),
|
||||
);
|
||||
// Second call to delete
|
||||
expect(mockRequest).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{ entryId: 456 },
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw AnilistTitleNotFoundError if trying to delete non-existent entry", async () => {
|
||||
mockRequest.mockResolvedValueOnce({ Media: { mediaListEntry: null } });
|
||||
|
||||
await expect(
|
||||
maybeUpdateWatchStatusOnAnilist(1, null, "token"),
|
||||
).rejects.toThrow("AnilistTitleNotFoundError");
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["@cloudflare/workers-types"],
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"~/*": ["src/*"]
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
|
||||
import {
|
||||
defineWorkersProject,
|
||||
readD1Migrations,
|
||||
} from "@cloudflare/vitest-pool-workers/config";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
import { configDefaults } from "vitest/config";
|
||||
|
||||
export default defineWorkersConfig({
|
||||
import path from "node:path";
|
||||
|
||||
export default defineWorkersProject(async () => {
|
||||
const migrationsPath = path.join(__dirname, "drizzle");
|
||||
let migrations: Awaited<ReturnType<typeof readD1Migrations>>;
|
||||
try {
|
||||
migrations = await readD1Migrations(migrationsPath);
|
||||
} catch (e) {
|
||||
console.warn("Could not read migrations", e);
|
||||
migrations = [];
|
||||
}
|
||||
|
||||
return {
|
||||
plugins: [tsconfigPaths()],
|
||||
test: {
|
||||
globals: false,
|
||||
@@ -14,7 +29,7 @@ export default defineWorkersConfig({
|
||||
},
|
||||
coverage: {
|
||||
...configDefaults.coverage,
|
||||
provider: "v8",
|
||||
provider: "istanbul",
|
||||
reporter: ["text", "json", "html"],
|
||||
exclude: [
|
||||
...configDefaults.coverage.exclude,
|
||||
@@ -28,4 +43,5 @@ export default defineWorkersConfig({
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user