@ably/ui 18.2.0 → 18.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import{describe,it,expect,beforeEach,afterEach,vi}from"vitest";import{renderHook,act}from"@testing-library/react";import{useThemedScrollpoints}from"./use-themed-scrollpoints";describe("useThemedScrollpoints",()=>{let observerCallback;let mockObserve;let mockUnobserve;let mockDisconnect;let originalIntersectionObserver;let originalRequestAnimationFrame;beforeEach(()=>{vi.useFakeTimers();originalIntersectionObserver=global.IntersectionObserver;originalRequestAnimationFrame=global.requestAnimationFrame;mockObserve=vi.fn();mockUnobserve=vi.fn();mockDisconnect=vi.fn();global.IntersectionObserver=vi.fn(callback=>{observerCallback=callback;return{observe:mockObserve,unobserve:mockUnobserve,disconnect:mockDisconnect}});global.requestAnimationFrame=vi.fn(cb=>{cb(0);return 0})});afterEach(()=>{vi.clearAllMocks();vi.useRealTimers();document.body.innerHTML="";global.IntersectionObserver=originalIntersectionObserver;global.requestAnimationFrame=originalRequestAnimationFrame});it("returns first scrollpoint className on mount",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:200,bottom:400});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));expect(result.current).toBe("");act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light")});it("returns empty string when no scrollpoints provided",()=>{const{result}=renderHook(()=>useThemedScrollpoints([]));expect(result.current).toBe("")});it("clears activeClassName when scrollpoints changes from non-empty to empty",()=>{const elem=document.createElement("div");elem.id="zone1";elem.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});document.body.appendChild(elem);const{result,rerender}=renderHook(({scrollpoints})=>useThemedScrollpoints(scrollpoints),{initialProps:{scrollpoints:[{id:"zone1",className:"theme-light"}]}});act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");rerender({scrollpoints:[]});expect(result.current).toBe("")});it("does not create IntersectionObserver when no scrollpoints provided",()=>{renderHook(()=>useThemedScrollpoints([]));expect(IntersectionObserver).not.toHaveBeenCalled()});it("creates IntersectionObserver with correct config",()=>{const scrollpoints=[{id:"zone1",className:"theme-light"}];const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);renderHook(()=>useThemedScrollpoints(scrollpoints));expect(IntersectionObserver).toHaveBeenCalledWith(expect.any(Function),expect.objectContaining({rootMargin:"-64px 0px 0px 0px",threshold:0}))});it("observes all scrollpoint elements that exist in DOM",()=>{const elem1=document.createElement("div");elem1.id="zone1";const elem2=document.createElement("div");elem2.id="zone2";document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(mockObserve).toHaveBeenCalledTimes(2);expect(mockObserve).toHaveBeenCalledWith(elem1);expect(mockObserve).toHaveBeenCalledWith(elem2)});it("logs warning for missing DOM elements",()=>{const consoleWarn=vi.spyOn(console,"warn").mockImplementation(()=>{});const scrollpoints=[{id:"non-existent",className:"theme"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(consoleWarn).toHaveBeenCalledWith(expect.stringContaining('Element with id "non-existent" not found'));consoleWarn.mockRestore()});it("observes existing elements and warns about missing ones",()=>{const consoleWarn=vi.spyOn(console,"warn").mockImplementation(()=>{});const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"missing",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(mockObserve).toHaveBeenCalledTimes(1);expect(mockObserve).toHaveBeenCalledWith(elem);expect(consoleWarn).toHaveBeenCalledWith(expect.stringContaining('Element with id "missing" not found'));consoleWarn.mockRestore()});it("updates className when intersection occurs",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:50,bottom:250});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-dark");act(()=>{observerCallback([{target:elem1,isIntersecting:true,boundingClientRect:{top:60,bottom:260,left:0,right:0,x:0,y:60,width:0,height:200}},{target:elem2,isIntersecting:true,boundingClientRect:{top:100,bottom:300,left:0,right:0,x:0,y:100,width:0,height:200}}],{})});expect(result.current).toBe("theme-light")});it("does not update className if same scrollpoint intersects again",()=>{const elem=document.createElement("div");elem.id="zone1";elem.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");const renderCount=result.current;observerCallback([{target:elem,isIntersecting:true,boundingClientRect:{top:0,bottom:200,left:0,right:0,x:0,y:0,width:0,height:200}}],{});expect(result.current).toBe(renderCount)});it("ignores non-intersecting entries",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:200,bottom:400});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");observerCallback([{target:elem2,isIntersecting:false}],{});expect(result.current).toBe("theme-light")});it("uses closest element to header when multiple entries intersect",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:50});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:60,bottom:200});const elem3=document.createElement("div");elem3.id="zone3";elem3.getBoundingClientRect=vi.fn().mockReturnValue({top:10,bottom:100});document.body.appendChild(elem1);document.body.appendChild(elem2);document.body.appendChild(elem3);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"},{id:"zone3",className:"theme-blue"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-dark");act(()=>{observerCallback([{target:elem1,isIntersecting:false,boundingClientRect:{top:0,bottom:50,left:0,right:0,x:0,y:0,width:0,height:50}},{target:elem2,isIntersecting:true,boundingClientRect:{top:100,bottom:300,left:0,right:0,x:0,y:100,width:0,height:200}},{target:elem3,isIntersecting:true,boundingClientRect:{top:62,bottom:152,left:0,right:0,x:0,y:62,width:0,height:90}}],{})});expect(result.current).toBe("theme-blue")});it("disconnects observer on unmount",()=>{const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"}];const{unmount}=renderHook(()=>useThemedScrollpoints(scrollpoints));unmount();expect(mockDisconnect).toHaveBeenCalled()});it("recreates observer when scrollpoints change",()=>{const elem1=document.createElement("div");elem1.id="zone1";const elem2=document.createElement("div");elem2.id="zone2";document.body.appendChild(elem1);document.body.appendChild(elem2);const{rerender}=renderHook(({scrollpoints})=>useThemedScrollpoints(scrollpoints),{initialProps:{scrollpoints:[{id:"zone1",className:"theme-light"}]}});expect(IntersectionObserver).toHaveBeenCalledTimes(1);expect(mockObserve).toHaveBeenCalledTimes(1);rerender({scrollpoints:[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}]});expect(mockDisconnect).toHaveBeenCalledTimes(1);expect(IntersectionObserver).toHaveBeenCalledTimes(2);expect(mockObserve).toHaveBeenCalledTimes(3)});it("uses requestAnimationFrame for state updates",()=>{const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));const rafSpy=vi.spyOn(global,"requestAnimationFrame");observerCallback([{target:elem,isIntersecting:true}],{});expect(rafSpy).toHaveBeenCalled();rafSpy.mockRestore()})});
1
+ import{describe,it,expect,beforeEach,afterEach,vi}from"vitest";import{renderHook,act}from"@testing-library/react";import{useThemedScrollpoints}from"./use-themed-scrollpoints";describe("useThemedScrollpoints",()=>{let observerCallback;let mockObserve;let mockUnobserve;let mockDisconnect;let originalIntersectionObserver;let originalRequestAnimationFrame;beforeEach(()=>{vi.useFakeTimers();originalIntersectionObserver=global.IntersectionObserver;originalRequestAnimationFrame=global.requestAnimationFrame;mockObserve=vi.fn();mockUnobserve=vi.fn();mockDisconnect=vi.fn();global.IntersectionObserver=vi.fn(function(callback){observerCallback=callback;return{observe:mockObserve,unobserve:mockUnobserve,disconnect:mockDisconnect}});global.requestAnimationFrame=vi.fn(cb=>{cb(0);return 0})});afterEach(()=>{vi.clearAllMocks();vi.useRealTimers();document.body.innerHTML="";global.IntersectionObserver=originalIntersectionObserver;global.requestAnimationFrame=originalRequestAnimationFrame});it("returns first scrollpoint className on mount",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:200,bottom:400});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));expect(result.current).toBe("");act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light")});it("returns empty string when no scrollpoints provided",()=>{const{result}=renderHook(()=>useThemedScrollpoints([]));expect(result.current).toBe("")});it("clears activeClassName when scrollpoints changes from non-empty to empty",()=>{const elem=document.createElement("div");elem.id="zone1";elem.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});document.body.appendChild(elem);const{result,rerender}=renderHook(({scrollpoints})=>useThemedScrollpoints(scrollpoints),{initialProps:{scrollpoints:[{id:"zone1",className:"theme-light"}]}});act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");rerender({scrollpoints:[]});expect(result.current).toBe("")});it("does not create IntersectionObserver when no scrollpoints provided",()=>{renderHook(()=>useThemedScrollpoints([]));expect(IntersectionObserver).not.toHaveBeenCalled()});it("creates IntersectionObserver with correct config",()=>{const scrollpoints=[{id:"zone1",className:"theme-light"}];const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);renderHook(()=>useThemedScrollpoints(scrollpoints));expect(IntersectionObserver).toHaveBeenCalledWith(expect.any(Function),expect.objectContaining({rootMargin:"-64px 0px 0px 0px",threshold:0}))});it("observes all scrollpoint elements that exist in DOM",()=>{const elem1=document.createElement("div");elem1.id="zone1";const elem2=document.createElement("div");elem2.id="zone2";document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(mockObserve).toHaveBeenCalledTimes(2);expect(mockObserve).toHaveBeenCalledWith(elem1);expect(mockObserve).toHaveBeenCalledWith(elem2)});it("logs warning for missing DOM elements",()=>{const consoleWarn=vi.spyOn(console,"warn").mockImplementation(()=>{});const scrollpoints=[{id:"non-existent",className:"theme"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(consoleWarn).toHaveBeenCalledWith(expect.stringContaining('Element with id "non-existent" not found'));consoleWarn.mockRestore()});it("observes existing elements and warns about missing ones",()=>{const consoleWarn=vi.spyOn(console,"warn").mockImplementation(()=>{});const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"missing",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));expect(mockObserve).toHaveBeenCalledTimes(1);expect(mockObserve).toHaveBeenCalledWith(elem);expect(consoleWarn).toHaveBeenCalledWith(expect.stringContaining('Element with id "missing" not found'));consoleWarn.mockRestore()});it("updates className when intersection occurs",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:50,bottom:250});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-dark");act(()=>{observerCallback([{target:elem1,isIntersecting:true,boundingClientRect:{top:60,bottom:260,left:0,right:0,x:0,y:60,width:0,height:200}},{target:elem2,isIntersecting:true,boundingClientRect:{top:100,bottom:300,left:0,right:0,x:0,y:100,width:0,height:200}}],{})});expect(result.current).toBe("theme-light")});it("does not update className if same scrollpoint intersects again",()=>{const elem=document.createElement("div");elem.id="zone1";elem.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");const renderCount=result.current;observerCallback([{target:elem,isIntersecting:true,boundingClientRect:{top:0,bottom:200,left:0,right:0,x:0,y:0,width:0,height:200}}],{});expect(result.current).toBe(renderCount)});it("ignores non-intersecting entries",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:200});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:200,bottom:400});document.body.appendChild(elem1);document.body.appendChild(elem2);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-light");observerCallback([{target:elem2,isIntersecting:false}],{});expect(result.current).toBe("theme-light")});it("uses closest element to header when multiple entries intersect",()=>{const elem1=document.createElement("div");elem1.id="zone1";elem1.getBoundingClientRect=vi.fn().mockReturnValue({top:0,bottom:50});const elem2=document.createElement("div");elem2.id="zone2";elem2.getBoundingClientRect=vi.fn().mockReturnValue({top:60,bottom:200});const elem3=document.createElement("div");elem3.id="zone3";elem3.getBoundingClientRect=vi.fn().mockReturnValue({top:10,bottom:100});document.body.appendChild(elem1);document.body.appendChild(elem2);document.body.appendChild(elem3);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"},{id:"zone3",className:"theme-blue"}];const{result}=renderHook(()=>useThemedScrollpoints(scrollpoints));act(()=>{vi.runAllTimers()});expect(result.current).toBe("theme-dark");act(()=>{observerCallback([{target:elem1,isIntersecting:false,boundingClientRect:{top:0,bottom:50,left:0,right:0,x:0,y:0,width:0,height:50}},{target:elem2,isIntersecting:true,boundingClientRect:{top:100,bottom:300,left:0,right:0,x:0,y:100,width:0,height:200}},{target:elem3,isIntersecting:true,boundingClientRect:{top:62,bottom:152,left:0,right:0,x:0,y:62,width:0,height:90}}],{})});expect(result.current).toBe("theme-blue")});it("disconnects observer on unmount",()=>{const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"}];const{unmount}=renderHook(()=>useThemedScrollpoints(scrollpoints));unmount();expect(mockDisconnect).toHaveBeenCalled()});it("recreates observer when scrollpoints change",()=>{const elem1=document.createElement("div");elem1.id="zone1";const elem2=document.createElement("div");elem2.id="zone2";document.body.appendChild(elem1);document.body.appendChild(elem2);const{rerender}=renderHook(({scrollpoints})=>useThemedScrollpoints(scrollpoints),{initialProps:{scrollpoints:[{id:"zone1",className:"theme-light"}]}});expect(IntersectionObserver).toHaveBeenCalledTimes(1);expect(mockObserve).toHaveBeenCalledTimes(1);rerender({scrollpoints:[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}]});expect(mockDisconnect).toHaveBeenCalledTimes(1);expect(IntersectionObserver).toHaveBeenCalledTimes(2);expect(mockObserve).toHaveBeenCalledTimes(3)});it("uses requestAnimationFrame for state updates",()=>{const elem=document.createElement("div");elem.id="zone1";document.body.appendChild(elem);const scrollpoints=[{id:"zone1",className:"theme-light"},{id:"zone2",className:"theme-dark"}];renderHook(()=>useThemedScrollpoints(scrollpoints));const rafSpy=vi.spyOn(global,"requestAnimationFrame");observerCallback([{target:elem,isIntersecting:true}],{});expect(rafSpy).toHaveBeenCalled();rafSpy.mockRestore()})});
2
2
  //# sourceMappingURL=use-themed-scrollpoints.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/hooks/use-themed-scrollpoints.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi } from \"vitest\";\nimport { renderHook, act } from \"@testing-library/react\";\nimport { useThemedScrollpoints } from \"./use-themed-scrollpoints\";\n\ndescribe(\"useThemedScrollpoints\", () => {\n let observerCallback: IntersectionObserverCallback;\n let mockObserve: ReturnType<typeof vi.fn>;\n let mockUnobserve: ReturnType<typeof vi.fn>;\n let mockDisconnect: ReturnType<typeof vi.fn>;\n let originalIntersectionObserver: typeof IntersectionObserver;\n let originalRequestAnimationFrame: typeof requestAnimationFrame;\n\n beforeEach(() => {\n vi.useFakeTimers();\n\n // Save originals to prevent test pollution\n originalIntersectionObserver = global.IntersectionObserver;\n originalRequestAnimationFrame = global.requestAnimationFrame;\n\n // Mock IntersectionObserver\n mockObserve = vi.fn();\n mockUnobserve = vi.fn();\n mockDisconnect = vi.fn();\n\n global.IntersectionObserver = vi.fn((callback) => {\n observerCallback = callback;\n return {\n observe: mockObserve,\n unobserve: mockUnobserve,\n disconnect: mockDisconnect,\n };\n }) as unknown as typeof IntersectionObserver;\n\n // Mock requestAnimationFrame\n global.requestAnimationFrame = vi.fn((cb) => {\n cb(0);\n return 0;\n });\n });\n\n afterEach(() => {\n vi.clearAllMocks();\n vi.useRealTimers();\n document.body.innerHTML = \"\";\n // Restore originals to prevent test pollution\n global.IntersectionObserver = originalIntersectionObserver;\n global.requestAnimationFrame = originalRequestAnimationFrame;\n });\n\n it(\"returns first scrollpoint className on mount\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 200, bottom: 400 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Initial state is empty\n expect(result.current).toBe(\"\");\n\n // Advance timer to trigger initial check\n act(() => {\n vi.runAllTimers();\n });\n\n // Should now have the first scrollpoint's className\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"returns empty string when no scrollpoints provided\", () => {\n const { result } = renderHook(() => useThemedScrollpoints([]));\n expect(result.current).toBe(\"\");\n });\n\n it(\"clears activeClassName when scrollpoints changes from non-empty to empty\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n elem.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n document.body.appendChild(elem);\n\n const { result, rerender } = renderHook(\n ({ scrollpoints }) => useThemedScrollpoints(scrollpoints),\n {\n initialProps: {\n scrollpoints: [{ id: \"zone1\", className: \"theme-light\" }],\n },\n },\n );\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Change to empty scrollpoints\n rerender({ scrollpoints: [] });\n\n // Should clear the className\n expect(result.current).toBe(\"\");\n });\n\n it(\"does not create IntersectionObserver when no scrollpoints provided\", () => {\n renderHook(() => useThemedScrollpoints([]));\n\n expect(IntersectionObserver).not.toHaveBeenCalled();\n });\n\n it(\"creates IntersectionObserver with correct config\", () => {\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n // Create DOM element\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(IntersectionObserver).toHaveBeenCalledWith(\n expect.any(Function),\n expect.objectContaining({\n rootMargin: \"-64px 0px 0px 0px\",\n threshold: 0,\n }),\n );\n });\n\n it(\"observes all scrollpoint elements that exist in DOM\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(mockObserve).toHaveBeenCalledTimes(2);\n expect(mockObserve).toHaveBeenCalledWith(elem1);\n expect(mockObserve).toHaveBeenCalledWith(elem2);\n });\n\n it(\"logs warning for missing DOM elements\", () => {\n const consoleWarn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n const scrollpoints = [{ id: \"non-existent\", className: \"theme\" }];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(consoleWarn).toHaveBeenCalledWith(\n expect.stringContaining('Element with id \"non-existent\" not found'),\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"observes existing elements and warns about missing ones\", () => {\n const consoleWarn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n // Create only one element\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"missing\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(mockObserve).toHaveBeenCalledTimes(1);\n expect(mockObserve).toHaveBeenCalledWith(elem);\n expect(consoleWarn).toHaveBeenCalledWith(\n expect.stringContaining('Element with id \"missing\" not found'),\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"updates className when intersection occurs\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 50, bottom: 250 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n // elem2 (top=50) is closer to header (64) than elem1 (top=0)\n // distance for elem1: |0 - 64| = 64\n // distance for elem2: |50 - 64| = 14\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-dark\");\n\n // Simulate scrolling - elem1 moves closer to header position\n act(() => {\n observerCallback(\n [\n {\n target: elem1,\n isIntersecting: true,\n boundingClientRect: {\n top: 60,\n bottom: 260,\n left: 0,\n right: 0,\n x: 0,\n y: 60,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem2,\n isIntersecting: true,\n boundingClientRect: {\n top: 100,\n bottom: 300,\n left: 0,\n right: 0,\n x: 0,\n y: 100,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n });\n\n // elem1 is now closer to header (distance=4 vs elem2 distance=36)\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"does not update className if same scrollpoint intersects again\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n elem.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n document.body.appendChild(elem);\n\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Simulate intersection with same element\n const renderCount = result.current;\n observerCallback(\n [\n {\n target: elem,\n isIntersecting: true,\n boundingClientRect: {\n top: 0,\n bottom: 200,\n left: 0,\n right: 0,\n x: 0,\n y: 0,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n // Should not trigger re-render (className unchanged)\n expect(result.current).toBe(renderCount);\n });\n\n it(\"ignores non-intersecting entries\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 200, bottom: 400 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Simulate non-intersecting entry\n observerCallback(\n [\n {\n target: elem2,\n isIntersecting: false,\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n // Should remain unchanged\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"uses closest element to header when multiple entries intersect\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 50 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n // zone2 top is at 60, which is closer to HEADER_HEIGHT (64) than zone3\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 60, bottom: 200 });\n const elem3 = document.createElement(\"div\");\n elem3.id = \"zone3\";\n elem3.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 10, bottom: 100 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n document.body.appendChild(elem3);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n { id: \"zone3\", className: \"theme-blue\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n // elem2 (top=60) is closest to header (64) with distance=4\n // elem3 (top=10) has distance=54\n // elem1 (top=0) doesn't contain header (bottom=50 < 64)\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-dark\");\n\n // Simulate scrolling where elem3 becomes closest to header\n act(() => {\n observerCallback(\n [\n {\n target: elem1,\n isIntersecting: false,\n boundingClientRect: {\n top: 0,\n bottom: 50,\n left: 0,\n right: 0,\n x: 0,\n y: 0,\n width: 0,\n height: 50,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem2,\n isIntersecting: true,\n boundingClientRect: {\n top: 100,\n bottom: 300,\n left: 0,\n right: 0,\n x: 0,\n y: 100,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem3,\n isIntersecting: true,\n boundingClientRect: {\n top: 62,\n bottom: 152,\n left: 0,\n right: 0,\n x: 0,\n y: 62,\n width: 0,\n height: 90,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n });\n\n // elem3 is now closest to HEADER_HEIGHT (64)\n // distance for elem2: |100 - 64| = 36\n // distance for elem3: |62 - 64| = 2\n expect(result.current).toBe(\"theme-blue\");\n });\n\n it(\"disconnects observer on unmount\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n const { unmount } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n unmount();\n\n expect(mockDisconnect).toHaveBeenCalled();\n });\n\n it(\"recreates observer when scrollpoints change\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const { rerender } = renderHook(\n ({ scrollpoints }) => useThemedScrollpoints(scrollpoints),\n {\n initialProps: {\n scrollpoints: [{ id: \"zone1\", className: \"theme-light\" }],\n },\n },\n );\n\n expect(IntersectionObserver).toHaveBeenCalledTimes(1);\n expect(mockObserve).toHaveBeenCalledTimes(1);\n\n // Change scrollpoints\n rerender({\n scrollpoints: [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ],\n });\n\n // Should disconnect old observer and create new one\n expect(mockDisconnect).toHaveBeenCalledTimes(1);\n expect(IntersectionObserver).toHaveBeenCalledTimes(2);\n expect(mockObserve).toHaveBeenCalledTimes(3); // 1 from first render + 2 from second\n });\n\n it(\"uses requestAnimationFrame for state updates\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n const rafSpy = vi.spyOn(global, \"requestAnimationFrame\");\n\n // Simulate intersection\n observerCallback(\n [\n {\n target: elem,\n isIntersecting: true,\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n expect(rafSpy).toHaveBeenCalled();\n\n rafSpy.mockRestore();\n });\n});\n"],"names":["describe","it","expect","beforeEach","afterEach","vi","renderHook","act","useThemedScrollpoints","observerCallback","mockObserve","mockUnobserve","mockDisconnect","originalIntersectionObserver","originalRequestAnimationFrame","useFakeTimers","global","IntersectionObserver","requestAnimationFrame","fn","callback","observe","unobserve","disconnect","cb","clearAllMocks","useRealTimers","document","body","innerHTML","elem1","createElement","id","getBoundingClientRect","mockReturnValue","top","bottom","elem2","appendChild","scrollpoints","className","result","current","toBe","runAllTimers","elem","rerender","initialProps","not","toHaveBeenCalled","toHaveBeenCalledWith","any","Function","objectContaining","rootMargin","threshold","toHaveBeenCalledTimes","consoleWarn","spyOn","console","mockImplementation","stringContaining","mockRestore","target","isIntersecting","boundingClientRect","left","right","x","y","width","height","renderCount","elem3","unmount","rafSpy"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/hooks/use-themed-scrollpoints.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi } from \"vitest\";\nimport { renderHook, act } from \"@testing-library/react\";\nimport { useThemedScrollpoints } from \"./use-themed-scrollpoints\";\n\ndescribe(\"useThemedScrollpoints\", () => {\n let observerCallback: IntersectionObserverCallback;\n let mockObserve: ReturnType<typeof vi.fn>;\n let mockUnobserve: ReturnType<typeof vi.fn>;\n let mockDisconnect: ReturnType<typeof vi.fn>;\n let originalIntersectionObserver: typeof IntersectionObserver;\n let originalRequestAnimationFrame: typeof requestAnimationFrame;\n\n beforeEach(() => {\n vi.useFakeTimers();\n\n // Save originals to prevent test pollution\n originalIntersectionObserver = global.IntersectionObserver;\n originalRequestAnimationFrame = global.requestAnimationFrame;\n\n // Mock IntersectionObserver\n mockObserve = vi.fn();\n mockUnobserve = vi.fn();\n mockDisconnect = vi.fn();\n\n // Vitest 4: constructor mocks must use `function`/`class`, not arrows —\n // arrows have no [[Construct]] and `new` against them throws.\n global.IntersectionObserver = vi.fn(function (\n callback: IntersectionObserverCallback,\n ) {\n observerCallback = callback;\n return {\n observe: mockObserve,\n unobserve: mockUnobserve,\n disconnect: mockDisconnect,\n };\n }) as unknown as typeof IntersectionObserver;\n\n // Mock requestAnimationFrame\n global.requestAnimationFrame = vi.fn((cb) => {\n cb(0);\n return 0;\n });\n });\n\n afterEach(() => {\n vi.clearAllMocks();\n vi.useRealTimers();\n document.body.innerHTML = \"\";\n // Restore originals to prevent test pollution\n global.IntersectionObserver = originalIntersectionObserver;\n global.requestAnimationFrame = originalRequestAnimationFrame;\n });\n\n it(\"returns first scrollpoint className on mount\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 200, bottom: 400 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Initial state is empty\n expect(result.current).toBe(\"\");\n\n // Advance timer to trigger initial check\n act(() => {\n vi.runAllTimers();\n });\n\n // Should now have the first scrollpoint's className\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"returns empty string when no scrollpoints provided\", () => {\n const { result } = renderHook(() => useThemedScrollpoints([]));\n expect(result.current).toBe(\"\");\n });\n\n it(\"clears activeClassName when scrollpoints changes from non-empty to empty\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n elem.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n document.body.appendChild(elem);\n\n const { result, rerender } = renderHook(\n ({ scrollpoints }) => useThemedScrollpoints(scrollpoints),\n {\n initialProps: {\n scrollpoints: [{ id: \"zone1\", className: \"theme-light\" }],\n },\n },\n );\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Change to empty scrollpoints\n rerender({ scrollpoints: [] });\n\n // Should clear the className\n expect(result.current).toBe(\"\");\n });\n\n it(\"does not create IntersectionObserver when no scrollpoints provided\", () => {\n renderHook(() => useThemedScrollpoints([]));\n\n expect(IntersectionObserver).not.toHaveBeenCalled();\n });\n\n it(\"creates IntersectionObserver with correct config\", () => {\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n // Create DOM element\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(IntersectionObserver).toHaveBeenCalledWith(\n expect.any(Function),\n expect.objectContaining({\n rootMargin: \"-64px 0px 0px 0px\",\n threshold: 0,\n }),\n );\n });\n\n it(\"observes all scrollpoint elements that exist in DOM\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(mockObserve).toHaveBeenCalledTimes(2);\n expect(mockObserve).toHaveBeenCalledWith(elem1);\n expect(mockObserve).toHaveBeenCalledWith(elem2);\n });\n\n it(\"logs warning for missing DOM elements\", () => {\n const consoleWarn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n const scrollpoints = [{ id: \"non-existent\", className: \"theme\" }];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(consoleWarn).toHaveBeenCalledWith(\n expect.stringContaining('Element with id \"non-existent\" not found'),\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"observes existing elements and warns about missing ones\", () => {\n const consoleWarn = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n // Create only one element\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"missing\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n expect(mockObserve).toHaveBeenCalledTimes(1);\n expect(mockObserve).toHaveBeenCalledWith(elem);\n expect(consoleWarn).toHaveBeenCalledWith(\n expect.stringContaining('Element with id \"missing\" not found'),\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"updates className when intersection occurs\", () => {\n // Create DOM elements\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 50, bottom: 250 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n // elem2 (top=50) is closer to header (64) than elem1 (top=0)\n // distance for elem1: |0 - 64| = 64\n // distance for elem2: |50 - 64| = 14\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-dark\");\n\n // Simulate scrolling - elem1 moves closer to header position\n act(() => {\n observerCallback(\n [\n {\n target: elem1,\n isIntersecting: true,\n boundingClientRect: {\n top: 60,\n bottom: 260,\n left: 0,\n right: 0,\n x: 0,\n y: 60,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem2,\n isIntersecting: true,\n boundingClientRect: {\n top: 100,\n bottom: 300,\n left: 0,\n right: 0,\n x: 0,\n y: 100,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n });\n\n // elem1 is now closer to header (distance=4 vs elem2 distance=36)\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"does not update className if same scrollpoint intersects again\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n elem.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n document.body.appendChild(elem);\n\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Simulate intersection with same element\n const renderCount = result.current;\n observerCallback(\n [\n {\n target: elem,\n isIntersecting: true,\n boundingClientRect: {\n top: 0,\n bottom: 200,\n left: 0,\n right: 0,\n x: 0,\n y: 0,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n // Should not trigger re-render (className unchanged)\n expect(result.current).toBe(renderCount);\n });\n\n it(\"ignores non-intersecting entries\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 200 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 200, bottom: 400 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-light\");\n\n // Simulate non-intersecting entry\n observerCallback(\n [\n {\n target: elem2,\n isIntersecting: false,\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n // Should remain unchanged\n expect(result.current).toBe(\"theme-light\");\n });\n\n it(\"uses closest element to header when multiple entries intersect\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n elem1.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 0, bottom: 50 });\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n // zone2 top is at 60, which is closer to HEADER_HEIGHT (64) than zone3\n elem2.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 60, bottom: 200 });\n const elem3 = document.createElement(\"div\");\n elem3.id = \"zone3\";\n elem3.getBoundingClientRect = vi\n .fn()\n .mockReturnValue({ top: 10, bottom: 100 });\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n document.body.appendChild(elem3);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n { id: \"zone3\", className: \"theme-blue\" },\n ];\n\n const { result } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n // Wait for initial check\n // elem2 (top=60) is closest to header (64) with distance=4\n // elem3 (top=10) has distance=54\n // elem1 (top=0) doesn't contain header (bottom=50 < 64)\n act(() => {\n vi.runAllTimers();\n });\n expect(result.current).toBe(\"theme-dark\");\n\n // Simulate scrolling where elem3 becomes closest to header\n act(() => {\n observerCallback(\n [\n {\n target: elem1,\n isIntersecting: false,\n boundingClientRect: {\n top: 0,\n bottom: 50,\n left: 0,\n right: 0,\n x: 0,\n y: 0,\n width: 0,\n height: 50,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem2,\n isIntersecting: true,\n boundingClientRect: {\n top: 100,\n bottom: 300,\n left: 0,\n right: 0,\n x: 0,\n y: 100,\n width: 0,\n height: 200,\n },\n } as unknown as IntersectionObserverEntry,\n {\n target: elem3,\n isIntersecting: true,\n boundingClientRect: {\n top: 62,\n bottom: 152,\n left: 0,\n right: 0,\n x: 0,\n y: 62,\n width: 0,\n height: 90,\n },\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n });\n\n // elem3 is now closest to HEADER_HEIGHT (64)\n // distance for elem2: |100 - 64| = 36\n // distance for elem3: |62 - 64| = 2\n expect(result.current).toBe(\"theme-blue\");\n });\n\n it(\"disconnects observer on unmount\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [{ id: \"zone1\", className: \"theme-light\" }];\n\n const { unmount } = renderHook(() => useThemedScrollpoints(scrollpoints));\n\n unmount();\n\n expect(mockDisconnect).toHaveBeenCalled();\n });\n\n it(\"recreates observer when scrollpoints change\", () => {\n const elem1 = document.createElement(\"div\");\n elem1.id = \"zone1\";\n const elem2 = document.createElement(\"div\");\n elem2.id = \"zone2\";\n document.body.appendChild(elem1);\n document.body.appendChild(elem2);\n\n const { rerender } = renderHook(\n ({ scrollpoints }) => useThemedScrollpoints(scrollpoints),\n {\n initialProps: {\n scrollpoints: [{ id: \"zone1\", className: \"theme-light\" }],\n },\n },\n );\n\n expect(IntersectionObserver).toHaveBeenCalledTimes(1);\n expect(mockObserve).toHaveBeenCalledTimes(1);\n\n // Change scrollpoints\n rerender({\n scrollpoints: [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ],\n });\n\n // Should disconnect old observer and create new one\n expect(mockDisconnect).toHaveBeenCalledTimes(1);\n expect(IntersectionObserver).toHaveBeenCalledTimes(2);\n expect(mockObserve).toHaveBeenCalledTimes(3); // 1 from first render + 2 from second\n });\n\n it(\"uses requestAnimationFrame for state updates\", () => {\n const elem = document.createElement(\"div\");\n elem.id = \"zone1\";\n document.body.appendChild(elem);\n\n const scrollpoints = [\n { id: \"zone1\", className: \"theme-light\" },\n { id: \"zone2\", className: \"theme-dark\" },\n ];\n\n renderHook(() => useThemedScrollpoints(scrollpoints));\n\n const rafSpy = vi.spyOn(global, \"requestAnimationFrame\");\n\n // Simulate intersection\n observerCallback(\n [\n {\n target: elem,\n isIntersecting: true,\n } as unknown as IntersectionObserverEntry,\n ],\n {} as IntersectionObserver,\n );\n\n expect(rafSpy).toHaveBeenCalled();\n\n rafSpy.mockRestore();\n });\n});\n"],"names":["describe","it","expect","beforeEach","afterEach","vi","renderHook","act","useThemedScrollpoints","observerCallback","mockObserve","mockUnobserve","mockDisconnect","originalIntersectionObserver","originalRequestAnimationFrame","useFakeTimers","global","IntersectionObserver","requestAnimationFrame","fn","callback","observe","unobserve","disconnect","cb","clearAllMocks","useRealTimers","document","body","innerHTML","elem1","createElement","id","getBoundingClientRect","mockReturnValue","top","bottom","elem2","appendChild","scrollpoints","className","result","current","toBe","runAllTimers","elem","rerender","initialProps","not","toHaveBeenCalled","toHaveBeenCalledWith","any","Function","objectContaining","rootMargin","threshold","toHaveBeenCalledTimes","consoleWarn","spyOn","console","mockImplementation","stringContaining","mockRestore","target","isIntersecting","boundingClientRect","left","right","x","y","width","height","renderCount","elem3","unmount","rafSpy"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{InsightsService}from"./service";import*as logger from"./logger";export class InsightsCommandQueue{executeInitInsights(config){this.debugMode=!!config.debug;if(this.debugMode){logger.debug("Initializing insights")}this.realImplementation=new InsightsService;this.realImplementation.initInsights(config);this.isInitialized=true;this.processQueue()}processQueue(){if(this.debugMode){logger.debug(`Processing ${this.queue.length} queued commands`)}while(this.queue.length>0){const cmd=this.queue.shift();if(cmd&&this.realImplementation&&typeof this.realImplementation[cmd.methodName]==="function"){try{if(this.debugMode){logger.debug(`Executing queued command: ${cmd.methodName}`,cmd.args)}const fn=this.realImplementation[cmd.methodName];fn.apply(this.realImplementation,cmd.args)}catch(e){if(this.debugMode){logger.error(`Error executing queued command: ${cmd.methodName}`,e)}}}}}initInsights(_config){}enableDebugMode(){}disableDebugMode(){}identify(_identity){}trackPageView(_options){}track(_event,_properties){}startSessionRecording(){}stopSessionRecording(){}setupObserver(){return()=>{}}constructor(){_define_property(this,"isInitialized",false);_define_property(this,"queue",[]);_define_property(this,"debugMode",false);_define_property(this,"realImplementation",null);return new Proxy(this,{get:(target,prop)=>{if(prop in target&&typeof target[prop]!=="function"){return target[prop]}return(...args)=>{if(!target.isInitialized||!target.realImplementation){if(prop==="initInsights"){target.executeInitInsights(args[0]);return}if(target.debugMode){logger.debug(`Queuing method call: ${String(prop)}`,args)}target.queue.push({methodName:prop,args});return}if(typeof target.realImplementation[prop]==="function"){return target.realImplementation[prop](...args)}}}})}}
1
+ function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import{InsightsService}from"./service";import*as logger from"./logger";export class InsightsCommandQueue{executeInitInsights(config){this.debugMode=!!config.debug;if(this.debugMode){logger.debug("Initializing insights")}this.realImplementation=new InsightsService;this.realImplementation.initInsights(config);this.isInitialized=true;this.processQueue()}processQueue(){if(this.debugMode){logger.debug(`Processing ${this.queue.length} queued commands`)}while(this.queue.length>0){const cmd=this.queue.shift();if(cmd&&this.realImplementation&&typeof this.realImplementation[cmd.methodName]==="function"){try{if(this.debugMode){logger.debug(`Executing queued command: ${cmd.methodName}`,cmd.args)}const fn=this.realImplementation[cmd.methodName];fn.apply(this.realImplementation,cmd.args)}catch(e){if(this.debugMode){logger.error(`Error executing queued command: ${cmd.methodName}`,e)}}}}}initInsights(_config){}enableDebugMode(){}disableDebugMode(){}identify(_identity){}trackPageView(_options){}track(_event,_properties){}setupObserver(){return()=>{}}constructor(){_define_property(this,"isInitialized",false);_define_property(this,"queue",[]);_define_property(this,"debugMode",false);_define_property(this,"realImplementation",null);return new Proxy(this,{get:(target,prop)=>{if(prop in target&&typeof target[prop]!=="function"){return target[prop]}return(...args)=>{if(!target.isInitialized||!target.realImplementation){if(prop==="initInsights"){target.executeInitInsights(args[0]);return}if(target.debugMode){logger.debug(`Queuing method call: ${String(prop)}`,args)}target.queue.push({methodName:prop,args});return}if(typeof target.realImplementation[prop]==="function"){return target.realImplementation[prop](...args)}}}})}}
2
2
  //# sourceMappingURL=command-queue.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/command-queue.ts"],"sourcesContent":["import {\n AnalyticsService,\n Command,\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport { InsightsService } from \"./service\";\nimport * as logger from \"./logger\";\n\n// Queue handler that will collect commands before initialization\nexport class InsightsCommandQueue implements AnalyticsService {\n private isInitialized: boolean = false;\n private queue: Command[] = [];\n private debugMode: boolean = false;\n private realImplementation: InsightsService | null = null;\n\n constructor() {\n // Create a proxy that will either queue commands or execute them directly\n return new Proxy(this, {\n get: (target: InsightsCommandQueue, prop: keyof InsightsCommandQueue) => {\n // Return actual properties of the queue\n if (prop in target && typeof target[prop] !== \"function\") {\n return target[prop];\n }\n\n // Return a function that either queues or executes the method call\n return (...args: unknown[]) => {\n if (!target.isInitialized || !target.realImplementation) {\n // Queue the command for later execution\n if (prop === \"initInsights\") {\n // Special handling for initInsights - execute it right away\n target.executeInitInsights(args[0] as InsightsConfig);\n return;\n }\n\n // For debug logging\n if (target.debugMode) {\n logger.debug(`Queuing method call: ${String(prop)}`, args);\n }\n\n target.queue.push({\n methodName: prop,\n args,\n });\n\n return;\n }\n\n // Execute the command immediately on the real implementation\n if (typeof target.realImplementation[prop] === \"function\") {\n return (\n target.realImplementation[prop] as (...args: unknown[]) => unknown\n )(...args);\n }\n };\n },\n });\n }\n\n // Special handling for init since it needs to happen right away\n private executeInitInsights(config: InsightsConfig): void {\n this.debugMode = !!config.debug;\n\n if (this.debugMode) {\n logger.debug(\"Initializing insights\");\n }\n\n // Create and initialize the real implementation\n this.realImplementation = new InsightsService();\n this.realImplementation.initInsights(config);\n\n // Mark as initialized and process the queue\n this.isInitialized = true;\n this.processQueue();\n }\n\n // Process all queued commands\n private processQueue(): void {\n if (this.debugMode) {\n logger.debug(`Processing ${this.queue.length} queued commands`);\n }\n\n while (this.queue.length > 0) {\n const cmd = this.queue.shift();\n if (\n cmd &&\n this.realImplementation &&\n typeof this.realImplementation[cmd.methodName] === \"function\"\n ) {\n try {\n if (this.debugMode) {\n logger.debug(\n `Executing queued command: ${cmd.methodName}`,\n cmd.args,\n );\n }\n\n const fn = this.realImplementation[cmd.methodName] as (\n ...args: unknown[]\n ) => unknown;\n\n // Execute the command with the real implementation\n fn.apply(this.realImplementation, cmd.args as unknown[]);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\n `Error executing queued command: ${cmd.methodName}`,\n e,\n );\n }\n }\n }\n }\n }\n\n // Implement all methods required by AnalyticsService to satisfy TypeScript\n // (These won't be called directly due to the proxy)\n initInsights(_config: InsightsConfig): void {}\n enableDebugMode(): void {}\n disableDebugMode(): void {}\n identify(_identity: InsightsIdentity): void {}\n trackPageView(_options?: TrackPageViewOptions): void {}\n track(_event: string, _properties?: Record<string, unknown>): void {}\n startSessionRecording(): void {}\n stopSessionRecording(): void {}\n setupObserver(): () => void {\n return () => {};\n }\n}\n"],"names":["InsightsService","logger","InsightsCommandQueue","executeInitInsights","config","debugMode","debug","realImplementation","initInsights","isInitialized","processQueue","queue","length","cmd","shift","methodName","args","fn","apply","e","error","_config","enableDebugMode","disableDebugMode","identify","_identity","trackPageView","_options","track","_event","_properties","startSessionRecording","stopSessionRecording","setupObserver","Proxy","get","target","prop","String","push"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/insights/command-queue.ts"],"sourcesContent":["import {\n AnalyticsService,\n Command,\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport { InsightsService } from \"./service\";\nimport * as logger from \"./logger\";\n\n// Queue handler that will collect commands before initialization\nexport class InsightsCommandQueue implements AnalyticsService {\n private isInitialized: boolean = false;\n private queue: Command[] = [];\n private debugMode: boolean = false;\n private realImplementation: InsightsService | null = null;\n\n constructor() {\n // Create a proxy that will either queue commands or execute them directly\n return new Proxy(this, {\n get: (target: InsightsCommandQueue, prop: keyof InsightsCommandQueue) => {\n // Return actual properties of the queue\n if (prop in target && typeof target[prop] !== \"function\") {\n return target[prop];\n }\n\n // Return a function that either queues or executes the method call\n return (...args: unknown[]) => {\n if (!target.isInitialized || !target.realImplementation) {\n // Queue the command for later execution\n if (prop === \"initInsights\") {\n // Special handling for initInsights - execute it right away\n target.executeInitInsights(args[0] as InsightsConfig);\n return;\n }\n\n // For debug logging\n if (target.debugMode) {\n logger.debug(`Queuing method call: ${String(prop)}`, args);\n }\n\n target.queue.push({\n methodName: prop,\n args,\n });\n\n return;\n }\n\n // Execute the command immediately on the real implementation\n if (typeof target.realImplementation[prop] === \"function\") {\n return (\n target.realImplementation[prop] as (...args: unknown[]) => unknown\n )(...args);\n }\n };\n },\n });\n }\n\n // Special handling for init since it needs to happen right away\n private executeInitInsights(config: InsightsConfig): void {\n this.debugMode = !!config.debug;\n\n if (this.debugMode) {\n logger.debug(\"Initializing insights\");\n }\n\n // Create and initialize the real implementation\n this.realImplementation = new InsightsService();\n this.realImplementation.initInsights(config);\n\n // Mark as initialized and process the queue\n this.isInitialized = true;\n this.processQueue();\n }\n\n // Process all queued commands\n private processQueue(): void {\n if (this.debugMode) {\n logger.debug(`Processing ${this.queue.length} queued commands`);\n }\n\n while (this.queue.length > 0) {\n const cmd = this.queue.shift();\n if (\n cmd &&\n this.realImplementation &&\n typeof this.realImplementation[cmd.methodName] === \"function\"\n ) {\n try {\n if (this.debugMode) {\n logger.debug(\n `Executing queued command: ${cmd.methodName}`,\n cmd.args,\n );\n }\n\n const fn = this.realImplementation[cmd.methodName] as (\n ...args: unknown[]\n ) => unknown;\n\n // Execute the command with the real implementation\n fn.apply(this.realImplementation, cmd.args as unknown[]);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\n `Error executing queued command: ${cmd.methodName}`,\n e,\n );\n }\n }\n }\n }\n }\n\n // Implement all methods required by AnalyticsService to satisfy TypeScript\n // (These won't be called directly due to the proxy)\n initInsights(_config: InsightsConfig): void {}\n enableDebugMode(): void {}\n disableDebugMode(): void {}\n identify(_identity: InsightsIdentity): void {}\n trackPageView(_options?: TrackPageViewOptions): void {}\n track(_event: string, _properties?: Record<string, unknown>): void {}\n setupObserver(): () => void {\n return () => {};\n }\n}\n"],"names":["InsightsService","logger","InsightsCommandQueue","executeInitInsights","config","debugMode","debug","realImplementation","initInsights","isInitialized","processQueue","queue","length","cmd","shift","methodName","args","fn","apply","e","error","_config","enableDebugMode","disableDebugMode","identify","_identity","trackPageView","_options","track","_event","_properties","setupObserver","Proxy","get","target","prop","String","push"],"mappings":"oLAOA,OAASA,eAAe,KAAQ,WAAY,AAC5C,WAAYC,WAAY,UAAW,AAGnC,QAAO,MAAMC,qBAkDX,AAAQC,oBAAoBC,MAAsB,CAAQ,CACxD,IAAI,CAACC,SAAS,CAAG,CAAC,CAACD,OAAOE,KAAK,CAE/B,GAAI,IAAI,CAACD,SAAS,CAAE,CAClBJ,OAAOK,KAAK,CAAC,wBACf,CAGA,IAAI,CAACC,kBAAkB,CAAG,IAAIP,gBAC9B,IAAI,CAACO,kBAAkB,CAACC,YAAY,CAACJ,OAGrC,CAAA,IAAI,CAACK,aAAa,CAAG,KACrB,IAAI,CAACC,YAAY,EACnB,CAGA,AAAQA,cAAqB,CAC3B,GAAI,IAAI,CAACL,SAAS,CAAE,CAClBJ,OAAOK,KAAK,CAAC,CAAC,WAAW,EAAE,IAAI,CAACK,KAAK,CAACC,MAAM,CAAC,gBAAgB,CAAC,CAChE,CAEA,MAAO,IAAI,CAACD,KAAK,CAACC,MAAM,CAAG,EAAG,CAC5B,MAAMC,IAAM,IAAI,CAACF,KAAK,CAACG,KAAK,GAC5B,GACED,KACA,IAAI,CAACN,kBAAkB,EACvB,OAAO,IAAI,CAACA,kBAAkB,CAACM,IAAIE,UAAU,CAAC,GAAK,WACnD,CACA,GAAI,CACF,GAAI,IAAI,CAACV,SAAS,CAAE,CAClBJ,OAAOK,KAAK,CACV,CAAC,0BAA0B,EAAEO,IAAIE,UAAU,CAAC,CAAC,CAC7CF,IAAIG,IAAI,CAEZ,CAEA,MAAMC,GAAK,IAAI,CAACV,kBAAkB,CAACM,IAAIE,UAAU,CAAC,CAKlDE,GAAGC,KAAK,CAAC,IAAI,CAACX,kBAAkB,CAAEM,IAAIG,IAAI,CAC5C,CAAE,MAAOG,EAAG,CACV,GAAI,IAAI,CAACd,SAAS,CAAE,CAClBJ,OAAOmB,KAAK,CACV,CAAC,gCAAgC,EAAEP,IAAIE,UAAU,CAAC,CAAC,CACnDI,EAEJ,CACF,CACF,CACF,CACF,CAIAX,aAAaa,OAAuB,CAAQ,CAAC,CAC7CC,iBAAwB,CAAC,CACzBC,kBAAyB,CAAC,CAC1BC,SAASC,SAA2B,CAAQ,CAAC,CAC7CC,cAAcC,QAA+B,CAAQ,CAAC,CACtDC,MAAMC,MAAc,CAAEC,WAAqC,CAAQ,CAAC,CACpEC,eAA4B,CAC1B,MAAO,KAAO,CAChB,CA7GA,aAAc,CALd,sBAAQtB,gBAAyB,OACjC,sBAAQE,QAAmB,EAAE,EAC7B,sBAAQN,YAAqB,OAC7B,sBAAQE,qBAA6C,MAInD,OAAO,IAAIyB,MAAM,IAAI,CAAE,CACrBC,IAAK,CAACC,OAA8BC,QAElC,GAAIA,QAAQD,QAAU,OAAOA,MAAM,CAACC,KAAK,GAAK,WAAY,CACxD,OAAOD,MAAM,CAACC,KAAK,AACrB,CAGA,MAAO,CAAC,GAAGnB,QACT,GAAI,CAACkB,OAAOzB,aAAa,EAAI,CAACyB,OAAO3B,kBAAkB,CAAE,CAEvD,GAAI4B,OAAS,eAAgB,CAE3BD,OAAO/B,mBAAmB,CAACa,IAAI,CAAC,EAAE,EAClC,MACF,CAGA,GAAIkB,OAAO7B,SAAS,CAAE,CACpBJ,OAAOK,KAAK,CAAC,CAAC,qBAAqB,EAAE8B,OAAOD,MAAM,CAAC,CAAEnB,KACvD,CAEAkB,OAAOvB,KAAK,CAAC0B,IAAI,CAAC,CAChBtB,WAAYoB,KACZnB,IACF,GAEA,MACF,CAGA,GAAI,OAAOkB,OAAO3B,kBAAkB,CAAC4B,KAAK,GAAK,WAAY,CACzD,OAAO,AACLD,OAAO3B,kBAAkB,CAAC4B,KAAK,IAC5BnB,KACP,CACF,CACF,CACF,EACF,CAqEF"}
@@ -1,2 +1,2 @@
1
- import{InsightsCommandQueue}from"./command-queue";const insights=new InsightsCommandQueue;export const initInsights=config=>insights.initInsights(config);export const enableDebugMode=()=>insights.enableDebugMode();export const disableDebugMode=()=>insights.disableDebugMode();export const identify=identity=>insights.identify(identity);export const trackPageView=options=>insights.trackPageView(options);export const track=(event,properties)=>insights.track(event,properties);export const startSessionRecording=()=>insights.startSessionRecording();export const stopSessionRecording=()=>insights.stopSessionRecording();export const setupObserver=()=>insights.setupObserver();
1
+ import{InsightsCommandQueue}from"./command-queue";const insights=new InsightsCommandQueue;export const initInsights=config=>insights.initInsights(config);export const enableDebugMode=()=>insights.enableDebugMode();export const disableDebugMode=()=>insights.disableDebugMode();export const identify=identity=>insights.identify(identity);export const trackPageView=options=>insights.trackPageView(options);export const track=(event,properties)=>insights.track(event,properties);export const setupObserver=()=>insights.setupObserver();
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/index.ts"],"sourcesContent":["import {\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport { InsightsCommandQueue } from \"./command-queue\";\nexport type { InsightsConfig };\n\n// Hi and welcome 👋\n//\n// The insights code is written using a Command Queue, or Deferred Execution pattern.\n// This pattern is useful when you need to queue up actions that should wait until\n// some initialization is complete. In this case, we want to queue up all the analytics\n// commands until the analytics service is initialized. This way, we can ensure that\n// no analytics events are lost during the initialization process. It looks wildly\n// different than other parts of Ably UI, but if you squint you realise it looks very\n// much like the services it's wrapping.\n//\n// There are three pieces working together here:\n// - The `AnalyticsService` interface, which defines the public methods that the insights\n// service will expose.\n// - The `InsightsCommandQueue` class, which is the main entry point for the insights\n// service. It acts as a proxy that will either queue up commands or execute\n// them directly on the real implementation.\n// - The `InsightsService` class, which is the real implementation that will be used\n// after initialization. It's responsible for initializing the underlying analytics\n// services (Mixpanel, Posthog & the data layer) and executing the queued commands.\n\n// Create the singleton instance with the command queue pattern\nconst insights = new InsightsCommandQueue();\n\n// Export the methods with the same interface as before\nexport const initInsights = (config: InsightsConfig) =>\n insights.initInsights(config);\nexport const enableDebugMode = () => insights.enableDebugMode();\nexport const disableDebugMode = () => insights.disableDebugMode();\nexport const identify = (identity: InsightsIdentity) =>\n insights.identify(identity);\nexport const trackPageView = (options?: TrackPageViewOptions) =>\n insights.trackPageView(options);\nexport const track = (event: string, properties?: Record<string, unknown>) =>\n insights.track(event, properties);\nexport const startSessionRecording = () => insights.startSessionRecording();\nexport const stopSessionRecording = () => insights.stopSessionRecording();\nexport const setupObserver = () => insights.setupObserver();\n"],"names":["InsightsCommandQueue","insights","initInsights","config","enableDebugMode","disableDebugMode","identify","identity","trackPageView","options","track","event","properties","startSessionRecording","stopSessionRecording","setupObserver"],"mappings":"AAKA,OAASA,oBAAoB,KAAQ,iBAAkB,CAwBvD,MAAMC,SAAW,IAAID,oBAGrB,QAAO,MAAME,aAAe,AAACC,QAC3BF,SAASC,YAAY,CAACC,OAAQ,AAChC,QAAO,MAAMC,gBAAkB,IAAMH,SAASG,eAAe,EAAG,AAChE,QAAO,MAAMC,iBAAmB,IAAMJ,SAASI,gBAAgB,EAAG,AAClE,QAAO,MAAMC,SAAW,AAACC,UACvBN,SAASK,QAAQ,CAACC,SAAU,AAC9B,QAAO,MAAMC,cAAgB,AAACC,SAC5BR,SAASO,aAAa,CAACC,QAAS,AAClC,QAAO,MAAMC,MAAQ,CAACC,MAAeC,aACnCX,SAASS,KAAK,CAACC,MAAOC,WAAY,AACpC,QAAO,MAAMC,sBAAwB,IAAMZ,SAASY,qBAAqB,EAAG,AAC5E,QAAO,MAAMC,qBAAuB,IAAMb,SAASa,oBAAoB,EAAG,AAC1E,QAAO,MAAMC,cAAgB,IAAMd,SAASc,aAAa,EAAG"}
1
+ {"version":3,"sources":["../../../src/core/insights/index.ts"],"sourcesContent":["import {\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport { InsightsCommandQueue } from \"./command-queue\";\nexport type { InsightsConfig };\n\n// Hi and welcome 👋\n//\n// The insights code is written using a Command Queue, or Deferred Execution pattern.\n// This pattern is useful when you need to queue up actions that should wait until\n// some initialization is complete. In this case, we want to queue up all the analytics\n// commands until the analytics service is initialized. This way, we can ensure that\n// no analytics events are lost during the initialization process. It looks wildly\n// different than other parts of Ably UI, but if you squint you realise it looks very\n// much like the services it's wrapping.\n//\n// There are three pieces working together here:\n// - The `AnalyticsService` interface, which defines the public methods that the insights\n// service will expose.\n// - The `InsightsCommandQueue` class, which is the main entry point for the insights\n// service. It acts as a proxy that will either queue up commands or execute\n// them directly on the real implementation.\n// - The `InsightsService` class, which is the real implementation that will be used\n// after initialization. It's responsible for initializing the underlying analytics\n// services (Mixpanel, Posthog & the data layer) and executing the queued commands.\n\n// Create the singleton instance with the command queue pattern\nconst insights = new InsightsCommandQueue();\n\n// Export the methods with the same interface as before\nexport const initInsights = (config: InsightsConfig) =>\n insights.initInsights(config);\nexport const enableDebugMode = () => insights.enableDebugMode();\nexport const disableDebugMode = () => insights.disableDebugMode();\nexport const identify = (identity: InsightsIdentity) =>\n insights.identify(identity);\nexport const trackPageView = (options?: TrackPageViewOptions) =>\n insights.trackPageView(options);\nexport const track = (event: string, properties?: Record<string, unknown>) =>\n insights.track(event, properties);\nexport const setupObserver = () => insights.setupObserver();\n"],"names":["InsightsCommandQueue","insights","initInsights","config","enableDebugMode","disableDebugMode","identify","identity","trackPageView","options","track","event","properties","setupObserver"],"mappings":"AAKA,OAASA,oBAAoB,KAAQ,iBAAkB,CAwBvD,MAAMC,SAAW,IAAID,oBAGrB,QAAO,MAAME,aAAe,AAACC,QAC3BF,SAASC,YAAY,CAACC,OAAQ,AAChC,QAAO,MAAMC,gBAAkB,IAAMH,SAASG,eAAe,EAAG,AAChE,QAAO,MAAMC,iBAAmB,IAAMJ,SAASI,gBAAgB,EAAG,AAClE,QAAO,MAAMC,SAAW,AAACC,UACvBN,SAASK,QAAQ,CAACC,SAAU,AAC9B,QAAO,MAAMC,cAAgB,AAACC,SAC5BR,SAASO,aAAa,CAACC,QAAS,AAClC,QAAO,MAAMC,MAAQ,CAACC,MAAeC,aACnCX,SAASS,KAAK,CAACC,MAAOC,WAAY,AACpC,QAAO,MAAMC,cAAgB,IAAMZ,SAASY,aAAa,EAAG"}
@@ -1,2 +1,2 @@
1
- import{describe,expect,beforeEach,afterEach,it,vi}from"vitest";import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";import*as logger from"./logger";import*as insights from"./index";vi.mock("./datalayer",()=>({track:vi.fn(),trackPageView:vi.fn()}));vi.mock("./mixpanel",()=>({initMixpanel:vi.fn(),enableDebugMode:vi.fn(),disableDebugMode:vi.fn(),identify:vi.fn(),trackPageView:vi.fn(),track:vi.fn(),startSessionRecording:vi.fn(),stopSessionRecording:vi.fn()}));vi.mock("./posthog",()=>({initPosthog:vi.fn(),enableDebugMode:vi.fn(),disableDebugMode:vi.fn(),identify:vi.fn(),trackPageView:vi.fn(),track:vi.fn(),startSessionRecording:vi.fn(),stopSessionRecording:vi.fn()}));vi.mock("./logger",()=>({debug:vi.fn(),info:vi.fn(),warn:vi.fn(),error:vi.fn()}));describe("Insights Command Queue",()=>{const testConfig={debug:true,mixpanelToken:"test-token",mixpanelAutoCapture:false,mixpanelRecordSessionsPercent:10,posthogApiKey:"test-key",posthogApiHost:"test-host"};const testIdentity={userId:"user-123",accountId:"account-456",organisationId:"org-789",email:"test@example.com",name:"Test User"};beforeEach(()=>{vi.clearAllMocks();vi.resetModules()});afterEach(()=>{document.body.replaceWith(document.body.cloneNode(true))});describe("Pre-initialization Queueing",()=>{it("should queue methods called before initialization",async()=>{insights.track("early_event",{early:true});insights.identify(testIdentity);insights.trackPageView();expect(mixpanel.track).not.toHaveBeenCalled();expect(posthog.track).not.toHaveBeenCalled();expect(datalayer.track).not.toHaveBeenCalled();expect(mixpanel.identify).not.toHaveBeenCalled();expect(posthog.identify).not.toHaveBeenCalled();expect(mixpanel.trackPageView).not.toHaveBeenCalled();expect(posthog.trackPageView).not.toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled();insights.initInsights(testConfig);expect(mixpanel.initMixpanel).toHaveBeenCalledWith(testConfig.mixpanelToken,testConfig.mixpanelAutoCapture,testConfig.debug,testConfig.mixpanelRecordSessionsPercent);expect(posthog.initPosthog).toHaveBeenCalledWith(testConfig.posthogApiKey,testConfig.posthogApiHost);expect(mixpanel.track).toHaveBeenCalledWith("early_event",{early:true});expect(posthog.track).toHaveBeenCalledWith("early_event",{early:true});expect(datalayer.track).toHaveBeenCalledWith("early_event",{early:true});expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);expect(posthog.identify).toHaveBeenCalledWith(testIdentity);expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should handle errors in queued methods gracefully",async()=>{mixpanel.track.mockImplementationOnce(()=>{throw new Error("Mixpanel error")});insights.track("error_event",{error:true});insights.trackPageView();insights.initInsights(testConfig);expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Mixpanel"),expect.any(Error));expect(posthog.track).toHaveBeenCalledWith("error_event",{error:true});expect(datalayer.track).toHaveBeenCalledWith("error_event",{error:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should report page view to GTM as well when includeDataLayer is true",()=>{insights.trackPageView({includeDataLayer:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).toHaveBeenCalled()})});describe("Post-initialization Direct Execution",()=>{beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should directly call methods after initialization",()=>{insights.track("post_init_event",{post:true});expect(mixpanel.track).toHaveBeenCalledWith("post_init_event",{post:true});expect(posthog.track).toHaveBeenCalledWith("post_init_event",{post:true});expect(datalayer.track).toHaveBeenCalledWith("post_init_event",{post:true})});it("should handle all exported methods correctly",()=>{insights.identify(testIdentity);expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);expect(posthog.identify).toHaveBeenCalledWith(testIdentity);insights.trackPageView();expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled();insights.startSessionRecording();expect(mixpanel.startSessionRecording).toHaveBeenCalled();expect(posthog.startSessionRecording).toHaveBeenCalled();insights.stopSessionRecording();expect(mixpanel.stopSessionRecording).toHaveBeenCalled();expect(posthog.stopSessionRecording).toHaveBeenCalled();insights.enableDebugMode();expect(mixpanel.enableDebugMode).toHaveBeenCalled();expect(posthog.enableDebugMode).toHaveBeenCalled();insights.disableDebugMode();expect(mixpanel.disableDebugMode).toHaveBeenCalled();expect(posthog.disableDebugMode).toHaveBeenCalled()});it("should report page view to GTM as well when includeDataLayer is true",()=>{insights.trackPageView({includeDataLayer:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).toHaveBeenCalled()})});describe("Observer Setup",()=>{beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should set up click event observer and track clicks",()=>{const cleanup=insights.setupObserver();const testElement=document.createElement("button");testElement.setAttribute("data-insight-event","button_clicked");testElement.setAttribute("data-insight-button-id","test-123");document.body.appendChild(testElement);testElement.click();expect(mixpanel.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});expect(posthog.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});expect(datalayer.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});cleanup();vi.clearAllMocks();testElement.click();expect(mixpanel.track).not.toHaveBeenCalled()});it("should handle nested elements correctly",()=>{insights.setupObserver();const parentElement=document.createElement("div");parentElement.setAttribute("data-insight-event","container_clicked");parentElement.setAttribute("data-insight-container-id","parent-container");const childElement=document.createElement("span");childElement.textContent="Click me";parentElement.appendChild(childElement);document.body.appendChild(parentElement);childElement.click();expect(mixpanel.track).toHaveBeenCalledWith("container_clicked",{containerId:"parent-container"})})});describe("Error Handling",()=>{it("should handle initialization errors gracefully",()=>{mixpanel.initMixpanel.mockImplementationOnce(()=>{throw new Error("Mixpanel init error")});expect(()=>{insights.initInsights(testConfig)}).not.toThrow();expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to initialize Mixpanel"),expect.any(Error))});it("should handle runtime errors in methods",()=>{insights.initInsights(testConfig);vi.clearAllMocks();mixpanel.track.mockImplementationOnce(()=>{throw new Error("Mixpanel track error")});posthog.track.mockImplementationOnce(()=>{throw new Error("Posthog track error")});expect(()=>{insights.track("error_test",{test:true})}).not.toThrow();expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Mixpanel"),expect.any(Error));expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Posthog"),expect.any(Error));expect(datalayer.track).toHaveBeenCalledWith("error_test",{test:true})})});describe("Debug Mode",()=>{it("should respect debug flag in config",()=>{insights.initInsights(testConfig);expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("Initializing insights"));vi.clearAllMocks();insights.track("debug_test",{debug:true});expect(logger.info).toHaveBeenCalledWith(expect.stringContaining("Tracking event"),expect.objectContaining({event:"debug_test",properties:{debug:true}}))});it("should not log debug info when debug is false",()=>{insights.initInsights({...testConfig,debug:false});vi.clearAllMocks();insights.track("no_debug_test",{debug:false});expect(logger.info).not.toHaveBeenCalled()})});describe("Arbitrary Properties",()=>{const customPageViewProperties={customProperty:"custom-value",anotherProperty:123,nestedObject:{foo:"bar"}};const customPageViewPropertiesWithDataLayer={customProperty:"custom-value",anotherProperty:456};const customPageViewPropertiesWithExcludeIds={customProperty:"test",count:789};const customIdentityProperties={customProperty:"user-custom-value",userSegment:"premium",signupDate:"2024-01-01"};const customMinimalIdentityProperties={customField1:"value1",customField2:999,customField3:{nested:true}};beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should pass arbitrary properties through trackPageView",()=>{insights.trackPageView(customPageViewProperties);expect(mixpanel.trackPageView).toHaveBeenCalledWith(customPageViewProperties);expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewProperties);expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should pass arbitrary properties through trackPageView with includeDataLayer",()=>{insights.trackPageView({includeDataLayer:true,...customPageViewPropertiesWithDataLayer});expect(mixpanel.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer);expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer);expect(datalayer.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer)});it("should pass arbitrary properties through trackPageView with excludeIds",()=>{const excludeIds=["id1","id2"];insights.trackPageView({excludeIds,...customPageViewPropertiesWithExcludeIds});expect(mixpanel.trackPageView).toHaveBeenCalledWith({excludeIds,...customPageViewPropertiesWithExcludeIds});expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithExcludeIds)});it("should pass arbitrary properties through identify",()=>{const identityWithCustomProps={...testIdentity,...customIdentityProperties};insights.identify(identityWithCustomProps);expect(mixpanel.identify).toHaveBeenCalledWith(identityWithCustomProps);expect(posthog.identify).toHaveBeenCalledWith(identityWithCustomProps)});it("should pass arbitrary properties through identify with minimal identity",()=>{const minimalIdentity={userId:"minimal-user",accountId:"minimal-account",...customMinimalIdentityProperties};insights.identify(minimalIdentity);expect(mixpanel.identify).toHaveBeenCalledWith(minimalIdentity);expect(posthog.identify).toHaveBeenCalledWith(minimalIdentity)})})});
1
+ import{describe,expect,beforeEach,afterEach,it,vi}from"vitest";import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";import*as logger from"./logger";import*as insights from"./index";vi.mock("./datalayer",()=>({track:vi.fn(),trackPageView:vi.fn()}));vi.mock("./mixpanel",()=>({initMixpanel:vi.fn(),enableDebugMode:vi.fn(),disableDebugMode:vi.fn(),identify:vi.fn(),trackPageView:vi.fn(),track:vi.fn()}));vi.mock("./posthog",()=>({initPosthog:vi.fn(),enableDebugMode:vi.fn(),disableDebugMode:vi.fn(),identify:vi.fn(),trackPageView:vi.fn(),track:vi.fn()}));vi.mock("./logger",()=>({debug:vi.fn(),info:vi.fn(),warn:vi.fn(),error:vi.fn()}));describe("Insights Command Queue",()=>{const testConfig={debug:true,mixpanelToken:"test-token",mixpanelAutoCapture:false,posthogApiKey:"test-key",posthogApiHost:"test-host"};const testIdentity={userId:"user-123",accountId:"account-456",organisationId:"org-789",email:"test@example.com",name:"Test User"};beforeEach(()=>{vi.clearAllMocks();vi.resetModules()});afterEach(()=>{document.body.replaceWith(document.body.cloneNode(true))});describe("Pre-initialization Queueing",()=>{it("should queue methods called before initialization",async()=>{insights.track("early_event",{early:true});insights.identify(testIdentity);insights.trackPageView();expect(mixpanel.track).not.toHaveBeenCalled();expect(posthog.track).not.toHaveBeenCalled();expect(datalayer.track).not.toHaveBeenCalled();expect(mixpanel.identify).not.toHaveBeenCalled();expect(posthog.identify).not.toHaveBeenCalled();expect(mixpanel.trackPageView).not.toHaveBeenCalled();expect(posthog.trackPageView).not.toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled();insights.initInsights(testConfig);expect(mixpanel.initMixpanel).toHaveBeenCalledWith(testConfig.mixpanelToken,testConfig.mixpanelAutoCapture,testConfig.debug,undefined);expect(posthog.initPosthog).toHaveBeenCalledWith(testConfig.posthogApiKey,testConfig.posthogApiHost);expect(mixpanel.track).toHaveBeenCalledWith("early_event",{early:true});expect(posthog.track).toHaveBeenCalledWith("early_event",{early:true});expect(datalayer.track).toHaveBeenCalledWith("early_event",{early:true});expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);expect(posthog.identify).toHaveBeenCalledWith(testIdentity);expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should handle errors in queued methods gracefully",async()=>{mixpanel.track.mockImplementationOnce(()=>{throw new Error("Mixpanel error")});insights.track("error_event",{error:true});insights.trackPageView();insights.initInsights(testConfig);expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Mixpanel"),expect.any(Error));expect(posthog.track).toHaveBeenCalledWith("error_event",{error:true});expect(datalayer.track).toHaveBeenCalledWith("error_event",{error:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should report page view to GTM as well when includeDataLayer is true",()=>{insights.trackPageView({includeDataLayer:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).toHaveBeenCalled()})});describe("Post-initialization Direct Execution",()=>{beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should directly call methods after initialization",()=>{insights.track("post_init_event",{post:true});expect(mixpanel.track).toHaveBeenCalledWith("post_init_event",{post:true});expect(posthog.track).toHaveBeenCalledWith("post_init_event",{post:true});expect(datalayer.track).toHaveBeenCalledWith("post_init_event",{post:true})});it("should handle all exported methods correctly",()=>{insights.identify(testIdentity);expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);expect(posthog.identify).toHaveBeenCalledWith(testIdentity);insights.trackPageView();expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).not.toHaveBeenCalled();insights.enableDebugMode();expect(mixpanel.enableDebugMode).toHaveBeenCalled();expect(posthog.enableDebugMode).toHaveBeenCalled();insights.disableDebugMode();expect(mixpanel.disableDebugMode).toHaveBeenCalled();expect(posthog.disableDebugMode).toHaveBeenCalled()});it("should report page view to GTM as well when includeDataLayer is true",()=>{insights.trackPageView({includeDataLayer:true});expect(mixpanel.trackPageView).toHaveBeenCalled();expect(posthog.trackPageView).toHaveBeenCalled();expect(datalayer.trackPageView).toHaveBeenCalled()})});describe("Observer Setup",()=>{beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should set up click event observer and track clicks",()=>{const cleanup=insights.setupObserver();const testElement=document.createElement("button");testElement.setAttribute("data-insight-event","button_clicked");testElement.setAttribute("data-insight-button-id","test-123");document.body.appendChild(testElement);testElement.click();expect(mixpanel.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});expect(posthog.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});expect(datalayer.track).toHaveBeenCalledWith("button_clicked",{buttonId:"test-123"});cleanup();vi.clearAllMocks();testElement.click();expect(mixpanel.track).not.toHaveBeenCalled()});it("should handle nested elements correctly",()=>{insights.setupObserver();const parentElement=document.createElement("div");parentElement.setAttribute("data-insight-event","container_clicked");parentElement.setAttribute("data-insight-container-id","parent-container");const childElement=document.createElement("span");childElement.textContent="Click me";parentElement.appendChild(childElement);document.body.appendChild(parentElement);childElement.click();expect(mixpanel.track).toHaveBeenCalledWith("container_clicked",{containerId:"parent-container"})})});describe("Error Handling",()=>{it("should handle initialization errors gracefully",()=>{mixpanel.initMixpanel.mockImplementationOnce(()=>{throw new Error("Mixpanel init error")});expect(()=>{insights.initInsights(testConfig)}).not.toThrow();expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to initialize Mixpanel"),expect.any(Error))});it("should handle runtime errors in methods",()=>{insights.initInsights(testConfig);vi.clearAllMocks();mixpanel.track.mockImplementationOnce(()=>{throw new Error("Mixpanel track error")});posthog.track.mockImplementationOnce(()=>{throw new Error("Posthog track error")});expect(()=>{insights.track("error_test",{test:true})}).not.toThrow();expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Mixpanel"),expect.any(Error));expect(logger.error).toHaveBeenCalledWith(expect.stringContaining("Failed to track event in Posthog"),expect.any(Error));expect(datalayer.track).toHaveBeenCalledWith("error_test",{test:true})})});describe("Debug Mode",()=>{it("should respect debug flag in config",()=>{insights.initInsights(testConfig);expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining("Initializing insights"));vi.clearAllMocks();insights.track("debug_test",{debug:true});expect(logger.info).toHaveBeenCalledWith(expect.stringContaining("Tracking event"),expect.objectContaining({event:"debug_test",properties:{debug:true}}))});it("should not log debug info when debug is false",()=>{insights.initInsights({...testConfig,debug:false});vi.clearAllMocks();insights.track("no_debug_test",{debug:false});expect(logger.info).not.toHaveBeenCalled()})});describe("Arbitrary Properties",()=>{const customPageViewProperties={customProperty:"custom-value",anotherProperty:123,nestedObject:{foo:"bar"}};const customPageViewPropertiesWithDataLayer={customProperty:"custom-value",anotherProperty:456};const customPageViewPropertiesWithExcludeIds={customProperty:"test",count:789};const customIdentityProperties={customProperty:"user-custom-value",userSegment:"premium",signupDate:"2024-01-01"};const customMinimalIdentityProperties={customField1:"value1",customField2:999,customField3:{nested:true}};beforeEach(()=>{insights.initInsights(testConfig);vi.clearAllMocks()});it("should pass arbitrary properties through trackPageView",()=>{insights.trackPageView(customPageViewProperties);expect(mixpanel.trackPageView).toHaveBeenCalledWith(customPageViewProperties);expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewProperties);expect(datalayer.trackPageView).not.toHaveBeenCalled()});it("should pass arbitrary properties through trackPageView with includeDataLayer",()=>{insights.trackPageView({includeDataLayer:true,...customPageViewPropertiesWithDataLayer});expect(mixpanel.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer);expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer);expect(datalayer.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithDataLayer)});it("should pass arbitrary properties through trackPageView with excludeIds",()=>{const excludeIds=["id1","id2"];insights.trackPageView({excludeIds,...customPageViewPropertiesWithExcludeIds});expect(mixpanel.trackPageView).toHaveBeenCalledWith({excludeIds,...customPageViewPropertiesWithExcludeIds});expect(posthog.trackPageView).toHaveBeenCalledWith(customPageViewPropertiesWithExcludeIds)});it("should pass arbitrary properties through identify",()=>{const identityWithCustomProps={...testIdentity,...customIdentityProperties};insights.identify(identityWithCustomProps);expect(mixpanel.identify).toHaveBeenCalledWith(identityWithCustomProps);expect(posthog.identify).toHaveBeenCalledWith(identityWithCustomProps)});it("should pass arbitrary properties through identify with minimal identity",()=>{const minimalIdentity={userId:"minimal-user",accountId:"minimal-account",...customMinimalIdentityProperties};insights.identify(minimalIdentity);expect(mixpanel.identify).toHaveBeenCalledWith(minimalIdentity);expect(posthog.identify).toHaveBeenCalledWith(minimalIdentity)})})});
2
2
  //# sourceMappingURL=index.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/index.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, afterEach, it, vi, Mock } from \"vitest\";\n\nimport * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport * as logger from \"./logger\";\nimport * as insights from \"./index\";\n\n// Mock the dependencies\nvi.mock(\"./datalayer\", () => ({\n track: vi.fn(),\n trackPageView: vi.fn(),\n}));\n\nvi.mock(\"./mixpanel\", () => ({\n initMixpanel: vi.fn(),\n enableDebugMode: vi.fn(),\n disableDebugMode: vi.fn(),\n identify: vi.fn(),\n trackPageView: vi.fn(),\n track: vi.fn(),\n startSessionRecording: vi.fn(),\n stopSessionRecording: vi.fn(),\n}));\n\nvi.mock(\"./posthog\", () => ({\n initPosthog: vi.fn(),\n enableDebugMode: vi.fn(),\n disableDebugMode: vi.fn(),\n identify: vi.fn(),\n trackPageView: vi.fn(),\n track: vi.fn(),\n startSessionRecording: vi.fn(),\n stopSessionRecording: vi.fn(),\n}));\n\nvi.mock(\"./logger\", () => ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n}));\n\ndescribe(\"Insights Command Queue\", () => {\n const testConfig = {\n debug: true,\n mixpanelToken: \"test-token\",\n mixpanelAutoCapture: false,\n mixpanelRecordSessionsPercent: 10,\n posthogApiKey: \"test-key\",\n posthogApiHost: \"test-host\",\n };\n\n const testIdentity = {\n userId: \"user-123\",\n accountId: \"account-456\",\n organisationId: \"org-789\",\n email: \"test@example.com\",\n name: \"Test User\",\n };\n\n beforeEach(() => {\n // Clear all mocks before each test\n vi.clearAllMocks();\n\n // Reset the module to clear any internal state\n vi.resetModules();\n });\n\n afterEach(() => {\n // Cleanup document event listeners\n document.body.replaceWith(document.body.cloneNode(true));\n });\n\n describe(\"Pre-initialization Queueing\", () => {\n it(\"should queue methods called before initialization\", async () => {\n // Call methods before initialization\n insights.track(\"early_event\", { early: true });\n insights.identify(testIdentity);\n insights.trackPageView();\n\n // Verify nothing has been called yet on the underlying services\n expect(mixpanel.track).not.toHaveBeenCalled();\n expect(posthog.track).not.toHaveBeenCalled();\n expect(datalayer.track).not.toHaveBeenCalled();\n expect(mixpanel.identify).not.toHaveBeenCalled();\n expect(posthog.identify).not.toHaveBeenCalled();\n expect(mixpanel.trackPageView).not.toHaveBeenCalled();\n expect(posthog.trackPageView).not.toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n\n // Now initialize\n insights.initInsights(testConfig);\n\n // Initialize should be called immediately\n expect(mixpanel.initMixpanel).toHaveBeenCalledWith(\n testConfig.mixpanelToken,\n testConfig.mixpanelAutoCapture,\n testConfig.debug,\n testConfig.mixpanelRecordSessionsPercent,\n );\n expect(posthog.initPosthog).toHaveBeenCalledWith(\n testConfig.posthogApiKey,\n testConfig.posthogApiHost,\n );\n\n // Queued methods should now be called in the correct order\n expect(mixpanel.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n expect(posthog.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n\n expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);\n expect(posthog.identify).toHaveBeenCalledWith(testIdentity);\n\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should handle errors in queued methods gracefully\", async () => {\n // Setup an error for one of the methods\n (mixpanel.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel error\");\n });\n\n // Call methods before initialization\n insights.track(\"error_event\", { error: true });\n insights.trackPageView();\n\n // Now initialize\n insights.initInsights(testConfig);\n\n // Should have logged the error but continued processing the queue\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Mixpanel\"),\n expect.any(Error),\n );\n\n // The other methods should still be called\n expect(posthog.track).toHaveBeenCalledWith(\"error_event\", {\n error: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"error_event\", {\n error: true,\n });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should report page view to GTM as well when includeDataLayer is true\", () => {\n insights.trackPageView({ includeDataLayer: true });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).toHaveBeenCalled();\n });\n });\n\n describe(\"Post-initialization Direct Execution\", () => {\n beforeEach(() => {\n // Initialize first\n insights.initInsights(testConfig);\n // Clear the mocks to focus on post-init behavior\n vi.clearAllMocks();\n });\n\n it(\"should directly call methods after initialization\", () => {\n // Call methods after initialization\n insights.track(\"post_init_event\", { post: true });\n\n // Should be called immediately\n expect(mixpanel.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n expect(posthog.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n });\n\n it(\"should handle all exported methods correctly\", () => {\n // Test each exported method\n insights.identify(testIdentity);\n expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);\n expect(posthog.identify).toHaveBeenCalledWith(testIdentity);\n\n insights.trackPageView();\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n\n insights.startSessionRecording();\n expect(mixpanel.startSessionRecording).toHaveBeenCalled();\n expect(posthog.startSessionRecording).toHaveBeenCalled();\n\n insights.stopSessionRecording();\n expect(mixpanel.stopSessionRecording).toHaveBeenCalled();\n expect(posthog.stopSessionRecording).toHaveBeenCalled();\n\n insights.enableDebugMode();\n expect(mixpanel.enableDebugMode).toHaveBeenCalled();\n expect(posthog.enableDebugMode).toHaveBeenCalled();\n\n insights.disableDebugMode();\n expect(mixpanel.disableDebugMode).toHaveBeenCalled();\n expect(posthog.disableDebugMode).toHaveBeenCalled();\n });\n\n it(\"should report page view to GTM as well when includeDataLayer is true\", () => {\n insights.trackPageView({ includeDataLayer: true });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).toHaveBeenCalled();\n });\n });\n\n describe(\"Observer Setup\", () => {\n beforeEach(() => {\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n });\n\n it(\"should set up click event observer and track clicks\", () => {\n // Setup observer\n const cleanup = insights.setupObserver();\n\n // Create a test element with insight attributes\n const testElement = document.createElement(\"button\");\n testElement.setAttribute(\"data-insight-event\", \"button_clicked\");\n testElement.setAttribute(\"data-insight-button-id\", \"test-123\");\n document.body.appendChild(testElement);\n\n // Simulate click\n testElement.click();\n\n // Should track the event\n expect(mixpanel.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n expect(posthog.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n\n // Test cleanup\n cleanup();\n\n // Reset tracking mocks\n vi.clearAllMocks();\n\n // Click again - should not track\n testElement.click();\n expect(mixpanel.track).not.toHaveBeenCalled();\n });\n\n it(\"should handle nested elements correctly\", () => {\n // Setup observer\n insights.setupObserver();\n\n // Create parent element with insight attributes\n const parentElement = document.createElement(\"div\");\n parentElement.setAttribute(\"data-insight-event\", \"container_clicked\");\n parentElement.setAttribute(\n \"data-insight-container-id\",\n \"parent-container\",\n );\n\n // Create child element without insights\n const childElement = document.createElement(\"span\");\n childElement.textContent = \"Click me\";\n\n // Nest elements\n parentElement.appendChild(childElement);\n document.body.appendChild(parentElement);\n\n // Click the child element\n childElement.click();\n\n // Should find and use the parent's insight attributes\n expect(mixpanel.track).toHaveBeenCalledWith(\"container_clicked\", {\n containerId: \"parent-container\",\n });\n });\n });\n\n describe(\"Error Handling\", () => {\n it(\"should handle initialization errors gracefully\", () => {\n // Setup an error in initialization\n (mixpanel.initMixpanel as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel init error\");\n });\n\n // Should not throw when initializing\n expect(() => {\n insights.initInsights(testConfig);\n }).not.toThrow();\n\n // Should log the error\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to initialize Mixpanel\"),\n expect.any(Error),\n );\n });\n\n it(\"should handle runtime errors in methods\", () => {\n // Initialize first\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n\n // Setup errors in tracking\n (mixpanel.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel track error\");\n });\n\n (posthog.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Posthog track error\");\n });\n\n // Should not throw when tracking\n expect(() => {\n insights.track(\"error_test\", { test: true });\n }).not.toThrow();\n\n // Should log the errors\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Mixpanel\"),\n expect.any(Error),\n );\n\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Posthog\"),\n expect.any(Error),\n );\n\n // Should still try to track with datalayer\n expect(datalayer.track).toHaveBeenCalledWith(\"error_test\", {\n test: true,\n });\n });\n });\n\n describe(\"Debug Mode\", () => {\n it(\"should respect debug flag in config\", () => {\n // Initialize with debug: true\n insights.initInsights(testConfig);\n\n // Should log debug messages\n expect(logger.debug).toHaveBeenCalledWith(\n expect.stringContaining(\"Initializing insights\"),\n );\n\n // Clear mocks and test tracking\n vi.clearAllMocks();\n insights.track(\"debug_test\", { debug: true });\n\n // Should log info about tracking\n expect(logger.info).toHaveBeenCalledWith(\n expect.stringContaining(\"Tracking event\"),\n expect.objectContaining({\n event: \"debug_test\",\n properties: { debug: true },\n }),\n );\n });\n\n it(\"should not log debug info when debug is false\", () => {\n // Initialize with debug: false\n insights.initInsights({\n ...testConfig,\n debug: false,\n });\n\n // Clear mocks and test tracking\n vi.clearAllMocks();\n insights.track(\"no_debug_test\", { debug: false });\n\n // Should not log info about tracking\n expect(logger.info).not.toHaveBeenCalled();\n });\n });\n\n describe(\"Arbitrary Properties\", () => {\n const customPageViewProperties = {\n customProperty: \"custom-value\",\n anotherProperty: 123,\n nestedObject: { foo: \"bar\" },\n };\n\n const customPageViewPropertiesWithDataLayer = {\n customProperty: \"custom-value\",\n anotherProperty: 456,\n };\n\n const customPageViewPropertiesWithExcludeIds = {\n customProperty: \"test\",\n count: 789,\n };\n\n const customIdentityProperties = {\n customProperty: \"user-custom-value\",\n userSegment: \"premium\",\n signupDate: \"2024-01-01\",\n };\n\n const customMinimalIdentityProperties = {\n customField1: \"value1\",\n customField2: 999,\n customField3: { nested: true },\n };\n\n beforeEach(() => {\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n });\n\n it(\"should pass arbitrary properties through trackPageView\", () => {\n // Call trackPageView with custom properties\n insights.trackPageView(customPageViewProperties);\n\n // Verify mixpanel received all properties\n expect(mixpanel.trackPageView).toHaveBeenCalledWith(\n customPageViewProperties,\n );\n\n // Verify posthog received all properties\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewProperties,\n );\n\n // Verify datalayer did not receive anything (includeDataLayer not set)\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should pass arbitrary properties through trackPageView with includeDataLayer\", () => {\n // Call trackPageView with custom properties and includeDataLayer\n insights.trackPageView({\n includeDataLayer: true,\n ...customPageViewPropertiesWithDataLayer,\n });\n\n // Verify mixpanel received custom properties (includeDataLayer filtered out)\n expect(mixpanel.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n\n // Verify posthog received custom properties (includeDataLayer filtered out)\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n\n // Verify datalayer received custom properties (includeDataLayer filtered out)\n expect(datalayer.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n });\n\n it(\"should pass arbitrary properties through trackPageView with excludeIds\", () => {\n const excludeIds = [\"id1\", \"id2\"];\n\n // Call trackPageView with custom properties and excludeIds\n insights.trackPageView({\n excludeIds,\n ...customPageViewPropertiesWithExcludeIds,\n });\n\n // Verify mixpanel received excludeIds and custom properties\n expect(mixpanel.trackPageView).toHaveBeenCalledWith({\n excludeIds,\n ...customPageViewPropertiesWithExcludeIds,\n });\n\n // Verify posthog received only custom properties (excludeIds filtered out)\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithExcludeIds,\n );\n });\n\n it(\"should pass arbitrary properties through identify\", () => {\n const identityWithCustomProps = {\n ...testIdentity,\n ...customIdentityProperties,\n };\n\n // Call identify with custom properties\n insights.identify(identityWithCustomProps);\n\n // Verify mixpanel received all properties\n expect(mixpanel.identify).toHaveBeenCalledWith(identityWithCustomProps);\n\n // Verify posthog received all properties\n expect(posthog.identify).toHaveBeenCalledWith(identityWithCustomProps);\n });\n\n it(\"should pass arbitrary properties through identify with minimal identity\", () => {\n const minimalIdentity = {\n userId: \"minimal-user\",\n accountId: \"minimal-account\",\n ...customMinimalIdentityProperties,\n };\n\n // Call identify with only required fields plus custom properties\n insights.identify(minimalIdentity);\n\n // Verify mixpanel received all properties\n expect(mixpanel.identify).toHaveBeenCalledWith(minimalIdentity);\n\n // Verify posthog received all properties\n expect(posthog.identify).toHaveBeenCalledWith(minimalIdentity);\n });\n });\n});\n"],"names":["describe","expect","beforeEach","afterEach","it","vi","datalayer","mixpanel","posthog","logger","insights","mock","track","fn","trackPageView","initMixpanel","enableDebugMode","disableDebugMode","identify","startSessionRecording","stopSessionRecording","initPosthog","debug","info","warn","error","testConfig","mixpanelToken","mixpanelAutoCapture","mixpanelRecordSessionsPercent","posthogApiKey","posthogApiHost","testIdentity","userId","accountId","organisationId","email","name","clearAllMocks","resetModules","document","body","replaceWith","cloneNode","early","not","toHaveBeenCalled","initInsights","toHaveBeenCalledWith","mockImplementationOnce","Error","stringContaining","any","includeDataLayer","post","cleanup","setupObserver","testElement","createElement","setAttribute","appendChild","click","buttonId","parentElement","childElement","textContent","containerId","toThrow","test","objectContaining","event","properties","customPageViewProperties","customProperty","anotherProperty","nestedObject","foo","customPageViewPropertiesWithDataLayer","customPageViewPropertiesWithExcludeIds","count","customIdentityProperties","userSegment","signupDate","customMinimalIdentityProperties","customField1","customField2","customField3","nested","excludeIds","identityWithCustomProps","minimalIdentity"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/insights/index.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, afterEach, it, vi, Mock } from \"vitest\";\n\nimport * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport * as logger from \"./logger\";\nimport * as insights from \"./index\";\n\n// Mock the dependencies\nvi.mock(\"./datalayer\", () => ({\n track: vi.fn(),\n trackPageView: vi.fn(),\n}));\n\nvi.mock(\"./mixpanel\", () => ({\n initMixpanel: vi.fn(),\n enableDebugMode: vi.fn(),\n disableDebugMode: vi.fn(),\n identify: vi.fn(),\n trackPageView: vi.fn(),\n track: vi.fn(),\n}));\n\nvi.mock(\"./posthog\", () => ({\n initPosthog: vi.fn(),\n enableDebugMode: vi.fn(),\n disableDebugMode: vi.fn(),\n identify: vi.fn(),\n trackPageView: vi.fn(),\n track: vi.fn(),\n}));\n\nvi.mock(\"./logger\", () => ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n}));\n\ndescribe(\"Insights Command Queue\", () => {\n const testConfig = {\n debug: true,\n mixpanelToken: \"test-token\",\n mixpanelAutoCapture: false,\n posthogApiKey: \"test-key\",\n posthogApiHost: \"test-host\",\n };\n\n const testIdentity = {\n userId: \"user-123\",\n accountId: \"account-456\",\n organisationId: \"org-789\",\n email: \"test@example.com\",\n name: \"Test User\",\n };\n\n beforeEach(() => {\n // Clear all mocks before each test\n vi.clearAllMocks();\n\n // Reset the module to clear any internal state\n vi.resetModules();\n });\n\n afterEach(() => {\n // Cleanup document event listeners\n document.body.replaceWith(document.body.cloneNode(true));\n });\n\n describe(\"Pre-initialization Queueing\", () => {\n it(\"should queue methods called before initialization\", async () => {\n // Call methods before initialization\n insights.track(\"early_event\", { early: true });\n insights.identify(testIdentity);\n insights.trackPageView();\n\n // Verify nothing has been called yet on the underlying services\n expect(mixpanel.track).not.toHaveBeenCalled();\n expect(posthog.track).not.toHaveBeenCalled();\n expect(datalayer.track).not.toHaveBeenCalled();\n expect(mixpanel.identify).not.toHaveBeenCalled();\n expect(posthog.identify).not.toHaveBeenCalled();\n expect(mixpanel.trackPageView).not.toHaveBeenCalled();\n expect(posthog.trackPageView).not.toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n\n // Now initialize\n insights.initInsights(testConfig);\n\n // Initialize should be called immediately\n expect(mixpanel.initMixpanel).toHaveBeenCalledWith(\n testConfig.mixpanelToken,\n testConfig.mixpanelAutoCapture,\n testConfig.debug,\n undefined,\n );\n expect(posthog.initPosthog).toHaveBeenCalledWith(\n testConfig.posthogApiKey,\n testConfig.posthogApiHost,\n );\n\n // Queued methods should now be called in the correct order\n expect(mixpanel.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n expect(posthog.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"early_event\", {\n early: true,\n });\n\n expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);\n expect(posthog.identify).toHaveBeenCalledWith(testIdentity);\n\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should handle errors in queued methods gracefully\", async () => {\n // Setup an error for one of the methods\n (mixpanel.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel error\");\n });\n\n // Call methods before initialization\n insights.track(\"error_event\", { error: true });\n insights.trackPageView();\n\n // Now initialize\n insights.initInsights(testConfig);\n\n // Should have logged the error but continued processing the queue\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Mixpanel\"),\n expect.any(Error),\n );\n\n // The other methods should still be called\n expect(posthog.track).toHaveBeenCalledWith(\"error_event\", {\n error: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"error_event\", {\n error: true,\n });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should report page view to GTM as well when includeDataLayer is true\", () => {\n insights.trackPageView({ includeDataLayer: true });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).toHaveBeenCalled();\n });\n });\n\n describe(\"Post-initialization Direct Execution\", () => {\n beforeEach(() => {\n // Initialize first\n insights.initInsights(testConfig);\n // Clear the mocks to focus on post-init behavior\n vi.clearAllMocks();\n });\n\n it(\"should directly call methods after initialization\", () => {\n // Call methods after initialization\n insights.track(\"post_init_event\", { post: true });\n\n // Should be called immediately\n expect(mixpanel.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n expect(posthog.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"post_init_event\", {\n post: true,\n });\n });\n\n it(\"should handle all exported methods correctly\", () => {\n // Test each exported method\n insights.identify(testIdentity);\n expect(mixpanel.identify).toHaveBeenCalledWith(testIdentity);\n expect(posthog.identify).toHaveBeenCalledWith(testIdentity);\n\n insights.trackPageView();\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n\n insights.enableDebugMode();\n expect(mixpanel.enableDebugMode).toHaveBeenCalled();\n expect(posthog.enableDebugMode).toHaveBeenCalled();\n\n insights.disableDebugMode();\n expect(mixpanel.disableDebugMode).toHaveBeenCalled();\n expect(posthog.disableDebugMode).toHaveBeenCalled();\n });\n\n it(\"should report page view to GTM as well when includeDataLayer is true\", () => {\n insights.trackPageView({ includeDataLayer: true });\n expect(mixpanel.trackPageView).toHaveBeenCalled();\n expect(posthog.trackPageView).toHaveBeenCalled();\n expect(datalayer.trackPageView).toHaveBeenCalled();\n });\n });\n\n describe(\"Observer Setup\", () => {\n beforeEach(() => {\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n });\n\n it(\"should set up click event observer and track clicks\", () => {\n // Setup observer\n const cleanup = insights.setupObserver();\n\n // Create a test element with insight attributes\n const testElement = document.createElement(\"button\");\n testElement.setAttribute(\"data-insight-event\", \"button_clicked\");\n testElement.setAttribute(\"data-insight-button-id\", \"test-123\");\n document.body.appendChild(testElement);\n\n // Simulate click\n testElement.click();\n\n // Should track the event\n expect(mixpanel.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n expect(posthog.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n expect(datalayer.track).toHaveBeenCalledWith(\"button_clicked\", {\n buttonId: \"test-123\",\n });\n\n // Test cleanup\n cleanup();\n\n // Reset tracking mocks\n vi.clearAllMocks();\n\n // Click again - should not track\n testElement.click();\n expect(mixpanel.track).not.toHaveBeenCalled();\n });\n\n it(\"should handle nested elements correctly\", () => {\n // Setup observer\n insights.setupObserver();\n\n // Create parent element with insight attributes\n const parentElement = document.createElement(\"div\");\n parentElement.setAttribute(\"data-insight-event\", \"container_clicked\");\n parentElement.setAttribute(\n \"data-insight-container-id\",\n \"parent-container\",\n );\n\n // Create child element without insights\n const childElement = document.createElement(\"span\");\n childElement.textContent = \"Click me\";\n\n // Nest elements\n parentElement.appendChild(childElement);\n document.body.appendChild(parentElement);\n\n // Click the child element\n childElement.click();\n\n // Should find and use the parent's insight attributes\n expect(mixpanel.track).toHaveBeenCalledWith(\"container_clicked\", {\n containerId: \"parent-container\",\n });\n });\n });\n\n describe(\"Error Handling\", () => {\n it(\"should handle initialization errors gracefully\", () => {\n // Setup an error in initialization\n (mixpanel.initMixpanel as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel init error\");\n });\n\n // Should not throw when initializing\n expect(() => {\n insights.initInsights(testConfig);\n }).not.toThrow();\n\n // Should log the error\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to initialize Mixpanel\"),\n expect.any(Error),\n );\n });\n\n it(\"should handle runtime errors in methods\", () => {\n // Initialize first\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n\n // Setup errors in tracking\n (mixpanel.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Mixpanel track error\");\n });\n\n (posthog.track as Mock).mockImplementationOnce(() => {\n throw new Error(\"Posthog track error\");\n });\n\n // Should not throw when tracking\n expect(() => {\n insights.track(\"error_test\", { test: true });\n }).not.toThrow();\n\n // Should log the errors\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Mixpanel\"),\n expect.any(Error),\n );\n\n expect(logger.error).toHaveBeenCalledWith(\n expect.stringContaining(\"Failed to track event in Posthog\"),\n expect.any(Error),\n );\n\n // Should still try to track with datalayer\n expect(datalayer.track).toHaveBeenCalledWith(\"error_test\", {\n test: true,\n });\n });\n });\n\n describe(\"Debug Mode\", () => {\n it(\"should respect debug flag in config\", () => {\n // Initialize with debug: true\n insights.initInsights(testConfig);\n\n // Should log debug messages\n expect(logger.debug).toHaveBeenCalledWith(\n expect.stringContaining(\"Initializing insights\"),\n );\n\n // Clear mocks and test tracking\n vi.clearAllMocks();\n insights.track(\"debug_test\", { debug: true });\n\n // Should log info about tracking\n expect(logger.info).toHaveBeenCalledWith(\n expect.stringContaining(\"Tracking event\"),\n expect.objectContaining({\n event: \"debug_test\",\n properties: { debug: true },\n }),\n );\n });\n\n it(\"should not log debug info when debug is false\", () => {\n // Initialize with debug: false\n insights.initInsights({\n ...testConfig,\n debug: false,\n });\n\n // Clear mocks and test tracking\n vi.clearAllMocks();\n insights.track(\"no_debug_test\", { debug: false });\n\n // Should not log info about tracking\n expect(logger.info).not.toHaveBeenCalled();\n });\n });\n\n describe(\"Arbitrary Properties\", () => {\n const customPageViewProperties = {\n customProperty: \"custom-value\",\n anotherProperty: 123,\n nestedObject: { foo: \"bar\" },\n };\n\n const customPageViewPropertiesWithDataLayer = {\n customProperty: \"custom-value\",\n anotherProperty: 456,\n };\n\n const customPageViewPropertiesWithExcludeIds = {\n customProperty: \"test\",\n count: 789,\n };\n\n const customIdentityProperties = {\n customProperty: \"user-custom-value\",\n userSegment: \"premium\",\n signupDate: \"2024-01-01\",\n };\n\n const customMinimalIdentityProperties = {\n customField1: \"value1\",\n customField2: 999,\n customField3: { nested: true },\n };\n\n beforeEach(() => {\n insights.initInsights(testConfig);\n vi.clearAllMocks();\n });\n\n it(\"should pass arbitrary properties through trackPageView\", () => {\n // Call trackPageView with custom properties\n insights.trackPageView(customPageViewProperties);\n\n // Verify mixpanel received all properties\n expect(mixpanel.trackPageView).toHaveBeenCalledWith(\n customPageViewProperties,\n );\n\n // Verify posthog received all properties\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewProperties,\n );\n\n // Verify datalayer did not receive anything (includeDataLayer not set)\n expect(datalayer.trackPageView).not.toHaveBeenCalled();\n });\n\n it(\"should pass arbitrary properties through trackPageView with includeDataLayer\", () => {\n // Call trackPageView with custom properties and includeDataLayer\n insights.trackPageView({\n includeDataLayer: true,\n ...customPageViewPropertiesWithDataLayer,\n });\n\n // Verify mixpanel received custom properties (includeDataLayer filtered out)\n expect(mixpanel.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n\n // Verify posthog received custom properties (includeDataLayer filtered out)\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n\n // Verify datalayer received custom properties (includeDataLayer filtered out)\n expect(datalayer.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithDataLayer,\n );\n });\n\n it(\"should pass arbitrary properties through trackPageView with excludeIds\", () => {\n const excludeIds = [\"id1\", \"id2\"];\n\n // Call trackPageView with custom properties and excludeIds\n insights.trackPageView({\n excludeIds,\n ...customPageViewPropertiesWithExcludeIds,\n });\n\n // Verify mixpanel received excludeIds and custom properties\n expect(mixpanel.trackPageView).toHaveBeenCalledWith({\n excludeIds,\n ...customPageViewPropertiesWithExcludeIds,\n });\n\n // Verify posthog received only custom properties (excludeIds filtered out)\n expect(posthog.trackPageView).toHaveBeenCalledWith(\n customPageViewPropertiesWithExcludeIds,\n );\n });\n\n it(\"should pass arbitrary properties through identify\", () => {\n const identityWithCustomProps = {\n ...testIdentity,\n ...customIdentityProperties,\n };\n\n // Call identify with custom properties\n insights.identify(identityWithCustomProps);\n\n // Verify mixpanel received all properties\n expect(mixpanel.identify).toHaveBeenCalledWith(identityWithCustomProps);\n\n // Verify posthog received all properties\n expect(posthog.identify).toHaveBeenCalledWith(identityWithCustomProps);\n });\n\n it(\"should pass arbitrary properties through identify with minimal identity\", () => {\n const minimalIdentity = {\n userId: \"minimal-user\",\n accountId: \"minimal-account\",\n ...customMinimalIdentityProperties,\n };\n\n // Call identify with only required fields plus custom properties\n insights.identify(minimalIdentity);\n\n // Verify mixpanel received all properties\n expect(mixpanel.identify).toHaveBeenCalledWith(minimalIdentity);\n\n // Verify posthog received all properties\n expect(posthog.identify).toHaveBeenCalledWith(minimalIdentity);\n });\n });\n});\n"],"names":["describe","expect","beforeEach","afterEach","it","vi","datalayer","mixpanel","posthog","logger","insights","mock","track","fn","trackPageView","initMixpanel","enableDebugMode","disableDebugMode","identify","initPosthog","debug","info","warn","error","testConfig","mixpanelToken","mixpanelAutoCapture","posthogApiKey","posthogApiHost","testIdentity","userId","accountId","organisationId","email","name","clearAllMocks","resetModules","document","body","replaceWith","cloneNode","early","not","toHaveBeenCalled","initInsights","toHaveBeenCalledWith","undefined","mockImplementationOnce","Error","stringContaining","any","includeDataLayer","post","cleanup","setupObserver","testElement","createElement","setAttribute","appendChild","click","buttonId","parentElement","childElement","textContent","containerId","toThrow","test","objectContaining","event","properties","customPageViewProperties","customProperty","anotherProperty","nestedObject","foo","customPageViewPropertiesWithDataLayer","customPageViewPropertiesWithExcludeIds","count","customIdentityProperties","userSegment","signupDate","customMinimalIdentityProperties","customField1","customField2","customField3","nested","excludeIds","identityWithCustomProps","minimalIdentity"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import mixpanel from"mixpanel-browser";export const initMixpanel=(token,autoCapture=false,debug=false,recordSessionsPercent=1)=>{const blockSelectors=["[data-mask]"];if(!token){console.warn("Mixpanel token not provided, skipping initialization");return}mixpanel.init(token,{debug:debug,persistence:"localStorage",autocapture:autoCapture?{block_selectors:blockSelectors,capture_text_content:true,pageview:false}:false,track_pageview:false,record_sessions_percent:recordSessionsPercent,record_mask_text_selector:"[data-mask]"})};export const enableDebugMode=()=>{mixpanel.set_config({debug:true})};export const disableDebugMode=()=>{mixpanel.set_config({debug:false})};export const identify=({userId,accountId,organisationId,email,name,...properties})=>{if(!userId){return}mixpanel.identify(userId.toString());const peopleProperties={...properties};if(email||name){peopleProperties.$email=email;peopleProperties.$name=name}if(organisationId){peopleProperties.organization_id=[organisationId.toString()]}if(Object.keys(peopleProperties).length>0){mixpanel.people.set(peopleProperties)}if(accountId){mixpanel.people.union({accounts:[accountId.toString()]})}};const redactUrlSegments=excludeIds=>{const pathSegments=window.location.pathname.split("/");const redactedSegments=pathSegments.map(segment=>{if(/^\d+$/.test(segment)||excludeIds.some(id=>id&&id!==""&&segment===id)){return"{redacted}"}return segment});const url=new URL(window.location.href);url.pathname=redactedSegments.join("/");return decodeURI(url.toString()).toLowerCase()};export const trackPageView=properties=>{const{excludeIds,...rest}=properties??{};mixpanel.track_pageview({redacted_path:redactUrlSegments(excludeIds??[]),...rest})};export const track=(event,properties)=>{mixpanel.track(event,properties)};export const startSessionRecording=()=>{mixpanel.start_session_recording()};export const stopSessionRecording=()=>{mixpanel.stop_session_recording()};
1
+ import mixpanel from"mixpanel-browser";export const MIXPANEL_EU_API_HOST="https://api-eu.mixpanel.com";export const initMixpanel=(token,autoCapture=false,debug=false,apiHost=MIXPANEL_EU_API_HOST)=>{const blockSelectors=["[data-mask]"];if(!token){console.warn("Mixpanel token not provided, skipping initialization");return}mixpanel.init(token,{api_host:apiHost,debug:debug,persistence:"localStorage",autocapture:autoCapture?{block_selectors:blockSelectors,capture_text_content:true,pageview:false}:false,track_pageview:false,record_sessions_percent:0})};export const enableDebugMode=()=>{mixpanel.set_config({debug:true})};export const disableDebugMode=()=>{mixpanel.set_config({debug:false})};export const identify=({userId,accountId,organisationId,email,name,...properties})=>{if(!userId){return}mixpanel.identify(userId.toString());const peopleProperties={...properties};if(email||name){peopleProperties.$email=email;peopleProperties.$name=name}if(organisationId){peopleProperties.organization_id=[organisationId.toString()]}if(Object.keys(peopleProperties).length>0){mixpanel.people.set(peopleProperties)}if(accountId){mixpanel.people.union({accounts:[accountId.toString()]})}};const redactUrlSegments=excludeIds=>{const pathSegments=window.location.pathname.split("/");const redactedSegments=pathSegments.map(segment=>{if(/^\d+$/.test(segment)||excludeIds.some(id=>id&&id!==""&&segment===id)){return"{redacted}"}return segment});const url=new URL(window.location.href);url.pathname=redactedSegments.join("/");return decodeURI(url.toString()).toLowerCase()};export const trackPageView=properties=>{const{excludeIds,...rest}=properties??{};mixpanel.track_pageview({redacted_path:redactUrlSegments(excludeIds??[]),...rest})};export const track=(event,properties)=>{mixpanel.track(event,properties)};
2
2
  //# sourceMappingURL=mixpanel.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/mixpanel.ts"],"sourcesContent":["import mixpanel from \"mixpanel-browser\";\n\nimport { InsightsIdentity, TrackPageViewOptions } from \"./types\";\n\nexport const initMixpanel = (\n token: string,\n autoCapture: boolean = false,\n debug: boolean = false,\n recordSessionsPercent = 1,\n) => {\n const blockSelectors = [\"[data-mask]\"];\n if (!token) {\n console.warn(\"Mixpanel token not provided, skipping initialization\");\n return;\n }\n\n mixpanel.init(token, {\n debug: debug,\n persistence: \"localStorage\",\n autocapture: autoCapture\n ? {\n block_selectors: blockSelectors,\n capture_text_content: true,\n pageview: false, // We'll track page views manually\n }\n : false,\n track_pageview: false, // We'll track page views manually\n record_sessions_percent: recordSessionsPercent,\n record_mask_text_selector: \"[data-mask]\",\n });\n};\n\nexport const enableDebugMode = () => {\n mixpanel.set_config({ debug: true });\n};\n\nexport const disableDebugMode = () => {\n mixpanel.set_config({ debug: false });\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n return;\n }\n\n mixpanel.identify(userId.toString());\n\n const peopleProperties: Record<string, unknown> = { ...properties };\n\n if (email || name) {\n peopleProperties.$email = email;\n peopleProperties.$name = name;\n }\n\n if (organisationId) {\n peopleProperties.organization_id = [organisationId.toString()];\n }\n\n if (Object.keys(peopleProperties).length > 0) {\n mixpanel.people.set(peopleProperties);\n }\n\n if (accountId) {\n mixpanel.people.union({ accounts: [accountId.toString()] });\n }\n};\n\n// Simple function to replace all digits and IDs in a URL path with {redacted},\n// purely to make reporting based on aggregates easier\nconst redactUrlSegments = (excludeIds: string[]) => {\n const pathSegments = window.location.pathname.split(\"/\");\n\n const redactedSegments = pathSegments.map((segment) => {\n // Redact if the segment contains only digits or matches any of the excluded IDs\n if (\n /^\\d+$/.test(segment) ||\n excludeIds.some((id) => id && id !== \"\" && segment === id)\n ) {\n return \"{redacted}\";\n }\n\n return segment;\n });\n\n // Join the segments back together\n const url = new URL(window.location.href);\n url.pathname = redactedSegments.join(\"/\");\n\n return decodeURI(url.toString()).toLowerCase();\n};\n\nexport const trackPageView = (properties?: TrackPageViewOptions) => {\n const { excludeIds, ...rest } = properties ?? {};\n\n // Add the redacted URL to the page view event for reporting\n mixpanel.track_pageview({\n redacted_path: redactUrlSegments(excludeIds ?? []),\n ...rest,\n });\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n mixpanel.track(event, properties);\n};\n\nexport const startSessionRecording = () => {\n mixpanel.start_session_recording();\n};\n\nexport const stopSessionRecording = () => {\n mixpanel.stop_session_recording();\n};\n"],"names":["mixpanel","initMixpanel","token","autoCapture","debug","recordSessionsPercent","blockSelectors","console","warn","init","persistence","autocapture","block_selectors","capture_text_content","pageview","track_pageview","record_sessions_percent","record_mask_text_selector","enableDebugMode","set_config","disableDebugMode","identify","userId","accountId","organisationId","email","name","properties","toString","peopleProperties","$email","$name","organization_id","Object","keys","length","people","set","union","accounts","redactUrlSegments","excludeIds","pathSegments","window","location","pathname","split","redactedSegments","map","segment","test","some","id","url","URL","href","join","decodeURI","toLowerCase","trackPageView","rest","redacted_path","track","event","startSessionRecording","start_session_recording","stopSessionRecording","stop_session_recording"],"mappings":"AAAA,OAAOA,aAAc,kBAAmB,AAIxC,QAAO,MAAMC,aAAe,CAC1BC,MACAC,YAAuB,KAAK,CAC5BC,MAAiB,KAAK,CACtBC,sBAAwB,CAAC,IAEzB,MAAMC,eAAiB,CAAC,cAAc,CACtC,GAAI,CAACJ,MAAO,CACVK,QAAQC,IAAI,CAAC,wDACb,MACF,CAEAR,SAASS,IAAI,CAACP,MAAO,CACnBE,MAAOA,MACPM,YAAa,eACbC,YAAaR,YACT,CACES,gBAAiBN,eACjBO,qBAAsB,KACtBC,SAAU,KACZ,EACA,MACJC,eAAgB,MAChBC,wBAAyBX,sBACzBY,0BAA2B,aAC7B,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BlB,SAASmB,UAAU,CAAC,CAAEf,MAAO,IAAK,EACpC,CAAE,AAEF,QAAO,MAAMgB,iBAAmB,KAC9BpB,SAASmB,UAAU,CAAC,CAAEf,MAAO,KAAM,EACrC,CAAE,AAEF,QAAO,MAAMiB,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACJ,GAAGC,WACc,IAGjB,GAAI,CAACL,OAAQ,CACX,MACF,CAEAtB,SAASqB,QAAQ,CAACC,OAAOM,QAAQ,IAEjC,MAAMC,iBAA4C,CAAE,GAAGF,UAAU,AAAC,EAElE,GAAIF,OAASC,KAAM,CACjBG,iBAAiBC,MAAM,CAAGL,KAC1BI,CAAAA,iBAAiBE,KAAK,CAAGL,IAC3B,CAEA,GAAIF,eAAgB,CAClBK,iBAAiBG,eAAe,CAAG,CAACR,eAAeI,QAAQ,GAAG,AAChE,CAEA,GAAIK,OAAOC,IAAI,CAACL,kBAAkBM,MAAM,CAAG,EAAG,CAC5CnC,SAASoC,MAAM,CAACC,GAAG,CAACR,iBACtB,CAEA,GAAIN,UAAW,CACbvB,SAASoC,MAAM,CAACE,KAAK,CAAC,CAAEC,SAAU,CAAChB,UAAUK,QAAQ,GAAG,AAAC,EAC3D,CACF,CAAE,CAIF,MAAMY,kBAAoB,AAACC,aACzB,MAAMC,aAAeC,OAAOC,QAAQ,CAACC,QAAQ,CAACC,KAAK,CAAC,KAEpD,MAAMC,iBAAmBL,aAAaM,GAAG,CAAC,AAACC,UAEzC,GACE,QAAQC,IAAI,CAACD,UACbR,WAAWU,IAAI,CAAC,AAACC,IAAOA,IAAMA,KAAO,IAAMH,UAAYG,IACvD,CACA,MAAO,YACT,CAEA,OAAOH,OACT,GAGA,MAAMI,IAAM,IAAIC,IAAIX,OAAOC,QAAQ,CAACW,IAAI,CACxCF,CAAAA,IAAIR,QAAQ,CAAGE,iBAAiBS,IAAI,CAAC,KAErC,OAAOC,UAAUJ,IAAIzB,QAAQ,IAAI8B,WAAW,EAC9C,CAEA,QAAO,MAAMC,cAAgB,AAAChC,aAC5B,KAAM,CAAEc,UAAU,CAAE,GAAGmB,KAAM,CAAGjC,YAAc,CAAC,EAG/C3B,SAASe,cAAc,CAAC,CACtB8C,cAAerB,kBAAkBC,YAAc,EAAE,EACjD,GAAGmB,IAAI,AACT,EACF,CAAE,AAEF,QAAO,MAAME,MAAQ,CAACC,MAAepC,cACnC3B,SAAS8D,KAAK,CAACC,MAAOpC,WACxB,CAAE,AAEF,QAAO,MAAMqC,sBAAwB,KACnChE,SAASiE,uBAAuB,EAClC,CAAE,AAEF,QAAO,MAAMC,qBAAuB,KAClClE,SAASmE,sBAAsB,EACjC,CAAE"}
1
+ {"version":3,"sources":["../../../src/core/insights/mixpanel.ts"],"sourcesContent":["import mixpanel from \"mixpanel-browser\";\n\nimport { InsightsIdentity, TrackPageViewOptions } from \"./types\";\n\nexport const MIXPANEL_EU_API_HOST = \"https://api-eu.mixpanel.com\";\n\nexport const initMixpanel = (\n token: string,\n autoCapture: boolean = false,\n debug: boolean = false,\n apiHost: string = MIXPANEL_EU_API_HOST,\n) => {\n const blockSelectors = [\"[data-mask]\"];\n if (!token) {\n console.warn(\"Mixpanel token not provided, skipping initialization\");\n return;\n }\n\n mixpanel.init(token, {\n api_host: apiHost,\n debug: debug,\n persistence: \"localStorage\",\n autocapture: autoCapture\n ? {\n block_selectors: blockSelectors,\n capture_text_content: true,\n pageview: false, // We'll track page views manually\n }\n : false,\n track_pageview: false, // We'll track page views manually\n record_sessions_percent: 0,\n });\n};\n\nexport const enableDebugMode = () => {\n mixpanel.set_config({ debug: true });\n};\n\nexport const disableDebugMode = () => {\n mixpanel.set_config({ debug: false });\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n return;\n }\n\n mixpanel.identify(userId.toString());\n\n const peopleProperties: Record<string, unknown> = { ...properties };\n\n if (email || name) {\n peopleProperties.$email = email;\n peopleProperties.$name = name;\n }\n\n if (organisationId) {\n peopleProperties.organization_id = [organisationId.toString()];\n }\n\n if (Object.keys(peopleProperties).length > 0) {\n mixpanel.people.set(peopleProperties);\n }\n\n if (accountId) {\n mixpanel.people.union({ accounts: [accountId.toString()] });\n }\n};\n\n// Simple function to replace all digits and IDs in a URL path with {redacted},\n// purely to make reporting based on aggregates easier\nconst redactUrlSegments = (excludeIds: string[]) => {\n const pathSegments = window.location.pathname.split(\"/\");\n\n const redactedSegments = pathSegments.map((segment) => {\n // Redact if the segment contains only digits or matches any of the excluded IDs\n if (\n /^\\d+$/.test(segment) ||\n excludeIds.some((id) => id && id !== \"\" && segment === id)\n ) {\n return \"{redacted}\";\n }\n\n return segment;\n });\n\n // Join the segments back together\n const url = new URL(window.location.href);\n url.pathname = redactedSegments.join(\"/\");\n\n return decodeURI(url.toString()).toLowerCase();\n};\n\nexport const trackPageView = (properties?: TrackPageViewOptions) => {\n const { excludeIds, ...rest } = properties ?? {};\n\n // Add the redacted URL to the page view event for reporting\n mixpanel.track_pageview({\n redacted_path: redactUrlSegments(excludeIds ?? []),\n ...rest,\n });\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n mixpanel.track(event, properties);\n};\n"],"names":["mixpanel","MIXPANEL_EU_API_HOST","initMixpanel","token","autoCapture","debug","apiHost","blockSelectors","console","warn","init","api_host","persistence","autocapture","block_selectors","capture_text_content","pageview","track_pageview","record_sessions_percent","enableDebugMode","set_config","disableDebugMode","identify","userId","accountId","organisationId","email","name","properties","toString","peopleProperties","$email","$name","organization_id","Object","keys","length","people","set","union","accounts","redactUrlSegments","excludeIds","pathSegments","window","location","pathname","split","redactedSegments","map","segment","test","some","id","url","URL","href","join","decodeURI","toLowerCase","trackPageView","rest","redacted_path","track","event"],"mappings":"AAAA,OAAOA,aAAc,kBAAmB,AAIxC,QAAO,MAAMC,qBAAuB,6BAA8B,AAElE,QAAO,MAAMC,aAAe,CAC1BC,MACAC,YAAuB,KAAK,CAC5BC,MAAiB,KAAK,CACtBC,QAAkBL,oBAAoB,IAEtC,MAAMM,eAAiB,CAAC,cAAc,CACtC,GAAI,CAACJ,MAAO,CACVK,QAAQC,IAAI,CAAC,wDACb,MACF,CAEAT,SAASU,IAAI,CAACP,MAAO,CACnBQ,SAAUL,QACVD,MAAOA,MACPO,YAAa,eACbC,YAAaT,YACT,CACEU,gBAAiBP,eACjBQ,qBAAsB,KACtBC,SAAU,KACZ,EACA,MACJC,eAAgB,MAChBC,wBAAyB,CAC3B,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BnB,SAASoB,UAAU,CAAC,CAAEf,MAAO,IAAK,EACpC,CAAE,AAEF,QAAO,MAAMgB,iBAAmB,KAC9BrB,SAASoB,UAAU,CAAC,CAAEf,MAAO,KAAM,EACrC,CAAE,AAEF,QAAO,MAAMiB,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACJ,GAAGC,WACc,IAGjB,GAAI,CAACL,OAAQ,CACX,MACF,CAEAvB,SAASsB,QAAQ,CAACC,OAAOM,QAAQ,IAEjC,MAAMC,iBAA4C,CAAE,GAAGF,UAAU,AAAC,EAElE,GAAIF,OAASC,KAAM,CACjBG,iBAAiBC,MAAM,CAAGL,KAC1BI,CAAAA,iBAAiBE,KAAK,CAAGL,IAC3B,CAEA,GAAIF,eAAgB,CAClBK,iBAAiBG,eAAe,CAAG,CAACR,eAAeI,QAAQ,GAAG,AAChE,CAEA,GAAIK,OAAOC,IAAI,CAACL,kBAAkBM,MAAM,CAAG,EAAG,CAC5CpC,SAASqC,MAAM,CAACC,GAAG,CAACR,iBACtB,CAEA,GAAIN,UAAW,CACbxB,SAASqC,MAAM,CAACE,KAAK,CAAC,CAAEC,SAAU,CAAChB,UAAUK,QAAQ,GAAG,AAAC,EAC3D,CACF,CAAE,CAIF,MAAMY,kBAAoB,AAACC,aACzB,MAAMC,aAAeC,OAAOC,QAAQ,CAACC,QAAQ,CAACC,KAAK,CAAC,KAEpD,MAAMC,iBAAmBL,aAAaM,GAAG,CAAC,AAACC,UAEzC,GACE,QAAQC,IAAI,CAACD,UACbR,WAAWU,IAAI,CAAC,AAACC,IAAOA,IAAMA,KAAO,IAAMH,UAAYG,IACvD,CACA,MAAO,YACT,CAEA,OAAOH,OACT,GAGA,MAAMI,IAAM,IAAIC,IAAIX,OAAOC,QAAQ,CAACW,IAAI,CACxCF,CAAAA,IAAIR,QAAQ,CAAGE,iBAAiBS,IAAI,CAAC,KAErC,OAAOC,UAAUJ,IAAIzB,QAAQ,IAAI8B,WAAW,EAC9C,CAEA,QAAO,MAAMC,cAAgB,AAAChC,aAC5B,KAAM,CAAEc,UAAU,CAAE,GAAGmB,KAAM,CAAGjC,YAAc,CAAC,EAG/C5B,SAASiB,cAAc,CAAC,CACtB6C,cAAerB,kBAAkBC,YAAc,EAAE,EACjD,GAAGmB,IAAI,AACT,EACF,CAAE,AAEF,QAAO,MAAME,MAAQ,CAACC,MAAepC,cACnC5B,SAAS+D,KAAK,CAACC,MAAOpC,WACxB,CAAE"}
@@ -1,2 +1,2 @@
1
- import{describe,expect,beforeEach,it,vi}from"vitest";import mixpanel from"mixpanel-browser";import{initMixpanel}from"./mixpanel";vi.mock("mixpanel-browser",()=>({default:{init:vi.fn()}}));describe("initMixpanel",()=>{beforeEach(()=>{vi.clearAllMocks()});it("does not initialize without a token",()=>{const consoleSpy=vi.spyOn(console,"warn").mockImplementation(()=>{});initMixpanel("");expect(mixpanel.init).not.toHaveBeenCalled();consoleSpy.mockRestore()});it("blocks [data-mask] elements in autocapture",()=>{initMixpanel("test-token",true);const config=mixpanel.init.mock.calls[0][1];expect(config.autocapture.block_selectors).toEqual(["[data-mask]"])});it("masks [data-mask] elements in session recordings",()=>{initMixpanel("test-token",true);const config=mixpanel.init.mock.calls[0][1];expect(config.record_mask_text_selector).toBe("[data-mask]")})});
1
+ import{describe,expect,beforeEach,it,vi}from"vitest";import mixpanel from"mixpanel-browser";import{initMixpanel,MIXPANEL_EU_API_HOST}from"./mixpanel";vi.mock("mixpanel-browser",()=>({default:{init:vi.fn()}}));describe("initMixpanel",()=>{beforeEach(()=>{vi.clearAllMocks()});it("does not initialize without a token",()=>{const consoleSpy=vi.spyOn(console,"warn").mockImplementation(()=>{});initMixpanel("");expect(mixpanel.init).not.toHaveBeenCalled();consoleSpy.mockRestore()});it("blocks [data-mask] elements in autocapture",()=>{initMixpanel("test-token",true);const config=mixpanel.init.mock.calls[0][1];expect(config.autocapture.block_selectors).toEqual(["[data-mask]"])});it("does not record sessions",()=>{initMixpanel("test-token",true);const config=mixpanel.init.mock.calls[0][1];expect(config.record_sessions_percent).toBe(0)});it("defaults api_host to the EU ingestion endpoint",()=>{initMixpanel("test-token");const config=mixpanel.init.mock.calls[0][1];expect(config.api_host).toBe(MIXPANEL_EU_API_HOST);expect(MIXPANEL_EU_API_HOST).toBe("https://api-eu.mixpanel.com")});it("allows overriding api_host",()=>{initMixpanel("test-token",false,false,"https://api.mixpanel.com");const config=mixpanel.init.mock.calls[0][1];expect(config.api_host).toBe("https://api.mixpanel.com")});it("falls back to the EU default when api_host is undefined",()=>{initMixpanel("test-token",false,false,undefined);const config=mixpanel.init.mock.calls[0][1];expect(config.api_host).toBe(MIXPANEL_EU_API_HOST)})});
2
2
  //# sourceMappingURL=mixpanel.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/mixpanel.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, it, vi } from \"vitest\";\nimport mixpanel from \"mixpanel-browser\";\n\nimport { initMixpanel } from \"./mixpanel\";\n\nvi.mock(\"mixpanel-browser\", () => ({\n default: {\n init: vi.fn(),\n },\n}));\n\ndescribe(\"initMixpanel\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"does not initialize without a token\", () => {\n const consoleSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n initMixpanel(\"\");\n expect(mixpanel.init).not.toHaveBeenCalled();\n consoleSpy.mockRestore();\n });\n\n it(\"blocks [data-mask] elements in autocapture\", () => {\n initMixpanel(\"test-token\", true);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.autocapture.block_selectors).toEqual([\"[data-mask]\"]);\n });\n\n it(\"masks [data-mask] elements in session recordings\", () => {\n initMixpanel(\"test-token\", true);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.record_mask_text_selector).toBe(\"[data-mask]\");\n });\n});\n"],"names":["describe","expect","beforeEach","it","vi","mixpanel","initMixpanel","mock","default","init","fn","clearAllMocks","consoleSpy","spyOn","console","mockImplementation","not","toHaveBeenCalled","mockRestore","config","calls","autocapture","block_selectors","toEqual","record_mask_text_selector","toBe"],"mappings":"AAIA,OAASA,QAAQ,CAAEC,MAAM,CAAEC,UAAU,CAAEC,EAAE,CAAEC,EAAE,KAAQ,QAAS,AAC9D,QAAOC,aAAc,kBAAmB,AAExC,QAASC,YAAY,KAAQ,YAAa,CAE1CF,GAAGG,IAAI,CAAC,mBAAoB,IAAO,CAAA,CACjCC,QAAS,CACPC,KAAML,GAAGM,EAAE,EACb,CACF,CAAA,GAEAV,SAAS,eAAgB,KACvBE,WAAW,KACTE,GAAGO,aAAa,EAClB,GAEAR,GAAG,sCAAuC,KACxC,MAAMS,WAAaR,GAAGS,KAAK,CAACC,QAAS,QAAQC,kBAAkB,CAAC,KAAO,GACvET,aAAa,IACbL,OAAOI,SAASI,IAAI,EAAEO,GAAG,CAACC,gBAAgB,GAC1CL,WAAWM,WAAW,EACxB,GAEAf,GAAG,6CAA8C,KAC/CG,aAAa,aAAc,MAE3B,MAAMa,OAAS,AAACd,SAASI,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EnB,OAAOkB,OAAOE,WAAW,CAACC,eAAe,EAAEC,OAAO,CAAC,CAAC,cAAc,CACpE,GAEApB,GAAG,mDAAoD,KACrDG,aAAa,aAAc,MAE3B,MAAMa,OAAS,AAACd,SAASI,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EnB,OAAOkB,OAAOK,yBAAyB,EAAEC,IAAI,CAAC,cAChD,EACF"}
1
+ {"version":3,"sources":["../../../src/core/insights/mixpanel.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, it, vi } from \"vitest\";\nimport mixpanel from \"mixpanel-browser\";\n\nimport { initMixpanel, MIXPANEL_EU_API_HOST } from \"./mixpanel\";\n\nvi.mock(\"mixpanel-browser\", () => ({\n default: {\n init: vi.fn(),\n },\n}));\n\ndescribe(\"initMixpanel\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"does not initialize without a token\", () => {\n const consoleSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n initMixpanel(\"\");\n expect(mixpanel.init).not.toHaveBeenCalled();\n consoleSpy.mockRestore();\n });\n\n it(\"blocks [data-mask] elements in autocapture\", () => {\n initMixpanel(\"test-token\", true);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.autocapture.block_selectors).toEqual([\"[data-mask]\"]);\n });\n\n it(\"does not record sessions\", () => {\n initMixpanel(\"test-token\", true);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.record_sessions_percent).toBe(0);\n });\n\n it(\"defaults api_host to the EU ingestion endpoint\", () => {\n initMixpanel(\"test-token\");\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.api_host).toBe(MIXPANEL_EU_API_HOST);\n expect(MIXPANEL_EU_API_HOST).toBe(\"https://api-eu.mixpanel.com\");\n });\n\n it(\"allows overriding api_host\", () => {\n initMixpanel(\"test-token\", false, false, \"https://api.mixpanel.com\");\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.api_host).toBe(\"https://api.mixpanel.com\");\n });\n\n it(\"falls back to the EU default when api_host is undefined\", () => {\n initMixpanel(\"test-token\", false, false, undefined);\n\n const config = (mixpanel.init as ReturnType<typeof vi.fn>).mock.calls[0][1];\n expect(config.api_host).toBe(MIXPANEL_EU_API_HOST);\n });\n});\n"],"names":["describe","expect","beforeEach","it","vi","mixpanel","initMixpanel","MIXPANEL_EU_API_HOST","mock","default","init","fn","clearAllMocks","consoleSpy","spyOn","console","mockImplementation","not","toHaveBeenCalled","mockRestore","config","calls","autocapture","block_selectors","toEqual","record_sessions_percent","toBe","api_host","undefined"],"mappings":"AAIA,OAASA,QAAQ,CAAEC,MAAM,CAAEC,UAAU,CAAEC,EAAE,CAAEC,EAAE,KAAQ,QAAS,AAC9D,QAAOC,aAAc,kBAAmB,AAExC,QAASC,YAAY,CAAEC,oBAAoB,KAAQ,YAAa,CAEhEH,GAAGI,IAAI,CAAC,mBAAoB,IAAO,CAAA,CACjCC,QAAS,CACPC,KAAMN,GAAGO,EAAE,EACb,CACF,CAAA,GAEAX,SAAS,eAAgB,KACvBE,WAAW,KACTE,GAAGQ,aAAa,EAClB,GAEAT,GAAG,sCAAuC,KACxC,MAAMU,WAAaT,GAAGU,KAAK,CAACC,QAAS,QAAQC,kBAAkB,CAAC,KAAO,GACvEV,aAAa,IACbL,OAAOI,SAASK,IAAI,EAAEO,GAAG,CAACC,gBAAgB,GAC1CL,WAAWM,WAAW,EACxB,GAEAhB,GAAG,6CAA8C,KAC/CG,aAAa,aAAc,MAE3B,MAAMc,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOE,WAAW,CAACC,eAAe,EAAEC,OAAO,CAAC,CAAC,cAAc,CACpE,GAEArB,GAAG,2BAA4B,KAC7BG,aAAa,aAAc,MAE3B,MAAMc,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOK,uBAAuB,EAAEC,IAAI,CAAC,EAC9C,GAEAvB,GAAG,iDAAkD,KACnDG,aAAa,cAEb,MAAMc,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOO,QAAQ,EAAED,IAAI,CAACnB,sBAC7BN,OAAOM,sBAAsBmB,IAAI,CAAC,8BACpC,GAEAvB,GAAG,6BAA8B,KAC/BG,aAAa,aAAc,MAAO,MAAO,4BAEzC,MAAMc,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOO,QAAQ,EAAED,IAAI,CAAC,2BAC/B,GAEAvB,GAAG,0DAA2D,KAC5DG,aAAa,aAAc,MAAO,MAAOsB,WAEzC,MAAMR,OAAS,AAACf,SAASK,IAAI,CAA8BF,IAAI,CAACa,KAAK,CAAC,EAAE,CAAC,EAAE,CAC3EpB,OAAOmB,OAAOO,QAAQ,EAAED,IAAI,CAACnB,qBAC/B,EACF"}
@@ -1,2 +1,2 @@
1
- import posthog from"posthog-js";export const initPosthog=(apiKey,apiHost)=>{if(!apiKey){return}posthog.init(apiKey,{api_host:apiHost,capture_pageview:false,session_recording:{maskTextSelector:"[data-mask]"}})};export const enableDebugMode=()=>{posthog.debug()};export const disableDebugMode=()=>{posthog.debug(false)};export const identify=({userId,accountId,organisationId,email,name,...properties})=>{if(!userId){return}if(userId!==posthog.get_distinct_id()){posthog.identify(userId,{email,name,...properties})}if(accountId){posthog.group("account",accountId)}if(organisationId){posthog.group("organisation",organisationId)}};export const trackPageView=properties=>{posthog.capture("$pageview",properties)};export const track=(event,properties)=>{posthog.capture(event,properties)};export const startSessionRecording=()=>{posthog.startSessionRecording()};export const stopSessionRecording=()=>{posthog.stopSessionRecording()};
1
+ import posthog from"posthog-js";export const initPosthog=(apiKey,apiHost)=>{if(!apiKey){return}posthog.init(apiKey,{api_host:apiHost,capture_pageview:false,disable_session_recording:true})};export const enableDebugMode=()=>{posthog.debug()};export const disableDebugMode=()=>{posthog.debug(false)};export const identify=({userId,accountId,organisationId,email,name,...properties})=>{if(!userId){return}if(userId!==posthog.get_distinct_id()){posthog.identify(userId,{email,name,...properties})}if(accountId){posthog.group("account",accountId)}if(organisationId){posthog.group("organisation",organisationId)}};export const trackPageView=properties=>{posthog.capture("$pageview",properties)};export const track=(event,properties)=>{posthog.capture(event,properties)};
2
2
  //# sourceMappingURL=posthog.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/posthog.ts"],"sourcesContent":["import posthog from \"posthog-js\";\n\nimport { InsightsIdentity } from \"./types\";\n\nexport const initPosthog = (apiKey: string, apiHost: string) => {\n if (!apiKey) {\n return;\n }\n\n posthog.init(apiKey, {\n api_host: apiHost,\n capture_pageview: false,\n session_recording: {\n maskTextSelector: \"[data-mask]\",\n },\n });\n};\n\nexport const enableDebugMode = () => {\n posthog.debug();\n};\n\nexport const disableDebugMode = () => {\n posthog.debug(false);\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n return;\n }\n\n if (userId !== posthog.get_distinct_id()) {\n posthog.identify(userId, { email, name, ...properties });\n }\n\n // Associate all events in this session with this account\n if (accountId) {\n posthog.group(\"account\", accountId);\n }\n\n // Associate all events in this session with this organisation (if available)\n if (organisationId) {\n posthog.group(\"organisation\", organisationId);\n }\n};\n\nexport const trackPageView = (properties?: Record<string, unknown>) => {\n posthog.capture(\"$pageview\", properties);\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n posthog.capture(event, properties);\n};\n\nexport const startSessionRecording = () => {\n posthog.startSessionRecording();\n};\n\nexport const stopSessionRecording = () => {\n posthog.stopSessionRecording();\n};\n"],"names":["posthog","initPosthog","apiKey","apiHost","init","api_host","capture_pageview","session_recording","maskTextSelector","enableDebugMode","debug","disableDebugMode","identify","userId","accountId","organisationId","email","name","properties","get_distinct_id","group","trackPageView","capture","track","event","startSessionRecording","stopSessionRecording"],"mappings":"AAAA,OAAOA,YAAa,YAAa,AAIjC,QAAO,MAAMC,YAAc,CAACC,OAAgBC,WAC1C,GAAI,CAACD,OAAQ,CACX,MACF,CAEAF,QAAQI,IAAI,CAACF,OAAQ,CACnBG,SAAUF,QACVG,iBAAkB,MAClBC,kBAAmB,CACjBC,iBAAkB,aACpB,CACF,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BT,QAAQU,KAAK,EACf,CAAE,AAEF,QAAO,MAAMC,iBAAmB,KAC9BX,QAAQU,KAAK,CAAC,MAChB,CAAE,AAEF,QAAO,MAAME,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACJ,GAAGC,WACc,IAGjB,GAAI,CAACL,OAAQ,CACX,MACF,CAEA,GAAIA,SAAWb,QAAQmB,eAAe,GAAI,CACxCnB,QAAQY,QAAQ,CAACC,OAAQ,CAAEG,MAAOC,KAAM,GAAGC,UAAU,AAAC,EACxD,CAGA,GAAIJ,UAAW,CACbd,QAAQoB,KAAK,CAAC,UAAWN,UAC3B,CAGA,GAAIC,eAAgB,CAClBf,QAAQoB,KAAK,CAAC,eAAgBL,eAChC,CACF,CAAE,AAEF,QAAO,MAAMM,cAAgB,AAACH,aAC5BlB,QAAQsB,OAAO,CAAC,YAAaJ,WAC/B,CAAE,AAEF,QAAO,MAAMK,MAAQ,CAACC,MAAeN,cACnClB,QAAQsB,OAAO,CAACE,MAAON,WACzB,CAAE,AAEF,QAAO,MAAMO,sBAAwB,KACnCzB,QAAQyB,qBAAqB,EAC/B,CAAE,AAEF,QAAO,MAAMC,qBAAuB,KAClC1B,QAAQ0B,oBAAoB,EAC9B,CAAE"}
1
+ {"version":3,"sources":["../../../src/core/insights/posthog.ts"],"sourcesContent":["import posthog from \"posthog-js\";\n\nimport { InsightsIdentity } from \"./types\";\n\nexport const initPosthog = (apiKey: string, apiHost: string) => {\n if (!apiKey) {\n return;\n }\n\n posthog.init(apiKey, {\n api_host: apiHost,\n capture_pageview: false,\n disable_session_recording: true,\n });\n};\n\nexport const enableDebugMode = () => {\n posthog.debug();\n};\n\nexport const disableDebugMode = () => {\n posthog.debug(false);\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties\n}: InsightsIdentity) => {\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n return;\n }\n\n if (userId !== posthog.get_distinct_id()) {\n posthog.identify(userId, { email, name, ...properties });\n }\n\n // Associate all events in this session with this account\n if (accountId) {\n posthog.group(\"account\", accountId);\n }\n\n // Associate all events in this session with this organisation (if available)\n if (organisationId) {\n posthog.group(\"organisation\", organisationId);\n }\n};\n\nexport const trackPageView = (properties?: Record<string, unknown>) => {\n posthog.capture(\"$pageview\", properties);\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n posthog.capture(event, properties);\n};\n"],"names":["posthog","initPosthog","apiKey","apiHost","init","api_host","capture_pageview","disable_session_recording","enableDebugMode","debug","disableDebugMode","identify","userId","accountId","organisationId","email","name","properties","get_distinct_id","group","trackPageView","capture","track","event"],"mappings":"AAAA,OAAOA,YAAa,YAAa,AAIjC,QAAO,MAAMC,YAAc,CAACC,OAAgBC,WAC1C,GAAI,CAACD,OAAQ,CACX,MACF,CAEAF,QAAQI,IAAI,CAACF,OAAQ,CACnBG,SAAUF,QACVG,iBAAkB,MAClBC,0BAA2B,IAC7B,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BR,QAAQS,KAAK,EACf,CAAE,AAEF,QAAO,MAAMC,iBAAmB,KAC9BV,QAAQS,KAAK,CAAC,MAChB,CAAE,AAEF,QAAO,MAAME,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACJ,GAAGC,WACc,IAGjB,GAAI,CAACL,OAAQ,CACX,MACF,CAEA,GAAIA,SAAWZ,QAAQkB,eAAe,GAAI,CACxClB,QAAQW,QAAQ,CAACC,OAAQ,CAAEG,MAAOC,KAAM,GAAGC,UAAU,AAAC,EACxD,CAGA,GAAIJ,UAAW,CACbb,QAAQmB,KAAK,CAAC,UAAWN,UAC3B,CAGA,GAAIC,eAAgB,CAClBd,QAAQmB,KAAK,CAAC,eAAgBL,eAChC,CACF,CAAE,AAEF,QAAO,MAAMM,cAAgB,AAACH,aAC5BjB,QAAQqB,OAAO,CAAC,YAAaJ,WAC/B,CAAE,AAEF,QAAO,MAAMK,MAAQ,CAACC,MAAeN,cACnCjB,QAAQqB,OAAO,CAACE,MAAON,WACzB,CAAE"}
@@ -1,2 +1,2 @@
1
- import{describe,expect,beforeEach,it,vi}from"vitest";import posthog from"posthog-js";import{initPosthog}from"./posthog";vi.mock("posthog-js",()=>({default:{init:vi.fn()}}));describe("initPosthog",()=>{beforeEach(()=>{vi.clearAllMocks()});it("does not initialize without an API key",()=>{initPosthog("","https://app.posthog.com");expect(posthog.init).not.toHaveBeenCalled()});it("initializes with session recording masking on [data-mask]",()=>{initPosthog("phc_test","https://app.posthog.com");expect(posthog.init).toHaveBeenCalledWith("phc_test",{api_host:"https://app.posthog.com",capture_pageview:false,session_recording:{maskTextSelector:"[data-mask]"}})})});
1
+ import{describe,expect,beforeEach,it,vi}from"vitest";import posthog from"posthog-js";import{initPosthog}from"./posthog";vi.mock("posthog-js",()=>({default:{init:vi.fn()}}));describe("initPosthog",()=>{beforeEach(()=>{vi.clearAllMocks()});it("does not initialize without an API key",()=>{initPosthog("","https://app.posthog.com");expect(posthog.init).not.toHaveBeenCalled()});it("initializes with session recording disabled",()=>{initPosthog("phc_test","https://app.posthog.com");expect(posthog.init).toHaveBeenCalledWith("phc_test",{api_host:"https://app.posthog.com",capture_pageview:false,disable_session_recording:true})})});
2
2
  //# sourceMappingURL=posthog.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/posthog.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, it, vi } from \"vitest\";\nimport posthog from \"posthog-js\";\n\nimport { initPosthog } from \"./posthog\";\n\nvi.mock(\"posthog-js\", () => ({\n default: {\n init: vi.fn(),\n },\n}));\n\ndescribe(\"initPosthog\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"does not initialize without an API key\", () => {\n initPosthog(\"\", \"https://app.posthog.com\");\n expect(posthog.init).not.toHaveBeenCalled();\n });\n\n it(\"initializes with session recording masking on [data-mask]\", () => {\n initPosthog(\"phc_test\", \"https://app.posthog.com\");\n\n expect(posthog.init).toHaveBeenCalledWith(\"phc_test\", {\n api_host: \"https://app.posthog.com\",\n capture_pageview: false,\n session_recording: {\n maskTextSelector: \"[data-mask]\",\n },\n });\n });\n});\n"],"names":["describe","expect","beforeEach","it","vi","posthog","initPosthog","mock","default","init","fn","clearAllMocks","not","toHaveBeenCalled","toHaveBeenCalledWith","api_host","capture_pageview","session_recording","maskTextSelector"],"mappings":"AAIA,OAASA,QAAQ,CAAEC,MAAM,CAAEC,UAAU,CAAEC,EAAE,CAAEC,EAAE,KAAQ,QAAS,AAC9D,QAAOC,YAAa,YAAa,AAEjC,QAASC,WAAW,KAAQ,WAAY,CAExCF,GAAGG,IAAI,CAAC,aAAc,IAAO,CAAA,CAC3BC,QAAS,CACPC,KAAML,GAAGM,EAAE,EACb,CACF,CAAA,GAEAV,SAAS,cAAe,KACtBE,WAAW,KACTE,GAAGO,aAAa,EAClB,GAEAR,GAAG,yCAA0C,KAC3CG,YAAY,GAAI,2BAChBL,OAAOI,QAAQI,IAAI,EAAEG,GAAG,CAACC,gBAAgB,EAC3C,GAEAV,GAAG,4DAA6D,KAC9DG,YAAY,WAAY,2BAExBL,OAAOI,QAAQI,IAAI,EAAEK,oBAAoB,CAAC,WAAY,CACpDC,SAAU,0BACVC,iBAAkB,MAClBC,kBAAmB,CACjBC,iBAAkB,aACpB,CACF,EACF,EACF"}
1
+ {"version":3,"sources":["../../../src/core/insights/posthog.test.ts"],"sourcesContent":["/**\n * @vitest-environment jsdom\n */\n\nimport { describe, expect, beforeEach, it, vi } from \"vitest\";\nimport posthog from \"posthog-js\";\n\nimport { initPosthog } from \"./posthog\";\n\nvi.mock(\"posthog-js\", () => ({\n default: {\n init: vi.fn(),\n },\n}));\n\ndescribe(\"initPosthog\", () => {\n beforeEach(() => {\n vi.clearAllMocks();\n });\n\n it(\"does not initialize without an API key\", () => {\n initPosthog(\"\", \"https://app.posthog.com\");\n expect(posthog.init).not.toHaveBeenCalled();\n });\n\n it(\"initializes with session recording disabled\", () => {\n initPosthog(\"phc_test\", \"https://app.posthog.com\");\n\n expect(posthog.init).toHaveBeenCalledWith(\"phc_test\", {\n api_host: \"https://app.posthog.com\",\n capture_pageview: false,\n disable_session_recording: true,\n });\n });\n});\n"],"names":["describe","expect","beforeEach","it","vi","posthog","initPosthog","mock","default","init","fn","clearAllMocks","not","toHaveBeenCalled","toHaveBeenCalledWith","api_host","capture_pageview","disable_session_recording"],"mappings":"AAIA,OAASA,QAAQ,CAAEC,MAAM,CAAEC,UAAU,CAAEC,EAAE,CAAEC,EAAE,KAAQ,QAAS,AAC9D,QAAOC,YAAa,YAAa,AAEjC,QAASC,WAAW,KAAQ,WAAY,CAExCF,GAAGG,IAAI,CAAC,aAAc,IAAO,CAAA,CAC3BC,QAAS,CACPC,KAAML,GAAGM,EAAE,EACb,CACF,CAAA,GAEAV,SAAS,cAAe,KACtBE,WAAW,KACTE,GAAGO,aAAa,EAClB,GAEAR,GAAG,yCAA0C,KAC3CG,YAAY,GAAI,2BAChBL,OAAOI,QAAQI,IAAI,EAAEG,GAAG,CAACC,gBAAgB,EAC3C,GAEAV,GAAG,8CAA+C,KAChDG,YAAY,WAAY,2BAExBL,OAAOI,QAAQI,IAAI,EAAEK,oBAAoB,CAAC,WAAY,CACpDC,SAAU,0BACVC,iBAAkB,MAClBC,0BAA2B,IAC7B,EACF,EACF"}
@@ -1,2 +1,2 @@
1
- function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";import*as logger from"./logger";export class InsightsService{initInsights({mixpanelToken,mixpanelAutoCapture,mixpanelRecordSessionsPercent=1,posthogApiKey,posthogApiHost,debug=false}){this.debugMode=!!debug;if(this.debugMode){logger.debug("InsightService: Initializing insights")}try{mixpanel.initMixpanel(mixpanelToken,mixpanelAutoCapture,this.debugMode,mixpanelRecordSessionsPercent)}catch(e){if(this.debugMode){logger.error("Failed to initialize Mixpanel",e)}}try{posthog.initPosthog(posthogApiKey,posthogApiHost)}catch(e){if(this.debugMode){logger.error("Failed to initialize Posthog",e)}}}enableDebugMode(){this.debugMode=true;logger.debug("Enabling debug mode");try{mixpanel.enableDebugMode();posthog.enableDebugMode()}catch(e){logger.error("Failed to enable debug mode",e)}}disableDebugMode(){this.debugMode=false;logger.debug("Disabling debug mode");try{mixpanel.disableDebugMode();posthog.disableDebugMode()}catch(e){logger.error("Failed to disable debug mode",e)}}identify(identity){const{userId,accountId,organisationId,email,name,...properties}=identity;if(!userId){if(this.debugMode){logger.warn("User ID not provided, skipping identify")}return}if(this.debugMode){logger.info("Identifying user",{userId,accountId,organisationId,email,name,...properties})}try{mixpanel.identify({userId,accountId,organisationId,email,name,...properties})}catch(e){if(this.debugMode){logger.error("Failed to identify user in Mixpanel",e)}}try{posthog.identify({userId,accountId,organisationId,email,name,...properties})}catch(e){if(this.debugMode){logger.error("Failed to identify user in Posthog",e)}}}trackPageView(options){const{excludeIds,includeDataLayer,...properties}=options??{};if(this.debugMode){logger.info("Tracking page view")}try{mixpanel.trackPageView({excludeIds,...properties})}catch(e){if(this.debugMode){logger.error("Failed to track page view in Mixpanel",e)}}try{posthog.trackPageView(properties)}catch(e){if(this.debugMode){logger.error("Failed to track page view in Posthog",e)}}if(includeDataLayer){try{datalayer.trackPageView(properties)}catch(e){if(this.debugMode){logger.error("Failed to track page view in GTM",e)}}}}track(event,properties){if(this.debugMode){logger.info("Tracking event",{event,properties})}try{mixpanel.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Mixpanel",e)}}try{posthog.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Posthog",e)}}try{datalayer.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Datalayer",e)}}}startSessionRecording(){if(this.debugMode){logger.info("Starting session recording")}try{mixpanel.startSessionRecording()}catch(e){if(this.debugMode){logger.error("Failed to start session recording in Mixpanel",e)}}try{posthog.startSessionRecording()}catch(e){if(this.debugMode){logger.error("Failed to start session recording in Posthog",e)}}}stopSessionRecording(){if(this.debugMode){logger.info("Stopping session recording")}try{mixpanel.stopSessionRecording()}catch(e){if(this.debugMode){logger.error("Failed to stop session recording in Mixpanel",e)}}try{posthog.stopSessionRecording()}catch(e){if(this.debugMode){logger.error("Failed to stop session recording in Posthog",e)}}}setupObserver(){const getInsightAttributes=element=>{const MAX_ATTRIBUTES=10;let count=0;const attributes={};for(const attr of Array.from(element.attributes)){if(count>=MAX_ATTRIBUTES)break;if(attr.name.startsWith("data-insight-")){if(!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name))continue;if(typeof attr.value!=="string"||attr.value.length>100)continue;const key=attr.name.replace("data-insight-","").split("-").map((part,index)=>index===0?part:part.charAt(0).toUpperCase()+part.slice(1)).join("");attributes[key]=attr.value;count++}}return attributes};const findClosestElementWithInsights=element=>{let current=element;while(current&&current!==document.body){const insights=getInsightAttributes(current);if(Object.keys(insights).length>0){return insights}current=current.parentElement}return null};const handleClick=event=>{if(!(event.target instanceof HTMLElement))return;const insights=findClosestElementWithInsights(event.target);if(insights){const{event:eventName,...properties}=insights;this.track(eventName||"element_clicked",properties)}};document.body.addEventListener("click",handleClick);return()=>{document.body.removeEventListener("click",handleClick)}}constructor(){_define_property(this,"debugMode",false)}}
1
+ function _define_property(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true})}else{obj[key]=value}return obj}import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";import*as logger from"./logger";export class InsightsService{initInsights({mixpanelToken,mixpanelAutoCapture,mixpanelApiHost,posthogApiKey,posthogApiHost,debug=false}){this.debugMode=!!debug;if(this.debugMode){logger.debug("InsightService: Initializing insights")}try{mixpanel.initMixpanel(mixpanelToken,mixpanelAutoCapture,this.debugMode,mixpanelApiHost)}catch(e){if(this.debugMode){logger.error("Failed to initialize Mixpanel",e)}}try{posthog.initPosthog(posthogApiKey,posthogApiHost)}catch(e){if(this.debugMode){logger.error("Failed to initialize Posthog",e)}}}enableDebugMode(){this.debugMode=true;logger.debug("Enabling debug mode");try{mixpanel.enableDebugMode();posthog.enableDebugMode()}catch(e){logger.error("Failed to enable debug mode",e)}}disableDebugMode(){this.debugMode=false;logger.debug("Disabling debug mode");try{mixpanel.disableDebugMode();posthog.disableDebugMode()}catch(e){logger.error("Failed to disable debug mode",e)}}identify(identity){const{userId,accountId,organisationId,email,name,...properties}=identity;if(!userId){if(this.debugMode){logger.warn("User ID not provided, skipping identify")}return}if(this.debugMode){logger.info("Identifying user",{userId,accountId,organisationId,email,name,...properties})}try{mixpanel.identify({userId,accountId,organisationId,email,name,...properties})}catch(e){if(this.debugMode){logger.error("Failed to identify user in Mixpanel",e)}}try{posthog.identify({userId,accountId,organisationId,email,name,...properties})}catch(e){if(this.debugMode){logger.error("Failed to identify user in Posthog",e)}}}trackPageView(options){const{excludeIds,includeDataLayer,...properties}=options??{};if(this.debugMode){logger.info("Tracking page view")}try{mixpanel.trackPageView({excludeIds,...properties})}catch(e){if(this.debugMode){logger.error("Failed to track page view in Mixpanel",e)}}try{posthog.trackPageView(properties)}catch(e){if(this.debugMode){logger.error("Failed to track page view in Posthog",e)}}if(includeDataLayer){try{datalayer.trackPageView(properties)}catch(e){if(this.debugMode){logger.error("Failed to track page view in GTM",e)}}}}track(event,properties){if(this.debugMode){logger.info("Tracking event",{event,properties})}try{mixpanel.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Mixpanel",e)}}try{posthog.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Posthog",e)}}try{datalayer.track(event,properties)}catch(e){if(this.debugMode){logger.error("Failed to track event in Datalayer",e)}}}setupObserver(){const getInsightAttributes=element=>{const MAX_ATTRIBUTES=10;let count=0;const attributes={};for(const attr of Array.from(element.attributes)){if(count>=MAX_ATTRIBUTES)break;if(attr.name.startsWith("data-insight-")){if(!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name))continue;if(typeof attr.value!=="string"||attr.value.length>100)continue;const key=attr.name.replace("data-insight-","").split("-").map((part,index)=>index===0?part:part.charAt(0).toUpperCase()+part.slice(1)).join("");attributes[key]=attr.value;count++}}return attributes};const findClosestElementWithInsights=element=>{let current=element;while(current&&current!==document.body){const insights=getInsightAttributes(current);if(Object.keys(insights).length>0){return insights}current=current.parentElement}return null};const handleClick=event=>{if(!(event.target instanceof HTMLElement))return;const insights=findClosestElementWithInsights(event.target);if(insights){const{event:eventName,...properties}=insights;this.track(eventName||"element_clicked",properties)}};document.body.addEventListener("click",handleClick);return()=>{document.body.removeEventListener("click",handleClick)}}constructor(){_define_property(this,"debugMode",false)}}
2
2
  //# sourceMappingURL=service.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/service.ts"],"sourcesContent":["import type {\n AnalyticsService,\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport * as logger from \"./logger\";\n\n// The real implementation that will be used after initialization\nexport class InsightsService implements AnalyticsService {\n private debugMode: boolean = false;\n\n initInsights({\n mixpanelToken,\n mixpanelAutoCapture,\n mixpanelRecordSessionsPercent = 1,\n posthogApiKey,\n posthogApiHost,\n debug = false,\n }: InsightsConfig): void {\n this.debugMode = !!debug;\n\n if (this.debugMode) {\n logger.debug(\"InsightService: Initializing insights\");\n }\n\n try {\n mixpanel.initMixpanel(\n mixpanelToken,\n mixpanelAutoCapture,\n this.debugMode,\n mixpanelRecordSessionsPercent,\n );\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to initialize Mixpanel\", e);\n }\n }\n\n try {\n posthog.initPosthog(posthogApiKey, posthogApiHost);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to initialize Posthog\", e);\n }\n }\n }\n\n enableDebugMode(): void {\n this.debugMode = true;\n logger.debug(\"Enabling debug mode\");\n\n try {\n mixpanel.enableDebugMode();\n posthog.enableDebugMode();\n } catch (e) {\n logger.error(\"Failed to enable debug mode\", e);\n }\n }\n\n disableDebugMode(): void {\n this.debugMode = false;\n logger.debug(\"Disabling debug mode\");\n\n try {\n mixpanel.disableDebugMode();\n posthog.disableDebugMode();\n } catch (e) {\n logger.error(\"Failed to disable debug mode\", e);\n }\n }\n\n identify(identity: InsightsIdentity): void {\n const { userId, accountId, organisationId, email, name, ...properties } =\n identity;\n\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n if (this.debugMode) {\n logger.warn(\"User ID not provided, skipping identify\");\n }\n return;\n }\n\n if (this.debugMode) {\n logger.info(\"Identifying user\", {\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n }\n\n try {\n mixpanel.identify({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Mixpanel\", e);\n }\n }\n\n try {\n posthog.identify({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Posthog\", e);\n }\n }\n }\n\n trackPageView(options?: TrackPageViewOptions): void {\n const { excludeIds, includeDataLayer, ...properties } = options ?? {};\n\n if (this.debugMode) {\n logger.info(\"Tracking page view\");\n }\n\n try {\n mixpanel.trackPageView({ excludeIds, ...properties });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Mixpanel\", e);\n }\n }\n\n try {\n posthog.trackPageView(properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Posthog\", e);\n }\n }\n\n if (includeDataLayer) {\n try {\n datalayer.trackPageView(properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in GTM\", e);\n }\n }\n }\n }\n\n track(event: string, properties?: Record<string, unknown>): void {\n if (this.debugMode) {\n logger.info(\"Tracking event\", { event, properties });\n }\n\n try {\n mixpanel.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Mixpanel\", e);\n }\n }\n\n try {\n posthog.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Posthog\", e);\n }\n }\n\n try {\n datalayer.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Datalayer\", e);\n }\n }\n }\n\n startSessionRecording(): void {\n if (this.debugMode) {\n logger.info(\"Starting session recording\");\n }\n\n try {\n mixpanel.startSessionRecording();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to start session recording in Mixpanel\", e);\n }\n }\n\n try {\n posthog.startSessionRecording();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to start session recording in Posthog\", e);\n }\n }\n }\n\n stopSessionRecording(): void {\n if (this.debugMode) {\n logger.info(\"Stopping session recording\");\n }\n\n try {\n mixpanel.stopSessionRecording();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to stop session recording in Mixpanel\", e);\n }\n }\n\n try {\n posthog.stopSessionRecording();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to stop session recording in Posthog\", e);\n }\n }\n }\n\n setupObserver(): () => void {\n // Helper to get all data-insight-* attributes from an element\n const getInsightAttributes = (\n element: HTMLElement,\n ): { event?: string; [key: string]: string | undefined } => {\n // limit how many data attributes we'll process\n const MAX_ATTRIBUTES = 10;\n let count = 0;\n\n const attributes: { event?: string; [key: string]: string | undefined } =\n {};\n\n for (const attr of Array.from(element.attributes)) {\n if (count >= MAX_ATTRIBUTES) break;\n if (attr.name.startsWith(\"data-insight-\")) {\n // Validate attribute name format\n if (!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name)) continue;\n\n // Sanitize attribute value\n if (typeof attr.value !== \"string\" || attr.value.length > 100)\n continue;\n\n // Convert data-insight-event-name to eventName\n const key = attr.name\n .replace(\"data-insight-\", \"\")\n .split(\"-\")\n .map((part: string, index: number) =>\n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1),\n )\n .join(\"\");\n attributes[key] = attr.value;\n count++;\n }\n }\n return attributes;\n };\n\n // Helper to find closest element with data-insight attributes\n const findClosestElementWithInsights = (element: HTMLElement) => {\n let current = element;\n while (current && current !== document.body) {\n const insights = getInsightAttributes(current);\n if (Object.keys(insights).length > 0) {\n return insights;\n }\n\n current = current.parentElement as HTMLElement;\n }\n return null;\n };\n\n // Global click handler\n const handleClick = (event: MouseEvent): void => {\n if (!(event.target instanceof HTMLElement)) return;\n const insights = findClosestElementWithInsights(event.target);\n if (insights) {\n // Extract special properties if they exist\n const { event: eventName, ...properties } = insights;\n this.track(eventName || \"element_clicked\", properties);\n }\n };\n\n // Add listener to document body to catch all clicks\n document.body.addEventListener(\"click\", handleClick);\n\n // Return cleanup function in case it's needed\n return () => {\n document.body.removeEventListener(\"click\", handleClick);\n };\n }\n}\n"],"names":["datalayer","mixpanel","posthog","logger","InsightsService","initInsights","mixpanelToken","mixpanelAutoCapture","mixpanelRecordSessionsPercent","posthogApiKey","posthogApiHost","debug","debugMode","initMixpanel","e","error","initPosthog","enableDebugMode","disableDebugMode","identify","identity","userId","accountId","organisationId","email","name","properties","warn","info","trackPageView","options","excludeIds","includeDataLayer","track","event","startSessionRecording","stopSessionRecording","setupObserver","getInsightAttributes","element","MAX_ATTRIBUTES","count","attributes","attr","Array","from","startsWith","test","value","length","key","replace","split","map","part","index","charAt","toUpperCase","slice","join","findClosestElementWithInsights","current","document","body","insights","Object","keys","parentElement","handleClick","target","HTMLElement","eventName","addEventListener","removeEventListener"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/core/insights/service.ts"],"sourcesContent":["import type {\n AnalyticsService,\n InsightsConfig,\n InsightsIdentity,\n TrackPageViewOptions,\n} from \"./types\";\nimport * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport * as logger from \"./logger\";\n\n// The real implementation that will be used after initialization\nexport class InsightsService implements AnalyticsService {\n private debugMode: boolean = false;\n\n initInsights({\n mixpanelToken,\n mixpanelAutoCapture,\n mixpanelApiHost,\n posthogApiKey,\n posthogApiHost,\n debug = false,\n }: InsightsConfig): void {\n this.debugMode = !!debug;\n\n if (this.debugMode) {\n logger.debug(\"InsightService: Initializing insights\");\n }\n\n try {\n mixpanel.initMixpanel(\n mixpanelToken,\n mixpanelAutoCapture,\n this.debugMode,\n mixpanelApiHost,\n );\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to initialize Mixpanel\", e);\n }\n }\n\n try {\n posthog.initPosthog(posthogApiKey, posthogApiHost);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to initialize Posthog\", e);\n }\n }\n }\n\n enableDebugMode(): void {\n this.debugMode = true;\n logger.debug(\"Enabling debug mode\");\n\n try {\n mixpanel.enableDebugMode();\n posthog.enableDebugMode();\n } catch (e) {\n logger.error(\"Failed to enable debug mode\", e);\n }\n }\n\n disableDebugMode(): void {\n this.debugMode = false;\n logger.debug(\"Disabling debug mode\");\n\n try {\n mixpanel.disableDebugMode();\n posthog.disableDebugMode();\n } catch (e) {\n logger.error(\"Failed to disable debug mode\", e);\n }\n }\n\n identify(identity: InsightsIdentity): void {\n const { userId, accountId, organisationId, email, name, ...properties } =\n identity;\n\n // In very rare cases we might have a user without an account, so we'll\n // let null/undefined/blank strings through on that one\n if (!userId) {\n if (this.debugMode) {\n logger.warn(\"User ID not provided, skipping identify\");\n }\n return;\n }\n\n if (this.debugMode) {\n logger.info(\"Identifying user\", {\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n }\n\n try {\n mixpanel.identify({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Mixpanel\", e);\n }\n }\n\n try {\n posthog.identify({\n userId,\n accountId,\n organisationId,\n email,\n name,\n ...properties,\n });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Posthog\", e);\n }\n }\n }\n\n trackPageView(options?: TrackPageViewOptions): void {\n const { excludeIds, includeDataLayer, ...properties } = options ?? {};\n\n if (this.debugMode) {\n logger.info(\"Tracking page view\");\n }\n\n try {\n mixpanel.trackPageView({ excludeIds, ...properties });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Mixpanel\", e);\n }\n }\n\n try {\n posthog.trackPageView(properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Posthog\", e);\n }\n }\n\n if (includeDataLayer) {\n try {\n datalayer.trackPageView(properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in GTM\", e);\n }\n }\n }\n }\n\n track(event: string, properties?: Record<string, unknown>): void {\n if (this.debugMode) {\n logger.info(\"Tracking event\", { event, properties });\n }\n\n try {\n mixpanel.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Mixpanel\", e);\n }\n }\n\n try {\n posthog.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Posthog\", e);\n }\n }\n\n try {\n datalayer.track(event, properties);\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track event in Datalayer\", e);\n }\n }\n }\n\n setupObserver(): () => void {\n // Helper to get all data-insight-* attributes from an element\n const getInsightAttributes = (\n element: HTMLElement,\n ): { event?: string; [key: string]: string | undefined } => {\n // limit how many data attributes we'll process\n const MAX_ATTRIBUTES = 10;\n let count = 0;\n\n const attributes: { event?: string; [key: string]: string | undefined } =\n {};\n\n for (const attr of Array.from(element.attributes)) {\n if (count >= MAX_ATTRIBUTES) break;\n if (attr.name.startsWith(\"data-insight-\")) {\n // Validate attribute name format\n if (!/^data-insight-[a-zA-Z0-9-]+$/.test(attr.name)) continue;\n\n // Sanitize attribute value\n if (typeof attr.value !== \"string\" || attr.value.length > 100)\n continue;\n\n // Convert data-insight-event-name to eventName\n const key = attr.name\n .replace(\"data-insight-\", \"\")\n .split(\"-\")\n .map((part: string, index: number) =>\n index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1),\n )\n .join(\"\");\n attributes[key] = attr.value;\n count++;\n }\n }\n return attributes;\n };\n\n // Helper to find closest element with data-insight attributes\n const findClosestElementWithInsights = (element: HTMLElement) => {\n let current = element;\n while (current && current !== document.body) {\n const insights = getInsightAttributes(current);\n if (Object.keys(insights).length > 0) {\n return insights;\n }\n\n current = current.parentElement as HTMLElement;\n }\n return null;\n };\n\n // Global click handler\n const handleClick = (event: MouseEvent): void => {\n if (!(event.target instanceof HTMLElement)) return;\n const insights = findClosestElementWithInsights(event.target);\n if (insights) {\n // Extract special properties if they exist\n const { event: eventName, ...properties } = insights;\n this.track(eventName || \"element_clicked\", properties);\n }\n };\n\n // Add listener to document body to catch all clicks\n document.body.addEventListener(\"click\", handleClick);\n\n // Return cleanup function in case it's needed\n return () => {\n document.body.removeEventListener(\"click\", handleClick);\n };\n }\n}\n"],"names":["datalayer","mixpanel","posthog","logger","InsightsService","initInsights","mixpanelToken","mixpanelAutoCapture","mixpanelApiHost","posthogApiKey","posthogApiHost","debug","debugMode","initMixpanel","e","error","initPosthog","enableDebugMode","disableDebugMode","identify","identity","userId","accountId","organisationId","email","name","properties","warn","info","trackPageView","options","excludeIds","includeDataLayer","track","event","setupObserver","getInsightAttributes","element","MAX_ATTRIBUTES","count","attributes","attr","Array","from","startsWith","test","value","length","key","replace","split","map","part","index","charAt","toUpperCase","slice","join","findClosestElementWithInsights","current","document","body","insights","Object","keys","parentElement","handleClick","target","HTMLElement","eventName","addEventListener","removeEventListener"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/types.ts"],"sourcesContent":["export type InsightsConfig = {\n debug: boolean;\n mixpanelToken: string;\n mixpanelAutoCapture: boolean;\n mixpanelRecordSessionsPercent: number;\n posthogApiKey: string;\n posthogApiHost: string;\n};\n\n// Define the interface for our analytics service\nexport interface AnalyticsService {\n initInsights: (config: InsightsConfig) => void;\n enableDebugMode: () => void;\n disableDebugMode: () => void;\n identify: (identity: InsightsIdentity) => void;\n trackPageView: (options?: TrackPageViewOptions) => void;\n track: (event: string, properties?: Record<string, unknown>) => void;\n startSessionRecording: () => void;\n stopSessionRecording: () => void;\n setupObserver: () => () => void;\n}\n\n// Command type for our queue\nexport type Command = {\n methodName: keyof AnalyticsService;\n args: unknown[];\n};\n\nexport type InsightsIdentity = {\n userId: string;\n accountId: string;\n organisationId?: string;\n email?: string;\n name?: string;\n} & Record<string, unknown>;\n\nexport type TrackPageViewOptions = {\n includeDataLayer?: boolean;\n excludeIds?: string[];\n} & Record<string, unknown>;\n"],"names":[],"mappings":"AAoCA,QAG4B"}
1
+ {"version":3,"sources":["../../../src/core/insights/types.ts"],"sourcesContent":["export type InsightsConfig = {\n debug: boolean;\n mixpanelToken: string;\n mixpanelAutoCapture: boolean;\n mixpanelApiHost?: string;\n posthogApiKey: string;\n posthogApiHost: string;\n};\n\n// Define the interface for our analytics service\nexport interface AnalyticsService {\n initInsights: (config: InsightsConfig) => void;\n enableDebugMode: () => void;\n disableDebugMode: () => void;\n identify: (identity: InsightsIdentity) => void;\n trackPageView: (options?: TrackPageViewOptions) => void;\n track: (event: string, properties?: Record<string, unknown>) => void;\n setupObserver: () => () => void;\n}\n\n// Command type for our queue\nexport type Command = {\n methodName: keyof AnalyticsService;\n args: unknown[];\n};\n\nexport type InsightsIdentity = {\n userId: string;\n accountId: string;\n organisationId?: string;\n email?: string;\n name?: string;\n} & Record<string, unknown>;\n\nexport type TrackPageViewOptions = {\n includeDataLayer?: boolean;\n excludeIds?: string[];\n} & Record<string, unknown>;\n"],"names":[],"mappings":"AAkCA,QAG4B"}
package/index.d.ts CHANGED
@@ -304,14 +304,6 @@ export default Button;
304
304
  //# sourceMappingURL=Button.d.ts.map
305
305
  }
306
306
 
307
- declare module '@ably/ui/core/Code/Code.test' {
308
- /**
309
- * @vitest-environment jsdom
310
- */
311
- export {};
312
- //# sourceMappingURL=Code.test.d.ts.map
313
- }
314
-
315
307
  declare module '@ably/ui/core/Code/component' {
316
308
  export default highlightEl;
317
309
  function highlightEl(el: any): void;
@@ -348,14 +340,6 @@ export default ApiKeySelector;
348
340
  //# sourceMappingURL=ApiKeySelector.d.ts.map
349
341
  }
350
342
 
351
- declare module '@ably/ui/core/CodeSnippet/CodeSnippet.test' {
352
- /**
353
- * @vitest-environment jsdom
354
- */
355
- export {};
356
- //# sourceMappingURL=CodeSnippet.test.d.ts.map
357
- }
358
-
359
343
  declare module '@ably/ui/core/CodeSnippet/CopyButton' {
360
344
  type CopyButtonProps = {
361
345
  onCopy: () => void;
@@ -430,11 +414,6 @@ export default languages;
430
414
  //# sourceMappingURL=languages.d.ts.map
431
415
  }
432
416
 
433
- declare module '@ably/ui/core/CodeSnippet/languages.test' {
434
- export {};
435
- //# sourceMappingURL=languages.test.d.ts.map
436
- }
437
-
438
417
  declare module '@ably/ui/core/CodeSnippet' {
439
418
  import React from "react";
440
419
  import { SDKType } from "@ably/ui/core/CodeSnippet/languages";
@@ -6321,14 +6300,6 @@ export function useThemedScrollpoints(scrollpoints: ThemedScrollpoint[]): string
6321
6300
  //# sourceMappingURL=use-themed-scrollpoints.d.ts.map
6322
6301
  }
6323
6302
 
6324
- declare module '@ably/ui/core/hooks/use-themed-scrollpoints.test' {
6325
- /**
6326
- * @vitest-environment jsdom
6327
- */
6328
- export {};
6329
- //# sourceMappingURL=use-themed-scrollpoints.test.d.ts.map
6330
- }
6331
-
6332
6303
  declare module '@ably/ui/core/insights/command-queue' {
6333
6304
  import { AnalyticsService, InsightsConfig, InsightsIdentity, TrackPageViewOptions } from "@ably/ui/core/types";
6334
6305
  export class InsightsCommandQueue implements AnalyticsService {
@@ -6345,8 +6316,6 @@ export class InsightsCommandQueue implements AnalyticsService {
6345
6316
  identify(_identity: InsightsIdentity): void;
6346
6317
  trackPageView(_options?: TrackPageViewOptions): void;
6347
6318
  track(_event: string, _properties?: Record<string, unknown>): void;
6348
- startSessionRecording(): void;
6349
- stopSessionRecording(): void;
6350
6319
  setupObserver(): () => void;
6351
6320
  }
6352
6321
  //# sourceMappingURL=command-queue.d.ts.map
@@ -6372,20 +6341,10 @@ export const disableDebugMode: () => void;
6372
6341
  export const identify: (identity: InsightsIdentity) => void;
6373
6342
  export const trackPageView: (options?: TrackPageViewOptions) => void;
6374
6343
  export const track: (event: string, properties?: Record<string, unknown>) => void;
6375
- export const startSessionRecording: () => void;
6376
- export const stopSessionRecording: () => void;
6377
6344
  export const setupObserver: () => () => void;
6378
6345
  //# sourceMappingURL=index.d.ts.map
6379
6346
  }
6380
6347
 
6381
- declare module '@ably/ui/core/insights/index.test' {
6382
- /**
6383
- * @vitest-environment jsdom
6384
- */
6385
- export {};
6386
- //# sourceMappingURL=index.test.d.ts.map
6387
- }
6388
-
6389
6348
  declare module '@ably/ui/core/insights/logger' {
6390
6349
  export const debug: (...args: unknown[]) => void;
6391
6350
  export const info: (...args: unknown[]) => void;
@@ -6396,25 +6355,16 @@ export const error: (...args: unknown[]) => void;
6396
6355
 
6397
6356
  declare module '@ably/ui/core/insights/mixpanel' {
6398
6357
  import { InsightsIdentity, TrackPageViewOptions } from "@ably/ui/core/types";
6399
- export const initMixpanel: (token: string, autoCapture?: boolean, debug?: boolean, recordSessionsPercent?: number) => void;
6358
+ export const MIXPANEL_EU_API_HOST = "https://api-eu.mixpanel.com";
6359
+ export const initMixpanel: (token: string, autoCapture?: boolean, debug?: boolean, apiHost?: string) => void;
6400
6360
  export const enableDebugMode: () => void;
6401
6361
  export const disableDebugMode: () => void;
6402
6362
  export const identify: ({ userId, accountId, organisationId, email, name, ...properties }: InsightsIdentity) => void;
6403
6363
  export const trackPageView: (properties?: TrackPageViewOptions) => void;
6404
6364
  export const track: (event: string, properties?: Record<string, unknown>) => void;
6405
- export const startSessionRecording: () => void;
6406
- export const stopSessionRecording: () => void;
6407
6365
  //# sourceMappingURL=mixpanel.d.ts.map
6408
6366
  }
6409
6367
 
6410
- declare module '@ably/ui/core/insights/mixpanel.test' {
6411
- /**
6412
- * @vitest-environment jsdom
6413
- */
6414
- export {};
6415
- //# sourceMappingURL=mixpanel.test.d.ts.map
6416
- }
6417
-
6418
6368
  declare module '@ably/ui/core/insights/posthog' {
6419
6369
  import { InsightsIdentity } from "@ably/ui/core/types";
6420
6370
  export const initPosthog: (apiKey: string, apiHost: string) => void;
@@ -6423,31 +6373,19 @@ export const disableDebugMode: () => void;
6423
6373
  export const identify: ({ userId, accountId, organisationId, email, name, ...properties }: InsightsIdentity) => void;
6424
6374
  export const trackPageView: (properties?: Record<string, unknown>) => void;
6425
6375
  export const track: (event: string, properties?: Record<string, unknown>) => void;
6426
- export const startSessionRecording: () => void;
6427
- export const stopSessionRecording: () => void;
6428
6376
  //# sourceMappingURL=posthog.d.ts.map
6429
6377
  }
6430
6378
 
6431
- declare module '@ably/ui/core/insights/posthog.test' {
6432
- /**
6433
- * @vitest-environment jsdom
6434
- */
6435
- export {};
6436
- //# sourceMappingURL=posthog.test.d.ts.map
6437
- }
6438
-
6439
6379
  declare module '@ably/ui/core/insights/service' {
6440
6380
  import type { AnalyticsService, InsightsConfig, InsightsIdentity, TrackPageViewOptions } from "@ably/ui/core/types";
6441
6381
  export class InsightsService implements AnalyticsService {
6442
6382
  private debugMode;
6443
- initInsights({ mixpanelToken, mixpanelAutoCapture, mixpanelRecordSessionsPercent, posthogApiKey, posthogApiHost, debug, }: InsightsConfig): void;
6383
+ initInsights({ mixpanelToken, mixpanelAutoCapture, mixpanelApiHost, posthogApiKey, posthogApiHost, debug, }: InsightsConfig): void;
6444
6384
  enableDebugMode(): void;
6445
6385
  disableDebugMode(): void;
6446
6386
  identify(identity: InsightsIdentity): void;
6447
6387
  trackPageView(options?: TrackPageViewOptions): void;
6448
6388
  track(event: string, properties?: Record<string, unknown>): void;
6449
- startSessionRecording(): void;
6450
- stopSessionRecording(): void;
6451
6389
  setupObserver(): () => void;
6452
6390
  }
6453
6391
  //# sourceMappingURL=service.d.ts.map
@@ -6458,7 +6396,7 @@ export type InsightsConfig = {
6458
6396
  debug: boolean;
6459
6397
  mixpanelToken: string;
6460
6398
  mixpanelAutoCapture: boolean;
6461
- mixpanelRecordSessionsPercent: number;
6399
+ mixpanelApiHost?: string;
6462
6400
  posthogApiKey: string;
6463
6401
  posthogApiHost: string;
6464
6402
  };
@@ -6469,8 +6407,6 @@ export interface AnalyticsService {
6469
6407
  identify: (identity: InsightsIdentity) => void;
6470
6408
  trackPageView: (options?: TrackPageViewOptions) => void;
6471
6409
  track: (event: string, properties?: Record<string, unknown>) => void;
6472
- startSessionRecording: () => void;
6473
- stopSessionRecording: () => void;
6474
6410
  setupObserver: () => () => void;
6475
6411
  }
6476
6412
  export type Command = {
@@ -6646,25 +6582,6 @@ export const sanitizeMarketingHtml: (input: string | null | undefined) => string
6646
6582
  //# sourceMappingURL=sanitize-html.d.ts.map
6647
6583
  }
6648
6584
 
6649
- declare module '@ably/ui/core/utils/sanitize-html.test' {
6650
- /**
6651
- * @vitest-environment jsdom
6652
- *
6653
- * Adversarial test corpus for the sanitize-html primitives. Cases are sourced
6654
- * from public XSS cheatsheets (OWASP, PortSwigger, html5sec.org) and from
6655
- * past DOMPurify CVEs / advisories. They exercise the wrappers, not DOMPurify
6656
- * itself — the assertion in every case is "the rendered string does not
6657
- * carry an executable payload through to a DOM that would fire on hydration".
6658
- *
6659
- * The shape of each assertion is intentionally strict: we don't trust that
6660
- * "alert" or "javascript" not appearing as a substring means safety, so most
6661
- * checks combine "tag/attr stripped" + "no live-handler attribute survives"
6662
- * + "raw text content may survive but is rendered as text".
6663
- */
6664
- export {};
6665
- //# sourceMappingURL=sanitize-html.test.d.ts.map
6666
- }
6667
-
6668
6585
  declare module '@ably/ui/core/utils/syntax-highlighter-registry' {
6669
6586
  export default registry;
6670
6587
  const registry: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ably/ui",
3
- "version": "18.2.0",
3
+ "version": "18.3.1",
4
4
  "description": "Home of the Ably design system library ([design.ably.com](https://design.ably.com)). It provides a showcase, development/test environment and a publishing pipeline for different distributables.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,11 +37,12 @@
37
37
  "@types/react": "^18.3.1",
38
38
  "@types/react-dom": "^18.3.0",
39
39
  "@types/svg-sprite": "^0.0.39",
40
- "@typescript-eslint/eslint-plugin": "^8.59.0",
41
- "@typescript-eslint/parser": "^8.59.0",
40
+ "@typescript-eslint/eslint-plugin": "^8.61.0",
41
+ "@typescript-eslint/parser": "^8.61.0",
42
42
  "@vitejs/plugin-react-swc": "^4.3.0",
43
- "@vitest/browser": "3.2.4",
44
- "@vitest/coverage-v8": "3.2.4",
43
+ "@vitest/browser": "^4.1.8",
44
+ "@vitest/browser-playwright": "^4.1.8",
45
+ "@vitest/coverage-v8": "^4.1.8",
45
46
  "autoprefixer": "^10.5.0",
46
47
  "eslint": "^8.57.1",
47
48
  "eslint-config-prettier": "^10.1.8",
@@ -49,15 +50,15 @@
49
50
  "eslint-plugin-react": "^7.35.0",
50
51
  "eslint-plugin-react-hooks": "^7.1.1",
51
52
  "eslint-plugin-react-perf": "^3.3.3",
52
- "eslint-plugin-storybook": "^10.3.3",
53
+ "eslint-plugin-storybook": "^10.4.0",
53
54
  "heroicons": "^2.2.0",
54
55
  "http-server": "14.1.1",
55
56
  "jsdom": "^29.1.0",
56
57
  "mixpanel-browser": "^2.79.0",
57
58
  "msw": "2.14.3",
58
59
  "msw-storybook-addon": "^2.0.7",
59
- "playwright": "^1.59.1",
60
- "posthog-js": "1.375.0",
60
+ "playwright": "^1.60.0",
61
+ "posthog-js": "1.383.3",
61
62
  "prettier": "^3.8.0",
62
63
  "storybook": "^10.4.0",
63
64
  "svg-sprite": "^2.0.4",
@@ -65,7 +66,7 @@
65
66
  "ts-node": "^10.9.2",
66
67
  "typescript": "6.0.2",
67
68
  "vite": "^7.3.2",
68
- "vitest": "^3.2.4"
69
+ "vitest": "^4.1.8"
69
70
  },
70
71
  "dependencies": {
71
72
  "@heroicons/react": "^2.2.0",
@@ -78,7 +79,7 @@
78
79
  "@radix-ui/react-tooltip": "^1.2.8",
79
80
  "array-flat-polyfill": "^1.0.1",
80
81
  "clsx": "^2.1.1",
81
- "dompurify": "^3.4.0",
82
+ "dompurify": "^3.4.9",
82
83
  "embla-carousel": "^8.6.0",
83
84
  "embla-carousel-autoplay": "^8.6.0",
84
85
  "embla-carousel-react": "^8.6.0",