import { createCache } from "@lib/cache.ts"; const HARDCOVER_API_URL = "https://api.hardcover.app/v1/graphql"; const CACHE_INTERVAL = 1000 * 60 * 60 * 24 * 7; const cache = createCache("hardcover", { expires: CACHE_INTERVAL }); export interface HardcoverBookResult { id: string; slug: string; title: string; subtitle?: string; author_names: string[]; cover_color?: string; description?: string; isbn?: string; isbn13?: string; release_year?: string; series_names?: string[]; rating?: number; ratings_count?: number; pages?: number; image?: string; } async function hardcoverFetch(query: string, variables: Record = {}) { const response = await fetch(HARDCOVER_API_URL, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": Deno.env.get("HARDCOVER_API_TOKEN") || "", }, body: JSON.stringify({ query, variables }), }); if (!response.ok) { throw new Error(`Hardcover API error: ${response.statusText}`); } const json = await response.json(); if (json.errors) { console.error("Hardcover GraphQL errors:", JSON.stringify(json.errors, null, 2)); throw new Error(`Hardcover GraphQL error: ${json.errors[0]?.message || "Unknown error"}`); } return json; } export const searchBook = async (query: string) => { const id = `query:booksearch:${query}`; if (cache.has(id)) return cache.get(id) as HardcoverBookResult[]; const graphqlQuery = ` query SearchBooks($query: String!, $perPage: Int, $page: Int) { search( query: $query query_type: "Book" per_page: $perPage page: $page ) { results } } `; const result = await hardcoverFetch(graphqlQuery, { query, perPage: 10, page: 1, }); const typesenseResponse = result.data?.search?.results; const hits = typesenseResponse?.hits; const books = (hits || []).map((hit: { document: HardcoverBookResult }) => hit.document); cache.set(id, books); return books as HardcoverBookResult[]; }; export const getBookDetails = async (id: string) => { const cacheId = `query:bookdetails:${id}`; if (cache.has(cacheId)) return cache.get(cacheId); const graphqlQuery = ` query GetBook($id: Int!) { books(where: { id: { _eq: $id } }) { id slug title subtitle description release_date release_year rating ratings_count pages cached_image cached_contributors featured_book_series { series { id name slug } } editions(limit: 1) { isbn_10 isbn_13 } } } `; const result = await hardcoverFetch(graphqlQuery, { id: parseInt(id) }); const bookData = result.data?.books?.[0]; if (bookData) { const isbn13 = bookData.editions?.[0]?.isbn_13; const isbn10 = bookData.editions?.[0]?.isbn_10; const cachedContributors = bookData.cached_contributors as Array<{ author: { name: string }; contribution: string }> | undefined; const authorName = cachedContributors?.[0]?.author?.name; const book = { ...bookData, isbn: isbn13 || isbn10 || "", isbn13, isbn10, author_names: authorName ? [authorName] : [], image: bookData.cached_image?.url, }; cache.set(cacheId, book); return book; } return null; };