api

package
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Index

Constants

View Source
const TBRListName = "Oku reading queue"

Variables

View Source
var (
	ErrBadRequest        = errors.New("bad request")                // 400
	ErrUnauthorized      = errors.New("unauthorized")               // 401
	ErrInsufficientScope = errors.New("insufficient scope")         // 403
	ErrUnsupportedOp     = errors.New("unsupported operation")      // 403
	ErrTopLevelLimit     = errors.New("too many top-level queries") // 403
	ErrOverCapacity      = errors.New("request exceeds capacity")   // 403
	ErrForbidden         = errors.New("forbidden")                  // 403
	ErrNotFound          = errors.New("not found")                  // 404
	ErrTimeout           = errors.New("server timeout")             // 408
	ErrRateLimited       = errors.New("rate limited")               // 429
	ErrUnavailable       = errors.New("service unavailable")        // 5xx
)

Sentinels for errors.Is against a *StatusError. See https://docs.hardcover.app/api/getting-started/#api-response-codes

View Source
var Version = "dev"

Version identifies the build in the User-Agent header. It defaults to "dev"; main can overwrite it with the binary's version at startup.

Functions

func IsNetworkError

func IsNetworkError(err error) bool

IsNetworkError reports whether an error should map to network/API exit code 2.

Types

type APIBook

type APIBook struct {
	ID             int               `json:"id"`
	Title          string            `json:"title"`
	Pages          int               `json:"pages"`
	Slug           string            `json:"slug"`
	Rating         float64           `json:"rating"`
	RatingsCount   int               `json:"ratings_count"`
	ReviewsCount   int               `json:"reviews_count"`
	UsersCount     int               `json:"users_count"`
	UsersReadCount int               `json:"users_read_count"`
	ReleaseDate    *string           `json:"release_date"`
	CachedTags     json.RawMessage   `json:"cached_tags"`
	Contributions  []APIContribution `json:"contributions"`
	Image          *APIImage         `json:"image"`
}

APIBook represents a book from the API.

type APIContribution

type APIContribution struct {
	Author struct {
		Name string `json:"name"`
	} `json:"author"`
}

APIContribution represents an author contribution to a book.

type APIGoal added in v0.6.0

type APIGoal struct {
	ID        int           `json:"id"`
	Goal      int           `json:"goal"`
	Metric    string        `json:"metric"`
	Progress  FlexibleFloat `json:"progress"`
	State     string        `json:"state"`
	StartDate *string       `json:"start_date"`
	EndDate   *string       `json:"end_date"`
}

APIGoal represents a reading goal from the API.

type APIImage

type APIImage struct {
	URL string `json:"url"`
}

APIImage represents an image URL from the API.

type APIReadingJournal added in v0.6.0

type APIReadingJournal struct {
	ID       int     `json:"id"`
	ActionAt *string `json:"action_at"`
	Event    string  `json:"event"`
}

APIReadingJournal represents a reading journal entry from the API.

type APIUserBook

type APIUserBook struct {
	ID            int               `json:"id"`
	StatusID      int               `json:"status_id"`
	Rating        *float64          `json:"rating"`
	ReviewRaw     *string           `json:"review_raw"`
	ReviewedAt    *string           `json:"reviewed_at"`
	UpdatedAt     *string           `json:"updated_at"`
	UserBookReads []APIUserBookRead `json:"user_book_reads"`
	Book          APIBook           `json:"book"`
}

APIUserBook represents a user-book relationship from the API.

type APIUserBookRead

type APIUserBookRead struct {
	ID            int     `json:"id"`
	ProgressPages int     `json:"progress_pages"`
	StartedAt     *string `json:"started_at"`
	FinishedAt    *string `json:"finished_at"`
}

APIUserBookRead represents a reading progress entry from the API.

type AuthError added in v0.4.5

type AuthError struct{}

AuthError reports that the API response did not contain an authenticated user.

func (*AuthError) Error added in v0.4.5

func (e *AuthError) Error() string

type BookDetail added in v0.10.0

type BookDetail struct {
	APIBook
	Description         string          `json:"description"`
	Headline            string          `json:"headline"`
	EditionsCount       int             `json:"editions_count"`
	RatingsDistribution json.RawMessage `json:"ratings_distribution"`
}

BookDetail contains community metadata fetched on demand, outside library sync.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client wraps the machinebox/graphql client with authentication and rate limiting.

func NewClient

func NewClient(token string) *Client

NewClient creates a new Hardcover API client with the given auth token. The token is normalised to include a "Bearer " prefix if missing.

func NewOAuthClient added in v0.12.0

func NewOAuthClient(ctx context.Context, src oauth2.TokenSource) *Client

NewOAuthClient creates a Hardcover API client authorized by src. The Authorization header is set per-request by src's own oauth2.Transport, which refreshes an expired token on demand.

func (*Client) BookDetail added in v0.10.0

func (c *Client) BookDetail(ctx context.Context, id int) (*BookDetail, error)

func (*Client) BookJournal added in v0.10.0

func (c *Client) BookJournal(ctx context.Context, bookID int) ([]JournalEntry, error)

func (*Client) CreateJournalEntry added in v0.10.0

func (c *Client) CreateJournalEntry(ctx context.Context, bookID int, event, entry string, privacy int) (int, error)

func (*Client) ExportLibrary added in v0.10.0

func (c *Client) ExportLibrary(ctx context.Context) ([]APIUserBook, error)

ExportLibrary pages by primary key order and includes every read, not only the latest.

func (*Client) GetAccountPrivacySetting added in v0.6.0

func (c *Client) GetAccountPrivacySetting(ctx context.Context) (int, error)

GetAccountPrivacySetting returns the user's default privacy setting ID (1=Public, 2=Followers, 3=Private).

func (*Client) GetBookRatingsByIDs

func (c *Client) GetBookRatingsByIDs(ctx context.Context, ids []int) (map[int]model.Book, error)

GetBookRatingsByIDs fetches rating metadata for a set of book IDs.

func (*Client) GetMe

func (c *Client) GetMe(ctx context.Context) (int, string, error)

GetMe returns the authenticated user's ID and username.

func (*Client) GoalSettings added in v0.10.0

func (c *Client) GoalSettings(ctx context.Context, id int) (GoalInput, error)

GoalSettings preserves filters, dates, description and privacy when editing a target.

func (*Client) ImportReads added in v0.10.0

func (c *Client) ImportReads(ctx context.Context, userBookID int, reads []APIUserBookRead) error

ImportReads replaces read history only for a newly inserted library record.

func (*Client) InsertProgressJournal added in v0.6.0

func (c *Client) InsertProgressJournal(ctx context.Context, bookID, progressPages, totalPages, privacySettingID int) error

InsertProgressJournal creates a "progress_updated" reading journal entry. Hardcover's website builds its activity heatmap from reading_journals, which the site creates on every progress update; update_user_book_read alone never produces one, so API-driven updates stay invisible on the calendar without this.

func (*Client) InsertUserBook

func (c *Client) InsertUserBook(ctx context.Context, bookID int, statusID int) (int, error)

InsertUserBook adds a book to the user's library with the given status and returns the user_book ID.

func (*Client) InsertUserBookRead

func (c *Client) InsertUserBookRead(ctx context.Context, userBookID int, progressPages int) (*APIUserBookRead, error)

InsertUserBookRead creates a new reading progress entry for the given user book.

func (*Client) ListGoals added in v0.6.0

func (c *Client) ListGoals(ctx context.Context) ([]APIGoal, error)

ListGoals returns the user's reading goals.

func (*Client) ListReadingJournals added in v0.6.0

func (c *Client) ListReadingJournals(ctx context.Context, userID int, since time.Time) ([]APIReadingJournal, error)

ListReadingJournals returns the user's reading journal entries since the given date (inclusive). Only the fields needed for activity aggregation are fetched.

func (*Client) ListUserBooks

func (c *Client) ListUserBooks(ctx context.Context, statusID int) ([]APIUserBook, error)

ListUserBooks returns the user's books filtered by status ID.

func (*Client) LookupISBN added in v0.10.0

func (c *Client) LookupISBN(ctx context.Context, isbn string) (int, error)

func (*Client) QueueBooks added in v0.10.0

func (c *Client) QueueBooks(ctx context.Context, listID int, books []RankedBook) error

QueueBooks appends at most five entries per request, respecting Hardcover's top-level field limit and avoiding one round trip per book on initial setup.

func (*Client) ReadingQueue added in v0.10.0

func (c *Client) ReadingQueue(ctx context.Context, create bool) (int, []RankedBook, error)

func (*Client) SaveGoal added in v0.10.0

func (c *Client) SaveGoal(ctx context.Context, id int, g GoalInput) (int, error)

func (*Client) SearchBooks

func (c *Client) SearchBooks(ctx context.Context, query string, perPage int, mode model.SearchMode) ([]model.SearchResult, error)

SearchBooks searches for books by query string and returns parsed results. User input travels as a GraphQL variable, never interpolated into the query document, so no escaping is needed.

func (*Client) SetQueuePositions added in v0.10.0

func (c *Client) SetQueuePositions(ctx context.Context, listID int, rows []RankedBook) error

SetQueuePositions updates the affected ranks in one database mutation request.

func (*Client) Trending added in v0.10.0

func (c *Client) Trending(ctx context.Context, duration string, limit int) ([]APIBook, error)

func (*Client) UpdateReadProgress

func (c *Client) UpdateReadProgress(ctx context.Context, userBookReadID int, progressPages int) error

UpdateReadProgress updates the progress pages on an existing user book read.

func (*Client) UpdateUserBookRating added in v0.4.1

func (c *Client) UpdateUserBookRating(ctx context.Context, userBookID int, rating float64) error

UpdateUserBookRating updates a user-book rating.

func (*Client) UpdateUserBookReviewAndRating added in v0.4.1

func (c *Client) UpdateUserBookReviewAndRating(
	ctx context.Context,
	userBookID int,
	rating float64,
	review string,
	reviewedAt string,
) error

UpdateUserBookReviewAndRating updates review text, reviewed timestamp, and rating.

func (*Client) UpdateUserBookStatus

func (c *Client) UpdateUserBookStatus(ctx context.Context, userBookID int, statusID int) error

UpdateUserBookStatus changes the status of an existing user book.

func (*Client) UpsertUserBookReads

func (c *Client) UpsertUserBookReads(ctx context.Context, userBookID int, progressPages int) error

UpsertUserBookReads creates or updates a reading progress entry for the given user book.

type FlexibleFloat

type FlexibleFloat float64

FlexibleFloat handles APIs that may return a number or a quoted number.

func (*FlexibleFloat) UnmarshalJSON

func (v *FlexibleFloat) UnmarshalJSON(data []byte) error

type FlexibleInt

type FlexibleInt int

FlexibleInt handles APIs that sometimes return a number and sometimes a quoted number.

func (*FlexibleInt) UnmarshalJSON

func (v *FlexibleInt) UnmarshalJSON(data []byte) error

type GoalInput added in v0.10.0

type GoalInput struct {
	Description      string         `json:"description"`
	Goal             int            `json:"goal"`
	Metric           string         `json:"metric"`
	StartDate        string         `json:"start_date"`
	EndDate          string         `json:"end_date"`
	Conditions       map[string]any `json:"conditions"`
	PrivacySettingID int            `json:"privacy_setting_id"`
}

func (GoalInput) Validate added in v0.10.0

func (g GoalInput) Validate() error

type ISBNMatchError added in v0.10.0

type ISBNMatchError struct {
	ISBN      string
	Ambiguous bool
}

ISBNMatchError distinguishes unmatched data from API/authentication failures.

func (*ISBNMatchError) Error added in v0.10.0

func (e *ISBNMatchError) Error() string

type InsertReadingJournalResponse added in v0.6.0

type InsertReadingJournalResponse struct {
	InsertReadingJournal struct {
		ReadingJournal *struct {
			ID int `json:"id"`
		} `json:"reading_journal"`
	} `json:"insert_reading_journal"`
}

InsertReadingJournalResponse is the response shape for inserting a reading journal entry.

type InsertUserBookReadResponse

type InsertUserBookReadResponse struct {
	InsertUserBookRead struct {
		ID           *int             `json:"id"`
		Error        *string          `json:"error"`
		UserBookRead *APIUserBookRead `json:"user_book_read"`
	} `json:"insert_user_book_read"`
}

InsertUserBookReadResponse is the response shape for inserting a user book read.

type InsertUserBookResponse

type InsertUserBookResponse struct {
	InsertUserBook struct {
		ID       *int `json:"id"`
		UserBook *struct {
			ID int `json:"id"`
		} `json:"user_book"`
		Error *string `json:"error"`
	} `json:"insert_user_book"`
}

InsertUserBookResponse is the response shape for inserting a user book.

type JournalEntry added in v0.10.0

type JournalEntry struct {
	ID       int    `json:"id"`
	BookID   int    `json:"book_id"`
	Event    string `json:"event"`
	Entry    string `json:"entry"`
	ActionAt string `json:"action_at"`
}

type MeResponse

type MeResponse struct {
	Me []MeUser `json:"me"`
}

MeResponse is the response shape for the me query.

type MeUser

type MeUser struct {
	ID                      int           `json:"id"`
	Username                string        `json:"username"`
	AccountPrivacySettingID *int          `json:"account_privacy_setting_id"`
	UserBooks               []APIUserBook `json:"user_books"`
	Goals                   []APIGoal     `json:"goals"`
}

MeUser represents a single user entry from the me query.

type NetworkError

type NetworkError struct {
	Err error
}

NetworkError marks transient request failures (timeouts, 429, 5xx, transport errors). The CLI maps these to exit code 2.

func (*NetworkError) Error

func (e *NetworkError) Error() string

func (*NetworkError) Unwrap

func (e *NetworkError) Unwrap() error

type RankedBook added in v0.10.0

type RankedBook struct {
	ID       int `json:"id"`
	BookID   int `json:"book_id"`
	Position int `json:"position"`
}

type RankedList added in v0.10.0

type RankedList struct {
	ID     int    `json:"id"`
	Name   string `json:"name"`
	Ranked bool   `json:"ranked"`
}

type RatingBucket added in v0.10.0

type RatingBucket struct {
	Rating float64 `json:"rating"`
	Count  int     `json:"count"`
}

func ParseRatingDistribution added in v0.10.0

func ParseRatingDistribution(raw json.RawMessage) ([]RatingBucket, error)

ParseRatingDistribution accepts Hardcover's array and older keyed-object shape.

type ReadingJournalsResponse added in v0.6.0

type ReadingJournalsResponse struct {
	ReadingJournals []APIReadingJournal `json:"reading_journals"`
}

ReadingJournalsResponse is the response shape for listing reading journals.

type SearchResponse

type SearchResponse struct {
	Search struct {
		Results json.RawMessage `json:"results"`
	} `json:"search"`
}

SearchResponse is the response shape for book search queries. The results field contains raw Typesense JSON.

type StatusError added in v0.4.4

type StatusError struct {
	Code int
	// APIError, Description and Scope are parsed from a JSON error body;
	// all are empty when the body isn't JSON.
	APIError    string // e.g. "invalid_token", "insufficient_scope"
	Description string
	Scope       string // permissions missing, for insufficient_scope
	// Body holds up to maxErrorBody bytes of the response body, with runs of
	// whitespace collapsed, so failures carry the server's explanation
	// instead of just a status number.
	Body string
	// RetryAfter is the parsed Retry-After header, or 0 when absent or unparseable.
	RetryAfter time.Duration
}

StatusError reports a non-2xx HTTP response from the API. The graphql library never exposes the status itself, so statusTransport converts non-2xx responses into this typed error at the transport layer.

func (*StatusError) Error added in v0.4.4

func (e *StatusError) Error() string

func (*StatusError) Is added in v0.12.0

func (e *StatusError) Is(target error) bool

Is lets callers match a StatusError with errors.Is. The specific 403 variants also satisfy ErrForbidden.

type TypesenseBookDoc

type TypesenseBookDoc struct {
	ID          FlexibleInt     `json:"id"`
	Title       string          `json:"title"`
	AuthorNames []string        `json:"author_names"`
	Pages       FlexibleInt     `json:"pages"`
	Slug        string          `json:"slug"`
	Rating      FlexibleFloat   `json:"rating"`
	Ratings     FlexibleInt     `json:"ratings_count"`
	Image       *TypesenseImage `json:"image"`
}

TypesenseBookDoc represents a book document from Typesense search.

type TypesenseHit added in v0.4.5

type TypesenseHit struct {
	Document TypesenseBookDoc `json:"document"`
}

TypesenseHit represents one search hit.

type TypesenseImage

type TypesenseImage struct {
	URL string `json:"url"`
}

TypesenseImage represents an image in Typesense search results.

type TypesenseResults

type TypesenseResults struct {
	Hits []TypesenseHit `json:"hits"`
}

TypesenseResults represents parsed Typesense search results.

func (*TypesenseResults) UnmarshalJSON added in v0.4.5

func (r *TypesenseResults) UnmarshalJSON(data []byte) error

UnmarshalJSON skips malformed individual hits while retaining valid results.

type UpdateUserBookReadResponse

type UpdateUserBookReadResponse struct {
	UpdateUserBookRead struct {
		ID    *int    `json:"id"`
		Error *string `json:"error"`
	} `json:"update_user_book_read"`
}

UpdateUserBookReadResponse is the response shape for updating a user book read.

type UpdateUserBookResponse

type UpdateUserBookResponse struct {
	UpdateUserBook struct {
		ID    *int    `json:"id"`
		Error *string `json:"error"`
	} `json:"update_user_book"`
}

UpdateUserBookResponse is the response shape for updating a user book.

type UpsertUserBookReadsResponse

type UpsertUserBookReadsResponse struct {
	UpsertUserBookReads struct {
		Error      *string `json:"error"`
		UserBookID *int    `json:"user_book_id"`
	} `json:"upsert_user_book_reads"`
}

UpsertUserBookReadsResponse is the response shape for upserting user book reads.

type UserBooksResponse

type UserBooksResponse struct {
	Me []MeUser `json:"me"`
}

UserBooksResponse is the response shape for listing user books.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL