@pyrx/synapse-browser
v0.4.0
Published
PYRX Synapse Browser Tracking SDK — lightweight client-side event tracking, Web Push (VAPID), In-App Messaging, and the optional in-app UI Kit (@pyrx/synapse-browser/ui)
Maintainers
Readme
Synapse CEP — JavaScript Tracking + Web Push SDK
Lightweight client-side SDK for tracking user events and (Phase 9.1+) subscribing browsers to Web Push notifications in the Synapse Customer Engagement Platform.
Size: ~22 KB minified (event tracking + Web Push surface)
Browser support (Web Push, Phase 9.1): Chrome ≥ 50, Firefox ≥ 44, Edge ≥ 17, Safari macOS ≥ 16. iOS Safari requires a PWA install + iOS 16.4+ — verification deferred to a 9.1.x follow-up.
Installation
Script Tag (recommended)
<!-- Synapse Tracking SDK -->
<script>
!(function () {
var s = (window.synapse =
window.synapse ||
function () {
(s.q = s.q || []).push(arguments);
});
var e = document.createElement("script");
e.type = "text/javascript";
e.async = true;
e.src = "https://synapse-events.pyrx.tech/sdk/v1/synapse.min.js";
var x = document.getElementsByTagName("script")[0];
x.parentNode.insertBefore(e, x);
})();
synapse("init", { apiKey: "psk_prod_YOUR_API_KEY" });
synapse("page");
</script>npm (for bundlers)
pnpm add @pyrx-crm/sdkimport { synapse } from "@pyrx-crm/sdk";
synapse("init", { apiKey: "psk_prod_YOUR_API_KEY" });Usage
Identify Users
Call after login to associate events with a known user:
synapse("identify", "user-123", {
email: "[email protected]",
first_name: "Jane",
last_name: "Doe",
plan: "pro",
});Track Events
synapse("track", "cart.abandoned", {
product: "Widget",
price: 29.99,
currency: "USD",
});
synapse("track", "feature.used", {
feature: "export_csv",
rows: 1500,
});Page Views
// Automatically captures URL, path, title, referrer
synapse("page");
// With custom properties
synapse("page", { section: "pricing" });Reset (on logout)
synapse("reset");Web Push (Phase 9.1)
Five new commands on the same facade subscribe a browser to Web Push and manage the subscription. The full setup guide ships in PR-3 — what follows is the SDK-side API surface.
Prerequisites
- Generate a VAPID credential in Dashboard → Settings → Push Credentials → VAPID (PR-3 surface). Copy the public key.
- Copy
node_modules/@pyrx/synapse-browser/dist/synapse-sw.jsto your site's public directory (PR-3 ships the file). Add topostinstallif you want automatic updates:{ "scripts": { "postinstall": "cp node_modules/@pyrx/synapse-browser/dist/synapse-sw.js public/synapse-sw.js" } } - Call
synapse('identify', userId)before subscribing — Web Push requires an identified user so the backend can associate the device with a contact.
enablePush — register the SW + prepare state
Safe to call from any lifecycle hook (no user gesture required):
synapse("enablePush", {
vapidPublicKey: "BPV9...", // from the dashboard
serviceWorkerPath: "/synapse-sw.js", // default
serviceWorkerScope: "/", // default
});On browsers without Service Worker / PushManager / Notification support (e.g. private windows, Safari iOS without PWA install), the call logs a one-line warning and resolves cleanly — your code never crashes.
requestPermission — show the permission prompt + subscribe
Must be called from a user gesture handler (click, tap). Browsers silently deny otherwise.
button.addEventListener("click", async () => {
try {
const subscription = await synapse("requestPermission");
console.log("Subscribed:", subscription.endpoint);
} catch (err) {
if (err.code === "permission_denied") {
// User said no — show your own UI rationale.
}
}
});disablePush — unsubscribe + delete device server-side
synapse("disablePush");Idempotent. Always succeeds locally even if the backend DELETE fails.
getSubscription — inspect current state
const sub = await synapse("getSubscription");
// → { endpoint, p256dh, auth, permission, deviceId } | nullsetPushPreferences — per-device preferences
synapse("setPushPreferences", {
quiet_hours: "22:00-07:00",
mute_channels: ["promo"],
});Stored on the backend devices.metadata JSONB column — schema is tenant-defined.
Error types (TypeScript)
import {
SynapsePushUnsupportedError,
SynapsePermissionDeniedError,
SynapsePushNotEnabledError,
SynapseUserNotIdentifiedError,
SynapseServiceWorkerError,
SynapseDeviceRegistrationError,
} from "@pyrx/synapse-browser";Every push error has a stable .code string (push_unsupported, permission_denied, etc.) safe to switch on.
In-App UI Kit (@pyrx/synapse-browser/ui)
The core SDK delivers in-app messages to a rendering callback and does not
draw UI — your app owns the pixels (see synapse('inApp.show', ...)). If you
just want a sensible default, the optional UI Kit ships pre-built, themeable
modal + banner renderers. It's a separate subpath export, so if you don't import
it, it adds zero bytes to your bundle (tree-shaken).
5-line integration
import { synapse } from "@pyrx/synapse-browser";
import { attachUIKit } from "@pyrx/synapse-browser/ui";
synapse("init", { apiKey: "psk_prod_xxx" });
synapse("identify", "user-123");
attachUIKit(synapse, { placement: "home_modal", layout: "modal" });attachUIKit registers an inApp.show callback that renders every fresh
message on placement with the chosen layout ('modal' or 'banner'),
wired to the SDK's dismiss + CTA telemetry automatically. It returns the
SDK's unregister function.
Direct render (full control)
Call the renderers yourself inside your own inApp.show callback:
import { renderModal, renderBanner } from "@pyrx/synapse-browser/ui";
synapse("inApp.show", "home_modal", (message) => {
const handle = renderModal(message, {
theme: { accent: "#7c3aed" }, // optional --pyrx-ui-* overrides
onDismiss: (id, reason) => {
/* … */
}, // optional; defaults to the SDK
onCtaClick: (id, ctaId) => {
/* … */
}, // optional; defaults to the SDK
});
// handle.element — the mounted root; handle.close() — remove it.
});Theming
The kit is self-contained (no web fonts, no CDN, no external assets) and
namespaces every style under .pyrx-ui, so it won't collide with your site.
Re-theme via options.theme (per-instance) or by setting --pyrx-ui-* CSS
variables globally. Overridable tokens: accent, accentHover,
accentForeground, surface, foreground, foregroundMuted, border,
backdrop, radius, fontFamily, zIndex. Default accent is the PYRX teal
#1d9e75.
Accessibility & security
- Modal:
role="dialog"+aria-modal, focus trap, Escape-to-dismiss, backdrop-click-to-dismiss, focus restored on close. - Banner:
role="status", non-blocking (no focus trap, no Escape). - All message text is rendered via
textContent(neverinnerHTML), and every URL sink (imagesrc, CTA navigation) is funnelled through one stricthttp(s)/relative-only sanitizer — ajavascript:/data:URL in tenant-authored content can never execute on your origin.
The kit is desktop-first but ships responsive CSS since it embeds in arbitrary sites. Scope: modal + banner (tooltip / carousel / inbox are not included).
Configuration
synapse("init", {
apiKey: "psk_prod_xxx", // Required
endpoint: "https://synapse-events.pyrx.tech", // Default
flushInterval: 5000, // ms between flushes (default: 5s)
flushSize: 10, // events before auto-flush (default: 10)
debug: false, // Console logging (default: false)
});How It Works
- Events are queued in memory and persisted to
localStorage - Queue flushes every 5 seconds or when 10 events accumulate
- Events are sent as a batch to
POST /v1/events/batch - On page unload,
sendBeaconensures delivery - Failed flushes retry with exponential backoff (1s, 2s, 4s)
- Queue survives page refreshes via
localStorage
