npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@haatlabs-core/provider-image-search

v0.10.0

Published

Image and web search through SearXNG, Brave or SerpApi behind one SearchProvider.

Readme

@haatlabs-core/provider-image-search

Install

pnpm add @haatlabs-core/provider-image-search

What it does

Image search through SearXNG (directly or through a services gateway), Brave or SerpApi, and web-page search through SearXNG, behind one SearchProvider interface. createImageSearchProvider(config, deps) and createWebSearchProvider wrap the provider in the rate-limit gate, the circuit breaker and retry (protect, @haatlabs-core/providers), and with deps.cache in a cachedCall layer outside the gate, so a hit spends no token.

Options

config: provider ('searxng' | 'brave' | 'serpapi'), timeoutMs, and that provider's searxngBaseUrl and gatewayKey, braveApiKey or serpApiKey (named without its key, it fails at construction). deps: fetch, limiter and rateLimit (the gate), sleep, now, and cache ({ store, ttlMs, onEvent }; a null store computes every time). Each search takes SearchOptions: limit, safeSearch (0-2), language, signal, call.

Errors

SEARCH_PROVIDER_ERROR (503) with a message saying what failed: a 429, a 5xx, no answer, a time out and a body that is not JSON are transient (with retryAfterMs from Retry-After, seconds or a date, at most five minutes); a 401 or 403 is permanent, naming what to check; any other status is permanent. SerpApi's "Google hasn't returned any results" is no results; any other SerpApi error is a transient "SerpApi could not run the search", its text not passed on. A caller's abort is rethrown as theirs. A bad query or limit is the core VALIDATION_FAILED. A project spreads IMAGE_SEARCH_ERRORS into defineErrors.

Gateway and deadlines

call (tier, priority, deadlineMs) goes to the gateway with a gatewayKey, tier 4 in the background without one. Every provider's own time limit is timeoutMs, or call.deadlineMs when that is sooner (localTimeoutMs), and the gateway is given that limit, never longer. The deadline is each attempt's: a retry starts it again; pass signal for a hard limit on the whole search. Through the gateway a refusal carries an UpstreamStatusError as its cause: isGatewayRefusal(error) is true for the gateway's 401 and 403, and only its own bad_request 400 is a contract error.

With deps.cache, a search that finds the same search already running waits for its answer (cachedCall's single-flight), looking again for up to waitMs (10 s) before it searches itself. That wait does not end on the caller's signal: an aborted search answers once it is over, so waitMs bounds how late.

Fixtures

@haatlabs-core/provider-image-search/fixtures: searchFixtures.searxngImages is recorded from SearXNG; braveImages and serpApiImages are written in the shapes Brave and SerpApi document, not recorded, until the owner records them with a trial key.

Testing

pnpm --filter @haatlabs-core/provider-image-search test (unit, on the fixtures) and test:int (a real SearXNG in a container; needs Docker). The live tests, with the owner's BRAVE_API_KEY and SERPAPI_API_KEY, run with pnpm exec vitest run --config vitest.real.config.ts and are skipped without the keys.

Hosted services

Without a search engine of your own, provider: 'haatlabs' with haatlabsApiKey (your RapidAPI key) searches through the Haatlabs API on RapidAPI. A refusal is SEARCH_PROVIDER_ERROR, transient as the API's was, with the API's error (SERVICE_QUOTA_EXCEEDED, SERVICE_KEY_REFUSED, …) as its cause. See @haatlabs-core/services and https://haatlabs.shop/docs/services/