The package
Two entry points matter to an Astro app: @geoffy/headless/astro for the framework
adapters, and @geoffy/headless for the framework-agnostic client underneath them.
Options every helper takes
Section titled “Options every helper takes”interface GeoffyClientOptions { siteKey: string; // from Geoffy settings; public, addresses rather than authorises origin?: string; // overrides GEOFFY_ORIGIN and the default, for this call timeoutMs?: number; // how long before giving up and rendering without Geoffy revalidateSeconds?: number; // Next.js only; accepted and ignored in Astro}timeoutMs is deliberately short: the fallback — render without the widget — is cheap,
while a stalled build is not.
@geoffy/headless/astro
Section titled “@geoffy/headless/astro”getGeoffyProductMarkup(options, handle, extra?)
Section titled “getGeoffyProductMarkup(options, handle, extra?)”const geoffy = await getGeoffyProductMarkup( { siteKey }, handle, { canonicalUrl, locale },);extra takes canonicalUrl and locale, both optional. locale is the language this page
renders, for example sr or de-DE. Only the language part is compared, so en-GB content
matches an en-US page. A value that is not a language tag is treated as unknown, and the
helper returns the content.
Resolves to null, or to:
interface GeoffyProductMarkup { jsonLdScript: string; // a complete <script type="application/ld+json"> element widgetHtml: string; // the visible widget, styles included artifact: GeoffyProductArtifact;}Both strings are pre-rendered HTML, so both need set:html. null covers every case —
nothing published, Geoffy unreachable, an error, a locale in a different language from the
published content, or a canonicalUrl that does not match — so your {geoffy && …} guards
handle every one of them.
artifact is the raw data behind the two strings, if you want to render your own markup
instead.
Pages with nothing to show cost no request
Section titled “Pages with nothing to show cost no request”The helper reads a small list of the products Geoffy has published, with the language and the
path of each, and keeps it in memory for 30 seconds. With that list, it returns null for an
unpublished product, a page in another language, or a page whose path is not the published
one, without asking Geoffy about the product. A newly published product appears within
those 30 seconds, or at your next build on a static site. There is nothing to configure.
If the list cannot be read, the helper asks about the product directly, as older versions do. A problem on Geoffy’s side never hides a published product.
createGeoffyTextEndpoint(options, name)
Section titled “createGeoffyTextEndpoint(options, name)”export const GET = createGeoffyTextEndpoint({ siteKey }, "llms.txt");name is "llms.txt", "llms-full.txt" or "agents.md". It refuses
"robots-rules.txt": that is a fragment you append to your own file, not a whole file to
serve. Answers 503 with Retry-After when Geoffy is degraded, never a fabricated 404.
createGeoffyProxyEndpoint(options)
Section titled “createGeoffyProxyEndpoint(options)”export const prerender = false;export const GET = createGeoffyProxyEndpoint({ siteKey });The /apps/geoffy/[...path] catch-all. prerender = false is required — without it
astro build fails outright. Query strings are not forwarded.
@geoffy/headless
Section titled “@geoffy/headless”The framework-agnostic client. You will use fetchGeoffyText for
robots.txt; the rest is what another framework’s adapter would be
built on.
| Export | Signature | Notes |
|---|---|---|
fetchGeoffyText | (options, name) => Promise<string | null> | null means unreachable. Accepts "robots-rules.txt", which the endpoint factory does not |
fetchGeoffyProduct | (options, handle) => Promise<GeoffyProductArtifact | null> | The raw artifact. null also when nothing is published for the handle |
serializeJsonLd | (node: unknown) => string | Escapes < safely for inline script |
compareCanonical | (page, artifact) => "match" | "mismatch" | "unknown" | Path only, not origin |
handleGeoffyProxy | (options, requestUrl) => Promise<Response> | What the proxy endpoint wraps |
resolveProxyPath | (requestUrl) => string | null | null for anything outside the namespace |
DEFAULT_GEOFFY_ORIGIN | "https://api.geoffy.ai" | |
PROXY_PREFIX | "/apps/geoffy" | The mount path is fixed; see the namespace |
GeoffyProductArtifact
Section titled “GeoffyProductArtifact”interface GeoffyProductArtifact { handle: string; canonicalUrl: string; // the URL Geoffy published against jsonLd: unknown; // schema.org Product node widgetHtml: string; // styles included — do not sanitise them out publishedAt: string | null;}Not published yet
Section titled “Not published yet”When your site is verified but nothing is published for a handle, Geoffy answers with a
successful { "handle": "blue-widget", "published": false }, not an error.
fetchGeoffyProduct returns null for it, so your guards render nothing.
Runtime
Section titled “Runtime”Node 18 or newer — the package uses global fetch, AbortController and crypto.subtle.
ESM only. React is an optional peer dependency and is not needed for the /astro
entry point.