feat: add type safety to AniList fetching methods

This commit is contained in:
2025-12-06 08:09:58 -05:00
parent 9b5cc7ea62
commit cc4f518de7

View File

@@ -1,3 +1,4 @@
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
import { DurableObject } from "cloudflare:workers"; import { DurableObject } from "cloudflare:workers";
import { type ResultOf } from "gql.tada"; import { type ResultOf } from "gql.tada";
import { print } from "graphql"; import { print } from "graphql";
@@ -35,11 +36,11 @@ export class AnilistDurableObject extends DurableObject {
super(state, env); super(state, env);
this.state = state; this.state = state;
} }
async fetch(request: Request): Promise<Response> { async fetch(request: Request) {
return new Response("Not found", { status: 404 }); return new Response("Not found", { status: 404 });
} }
async getTitle(id: number, token?: string): Promise<any> { async getTitle(id: number, token?: string) {
const storageKey = id.toString(); const storageKey = id.toString();
const cache = await this.state.storage.get(storageKey); const cache = await this.state.storage.get(storageKey);
if (cache) { if (cache) {
@@ -75,7 +76,7 @@ export class AnilistDurableObject extends DurableObject {
return media; return media;
} }
async getNextEpisodeAiringAt(id: number): Promise<any> { async getNextEpisodeAiringAt(id: number) {
const storageKey = `next_airing:${id}`; const storageKey = `next_airing:${id}`;
const TTL = 60 * 60 * 1000; const TTL = 60 * 60 * 1000;
@@ -91,7 +92,7 @@ export class AnilistDurableObject extends DurableObject {
); );
} }
async search(query: string, page: number, limit: number): Promise<any> { async search(query: string, page: number, limit: number) {
const storageKey = `search:${JSON.stringify({ query, page, limit })}`; const storageKey = `search:${JSON.stringify({ query, page, limit })}`;
const TTL = 60 * 60 * 1000; const TTL = 60 * 60 * 1000;
return this.handleCachedRequest( return this.handleCachedRequest(
@@ -114,7 +115,7 @@ export class AnilistDurableObject extends DurableObject {
nextSeason: any, nextSeason: any,
nextYear: number, nextYear: number,
limit: number, limit: number,
): Promise<any> { ) {
// No caching for browse popular as it returns a Response object in the original code? // No caching for browse popular as it returns a Response object in the original code?
// Wait, the original code had caching logic but it was commented out or mixed? // Wait, the original code had caching logic but it was commented out or mixed?
// The original code returned a Response directly for BrowsePopular without caching in the switch case, // The original code returned a Response directly for BrowsePopular without caching in the switch case,
@@ -133,11 +134,7 @@ export class AnilistDurableObject extends DurableObject {
}); });
} }
async nextSeasonPopular( async nextSeasonPopular(nextSeason: any, nextYear: number, limit: number) {
nextSeason: any,
nextYear: number,
limit: number,
): Promise<any> {
const storageKey = `next_season:${JSON.stringify({ nextSeason, nextYear, limit })}`; const storageKey = `next_season:${JSON.stringify({ nextSeason, nextYear, limit })}`;
const TTL = 60 * 60 * 1000; const TTL = 60 * 60 * 1000;
return this.handleCachedRequest( return this.handleCachedRequest(
@@ -158,7 +155,7 @@ export class AnilistDurableObject extends DurableObject {
limit: number, limit: number,
season: any, season: any,
seasonYear: number, seasonYear: number,
): Promise<any> { ) {
// The original code had unreachable cache logic. // The original code had unreachable cache logic.
// I will implement it with caching if possible, but let's follow the pattern. // I will implement it with caching if possible, but let's follow the pattern.
// Actually, let's enable caching as it seems intended. // Actually, let's enable caching as it seems intended.
@@ -179,7 +176,7 @@ export class AnilistDurableObject extends DurableObject {
); );
} }
async getTrendingTitles(page: number, limit: number): Promise<any> { async getTrendingTitles(page: number, limit: number) {
const storageKey = `trending:${JSON.stringify({ page, limit })}`; const storageKey = `trending:${JSON.stringify({ page, limit })}`;
const TTL = 60 * 60 * 1000; const TTL = 60 * 60 * 1000;
return this.handleCachedRequest( return this.handleCachedRequest(
@@ -199,7 +196,7 @@ export class AnilistDurableObject extends DurableObject {
page: number, page: number,
airingAtLowerBound: number, airingAtLowerBound: number,
airingAtUpperBound: number, airingAtUpperBound: number,
): Promise<any> { ) {
const storageKey = `upcoming:${JSON.stringify({ page, airingAtLowerBound, airingAtUpperBound })}`; const storageKey = `upcoming:${JSON.stringify({ page, airingAtLowerBound, airingAtUpperBound })}`;
const TTL = 60 * 60 * 1000; const TTL = 60 * 60 * 1000;
return this.handleCachedRequest( return this.handleCachedRequest(
@@ -216,7 +213,7 @@ export class AnilistDurableObject extends DurableObject {
); );
} }
async getUser(token: string): Promise<any> { async getUser(token: string) {
const storageKey = `user:${token}`; const storageKey = `user:${token}`;
// 1 month // 1 month
const TTL = 60 * 60 * 24 * 30 * 1000; const TTL = 60 * 60 * 24 * 30 * 1000;
@@ -230,7 +227,7 @@ export class AnilistDurableObject extends DurableObject {
); );
} }
async getUserProfile(token: string): Promise<any> { async getUserProfile(token: string) {
const data = await this.fetchFromAnilist( const data = await this.fetchFromAnilist(
GetUserProfileQuery, GetUserProfileQuery,
{ token }, { token },
@@ -243,7 +240,7 @@ export class AnilistDurableObject extends DurableObject {
titleId: number, titleId: number,
episodeNumber: number, episodeNumber: number,
token: string, token: string,
): Promise<any> { ) {
const data = await this.fetchFromAnilist( const data = await this.fetchFromAnilist(
MarkEpisodeAsWatchedMutation, MarkEpisodeAsWatchedMutation,
{ titleId, episodeNumber }, { titleId, episodeNumber },
@@ -252,7 +249,7 @@ export class AnilistDurableObject extends DurableObject {
return data?.SaveMediaListEntry; return data?.SaveMediaListEntry;
} }
async markTitleAsWatched(titleId: number, token: string): Promise<any> { async markTitleAsWatched(titleId: number, token: string) {
const data = await this.fetchFromAnilist( const data = await this.fetchFromAnilist(
MarkTitleAsWatchedMutation, MarkTitleAsWatchedMutation,
{ titleId }, { titleId },
@@ -289,9 +286,9 @@ export class AnilistDurableObject extends DurableObject {
} }
// Helper to handle caching logic // Helper to handle caching logic
async handleCachedRequest( async handleCachedRequest<T>(
key: string, key: string,
fetcher: () => Promise<any>, fetcher: () => Promise<T>,
ttl?: number, ttl?: number,
) { ) {
const cache = await this.state.storage.get(key); const cache = await this.state.storage.get(key);
@@ -325,11 +322,11 @@ export class AnilistDurableObject extends DurableObject {
} }
} }
async fetchFromAnilist( async fetchFromAnilist<Result = any, Variables = any>(
query: any, query: TypedDocumentNode<Result, Variables>,
variables: any, variables: Variables,
token?: string | undefined, token?: string | undefined,
): Promise<any> { ): Promise<Result> {
const headers: any = { const headers: any = {
"Content-Type": "application/json", "Content-Type": "application/json",
}; };