Web
Release history for the Swan Web SDK.
[web/1.1.1] — 2026-06-25
Distribution: npm — @loyalytics/swan-web-sdk@1.1.1
Changed
- Device registration resolves the device identifier more robustly from the registration response. The SDK now reliably obtains the device identifier when registering, keeping it consistently available for events and attribution.
[web/1.1.0] — 2026-06-22
Distribution: npm — @loyalytics/swan-web-sdk@1.1.0
Changed
- Silent push notifications no longer display a notification. A push delivered with the silent flag is now received and acknowledged in the background without showing a notification to the user, matching the iOS and Android SDKs. Non-silent pushes are unchanged.
Fixed
- Logging out now starts a fresh anonymous identity. When a user who first used the app anonymously and later signed in subsequently logs out, the SDK begins a new anonymous identity instead of reusing the one that had been merged into their account on sign-in. This prevents activity after logout from being attributed to the signed-out user.
[web/1.0.3] — 2026-05-26
Distribution: npm — @loyalytics/swan-web-sdk@1.0.3
Changed
- Service Worker delivery + click ACKs now POST to
/web-push-trackinginstead of/mobile-push-tracking. Web push events are routed to a dedicated webhook route on the Swan backend, giving per-channel telemetry (web vs mobile push delivery / open rates) without conflating them on shared dashboards. Functionally equivalent for end users — the SW still POSTs the same payload shape; only the URL changes.
Documentation
- Stale init snippet in the Push Notifications guide (which still showed
vapidPublicKey+serviceWorkerUrlas required init args) replaced with the current minimal init shape:Swan.initialize('your-app-id', { production: true }). The SDK reads VAPID from the device-register response;serviceWorkerUrldefaults to/swan-service-worker.js.
[web/1.0.2] — 2026-05-26
Distribution: npm — @loyalytics/swan-web-sdk@1.0.2
Fixed
Swan.unsubscribePush()now actually clears the subscription on the Swan backend. Pre-1.0.2 the SDK POSTed the full subscription object alongsidestatus: "revoked", which the backend'ssetPushSubscriptionhandler treated as a subscribe (re-setting/subscriptionto its current value). The customer's push state on Swan stayed "subscribed" even though the browser had revoked the subscription locally — meaning a tenant who'd opted out would continue to be a candidate for push sends until manually wiped. The SDK now sendssubscription: null(the documented unsubscribe sentinel perspec/wire/push-subscription.yaml), so the backend's unsubscribe branch fires correctly.
[web/1.0.1] — 2026-05-26
Distribution: npm — @loyalytics/swan-web-sdk@1.0.1
Browser floor: Chrome 90+ · Firefox 90+ · Safari 14+ · Edge 90+
Added
- Backend-served VAPID public key. Host apps no longer need to copy/paste a VAPID public key into
Swan.initialize(...). The SDK reads it from thePOST /v2/device/registerresponse (webPush.vapidPublicKeyfield) and uses it transparently.SwanConfig.vapidPublicKeyremains as an optional override for test rigs or environments where the backend hasn't yet served the field. Swan.resyncSubscription()— re-POSTs the current browser push subscription to the Swan backend without re-prompting for permission or re-subscribing. Use when the backend's stored copy of the subscription is suspected stale.SwanConfig.pushNotifications.enabled— master gate for the Web Push subsystem (defaulttrue, opt-out). When disabled,Swan.subscribeToPush()returnsfalseimmediately without registering the Service Worker or prompting. Matches the iOS and Android SDK shape.SwanConfig.location.enabled— opt-in flag for the location-tagging surface (defaultfalse).Swan.isLocationEnabled()now reflects this flag (previously reflected whetherupdateLocation()had been called). Matches the iOS and Android SDK shape.- VAPID rotation handling. When the tenant's VAPID public key changes between sessions, the SDK detects the rotation, force-unsubscribes the now-dead existing browser subscription, and re-subscribes with the new key — automatically.
pushsubscriptionchangeService Worker handler. When the browser invalidates a subscription on its own (gateway-side rotation, browser internals), the SDK re-subscribes from the SW + re-POSTs to the backend automatically. Previously the SW was a no-op and the user went silent until the host page next calledsubscribeToPush().keyValuePairsauto-parse.addNotificationOpenedListenerreceiveskeyValuePairsas an object regardless of whether the wire payload encoded it as JSON-stringified text or a native object.
Documentation
- Subscription idempotency contract documented in the Web getting-started guide — including the recommended
isPushSubscribed()gate pattern. - Per-browser web-push coverage rendered as a proper table.
- Roadmap-era "Planned API" callouts removed from the per-feature guides; each Web tab now shows working code.
Notes
- 1.0.0 was published less than a day before 1.0.1; we treat this as a same-cycle correction. Customers on 1.0.0 should upgrade.
[web/1.0.0] — 2026-05-19
Distribution: npm — @loyalytics/swan-web-sdk@1.0.0
Browser floor: Chrome 90+ · Firefox 90+ · Safari 14+ · Edge 90+
Bundle: ~52 kB raw / ~12 kB gzipped (CJS) · zero runtime dependencies
Added
-
First customer release of the Swan Web SDK. Identity, event tracking, and offline-resilient delivery for browser-based host apps. The public surface is a namespace-exported
Swanobject plus aSwanEventshelper namespace, importable as either named or default. -
Identity flow —
Swan.identify(id, attrs?)(fire-and-forget),Swan.login(id, attrs?)(async, returns{ cdid, profileSwitched }),Swan.logout()(clears local state even on network failure),Swan.addSwanIdentifierChangedListener(cb). Pre-CDID-swap queue flush is a hard invariant: events queued before login are POSTed under the anonymous CDID. -
Event tracking —
Swan.track(name, attrs?),Swan.screen(name, attrs?),Swan.flush(),Swan.getQueueSize(). Every event is auto-enriched with platform, deviceId, sessionId, country, currency, businessUnit, currentScreenName. Wire format is byte-stable with the Android, iOS, and React Native SDKs (LZW64-compressed). -
Super-properties —
setCountry,setCurrency,setBusinessUnit,setCurrentScreenName. Stamped on every subsequent event'sdatapayload. -
Semantic e-commerce helpers (
SwanEvents) — 33 typed methods covering discovery, cart, wishlist, checkout, order, account, marketing, and lifecycle. RN-typo wire names preserved verbatim (productAddedToaddTocart,orderExperianceRating) for cross-platform wire stability. -
Profile enrichment —
Swan.enrichProfile(attrs)queues aPROFILE_ENRICHrow that routes to/v2/customer/enrich-profile(separate from the/v2/trackEventbatch).CDIDis resolved at flush time fromcurrentCDID(logged in) orgeneratedCDID(anonymous). -
Offline-resilient queue — events persist to
localStorage(keyswan-sdk.v1.queue) on every enqueue / dequeue, so pending events survive a page reload or tab restore. Queue capmaxQueueSize=5000with drop-oldest overflow. Survives corrupt JSON, quota-exceeded, and SSR-style environments without throwing. -
Network resilience — each queue row carries a
retryCountand lifecyclestatus(pending/sending/failed). Failed flushes increment retryCount; atMAX_RETRIES=3the row flips terminal. Retry delays follow exponential backoff (2s, 4s, 8s). -
Stale-sending recovery + 7-day cleanup — on
initialize(), rows stuck insendingfor >5 minutes (presumed crashed mid-flush) revert topending; failed rows older than 7 days are pruned. -
Auto-flush on network restore —
window.onlinetriggers a queue flush, so reconnecting drains pending events without waiting for the next periodic interval. -
Foreground / background lifecycle —
document.visibilitychange → hiddenforce-flushes the queue (web equivalent of mobile background).SwanEvents.appLaunched()is the host-callable foreground hook. -
Self-telemetry lifecycle events —
addInitializedListener,addDeviceRegistrationFailedListener. The SDK emits these to the host app but does NOT upload self-telemetry to the backend. -
Session ID — UUID v4, attached to every event's
data.sessionId, rotates after 20 minutes of inactivity. Surface:Swan.getCurrentSessionId(). -
Device registration —
POST /v2/device/registeroninitialize()with credential caching inlocalStorage, automatic retry onwindow.online, and per-failure classification (network/timeout/http/shape/unknown) surfaced throughaddDeviceRegistrationFailedListener.
Notes
- The SDK is initialized synchronously and returns within 100 ms; device registration runs in the background. Calls to
Swan.track/Swan.identifybefore registration completes are queued, not dropped. - Web Push —
Swan.subscribeToPush()/unsubscribePush()/isPushSubscribed()register a Service Worker and subscribe via the browser's Push API. The bundled SW (@loyalytics/swan-web-sdk/sw) handlespush(showNotification + delivery ACK),notificationclick(click ACK + deeplink routing), and bridges toaddNotificationOpenedListener/addDeepLinkOpenedListeneron the host page. Host supplies the per-tenant VAPID public key viaSwanConfig.vapidPublicKey. SeeBROWSER_SUPPORT.mdfor the per-browser matrix and the manual smoke checklist. - No runtime dependencies. The SDK ships zero
dependencies/peerDependencies/optionalDependencies— browserfetch+ DOM +localStorageonly.