@efcaquila/sites
v0.2.1
Published
EFC websites middleware: wires a bespoke site's lead forms to the Aquila Edge rail (store-and-forward lead capture with first-touch marketing attribution).
Readme
@efcaquila/sites
The Websites-by-EFC middleware: wires a bespoke site's lead forms to the Aquila Edge rail — store-and-forward lead capture (leads are never lost to Aquila downtime) with Edge-side abuse controls (per-site origin allow-list, honeypot, optional Cloudflare Turnstile) and first-touch marketing attribution (utm params, gclid/fbclid, referrer, landing page, GA4 ids) riding every lead into Aquila, where it lands on the member's metadata.
Replaces the per-site Netlify function / shared Supabase submit-lead rail: same guarantees, one
platform, no server-side secrets in the site at all — the site ships only its public embed token.
Install
npm install @efcaquila/sitesThe two values you need
Both come from the site's Website tab in Aquila → Launch → Forms handover (EFC staff read them there and pass them on; each has a copy button):
| configure() key | Shown on the tab as | What it is |
| ----------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| edge | Forms address | The Aquila Edge base URL the forms submit to. Per environment, not per site — EFC supplies the one for the environment the site is going into. |
| site | Embed token | The site's identifier. Per site. A site that has never been built or uploaded has none yet — EFC staff mint one from the same panel before your first upload. |
Neither value is a secret (a live site's own pages carry both), so they can travel by email. The token is baked into your build: if EFC ever rotates it, your forms stop until you rebuild with the new token and the new zip is uploaded.
Usage
// once, at app start (also captures the visitor's first-touch attribution):
import { configure } from "@efcaquila/sites";
configure({
edge: "<forms address>", // from the Website tab — per environment
site: "<embed token>", // from the Website tab — per site
});// in the form submit handler:
import { submitLead } from "@efcaquila/sites";
const result = await submitLead(
"contact", // or "trial"
{
firstName: values.firstName,
lastName: values.lastName,
email: values.email,
phone: values.phone,
message: values.program ? `Programme: ${values.program}` : undefined,
_hp: values.company, // bind a HIDDEN input — a filled value silently drops the bot
},
{ turnstile: turnstileToken } // when Turnstile is enabled on the form
);
if (result.success) {
// stored durably at the Edge; forwarded into Aquila out of band (result.reference for support)
} else if (result.error === "invalid") {
// result.fieldErrors: field → message — the one refusal the visitor can fix
}Field names to avoid
The Edge treats a non-empty value in any of these as a bot filling a hidden input, answers with a fake success and stores nothing — so never give a visible input one of these names:
_hp— the honeypot. Bind your hidden input to this one.honeypot
Exported as HONEYPOT_FIELDS for a lint or a test. Everything else rides along verbatim (a
website field, for instance, is a perfectly good lead field and is stored with the lead).
Migration notes (from the Netlify / Supabase rail)
- Attribution carries over: this package uses the same
efc_attribution_v1localStorage key and field names as the existingattribution.js, so visitors who landed before the migration keep their first-touch record. Remove the<script src="/attribution.js">tag — the package owns capture now. - Turnstile: keep rendering the widget; pass the token via
options.turnstile. Verification moves to the Edge. - Honeypot: keep the hidden field; send its value as
fields._hp. - No env vars / API keys: delete
AQUILA_API_KEYetc. The embed token is public; abuse is handled by the Edge's origin allow-list + Turnstile + rate limiting. - Enquiries are only accepted from the site's own addresses — its draft preview address, its
published address, and its custom domain once verified. A form tried from your own machine is
refused (
error: "rejected"), so test by uploading the build and submitting from the draft preview address. - The Aquila upload endpoint detects this package in a built bundle and shows staff whether an uploaded site is wired to the lead rail.
Publish
npm run build emits dist/; npm test runs the suite (it pins the SDK marker to
package.json's version — bump both together). Publish from this folder with npm publish.
