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

@x12i/web-queries

v1.3.0

Published

Question-driven web queries — plans queries, calls search-adapter, returns normalized web context.

Downloads

1,051

Readme

@x12i/web-queries

Question-driven web scoping: plan search queries from a question (+ optional facts), execute via @x12i/search-adapter, return normalized web context.

Current version: 1.3.0

Install

npm install @x12i/web-queries @x12i/search-adapter

Requires Node 18+ and TAVILY_API_KEY for live search (via search-adapter).

Quick start (core)

import { createSearchAdapter } from "@x12i/search-adapter";
import { createWebScoper } from "@x12i/web-queries";

const scoper = createWebScoper({
  searchAdapter: createSearchAdapter(),
});

const result = await scoper.search({
  question: "What is the current patch status for CVE-2021-44228?",
  record: { cveId: "CVE-2021-44228" },
});

if (result.ok) {
  console.log(result.context!.summary);
  console.log(result.context!.findings);
  console.log(result.context!.sources);
  console.log(result.context!.meta.queriesUsed);
}

API overview

| Method | Role | |--------|------| | search({ question, record?, options? }) | Core — one question → one WebContext | | searchMany(pack) | Appendix A — multi-question pack with shared URL fetch, snapshot short-circuit, optional cache | | plan({ question, record?, options? }) | Preview planned queries without executing search |


Core — search()

Input

{
  requestId?: string;                     // optional correlation id; generated when absent
  question: string;                        // required
  record?: Record<string, unknown>;        // optional known facts
  options?: {
    maxQueries?, includeDomains?, excludeDomains?, preferredDomains?,
    freshnessDays?, topic?, gapFill?, gapType?,
    hints?, queryTemplates?, fetchPages?, fetchTopK?
  };
}

Success output

{
  requestId: string,
  ok: true,
  context: {
    summary?: string,
    summaryOrigin?: "provider" | "derived",
    findings: WebFinding[],   // sourceIds[] → lookup in sources
    sources: WebSource[],
    meta: {
      requestId,
      queriesUsed, retrievedAt, sourceCount, evidenceCount, discoveredCount,
      intent, strategy, shape, timingMs
    }
  }
}

Failure output

{
  requestId: string,
  ok: false,
  error: {
    reason: "no_queries" | "no_adapter" | "search_failed" | "not_eligible" | "error",
    message?: string
  }
}

Appendix A — searchMany() pack

Batch several questions with optimized shared fetching. Requires searchAdapter.searchMany and searchAdapter.fetchUrlContent.

Input

{
  requestId?: string,                      // optional correlation id shared by all questions
  questions: [
    {
      id: string,                           // stable key for results
      question: string,
      purpose?: string,
      record?: Record<string, unknown>,     // overrides pack record
      options?: WebScopeOptions,
      mappedProperty?: string,              // dot-path into snapshot/record
      queryTemplates?: string[],
    }
  ],
  record?: Record<string, unknown>,         // shared facts
  snapshot?: Record<string, unknown>,       // for mappedProperty short-circuit
  options?: WebScopeOptions,                // shared per-call defaults
  forceWeb?: boolean,                       // skip snapshot/cache short-circuits
  primaryQuestionId?: string,               // pack.primary selection
  concurrency?: number,                     // default 3
  dedupe?: "exact" | "normalized" | "off", // default "normalized"
}

Example

const pack = await scoper.searchMany({
  record: { cveId: "CVE-2021-44228" },
  snapshot: { patchStatus: "patched in 2.17.1" },
  questions: [
    {
      id: "patch-status",
      question: "What is the patch status for CVE-2021-44228?",
      mappedProperty: "patchStatus",
    },
    { id: "timeline", question: "When was CVE-2021-44228 first exploited?" },
  ],
  concurrency: 3,
});

Pack output

{
  requestId: string,
  ok: true,
  results: {
    "patch-status": {
      ok: true,
      status: "resolved_from_snapshot",  // | resolved_from_cache | web | miss
      context?: WebContext,
      resolvedFrom?: { mappedProperty, value },
      error?: { reason, message },
    },
    "timeline": { ok: true, status: "web", context: { /* WebContext */ } },
  },
  pack: {
    primaryQuestionId: "patch-status",
    primary?: WebContext,                // convenience aggregate
    scopes: Record<string, WebScopePackScopeEntry>,
  }
}

Pack execution pipeline

  1. Dedupe questions within the pack (duplicate ids share the first outcome)
  2. Per unique question: snapshot short-circuit → optional cache → discovery-only searchMany
  3. Collect discovered URLs across all questions; dedupe by normalized URL
  4. fetchUrlContent once per unique URL; merge evidence into each question
  5. Map to WebContext; optional cache save

Question status values:

| Status | Meaning | |--------|---------| | resolved_from_snapshot | mappedProperty resolved from snapshot or pack record | | resolved_from_cache | Returned by config.cache.get | | web | Live web search + fetch completed | | miss | Failed (no_adapter, no_queries, search_failed, etc.) |


Configuration

createWebScoper({
  searchAdapter: createSearchAdapter(),   // required for search() and searchMany()

  defaults: {
    maxQueries: 3,
    maxFindings: 10,
    maxSources: 10,
    maxResultsPerQuery: 5,
  },

  snippets: {
    include: false,        // include source snippets in output
    maxCharsPerSource: 512,
    fetchPages: false,     // core search(): fetch top URLs via adapter
    fetchTopK: 3,          // pack mode: URLs to fetch per question after discovery
  },

  rawPassthrough: false,   // attach adapter SearchManyResult at context.raw

  isEligible: (input) => true,

  onEvent: ({ phase }) => { /* "plan" | "search" | "map" */ },

  cache: {                 // optional; used by searchMany pack
    get: async ({ requestId, question, record }) => null,
    save: async ({ requestId, question, record, context }) => {},
  },
});

Per-call overrides via options on each search() or pack question.

requestId is returned on every search() and searchMany() result, including failures. When omitted, @x12i/web-queries generates one and stamps it into context.meta.requestId for returned contexts.


Development

From monorepo root:

npm install
npm run build --workspace=@x12i/web-queries
npm run test --workspace=@x12i/web-queries
npm run test:integration --workspace=@x12i/web-queries

Integration tests require TAVILY_API_KEY in web-queries/.env.


Memorix persistence (remember prior research)

For clients that should reuse prior scoped research across calls, use the companion package @x12i/web-scoper-memorix. It is a drop-in wrapper around @x12i/web-queries with the same search(), searchMany(), and plan() API plus automatic Memorix knowledge caching.

import { createMemorixWebScoperFromEnv } from "@x12i/web-scoper-memorix";

const scoper = await createMemorixWebScoperFromEnv();

const first = await scoper.search({
  question: "{{cveId}} patch status",
  record: { cveId: "CVE-2021-44228" },
});

// Second call with the same template + facts returns from Memorix when TTL has not expired.
const second = await scoper.search({
  question: "{{cveId}} patch status",
  record: { cveId: "CVE-2021-44228" },
});

await scoper.close();

Requires MONGO_URI. TTL defaults and overrides are documented in the memorix package README.

Related packages