@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:
modeand finalshape. Both arenullon 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, orgeo-fallback. Saved choices and renewal take precedence over a previously resolved verdict.storedChoice:unread,none,valid,revision-mismatch,taxonomy-version,expired,unverified-mirror, ormalformed;renewalRequiredindicates when an unreadable or outdated choice forces opt-in. Genuine expiry uses the existing first-visit policy.requests.prefsandrequests.geo:outcomeisnot-requested,pending,success,timeout,http-error,network-error, orinvalid-contract. Each reportshttpStatus,version(X-LS-Consent-Version),requestId(X-LS-Request-Id), andgeoSource(cloudflare,unknown, ornull). Missing headers arenull; 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.
