@builtonveya/analytics-web
Veya Analytics SDK for the web. Auto-detects page types, product impressions and cart changes on restaurant ordering sites, and ships events to the Veya analytics pipeline in batches.
Shares its core with @builtonveya/analytics-react-native (same event taxonomy, same version line).
Install
npm / yarn / pnpm
npm install @builtonveya/analytics-web
import veya from '@builtonveya/analytics-web';
await veya.init({ tenantSlug: 'your-tenant-slug' });
Script tag (the package ships a self-contained browser bundle that sets window.veya)
<script src="https://cdn.jsdelivr.net/npm/@builtonveya/analytics-web@4/dist/veya-analytics.min.js"></script>
<script>
veya.init({ tenantSlug: 'your-tenant-slug' });
</script>
Pin the major (@4) for automatic patch updates, or an exact version (@4.0.0) for a frozen build.
Configure
await veya.init({
tenantSlug: 'your-tenant-slug', // required
endpoint: 'https://analytics.builtonveya.com/v1/events', // the default
storeId: 'store-81', // if known at init; otherwise call veya.setStore(id, name)
debug: false,
autoDetect: true, // page-type detection on navigation
autoTrackProducts: true, // product impressions
autoTrackCart: true, // cart change detection
interceptOloApi: false, // opt-in: patches window.fetch to read Olo order responses
batchSize: 10,
flushInterval: 5000,
sessionTimeout: 1800000, // 30 minutes
});
Track
Everything the core SDK offers is on the default export: track, pageView, productView,
productClick, addToCart, removeFromCart, updateCartQuantity, setCart, checkoutStart,
checkoutStep, addPaymentInfo, checkoutComplete, loginAttempt, loginSuccess, loginFailure, logout, identify,
storeSelect, setStore, search, loyaltyLogin, rewardView, rewardApply, rewardRemove, promoApply,
promoRemove, setExperiment, error, flush, getSessionId, getVisitorId, getCustomerId.
veya.setStore('81', 'Earthbar Flagship - West Hollywood');
veya.addToCart({ id: 'olo:item:106702642', name: 'The Workout Smoothie', price: 12.95 }, 1);
veya.checkoutComplete({ order_id: '46749…', total: 12.95, items: [...] });
init() can be called more than once: calls made while one is in flight share it, and later calls
resolve straight away, so React StrictMode's double effects are harmless.
Automatic events
| Event | When | Properties |
|---|---|---|
session_start | Once per new session — not again when a stored session is resumed (a full page load within the 30-minute timeout) | platform |
page_view | On load, on client-side navigation, and when the page comes back from the browser's back/forward cache, only with autoDetect: true (the default). With autoDetect: false, call veya.pageView() yourself (a restore fires pageshow with event.persisted === true) | page_url, page_title, page_type, referrer, utm_* |
page_hidden / page_visible | The tab is hidden / shown again | page_hidden: time_on_page (ms since the last page view or back/forward-cache restore); page_visible: none |
scroll_depth | The page is scrolled past 25 / 50 / 75 / 100% — each once per page view, reset by every page view and by a back/forward-cache restore; pages that don't scroll report nothing | depth |
page_unload | The page is left (pagehide): full navigation, tab close, or going into the back/forward cache | time_on_page; persisted — true when the page went into the back/forward cache (it may be restored) rather than being unloaded |
error | Uncaught errors and unhandled promise rejections | error_type, error_message, … |
Properties named exactly email, phone, password, ssn, credit_card, cvv, address or
street are dropped from every event (and from identify() traits) as a backstop against sending
personal data by mistake. Only those exact top-level keys: don't rely on it to catch
delivery_address, streetAddress or nested objects. ZIP / postal codes are allowed: send
address_postal_code (5-digit ZIP) for delivery-area analysis, never the street address.
Changes in 4.0.4
- Calling
identify(),loginSuccess(),logout()orsetExperiment()beforeinit()no longer stores an empty session id, and a stored session without an id is never resumed. Before, every event in such a session was rejected for a missingsession_id. Their effect on login state and experiments survives only wheninit()starts a new session, so still call them afterawait veya.init().
Changes in 4.0.3
- The PII backstop no longer drops
postal_codeandzip— only the street-leveladdressandstreet(plus the existing email / phone / password / SSN / card / CVV keys). A ZIP is coarse enough to keep, and it's what delivery-area analysis runs on; 4.0.2 dropped it along with the street address.
Changes in 4.0.2
page_unloadis sent onpagehideinstead ofbeforeunload. Abeforeunloadlistener keeps Firefox from putting the page in its back/forward cache, so Back reloaded the page instead of restoring it;pagehidedoesn't, and mobile browsers fire it more reliably when a tab is closed. New propertypersisted:truewhen the page went into the back/forward cache rather than unloading.- A page restored from the back/forward cache restarts
time_on_pageand thescroll_depththresholds, and sends apage_viewwhenautoDetectis on. Before,time_on_pageafter Back counted from the original load and scroll depth never fired again. - The PII backstop also drops
address,street,postal_codeandzip(4.0.3 keeps the ZIP keys again).queryis deliberately not on the list: search events use it for what was typed. - Removed the
aiEndpointanduseAiClassificationoptions: nothing read them, andaiEndpointdefaulted to a host that doesn't resolve. TypeScript code that still passes them gets an excess-property error; delete them.
Changes in 4.0.1
session_startis sent (it was dropped becauseinit()tracked it before marking itself initialized), once per new session.- No automatic
page_viewwithautoDetect: false— 4.0.0 sent one on every load in addition to the app's own. - New automatic events:
page_hidden/page_visibleandscroll_depth(see above). - New method:
addPaymentInfo(paymentMethod, properties?)→add_payment_infowith{ payment_method }at funnel stepcheckout_payment(4). - Unloading the page sends
page_unloadinstead ofsession_end(the session carries on across full navigations). - Defaults:
endpointis the livehttps://analytics.builtonveya.com/v1/events(4.0.0 pointed at a host that doesn't resolve);interceptOloApiis now opt-in. - Concurrent
init()calls share one run instead of both setting up listeners and sendingsession_start.
Server-side rendering
Importing the package on the server is safe: module load only registers window.veya when a
window exists, and nothing touches the DOM until init() runs. Call init() from a client-side
effect.
Build & publish
cd sdk/web
npm ci
npm run build # dist/esm (ESM + types), dist/cjs, dist/veya-analytics[.min].js
npm run smoke # loads all three outputs and checks the public surface
npm publish # prepublishOnly runs build + smoke; scope @builtonveya is public
Bump version in package.json in step with the React Native package when the shared core changes.