@ably/ui 15.7.0-dev.a9daaf05 → 15.8.0-dev.1527ef06

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.
package/core/Flash.js CHANGED
@@ -1,2 +1,2 @@
1
- import React,{useEffect,useState,useRef}from"react";import DOMPurify from"dompurify";import{getRemoteDataStore}from"./remote-data-store.js";import ConnectStateWrapper from"./ConnectStateWrapper";import Icon from"./Icon";const REDUCER_KEY="flashes";const FLASH_DATA_ID="ui-flashes";const initialState={items:[]};const reducerFlashes={[REDUCER_KEY]:(state=initialState,action)=>{switch(action.type){case"flash/push":{const flashes=Array.isArray(action.payload)?action.payload:[action.payload];return{items:[...state.items,...flashes]}}default:return state}}};const selectFlashes=store=>store.getState()[REDUCER_KEY];const FLASH_BG_COLOR={error:"bg-gui-error",success:"bg-zingy-green",notice:"bg-electric-cyan",info:"bg-electric-cyan",alert:"bg-active-orange"};const FLASH_TEXT_COLOR={error:"text-white",success:"text-cool-black",notice:"text-cool-black",info:"text-cool-black",alert:"text-white"};const AUTO_HIDE=["success","info","notice"];const AUTO_HIDE_TIME=8e3;const useAutoHide=(type,closeFlash)=>{const timeoutId=useRef(null);useEffect(()=>{if(AUTO_HIDE.includes(type)){timeoutId.current=setTimeout(()=>{closeFlash()},AUTO_HIDE_TIME)}return()=>{if(timeoutId.current){clearTimeout(timeoutId.current)}}},[])};const Flash=({id,type,content,removeFlash})=>{const ref=useRef(null);const[closed,setClosed]=useState(false);const[flashHeight,setFlashHeight]=useState(0);const[triggerEntryAnimation,setTriggerEntryAnimation]=useState(false);const closeFlash=()=>{if(ref.current){setFlashHeight(ref.current.getBoundingClientRect().height)}setClosed(true);setTimeout(()=>{if(id){removeFlash(id)}},100)};useEffect(()=>setTriggerEntryAnimation(true),[]);useAutoHide(type,closeFlash);const animateEntry=triggerEntryAnimation&&!closed;let style;if(flashHeight&&!closed){style={height:`${flashHeight}px`}}else if(closed){style={height:0,marginTop:0,zIndex:-1}}else{style={}}const safeContent=DOMPurify.sanitize(content,{ALLOWED_TAGS:["a"],ALLOWED_ATTR:["href","data-method","rel"]});const withIcons={notice:"icon-gui-ably-badge",success:"icon-gui-check-outline",error:"icon-gui-exclamation-triangle-outline",alert:"icon-gui-exclamation-triangle-outline",info:""};const iconColor={notice:"text-cool-black",success:"text-cool-black",error:"text-white",alert:"text-white",info:""};return React.createElement("div",{className:`ui-flash-message ui-grid-px ${animateEntry?"ui-flash-message-enter":""}`,style:style,ref:ref,"data-id":"ui-flash"},React.createElement("div",{className:`${FLASH_BG_COLOR[type]} p-32 flex align-center rounded shadow-container-subtle`},withIcons[type]&&iconColor[type]&&React.createElement(Icon,{name:withIcons[type],color:iconColor[type],size:"1.5rem",additionalCSS:"mr-16 self-baseline"}),React.createElement("p",{className:`ui-text-p1 mr-16 ${FLASH_TEXT_COLOR[type]}`,dangerouslySetInnerHTML:{__html:safeContent}}),React.createElement("button",{type:"button",className:"p-0 ml-auto self-start focus:outline-none",onClick:closeFlash},iconColor[type]&&React.createElement(Icon,{name:"icon-gui-x-mark-outline",color:iconColor[type],size:"1.5rem",additionalCSS:"transition-colors"}))))};const Flashes=({flashes})=>{const[flashesWithIds,setFlashesWithIds]=useState([]);const removeFlash=flashId=>setFlashesWithIds(items=>items.filter(item=>item.id!==flashId));useEffect(()=>{setFlashesWithIds(state=>{return[...state,...(flashes?.items??[]).map(flash=>({...flash,id:Math.random().toString(36).slice(2),removed:false}))]})},[flashes]);return React.createElement("div",{className:"ui-flash","data-id":FLASH_DATA_ID},flashesWithIds.filter(item=>!item.removed).map(flash=>React.createElement(Flash,{key:flash.id,...flash,removeFlash:removeFlash})))};const BackendFlashes=({flashes})=>{useEffect(()=>{const transformedFlashes=flashes.map(flash=>{const[type,content]=flash;return{type,content}})||[];if(transformedFlashes.length>0){const store=getRemoteDataStore();store.dispatch({type:"flash/push",payload:transformedFlashes})}},[]);const WrappedFlashes=ConnectStateWrapper(Flashes,{flashes:selectFlashes});return React.createElement(WrappedFlashes,null)};export{reducerFlashes,FLASH_DATA_ID,Flashes};export default BackendFlashes;
1
+ import React,{useEffect,useState,useRef}from"react";import DOMPurify from"dompurify";import{getRemoteDataStore}from"./remote-data-store.js";import ConnectStateWrapper from"./ConnectStateWrapper";import Icon from"./Icon";const REDUCER_KEY="flashes";const FLASH_DATA_ID="ui-flashes";const initialState={items:[]};const reducerFlashes={[REDUCER_KEY]:(state=initialState,action)=>{switch(action.type){case"flash/push":{const flashes=Array.isArray(action.payload)?action.payload:[action.payload];return{items:[...state.items,...flashes]}}default:return state}}};const selectFlashes=store=>store.getState()[REDUCER_KEY];const FLASH_BG_COLOR={error:"bg-gui-error",success:"bg-zingy-green",notice:"bg-electric-cyan",info:"bg-electric-cyan",alert:"bg-active-orange"};const FLASH_TEXT_COLOR={error:"text-white",success:"text-cool-black",notice:"text-cool-black",info:"text-cool-black",alert:"text-white"};const AUTO_HIDE=["success","info","notice"];const AUTO_HIDE_TIME=8e3;const useAutoHide=(type,closeFlash)=>{const timeoutId=useRef(null);useEffect(()=>{if(AUTO_HIDE.includes(type)){timeoutId.current=setTimeout(()=>{closeFlash()},AUTO_HIDE_TIME)}return()=>{if(timeoutId.current){clearTimeout(timeoutId.current)}}},[])};const Flash=({id,type,content,removeFlash})=>{const ref=useRef(null);const[closed,setClosed]=useState(false);const[flashHeight,setFlashHeight]=useState(0);const[triggerEntryAnimation,setTriggerEntryAnimation]=useState(false);const closeFlash=()=>{if(ref.current){setFlashHeight(ref.current.getBoundingClientRect().height)}setClosed(true);setTimeout(()=>{if(id){removeFlash(id)}},100)};useEffect(()=>setTriggerEntryAnimation(true),[]);useAutoHide(type,closeFlash);const animateEntry=triggerEntryAnimation&&!closed;let style;if(flashHeight&&!closed){style={height:`${flashHeight}px`}}else if(closed){style={height:0,marginTop:0,zIndex:-1}}else{style={}}const safeContent=DOMPurify.sanitize(content,{ALLOWED_TAGS:["a"],ALLOWED_ATTR:["href","data-method","rel"]});const withIcons={notice:"icon-gui-ably-badge",success:"icon-gui-check-outline",error:"icon-gui-exclamation-triangle-outline",alert:"icon-gui-exclamation-triangle-outline",info:""};const iconColor={notice:"text-cool-black",success:"text-cool-black",error:"text-white",alert:"text-white",info:""};return React.createElement("div",{className:`ui-flash-message ui-grid-px ${animateEntry?"ui-flash-message-enter":""}`,style:style,ref:ref,"data-id":"ui-flash"},React.createElement("div",{className:`${FLASH_BG_COLOR[type]} p-32 flex align-center rounded shadow-container-subtle`},withIcons[type]&&iconColor[type]&&React.createElement(Icon,{name:withIcons[type],color:iconColor[type],size:"1.5rem",additionalCSS:"mr-16 self-baseline"}),React.createElement("p",{className:`ui-text-p1 mr-16 ${FLASH_TEXT_COLOR[type]}`,dangerouslySetInnerHTML:{__html:safeContent}}),React.createElement("button",{type:"button",className:"p-0 ml-auto self-start focus:outline-none",onClick:closeFlash},iconColor[type]&&React.createElement(Icon,{name:"icon-gui-x-mark-outline",color:iconColor[type],size:"1.5rem",additionalCSS:"transition-colors"}))))};const Flashes=({flashes})=>{const[flashesWithIds,setFlashesWithIds]=useState([]);const removeFlash=flashId=>setFlashesWithIds(items=>items.filter(item=>item.id!==flashId));useEffect(()=>{setFlashesWithIds(state=>{return[...state,...(flashes?.items??[]).map(flash=>({...flash,id:Math.random().toString(36).slice(2),removed:false,removeFlash}))]})},[flashes]);return React.createElement("div",{className:"ui-flash","data-id":FLASH_DATA_ID},flashesWithIds.filter(item=>!item.removed).map(flash=>React.createElement(Flash,{key:flash.id,...flash})))};const BackendFlashes=({flashes})=>{useEffect(()=>{const transformedFlashes=flashes.map(flash=>{const[type,content]=flash;return{type,content}})||[];if(transformedFlashes.length>0){const store=getRemoteDataStore();store.dispatch({type:"flash/push",payload:transformedFlashes})}},[]);const WrappedFlashes=ConnectStateWrapper(Flashes,{flashes:selectFlashes});return React.createElement(WrappedFlashes,null)};export{reducerFlashes,FLASH_DATA_ID,Flashes};export default BackendFlashes;
2
2
  //# sourceMappingURL=Flash.js.map
package/core/Flash.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/Flash.tsx"],"sourcesContent":["import React, { useEffect, useState, useRef } from \"react\";\nimport DOMPurify from \"dompurify\";\nimport { getRemoteDataStore } from \"./remote-data-store.js\";\nimport ConnectStateWrapper from \"./ConnectStateWrapper\";\nimport Icon from \"./Icon\";\nimport { ColorClass } from \"./styles/colors/types\";\nimport { IconName } from \"./Icon/types\";\n\ntype FlashPropsType = \"error\" | \"success\" | \"notice\" | \"info\" | \"alert\";\n\ntype FlashProps = {\n id: string;\n removed: boolean;\n type: FlashPropsType;\n content: string;\n removeFlash: (id: string) => void;\n};\n\ntype FlashesProps = {\n flashes: { items: FlashProps[] };\n};\n\ntype BackendFlashesProps = {\n flashes: string[][];\n};\n\nconst REDUCER_KEY = \"flashes\";\nconst FLASH_DATA_ID = \"ui-flashes\";\n\nconst initialState = { items: [] };\n\nconst reducerFlashes = {\n [REDUCER_KEY]: (\n state: {\n items: FlashProps[];\n } = initialState,\n action: { type: string; payload: FlashProps | FlashProps[] },\n ) => {\n switch (action.type) {\n case \"flash/push\": {\n const flashes = Array.isArray(action.payload)\n ? action.payload\n : [action.payload];\n return { items: [...state.items, ...flashes] };\n }\n default:\n return state;\n }\n },\n};\n\n// Not cool but redux isn't a long term plan here\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst selectFlashes = (store: any): { items: FlashProps[] } =>\n store.getState()[REDUCER_KEY];\n\nconst FLASH_BG_COLOR = {\n error: \"bg-gui-error\",\n success: \"bg-zingy-green\",\n notice: \"bg-electric-cyan\",\n info: \"bg-electric-cyan\",\n alert: \"bg-active-orange\",\n};\n\nconst FLASH_TEXT_COLOR = {\n error: \"text-white\",\n success: \"text-cool-black\",\n notice: \"text-cool-black\",\n info: \"text-cool-black\",\n alert: \"text-white\",\n};\n\nconst AUTO_HIDE = [\"success\", \"info\", \"notice\"];\nconst AUTO_HIDE_TIME = 8000;\n\nconst useAutoHide = (type: string, closeFlash: () => void) => {\n const timeoutId = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (AUTO_HIDE.includes(type)) {\n timeoutId.current = setTimeout(() => {\n closeFlash();\n }, AUTO_HIDE_TIME);\n }\n\n return () => {\n if (timeoutId.current) {\n clearTimeout(timeoutId.current);\n }\n };\n }, []);\n};\n\nconst Flash = ({ id, type, content, removeFlash }: FlashProps) => {\n const ref = useRef<HTMLDivElement>(null);\n const [closed, setClosed] = useState(false);\n const [flashHeight, setFlashHeight] = useState(0);\n const [triggerEntryAnimation, setTriggerEntryAnimation] = useState(false);\n\n const closeFlash = () => {\n if (ref.current) {\n setFlashHeight(ref.current.getBoundingClientRect().height);\n }\n\n setClosed(true);\n\n setTimeout(() => {\n if (id) {\n removeFlash(id);\n }\n }, 100);\n };\n\n useEffect(() => setTriggerEntryAnimation(true), []);\n useAutoHide(type, closeFlash);\n\n const animateEntry = triggerEntryAnimation && !closed;\n\n let style;\n\n if (flashHeight && !closed) {\n style = { height: `${flashHeight}px` };\n } else if (closed) {\n style = { height: 0, marginTop: 0, zIndex: -1 };\n } else {\n style = {};\n }\n\n const safeContent = DOMPurify.sanitize(content, {\n ALLOWED_TAGS: [\"a\"],\n ALLOWED_ATTR: [\"href\", \"data-method\", \"rel\"],\n });\n\n const withIcons: Record<FlashPropsType, IconName | \"\"> = {\n notice: \"icon-gui-ably-badge\",\n success: \"icon-gui-check-outline\",\n error: \"icon-gui-exclamation-triangle-outline\",\n alert: \"icon-gui-exclamation-triangle-outline\",\n info: \"\",\n };\n\n const iconColor: Record<FlashPropsType, ColorClass | \"\"> = {\n notice: \"text-cool-black\",\n success: \"text-cool-black\",\n error: \"text-white\",\n alert: \"text-white\",\n info: \"\",\n };\n\n return (\n <div\n className={`ui-flash-message ui-grid-px ${\n animateEntry ? \"ui-flash-message-enter\" : \"\"\n }`}\n style={style}\n ref={ref}\n data-id=\"ui-flash\"\n >\n <div\n className={`${FLASH_BG_COLOR[type]} p-32 flex align-center rounded shadow-container-subtle`}\n >\n {withIcons[type] && iconColor[type] && (\n <Icon\n name={withIcons[type]}\n color={iconColor[type]}\n size=\"1.5rem\"\n additionalCSS=\"mr-16 self-baseline\"\n />\n )}\n <p\n className={`ui-text-p1 mr-16 ${FLASH_TEXT_COLOR[type]}`}\n dangerouslySetInnerHTML={{ __html: safeContent }}\n />\n <button\n type=\"button\"\n className=\"p-0 ml-auto self-start focus:outline-none\"\n onClick={closeFlash}\n >\n {iconColor[type] && (\n <Icon\n name=\"icon-gui-x-mark-outline\"\n color={iconColor[type]}\n size=\"1.5rem\"\n additionalCSS=\"transition-colors\"\n />\n )}\n </button>\n </div>\n </div>\n );\n};\n\nconst Flashes = ({ flashes }: FlashesProps) => {\n const [flashesWithIds, setFlashesWithIds] = useState<FlashProps[]>([]);\n\n const removeFlash = (flashId: string) =>\n setFlashesWithIds((items) => items.filter((item) => item.id !== flashId));\n\n useEffect(() => {\n setFlashesWithIds((state) => {\n return [\n ...state,\n ...(flashes?.items ?? []).map((flash) => ({\n ...flash,\n id: Math.random().toString(36).slice(2),\n removed: false,\n })),\n ];\n });\n }, [flashes]);\n\n return (\n <div className=\"ui-flash\" data-id={FLASH_DATA_ID}>\n {flashesWithIds\n .filter((item) => !item.removed)\n .map((flash) => (\n <Flash key={flash.id} {...flash} removeFlash={removeFlash} />\n ))}\n </div>\n );\n};\n\nconst BackendFlashes = ({ flashes }: BackendFlashesProps) => {\n useEffect(() => {\n const transformedFlashes =\n flashes.map((flash) => {\n const [type, content] = flash;\n return { type, content };\n }) || [];\n\n if (transformedFlashes.length > 0) {\n const store = getRemoteDataStore();\n\n store.dispatch({\n type: \"flash/push\",\n payload: transformedFlashes,\n });\n }\n }, []);\n\n const WrappedFlashes = ConnectStateWrapper(Flashes, {\n flashes: selectFlashes,\n });\n\n return <WrappedFlashes />;\n};\n\nexport { reducerFlashes, FLASH_DATA_ID, Flashes };\nexport default BackendFlashes;\n"],"names":["React","useEffect","useState","useRef","DOMPurify","getRemoteDataStore","ConnectStateWrapper","Icon","REDUCER_KEY","FLASH_DATA_ID","initialState","items","reducerFlashes","state","action","type","flashes","Array","isArray","payload","selectFlashes","store","getState","FLASH_BG_COLOR","error","success","notice","info","alert","FLASH_TEXT_COLOR","AUTO_HIDE","AUTO_HIDE_TIME","useAutoHide","closeFlash","timeoutId","includes","current","setTimeout","clearTimeout","Flash","id","content","removeFlash","ref","closed","setClosed","flashHeight","setFlashHeight","triggerEntryAnimation","setTriggerEntryAnimation","getBoundingClientRect","height","animateEntry","style","marginTop","zIndex","safeContent","sanitize","ALLOWED_TAGS","ALLOWED_ATTR","withIcons","iconColor","div","className","data-id","name","color","size","additionalCSS","p","dangerouslySetInnerHTML","__html","button","onClick","Flashes","flashesWithIds","setFlashesWithIds","flashId","filter","item","map","flash","Math","random","toString","slice","removed","key","BackendFlashes","transformedFlashes","length","dispatch","WrappedFlashes"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/Flash.tsx"],"sourcesContent":["import React, { useEffect, useState, useRef } from \"react\";\nimport DOMPurify from \"dompurify\";\nimport { getRemoteDataStore } from \"./remote-data-store.js\";\nimport ConnectStateWrapper from \"./ConnectStateWrapper\";\nimport Icon from \"./Icon\";\nimport { ColorClass } from \"./styles/colors/types\";\nimport { IconName } from \"./Icon/types\";\n\ntype FlashPropsType = \"error\" | \"success\" | \"notice\" | \"info\" | \"alert\";\n\ntype FlashProps = {\n id: string;\n removed: boolean;\n type: FlashPropsType;\n content: string;\n removeFlash: (id: string) => void;\n};\n\ntype FlashesProps = {\n flashes: { items: Pick<FlashProps, \"type\" | \"content\">[] };\n};\n\ntype BackendFlashesProps = {\n flashes: string[][];\n};\n\nconst REDUCER_KEY = \"flashes\";\nconst FLASH_DATA_ID = \"ui-flashes\";\n\nconst initialState = { items: [] };\n\nconst reducerFlashes = {\n [REDUCER_KEY]: (\n state: {\n items: FlashProps[];\n } = initialState,\n action: { type: string; payload: FlashProps | FlashProps[] },\n ) => {\n switch (action.type) {\n case \"flash/push\": {\n const flashes = Array.isArray(action.payload)\n ? action.payload\n : [action.payload];\n return { items: [...state.items, ...flashes] };\n }\n default:\n return state;\n }\n },\n};\n\n// Not cool but redux isn't a long term plan here\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst selectFlashes = (store: any): { items: FlashProps[] } =>\n store.getState()[REDUCER_KEY];\n\nconst FLASH_BG_COLOR = {\n error: \"bg-gui-error\",\n success: \"bg-zingy-green\",\n notice: \"bg-electric-cyan\",\n info: \"bg-electric-cyan\",\n alert: \"bg-active-orange\",\n};\n\nconst FLASH_TEXT_COLOR = {\n error: \"text-white\",\n success: \"text-cool-black\",\n notice: \"text-cool-black\",\n info: \"text-cool-black\",\n alert: \"text-white\",\n};\n\nconst AUTO_HIDE = [\"success\", \"info\", \"notice\"];\nconst AUTO_HIDE_TIME = 8000;\n\nconst useAutoHide = (type: string, closeFlash: () => void) => {\n const timeoutId = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (AUTO_HIDE.includes(type)) {\n timeoutId.current = setTimeout(() => {\n closeFlash();\n }, AUTO_HIDE_TIME);\n }\n\n return () => {\n if (timeoutId.current) {\n clearTimeout(timeoutId.current);\n }\n };\n }, []);\n};\n\nconst Flash = ({ id, type, content, removeFlash }: FlashProps) => {\n const ref = useRef<HTMLDivElement>(null);\n const [closed, setClosed] = useState(false);\n const [flashHeight, setFlashHeight] = useState(0);\n const [triggerEntryAnimation, setTriggerEntryAnimation] = useState(false);\n\n const closeFlash = () => {\n if (ref.current) {\n setFlashHeight(ref.current.getBoundingClientRect().height);\n }\n\n setClosed(true);\n\n setTimeout(() => {\n if (id) {\n removeFlash(id);\n }\n }, 100);\n };\n\n useEffect(() => setTriggerEntryAnimation(true), []);\n useAutoHide(type, closeFlash);\n\n const animateEntry = triggerEntryAnimation && !closed;\n\n let style;\n\n if (flashHeight && !closed) {\n style = { height: `${flashHeight}px` };\n } else if (closed) {\n style = { height: 0, marginTop: 0, zIndex: -1 };\n } else {\n style = {};\n }\n\n const safeContent = DOMPurify.sanitize(content, {\n ALLOWED_TAGS: [\"a\"],\n ALLOWED_ATTR: [\"href\", \"data-method\", \"rel\"],\n });\n\n const withIcons: Record<FlashPropsType, IconName | \"\"> = {\n notice: \"icon-gui-ably-badge\",\n success: \"icon-gui-check-outline\",\n error: \"icon-gui-exclamation-triangle-outline\",\n alert: \"icon-gui-exclamation-triangle-outline\",\n info: \"\",\n };\n\n const iconColor: Record<FlashPropsType, ColorClass | \"\"> = {\n notice: \"text-cool-black\",\n success: \"text-cool-black\",\n error: \"text-white\",\n alert: \"text-white\",\n info: \"\",\n };\n\n return (\n <div\n className={`ui-flash-message ui-grid-px ${\n animateEntry ? \"ui-flash-message-enter\" : \"\"\n }`}\n style={style}\n ref={ref}\n data-id=\"ui-flash\"\n >\n <div\n className={`${FLASH_BG_COLOR[type]} p-32 flex align-center rounded shadow-container-subtle`}\n >\n {withIcons[type] && iconColor[type] && (\n <Icon\n name={withIcons[type]}\n color={iconColor[type]}\n size=\"1.5rem\"\n additionalCSS=\"mr-16 self-baseline\"\n />\n )}\n <p\n className={`ui-text-p1 mr-16 ${FLASH_TEXT_COLOR[type]}`}\n dangerouslySetInnerHTML={{ __html: safeContent }}\n />\n <button\n type=\"button\"\n className=\"p-0 ml-auto self-start focus:outline-none\"\n onClick={closeFlash}\n >\n {iconColor[type] && (\n <Icon\n name=\"icon-gui-x-mark-outline\"\n color={iconColor[type]}\n size=\"1.5rem\"\n additionalCSS=\"transition-colors\"\n />\n )}\n </button>\n </div>\n </div>\n );\n};\n\nconst Flashes = ({ flashes }: FlashesProps) => {\n const [flashesWithIds, setFlashesWithIds] = useState<FlashProps[]>([]);\n\n const removeFlash = (flashId: string) =>\n setFlashesWithIds((items) => items.filter((item) => item.id !== flashId));\n\n useEffect(() => {\n setFlashesWithIds((state) => {\n return [\n ...state,\n ...(flashes?.items ?? []).map((flash) => ({\n ...flash,\n id: Math.random().toString(36).slice(2),\n removed: false,\n removeFlash,\n })),\n ];\n });\n }, [flashes]);\n\n return (\n <div className=\"ui-flash\" data-id={FLASH_DATA_ID}>\n {flashesWithIds\n .filter((item) => !item.removed)\n .map((flash) => (\n <Flash key={flash.id} {...flash} />\n ))}\n </div>\n );\n};\n\nconst BackendFlashes = ({ flashes }: BackendFlashesProps) => {\n useEffect(() => {\n const transformedFlashes =\n flashes.map((flash) => {\n const [type, content] = flash;\n return { type, content };\n }) || [];\n\n if (transformedFlashes.length > 0) {\n const store = getRemoteDataStore();\n\n store.dispatch({\n type: \"flash/push\",\n payload: transformedFlashes,\n });\n }\n }, []);\n\n const WrappedFlashes = ConnectStateWrapper(Flashes, {\n flashes: selectFlashes,\n });\n\n return <WrappedFlashes />;\n};\n\nexport { reducerFlashes, FLASH_DATA_ID, Flashes };\nexport default BackendFlashes;\n"],"names":["React","useEffect","useState","useRef","DOMPurify","getRemoteDataStore","ConnectStateWrapper","Icon","REDUCER_KEY","FLASH_DATA_ID","initialState","items","reducerFlashes","state","action","type","flashes","Array","isArray","payload","selectFlashes","store","getState","FLASH_BG_COLOR","error","success","notice","info","alert","FLASH_TEXT_COLOR","AUTO_HIDE","AUTO_HIDE_TIME","useAutoHide","closeFlash","timeoutId","includes","current","setTimeout","clearTimeout","Flash","id","content","removeFlash","ref","closed","setClosed","flashHeight","setFlashHeight","triggerEntryAnimation","setTriggerEntryAnimation","getBoundingClientRect","height","animateEntry","style","marginTop","zIndex","safeContent","sanitize","ALLOWED_TAGS","ALLOWED_ATTR","withIcons","iconColor","div","className","data-id","name","color","size","additionalCSS","p","dangerouslySetInnerHTML","__html","button","onClick","Flashes","flashesWithIds","setFlashesWithIds","flashId","filter","item","map","flash","Math","random","toString","slice","removed","key","BackendFlashes","transformedFlashes","length","dispatch","WrappedFlashes"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/Meganav.tsx"],"sourcesContent":["import React, { ReactNode, useEffect, useState } from \"react\";\n\nimport { connectState } from \"./remote-data-store.js\";\nimport { selectSessionData } from \"./remote-session-data.js\";\n\nimport Logo from \"./Logo\";\nimport MeganavData from \"./Meganav/component.json\";\nimport MeganavScripts from \"./Meganav/component.js\";\nimport MeganavItemsDesktop from \"./MeganavItemsDesktop\";\nimport MeganavItemsSignedIn from \"./MeganavItemsSignedIn\";\nimport MeganavItemsMobile from \"./MeganavItemsMobile\";\nimport Notice from \"./Notice\";\nimport _absUrl from \"./url-base.js\";\nimport MeganavContentProducts from \"./MeganavContentProducts\";\nimport MeganavContentUseCases from \"./MeganavContentUseCases\";\nimport MeganavContentCompany from \"./MeganavContentCompany\";\nimport MeganavContentDevelopers from \"./MeganavContentDevelopers\";\nimport MeganavSearch from \"./MeganavSearch\";\nimport { ColorClass } from \"./styles/colors/types\";\n\nexport type MeganavTheme = {\n backgroundColor?: ColorClass;\n textColor?: ColorClass;\n buttonBackgroundColor?: ColorClass;\n buttonTextColor?: ColorClass;\n mobileMenuColor: ColorClass;\n logoTextColor?: ColorClass;\n barShadow?: string;\n};\n\nexport type AbsUrl = (path: string) => string;\n\nexport type MeganavPaths = {\n logo?: string;\n iconSprites: string;\n ablyStack: string;\n blogThumb1: string;\n blogThumb2: string;\n blogThumb3: string;\n awsLogo?: string;\n};\n\nexport type MeganavPanels = {\n [index: string]: ({\n paths,\n absUrl,\n statusUrl,\n }: {\n paths?: MeganavPaths;\n absUrl: (path: string) => string;\n statusUrl: string;\n }) => ReactNode;\n};\n\nexport type MeganavSessionState = {\n signedIn: boolean;\n logOut: {\n token: string;\n href: string;\n text: string;\n };\n accountName: string;\n preferredEmail: string;\n account: {\n links: {\n dashboard: {\n href: string;\n };\n };\n };\n mySettings: {\n text: string;\n href: string;\n };\n myAccessTokens: {\n text: string;\n href: string;\n };\n};\n\ntype SignInProps = {\n sessionState: MeganavSessionState;\n theme: MeganavTheme;\n loginLink: string;\n absUrl: AbsUrl;\n searchDataId?: string;\n};\n\n// This type is based on the API response from the notice API and the data\n// passed into the Meganav component, which then turns it into something\n// the Notice component can use. The type is exported for the benefit of\n// Voltaire\nexport type MeganavNoticeProps = {\n props: {\n title: string;\n bodyText: string;\n buttonLink: string;\n buttonLabel: string;\n closeBtn: boolean;\n };\n config: {\n cookieId: string;\n noticeId: string | number;\n options: {\n collapse: boolean;\n };\n };\n};\n\ntype MeganavProps = {\n paths?: MeganavPaths;\n themeName: \"white\" | \"black\" | \"transparentToWhite\";\n notice?: MeganavNoticeProps;\n loginLink?: string;\n urlBase?: string;\n addSearchApiKey: string;\n statusUrl: string;\n searchDataId?: string;\n};\n\nconst SignIn = ({\n sessionState,\n theme,\n loginLink,\n absUrl,\n searchDataId,\n}: SignInProps) => {\n return sessionState.signedIn ? (\n <MeganavItemsSignedIn\n absUrl={absUrl}\n sessionState={sessionState}\n theme={theme}\n searchDataId={searchDataId}\n />\n ) : (\n <ul className=\"hidden md:flex items-center\">\n <li className=\"ui-meganav-item\">\n <a\n href={absUrl(\"/contact\")}\n className={`ui-meganav-link ${theme.textColor}`}\n data-id=\"meganav-link\"\n >\n Contact us\n </a>\n </li>\n <li className=\"ui-meganav-item\">\n <a\n href={absUrl(loginLink)}\n className={`ui-meganav-link mr-0 ${theme.textColor}`}\n data-id=\"meganav-link\"\n >\n Login\n </a>\n </li>\n <li className=\"ui-meganav-item\">\n <MeganavSearch absUrl={absUrl} dataId={searchDataId} />\n </li>\n <li className=\"ui-meganav-item\">\n <a\n href={absUrl(\"/sign-up\")}\n data-id=\"meganav-sign-up-btn\"\n className={`ui-btn p-btn-small ${theme.buttonBackgroundColor} ${theme.buttonTextColor}`}\n >\n Sign up free\n </a>\n </li>\n </ul>\n );\n};\n\nconst SignInPlaceholder = () => <div />;\n\nconst panels = {\n MeganavContentProducts,\n MeganavContentUseCases,\n MeganavContentCompany,\n MeganavContentDevelopers,\n};\n\nconst Meganav = ({\n paths,\n themeName = \"white\",\n notice,\n loginLink = \"/login\",\n urlBase,\n addSearchApiKey,\n statusUrl,\n searchDataId,\n}: MeganavProps) => {\n const [sessionState, setSessionState] = useState<MeganavSessionState>();\n\n useEffect(() => {\n // Note if state is never updated, sessionState stays null and never removes the placeholder.\n // This makes SSR consistent (ie. we always show the placeholder)\n connectState(selectSessionData, setSessionState);\n }, []);\n\n useEffect(() => {\n const teardown = MeganavScripts({ themeName, addSearchApiKey });\n return () => teardown();\n }, [sessionState]);\n\n const theme = MeganavData.themes[themeName] as MeganavTheme;\n const absUrl = (path: string) => _absUrl(path, urlBase);\n\n return (\n <nav\n className={`ui-meganav-wrapper ${theme.backgroundColor} ${theme.barShadow}`}\n data-id=\"meganav\"\n aria-label=\"Main\"\n >\n {notice && <Notice {...notice.props} config={notice.config} />}\n <div className=\"ui-meganav ui-grid-px\">\n <div className=\"mr-24\">\n <Logo dataId=\"meganav-logo\" href={urlBase} logoUrl={paths?.logo} />\n </div>\n\n <MeganavItemsDesktop\n panels={panels}\n paths={paths}\n theme={theme}\n absUrl={absUrl}\n statusUrl={statusUrl}\n />\n\n {/* Because we load the session state through fetch, we display a placeholder until fetch returns */}\n {sessionState ? (\n <SignIn\n sessionState={sessionState}\n theme={theme}\n loginLink={loginLink}\n absUrl={absUrl}\n searchDataId={searchDataId}\n />\n ) : (\n <SignInPlaceholder />\n )}\n\n <MeganavItemsMobile\n panels={panels}\n sessionState={sessionState}\n paths={paths}\n theme={theme}\n loginLink={loginLink}\n absUrl={absUrl}\n statusUrl={statusUrl}\n searchDataId={searchDataId}\n />\n </div>\n </nav>\n );\n};\n\nexport default Meganav;\n"],"names":["React","useEffect","useState","connectState","selectSessionData","Logo","MeganavData","MeganavScripts","MeganavItemsDesktop","MeganavItemsSignedIn","MeganavItemsMobile","Notice","_absUrl","MeganavContentProducts","MeganavContentUseCases","MeganavContentCompany","MeganavContentDevelopers","MeganavSearch","SignIn","sessionState","theme","loginLink","absUrl","searchDataId","signedIn","ul","className","li","a","href","textColor","data-id","dataId","buttonBackgroundColor","buttonTextColor","SignInPlaceholder","div","panels","Meganav","paths","themeName","notice","urlBase","addSearchApiKey","statusUrl","setSessionState","teardown","themes","path","nav","backgroundColor","barShadow","aria-label","props","config","logoUrl","logo"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/Meganav.tsx"],"sourcesContent":["import React, { ReactNode, useEffect, useState } from \"react\";\n\nimport { connectState } from \"./remote-data-store.js\";\nimport { selectSessionData } from \"./remote-session-data.js\";\n\nimport Logo from \"./Logo\";\nimport MeganavData from \"./Meganav/component.json\";\nimport MeganavScripts from \"./Meganav/component.js\";\nimport MeganavItemsDesktop from \"./MeganavItemsDesktop\";\nimport MeganavItemsSignedIn from \"./MeganavItemsSignedIn\";\nimport MeganavItemsMobile from \"./MeganavItemsMobile\";\nimport Notice from \"./Notice\";\nimport _absUrl from \"./url-base.js\";\nimport MeganavContentProducts from \"./MeganavContentProducts\";\nimport MeganavContentUseCases from \"./MeganavContentUseCases\";\nimport MeganavContentCompany from \"./MeganavContentCompany\";\nimport MeganavContentDevelopers from \"./MeganavContentDevelopers\";\nimport MeganavSearch from \"./MeganavSearch\";\nimport { ColorClass } from \"./styles/colors/types\";\n\nexport type MeganavTheme = {\n backgroundColor?: ColorClass;\n textColor?: ColorClass;\n buttonBackgroundColor?: ColorClass;\n buttonTextColor?: ColorClass;\n mobileMenuColor: ColorClass;\n logoTextColor?: ColorClass;\n barShadow?: string;\n};\n\nexport type AbsUrl = (path: string) => string;\n\nexport type MeganavPaths = {\n logo?: string;\n iconSprites: string;\n ablyStack: string;\n awsLogo?: string;\n};\n\nexport type MeganavPanels = {\n [index: string]: ({\n paths,\n absUrl,\n statusUrl,\n }: {\n paths?: MeganavPaths;\n absUrl: (path: string) => string;\n statusUrl: string;\n }) => ReactNode;\n};\n\nexport type MeganavSessionState = {\n signedIn: boolean;\n logOut: {\n token: string;\n href: string;\n text: string;\n };\n accountName: string;\n preferredEmail: string;\n account: {\n links: {\n dashboard: {\n href: string;\n };\n };\n };\n mySettings: {\n text: string;\n href: string;\n };\n myAccessTokens: {\n text: string;\n href: string;\n };\n};\n\ntype SignInProps = {\n sessionState: MeganavSessionState;\n theme: MeganavTheme;\n loginLink: string;\n absUrl: AbsUrl;\n searchDataId?: string;\n};\n\n// This type is based on the API response from the notice API and the data\n// passed into the Meganav component, which then turns it into something\n// the Notice component can use. The type is exported for the benefit of\n// Voltaire\nexport type NoticeApiProps = {\n props: {\n title: string;\n bodyText: string;\n buttonLink: string;\n buttonLabel: string;\n closeBtn: boolean;\n };\n config: {\n cookieId: string;\n noticeId: string | number;\n options: {\n collapse: boolean;\n };\n };\n};\n\ntype MeganavProps = {\n paths?: MeganavPaths;\n themeName: \"white\" | \"black\" | \"transparentToWhite\";\n notice?: NoticeApiProps;\n loginLink?: string;\n urlBase?: string;\n addSearchApiKey: string;\n statusUrl: string;\n searchDataId?: string;\n};\n\nconst SignIn = ({\n sessionState,\n theme,\n loginLink,\n absUrl,\n searchDataId,\n}: SignInProps) => {\n return sessionState.signedIn ? (\n <MeganavItemsSignedIn\n absUrl={absUrl}\n sessionState={sessionState}\n theme={theme}\n searchDataId={searchDataId}\n />\n ) : (\n <ul className=\"hidden md:flex items-center\">\n <li className=\"ui-meganav-item\">\n <a\n href={absUrl(\"/contact\")}\n className={`ui-meganav-link ${theme.textColor}`}\n data-id=\"meganav-link\"\n >\n Contact us\n </a>\n </li>\n <li className=\"ui-meganav-item\">\n <a\n href={absUrl(loginLink)}\n className={`ui-meganav-link mr-0 ${theme.textColor}`}\n data-id=\"meganav-link\"\n >\n Login\n </a>\n </li>\n <li className=\"ui-meganav-item\">\n <MeganavSearch absUrl={absUrl} dataId={searchDataId} />\n </li>\n <li className=\"ui-meganav-item\">\n <a\n href={absUrl(\"/sign-up\")}\n data-id=\"meganav-sign-up-btn\"\n className={`ui-btn p-btn-small ${theme.buttonBackgroundColor} ${theme.buttonTextColor}`}\n >\n Sign up free\n </a>\n </li>\n </ul>\n );\n};\n\nconst SignInPlaceholder = () => <div />;\n\nconst panels = {\n MeganavContentProducts,\n MeganavContentUseCases,\n MeganavContentCompany,\n MeganavContentDevelopers,\n};\n\nconst Meganav = ({\n paths,\n themeName = \"white\",\n notice,\n loginLink = \"/login\",\n urlBase,\n addSearchApiKey,\n statusUrl,\n searchDataId,\n}: MeganavProps) => {\n const [sessionState, setSessionState] = useState<MeganavSessionState>();\n\n useEffect(() => {\n // Note if state is never updated, sessionState stays null and never removes the placeholder.\n // This makes SSR consistent (ie. we always show the placeholder)\n connectState(selectSessionData, setSessionState);\n }, []);\n\n useEffect(() => {\n const teardown = MeganavScripts({ themeName, addSearchApiKey });\n return () => teardown();\n }, [sessionState]);\n\n const theme = MeganavData.themes[themeName] as MeganavTheme;\n const absUrl = (path: string) => _absUrl(path, urlBase);\n\n return (\n <nav\n className={`ui-meganav-wrapper ${theme.backgroundColor} ${theme.barShadow}`}\n data-id=\"meganav\"\n aria-label=\"Main\"\n >\n {notice && <Notice {...notice.props} config={notice.config} />}\n <div className=\"ui-meganav ui-grid-px\">\n <div className=\"mr-24\">\n <Logo dataId=\"meganav-logo\" href={urlBase} logoUrl={paths?.logo} />\n </div>\n\n <MeganavItemsDesktop\n panels={panels}\n paths={paths}\n theme={theme}\n absUrl={absUrl}\n statusUrl={statusUrl}\n />\n\n {/* Because we load the session state through fetch, we display a placeholder until fetch returns */}\n {sessionState ? (\n <SignIn\n sessionState={sessionState}\n theme={theme}\n loginLink={loginLink}\n absUrl={absUrl}\n searchDataId={searchDataId}\n />\n ) : (\n <SignInPlaceholder />\n )}\n\n <MeganavItemsMobile\n panels={panels}\n sessionState={sessionState}\n paths={paths}\n theme={theme}\n loginLink={loginLink}\n absUrl={absUrl}\n statusUrl={statusUrl}\n searchDataId={searchDataId}\n />\n </div>\n </nav>\n );\n};\n\nexport default Meganav;\n"],"names":["React","useEffect","useState","connectState","selectSessionData","Logo","MeganavData","MeganavScripts","MeganavItemsDesktop","MeganavItemsSignedIn","MeganavItemsMobile","Notice","_absUrl","MeganavContentProducts","MeganavContentUseCases","MeganavContentCompany","MeganavContentDevelopers","MeganavSearch","SignIn","sessionState","theme","loginLink","absUrl","searchDataId","signedIn","ul","className","li","a","href","textColor","data-id","dataId","buttonBackgroundColor","buttonTextColor","SignInPlaceholder","div","panels","Meganav","paths","themeName","notice","urlBase","addSearchApiKey","statusUrl","setSessionState","teardown","themes","path","nav","backgroundColor","barShadow","aria-label","props","config","logoUrl","logo"],"mappings":"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"}
package/core/Notice.js CHANGED
@@ -1,2 +1,2 @@
1
- import React,{useEffect}from"react";import Icon from"./Icon";import cn from"./utils/cn.js";import NoticeScripts from"./Notice/component.js";const defaultTextColor="text-neutral-1300";const contentWrapperClasses="w-full pr-8 ui-text-p4 self-center";const ContentWrapper=({buttonLink,textColor=defaultTextColor,children})=>buttonLink?React.createElement("a",{href:buttonLink,className:cn(contentWrapperClasses,textColor)},children):React.createElement("div",{className:cn(contentWrapperClasses,textColor)},children);const Notice=({buttonLink,buttonLabel,bodyText,title,config,closeBtn,bgColor="bg-orange-100",textColor=defaultTextColor})=>{useEffect(()=>{NoticeScripts({bannerContainer:document.querySelector('[data-id="ui-notice"]'),cookieId:config?.cookieId,noticeId:config?.noticeId,options:{collapse:config?.options?.collapse||false}})},[]);return React.createElement("div",{className:cn("ui-announcement",bgColor,textColor),"data-id":"ui-notice",style:{maxHeight:0,overflow:"hidden"}},React.createElement("div",{className:"ui-grid-px py-16 max-w-screen-xl mx-auto flex items-start"},React.createElement(ContentWrapper,{buttonLink:buttonLink??"#"},React.createElement("strong",{className:"font-bold whitespace-nowrap pr-4"},title),React.createElement("span",{className:"pr-4"},bodyText),buttonLabel&&React.createElement("span",{className:"cursor-pointer whitespace-nowrap text-gui-blue-default-light"},buttonLabel)),closeBtn&&React.createElement("button",{type:"button",className:"ml-auto h-20 w-20 border-none bg-none self-baseline"},React.createElement(Icon,{name:"icon-gui-x-mark-outline",size:"1.25rem",color:textColor}))))};export default Notice;
1
+ import React,{useEffect}from"react";import DOMPurify from"dompurify";import Icon from"./Icon";import cn from"./utils/cn.js";import NoticeScripts from"./Notice/component.js";import useRailsUjsLinks from"./hooks/use-rails-ujs-hooks";const defaultTextColor="text-neutral-1300";const contentWrapperClasses="w-full pr-8 ui-text-p4 self-center";const ContentWrapper=({buttonLink,textColor=defaultTextColor,children})=>buttonLink?React.createElement("a",{href:buttonLink,className:cn(contentWrapperClasses,textColor)},children):React.createElement("div",{className:cn(contentWrapperClasses,textColor)},children);const Notice=({buttonLink,buttonLabel,bodyText="",title,config,closeBtn,bgColor="bg-orange-100",textColor=defaultTextColor})=>{useEffect(()=>{NoticeScripts({bannerContainer:document.querySelector('[data-id="ui-notice"]'),cookieId:config?.cookieId,noticeId:config?.noticeId,options:{collapse:config?.options?.collapse||false}})},[]);const safeContent=DOMPurify.sanitize(bodyText,{ALLOWED_TAGS:["a"],ALLOWED_ATTR:["href","data-method","rel"]});const contentRef=useRailsUjsLinks();return React.createElement("div",{className:cn("ui-announcement",bgColor,textColor),"data-id":"ui-notice",style:{maxHeight:0,overflow:"hidden"}},React.createElement("div",{className:"ui-grid-px py-16 max-w-screen-xl mx-auto flex items-start"},React.createElement(ContentWrapper,{buttonLink:buttonLink??"#"},React.createElement("strong",{className:"font-bold whitespace-nowrap pr-4"},title),React.createElement("span",{ref:contentRef,className:"pr-4",dangerouslySetInnerHTML:{__html:safeContent}}),buttonLabel&&React.createElement("span",{className:"cursor-pointer whitespace-nowrap text-gui-blue-default-light"},buttonLabel)),closeBtn&&React.createElement("button",{type:"button",className:"ml-auto h-20 w-20 border-none bg-none self-baseline"},React.createElement(Icon,{name:"icon-gui-x-mark-outline",size:"1.25rem",color:textColor}))))};export default Notice;
2
2
  //# sourceMappingURL=Notice.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/Notice.tsx"],"sourcesContent":["import React, { ReactNode, useEffect } from \"react\";\n\nimport { ColorClass, ColorThemeSet } from \"./styles/colors/types\";\nimport Icon from \"./Icon\";\nimport cn from \"./utils/cn.js\";\nimport NoticeScripts from \"./Notice/component.js\";\n\ntype ContentWrapperProps = {\n buttonLink: string;\n children: ReactNode;\n textColor?: ColorClass | ColorThemeSet;\n};\n\n// TODO(jamiehenson):\n// This type is a bit messed up currently due to the NoticeScripts import being interpreted as NoticeProps.\n// Plan is to TS-ify the JS assets too, so this can be rectified then. The NoticeScripts-oriented props are\n// the ones after the line break.\nexport type NoticeProps = {\n buttonLink?: string;\n buttonLabel?: string;\n bodyText?: string;\n title?: string;\n closeBtn?: boolean;\n config?: {\n options: {\n collapse: boolean;\n };\n noticeId: string | number;\n cookieId: string;\n };\n bgColor?: string;\n textColor?: ColorClass | ColorThemeSet;\n\n bannerContainer?: Element | null;\n cookieId?: string;\n noticeId?: string;\n options?: { collapse: boolean };\n};\n\nconst defaultTextColor = \"text-neutral-1300\";\n\nconst contentWrapperClasses = \"w-full pr-8 ui-text-p4 self-center\";\n\nconst ContentWrapper = ({\n buttonLink,\n textColor = defaultTextColor,\n children,\n}: ContentWrapperProps) =>\n buttonLink ? (\n <a href={buttonLink} className={cn(contentWrapperClasses, textColor)}>\n {children}\n </a>\n ) : (\n <div className={cn(contentWrapperClasses, textColor)}>{children}</div>\n );\n\nconst Notice = ({\n buttonLink,\n buttonLabel,\n bodyText,\n title,\n config,\n closeBtn,\n bgColor = \"bg-orange-100\",\n textColor = defaultTextColor,\n}: NoticeProps) => {\n useEffect(() => {\n NoticeScripts({\n bannerContainer: document.querySelector('[data-id=\"ui-notice\"]'),\n cookieId: config?.cookieId,\n noticeId: config?.noticeId,\n options: {\n collapse: config?.options?.collapse || false,\n },\n });\n }, []);\n\n return (\n <div\n className={cn(\"ui-announcement\", bgColor, textColor)}\n data-id=\"ui-notice\"\n style={{ maxHeight: 0, overflow: \"hidden\" }}\n >\n <div className=\"ui-grid-px py-16 max-w-screen-xl mx-auto flex items-start\">\n <ContentWrapper buttonLink={buttonLink ?? \"#\"}>\n <strong className=\"font-bold whitespace-nowrap pr-4\">{title}</strong>\n <span className=\"pr-4\">{bodyText}</span>\n {buttonLabel && (\n <span className=\"cursor-pointer whitespace-nowrap text-gui-blue-default-light\">\n {buttonLabel}\n </span>\n )}\n </ContentWrapper>\n\n {closeBtn && (\n <button\n type=\"button\"\n className=\"ml-auto h-20 w-20 border-none bg-none self-baseline\"\n >\n <Icon\n name=\"icon-gui-x-mark-outline\"\n size=\"1.25rem\"\n color={textColor}\n />\n </button>\n )}\n </div>\n </div>\n );\n};\n\nexport default Notice;\n"],"names":["React","useEffect","Icon","cn","NoticeScripts","defaultTextColor","contentWrapperClasses","ContentWrapper","buttonLink","textColor","children","a","href","className","div","Notice","buttonLabel","bodyText","title","config","closeBtn","bgColor","bannerContainer","document","querySelector","cookieId","noticeId","options","collapse","data-id","style","maxHeight","overflow","strong","span","button","type","name","size","color"],"mappings":"AAAA,OAAOA,OAAoBC,SAAS,KAAQ,OAAQ,AAGpD,QAAOC,SAAU,QAAS,AAC1B,QAAOC,OAAQ,eAAgB,AAC/B,QAAOC,kBAAmB,uBAAwB,CAkClD,MAAMC,iBAAmB,oBAEzB,MAAMC,sBAAwB,qCAE9B,MAAMC,eAAiB,CAAC,CACtBC,UAAU,CACVC,UAAYJ,gBAAgB,CAC5BK,QAAQ,CACY,GACpBF,WACE,oBAACG,KAAEC,KAAMJ,WAAYK,UAAWV,GAAGG,sBAAuBG,YACvDC,UAGH,oBAACI,OAAID,UAAWV,GAAGG,sBAAuBG,YAAaC,UAG3D,MAAMK,OAAS,CAAC,CACdP,UAAU,CACVQ,WAAW,CACXC,QAAQ,CACRC,KAAK,CACLC,MAAM,CACNC,QAAQ,CACRC,QAAU,eAAe,CACzBZ,UAAYJ,gBAAgB,CAChB,IACZJ,UAAU,KACRG,cAAc,CACZkB,gBAAiBC,SAASC,aAAa,CAAC,yBACxCC,SAAUN,QAAQM,SAClBC,SAAUP,QAAQO,SAClBC,QAAS,CACPC,SAAUT,QAAQQ,SAASC,UAAY,KACzC,CACF,EACF,EAAG,EAAE,EAEL,OACE,oBAACd,OACCD,UAAWV,GAAG,kBAAmBkB,QAASZ,WAC1CoB,UAAQ,YACRC,MAAO,CAAEC,UAAW,EAAGC,SAAU,QAAS,GAE1C,oBAAClB,OAAID,UAAU,6DACb,oBAACN,gBAAeC,WAAYA,YAAc,KACxC,oBAACyB,UAAOpB,UAAU,oCAAoCK,OACtD,oBAACgB,QAAKrB,UAAU,QAAQI,UACvBD,aACC,oBAACkB,QAAKrB,UAAU,gEACbG,cAKNI,UACC,oBAACe,UACCC,KAAK,SACLvB,UAAU,uDAEV,oBAACX,MACCmC,KAAK,0BACLC,KAAK,UACLC,MAAO9B,cAOrB,CAEA,gBAAeM,MAAO"}
1
+ {"version":3,"sources":["../../src/core/Notice.tsx"],"sourcesContent":["import React, { ReactNode, useEffect } from \"react\";\nimport DOMPurify from \"dompurify\";\n\nimport { ColorClass, ColorThemeSet } from \"./styles/colors/types\";\nimport Icon from \"./Icon\";\nimport cn from \"./utils/cn.js\";\nimport NoticeScripts from \"./Notice/component.js\";\n\ntype ContentWrapperProps = {\n buttonLink: string;\n children: ReactNode;\n textColor?: ColorClass | ColorThemeSet;\n};\nimport useRailsUjsLinks from \"./hooks/use-rails-ujs-hooks\";\n\n// TODO(jamiehenson):\n// This type is a bit messed up currently due to the NoticeScripts import being interpreted as NoticeProps.\n// Plan is to TS-ify the JS assets too, so this can be rectified then. The NoticeScripts-oriented props are\n// the ones after the line break.\nexport type NoticeProps = {\n buttonLink?: string;\n buttonLabel?: string;\n bodyText?: string;\n title?: string;\n closeBtn?: boolean;\n config?: {\n options: {\n collapse: boolean;\n };\n noticeId: string | number;\n cookieId: string;\n };\n bgColor?: string;\n textColor?: ColorClass | ColorThemeSet;\n\n bannerContainer?: Element | null;\n cookieId?: string;\n noticeId?: string;\n options?: { collapse: boolean };\n};\n\nconst defaultTextColor = \"text-neutral-1300\";\n\nconst contentWrapperClasses = \"w-full pr-8 ui-text-p4 self-center\";\n\nconst ContentWrapper = ({\n buttonLink,\n textColor = defaultTextColor,\n children,\n}: ContentWrapperProps) =>\n buttonLink ? (\n <a href={buttonLink} className={cn(contentWrapperClasses, textColor)}>\n {children}\n </a>\n ) : (\n <div className={cn(contentWrapperClasses, textColor)}>{children}</div>\n );\n\nconst Notice = ({\n buttonLink,\n buttonLabel,\n bodyText = \"\",\n title,\n config,\n closeBtn,\n bgColor = \"bg-orange-100\",\n textColor = defaultTextColor,\n}: NoticeProps) => {\n useEffect(() => {\n NoticeScripts({\n bannerContainer: document.querySelector('[data-id=\"ui-notice\"]'),\n cookieId: config?.cookieId,\n noticeId: config?.noticeId,\n options: {\n collapse: config?.options?.collapse || false,\n },\n });\n }, []);\n\n const safeContent = DOMPurify.sanitize(bodyText, {\n ALLOWED_TAGS: [\"a\"],\n ALLOWED_ATTR: [\"href\", \"data-method\", \"rel\"],\n });\n\n const contentRef = useRailsUjsLinks();\n\n return (\n <div\n className={cn(\"ui-announcement\", bgColor, textColor)}\n data-id=\"ui-notice\"\n style={{ maxHeight: 0, overflow: \"hidden\" }}\n >\n <div className=\"ui-grid-px py-16 max-w-screen-xl mx-auto flex items-start\">\n <ContentWrapper buttonLink={buttonLink ?? \"#\"}>\n <strong className=\"font-bold whitespace-nowrap pr-4\">{title}</strong>\n <span\n ref={contentRef}\n className=\"pr-4\"\n dangerouslySetInnerHTML={{\n __html: safeContent,\n }}\n ></span>\n {buttonLabel && (\n <span className=\"cursor-pointer whitespace-nowrap text-gui-blue-default-light\">\n {buttonLabel}\n </span>\n )}\n </ContentWrapper>\n\n {closeBtn && (\n <button\n type=\"button\"\n className=\"ml-auto h-20 w-20 border-none bg-none self-baseline\"\n >\n <Icon\n name=\"icon-gui-x-mark-outline\"\n size=\"1.25rem\"\n color={textColor}\n />\n </button>\n )}\n </div>\n </div>\n );\n};\n\nexport default Notice;\n"],"names":["React","useEffect","DOMPurify","Icon","cn","NoticeScripts","useRailsUjsLinks","defaultTextColor","contentWrapperClasses","ContentWrapper","buttonLink","textColor","children","a","href","className","div","Notice","buttonLabel","bodyText","title","config","closeBtn","bgColor","bannerContainer","document","querySelector","cookieId","noticeId","options","collapse","safeContent","sanitize","ALLOWED_TAGS","ALLOWED_ATTR","contentRef","data-id","style","maxHeight","overflow","strong","span","ref","dangerouslySetInnerHTML","__html","button","type","name","size","color"],"mappings":"AAAA,OAAOA,OAAoBC,SAAS,KAAQ,OAAQ,AACpD,QAAOC,cAAe,WAAY,AAGlC,QAAOC,SAAU,QAAS,AAC1B,QAAOC,OAAQ,eAAgB,AAC/B,QAAOC,kBAAmB,uBAAwB,AAOlD,QAAOC,qBAAsB,6BAA8B,CA4B3D,MAAMC,iBAAmB,oBAEzB,MAAMC,sBAAwB,qCAE9B,MAAMC,eAAiB,CAAC,CACtBC,UAAU,CACVC,UAAYJ,gBAAgB,CAC5BK,QAAQ,CACY,GACpBF,WACE,oBAACG,KAAEC,KAAMJ,WAAYK,UAAWX,GAAGI,sBAAuBG,YACvDC,UAGH,oBAACI,OAAID,UAAWX,GAAGI,sBAAuBG,YAAaC,UAG3D,MAAMK,OAAS,CAAC,CACdP,UAAU,CACVQ,WAAW,CACXC,SAAW,EAAE,CACbC,KAAK,CACLC,MAAM,CACNC,QAAQ,CACRC,QAAU,eAAe,CACzBZ,UAAYJ,gBAAgB,CAChB,IACZN,UAAU,KACRI,cAAc,CACZmB,gBAAiBC,SAASC,aAAa,CAAC,yBACxCC,SAAUN,QAAQM,SAClBC,SAAUP,QAAQO,SAClBC,QAAS,CACPC,SAAUT,QAAQQ,SAASC,UAAY,KACzC,CACF,EACF,EAAG,EAAE,EAEL,MAAMC,YAAc7B,UAAU8B,QAAQ,CAACb,SAAU,CAC/Cc,aAAc,CAAC,IAAI,CACnBC,aAAc,CAAC,OAAQ,cAAe,MAAM,AAC9C,GAEA,MAAMC,WAAa7B,mBAEnB,OACE,oBAACU,OACCD,UAAWX,GAAG,kBAAmBmB,QAASZ,WAC1CyB,UAAQ,YACRC,MAAO,CAAEC,UAAW,EAAGC,SAAU,QAAS,GAE1C,oBAACvB,OAAID,UAAU,6DACb,oBAACN,gBAAeC,WAAYA,YAAc,KACxC,oBAAC8B,UAAOzB,UAAU,oCAAoCK,OACtD,oBAACqB,QACCC,IAAKP,WACLpB,UAAU,OACV4B,wBAAyB,CACvBC,OAAQb,WACV,IAEDb,aACC,oBAACuB,QAAK1B,UAAU,gEACbG,cAKNI,UACC,oBAACuB,UACCC,KAAK,SACL/B,UAAU,uDAEV,oBAACZ,MACC4C,KAAK,0BACLC,KAAK,UACLC,MAAOtC,cAOrB,CAEA,gBAAeM,MAAO"}
@@ -0,0 +1,2 @@
1
+ import{useEffect,useRef}from"react";const useRailsUjsLinks=()=>{const containerRef=useRef(null);useEffect(()=>{const container=containerRef.current;if(!container)return;const handleClick=event=>{const target=event.target;const link=target.closest("a[data-method]");if(!link)return;if(!container.contains(link))return;event.preventDefault();const method=link.dataset.method?.toLowerCase()??"get";const href=link.getAttribute("href");if(!href){console.warn("Rails UJS link has no href attribute");return}if(method!=="post"&&method!=="delete")return;const csrfParam=document.querySelector('meta[name="csrf-param"]')?.content;const csrfToken=document.querySelector('meta[name="csrf-token"]')?.content;const form=document.createElement("form");form.method="POST";form.action=href;form.style.display="none";if(csrfParam&&csrfToken){const csrfInput=document.createElement("input");csrfInput.type="hidden";csrfInput.name=csrfParam;csrfInput.value=csrfToken;form.appendChild(csrfInput)}else{console.warn("No CSRF token found in document")}if(method!=="post"){const methodInput=document.createElement("input");methodInput.type="hidden";methodInput.name="_method";methodInput.value=method;form.appendChild(methodInput)}document.body.appendChild(form);form.submit()};container.addEventListener("click",handleClick);return()=>container.removeEventListener("click",handleClick)},[]);return containerRef};export default useRailsUjsLinks;
2
+ //# sourceMappingURL=use-rails-ujs-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core/hooks/use-rails-ujs-hooks.ts"],"sourcesContent":["import { useEffect, useRef, RefObject } from \"react\";\n\ntype HttpMethod = \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n\ninterface RailsUjsLink extends HTMLAnchorElement {\n dataset: {\n method?: string;\n confirm?: string;\n remote?: string;\n };\n}\n\ninterface CsrfMetaTag extends HTMLMetaElement {\n content: string;\n}\n\nconst useRailsUjsLinks = (): RefObject<HTMLDivElement> => {\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleClick = (event: MouseEvent): void => {\n const target = event.target as HTMLElement;\n const link = target.closest<RailsUjsLink>(\"a[data-method]\");\n if (!link) return;\n\n // Check if the clicked link is within this component's container\n if (!container.contains(link)) return;\n\n event.preventDefault();\n\n const method = (link.dataset.method?.toLowerCase() ??\n \"get\") as HttpMethod;\n const href = link.getAttribute(\"href\");\n\n if (!href) {\n console.warn(\"Rails UJS link has no href attribute\");\n return;\n }\n\n // Only handle POST requests for now, expand for other methods if needed\n if (method !== \"post\" && method !== \"delete\") return;\n\n const csrfParam = document.querySelector<CsrfMetaTag>(\n 'meta[name=\"csrf-param\"]',\n )?.content;\n\n const csrfToken = document.querySelector<CsrfMetaTag>(\n 'meta[name=\"csrf-token\"]',\n )?.content;\n\n // Create and submit a hidden form\n const form = document.createElement(\"form\");\n form.method = \"POST\";\n form.action = href;\n form.style.display = \"none\";\n\n // Add CSRF token if present\n if (csrfParam && csrfToken) {\n const csrfInput = document.createElement(\"input\");\n csrfInput.type = \"hidden\";\n csrfInput.name = csrfParam;\n csrfInput.value = csrfToken;\n form.appendChild(csrfInput);\n } else {\n console.warn(\"No CSRF token found in document\");\n }\n\n // Add method override for non-POST methods\n if (method !== \"post\") {\n const methodInput = document.createElement(\"input\");\n methodInput.type = \"hidden\";\n methodInput.name = \"_method\";\n methodInput.value = method;\n form.appendChild(methodInput);\n }\n\n document.body.appendChild(form);\n form.submit();\n };\n\n container.addEventListener(\"click\", handleClick);\n return () => container.removeEventListener(\"click\", handleClick);\n }, []);\n\n return containerRef;\n};\n\nexport default useRailsUjsLinks;\n"],"names":["useEffect","useRef","useRailsUjsLinks","containerRef","container","current","handleClick","event","target","link","closest","contains","preventDefault","method","dataset","toLowerCase","href","getAttribute","console","warn","csrfParam","document","querySelector","content","csrfToken","form","createElement","action","style","display","csrfInput","type","name","value","appendChild","methodInput","body","submit","addEventListener","removeEventListener"],"mappings":"AAAA,OAASA,SAAS,CAAEC,MAAM,KAAmB,OAAQ,CAgBrD,MAAMC,iBAAmB,KACvB,MAAMC,aAAeF,OAAuB,MAE5CD,UAAU,KACR,MAAMI,UAAYD,aAAaE,OAAO,CACtC,GAAI,CAACD,UAAW,OAEhB,MAAME,YAAc,AAACC,QACnB,MAAMC,OAASD,MAAMC,MAAM,CAC3B,MAAMC,KAAOD,OAAOE,OAAO,CAAe,kBAC1C,GAAI,CAACD,KAAM,OAGX,GAAI,CAACL,UAAUO,QAAQ,CAACF,MAAO,OAE/BF,MAAMK,cAAc,GAEpB,MAAMC,OAAUJ,KAAKK,OAAO,CAACD,MAAM,EAAEE,eACnC,MACF,MAAMC,KAAOP,KAAKQ,YAAY,CAAC,QAE/B,GAAI,CAACD,KAAM,CACTE,QAAQC,IAAI,CAAC,wCACb,MACF,CAGA,GAAIN,SAAW,QAAUA,SAAW,SAAU,OAE9C,MAAMO,UAAYC,SAASC,aAAa,CACtC,4BACCC,QAEH,MAAMC,UAAYH,SAASC,aAAa,CACtC,4BACCC,QAGH,MAAME,KAAOJ,SAASK,aAAa,CAAC,OACpCD,CAAAA,KAAKZ,MAAM,CAAG,MACdY,CAAAA,KAAKE,MAAM,CAAGX,IACdS,CAAAA,KAAKG,KAAK,CAACC,OAAO,CAAG,OAGrB,GAAIT,WAAaI,UAAW,CAC1B,MAAMM,UAAYT,SAASK,aAAa,CAAC,QACzCI,CAAAA,UAAUC,IAAI,CAAG,QACjBD,CAAAA,UAAUE,IAAI,CAAGZ,SACjBU,CAAAA,UAAUG,KAAK,CAAGT,UAClBC,KAAKS,WAAW,CAACJ,UACnB,KAAO,CACLZ,QAAQC,IAAI,CAAC,kCACf,CAGA,GAAIN,SAAW,OAAQ,CACrB,MAAMsB,YAAcd,SAASK,aAAa,CAAC,QAC3CS,CAAAA,YAAYJ,IAAI,CAAG,QACnBI,CAAAA,YAAYH,IAAI,CAAG,SACnBG,CAAAA,YAAYF,KAAK,CAAGpB,OACpBY,KAAKS,WAAW,CAACC,YACnB,CAEAd,SAASe,IAAI,CAACF,WAAW,CAACT,MAC1BA,KAAKY,MAAM,EACb,EAEAjC,UAAUkC,gBAAgB,CAAC,QAAShC,aACpC,MAAO,IAAMF,UAAUmC,mBAAmB,CAAC,QAASjC,YACtD,EAAG,EAAE,EAEL,OAAOH,YACT,CAEA,gBAAeD,gBAAiB"}
@@ -0,0 +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(){}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)}}}})}}
2
+ //# sourceMappingURL=command-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core/insights/command-queue.ts"],"sourcesContent":["import {\n AnalyticsService,\n Command,\n InsightsConfig,\n InsightsIdentity,\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: string) => {\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 as keyof AnalyticsService,\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 target.realImplementation[prop](...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(): 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","track","_event","_properties","startSessionRecording","stopSessionRecording","setupObserver","constructor","Proxy","get","target","prop","String","push"],"mappings":"oLAMA,OAASA,eAAe,KAAQ,WAAY,AAC5C,WAAYC,WAAY,UAAW,AAGnC,QAAO,MAAMC,qBAgDX,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,eAAsB,CAAC,CACvBC,MAAMC,MAAc,CAAEC,WAAqC,CAAQ,CAAC,CACpEC,uBAA8B,CAAC,CAC/BC,sBAA6B,CAAC,CAC9BC,eAA4B,CAC1B,MAAO,KAAO,CAChB,CA7GAC,aAAc,CALd,sBAAQxB,gBAAyB,OACjC,sBAAQE,QAAmB,EAAE,EAC7B,sBAAQN,YAAqB,OAC7B,sBAAQE,qBAA6C,MAInD,OAAO,IAAI2B,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,GAAGrB,QACT,GAAI,CAACoB,OAAO3B,aAAa,EAAI,CAAC2B,OAAO7B,kBAAkB,CAAE,CAEvD,GAAI8B,OAAS,eAAgB,CAE3BD,OAAOjC,mBAAmB,CAACa,IAAI,CAAC,EAAE,EAClC,MACF,CAGA,GAAIoB,OAAO/B,SAAS,CAAE,CACpBJ,OAAOK,KAAK,CAAC,CAAC,qBAAqB,EAAEgC,OAAOD,MAAM,CAAC,CAAErB,KACvD,CAEAoB,OAAOzB,KAAK,CAAC4B,IAAI,CAAC,CAChBxB,WAAYsB,KACZrB,IACF,GAEA,MACF,CAGA,GAAI,OAAOoB,OAAO7B,kBAAkB,CAAC8B,KAAK,GAAK,WAAY,CACzD,OAAOD,OAAO7B,kBAAkB,CAAC8B,KAAK,IAAIrB,KAC5C,CACF,CACF,CACF,EACF,CAuEF"}
@@ -1,2 +1,2 @@
1
- export const initDatalayer=()=>{const dataLayer=window.dataLayer||[];window.dataLayer=dataLayer};export const track=(event,properties)=>{window.dataLayer.push({event,...properties})};
1
+ export const track=(event,properties)=>{if(typeof window==="undefined"){return}const dataLayer=window.dataLayer||[];window.dataLayer=dataLayer;window.dataLayer.push({event,...properties})};
2
2
  //# sourceMappingURL=datalayer.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/datalayer.ts"],"sourcesContent":["declare global {\n interface Window {\n dataLayer: unknown[];\n }\n}\n\nexport const initDatalayer = () => {\n const dataLayer = window.dataLayer || [];\n window.dataLayer = dataLayer;\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n window.dataLayer.push({\n event,\n ...properties,\n });\n};\n"],"names":["initDatalayer","dataLayer","window","track","event","properties","push"],"mappings":"AAMA,OAAO,MAAMA,cAAgB,KAC3B,MAAMC,UAAYC,OAAOD,SAAS,EAAI,EAAE,AACxCC,CAAAA,OAAOD,SAAS,CAAGA,SACrB,CAAE,AAEF,QAAO,MAAME,MAAQ,CAACC,MAAeC,cACnCH,OAAOD,SAAS,CAACK,IAAI,CAAC,CACpBF,MACA,GAAGC,UAAU,AACf,EACF,CAAE"}
1
+ {"version":3,"sources":["../../../src/core/insights/datalayer.ts"],"sourcesContent":["declare global {\n interface Window {\n dataLayer: unknown[];\n }\n}\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n if (typeof window === \"undefined\") {\n return;\n }\n\n const dataLayer = window.dataLayer || [];\n window.dataLayer = dataLayer;\n\n window.dataLayer.push({\n event,\n ...properties,\n });\n};\n"],"names":["track","event","properties","window","dataLayer","push"],"mappings":"AAMA,OAAO,MAAMA,MAAQ,CAACC,MAAeC,cACnC,GAAI,OAAOC,SAAW,YAAa,CACjC,MACF,CAEA,MAAMC,UAAYD,OAAOC,SAAS,EAAI,EAAE,AACxCD,CAAAA,OAAOC,SAAS,CAAGA,UAEnBD,OAAOC,SAAS,CAACC,IAAI,CAAC,CACpBJ,MACA,GAAGC,UAAU,AACf,EACF,CAAE"}
@@ -1,2 +1,2 @@
1
- import*as datalayer from"./datalayer";import*as mixpanel from"./mixpanel";import*as posthog from"./posthog";let debugMode=false;export const initInsights=({mixpanelToken,mixpanelAutoCapture,mixpanelRecordSessionsPercent=1,posthogApiKey,posthogApiHost,debug=false})=>{debugMode=!!debug;try{mixpanel.initMixpanel(mixpanelToken,mixpanelAutoCapture,debugMode,mixpanelRecordSessionsPercent)}catch(e){if(debugMode){console.error("Failed to initialize Mixpanel",e)}}try{posthog.initPosthog(posthogApiKey,posthogApiHost)}catch(e){if(debugMode){console.error("Failed to initialize Posthog",e)}}try{datalayer.initDatalayer()}catch(e){if(debugMode){console.error("Failed to initialize Datalayer",e)}}};export const enableDebugMode=()=>{debugMode=true;try{mixpanel.enableDebugMode();posthog.enableDebugMode()}catch(e){console.error("Failed to enable debug mode",e)}};export const disableDebugMode=()=>{debugMode=false;try{mixpanel.disableDebugMode();posthog.disableDebugMode()}catch(e){console.error("Failed to disable debug mode",e)}};export const identify=({userId,accountId,organisationId,email,name})=>{if(!userId){if(debugMode){console.warn("User ID not provided, skipping identify")}return}try{mixpanel.identify({userId,accountId,organisationId,email,name})}catch(e){if(debugMode){console.error("Failed to identify user in Mixpanel",e)}}try{posthog.identify({userId,accountId,organisationId,email,name})}catch(e){if(debugMode){console.error("Failed to identify user in Posthog",e)}}};export const trackPageView=()=>{try{mixpanel.trackPageView()}catch(e){if(debugMode){console.error("Failed to track page view in Mixpanel",e)}}try{posthog.trackPageView()}catch(e){if(debugMode){console.error("Failed to track page view in Posthog",e)}}};export const track=(event,properties)=>{try{mixpanel.track(event,properties)}catch(e){if(debugMode){console.error("Failed to track event in Mixpanel",e)}}try{posthog.track(event,properties)}catch(e){if(debugMode){console.error("Failed to track event in Posthog",e)}}try{datalayer.track(event,properties)}catch(e){if(debugMode){console.error("Failed to track event in Datalayer",e)}}};export const startSessionRecording=()=>{try{mixpanel.startSessionRecording()}catch(e){if(debugMode){console.error("Failed to start session recording in Mixpanel",e)}}try{posthog.startSessionRecording()}catch(e){if(debugMode){console.error("Failed to start session recording in Posthog",e)}}};export const stopSessionRecording=()=>{try{mixpanel.stopSessionRecording()}catch(e){if(debugMode){console.error("Failed to stop session recording in Mixpanel",e)}}try{posthog.stopSessionRecording()}catch(e){if(debugMode){console.error("Failed to stop session recording in Posthog",e)}}};export const setupObserver=()=>{const getInsightAttributes=element=>{const MAX_ATTRIBUTES=10;let count=0;const attributes={};for(const attr of 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;track(eventName||"element_clicked",properties)}};document.body.addEventListener("click",handleClick);return()=>{document.body.removeEventListener("click",handleClick)}};
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=()=>insights.trackPageView();export const track=(event,properties)=>insights.track(event,properties);export const startSessionRecording=()=>insights.startSessionRecording();export const stopSessionRecording=()=>insights.stopSessionRecording();export const setupObserver=()=>insights.setupObserver();
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/core/insights/index.ts"],"sourcesContent":["import * as datalayer from \"./datalayer\";\nimport * as mixpanel from \"./mixpanel\";\nimport * as posthog from \"./posthog\";\nimport { InsightsIdentity } from \"./types\";\n\nexport type InsightsConfig = {\n debug: boolean;\n mixpanelToken: string;\n mixpanelAutoCapture: boolean;\n mixpanelRecordSessionsPercent: number;\n posthogApiKey: string;\n posthogApiHost: string;\n};\n\nlet debugMode = false;\n\nexport const initInsights = ({\n mixpanelToken,\n mixpanelAutoCapture,\n mixpanelRecordSessionsPercent = 1,\n posthogApiKey,\n posthogApiHost,\n debug = false,\n}: InsightsConfig) => {\n debugMode = !!debug;\n\n try {\n mixpanel.initMixpanel(\n mixpanelToken,\n mixpanelAutoCapture,\n debugMode,\n mixpanelRecordSessionsPercent,\n );\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to initialize Mixpanel\", e);\n }\n }\n\n try {\n posthog.initPosthog(posthogApiKey, posthogApiHost);\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to initialize Posthog\", e);\n }\n }\n\n try {\n datalayer.initDatalayer();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to initialize Datalayer\", e);\n }\n }\n};\n\nexport const enableDebugMode = () => {\n debugMode = true;\n try {\n mixpanel.enableDebugMode();\n posthog.enableDebugMode();\n } catch (e) {\n console.error(\"Failed to enable debug mode\", e);\n }\n};\n\nexport const disableDebugMode = () => {\n debugMode = false;\n try {\n mixpanel.disableDebugMode();\n posthog.disableDebugMode();\n } catch (e) {\n console.error(\"Failed to disable debug mode\", e);\n }\n};\n\nexport const identify = ({\n userId,\n accountId,\n organisationId,\n email,\n name,\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 if (debugMode) {\n console.warn(\"User ID not provided, skipping identify\");\n }\n return;\n }\n\n try {\n mixpanel.identify({ userId, accountId, organisationId, email, name });\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to identify user in Mixpanel\", e);\n }\n }\n\n try {\n posthog.identify({ userId, accountId, organisationId, email, name });\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to identify user in Posthog\", e);\n }\n }\n};\n\nexport const trackPageView = () => {\n try {\n mixpanel.trackPageView();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track page view in Mixpanel\", e);\n }\n }\n\n try {\n posthog.trackPageView();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track page view in Posthog\", e);\n }\n }\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n try {\n mixpanel.track(event, properties);\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track event in Mixpanel\", e);\n }\n }\n\n try {\n posthog.track(event, properties);\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track event in Posthog\", e);\n }\n }\n\n try {\n datalayer.track(event, properties);\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to track event in Datalayer\", e);\n }\n }\n};\n\nexport const startSessionRecording = () => {\n try {\n mixpanel.startSessionRecording();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to start session recording in Mixpanel\", e);\n }\n }\n\n try {\n posthog.startSessionRecording();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to start session recording in Posthog\", e);\n }\n }\n};\n\nexport const stopSessionRecording = () => {\n try {\n mixpanel.stopSessionRecording();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to stop session recording in Mixpanel\", e);\n }\n }\n\n try {\n posthog.stopSessionRecording();\n } catch (e) {\n if (debugMode) {\n console.error(\"Failed to stop session recording in Posthog\", e);\n }\n }\n};\n\ntype InsightAttributes = {\n event?: string;\n [key: string]: string | undefined;\n};\n\nexport const setupObserver = () => {\n // Helper to get all data-insight-* attributes from an element\n const getInsightAttributes = (element): InsightAttributes => {\n // limit how many data attributes we'll process\n const MAX_ATTRIBUTES = 10;\n let count = 0;\n\n const attributes: InsightAttributes = {};\n\n for (const attr of 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) continue;\n\n // Convert data-insight-event-name to eventName\n const key = attr.name\n .replace(\"data-insight-\", \"\")\n .split(\"-\")\n .map((part, index) =>\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) => {\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 current = current.parentElement;\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 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"],"names":["datalayer","mixpanel","posthog","debugMode","initInsights","mixpanelToken","mixpanelAutoCapture","mixpanelRecordSessionsPercent","posthogApiKey","posthogApiHost","debug","initMixpanel","e","console","error","initPosthog","initDatalayer","enableDebugMode","disableDebugMode","identify","userId","accountId","organisationId","email","name","warn","trackPageView","track","event","properties","startSessionRecording","stopSessionRecording","setupObserver","getInsightAttributes","element","MAX_ATTRIBUTES","count","attributes","attr","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/index.ts"],"sourcesContent":["import { InsightsConfig, InsightsIdentity } 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 = () => insights.trackPageView();\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","track","event","properties","startSessionRecording","stopSessionRecording","setupObserver"],"mappings":"AACA,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,IAAMP,SAASO,aAAa,EAAG,AAC5D,QAAO,MAAMC,MAAQ,CAACC,MAAeC,aACnCV,SAASQ,KAAK,CAACC,MAAOC,WAAY,AACpC,QAAO,MAAMC,sBAAwB,IAAMX,SAASW,qBAAqB,EAAG,AAC5E,QAAO,MAAMC,qBAAuB,IAAMZ,SAASY,oBAAoB,EAAG,AAC1E,QAAO,MAAMC,cAAgB,IAAMb,SAASa,aAAa,EAAG"}
@@ -0,0 +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()}));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();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()});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()})});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();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()})});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()})})});
2
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +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 } 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}));\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\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 });\n\n it(\"should handle errors in queued methods gracefully\", async () => {\n // Setup an error for one of the methods\n mixpanel.track.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 });\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\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\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.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.mockImplementationOnce(() => {\n throw new Error(\"Mixpanel track error\");\n });\n\n posthog.track.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"],"names":["describe","expect","beforeEach","afterEach","it","vi","datalayer","mixpanel","posthog","logger","insights","mock","track","fn","initMixpanel","enableDebugMode","disableDebugMode","identify","trackPageView","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","post","cleanup","setupObserver","testElement","createElement","setAttribute","appendChild","click","buttonId","parentElement","childElement","textContent","containerId","toThrow","test","objectContaining","event","properties"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const styles=[`background: #ff5416`,`border-radius: 0.5em`,`color: white`,`font-weight: bold`,`padding: 2px 0.5em`].join(";");export const debug=(...args)=>{console.debug(`%cinsights`,styles,...args)};export const info=(...args)=>{console.info(`%cinsights`,styles,...args)};export const warn=(...args)=>{console.warn(`%cinsights`,styles,...args)};export const error=(...args)=>{console.error(`%cinsights`,styles,...args)};
2
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core/insights/logger.ts"],"sourcesContent":["// Inspired by Gatsby's logging\n\nconst styles = [\n `background: #ff5416`, // orange-600\n `border-radius: 0.5em`,\n `color: white`,\n `font-weight: bold`,\n `padding: 2px 0.5em`,\n].join(\";\");\n\nexport const debug = (...args: unknown[]) => {\n console.debug(`%cinsights`, styles, ...args);\n};\n\nexport const info = (...args: unknown[]) => {\n console.info(`%cinsights`, styles, ...args);\n};\n\nexport const warn = (...args: unknown[]) => {\n console.warn(`%cinsights`, styles, ...args);\n};\n\nexport const error = (...args: unknown[]) => {\n console.error(`%cinsights`, styles, ...args);\n};\n"],"names":["styles","join","debug","args","console","info","warn","error"],"mappings":"AAEA,MAAMA,OAAS,CACb,CAAC,mBAAmB,CAAC,CACrB,CAAC,oBAAoB,CAAC,CACtB,CAAC,YAAY,CAAC,CACd,CAAC,iBAAiB,CAAC,CACnB,CAAC,kBAAkB,CAAC,CACrB,CAACC,IAAI,CAAC,IAEP,QAAO,MAAMC,MAAQ,CAAC,GAAGC,QACvBC,QAAQF,KAAK,CAAC,CAAC,UAAU,CAAC,CAAEF,UAAWG,KACzC,CAAE,AAEF,QAAO,MAAME,KAAO,CAAC,GAAGF,QACtBC,QAAQC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAEL,UAAWG,KACxC,CAAE,AAEF,QAAO,MAAMG,KAAO,CAAC,GAAGH,QACtBC,QAAQE,IAAI,CAAC,CAAC,UAAU,CAAC,CAAEN,UAAWG,KACxC,CAAE,AAEF,QAAO,MAAMI,MAAQ,CAAC,GAAGJ,QACvBC,QAAQG,KAAK,CAAC,CAAC,UAAU,CAAC,CAAEP,UAAWG,KACzC,CAAE"}
@@ -1,2 +1,2 @@
1
- import mixpanel from"mixpanel-browser";export const initMixpanel=(token,autoCapture=false,debug=false,recordSessionsPercent=1)=>{const blockSelectors=["[ph-no-capture]",'[data-sl="mask"]'];if(!token){console.warn("Mixpanel token not provided, skipping initialization");return}mixpanel.init(token,{debug:debug,persistence:"localStorage",autocapture:autoCapture?{block_selectors:blockSelectors}:false,track_pageview:false,record_sessions_percent:recordSessionsPercent})};export const enableDebugMode=()=>{mixpanel.set_config({debug:true})};export const disableDebugMode=()=>{mixpanel.set_config({debug:false})};export const identify=({userId,accountId,organisationId,email,name})=>{if(!userId){return}mixpanel.identify(userId);if(email||name){mixpanel.people.set({$email:email,$name:name})}if(accountId){mixpanel.people.union({account_id:[accountId]})}if(organisationId){mixpanel.people.set({organisation_id:[organisationId]})}};export const trackPageView=mixpanel.track_pageview;export const track=mixpanel.track;export const startSessionRecording=mixpanel.start_session_recording;export const stopSessionRecording=mixpanel.stop_session_recording;
1
+ import mixpanel from"mixpanel-browser";export const initMixpanel=(token,autoCapture=false,debug=false,recordSessionsPercent=1)=>{const blockSelectors=["[ph-no-capture]",'[data-sl="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}:false,track_pageview:false,record_sessions_percent:recordSessionsPercent,record_mask_text_selector:null})};export const enableDebugMode=()=>{mixpanel.set_config({debug:true})};export const disableDebugMode=()=>{mixpanel.set_config({debug:false})};export const identify=({userId,accountId,organisationId,email,name})=>{if(!userId){return}mixpanel.identify(userId.toString());if(email||name){mixpanel.people.set({$email:email,$name:name})}if(accountId){mixpanel.people.union({accounts:[accountId.toString()]})}if(organisationId){mixpanel.people.set({organization_id:[organisationId.toString()]})}};const redactUrlSegments=()=>{const pathSegments=window.location.pathname.split("/");const redactedSegments=pathSegments.map(segment=>{return/^\d+$/.test(segment)?"{redacted}":segment});const url=new URL(window.location.href);url.pathname=redactedSegments.join("/");return decodeURI(url.toString()).toLowerCase()};export const trackPageView=()=>{mixpanel.track_pageview({redacted_path:redactUrlSegments()})};export const track=(event,properties)=>{mixpanel.track(event,properties)};export const startSessionRecording=()=>{mixpanel.start_session_recording()};export const stopSessionRecording=()=>{mixpanel.stop_session_recording()};
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 } from \"./types\";\n\nexport const initMixpanel = (\n token: string,\n autoCapture: boolean = false,\n debug: boolean = false,\n recordSessionsPercent: number = 1,\n) => {\n const blockSelectors = [\"[ph-no-capture]\", '[data-sl=\"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 }\n : false,\n track_pageview: false, // We'll track page views manually\n record_sessions_percent: recordSessionsPercent,\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}: 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);\n\n if (email || name) {\n mixpanel.people.set({ $email: email, $name: name });\n }\n\n if (accountId) {\n mixpanel.people.union({ account_id: [accountId] });\n }\n\n if (organisationId) {\n mixpanel.people.set({ organisation_id: [organisationId] });\n }\n};\n\nexport const trackPageView = mixpanel.track_pageview;\n\nexport const track = mixpanel.track;\n\nexport const startSessionRecording = mixpanel.start_session_recording;\n\nexport const stopSessionRecording = mixpanel.stop_session_recording;\n"],"names":["mixpanel","initMixpanel","token","autoCapture","debug","recordSessionsPercent","blockSelectors","console","warn","init","persistence","autocapture","block_selectors","track_pageview","record_sessions_percent","enableDebugMode","set_config","disableDebugMode","identify","userId","accountId","organisationId","email","name","people","set","$email","$name","union","account_id","organisation_id","trackPageView","track","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,sBAAgC,CAAC,IAEjC,MAAMC,eAAiB,CAAC,kBAAmB,mBAAmB,CAC9D,GAAI,CAACJ,MAAO,CACVK,QAAQC,IAAI,CAAC,wDACb,MACF,CAEAR,SAASS,IAAI,CAACP,MAAO,CACnBE,MAAOA,MACPM,YAAa,eACbC,YAAaR,YACT,CACES,gBAAiBN,cACnB,EACA,MACJO,eAAgB,MAChBC,wBAAyBT,qBAC3B,EACF,CAAE,AAEF,QAAO,MAAMU,gBAAkB,KAC7Bf,SAASgB,UAAU,CAAC,CAAEZ,MAAO,IAAK,EACpC,CAAE,AAEF,QAAO,MAAMa,iBAAmB,KAC9BjB,SAASgB,UAAU,CAAC,CAAEZ,MAAO,KAAM,EACrC,CAAE,AAEF,QAAO,MAAMc,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACa,IAGjB,GAAI,CAACJ,OAAQ,CACX,MACF,CAEAnB,SAASkB,QAAQ,CAACC,QAElB,GAAIG,OAASC,KAAM,CACjBvB,SAASwB,MAAM,CAACC,GAAG,CAAC,CAAEC,OAAQJ,MAAOK,MAAOJ,IAAK,EACnD,CAEA,GAAIH,UAAW,CACbpB,SAASwB,MAAM,CAACI,KAAK,CAAC,CAAEC,WAAY,CAACT,UAAU,AAAC,EAClD,CAEA,GAAIC,eAAgB,CAClBrB,SAASwB,MAAM,CAACC,GAAG,CAAC,CAAEK,gBAAiB,CAACT,eAAe,AAAC,EAC1D,CACF,CAAE,AAEF,QAAO,MAAMU,cAAgB/B,SAASa,cAAc,AAAC,AAErD,QAAO,MAAMmB,MAAQhC,SAASgC,KAAK,AAAC,AAEpC,QAAO,MAAMC,sBAAwBjC,SAASkC,uBAAuB,AAAC,AAEtE,QAAO,MAAMC,qBAAuBnC,SAASoC,sBAAsB,AAAC"}
1
+ {"version":3,"sources":["../../../src/core/insights/mixpanel.ts"],"sourcesContent":["import mixpanel from \"mixpanel-browser\";\n\nimport { InsightsIdentity } from \"./types\";\n\nexport const initMixpanel = (\n token: string,\n autoCapture: boolean = false,\n debug: boolean = false,\n recordSessionsPercent = 1,\n) => {\n const blockSelectors = [\"[ph-no-capture]\", '[data-sl=\"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 }\n : false,\n track_pageview: false, // We'll track page views manually\n record_sessions_percent: recordSessionsPercent,\n record_mask_text_selector: null, // Prevents all text from being masked - we have other masking configured/enabled\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}: 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 if (email || name) {\n mixpanel.people.set({ $email: email, $name: name });\n }\n\n if (accountId) {\n mixpanel.people.union({ accounts: [accountId.toString()] });\n }\n\n if (organisationId) {\n mixpanel.people.set({ organization_id: [organisationId.toString()] });\n }\n};\n\n// Simple function to replace all digits in a URL path with {redacted},\n// purely to make reporting based on aggregates easier\nconst redactUrlSegments = () => {\n const pathSegments = window.location.pathname.split(\"/\");\n\n const redactedSegments = pathSegments.map((segment) => {\n // Check if the segment contains only digits\n return /^\\d+$/.test(segment) ? \"{redacted}\" : 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 = () => {\n // Add the redacted URL to the page view event for reporting\n mixpanel.track_pageview({\n redacted_path: redactUrlSegments(),\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","track_pageview","record_sessions_percent","record_mask_text_selector","enableDebugMode","set_config","disableDebugMode","identify","userId","accountId","organisationId","email","name","toString","people","set","$email","$name","union","accounts","organization_id","redactUrlSegments","pathSegments","window","location","pathname","split","redactedSegments","map","segment","test","url","URL","href","join","decodeURI","toLowerCase","trackPageView","redacted_path","track","event","properties","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,kBAAmB,mBAAmB,CAC9D,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,IACxB,EACA,MACJC,eAAgB,MAChBC,wBAAyBV,sBACzBW,0BAA2B,IAC7B,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BjB,SAASkB,UAAU,CAAC,CAAEd,MAAO,IAAK,EACpC,CAAE,AAEF,QAAO,MAAMe,iBAAmB,KAC9BnB,SAASkB,UAAU,CAAC,CAAEd,MAAO,KAAM,EACrC,CAAE,AAEF,QAAO,MAAMgB,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACa,IAGjB,GAAI,CAACJ,OAAQ,CACX,MACF,CAEArB,SAASoB,QAAQ,CAACC,OAAOK,QAAQ,IAEjC,GAAIF,OAASC,KAAM,CACjBzB,SAAS2B,MAAM,CAACC,GAAG,CAAC,CAAEC,OAAQL,MAAOM,MAAOL,IAAK,EACnD,CAEA,GAAIH,UAAW,CACbtB,SAAS2B,MAAM,CAACI,KAAK,CAAC,CAAEC,SAAU,CAACV,UAAUI,QAAQ,GAAG,AAAC,EAC3D,CAEA,GAAIH,eAAgB,CAClBvB,SAAS2B,MAAM,CAACC,GAAG,CAAC,CAAEK,gBAAiB,CAACV,eAAeG,QAAQ,GAAG,AAAC,EACrE,CACF,CAAE,CAIF,MAAMQ,kBAAoB,KACxB,MAAMC,aAAeC,OAAOC,QAAQ,CAACC,QAAQ,CAACC,KAAK,CAAC,KAEpD,MAAMC,iBAAmBL,aAAaM,GAAG,CAAC,AAACC,UAEzC,MAAO,QAAQC,IAAI,CAACD,SAAW,aAAeA,OAChD,GAGA,MAAME,IAAM,IAAIC,IAAIT,OAAOC,QAAQ,CAACS,IAAI,CACxCF,CAAAA,IAAIN,QAAQ,CAAGE,iBAAiBO,IAAI,CAAC,KAErC,OAAOC,UAAUJ,IAAIlB,QAAQ,IAAIuB,WAAW,EAC9C,CAEA,QAAO,MAAMC,cAAgB,KAE3BlD,SAASc,cAAc,CAAC,CACtBqC,cAAejB,mBACjB,EACF,CAAE,AAEF,QAAO,MAAMkB,MAAQ,CAACC,MAAeC,cACnCtD,SAASoD,KAAK,CAACC,MAAOC,WACxB,CAAE,AAEF,QAAO,MAAMC,sBAAwB,KACnCvD,SAASwD,uBAAuB,EAClC,CAAE,AAEF,QAAO,MAAMC,qBAAuB,KAClCzD,SAAS0D,sBAAsB,EACjC,CAAE"}
@@ -1,2 +1,2 @@
1
- import posthog from"posthog-js";export const initPosthog=(apiKey,apiHost)=>{posthog.init(apiKey,{api_host:apiHost,capture_pageview:false})};export const enableDebugMode=()=>{posthog.debug()};export const disableDebugMode=()=>{posthog.debug(false)};export const identify=({userId,accountId,organisationId,email,name})=>{if(!userId){return}if(userId!==posthog.get_distinct_id()){posthog.identify(userId,{email,name})}if(accountId){posthog.group("account",accountId)}if(organisationId){posthog.group("organisation",organisationId)}};export const trackPageView=()=>{posthog.capture("$pageview")};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)=>{posthog.init(apiKey,{api_host:apiHost,capture_pageview:false})};export const enableDebugMode=()=>{posthog.debug()};export const disableDebugMode=()=>{posthog.debug(false)};export const identify=({userId,accountId,organisationId,email,name})=>{if(!userId){return}if(userId!==posthog.get_distinct_id()){posthog.identify(userId,{email,name})}if(accountId){posthog.group("account",accountId)}if(organisationId){posthog.group("organisation",organisationId)}};export const trackPageView=()=>{posthog.capture("$pageview")};export const track=(event,properties)=>{posthog.capture(event,properties)};export const startSessionRecording=()=>{posthog.startSessionRecording()};export const stopSessionRecording=()=>{posthog.stopSessionRecording()};
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 posthog.init(apiKey, {\n api_host: apiHost,\n capture_pageview: false,\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}: 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 });\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 = () => {\n posthog.capture(\"$pageview\");\n};\n\nexport const track = (event: string, properties?: Record<string, unknown>) => {\n posthog.capture(event, properties);\n};\n\nexport const startSessionRecording = posthog.startSessionRecording;\n\nexport const stopSessionRecording = posthog.stopSessionRecording;\n"],"names":["posthog","initPosthog","apiKey","apiHost","init","api_host","capture_pageview","enableDebugMode","debug","disableDebugMode","identify","userId","accountId","organisationId","email","name","get_distinct_id","group","trackPageView","capture","track","event","properties","startSessionRecording","stopSessionRecording"],"mappings":"AAAA,OAAOA,YAAa,YAAa,AAIjC,QAAO,MAAMC,YAAc,CAACC,OAAgBC,WAC1CH,QAAQI,IAAI,CAACF,OAAQ,CACnBG,SAAUF,QACVG,iBAAkB,KACpB,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BP,QAAQQ,KAAK,EACf,CAAE,AAEF,QAAO,MAAMC,iBAAmB,KAC9BT,QAAQQ,KAAK,CAAC,MAChB,CAAE,AAEF,QAAO,MAAME,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACa,IAGjB,GAAI,CAACJ,OAAQ,CACX,MACF,CAEA,GAAIA,SAAWX,QAAQgB,eAAe,GAAI,CACxChB,QAAQU,QAAQ,CAACC,OAAQ,CAAEG,MAAOC,IAAK,EACzC,CAGA,GAAIH,UAAW,CACbZ,QAAQiB,KAAK,CAAC,UAAWL,UAC3B,CAGA,GAAIC,eAAgB,CAClBb,QAAQiB,KAAK,CAAC,eAAgBJ,eAChC,CACF,CAAE,AAEF,QAAO,MAAMK,cAAgB,KAC3BlB,QAAQmB,OAAO,CAAC,YAClB,CAAE,AAEF,QAAO,MAAMC,MAAQ,CAACC,MAAeC,cACnCtB,QAAQmB,OAAO,CAACE,MAAOC,WACzB,CAAE,AAEF,QAAO,MAAMC,sBAAwBvB,QAAQuB,qBAAqB,AAAC,AAEnE,QAAO,MAAMC,qBAAuBxB,QAAQwB,oBAAoB,AAAC"}
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 posthog.init(apiKey, {\n api_host: apiHost,\n capture_pageview: false,\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}: 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 });\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 = () => {\n posthog.capture(\"$pageview\");\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","enableDebugMode","debug","disableDebugMode","identify","userId","accountId","organisationId","email","name","get_distinct_id","group","trackPageView","capture","track","event","properties","startSessionRecording","stopSessionRecording"],"mappings":"AAAA,OAAOA,YAAa,YAAa,AAIjC,QAAO,MAAMC,YAAc,CAACC,OAAgBC,WAC1CH,QAAQI,IAAI,CAACF,OAAQ,CACnBG,SAAUF,QACVG,iBAAkB,KACpB,EACF,CAAE,AAEF,QAAO,MAAMC,gBAAkB,KAC7BP,QAAQQ,KAAK,EACf,CAAE,AAEF,QAAO,MAAMC,iBAAmB,KAC9BT,QAAQQ,KAAK,CAAC,MAChB,CAAE,AAEF,QAAO,MAAME,SAAW,CAAC,CACvBC,MAAM,CACNC,SAAS,CACTC,cAAc,CACdC,KAAK,CACLC,IAAI,CACa,IAGjB,GAAI,CAACJ,OAAQ,CACX,MACF,CAEA,GAAIA,SAAWX,QAAQgB,eAAe,GAAI,CACxChB,QAAQU,QAAQ,CAACC,OAAQ,CAAEG,MAAOC,IAAK,EACzC,CAGA,GAAIH,UAAW,CACbZ,QAAQiB,KAAK,CAAC,UAAWL,UAC3B,CAGA,GAAIC,eAAgB,CAClBb,QAAQiB,KAAK,CAAC,eAAgBJ,eAChC,CACF,CAAE,AAEF,QAAO,MAAMK,cAAgB,KAC3BlB,QAAQmB,OAAO,CAAC,YAClB,CAAE,AAEF,QAAO,MAAMC,MAAQ,CAACC,MAAeC,cACnCtB,QAAQmB,OAAO,CAACE,MAAOC,WACzB,CAAE,AAEF,QAAO,MAAMC,sBAAwB,KACnCvB,QAAQuB,qBAAqB,EAC/B,CAAE,AAEF,QAAO,MAAMC,qBAAuB,KAClCxB,QAAQwB,oBAAoB,EAC9B,CAAE"}
@@ -0,0 +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}=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})}try{mixpanel.identify({userId,accountId,organisationId,email,name})}catch(e){if(this.debugMode){logger.error("Failed to identify user in Mixpanel",e)}}try{posthog.identify({userId,accountId,organisationId,email,name})}catch(e){if(this.debugMode){logger.error("Failed to identify user in Posthog",e)}}}trackPageView(){if(this.debugMode){logger.info("Tracking page view")}try{mixpanel.trackPageView()}catch(e){if(this.debugMode){logger.error("Failed to track page view in Mixpanel",e)}}try{posthog.trackPageView()}catch(e){if(this.debugMode){logger.error("Failed to track page view in Posthog",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 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
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core/insights/service.ts"],"sourcesContent":["import type {\n AnalyticsService,\n InsightsConfig,\n InsightsIdentity,\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 } = 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 });\n }\n\n try {\n mixpanel.identify({ userId, accountId, organisationId, email, name });\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({ userId, accountId, organisationId, email, name });\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to identify user in Posthog\", e);\n }\n }\n }\n\n trackPageView(): void {\n if (this.debugMode) {\n logger.info(\"Tracking page view\");\n }\n\n try {\n mixpanel.trackPageView();\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();\n } catch (e) {\n if (this.debugMode) {\n logger.error(\"Failed to track page view in Posthog\", e);\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,\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 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, index) =>\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) => {\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 current = current.parentElement;\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","warn","info","trackPageView","track","event","properties","startSessionRecording","stopSessionRecording","setupObserver","getInsightAttributes","element","MAX_ATTRIBUTES","count","attributes","attr","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 InsightsIdentity = {\n userId: string;\n accountId: string;\n organisationId?: string;\n email?: string;\n name?: string;\n};\n"],"names":[],"mappings":"AAAA,QAME"}
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: () => 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};\n"],"names":[],"mappings":"AA4BA,QAME"}
package/index.d.ts CHANGED
@@ -440,7 +440,7 @@ type FlashProps = {
440
440
  };
441
441
  type FlashesProps = {
442
442
  flashes: {
443
- items: FlashProps[];
443
+ items: Pick<FlashProps, "type" | "content">[];
444
444
  };
445
445
  };
446
446
  type BackendFlashesProps = {
@@ -751,9 +751,6 @@ export type MeganavPaths = {
751
751
  logo?: string;
752
752
  iconSprites: string;
753
753
  ablyStack: string;
754
- blogThumb1: string;
755
- blogThumb2: string;
756
- blogThumb3: string;
757
754
  awsLogo?: string;
758
755
  };
759
756
  export type MeganavPanels = {
@@ -788,7 +785,7 @@ export type MeganavSessionState = {
788
785
  href: string;
789
786
  };
790
787
  };
791
- export type MeganavNoticeProps = {
788
+ export type NoticeApiProps = {
792
789
  props: {
793
790
  title: string;
794
791
  bodyText: string;
@@ -807,7 +804,7 @@ export type MeganavNoticeProps = {
807
804
  type MeganavProps = {
808
805
  paths?: MeganavPaths;
809
806
  themeName: "white" | "black" | "transparentToWhite";
810
- notice?: MeganavNoticeProps;
807
+ notice?: NoticeApiProps;
811
808
  loginLink?: string;
812
809
  urlBase?: string;
813
810
  addSearchApiKey: string;
@@ -1490,6 +1487,13 @@ export function queryIdAll(val: any, root?: Document): NodeListOf<Element>;
1490
1487
  //# sourceMappingURL=dom-query.d.ts.map
1491
1488
  }
1492
1489
 
1490
+ declare module '@ably/ui/core/hooks/use-rails-ujs-hooks' {
1491
+ import { RefObject } from "react";
1492
+ const useRailsUjsLinks: () => RefObject<HTMLDivElement>;
1493
+ export default useRailsUjsLinks;
1494
+ //# sourceMappingURL=use-rails-ujs-hooks.d.ts.map
1495
+ }
1496
+
1493
1497
  declare module '@ably/ui/core/hubspot-chat-toggle' {
1494
1498
  export default function toggleChatWidget(params: any): (() => void) | undefined;
1495
1499
  //# sourceMappingURL=hubspot-chat-toggle.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ably/ui",
3
- "version": "15.7.0-dev.a9daaf05",
3
+ "version": "15.8.0-dev.1527ef06",
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",
@@ -34,7 +34,7 @@
34
34
  "@types/svg-sprite": "^0.0.39",
35
35
  "@typescript-eslint/eslint-plugin": "^8.25.0",
36
36
  "@typescript-eslint/parser": "^8.25.0",
37
- "@vitejs/plugin-react": "^4.2.1",
37
+ "@vitejs/plugin-react-swc": "^3.8.0",
38
38
  "@whitespace/storybook-addon-html": "^6.1.1",
39
39
  "autoprefixer": "^10.0.2",
40
40
  "eslint": "^8.57.0",
@@ -43,6 +43,7 @@
43
43
  "eslint-plugin-storybook": "^0.11.4",
44
44
  "heroicons": "^2.2.0",
45
45
  "http-server": "14.1.1",
46
+ "jsdom": "^26.0.0",
46
47
  "mixpanel-browser": "^2.60.0",
47
48
  "msw": "2.7.1",
48
49
  "msw-storybook-addon": "^2.0.2",
@@ -56,7 +57,8 @@
56
57
  "tailwindcss": "^3.3.6",
57
58
  "ts-node": "^10.9.2",
58
59
  "typescript": "5.7.3",
59
- "vite": "^6.2.0"
60
+ "vite": "^6.2.0",
61
+ "vitest": "^3.0.8"
60
62
  },
61
63
  "scripts": {
62
64
  "build:prebuild": "rm -rf core reset && mkdir -p dist/core",
@@ -75,8 +77,10 @@
75
77
  "start": "vite --port 5000",
76
78
  "storybook": "yarn build && storybook dev -p 6006",
77
79
  "build-storybook": "yarn build && storybook build --quiet -o preview",
78
- "test": "npx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook && yarn http-server preview --port 6007 --silent\" \"wait-on tcp:6007 && yarn test-storybook --url http://127.0.0.1:6007\"",
79
- "test:update-snapshots": "npx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook && yarn http-server preview --port 6007 --silent\" \"wait-on tcp:6007 && yarn test-storybook -u --url http://127.0.0.1:6007\""
80
+ "test": "yarn test:storybook && yarn test:vitest",
81
+ "test:storybook": "npx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook && yarn http-server preview --port 6007 --silent\" \"wait-on tcp:6007 && yarn test-storybook --url http://127.0.0.1:6007\"",
82
+ "test:update-snapshots": "npx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook && yarn http-server preview --port 6007 --silent\" \"wait-on tcp:6007 && yarn test-storybook -u --url http://127.0.0.1:6007\"",
83
+ "test:vitest": "vitest --environment=jsdom --dir=src"
80
84
  },
81
85
  "dependencies": {
82
86
  "@radix-ui/react-accordion": "^1.2.1",