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

@rfjs/es-client

v0.1.0

Published

Elasticsearch / OpenSearch execution layer — client-agnostic search, pagination, highlight over @rfjs/es-query

Readme

@rfjs/es-client

English · 繁體中文

建構在 @rfjs/es-query 之上、與 client 無關的執行層。它不直接 import 任何 Elasticsearch / OpenSearch client —— 你把自己的 client 包成一個 SearchTransport,本套件就在 其上提供 search / count / msearch、深分頁(search_after + Point-In-Time),以及通用 highlight 工具。

支援 **Elasticsearch(8.x / 9.x)**與 OpenSearch(2.x / 3.x)

安裝

pnpm add @rfjs/es-client @rfjs/es-query
# 自備 client,例如 @elastic/elasticsearch 或 @opensearch-project/opensearch

包裝 client

import { Client } from '@elastic/elasticsearch';
import { fromElasticClient } from '@rfjs/es-client';

const transport = fromElasticClient(new Client({ node: 'http://localhost:9200' }));
import { Client } from '@opensearch-project/opensearch';
import { fromOpenSearchClient } from '@rfjs/es-client';

const transport = fromOpenSearchClient(new Client({ node: 'http://localhost:9200' }));

兩者都回傳相同的 SearchTransport,所以下面的用法不論目標都一致。adapter 會把兩個 client 的回應 形狀(Elasticsearch 直接回 body;OpenSearch 回 { body })與各自的 Point-In-Time API 正規化。

Search / count / msearch

搭配 @rfjs/es-querybuildSearchBody

import { buildSearchBody } from '@rfjs/es-query';
import { search } from '@rfjs/es-client';

const body = buildSearchBody(
  { logic: 'and', filters: [{ field: 'status', condition: 'eq', value: 'open' }] },
  { size: 20, sort: [{ field: 'createdAt', order: 'desc' }] },
);

const { total, hits, sources } = await search<{ id: string }>(transport, { index: 'tickets', body });

count(transport, { index, body }) 回傳數字;msearch(transport, requests) 每個請求回傳一個結果。

深分頁(search_after + PIT)

paginateAll 開一個 Point-In-Time,用 search_after 分批走過所有符合的文件,完成時關閉 PIT (即使發生錯誤也會關閉)。你的 body 應包含 sort,這樣每筆 hit 才會帶 sort 值作為游標。

import { paginateAll } from '@rfjs/es-client';

for await (const batch of paginateAll<{ id: string }>(transport, {
  index: 'tickets',
  body: { query: { match_all: {} }, sort: [{ createdAt: { order: 'asc' } }, { _id: 'asc' }] },
  pageSize: 1000,
})) {
  // 每批最多處理 1000 筆 hit
}

Highlight

import { buildHighlight, parseHighlight } from '@rfjs/es-client';

const body = { query: { match: { body: 'refund' } }, ...buildHighlight({ fields: ['body'] }) };
const { hits } = await search(transport, { index: 'tickets', body });
const snippets = parseHighlight(hits[0]); // { body: ['… <em>refund</em> …'] }

SearchTransport

若你的目標是其他執行環境(proxy、serverless function、原始 HTTP),可直接實作此契約:

interface SearchTransport {
  search<T>(req): Promise<EsSearchResponse<T>>;
  count(req): Promise<number>;
  msearch<T>(req): Promise<EsSearchResponse<T>[]>;
  openPit(req): Promise<string>;
  closePit(id): Promise<void>;
}

授權

ISC