JITSU-135 feat(console): add Microsoft Clarity and Hotjar device destinations - #1424
Conversation
…inations
Both are client-side session-analytics tools, so they run in the browser via
the @jitsu/js SDK as internal device plugins, following the logrocket/ga4-tag/gtm
pattern.
- clarity: identify -> clarity('identify') + traits as custom tags; track ->
clarity('event') + optional properties as tags; cookie-consent option.
- hotjar: identify -> hj('identify', userId, attrs); track -> hj('event')
(name sanitized); opt-in page -> hj('stateChange') for SPAs.
Wires the id across the console catalog, jitsu-js plugin registry, and the
ingest DeviceOptions map (which classifies the destination as client-side and
tells the SDK which plugin to run).
d87a8a5 to
1784ba7
Compare
Mirrors GTM's loadGtm. When off, Jitsu skips injecting the vendor tag (and, for Hotjar, skips setting _hjSettings) and only forwards events to the already-loaded window.clarity/hj. The self-queuing stub is still installed so early events aren't lost. Avoids double-loading Clarity/Hotjar when the page already includes them. The projectId/siteId is only used when Jitsu loads the tag.
Replaces the hand-drawn placeholder with Clarity's official mark, sourced from Clarity's own static assets (claritystatic.azureedge.net/images/logo.svg).
Device destinations must also be registered as no-op builtin functions so rotor's function-chain builder resolves them; without the entry getBuiltinFunction returns undefined and rotor throws "no functions assigned to it's destination type" for clarity/hotjar connections. Mirrors the existing tag/gtm/logrocket/ga4-tag no-op entries. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVmXNhA3FRyKRc4cdK1k4i
…-hotjar-destinations
There was a problem hiding this comment.
Reviewed the Clarity/Hotjar device-destination additions across ingest mappings, destination registration, plugin runtime logic, and console schema wiring. I found a few correctness risks worth addressing: global singleton state can mix multiple destination configs, and failed script loads can leave unbounded in-memory queues.
…queue Addresses code-review findings: - window.clarity / window.hj are vendor-owned singletons, so a second destination with a different projectId/siteId can't get its own instance. Track the active id and warn on mismatch instead of silently routing events to the wrong project/site (first destination to load the tag wins). - On script-load failure, replace the queuing stub with a no-op (dropping its queue) and short-circuit handle() when state is "failed", so events no longer accumulate unbounded in window.clarity.q / window.hj.q for the rest of the page lifetime. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVmXNhA3FRyKRc4cdK1k4i
…er a passive one Follow-up code-review finding: when loadClarity/loadHotjar===false ran first it set global state to "loaded", so a later load-enabled destination early-returned at the guard and never loaded the real SDK - stranding events in window.clarity.q / window.hj.q forever. Introduce a transitional "passive" state for the load-it-yourself path. A subsequent load-enabled destination transitions passive -> loading and takes ownership of loading the tag. Only loading/loaded/failed are terminal for the singleton-ownership guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVmXNhA3FRyKRc4cdK1k4i
There was a problem hiding this comment.
Reviewed the Clarity/Hotjar device-destination additions across jitsu-js, destination wiring, and console schema updates. The implementation looks solid overall; I left two inline questions about a possible long-session memory-growth edge case when loadClarity/loadHotjar is disabled and the vendor script never actually appears.
Follow-up code-review question: in loadClarity/loadHotjar===false mode, if the page never loads the vendor tag (misconfig, blocked snippet), the self-queuing stub would grow window.clarity.q / window.hj.q unbounded for the whole session. Cap the stub queue at 1000 entries. The real vendor script drains the queue within seconds, so the cap is only reached when the tag never loads; it's generous enough that a normal slow load never drops events. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVmXNhA3FRyKRc4cdK1k4i
There was a problem hiding this comment.
Reviewed the diff from 850f6cc to d871271, including the new Clarity/Hotjar device plugins, destination wiring in console/bulker, and builtin destination-function registrations.\n\nI also checked existing PR review threads and skipped previously resolved findings.\n\nNo new actionable bugs, security issues, or correctness regressions stood out in the current patch.
Adds two client-side device (tag) destinations — Microsoft Clarity and Hotjar. Both are session-analytics tools (heatmaps, recordings, surveys) whose value comes from running a vendor JS snippet in the visitor's browser, so they run through the
@jitsu/jsSDK as internal device plugins, following the existinglogrocket/ga4-tag/gtmpattern.JITSU-135Event mapping
Microsoft Clarity (tag from
clarity.ms/tag/<projectId>)identify→clarity('identify', userId)+ traits forwarded as filterable custom tags (clarity('set', k, v));name/emailused as friendly name.track→clarity('event', name); event properties forwarded as custom tags when enabled (default on — Clarity events carry only a name).page→ no-op (Clarity auto-records page views).projectId,cookieConsent,trackProperties(tags | ignore).Hotjar (tag from
static.hotjar.com/c/hotjar-<siteId>.js?sv=6)identify→hj('identify', userId, attributes)(null userId allowed).track→hj('event', name)(name sanitized: ≤250 chars, spaces →_); Hotjar has no per-event property API, so properties can be merged into the user record when enabled (default off).page→hj('stateChange', path)behind an opt-in SPA toggle (default off).siteId,spaPageViews,trackProperties(ignore | attributes).Reference: RudderStack
Hotjar/MicrosoftClarityintegrations.Changes
libs/jitsu-js/src/destination-plugins/{clarity,hotjar}.ts— browser plugins.libs/jitsu-js/src/destination-plugins/index.ts— register ininternalDestinationPlugins.webapps/console/lib/schema/destinations.tsx+icons/{clarity,hotjar}.tsx— catalog entries + icons.bulker/ingest/destination_types.go—DeviceOptionsmap entries (classifies each as client-side and tells the SDK which plugin to run).The
id(clarity,hotjar) matches across catalog,deviceOptions.name, plugin, and Go map.Verification
bulker/ingestcompiles; jitsu-jstsc --noEmitpasses; consolepnpm codegen+tsc --noEmitpasses.window.clarity(...)/window.hj(...)fire.Docs (pages, catalog cards, logos) are in a separate PR against
jitsucom/websites.