@agentshop/seo) has zero runtime dependencies and works in any server environment with global fetch (Node ≥ 18, edge runtimes, workerd). All fetch helpers follow the never-throw contract: null on any failure, never an exception.
Fetch helpers
openGraph.type is "website".
Organization + WebSite + optional FAQPage).
{ scriptUrl, token, eventsUrl, shopDomain }. Resolve it per render rather than storing the values: the pixel token can change, and one frozen in a deployed build stops working silently. See Storefront analytics.
Request and reports it if it’s a crawler — the whole recipe in one call: the isbot gate, stripping url and referrer to origin + pathname, and the POST. Resolves false when the request isn’t a bot, the URL is unreportable, or delivery failed; it never throws and never blocks the response.
Pass statusCode on a runtime that performed the fetch itself (a Cloudflare Worker). The Next.js proxy wrapper omits it deliberately — see AI crawler visibility.
Meta builders
meta descriptor array from a bundle: title, description, canonical, Open Graph (including og:type=product), Twitter tags, and the JSON-LD via a "script:ld+json" descriptor. Returns [] for a null bundle.
For the bundle → Next.js Metadata mapper, use toNextMetadata. It is not a core export — it ships only from @agentshop/seo/next, so non-Next consumers (Remix, Hydrogen, plain Node) aren’t forced to install next to typecheck.
Serialization
<script type="application/ld+json"> tag, escaping every < to its JSON unicode form (\u003c) so a data field containing </script> (product titles, vendor names, FAQ answers…) can’t break out of the script block. The result is still valid JSON. Always use this instead of JSON.stringify when writing your own script tags — see Security.
Validation
Types
SeoClientConfig is documented in Configuration.
