UNPKG

hacker-news-reel

Version:

A lightweight, typed client for the Hacker News API with validation using Zod

248 lines (247 loc) 8.23 kB
import { z } from 'zod'; import type { ClientHooks } from './hooks'; import type { ClientOptions } from './types'; /** * Types of content that can be searched for in Hacker News */ export type HackerNewsSearchTag = 'story' | 'comment' | 'poll' | 'pollopt' | 'show_hn' | 'ask_hn' | 'front_page' | string; /** * Available numeric fields that can be filtered on */ export type HackerNewsNumericField = 'created_at_i' | 'points' | 'num_comments'; /** * Type of numeric filter operator for search queries */ export type NumericFilterOperator = '<' | '<=' | '=' | '>' | '>='; /** * Numeric filter configuration for search queries */ export interface NumericFilter { field: HackerNewsNumericField; operator: NumericFilterOperator; value: number; } /** * Options for the searchStories function */ export interface SearchOptions { /** * Tags to filter search results by */ tags?: HackerNewsSearchTag[]; /** * Numeric filters to apply to search results */ numericFilters?: NumericFilter[]; /** * Page number to return (0-based) */ page?: number; /** * Number of results per page */ hitsPerPage?: number; /** * By default, sorts by relevance. Set to true to sort by date. */ sortByDate?: boolean; /** * Restrict search to specific attributes (e.g., 'url', 'title') */ restrictSearchableAttributes?: string[]; /** * Author username to filter by */ author?: string; /** * Story ID to filter comments by */ storyId?: number; } /** * Schema for a search result hit from the Algolia API */ declare const HackerNewsSearchHitSchema: z.ZodObject<{ objectID: z.ZodString; title: z.ZodOptional<z.ZodNullable<z.ZodString>>; url: z.ZodOptional<z.ZodNullable<z.ZodString>>; author: z.ZodString; points: z.ZodOptional<z.ZodNullable<z.ZodNumber>>; story_text: z.ZodOptional<z.ZodNullable<z.ZodString>>; comment_text: z.ZodOptional<z.ZodNullable<z.ZodString>>; _tags: z.ZodArray<z.ZodString, "many">; created_at: z.ZodString; created_at_i: z.ZodNumber; num_comments: z.ZodOptional<z.ZodNullable<z.ZodNumber>>; }, "strip", z.ZodTypeAny, { created_at_i: number; objectID: string; author: string; _tags: string[]; created_at: string; url?: string | null | undefined; title?: string | null | undefined; points?: number | null | undefined; num_comments?: number | null | undefined; story_text?: string | null | undefined; comment_text?: string | null | undefined; }, { created_at_i: number; objectID: string; author: string; _tags: string[]; created_at: string; url?: string | null | undefined; title?: string | null | undefined; points?: number | null | undefined; num_comments?: number | null | undefined; story_text?: string | null | undefined; comment_text?: string | null | undefined; }>; /** * Schema for Algolia search response */ declare const HackerNewsSearchResponseSchema: z.ZodObject<{ hits: z.ZodArray<z.ZodObject<{ objectID: z.ZodString; title: z.ZodOptional<z.ZodNullable<z.ZodString>>; url: z.ZodOptional<z.ZodNullable<z.ZodString>>; author: z.ZodString; points: z.ZodOptional<z.ZodNullable<z.ZodNumber>>; story_text: z.ZodOptional<z.ZodNullable<z.ZodString>>; comment_text: z.ZodOptional<z.ZodNullable<z.ZodString>>; _tags: z.ZodArray<z.ZodString, "many">; created_at: z.ZodString; created_at_i: z.ZodNumber; num_comments: z.ZodOptional<z.ZodNullable<z.ZodNumber>>; }, "strip", z.ZodTypeAny, { created_at_i: number; objectID: string; author: string; _tags: string[]; created_at: string; url?: string | null | undefined; title?: string | null | undefined; points?: number | null | undefined; num_comments?: number | null | undefined; story_text?: string | null | undefined; comment_text?: string | null | undefined; }, { created_at_i: number; objectID: string; author: string; _tags: string[]; created_at: string; url?: string | null | undefined; title?: string | null | undefined; points?: number | null | undefined; num_comments?: number | null | undefined; story_text?: string | null | undefined; comment_text?: string | null | undefined; }>, "many">; page: z.ZodNumber; nbHits: z.ZodNumber; nbPages: z.ZodNumber; hitsPerPage: z.ZodNumber; processingTimeMS: z.ZodNumber; query: z.ZodString; }, "strip", z.ZodTypeAny, { hits: { created_at_i: number; objectID: string; author: string; _tags: string[]; created_at: string; url?: string | null | undefined; title?: string | null | undefined; points?: number | null | undefined; num_comments?: number | null | undefined; story_text?: string | null | undefined; comment_text?: string | null | undefined; }[]; page: number; nbHits: number; nbPages: number; hitsPerPage: number; processingTimeMS: number; query: string; }, { hits: { created_at_i: number; objectID: string; author: string; _tags: string[]; created_at: string; url?: string | null | undefined; title?: string | null | undefined; points?: number | null | undefined; num_comments?: number | null | undefined; story_text?: string | null | undefined; comment_text?: string | null | undefined; }[]; page: number; nbHits: number; nbPages: number; hitsPerPage: number; processingTimeMS: number; query: string; }>; /** * Type for a search result hit from the Algolia API */ export type HackerNewsSearchHit = z.infer<typeof HackerNewsSearchHitSchema>; /** * Type for the Algolia search response */ export type HackerNewsSearchResponse = z.infer<typeof HackerNewsSearchResponseSchema>; /** * Creates a search client with configurable fetch implementation * @param options Client options including optional fetch function and AbortSignal * @returns Object containing all HackerNews search API methods * * Example with AbortSignal: * ```ts * // Create an AbortController to cancel requests when needed * const controller = new AbortController(); * const client = createSearchClient({ signal: controller.signal }); * * // Later, to cancel all in-flight requests: * controller.abort(); * ``` */ export declare const createSearchClient: ({ limiter, fetch: _fetch, retry, signal, hooks, }?: ClientOptions) => { searchStories: (query: string, options?: SearchOptions) => Promise<HackerNewsSearchResponse>; getFrontPageStories: (options?: Omit<SearchOptions, "tags">) => Promise<HackerNewsSearchResponse>; invalidateSearchCache: (query: string, options?: SearchOptions) => void; clearSearchCache: () => void; /** * Register hooks with the search client * @param hooks Object with beforeFetch, afterFetch, and/or onError functions * @returns The client instance for chaining * * Example: * ```ts * searchClient.use({ * beforeFetch: (url, opts) => { * console.log(`Searching ${url}`); * return { url, options: opts }; * }, * afterFetch: (response) => { * console.log(`Search returned ${response.status}`); * return response; * }, * onError: (err) => { * console.error(`Error in search: ${err.message}`); * } * }); * ``` */ use: (hooks: ClientHooks) => /*elided*/ any; /** * Clear all registered hooks * @returns The client instance for chaining */ clearHooks: () => /*elided*/ any; }; export declare const searchStories: (query: string, options?: SearchOptions) => Promise<HackerNewsSearchResponse>, getFrontPageStories: (options?: Omit<SearchOptions, "tags">) => Promise<HackerNewsSearchResponse>, invalidateSearchCache: (query: string, options?: SearchOptions) => void, clearSearchCache: () => void; export {};