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

@aifeatures/react

v0.20.0

Published

React components for aifeatures sites: form CAPTCHA (Cloudflare Turnstile), headless payments (product price, buy button, order confirmation), and cookie consent (geo-adaptive banner, category-gated scripts, preferences)

Readme

@aifeatures/react

Public feature API configuration

AifeaturesProvider configures the public API base for forms and captcha, products, carts, checkout, order status, geo suggestions, and customer accounts:

import { AifeaturesProvider } from '@aifeatures/react';

<AifeaturesProvider apiUrl="/_ls">
  <App />
</AifeaturesProvider>

Every feature component and hook resolves its API base in this order: an explicit apiUrl, the nearest AifeaturesProvider, then https://aifeatures.dev. Without a provider, existing installed source keeps its platform requests after an upgrade. Adding a provider is an explicit source change; upgrading the package does not migrate forms or rewrite URLs. Relative bases remain relative during SSR and hydration; the resolver does not read window.

Authored form actions and fetch calls must use the same base as their captcha. The provider cannot rewrite an existing form action. Use useAifeaturesApiUrl(explicitApiUrl?) in the component that owns submission:

import { AifeaturesCaptcha, useAifeaturesApiUrl } from '@aifeatures/react';

function ContactForm({ formId, apiUrl: explicitApiUrl }: {
  formId: string;
  apiUrl?: string;
}) {
  const apiUrl = useAifeaturesApiUrl(explicitApiUrl);
  return (
    <form action={`${apiUrl}/f/${formId}`} method="POST">
      <input name="email" type="email" required />
      <AifeaturesCaptcha formId={formId} apiUrl={explicitApiUrl} />
      <button type="submit">Send</button>
    </form>
  );
}

Pass base URLs without a trailing slash. apiUrl="" explicitly selects root API paths. Opted-in production hosts and previews must serve /_ls through site-services; local development needs that same trusted forwarding path. The provider changes request destinations only. Payment redirects and Turnstile's external script retain their existing behavior; a failed submission does not fall back to a different API origin.

ConsentProvider has independent configuration: its existing default remains /_ls, even without AifeaturesProvider, and its own explicit apiUrl still wins.

Product reviews

The review components are part of the pending minor release after 0.9.0 (expected 0.10.0). They are available in this workspace build; verify the published package exports before upgrading a customer site or adding imports to it.

Mount one composer on the product page, using the actual product ID:

import { ProductRating, ProductReviews, ProductReviewForm } from '@aifeatures/react';

function Reviews({ productId }: { productId: string }) {
  return <>
    <ProductRating productId={productId} />
    <ProductReviews productId={productId} />
    <ProductReviewForm productId={productId} />
  </>;
}

These components use the same optional apiUrl and AifeaturesProvider resolution as the other features. ProductRating is useful beside a product title; ProductReviews includes its own summary, distribution, filters, pagination, and merchant replies. Confirmed customer mutations refresh subscribed widgets for the same product. Public text is rendered as escaped text. No customer email or order identifiers are displayed.

Keep AccountPage mounted at the published /account route. The composer starts email sign-in; AccountPage exchanges the code, saves the existing same-origin customer session, and returns to the validated product route at #product-review-form. Account review history provides product links and withdrawal, without a second editor. Sign-in must finish on the store's canonical serving origin: apex, www, and secondary domains do not share browser sessions or drafts.

The server checks store readiness, site collection settings, production Turnstile keys, and the global collection switch. Pass preview to ProductReviews and ProductReviewForm in previews; the server also refuses preview writes. A disabled store setting pauses new reviews and resubmissions while preserving public reviews, withdrawal, and protected editing on active product pages. A global collection pause stops all content writes. A verified-purchase badge is derived from historical purchases of that exact product at that store; it is never supplied by the browser.

The composer owns its fields, JSON requests, validation, and draft recovery. Drafts are scoped to the authenticated identity and product and expire after 24 hours in the current browser. Expired or failed CAPTCHA challenges preserve the draft. An uncertain write triggers a saved-review read before another attempt; a stale revision requires the customer to review the latest state.

Only the package-internal Turnstile widget lifecycle and server verification adapter are shared with contact forms. AifeaturesCaptcha retains its formId configuration, hidden response field, consent behavior, and /f/:formId contracts. Reviews use /v1/p/... and /v1/account/... JSON endpoints. Future booking features can reuse the small CAPTCHA adapter while owning their own availability, capacity, reservation, calendar, and notification behavior.

See product-review operations and release gates before enabling collection.

Consent runtime

ConsentProvider requests /_ls/v1/geo and /_ls/v1/prefs/{configId} on the current site's origin by default. Published sites, shared/editor previews, and local development must serve those two paths through site-services. No hostname is needed in generated site code:

<ConsentProvider configId={consentConfigId}>
  <App />
  <ConsentBanner />
</ConsentProvider>

An explicit apiUrl still selects another base URL, including the existing public API and Storybook's mock hosts. Response bodies must match the consent contract, including country, region, and shape on geo responses. The legacy country-only geo response is invalid and causes a strict fallback. Additive response headers are diagnostic information; missing or unfamiliar versions do not override a valid body.

Inspect a decision in DevTools

Run this in the browser console after the provider mounts:

window.__LS_CONSENT_DEBUG__()

The function returns copies of current snapshots for up to ten active providers, identified by the site's configId. It accepts no commands, does not change consent, and removes a provider's snapshot on unmount. Nothing is persisted or sent anywhere. Snapshots contain no country, region, IP, saved grants, visitor identifier, or request/response body.

Each snapshot includes:

  • mode and final shape. Both are null on a returning visitor's zero-request path because the saved choice applies without resolving geography or owner mode.
  • reason: unresolved, stored-choice, renewal-required, geo, unknown-geo, strict-everywhere, off, prefs-fallback, or geo-fallback. Saved choices and renewal take precedence over a previously resolved verdict.
  • storedChoice: unread, none, valid, revision-mismatch, taxonomy-version, expired, unverified-mirror, or malformed; renewalRequired indicates when an unreadable or outdated choice forces opt-in. Genuine expiry uses the existing first-visit policy.
  • requests.prefs and requests.geo: outcome is not-requested, pending, success, timeout, http-error, network-error, or invalid-contract. Each reports httpStatus, version (X-LS-Consent-Version), requestId (X-LS-Request-Id), and geoSource (cloudflare, unknown, or null). Missing headers are null; version and request ID values are limited to 128 characters.

Requests have a two-second deadline covering headers and body. Failures produce a console warning with the safe request diagnostics. Config failure uses the conservative strict fallback; geo failure uses the existing conservative verdict under the resolved owner setting. A valid saved choice still makes zero automatic requests. Opening preferences on a legacy installation without a baked policy path may fetch display config; that request is also reported.

This mechanism is for troubleshooting in DevTools. Application integrations should continue to use useConsent() and the shared consent components.